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,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
|
|
5
|
+
module Poetry
|
|
6
|
+
module Ui
|
|
7
|
+
# Deferred family: lazy-loaded regions with loading and error states.
|
|
8
|
+
module Deferred
|
|
9
|
+
# A deferred region: a Turbo Frame that fetches its content when it
|
|
10
|
+
# becomes VISIBLE (loading: :lazy, the default - so a deferred Tabs
|
|
11
|
+
# panel or HoverCard body loads on first reveal with no extra
|
|
12
|
+
# wiring) or right after paint (:eager). The block is the loading
|
|
13
|
+
# placeholder (a Skeleton renders when absent), and a failed fetch
|
|
14
|
+
# shows a visible, retryable error card instead of silent
|
|
15
|
+
# blankness.
|
|
16
|
+
#
|
|
17
|
+
# @example
|
|
18
|
+
# render Poetry::Ui::Deferred::Component.new(src: "/dashboard/activity")
|
|
19
|
+
class Component < Poetry::Core::Component
|
|
20
|
+
# Projected into the registry, llms.txt, and the agent surface.
|
|
21
|
+
AGENT_RULES = [
|
|
22
|
+
"Use poetry_deferred(src:) for expensive regions - never a spinner div + a hand-rolled fetch.",
|
|
23
|
+
"loading: :lazy (the default) fetches on visibility: a deferred region inside a hidden " \
|
|
24
|
+
"Tabs panel (with_tab defer:) or HoverCard (defer:) loads on first reveal for free.",
|
|
25
|
+
"The block is the placeholder (a Skeleton renders when absent); failure shows a retryable " \
|
|
26
|
+
"error card automatically - never hand-wire loading or error states around it."
|
|
27
|
+
].freeze
|
|
28
|
+
|
|
29
|
+
use_stimulus do
|
|
30
|
+
# src rides the controller value, NOT the frame markup: connect()
|
|
31
|
+
# arms it, so a fast response can never beat the controllers
|
|
32
|
+
# module graph to the frame.
|
|
33
|
+
on :root do
|
|
34
|
+
controller :deferred do
|
|
35
|
+
register
|
|
36
|
+
value :src
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
# Template-consumed elements (previously hand-written strings).
|
|
40
|
+
on :placeholder do
|
|
41
|
+
controller(:deferred) { target :placeholder }
|
|
42
|
+
end
|
|
43
|
+
on :error_template do
|
|
44
|
+
controller(:deferred) { target :error }
|
|
45
|
+
end
|
|
46
|
+
on :retry do
|
|
47
|
+
controller(:deferred) { action :retry, on: :click }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
option :src, :string, required: true, doc: "The URL to fetch - required; rendering without it raises."
|
|
52
|
+
option :loading, :symbol, default: :lazy,
|
|
53
|
+
doc: ":lazy fetches when the frame becomes visible; :eager fetches right after paint."
|
|
54
|
+
|
|
55
|
+
part "deferred", "The <turbo-frame> root - src is armed at connect(); failure is " \
|
|
56
|
+
"a state reflected here, never silent blankness",
|
|
57
|
+
states: {
|
|
58
|
+
"data-error" => "a frame fetch failed (error response, missing frame, or " \
|
|
59
|
+
"network error) - the controller stamps the error card; " \
|
|
60
|
+
"retry clears it"
|
|
61
|
+
}
|
|
62
|
+
part "deferred-error", "The retryable error card, stamped into the frame from the " \
|
|
63
|
+
"slotted <template> on failure"
|
|
64
|
+
|
|
65
|
+
# Enforces the required src.
|
|
66
|
+
# @api private
|
|
67
|
+
def before_render
|
|
68
|
+
raise ArgumentError, "poetry_deferred requires src:" if src.blank?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# No src in the markup ON PURPOSE: the URL rides the controller
|
|
72
|
+
# value and connect() arms it, so a fast response can never beat
|
|
73
|
+
# the controllers module graph to the frame (Turbo 8's
|
|
74
|
+
# frame-missing default would promote that race to a full-page
|
|
75
|
+
# visit).
|
|
76
|
+
# @api private
|
|
77
|
+
def root_attributes
|
|
78
|
+
html_attributes.merge_if_not_set(
|
|
79
|
+
{
|
|
80
|
+
"id" => "poetry-deferred-#{Digest::MD5.hexdigest(src.to_s).first(8)}",
|
|
81
|
+
"loading" => loading, "data-slot" => "deferred"
|
|
82
|
+
}.merge(stimulus_attributes_for(:root)).merge(component_data_attributes)
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private :root_attributes
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Deferred
|
|
6
|
+
# The Deferred preview. The dummy loads no Turbo, so the frame stays
|
|
7
|
+
# inert: what renders (and what the baseline captures) IS the
|
|
8
|
+
# placeholder state - deterministic by construction. Live loading /
|
|
9
|
+
# error / retry behavior is proven in poetry-core's deferred.test.js
|
|
10
|
+
# and on the docs site's /deferred page.
|
|
11
|
+
class Preview < Poetry::Core::Preview::Base
|
|
12
|
+
def default
|
|
13
|
+
render_component(src: "/previews/activity")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def custom_placeholder
|
|
17
|
+
render_component(src: "/previews/activity") do
|
|
18
|
+
"Fetching the latest activity…"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<%= content_tag(:div, **root_attributes.to_attributes) do %>
|
|
2
|
+
<%= trigger %>
|
|
3
|
+
<%= content_tag(:dialog, **Poetry::Core::HTML::Attributes.new(dialog_attributes).to_attributes) do %>
|
|
4
|
+
<div data-slot="dialog-header" class="<%= css(:header) %>">
|
|
5
|
+
<h2 id="<%= title_id %>" data-slot="dialog-title" class="<%= css(:title) %>"><%= title %></h2>
|
|
6
|
+
<% if description? %>
|
|
7
|
+
<p id="<%= description_id %>" data-slot="dialog-description" class="<%= css(:description) %>"><%= description %></p>
|
|
8
|
+
<% end %>
|
|
9
|
+
</div>
|
|
10
|
+
<%= content %>
|
|
11
|
+
<% if footer? %><div data-slot="dialog-footer" class="<%= css(:footer) %>"><%= footer %></div><% end %>
|
|
12
|
+
<% if show_close_button %>
|
|
13
|
+
<%= render Poetry::Ui::Button::Component.new(variant: :ghost, size: :"icon-sm",
|
|
14
|
+
label: t("poetry.dialog.close"),
|
|
15
|
+
class: css(:close),
|
|
16
|
+
data: { slot: "dialog-close", action: close_action }) do %>
|
|
17
|
+
<%= render Poetry::Ui::Icon::Component.new(name: :x) %>
|
|
18
|
+
<% end %>
|
|
19
|
+
<% end %>
|
|
20
|
+
<% end %>
|
|
21
|
+
<% end %>
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
# Dialog family: the modal overlay on the native <dialog> element.
|
|
6
|
+
module Dialog
|
|
7
|
+
# A modal dialog built ON the platform: a native <dialog> +
|
|
8
|
+
# showModal() owns focus trapping, Esc, top-layer stacking, and
|
|
9
|
+
# focus return; poetry adds the data-open/data-closed pair,
|
|
10
|
+
# backdrop dismissal, and the scroll lock. The title is REQUIRED
|
|
11
|
+
# (the accessible name - aria-labelledby is always wired).
|
|
12
|
+
#
|
|
13
|
+
# @example A confirmation dialog
|
|
14
|
+
# render Poetry::Ui::Dialog::Component.new do |dialog|
|
|
15
|
+
# dialog.with_trigger(variant: :outline) { "Open" }
|
|
16
|
+
# dialog.with_title { "Are you sure?" }
|
|
17
|
+
# dialog.with_description { "This cannot be undone." }
|
|
18
|
+
# end
|
|
19
|
+
class Component < Poetry::Core::Component
|
|
20
|
+
include Poetry::Ui::ComposableTrigger
|
|
21
|
+
include Poetry::Ui::FamilyIdentity
|
|
22
|
+
|
|
23
|
+
# Projected into the registry, llms.txt, and the agent surface.
|
|
24
|
+
AGENT_RULES = [
|
|
25
|
+
ComposableTrigger::AGENT_RULE,
|
|
26
|
+
"Open dialogs with with_trigger(...) - never a hand-wired button.",
|
|
27
|
+
"with_title is REQUIRED (the accessible name); with_description when the purpose needs explaining.",
|
|
28
|
+
"Confirmations that must not be lost use dismissible: false (backdrop clicks stop closing).",
|
|
29
|
+
"show_close_button: false removes the corner X - keep a footer action (Esc still closes).",
|
|
30
|
+
"Destructive confirmations pair a destructive Button in the footer - never auto-submit."
|
|
31
|
+
].freeze
|
|
32
|
+
|
|
33
|
+
# The required slots, stated statically so static checks can flag
|
|
34
|
+
# a missing title without rendering.
|
|
35
|
+
REQUIRED_SLOTS = { title: "the accessible name" }.freeze
|
|
36
|
+
|
|
37
|
+
# The forwarding-lambda fact: with_trigger renders a Button -
|
|
38
|
+
# callers get Button's full typed-slot contract statically.
|
|
39
|
+
SLOT_RENDERS = { trigger: Button::Component }.freeze
|
|
40
|
+
|
|
41
|
+
renders_one :trigger,
|
|
42
|
+
doc: "The trigger is a poetry Button wired to open the dialog - agents pass Button props: " \
|
|
43
|
+
"with_trigger(variant: :outline) { \"Open\" }.",
|
|
44
|
+
renders: lambda { |**options, &block|
|
|
45
|
+
composed_trigger({ "data-action" => stimulus_action(:open) }, options, &block) || begin
|
|
46
|
+
merger = Poetry::Core::Config.current.stimulus_merger
|
|
47
|
+
wired_data = { action: stimulus_action(:open) }
|
|
48
|
+
options[:data] = wired_data.merge(options[:data] || {}) do |key, wired, caller|
|
|
49
|
+
key == :action ? merger.merge_actions(wired, caller) : caller
|
|
50
|
+
end
|
|
51
|
+
Button::Component.new(**options, &block)
|
|
52
|
+
end
|
|
53
|
+
}
|
|
54
|
+
renders_one :title, doc: "The heading - the dialog's accessible name; required."
|
|
55
|
+
renders_one :description, doc: "Muted copy under the title, wired to aria-describedby."
|
|
56
|
+
renders_one :footer, doc: "The action row at the bottom of the panel."
|
|
57
|
+
|
|
58
|
+
# Sheet and Drawer subclass this and REDECLARE both elements with
|
|
59
|
+
# their own controllers (replace-on-redeclare); the trigger lambda
|
|
60
|
+
# and close_action late-bind through stimulus_action, so subclasses
|
|
61
|
+
# never re-type the entry points.
|
|
62
|
+
use_stimulus do
|
|
63
|
+
on :root do
|
|
64
|
+
controller :dialog do
|
|
65
|
+
register
|
|
66
|
+
value :dismissible
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
on :content do
|
|
70
|
+
controller :dialog do
|
|
71
|
+
target :dialog
|
|
72
|
+
action :close, on: :cancel
|
|
73
|
+
action :backdrop_close, on: :click
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
# Forwarded descriptors (bare = element-default click): the
|
|
77
|
+
# trigger Button and the corner close button.
|
|
78
|
+
on :trigger do
|
|
79
|
+
controller(:dialog) { action :open }
|
|
80
|
+
end
|
|
81
|
+
on :close do
|
|
82
|
+
controller(:dialog) { action :close }
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# The operate surface: open/close a rendered dialog (registered per
|
|
87
|
+
# instance, opt-in - see poetry-agent). Bare actions on purpose:
|
|
88
|
+
# Sheet and Drawer re-controller the root, and a bare `executes:`
|
|
89
|
+
# re-resolves to THEIR controller at projection time.
|
|
90
|
+
tool :open,
|
|
91
|
+
description: "Open the dialog.",
|
|
92
|
+
executes: :open,
|
|
93
|
+
mutating: true
|
|
94
|
+
tool :close,
|
|
95
|
+
description: "Close the dialog.",
|
|
96
|
+
executes: :close,
|
|
97
|
+
mutating: true
|
|
98
|
+
|
|
99
|
+
option :dismissible, :boolean, default: true,
|
|
100
|
+
doc: "Backdrop clicks close the dialog; false keeps confirmations from being " \
|
|
101
|
+
"dismissed accidentally (Esc still closes)."
|
|
102
|
+
|
|
103
|
+
option :show_close_button, :boolean, default: true,
|
|
104
|
+
doc: "Renders the corner X; false forces a deliberate footer choice " \
|
|
105
|
+
"(footer actions and Esc remain). Sheet inherits this."
|
|
106
|
+
option :content_class, :string,
|
|
107
|
+
doc: "Extra classes merged onto the <dialog> panel (e.g. \"max-h-[50vh]\" caps a top/bottom sheet)."
|
|
108
|
+
|
|
109
|
+
part "dialog", "Root wrapper around the trigger and the <dialog> element"
|
|
110
|
+
part "dialog-content", "The <dialog> panel - positioning, animation, and the open " \
|
|
111
|
+
"state ride here",
|
|
112
|
+
states: {
|
|
113
|
+
"data-open" => "panel is open (the controller flips the pair at runtime)",
|
|
114
|
+
"data-closed" => "panel is closed or animating out (the server-rendered state)"
|
|
115
|
+
}
|
|
116
|
+
part "dialog-header", "Title block at the top of the panel"
|
|
117
|
+
part "dialog-title", "The heading - the dialog's accessible name (required slot)"
|
|
118
|
+
part "dialog-description", "Muted copy under the title, wired to aria-describedby"
|
|
119
|
+
part "dialog-footer", "Action row at the bottom of the panel"
|
|
120
|
+
|
|
121
|
+
# Enforces the required title.
|
|
122
|
+
# @api private
|
|
123
|
+
def before_render
|
|
124
|
+
raise ArgumentError, "#{family_name} requires with_title (the accessible name)" unless title?
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# The title's id - the aria-labelledby target.
|
|
128
|
+
# @api private
|
|
129
|
+
def title_id
|
|
130
|
+
"#{instance_id}-title"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# The description's id - the aria-describedby target.
|
|
134
|
+
# @api private
|
|
135
|
+
def description_id
|
|
136
|
+
"#{instance_id}-description"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Attributes for the <dialog> panel.
|
|
140
|
+
# @api private
|
|
141
|
+
def dialog_attributes
|
|
142
|
+
attrs = {
|
|
143
|
+
"class" => css(:content, class: panel_classes),
|
|
144
|
+
"data-slot" => "#{family_slot_prefix}-content",
|
|
145
|
+
**panel_stamps,
|
|
146
|
+
"data-closed" => "",
|
|
147
|
+
"aria-labelledby" => title_id
|
|
148
|
+
}.merge(stimulus_attributes_for(:content))
|
|
149
|
+
attrs["aria-describedby"] = description_id if description?
|
|
150
|
+
attrs
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Validated action descriptor for the template's close button -
|
|
154
|
+
# resolves against the class's OWN declarations (Sheet/Drawer get
|
|
155
|
+
# their controller without overriding).
|
|
156
|
+
# @api private
|
|
157
|
+
def close_action
|
|
158
|
+
stimulus_action(:close)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# The dialog-family root wrapper is NON-VISUAL - the <dialog> is
|
|
162
|
+
# the visual root. The style resolver renders variant branches only
|
|
163
|
+
# at the dictionary root, so subclasses with a root-level style
|
|
164
|
+
# axis (Sheet's side:, Drawer's direction:) merge the branch into
|
|
165
|
+
# :content themselves - and the inherited html_attributes would
|
|
166
|
+
# paint the same branch on the in-flow wrapper too (the closed
|
|
167
|
+
# Drawer drew its themed border + w-full across the docs mounts).
|
|
168
|
+
# Only the tailwind resolution is stripped: in :bem mode the root
|
|
169
|
+
# block--modifier tokens ARE the host's styling contract.
|
|
170
|
+
# @api private
|
|
171
|
+
def html_attributes
|
|
172
|
+
return super unless Poetry::Core::Config.current.css_mode == :tailwind
|
|
173
|
+
|
|
174
|
+
@html_attributes.merge(class: classnames(@html_attributes[:class]))
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
private
|
|
178
|
+
|
|
179
|
+
# Subclass seams for the panel: extra class values and the family
|
|
180
|
+
# stamps between data-slot and data-closed (Sheet stamps its side,
|
|
181
|
+
# Drawer its swipe direction).
|
|
182
|
+
def panel_classes
|
|
183
|
+
content_class
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def panel_stamps
|
|
187
|
+
{}
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
private :title_id, :description_id, :dialog_attributes, :close_action, :html_attributes
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Dialog
|
|
6
|
+
class Preview < Poetry::Core::Preview::Base
|
|
7
|
+
def default
|
|
8
|
+
render_component do |dialog|
|
|
9
|
+
dialog.with_trigger(variant: :outline) { "Edit profile" }
|
|
10
|
+
dialog.with_title { "Edit profile" }
|
|
11
|
+
dialog.with_description { "Make changes and save when done." }
|
|
12
|
+
dialog.with_footer do
|
|
13
|
+
embed(Poetry::Ui::Button::Component.new.with_content("Save changes"))
|
|
14
|
+
end
|
|
15
|
+
"Profile form goes here."
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def confirmation
|
|
20
|
+
render_component(dismissible: false) do |dialog|
|
|
21
|
+
dialog.with_trigger(variant: :destructive) { "Delete account" }
|
|
22
|
+
dialog.with_title { "Are you absolutely sure?" }
|
|
23
|
+
dialog.with_description { "This permanently deletes your account and all data." }
|
|
24
|
+
dialog.with_footer do
|
|
25
|
+
embed(Poetry::Ui::Button::Component.new(variant: :destructive).with_content("Delete"))
|
|
26
|
+
end
|
|
27
|
+
""
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Dialog
|
|
6
|
+
# The root is a non-visual wrapper; the visual surface is the native
|
|
7
|
+
# <dialog> element (the :content element), centered by the top layer.
|
|
8
|
+
# Re-expressed through the cn-* theme layer: the panel chrome,
|
|
9
|
+
# backdrop tint, and enter animation ride themes/default.css; the
|
|
10
|
+
# native-dialog mechanisms stay inline.
|
|
11
|
+
class Style < Poetry::Core::Style
|
|
12
|
+
# open:grid, NOT grid: a bare display class would defeat the UA's
|
|
13
|
+
# dialog:not([open]) { display: none } and render the dialog inline
|
|
14
|
+
# while closed (invisible to jsdom, which has no UA stylesheet).
|
|
15
|
+
# No `relative`: a modal <dialog> lives in the top layer, where the
|
|
16
|
+
# browser drops a `position: relative` back to the UA `absolute` and
|
|
17
|
+
# pins the panel to the DOCUMENT origin (it then scrolls off-screen
|
|
18
|
+
# when opened below the fold). Letting position fall through to the
|
|
19
|
+
# UA `:modal` rule keeps it viewport-fixed and centered.
|
|
20
|
+
element :content, "cn-dialog-content m-auto open:grid"
|
|
21
|
+
|
|
22
|
+
element :header, "cn-dialog-header flex flex-col"
|
|
23
|
+
element :title, "cn-dialog-title"
|
|
24
|
+
element :description, "cn-dialog-description"
|
|
25
|
+
element :footer, "cn-dialog-footer flex flex-col-reverse gap-2 sm:flex-row sm:justify-end"
|
|
26
|
+
element :close, "cn-dialog-close"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<%= content_tag(:div, **root_attributes.to_attributes) do %>
|
|
2
|
+
<%= trigger %>
|
|
3
|
+
<%= content_tag(:dialog, **Poetry::Core::HTML::Attributes.new(dialog_attributes).to_attributes) do %>
|
|
4
|
+
<% if show_swipe_handle %>
|
|
5
|
+
<div data-slot="drawer-swipe-handle" aria-hidden="true" class="<%= css(:handle) %>"></div>
|
|
6
|
+
<% end %>
|
|
7
|
+
<div data-slot="drawer-header" class="<%= css(:header) %>">
|
|
8
|
+
<h2 id="<%= title_id %>" data-slot="drawer-title" class="<%= css(:title) %>"><%= title %></h2>
|
|
9
|
+
<% if description? %>
|
|
10
|
+
<p id="<%= description_id %>" data-slot="drawer-description" class="<%= css(:description) %>"><%= description %></p>
|
|
11
|
+
<% end %>
|
|
12
|
+
</div>
|
|
13
|
+
<div data-slot="drawer-body" class="<%= css(:body) %>"><%= content %></div>
|
|
14
|
+
<% if footer? %><div data-slot="drawer-footer" class="<%= css(:footer) %>"><%= footer %></div><% end %>
|
|
15
|
+
<% end %>
|
|
16
|
+
<% end %>
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
# Drawer family: the swipeable edge sheet on the dialog spine.
|
|
6
|
+
module Drawer
|
|
7
|
+
# A drawer: an edge-anchored dialog you can SWIPE away, most at
|
|
8
|
+
# home as the mobile bottom sheet. Everything hard is inherited
|
|
9
|
+
# from Dialog (the native <dialog> focus trap, required title,
|
|
10
|
+
# dismissible:); the drawer adds the swipe gesture, the
|
|
11
|
+
# edge-rounded popup with transition-driven enter/exit, the
|
|
12
|
+
# optional grab handle, and snap_points: preset resting heights
|
|
13
|
+
# for bottom sheets.
|
|
14
|
+
#
|
|
15
|
+
# Nested drawer stacking visuals and bleed are not supported.
|
|
16
|
+
#
|
|
17
|
+
# @example
|
|
18
|
+
# render Poetry::Ui::Drawer::Component.new(show_swipe_handle: true) do |drawer|
|
|
19
|
+
# drawer.with_trigger { "Open drawer" }
|
|
20
|
+
# drawer.with_title { "Move goal" }
|
|
21
|
+
# drawer.with_description { "Set your daily activity goal." }
|
|
22
|
+
# "Drawer body"
|
|
23
|
+
# end
|
|
24
|
+
class Component < Dialog::Component
|
|
25
|
+
# The closed vocabulary for the direction axis.
|
|
26
|
+
DIRECTIONS = %i[down up left right].freeze
|
|
27
|
+
|
|
28
|
+
# Accepted CSS length spellings for snap points (px or rem).
|
|
29
|
+
SNAP_POINT_LENGTH = /\A\d+(\.\d+)?(px|rem)\z/
|
|
30
|
+
|
|
31
|
+
# Projected into the registry, llms.txt, and the agent surface.
|
|
32
|
+
AGENT_RULES = [
|
|
33
|
+
"Open drawers with with_trigger(...) - never a hand-wired button.",
|
|
34
|
+
"with_title is REQUIRED (the accessible name) - the inherited Dialog rule.",
|
|
35
|
+
"direction: is the DISMISS direction: :down is the mobile bottom sheet (the default); " \
|
|
36
|
+
"left/right make an edge panel - prefer Sheet on desktop.",
|
|
37
|
+
"show_swipe_handle: true renders the grab pill - use it on bottom sheets so the " \
|
|
38
|
+
"gesture is discoverable.",
|
|
39
|
+
"Esc and the backdrop still dismiss (the platform trap) - the swipe is an addition, " \
|
|
40
|
+
"never the only way out.",
|
|
41
|
+
"modal: false keeps the page interactive (no scrim, no focus trap) - pair a wired " \
|
|
42
|
+
"footer close; Esc while focus is inside still exits.",
|
|
43
|
+
"snap_points: [\"31rem\", 1] snaps a bottom sheet between preset heights (ascending " \
|
|
44
|
+
"fractions or px/rem lengths; opens at the first) - direction: :down only."
|
|
45
|
+
].freeze
|
|
46
|
+
|
|
47
|
+
# Replace-on-redeclare: both elements re-controller to :drawer;
|
|
48
|
+
# the inherited trigger lambda late-binds through stimulus_action.
|
|
49
|
+
use_stimulus do
|
|
50
|
+
on :root do
|
|
51
|
+
controller :drawer do
|
|
52
|
+
register
|
|
53
|
+
value :dismissible
|
|
54
|
+
value :direction
|
|
55
|
+
value :modal
|
|
56
|
+
value :snap_points, from: :snap_points_json, if: -> { snap_points.present? }
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
on :content do
|
|
60
|
+
controller :drawer do
|
|
61
|
+
target :dialog
|
|
62
|
+
action :close, on: :cancel
|
|
63
|
+
action :backdrop_close, on: :click
|
|
64
|
+
# A non-modal dialog never fires cancel - Esc rides its own
|
|
65
|
+
# keydown exit (guarded controller-side to modal: false).
|
|
66
|
+
action :escape_close, on: :keydown, unless: :modal
|
|
67
|
+
action :swipe_start, on: :pointerdown
|
|
68
|
+
action :swipe_move, on: :pointermove
|
|
69
|
+
action :swipe_end, on: :pointerup
|
|
70
|
+
action :swipe_cancel, on: :pointercancel
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
on :trigger do
|
|
74
|
+
controller(:drawer) { action :open }
|
|
75
|
+
end
|
|
76
|
+
# The Drawer renders no corner close button - an empty
|
|
77
|
+
# redeclaration ERASES Dialog's inherited :close element (and
|
|
78
|
+
# with it the dialog identifier that would make unqualified
|
|
79
|
+
# stimulus_action ambiguous).
|
|
80
|
+
on :close
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
style :direction, default: :down, required: true, variants: DIRECTIONS,
|
|
84
|
+
doc: "The dismiss direction - :down is the mobile bottom sheet; the edge chrome and swipe " \
|
|
85
|
+
"axis derive from it."
|
|
86
|
+
|
|
87
|
+
option :show_swipe_handle, :boolean, default: false,
|
|
88
|
+
doc: "Renders the grab pill so the swipe gesture is discoverable."
|
|
89
|
+
|
|
90
|
+
option :modal, :boolean, default: true,
|
|
91
|
+
doc: "Non-modal (false) opens with show() - no top layer, no scrim, no focus trap, " \
|
|
92
|
+
"no scroll lock; the page behind stays interactive. Esc (while focus is " \
|
|
93
|
+
"inside), the swipe, and any wired close button still exit; there is no " \
|
|
94
|
+
"backdrop to click, so pointer dismissal is off by nature."
|
|
95
|
+
|
|
96
|
+
option :snap_points, ActiveModel::Type::Value.new,
|
|
97
|
+
doc: "Preset resting heights for a bottom sheet, ascending: fractions of the full height (0..1] or " \
|
|
98
|
+
"CSS px/rem lengths ([\"31rem\", 1]). The popup runs full-height and opens at the first point; " \
|
|
99
|
+
"drags move between points, below the first dismisses. direction: :down only."
|
|
100
|
+
|
|
101
|
+
part "drawer", "Root wrapper around the trigger and the <dialog> element"
|
|
102
|
+
part "drawer-content", "The <dialog> popup - the edge chrome, presence animation, and " \
|
|
103
|
+
"the swipe contract all ride here (::backdrop inherits the " \
|
|
104
|
+
"swipe vars, so the overlay fade rides along)",
|
|
105
|
+
states: {
|
|
106
|
+
"data-open" => "popup is open (the controller flips the pair at runtime)",
|
|
107
|
+
"data-closed" => "popup is closed or animating out (the server-rendered state)",
|
|
108
|
+
"data-swipe-direction" => { condition: "always - the dismiss direction",
|
|
109
|
+
values: DIRECTIONS.map(&:to_s) },
|
|
110
|
+
"data-swiping" => "a pointer drag is tracking (transitions go duration-0 - the " \
|
|
111
|
+
"drawer follows the finger)",
|
|
112
|
+
"data-snap-points" => "snap_points: present - the popup runs full-height and " \
|
|
113
|
+
"--drawer-snap-point-offset rests it at the current point",
|
|
114
|
+
"data-starting-style" => "the enter transition's first frame (the presence " \
|
|
115
|
+
"helper's two-frame trick)",
|
|
116
|
+
"data-ending-style" => "held through the exit transition before the native " \
|
|
117
|
+
"close()"
|
|
118
|
+
},
|
|
119
|
+
vars: {
|
|
120
|
+
"--drawer-swipe-movement-x" => "px dragged toward a left/right dismissal " \
|
|
121
|
+
"(controller-written during swipes)",
|
|
122
|
+
"--drawer-swipe-movement-y" => "px dragged toward an up/down dismissal " \
|
|
123
|
+
"(controller-written during swipes)",
|
|
124
|
+
"--drawer-swipe-progress" => "0..1 fraction of the dismiss travel (the backdrop " \
|
|
125
|
+
"fade rides it)",
|
|
126
|
+
"--drawer-swipe-strength" => "remaining-travel factor set on release - scales " \
|
|
127
|
+
"the exit duration so a mostly-swiped drawer " \
|
|
128
|
+
"closes fast"
|
|
129
|
+
}
|
|
130
|
+
part "drawer-swipe-handle", "The grab pill (show_swipe_handle: true, aria-hidden) - a " \
|
|
131
|
+
"drag may always start on it"
|
|
132
|
+
part "drawer-header", "Title block at the top of the popup"
|
|
133
|
+
part "drawer-title", "The heading - the drawer's accessible name (required slot)"
|
|
134
|
+
part "drawer-description", "Muted copy under the title, wired to aria-describedby"
|
|
135
|
+
part "drawer-body", "The scrollable content region between header and footer"
|
|
136
|
+
part "drawer-footer", "Action row pinned to the bottom of the popup"
|
|
137
|
+
|
|
138
|
+
# Enforces the inherited title contract plus snap-point validity.
|
|
139
|
+
# @api private
|
|
140
|
+
def before_render
|
|
141
|
+
super
|
|
142
|
+
validate_snap_points! if snap_points.present?
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# The parent's show_close_button does not apply: a Drawer has no
|
|
146
|
+
# corner X (it closes by swipe, backdrop, or footer actions), so
|
|
147
|
+
# the inherited option is hidden from introspection. A projected
|
|
148
|
+
# option the template ignores would be a contract lie.
|
|
149
|
+
# @api private
|
|
150
|
+
def self.option_attributes
|
|
151
|
+
super - %i[show_close_button]
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# The <dialog> IS the drawer popup: the chrome + swipe wiring land
|
|
155
|
+
# here. The swipe vars are written to this element by the controller
|
|
156
|
+
# and ::backdrop inherits them (the overlay fade rides along).
|
|
157
|
+
# @api private
|
|
158
|
+
def dialog_attributes
|
|
159
|
+
attrs = {
|
|
160
|
+
# Non-modal show() skips the top layer, so the UA :modal
|
|
161
|
+
# positioning (fixed + inset:0) must be re-stated explicitly -
|
|
162
|
+
# the direction margins and over-constraint sizing then work
|
|
163
|
+
# exactly as they do in the top layer.
|
|
164
|
+
"class" => css(:content, class: [Style.direction(direction),
|
|
165
|
+
(css(:nonmodal) unless modal)].compact.join(" ")),
|
|
166
|
+
"data-slot" => "drawer-content",
|
|
167
|
+
"data-swipe-direction" => direction,
|
|
168
|
+
"data-closed" => "",
|
|
169
|
+
"aria-labelledby" => title_id
|
|
170
|
+
}.merge(stimulus_attributes_for(:content))
|
|
171
|
+
# The attribute drives the dictionary's full-height sizing; the
|
|
172
|
+
# controller reads the value for the offset physics.
|
|
173
|
+
attrs["data-snap-points"] = "" if snap_points.present?
|
|
174
|
+
attrs["aria-describedby"] = description_id if description?
|
|
175
|
+
attrs
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
private
|
|
179
|
+
|
|
180
|
+
def validate_snap_points!
|
|
181
|
+
unless direction == :down
|
|
182
|
+
raise ArgumentError, "Drawer snap_points: is a bottom-sheet recipe - direction: :down only"
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
valid = snap_points.is_a?(Array) && snap_points.any? && snap_points.all? do |point|
|
|
186
|
+
(point.is_a?(Numeric) && point.positive? && point <= 1) ||
|
|
187
|
+
(point.is_a?(String) && point.match?(SNAP_POINT_LENGTH))
|
|
188
|
+
end
|
|
189
|
+
return if valid
|
|
190
|
+
|
|
191
|
+
raise ArgumentError, "Drawer snap_points: entries are fractions in (0, 1] or CSS " \
|
|
192
|
+
'px/rem lengths ("31rem", "400px"), ascending'
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Host-facing close descriptor (no template consumer) - the
|
|
196
|
+
# explicit click event preserved from the pre-declaration shape.
|
|
197
|
+
def close_action
|
|
198
|
+
stimulus_action(:close, on: :click)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def snap_points_json = snap_points.to_json
|
|
202
|
+
|
|
203
|
+
private :dialog_attributes
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|