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,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module DropdownMenu
|
|
6
|
+
# Re-expressed through the cn-* theme layer: panel/item chrome,
|
|
7
|
+
# focus treatments, and the animate chains ride themes/default.css;
|
|
8
|
+
# popper vars, overflow, and the poetry indicator-visibility switch
|
|
9
|
+
# stay inline. Icon glyph classes stay inline per upstream (TSX
|
|
10
|
+
# inlines them on the lucide elements).
|
|
11
|
+
class Style < Poetry::Core::Style
|
|
12
|
+
element :content, "cn-dropdown-menu-content cn-menu-translucent z-50 max-h-(--available-height) " \
|
|
13
|
+
"origin-(--transform-origin) overflow-x-hidden overflow-y-auto " \
|
|
14
|
+
"data-closed:overflow-hidden outline-none w-(--anchor-width)"
|
|
15
|
+
|
|
16
|
+
# group/dropdown-menu-item: bare marker, no CSS - the vega
|
|
17
|
+
# shortcut re-color (group-focus/dropdown-menu-item) keys on it.
|
|
18
|
+
element :item, "cn-dropdown-menu-item group/dropdown-menu-item relative flex cursor-default " \
|
|
19
|
+
"items-center outline-hidden select-none [&_svg]:pointer-events-none [&_svg]:shrink-0 " \
|
|
20
|
+
"data-disabled:opacity-50 data-disabled:pointer-events-none"
|
|
21
|
+
|
|
22
|
+
element :checkbox_item, "cn-dropdown-menu-checkbox-item relative flex cursor-default items-center " \
|
|
23
|
+
"outline-hidden select-none [&_svg]:pointer-events-none [&_svg]:shrink-0 " \
|
|
24
|
+
"data-disabled:opacity-50 data-disabled:pointer-events-none"
|
|
25
|
+
|
|
26
|
+
element :radio_item, "cn-dropdown-menu-radio-item relative flex cursor-default items-center outline-hidden " \
|
|
27
|
+
"select-none [&_svg]:pointer-events-none [&_svg]:shrink-0 data-disabled:opacity-50 " \
|
|
28
|
+
"data-disabled:pointer-events-none"
|
|
29
|
+
|
|
30
|
+
element :label, "cn-dropdown-menu-label"
|
|
31
|
+
|
|
32
|
+
element :separator, "cn-dropdown-menu-separator"
|
|
33
|
+
|
|
34
|
+
element :shortcut, "cn-dropdown-menu-shortcut"
|
|
35
|
+
|
|
36
|
+
element :sub_trigger, "cn-dropdown-menu-sub-trigger flex cursor-default items-center outline-hidden " \
|
|
37
|
+
"select-none [&_svg]:pointer-events-none [&_svg]:shrink-0 data-popup-open:bg-accent " \
|
|
38
|
+
"data-popup-open:text-accent-foreground"
|
|
39
|
+
|
|
40
|
+
element :sub_content, "cn-dropdown-menu-sub-content z-50 origin-(--transform-origin) overflow-hidden w-auto"
|
|
41
|
+
|
|
42
|
+
# The indicator wrapper span, named dropdown-menu-item-indicator
|
|
43
|
+
# (the self-identification rule).
|
|
44
|
+
element :item_indicator, "cn-dropdown-menu-item-indicator pointer-events-none absolute " \
|
|
45
|
+
"flex items-center justify-center"
|
|
46
|
+
|
|
47
|
+
# The indicator stays in the DOM (never unmounted while
|
|
48
|
+
# unchecked); the parent item's data-checked/data-unchecked pair
|
|
49
|
+
# drives visibility, so the controller's aria-checked/data-checked
|
|
50
|
+
# flip is the whole toggle.
|
|
51
|
+
element :item_indicator_state, "[[data-unchecked]>&]:hidden"
|
|
52
|
+
|
|
53
|
+
# The source's inline lucide icon classes, named per part.
|
|
54
|
+
element :indicator_check, "size-4"
|
|
55
|
+
element :indicator_circle, "size-2 fill-current"
|
|
56
|
+
element :sub_indicator, "ml-auto size-4"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%= content_tag(:div, **root_attributes.to_attributes) do %>
|
|
2
|
+
<% if header? %>
|
|
3
|
+
<div data-slot="empty-header" class="<%= css(:header) %>">
|
|
4
|
+
<% if media? %><div data-slot="empty-icon" data-variant="<%= media_variant %>" class="<%= media_classes %>"><%= media %></div><% end %>
|
|
5
|
+
<% if title? %><%= content_tag(title_tag, title, "data-slot" => "empty-title", class: css(:title)) %><% end %>
|
|
6
|
+
<% if description? %><div data-slot="empty-description" class="<%= css(:description) %>"><%= description %></div><% end %>
|
|
7
|
+
</div>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% if content.present? %><div data-slot="empty-content" class="<%= css(:content) %>"><%= content %></div><% end %>
|
|
10
|
+
<% end %>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
# The Empty family - the empty-state block for collections with nothing in them.
|
|
6
|
+
module Empty
|
|
7
|
+
# The Empty state - what a collection shows when there is nothing in
|
|
8
|
+
# it: an optional media glyph, a title, a description, and the actions
|
|
9
|
+
# that fix the emptiness (the content block). Slots compose the header;
|
|
10
|
+
# the content block becomes empty-content.
|
|
11
|
+
#
|
|
12
|
+
# @example Empty collection with a next action
|
|
13
|
+
# render Poetry::Ui::Empty::Component.new do |empty|
|
|
14
|
+
# empty.with_title { "No projects yet" }
|
|
15
|
+
# empty.with_description { "Create your first project to get started." }
|
|
16
|
+
# poetry_button { "New project" }
|
|
17
|
+
# end
|
|
18
|
+
class Component < Poetry::Core::Component
|
|
19
|
+
# Projected into the registry, llms.txt, and the agent surface.
|
|
20
|
+
AGENT_RULES = [
|
|
21
|
+
"An empty collection gets an Empty state with a next action - never a bare 'No results' div.",
|
|
22
|
+
"Compose with the slots (media/title/description); the actions are the content block.",
|
|
23
|
+
"The title renders as a real heading (h3 default) - set title_tag: to fit the page outline.",
|
|
24
|
+
"media_variant: :icon gives the rounded muted icon tile; wrap a poetry_icon in with_media."
|
|
25
|
+
].freeze
|
|
26
|
+
|
|
27
|
+
renders_one :media, doc: "Optional leading visual above the title - an icon or illustration."
|
|
28
|
+
renders_one :title, doc: "The headline, rendered as a real heading (see title_tag)."
|
|
29
|
+
renders_one :description, doc: "Muted copy under the title."
|
|
30
|
+
|
|
31
|
+
option :title_tag, :symbol, default: :h3,
|
|
32
|
+
doc: "The title's heading level - a real heading element, so set it to fit the " \
|
|
33
|
+
"page outline."
|
|
34
|
+
option :media_variant, :symbol, default: :default,
|
|
35
|
+
doc: "The media slot's treatment; :icon gives the rounded muted icon tile."
|
|
36
|
+
|
|
37
|
+
validates :title_tag, inclusion: { in: %i[h1 h2 h3 h4 h5 h6] }
|
|
38
|
+
validates :media_variant, inclusion: { in: %i[default icon] }
|
|
39
|
+
|
|
40
|
+
part "empty", "The empty-state root - the centered header and content stack"
|
|
41
|
+
part "empty-header", "Wrapper around media/title/description - renders when at " \
|
|
42
|
+
"least one of those slots is set"
|
|
43
|
+
part "empty-icon", "The media slot's box - media_variant: :icon gives the rounded " \
|
|
44
|
+
"muted icon tile",
|
|
45
|
+
states: {
|
|
46
|
+
"data-variant" => { condition: "always - the resolved media_variant",
|
|
47
|
+
values: %w[default icon] }
|
|
48
|
+
}
|
|
49
|
+
part "empty-title", "The title as a real heading (title_tag, h3 by default)"
|
|
50
|
+
part "empty-description", "Muted copy under the title"
|
|
51
|
+
part "empty-content", "The actions that fix the emptiness - the content block " \
|
|
52
|
+
"renders here"
|
|
53
|
+
|
|
54
|
+
# Whether the header wrapper renders (any of media/title/description set).
|
|
55
|
+
# @api private
|
|
56
|
+
def header?
|
|
57
|
+
media? || title? || description?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# The media box's classes for the resolved media_variant.
|
|
61
|
+
# @api private
|
|
62
|
+
def media_classes
|
|
63
|
+
variant = media_variant == :icon ? :media_icon : :media_default
|
|
64
|
+
"#{css(:media)} #{css(variant)}"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# The empty-state root's attributes.
|
|
68
|
+
# @api private
|
|
69
|
+
def root_attributes
|
|
70
|
+
html_attributes.merge_if_not_set(
|
|
71
|
+
{ "data-slot" => "empty" }.merge(component_data_attributes)
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private :header?, :media_classes, :root_attributes
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Empty
|
|
6
|
+
# The Empty preview: the basic no-data state and the icon-tile
|
|
7
|
+
# variant with actions.
|
|
8
|
+
class Preview < Poetry::Core::Preview::Base
|
|
9
|
+
def default
|
|
10
|
+
render_component do |empty|
|
|
11
|
+
empty.with_title { "No projects yet" }
|
|
12
|
+
empty.with_description { "Get started by creating your first project." }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def with_icon_and_actions
|
|
17
|
+
render_component(media_variant: :icon) do |empty|
|
|
18
|
+
empty.with_media { embed(Poetry::Ui::Icon::Component.new(name: :folder)) }
|
|
19
|
+
empty.with_title { "No projects yet" }
|
|
20
|
+
empty.with_description { "Get started by creating your first project." }
|
|
21
|
+
embed(Poetry::Ui::Button::Component.new(size: :sm).with_content("Create project"))
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Empty
|
|
6
|
+
# Re-expressed through the cn-* theme layer. The title wears
|
|
7
|
+
# cn-font-heading (upstream parity): a no-op until a theme
|
|
8
|
+
# defines --font-heading - the sanctioned heading-font hook.
|
|
9
|
+
class Style < Poetry::Core::Style
|
|
10
|
+
base "cn-empty flex w-full min-w-0 flex-1 flex-col items-center justify-center " \
|
|
11
|
+
"text-center text-balance"
|
|
12
|
+
|
|
13
|
+
element :header, "cn-empty-header flex max-w-sm flex-col items-center"
|
|
14
|
+
# The media wrapper splits shared chrome from its two variants
|
|
15
|
+
# (default: transparent; icon: the rounded muted tile).
|
|
16
|
+
element :media, "cn-empty-media flex shrink-0 items-center justify-center " \
|
|
17
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0"
|
|
18
|
+
element :media_default, "cn-empty-media-default"
|
|
19
|
+
element :media_icon, "cn-empty-media-icon"
|
|
20
|
+
element :title, "cn-empty-title cn-font-heading"
|
|
21
|
+
element :description, "cn-empty-description text-muted-foreground [&>a]:underline " \
|
|
22
|
+
"[&>a]:underline-offset-4 [&>a:hover]:text-primary"
|
|
23
|
+
element :content, "cn-empty-content flex w-full max-w-sm min-w-0 flex-col items-center text-balance"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
# Gives an including component its family identity, derived from the
|
|
6
|
+
# namespace (Poetry::Ui::HoverCard::Component -> "HoverCard" /
|
|
7
|
+
# "hover-card"): the data-slot prefix, error nouns, and the sibling
|
|
8
|
+
# Style dictionary all resolve through it, so shared modules never
|
|
9
|
+
# hard-code a family name. Also supplies the root-shell attributes
|
|
10
|
+
# and the instance-id seed; a family with a different root or id
|
|
11
|
+
# shape simply overrides.
|
|
12
|
+
module FamilyIdentity
|
|
13
|
+
# The family root's data-slot, Stimulus, and self-identification
|
|
14
|
+
# attributes, merged under any caller-passed HTML attributes.
|
|
15
|
+
# @api private
|
|
16
|
+
def root_attributes
|
|
17
|
+
html_attributes.merge_if_not_set(
|
|
18
|
+
{ "data-slot" => family_slot_prefix }
|
|
19
|
+
.merge(stimulus_attributes_for(:root))
|
|
20
|
+
.merge(component_data_attributes)
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def family_namespace
|
|
27
|
+
self.class.module_parent
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def family_name
|
|
31
|
+
@family_name ||= family_namespace.name.demodulize
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def family_slot_prefix
|
|
35
|
+
@family_slot_prefix ||= family_name.underscore.dasherize
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def family_style
|
|
39
|
+
family_namespace::Style
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Server-stable unique id seed (two of a family on one page must
|
|
43
|
+
# not share ids); portal-safe by convention - controllers resolve
|
|
44
|
+
# content via the id pair, never a Stimulus target.
|
|
45
|
+
def instance_id
|
|
46
|
+
@instance_id ||= poetry_instance_id("poetry-#{family_slot_prefix}")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private :root_attributes
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<%= content_tag(:div, **root_attributes.to_attributes) do %>
|
|
2
|
+
<% if label_text.present? %>
|
|
3
|
+
<% if group %>
|
|
4
|
+
<%= render Poetry::Ui::Label::Component.new(for_id: nil, id: label_id, class: css(:label),
|
|
5
|
+
data: { slot: "field-label" }).with_content(label_text) %>
|
|
6
|
+
<% else %>
|
|
7
|
+
<%= render Poetry::Ui::Label::Component.new(for_id: id, class: css(:label),
|
|
8
|
+
data: { slot: "field-label" }).with_content(label_text) %>
|
|
9
|
+
<% end %>
|
|
10
|
+
<% end %>
|
|
11
|
+
<% hint_body = hint_block ? capture(&hint_block) : hint %>
|
|
12
|
+
<% hint_tag = capture do %>
|
|
13
|
+
<% if hint_present? %><p id="<%= hint_id %>" data-slot="field-description" class="<%= css(:hint) %>"><%= hint_body %></p><% end %>
|
|
14
|
+
<% end %>
|
|
15
|
+
<%= hint_tag if hint_position == :above %>
|
|
16
|
+
<%= content %>
|
|
17
|
+
<%= hint_tag if hint_position == :below %>
|
|
18
|
+
<% if error.present? %><p id="<%= error_id %>" data-slot="field-error" class="<%= css(:error) %>"><%= error %></p><% end %>
|
|
19
|
+
<% end %>
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
# The Field family - the label/control/hint/error wrapper for one form control.
|
|
6
|
+
module Field
|
|
7
|
+
# The Field wrapper - label, control, hint, and error as one unit
|
|
8
|
+
# with the aria wiring done for you: the control (the content
|
|
9
|
+
# block) receives its id from the field, and control_attributes
|
|
10
|
+
# carries aria-invalid + aria-describedby pointing at the hint and
|
|
11
|
+
# error ids. The FormBuilder composes this from model truth; Field
|
|
12
|
+
# itself is model-agnostic.
|
|
13
|
+
#
|
|
14
|
+
# @example
|
|
15
|
+
# render Poetry::Ui::Field::Component.new(
|
|
16
|
+
# id: "email", label_text: "Email", hint: "We never share it."
|
|
17
|
+
# ) do |field|
|
|
18
|
+
# tag.input(type: "email", name: "email", **field.control_attributes)
|
|
19
|
+
# end
|
|
20
|
+
class Component < Poetry::Core::Component
|
|
21
|
+
# The closed vocabulary for the orientation axis.
|
|
22
|
+
ORIENTATIONS = %i[vertical horizontal setting responsive].freeze
|
|
23
|
+
|
|
24
|
+
# The closed vocabulary for the hint_position axis.
|
|
25
|
+
HINT_POSITIONS = %i[below above].freeze
|
|
26
|
+
|
|
27
|
+
# Projected into the registry, llms.txt, and the agent surface.
|
|
28
|
+
AGENT_RULES = [
|
|
29
|
+
"Wire the control with field.control_attributes - never hand-write aria-describedby.",
|
|
30
|
+
"Error text arrives via error: (from model errors upstream) - never a bare red <p>.",
|
|
31
|
+
"hint: (escaped string) for pure data; with_hint { } for authored markup (links) - " \
|
|
32
|
+
"call it BEFORE the control so the hint id lands in aria-describedby.",
|
|
33
|
+
"orientation: :horizontal is the boolean-control layout (checkbox/switch left, " \
|
|
34
|
+
"label + hint stacked right) - text inputs and groups stay vertical.",
|
|
35
|
+
"orientation: :responsive stacks by default and flips label-left / control-right " \
|
|
36
|
+
"once its poetry_field_group container passes the md mark - the settings-page " \
|
|
37
|
+
"recipe (it needs that FieldGroup ancestor to measure against)."
|
|
38
|
+
].freeze
|
|
39
|
+
|
|
40
|
+
style :orientation, default: :vertical, required: true, variants: ORIENTATIONS,
|
|
41
|
+
doc: "The layout axis. :horizontal is the boolean-control pattern: the control lands in " \
|
|
42
|
+
"the first grid column, label + hint/error stack in the second, and the control " \
|
|
43
|
+
"row-centers against the label line."
|
|
44
|
+
|
|
45
|
+
option :id, :string, required: true, doc: "The control's DOM id - the hint/error/label ids derive from it."
|
|
46
|
+
option :label_text, :string, doc: "The visible label text, associated with the control via for=."
|
|
47
|
+
option :hint, :string,
|
|
48
|
+
doc: "Plain-text guidance under the control (escaped wholesale); use with_hint for authored markup."
|
|
49
|
+
option :hint_position, :symbol, default: :below,
|
|
50
|
+
doc: "Where the hint renders relative to the control - :above puts guidance " \
|
|
51
|
+
"before a tall control. aria-describedby is identical either way; this " \
|
|
52
|
+
"is visual order only."
|
|
53
|
+
option :error, :string,
|
|
54
|
+
doc: "The error line (typically from model errors) - presence flips the invalid skin and leads the " \
|
|
55
|
+
"control's aria-describedby."
|
|
56
|
+
option :invalid, :boolean, default: false,
|
|
57
|
+
doc: "Flips the invalid skin (data-invalid + aria-invalid) WITHOUT an error line. " \
|
|
58
|
+
"error: implies it; use invalid: alone when the hint copy IS the " \
|
|
59
|
+
"requirement."
|
|
60
|
+
option :required, :boolean, default: false,
|
|
61
|
+
doc: "Marks the control required via aria-required only - never the native " \
|
|
62
|
+
"required attribute."
|
|
63
|
+
option :group, :boolean, default: false,
|
|
64
|
+
doc: "group: the control is a role-bearing <div> (RadioGroup, Slider) - label[for] " \
|
|
65
|
+
"would be inert (Chrome flags it), so the label drops for=, carries label_id, " \
|
|
66
|
+
"and control_attributes names the group via aria-labelledby (the visible " \
|
|
67
|
+
"label, i18n-proof)."
|
|
68
|
+
|
|
69
|
+
part "field", "The quartet's grid root - label, control, hint, and error stack inside",
|
|
70
|
+
states: {
|
|
71
|
+
"data-invalid" => { condition: "always - true when error: is present " \
|
|
72
|
+
"or invalid: is set, else false",
|
|
73
|
+
values: %w[true false] },
|
|
74
|
+
"data-orientation" => { condition: "always - the resolved orientation " \
|
|
75
|
+
"(horizontal is the boolean-control layout)",
|
|
76
|
+
values: ORIENTATIONS.map(&:to_s) }
|
|
77
|
+
}
|
|
78
|
+
part "field-label", "The Label (composed) wearing the source's field-label slot - names the control"
|
|
79
|
+
part "field-description", "The hint <p> (the source's description) - its id lands in the control's " \
|
|
80
|
+
"aria-describedby"
|
|
81
|
+
part "field-error", "The error <p> - present only when error: is set; its id leads " \
|
|
82
|
+
"the control's aria-describedby"
|
|
83
|
+
part "checkbox-input", "A nested Checkbox's hidden native input - the toggle renders " \
|
|
84
|
+
"as a wrapper-free fragment, so its sibling form store sits " \
|
|
85
|
+
"directly in the field's DOM (the horizontal boolean-control " \
|
|
86
|
+
"layout)"
|
|
87
|
+
part "switch-input", "A nested Switch's hidden native input - the same wrapper-free " \
|
|
88
|
+
"fragment escape as checkbox-input (the setting-row layout)"
|
|
89
|
+
|
|
90
|
+
# Validates hint_position and forces the content capture.
|
|
91
|
+
# @api private
|
|
92
|
+
def before_render
|
|
93
|
+
# Force the content block first: with_hint registers during the
|
|
94
|
+
# capture, and the template's hint tag must see it.
|
|
95
|
+
content
|
|
96
|
+
|
|
97
|
+
return if HINT_POSITIONS.include?(hint_position)
|
|
98
|
+
|
|
99
|
+
raise ArgumentError, "Field hint_position: #{hint_position.inspect} must be one of " \
|
|
100
|
+
"#{HINT_POSITIONS.inspect}"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# The hint element's id (referenced from aria-describedby).
|
|
104
|
+
# @api private
|
|
105
|
+
def hint_id = "#{id}-hint"
|
|
106
|
+
# The error element's id (leads aria-describedby).
|
|
107
|
+
# @api private
|
|
108
|
+
def error_id = "#{id}-error"
|
|
109
|
+
# The label element's id (referenced from aria-labelledby when group:).
|
|
110
|
+
# @api private
|
|
111
|
+
def label_id = "#{id}-label"
|
|
112
|
+
|
|
113
|
+
# Block-form hint for authored markup - a link or emphasis inside
|
|
114
|
+
# the guidance. Call it BEFORE the control renders, so the hint id
|
|
115
|
+
# lands in the control's aria-describedby; conflicts with hint: -
|
|
116
|
+
# use one or the other. The captured buffer renders as-is and is
|
|
117
|
+
# never re-blessed: ERB-authored markup stays markup, every
|
|
118
|
+
# interpolated value escapes normally, and a plain-String return
|
|
119
|
+
# is escaped by capture. Untrusted data belongs in hint: (escaped
|
|
120
|
+
# wholesale) or inside <%= %> in the block - never pre-marked
|
|
121
|
+
# html_safe.
|
|
122
|
+
#
|
|
123
|
+
# @example A hint containing a link
|
|
124
|
+
# <% field.with_hint do %>
|
|
125
|
+
# Forgot it? <%= link_to "Reset your password", reset_path %>
|
|
126
|
+
# <% end %>
|
|
127
|
+
def with_hint(&block)
|
|
128
|
+
raise ArgumentError, "Field with_hint conflicts with hint: - use one or the other" if hint.present?
|
|
129
|
+
if @control_attributes_issued
|
|
130
|
+
raise ArgumentError, "Field with_hint must be called before the control renders - " \
|
|
131
|
+
"control_attributes already handed out aria-describedby " \
|
|
132
|
+
"without the hint id"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
@hint_block = block
|
|
136
|
+
self
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# The captured hint block, consumed by the template.
|
|
140
|
+
# @api private
|
|
141
|
+
attr_reader :hint_block
|
|
142
|
+
|
|
143
|
+
# Whether any hint (string or block form) is present.
|
|
144
|
+
# @api private
|
|
145
|
+
def hint_present? = hint.present? || !@hint_block.nil?
|
|
146
|
+
|
|
147
|
+
# Whether the field wears the invalid skin (error: or invalid:).
|
|
148
|
+
# @api private
|
|
149
|
+
def invalid? = invalid || error.present?
|
|
150
|
+
|
|
151
|
+
# Everything the control inside the field must carry - the id,
|
|
152
|
+
# aria-describedby (error id first, then hint id), aria-invalid,
|
|
153
|
+
# aria-required, and aria-labelledby when group:. Merge it into
|
|
154
|
+
# the control's attributes (the FormBuilder does this for you).
|
|
155
|
+
def control_attributes
|
|
156
|
+
@control_attributes_issued = true
|
|
157
|
+
attrs = { "id" => id }
|
|
158
|
+
attrs["aria-labelledby"] = label_id if group && label_text.present?
|
|
159
|
+
describedby = []
|
|
160
|
+
describedby << error_id if error.present?
|
|
161
|
+
describedby << hint_id if hint_present?
|
|
162
|
+
attrs["aria-describedby"] = describedby.join(" ") if describedby.any?
|
|
163
|
+
attrs["aria-invalid"] = true if invalid?
|
|
164
|
+
# The aria-required-only rule: never the native required
|
|
165
|
+
# attribute - no native bubbles, no double announcement.
|
|
166
|
+
attrs["aria-required"] = true if required
|
|
167
|
+
attrs
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# The quartet root's attributes.
|
|
171
|
+
# @api private
|
|
172
|
+
def root_attributes
|
|
173
|
+
html_attributes.merge_if_not_set(
|
|
174
|
+
{ "data-slot" => "field", "data-invalid" => invalid?,
|
|
175
|
+
"data-orientation" => orientation }.merge(component_data_attributes)
|
|
176
|
+
)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
private :hint_present?, :root_attributes
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<%# Shared template for every Field preview example: render the field with
|
|
2
|
+
a real Input wired through control_attributes - the canonical host
|
|
3
|
+
composition (see eval/arms/form_field/poetry.html.erb). group: wraps
|
|
4
|
+
the field in the FieldGroup @container scope responsive fields key on. %>
|
|
5
|
+
<% field_markup = capture do %>
|
|
6
|
+
<%= render component do |field| %>
|
|
7
|
+
<% if local_assigns[:hint_link] %>
|
|
8
|
+
<% field.with_hint do %>Short and lowercase - see the <a class="underline underline-offset-4" href="/docs/naming">naming guide</a>.<% end %>
|
|
9
|
+
<% end %>
|
|
10
|
+
<% if local_assigns[:checkbox_options] %>
|
|
11
|
+
<%= render Poetry::Ui::Checkbox::Component.new(**checkbox_options,
|
|
12
|
+
**field.control_attributes.transform_keys(&:to_sym)) %>
|
|
13
|
+
<% elsif local_assigns[:switch_options] %>
|
|
14
|
+
<%= render Poetry::Ui::Switch::Component.new(**switch_options,
|
|
15
|
+
**field.control_attributes.transform_keys(&:to_sym)) %>
|
|
16
|
+
<% else %>
|
|
17
|
+
<%= render Poetry::Ui::Input::Component.new(**input_options,
|
|
18
|
+
**field.control_attributes.transform_keys(&:to_sym)) %>
|
|
19
|
+
<% end %>
|
|
20
|
+
<% end %>
|
|
21
|
+
<% end %>
|
|
22
|
+
<div class="p-4">
|
|
23
|
+
<% if local_assigns[:group] %>
|
|
24
|
+
<%= render Poetry::Ui::FieldGroup::Component.new do %>
|
|
25
|
+
<%= field_markup %>
|
|
26
|
+
<% end %>
|
|
27
|
+
<% else %>
|
|
28
|
+
<%= field_markup %>
|
|
29
|
+
<% end %>
|
|
30
|
+
</div>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Field
|
|
6
|
+
# The Field preview matrix - the label/control/hint/error quartet with
|
|
7
|
+
# the control wired through control_attributes (the aria plumbing the
|
|
8
|
+
# component exists to own). Rendering rides the sidecar preview.html.erb:
|
|
9
|
+
# nesting a control inside the field needs a real view context, and
|
|
10
|
+
# Preview#render inside a content block is the render_args DSL, not
|
|
11
|
+
# ActionView's render.
|
|
12
|
+
class Preview < Poetry::Core::Preview::Base
|
|
13
|
+
def default
|
|
14
|
+
render_with(component: Component.new(id: "field-email", label_text: "Email"),
|
|
15
|
+
input_options: { type: "email", name: "email" })
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def with_hint
|
|
19
|
+
render_with(component: Component.new(id: "field-handle", label_text: "Handle",
|
|
20
|
+
hint: "Public, letters and dashes only."),
|
|
21
|
+
input_options: { name: "handle" })
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def with_error
|
|
25
|
+
render_with(component: Component.new(id: "field-work-email", label_text: "Work email",
|
|
26
|
+
hint: "We never share it.", error: "can't be blank",
|
|
27
|
+
required: true),
|
|
28
|
+
input_options: { type: "email", name: "work_email" })
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# The block-form hint: ERB-authored markup (the link) rides the
|
|
32
|
+
# same cn-field-description skin and aria-describedby wiring as
|
|
33
|
+
# the hint: option.
|
|
34
|
+
def with_hint_link
|
|
35
|
+
render_with(component: Component.new(id: "field-workspace", label_text: "Workspace"),
|
|
36
|
+
input_options: { name: "workspace" }, hint_link: true)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# The SETTING ROW: label + hint stacked left, control right and
|
|
40
|
+
# centered on the label line (upstream's content-first horizontal
|
|
41
|
+
# Field - the switch-description pattern).
|
|
42
|
+
def setting_with_switch
|
|
43
|
+
render_with(component: Component.new(id: "field-share", label_text: "Share across devices",
|
|
44
|
+
hint: "Focus is shared across devices, and turns " \
|
|
45
|
+
"off when you leave the app.",
|
|
46
|
+
orientation: :setting),
|
|
47
|
+
switch_options: { name: "share" })
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The container-driven layout: stacked by default, label-left /
|
|
51
|
+
# control-right once the FieldGroup container passes the md mark
|
|
52
|
+
# (the sidecar wraps this one in the FieldGroup @container scope).
|
|
53
|
+
def responsive
|
|
54
|
+
render_with(component: Component.new(id: "field-display-name", label_text: "Display name",
|
|
55
|
+
hint: "Shown on your public profile.",
|
|
56
|
+
orientation: :responsive),
|
|
57
|
+
input_options: { name: "display_name" }, group: true)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# The boolean-control layout: control left, label + hint stacked
|
|
61
|
+
# right, box centered on the label line (the checkbox/switch field
|
|
62
|
+
# pattern upstream demos with orientation=horizontal).
|
|
63
|
+
def horizontal_with_checkbox
|
|
64
|
+
render_with(component: Component.new(id: "field-newsletter", label_text: "Email newsletter",
|
|
65
|
+
hint: "Sent weekly. Unsubscribe anytime.",
|
|
66
|
+
orientation: :horizontal),
|
|
67
|
+
checkbox_options: { name: "newsletter", checked: true })
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Field
|
|
6
|
+
# Re-expressed through the cn-* theme layer. Poetry's Field is
|
|
7
|
+
# its own small shape (grid + hint + error); the cn names borrow
|
|
8
|
+
# upstream's vocabulary where semantics align (:hint wears
|
|
9
|
+
# cn-field-description - the style hook tracks upstream naming, the
|
|
10
|
+
# data-slot stays poetry's).
|
|
11
|
+
class Style < Poetry::Core::Style
|
|
12
|
+
base "cn-field grid group/field w-full"
|
|
13
|
+
|
|
14
|
+
# The grid re-flow is structural and rides the dictionary like
|
|
15
|
+
# upstream's cva (control auto-places into column 1, label pins to
|
|
16
|
+
# column 2 row 1, hint/error stack under it - DOM stays label-first
|
|
17
|
+
# for the for= association); the COLUMN GAP is design and lives
|
|
18
|
+
# theme-side under the cn name. Vertical is the base state and
|
|
19
|
+
# emits nothing (the marker/core-X empty-variant precedent).
|
|
20
|
+
variant :orientation, {
|
|
21
|
+
vertical: "",
|
|
22
|
+
horizontal: "cn-field-orientation-horizontal grid-cols-[auto_1fr] items-center " \
|
|
23
|
+
"[&>[data-slot=field-label]]:col-start-2 [&>[data-slot=field-label]]:row-start-1 " \
|
|
24
|
+
"[&>[data-slot=field-description]]:col-start-2 [&>[data-slot=field-error]]:col-start-2",
|
|
25
|
+
# The SETTING ROW (label + hint left, control right - upstream's
|
|
26
|
+
# content-first horizontal Field): the horizontal treatment
|
|
27
|
+
# mirrored, control auto-places beside the label line.
|
|
28
|
+
setting: "cn-field-orientation-horizontal grid-cols-[1fr_auto] items-center " \
|
|
29
|
+
"[&>[data-slot=field-label]]:col-start-1 [&>[data-slot=field-label]]:row-start-1 " \
|
|
30
|
+
"[&>[data-slot=field-description]]:col-start-1 [&>[data-slot=field-error]]:col-start-1",
|
|
31
|
+
# Horizontal mirrored and container-gated: stacked below the
|
|
32
|
+
# FieldGroup's md mark; above it the label + hint pair stacks
|
|
33
|
+
# tight in column 1 (the theme narrows the row gap to its
|
|
34
|
+
# cn-field-content value via the marker class) and the control
|
|
35
|
+
# spans both rows in column 2, centered against the whole pair
|
|
36
|
+
# - upstream's FieldContent geometry, expressed on poetry's
|
|
37
|
+
# flat quartet DOM. Outside a @container/field-group scope the
|
|
38
|
+
# query never fires and the field stays vertical (upstream
|
|
39
|
+
# parity: "container-aware parents").
|
|
40
|
+
# rubocop:disable Layout/LineLength -- each line is one indivisible
|
|
41
|
+
# utility token, and Tailwind scans this literal source (an
|
|
42
|
+
# interpolated shared prefix would silently drop the CSS).
|
|
43
|
+
responsive: "cn-field-orientation-responsive " \
|
|
44
|
+
"@md/field-group:grid-cols-[1fr_auto] " \
|
|
45
|
+
"@md/field-group:[&>[data-slot=field-label]]:col-start-1 " \
|
|
46
|
+
"@md/field-group:[&>[data-slot=field-label]]:row-start-1 " \
|
|
47
|
+
"@md/field-group:[&>[data-slot=field-label]]:self-center " \
|
|
48
|
+
"@md/field-group:[&>[data-slot=field-description]]:col-start-1 " \
|
|
49
|
+
"@md/field-group:[&>[data-slot=field-error]]:col-start-1 " \
|
|
50
|
+
"@md/field-group:[&>:not([data-slot=field-label],[data-slot=field-description],[data-slot=field-error])]:col-start-2 " \
|
|
51
|
+
"@md/field-group:[&>:not([data-slot=field-label],[data-slot=field-description],[data-slot=field-error])]:row-start-1 " \
|
|
52
|
+
"@md/field-group:[&>:not([data-slot=field-label],[data-slot=field-description],[data-slot=field-error])]:row-span-2 " \
|
|
53
|
+
"@md/field-group:[&>:not([data-slot=field-label],[data-slot=field-description],[data-slot=field-error])]:self-center"
|
|
54
|
+
# rubocop:enable Layout/LineLength
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# The Label wears the source's field-label slot and its structural
|
|
58
|
+
# tokens on top of its own (a composed Label, the outer's part).
|
|
59
|
+
element :label, "cn-field-label group/field-label peer/field-label flex w-fit items-center " \
|
|
60
|
+
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col"
|
|
61
|
+
element :hint, "cn-field-description [&>a:hover]:text-primary [&>a]:underline " \
|
|
62
|
+
"[&>a]:underline-offset-4 font-normal group-has-data-horizontal/field:text-balance " \
|
|
63
|
+
"last:mt-0 leading-normal nth-last-2:-mt-1"
|
|
64
|
+
element :error, "cn-field-error font-normal"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|