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,396 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
# A one-question-at-a-time survey form.
|
|
6
|
+
module Questionnaire
|
|
7
|
+
# A one-question-at-a-time survey. Reach for it when a flow asks
|
|
8
|
+
# several questions in sequence and should submit as one form. The
|
|
9
|
+
# root IS a real form (form_with), items are fieldsets with legend
|
|
10
|
+
# titles, choices are native radio/checkbox inputs, and the free-text
|
|
11
|
+
# answer is a real input - the whole thing serializes as ordinary
|
|
12
|
+
# Rails params with zero JS. The server renders the complete initial
|
|
13
|
+
# state (active item, statuses, shortcut labels, button visibility);
|
|
14
|
+
# the controller owns the runtime transitions.
|
|
15
|
+
#
|
|
16
|
+
# Multiple-selection items name their checkboxes "<name>[]" so
|
|
17
|
+
# params arrive as arrays.
|
|
18
|
+
#
|
|
19
|
+
# @example
|
|
20
|
+
# render Poetry::Ui::Questionnaire::Component.new(url: "/surveys") do |survey|
|
|
21
|
+
# survey.with_item(name: "mood", title: "How was your week?") do |item|
|
|
22
|
+
# item.with_choice(value: "good", label: "Good")
|
|
23
|
+
# item.with_choice(value: "bad", label: "Bad")
|
|
24
|
+
# end
|
|
25
|
+
# end
|
|
26
|
+
class Component < Poetry::Core::Component
|
|
27
|
+
# The closed vocabulary for the shortcuts axis.
|
|
28
|
+
SHORTCUT_MODES = %i[letters numbers].freeze
|
|
29
|
+
# The key labels each shortcut mode assigns, in document order.
|
|
30
|
+
SHORTCUT_KEYS = {
|
|
31
|
+
letters: ("A".."Z").to_a,
|
|
32
|
+
numbers: ("1".."9").to_a
|
|
33
|
+
}.freeze
|
|
34
|
+
|
|
35
|
+
# Projected into the registry, llms.txt, and the agent surface.
|
|
36
|
+
AGENT_RULES = [
|
|
37
|
+
"The root is a REAL form (url:/method:) - answers submit as ordinary params; " \
|
|
38
|
+
"validate server-side and re-render invalid items with error:.",
|
|
39
|
+
"One with_item per question (name: is the param key); choices via item.with_choice, " \
|
|
40
|
+
"an optional free-text answer via item.with_input.",
|
|
41
|
+
"multiple: true renders checkboxes named <name>[] (Rails array params) - " \
|
|
42
|
+
"a recorded divergence from the ported source's repeated bare names.",
|
|
43
|
+
"required: true gates Next/submit client-side; the server stays the truth on submit.",
|
|
44
|
+
"shortcuts: :letters or :numbers labels each choice with a key (server-rendered) " \
|
|
45
|
+
"and enables one-keystroke answering.",
|
|
46
|
+
"Skip renders only while the active item is optional - never force-hide it.",
|
|
47
|
+
"with_progress { custom } replaces the readout; data-current/data-total on the " \
|
|
48
|
+
"progress element and a [data-progress-count] child stay live for segment bars " \
|
|
49
|
+
"and counters."
|
|
50
|
+
].freeze
|
|
51
|
+
|
|
52
|
+
renders_one :progress, doc: "with_progress (bare) renders the auto \"Question X of Y\" text; with_progress " \
|
|
53
|
+
"{ custom } replaces it (marked data-custom so the controller leaves it alone). " \
|
|
54
|
+
"class: merges onto the progress element (e.g. w-full for a full-width segment " \
|
|
55
|
+
"bar over the base w-fit)."
|
|
56
|
+
alias __vc_with_progress with_progress
|
|
57
|
+
|
|
58
|
+
# Opts the progress readout in. Bare, it renders the live
|
|
59
|
+
# "Question X of Y" text; a block replaces the text; class: merges
|
|
60
|
+
# onto the progress element.
|
|
61
|
+
def with_progress(**options, &)
|
|
62
|
+
@progress_class = options[:class]
|
|
63
|
+
__vc_with_progress(&)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @api private
|
|
67
|
+
attr_reader :progress_class
|
|
68
|
+
|
|
69
|
+
use_stimulus do
|
|
70
|
+
on :root do
|
|
71
|
+
controller :questionnaire do
|
|
72
|
+
register
|
|
73
|
+
value :shortcuts, from: :shortcuts_string, if: -> { shortcuts.present? }
|
|
74
|
+
action :keydown, on: :keydown
|
|
75
|
+
action :submit, on: :submit
|
|
76
|
+
action :reset, on: :reset
|
|
77
|
+
action :change, on: :change
|
|
78
|
+
action :input, on: :input
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
on :progress do
|
|
82
|
+
controller(:questionnaire) { target :progress }
|
|
83
|
+
end
|
|
84
|
+
on :previous do
|
|
85
|
+
controller :questionnaire do
|
|
86
|
+
target :previous
|
|
87
|
+
action :previous
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
on :skip do
|
|
91
|
+
controller :questionnaire do
|
|
92
|
+
target :skip
|
|
93
|
+
action :skip
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
on :next_button do
|
|
97
|
+
controller :questionnaire do
|
|
98
|
+
target :next
|
|
99
|
+
action :next
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
on :submit_button do
|
|
103
|
+
controller(:questionnaire) { target :submit }
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
option :url, :string, required: true, doc: "The form's submit URL - answers post here as ordinary params."
|
|
108
|
+
option :http_method, :symbol, default: :post,
|
|
109
|
+
doc: "The form's HTTP verb. Named http_method (not method:) - an option named " \
|
|
110
|
+
"`method` would shadow Object#method."
|
|
111
|
+
option :id, :string, doc: "The root form's DOM id; item element ids derive from it."
|
|
112
|
+
option :shortcuts, :symbol,
|
|
113
|
+
doc: "nil (off), :letters (A, B, C...) or :numbers (1-9): server- rendered key labels + one-keystroke " \
|
|
114
|
+
"answering."
|
|
115
|
+
option :default_item, :string, doc: "The initially active item by name; default is the first item."
|
|
116
|
+
option :previous_label, :string, default: "Previous", doc: "The back-navigation button's text."
|
|
117
|
+
option :skip_label, :string, default: "Skip",
|
|
118
|
+
doc: "The skip button's text (shown only while the active item is optional)."
|
|
119
|
+
option :next_label, :string, default: "Next", doc: "The forward-navigation button's text."
|
|
120
|
+
option :submit_label, :string, default: "Submit",
|
|
121
|
+
doc: "The final submit button's text (replaces Next on the last item)."
|
|
122
|
+
|
|
123
|
+
validates :shortcuts, inclusion: { in: SHORTCUT_MODES }, allow_nil: true
|
|
124
|
+
|
|
125
|
+
part "questionnaire", "The root <form> the controller drives - navigation, validation " \
|
|
126
|
+
"gating, and the keyboard map all ride here"
|
|
127
|
+
part "questionnaire-progress", "The polite progressbar ('Question X of Y'; block " \
|
|
128
|
+
"content replaces the text). Always carries live " \
|
|
129
|
+
"data-current/data-total, and a [data-progress-count] " \
|
|
130
|
+
"child gets the live 'X of Y' text - custom segment " \
|
|
131
|
+
"bars ride data-[current=N] variants",
|
|
132
|
+
states: {
|
|
133
|
+
"data-custom" => "block content supplied - the controller leaves the text alone",
|
|
134
|
+
"data-current" => "always - the active question number (live)",
|
|
135
|
+
"data-total" => "always - the enabled question count (live)"
|
|
136
|
+
}
|
|
137
|
+
part "questionnaire-item", "One question <fieldset> - exactly one is active",
|
|
138
|
+
states: {
|
|
139
|
+
"data-name" => "always - the item's param name",
|
|
140
|
+
"data-active" => "the visible question (inactive items are hidden + inert)",
|
|
141
|
+
"data-status" => { condition: "always - the answer state",
|
|
142
|
+
values: %w[unanswered answered skipped] },
|
|
143
|
+
"data-required" => "required: true - Skip hides and Next validates",
|
|
144
|
+
"data-multiple" => "multiple: true - checkbox choices named <name>[]",
|
|
145
|
+
"data-invalid" => "validation attempted and unanswered - the error shows",
|
|
146
|
+
"data-validated" => "a navigation has demanded this answer at least once",
|
|
147
|
+
"data-skipped" => "explicitly skipped (cleared by any interaction)"
|
|
148
|
+
}
|
|
149
|
+
part "questionnaire-title", "The question heading - a real <legend>"
|
|
150
|
+
part "questionnaire-description", "Muted copy under the title; its id rides the " \
|
|
151
|
+
"fieldset's aria-describedby"
|
|
152
|
+
part "questionnaire-choices", "The answer grid for one item"
|
|
153
|
+
part "questionnaire-choice", "One answer <label> wrapping its native input",
|
|
154
|
+
states: {
|
|
155
|
+
"data-type" => { condition: "always - the input kind",
|
|
156
|
+
values: %w[radio checkbox] },
|
|
157
|
+
"data-checked" => "the choice is selected",
|
|
158
|
+
"data-unchecked" => "the choice is not selected",
|
|
159
|
+
"data-shortcut" => "shortcuts: on - the key label (A, B... or 1-9)",
|
|
160
|
+
"data-disabled" => "choice or item disabled"
|
|
161
|
+
}
|
|
162
|
+
part "questionnaire-choice-input", "THE native radio/checkbox - stretched invisibly " \
|
|
163
|
+
"over the row (paste of the input-otp posture)",
|
|
164
|
+
states: {
|
|
165
|
+
"data-checked" => "selected", "data-unchecked" => "not selected"
|
|
166
|
+
}
|
|
167
|
+
part "questionnaire-choice-indicator", "The box/circle glyph (aria-hidden) - dot for " \
|
|
168
|
+
"radio, check for checkbox"
|
|
169
|
+
part "questionnaire-choice-indicator-dot", "The radio dot (shown while checked)"
|
|
170
|
+
part "questionnaire-choice-indicator-check", "The checkbox check icon (shown while " \
|
|
171
|
+
"checked)"
|
|
172
|
+
part "questionnaire-choice-label", "The label column - answer text over the optional " \
|
|
173
|
+
"description"
|
|
174
|
+
part "questionnaire-choice-description", "Muted copy under the answer text"
|
|
175
|
+
part "questionnaire-choice-shortcut", "The key hint chip (aria-hidden; hidden unless " \
|
|
176
|
+
"the choice carries data-shortcut)"
|
|
177
|
+
part "questionnaire-input-wrapper", "The free-text answer's positioning wrapper"
|
|
178
|
+
part "questionnaire-input", "The free-text answer - a real text input named after " \
|
|
179
|
+
"the item",
|
|
180
|
+
states: {
|
|
181
|
+
"data-filled" => "has a value (counts as answered)",
|
|
182
|
+
"data-empty" => "blank"
|
|
183
|
+
}
|
|
184
|
+
part "questionnaire-error", "The validation message (hidden until a navigation " \
|
|
185
|
+
"demands the answer; role=alert while shown)"
|
|
186
|
+
part "questionnaire-actions", "The navigation row - Previous / Skip / Next / Submit " \
|
|
187
|
+
"(the buttons ride composed Buttons, so those elements " \
|
|
188
|
+
"belong to Button's anatomy, not this contract; each " \
|
|
189
|
+
"carries data-visible/data-hidden + hidden/inert)"
|
|
190
|
+
|
|
191
|
+
# @api private
|
|
192
|
+
def before_render
|
|
193
|
+
# Evaluate the composition block first - with_item is hand-rolled
|
|
194
|
+
# (not a VC slot), so nothing else forces the block this early.
|
|
195
|
+
content
|
|
196
|
+
raise ArgumentError, "Questionnaire needs at least one with_item" if item_models.empty?
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Adds one question. name: is the param key, title: the visible
|
|
200
|
+
# heading; the yielded builder takes answers (with_choice) and an
|
|
201
|
+
# optional free-text field (with_input).
|
|
202
|
+
# Hand-rolled (not a VC slot): the yielded builder is a plain
|
|
203
|
+
# object, and ViewComponent lambda slots only forward to component
|
|
204
|
+
# returns - a builder return wraps as nil.
|
|
205
|
+
def with_item(**, &block)
|
|
206
|
+
item = Item.new(**)
|
|
207
|
+
block&.call(item)
|
|
208
|
+
item_models << item
|
|
209
|
+
item
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# @api private
|
|
213
|
+
def item_models = (@item_models ||= [])
|
|
214
|
+
|
|
215
|
+
# @api private
|
|
216
|
+
def questionnaire_id
|
|
217
|
+
@questionnaire_id ||= if (token = dom_id_token(id))
|
|
218
|
+
"questionnaire-#{token}"
|
|
219
|
+
else
|
|
220
|
+
poetry_instance_id("questionnaire")
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# @api private
|
|
225
|
+
def enabled_items = item_models.reject(&:disabled)
|
|
226
|
+
|
|
227
|
+
# @api private
|
|
228
|
+
def active_item
|
|
229
|
+
@active_item ||= enabled_items.find { |item| item.name == default_item } ||
|
|
230
|
+
enabled_items.first
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# @api private
|
|
234
|
+
def active_index = enabled_items.index(active_item) || 0
|
|
235
|
+
|
|
236
|
+
# @api private
|
|
237
|
+
def first? = active_index.zero?
|
|
238
|
+
# @api private
|
|
239
|
+
def last? = active_index >= enabled_items.size - 1
|
|
240
|
+
|
|
241
|
+
# @api private
|
|
242
|
+
def progress_label
|
|
243
|
+
"Question #{active_index + 1} of #{enabled_items.size}"
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# @api private
|
|
247
|
+
def shortcuts_string = shortcuts.to_s
|
|
248
|
+
|
|
249
|
+
# Server-side shortcut assignment: keys in document order per item;
|
|
250
|
+
# the controller only handles keystrokes.
|
|
251
|
+
# @api private
|
|
252
|
+
def shortcut_for(_item, index)
|
|
253
|
+
return nil unless shortcuts
|
|
254
|
+
|
|
255
|
+
SHORTCUT_KEYS.fetch(shortcuts)[index]
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# @api private
|
|
259
|
+
def item_dom_id(item, suffix)
|
|
260
|
+
"#{questionnaire_id}-#{item.name.to_s.parameterize}-#{suffix}"
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# @api private
|
|
264
|
+
def field_name(item) = item.multiple ? "#{item.name}[]" : item.name.to_s
|
|
265
|
+
|
|
266
|
+
# @api private
|
|
267
|
+
def input_type(item) = item.multiple ? "checkbox" : "radio"
|
|
268
|
+
|
|
269
|
+
# @api private
|
|
270
|
+
def root_attributes
|
|
271
|
+
html_attributes.merge_if_not_set(
|
|
272
|
+
{ "data-slot" => "questionnaire", "id" => questionnaire_id }
|
|
273
|
+
.merge(stimulus_attributes_for(:root))
|
|
274
|
+
.merge(component_data_attributes)
|
|
275
|
+
)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# @api private
|
|
279
|
+
def item_attributes(item)
|
|
280
|
+
active = item == active_item
|
|
281
|
+
attrs = {
|
|
282
|
+
"class" => css(:item, class: item.class_name), "data-slot" => "questionnaire-item",
|
|
283
|
+
"data-name" => item.name, "data-status" => item.status,
|
|
284
|
+
"aria-describedby" => (item.description ? item_dom_id(item, "description") : nil)
|
|
285
|
+
}.compact
|
|
286
|
+
attrs["data-active"] = "" if active
|
|
287
|
+
attrs["hidden"] = "hidden" unless active
|
|
288
|
+
attrs["inert"] = "inert" unless active
|
|
289
|
+
attrs["data-required"] = "" if item.required
|
|
290
|
+
attrs["data-multiple"] = "" if item.multiple
|
|
291
|
+
attrs["disabled"] = "disabled" if item.disabled
|
|
292
|
+
attrs
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Symbol-keyed Button kwargs for one nav action: part classes,
|
|
296
|
+
# slot, wiring, and the server-rendered visibility stamp.
|
|
297
|
+
# @api private
|
|
298
|
+
def nav_button_options(kind, hidden:)
|
|
299
|
+
stimulus_element = { next: :next_button, submit: :submit_button }.fetch(kind, kind)
|
|
300
|
+
attrs = { class: css(kind), "data-slot": "questionnaire-#{kind}" }
|
|
301
|
+
attrs.merge!(stimulus_attributes_for(stimulus_element).transform_keys(&:to_sym))
|
|
302
|
+
if hidden
|
|
303
|
+
attrs.merge!(hidden: true, inert: true, tabindex: -1,
|
|
304
|
+
"aria-hidden": "true", "data-hidden": "")
|
|
305
|
+
else
|
|
306
|
+
attrs[:"data-visible"] = ""
|
|
307
|
+
end
|
|
308
|
+
attrs
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# @api private
|
|
312
|
+
def choice_attributes(item, choice, index)
|
|
313
|
+
attrs = {
|
|
314
|
+
"class" => css(:choice), "data-slot" => "questionnaire-choice",
|
|
315
|
+
"data-type" => input_type(item)
|
|
316
|
+
}
|
|
317
|
+
attrs[choice.checked ? "data-checked" : "data-unchecked"] = ""
|
|
318
|
+
attrs["data-disabled"] = "" if choice.disabled || item.disabled
|
|
319
|
+
if (key = shortcut_for(item, index))
|
|
320
|
+
attrs["data-shortcut"] = key
|
|
321
|
+
end
|
|
322
|
+
attrs
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# One answer row's data, built via Item#with_choice.
|
|
326
|
+
# @api private
|
|
327
|
+
Choice = Struct.new(:value, :label, :description, :checked, :disabled, keyword_init: true)
|
|
328
|
+
# The free-text answer's data, built via Item#with_input.
|
|
329
|
+
# @api private
|
|
330
|
+
TextInput = Struct.new(:label, :placeholder, :value, keyword_init: true)
|
|
331
|
+
|
|
332
|
+
# One question: title/description as args, choices and the
|
|
333
|
+
# optional free-text input via the yielded builder.
|
|
334
|
+
class Item
|
|
335
|
+
attr_reader :name, :title, :description, :required, :multiple, :disabled,
|
|
336
|
+
:error, :choices, :input, :class_name
|
|
337
|
+
|
|
338
|
+
# @api private
|
|
339
|
+
def initialize(name:, title:, **options)
|
|
340
|
+
@name = name
|
|
341
|
+
@title = title
|
|
342
|
+
@class_name = options[:class]
|
|
343
|
+
@description = options[:description]
|
|
344
|
+
@required = options.fetch(:required, false)
|
|
345
|
+
@multiple = options.fetch(:multiple, false)
|
|
346
|
+
@disabled = options.fetch(:disabled, false)
|
|
347
|
+
@error = options[:error]
|
|
348
|
+
@choices = []
|
|
349
|
+
@input = nil
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# Adds one answer to the question.
|
|
353
|
+
# @param value [String] the submitted param value
|
|
354
|
+
# @param label [String] the visible answer text
|
|
355
|
+
# @param description [String, nil] muted copy under the answer text
|
|
356
|
+
# @param checked [Boolean] pre-selects the answer
|
|
357
|
+
# @param disabled [Boolean] renders the answer unpickable
|
|
358
|
+
def with_choice(value:, label:, description: nil, checked: false, disabled: false)
|
|
359
|
+
@choices << Choice.new(value: value, label: label, description: description,
|
|
360
|
+
checked: checked, disabled: disabled)
|
|
361
|
+
self
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# Adds the optional free-text answer field, named after the item.
|
|
365
|
+
# @param label [String] the field's accessible label
|
|
366
|
+
# @param placeholder [String, nil] hint text while empty
|
|
367
|
+
# @param value [String, nil] the pre-filled answer
|
|
368
|
+
def with_input(label:, placeholder: nil, value: nil)
|
|
369
|
+
@input = TextInput.new(label: label, placeholder: placeholder, value: value)
|
|
370
|
+
self
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# @api private
|
|
374
|
+
def default_error
|
|
375
|
+
if required
|
|
376
|
+
"Choose an answer to continue."
|
|
377
|
+
else
|
|
378
|
+
"Choose an answer or skip this question."
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
# @api private
|
|
383
|
+
def error_message = error || default_error
|
|
384
|
+
|
|
385
|
+
# @api private
|
|
386
|
+
def answered?
|
|
387
|
+
choices.any?(&:checked) || input&.value.to_s.strip != ""
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
# @api private
|
|
391
|
+
def status = answered? ? "answered" : "unanswered"
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Questionnaire
|
|
6
|
+
# The Questionnaire preview matrix: the hero three-question flow
|
|
7
|
+
# (required single choice + free text, optional multiple, required
|
|
8
|
+
# closer), plus the per-axis scenarios the contracts hold to DOM -
|
|
9
|
+
# shortcuts, an answered/checked state, a skipped optional item, a
|
|
10
|
+
# disabled choice, and a custom progress readout.
|
|
11
|
+
class Preview < Poetry::Core::Preview::Base
|
|
12
|
+
def default
|
|
13
|
+
render_component(url: "#", shortcuts: :letters, class: "w-full max-w-md") do |q|
|
|
14
|
+
q.with_progress
|
|
15
|
+
q.with_item(name: "direction", title: "What should the agent build next?",
|
|
16
|
+
description: "Choose a direction or describe another task.",
|
|
17
|
+
required: true) do |item|
|
|
18
|
+
item.with_choice(value: "tool-calls", label: "Tool call timeline",
|
|
19
|
+
description: "Show what the agent ran and what came back.")
|
|
20
|
+
item.with_choice(value: "approvals", label: "Approval checkpoints",
|
|
21
|
+
description: "Ask before sensitive or destructive actions.")
|
|
22
|
+
item.with_input(label: "Another agent feature", placeholder: "Describe another feature…")
|
|
23
|
+
end
|
|
24
|
+
q.with_item(name: "signals", title: "What should every update include?",
|
|
25
|
+
description: "Select all that apply, or skip this question.",
|
|
26
|
+
multiple: true) do |item|
|
|
27
|
+
item.with_choice(value: "progress", label: "Progress")
|
|
28
|
+
item.with_choice(value: "decisions", label: "Decisions")
|
|
29
|
+
item.with_choice(value: "risks", label: "Risks")
|
|
30
|
+
end
|
|
31
|
+
q.with_item(name: "timing", title: "When should work begin?", required: true) do |item|
|
|
32
|
+
item.with_choice(value: "now", label: "Start now")
|
|
33
|
+
item.with_choice(value: "backlog", label: "Add it to the backlog")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Server-rendered answered state: checked choice, answered status,
|
|
39
|
+
# the second item active via default_item.
|
|
40
|
+
def answered_and_resumed
|
|
41
|
+
render_component(url: "#", default_item: "detail", class: "w-full max-w-md") do |q|
|
|
42
|
+
q.with_progress
|
|
43
|
+
q.with_item(name: "prototype", title: "What should we prototype?", required: true) do |item|
|
|
44
|
+
item.with_choice(value: "delegation", label: "Delegation", checked: true)
|
|
45
|
+
item.with_choice(value: "prompts", label: "Question prompts")
|
|
46
|
+
end
|
|
47
|
+
q.with_item(name: "detail", title: "How much detail?") do |item|
|
|
48
|
+
item.with_choice(value: "focused", label: "Focused")
|
|
49
|
+
item.with_choice(value: "complete", label: "Complete flow")
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Server-rendered CHECKED choices of both kinds on one screen: the
|
|
55
|
+
# checkbox's check glyph and the radio's dot, seated in their boxes -
|
|
56
|
+
# the geometry a golden must hold (an unsized glyph hangs off the box).
|
|
57
|
+
def checked_choices
|
|
58
|
+
render_component(url: "#", shortcuts: :letters, class: "w-full max-w-md") do |q|
|
|
59
|
+
q.with_progress
|
|
60
|
+
q.with_item(name: "signals", title: "What should every update include?",
|
|
61
|
+
description: "Two are already selected.", multiple: true) do |item|
|
|
62
|
+
item.with_choice(value: "progress", label: "Progress", checked: true)
|
|
63
|
+
item.with_choice(value: "decisions", label: "Decisions",
|
|
64
|
+
description: "Which options were weighed and what won.", checked: true)
|
|
65
|
+
item.with_choice(value: "risks", label: "Risks")
|
|
66
|
+
end
|
|
67
|
+
q.with_item(name: "timing", title: "When should work begin?", required: true) do |item|
|
|
68
|
+
item.with_choice(value: "now", label: "Start now", checked: true)
|
|
69
|
+
item.with_choice(value: "backlog", label: "Add it to the backlog")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Number shortcuts + a disabled choice + a pre-filled free answer.
|
|
75
|
+
def numbers_and_disabled
|
|
76
|
+
render_component(url: "#", shortcuts: :numbers, class: "w-full max-w-md") do |q|
|
|
77
|
+
q.with_item(name: "cadence", title: "How often should we check in?",
|
|
78
|
+
description: "Weekly keeps the loop tight.") do |item|
|
|
79
|
+
item.with_choice(value: "daily", label: "Daily", disabled: true)
|
|
80
|
+
item.with_choice(value: "weekly", label: "Weekly")
|
|
81
|
+
item.with_input(label: "Another cadence", placeholder: "Every…", value: "Fortnightly")
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# A custom progress readout (data-custom - the controller leaves it).
|
|
87
|
+
def custom_progress
|
|
88
|
+
render_component(url: "#", class: "w-full max-w-md") do |q|
|
|
89
|
+
q.with_progress { "Step 1 · Kickoff" }
|
|
90
|
+
q.with_item(name: "kickoff", title: "Ready to start?", required: true) do |item|
|
|
91
|
+
item.with_choice(value: "yes", label: "Yes")
|
|
92
|
+
item.with_choice(value: "later", label: "Later")
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Questionnaire
|
|
6
|
+
# Style dictionary for the Questionnaire family: structural classes
|
|
7
|
+
# inline; the visual voice lives in each theme's cn-questionnaire-*
|
|
8
|
+
# rules.
|
|
9
|
+
class Style < Poetry::Core::Style
|
|
10
|
+
base "cn-questionnaire flex w-full min-w-0 flex-col"
|
|
11
|
+
|
|
12
|
+
element :progress, "cn-questionnaire-progress min-h-[1lh] w-fit min-w-[14ch] font-medium " \
|
|
13
|
+
"text-muted-foreground tabular-nums"
|
|
14
|
+
element :item, "cn-questionnaire-item min-w-0 border-0 p-0 outline-none"
|
|
15
|
+
element :title, "cn-questionnaire-title cn-font-heading text-pretty"
|
|
16
|
+
element :description, "cn-questionnaire-description text-pretty text-muted-foreground"
|
|
17
|
+
element :choices, "cn-questionnaire-choices group/questionnaire-choices grid min-w-0"
|
|
18
|
+
element :choice, "cn-questionnaire-choice group/questionnaire-choice relative flex " \
|
|
19
|
+
"min-h-11 cursor-pointer items-start text-start transition-colors " \
|
|
20
|
+
"outline-none select-none data-disabled:pointer-events-none " \
|
|
21
|
+
"data-disabled:cursor-not-allowed data-disabled:opacity-50"
|
|
22
|
+
element :choice_input, "absolute inset-0 z-10 size-full cursor-pointer opacity-0"
|
|
23
|
+
element :choice_indicator, "cn-questionnaire-choice-indicator pointer-events-none " \
|
|
24
|
+
"relative flex shrink-0 items-center justify-center border " \
|
|
25
|
+
"group-data-[type=radio]/questionnaire-choice:rounded-full"
|
|
26
|
+
element :choice_indicator_dot, "cn-questionnaire-choice-indicator-dot hidden rounded-full " \
|
|
27
|
+
"group-data-[type=checkbox]/questionnaire-choice:hidden " \
|
|
28
|
+
"group-data-checked/questionnaire-choice:block"
|
|
29
|
+
element :choice_indicator_check, "cn-questionnaire-choice-indicator-check hidden " \
|
|
30
|
+
"group-data-[type=radio]/questionnaire-choice:hidden " \
|
|
31
|
+
"group-data-checked/questionnaire-choice:block"
|
|
32
|
+
element :choice_label, "cn-questionnaire-choice-content flex min-w-0 flex-1 flex-col leading-snug"
|
|
33
|
+
element :choice_description, "cn-questionnaire-choice-description"
|
|
34
|
+
element :choice_shortcut, "cn-questionnaire-shortcut pointer-events-none ms-auto hidden shrink-0 " \
|
|
35
|
+
"group-data-[shortcut]/questionnaire-choice:inline-flex"
|
|
36
|
+
element :input_wrapper, "cn-questionnaire-input-wrapper group/questionnaire-input " \
|
|
37
|
+
"relative min-w-0"
|
|
38
|
+
element :input, "cn-questionnaire-input min-h-11 w-full min-w-0 " \
|
|
39
|
+
"transition-[color,box-shadow,background-color] outline-none " \
|
|
40
|
+
"disabled:pointer-events-none disabled:cursor-not-allowed " \
|
|
41
|
+
"disabled:opacity-50 sm:min-h-0 selection:bg-primary " \
|
|
42
|
+
"selection:text-primary-foreground placeholder:text-muted-foreground"
|
|
43
|
+
element :error, "cn-questionnaire-error text-destructive"
|
|
44
|
+
element :actions, "cn-questionnaire-actions grid min-h-11 w-full " \
|
|
45
|
+
"grid-cols-[minmax(0,1fr)_auto_auto] items-center"
|
|
46
|
+
element :previous, "col-start-1 row-start-1 min-h-11 justify-self-start sm:min-h-0"
|
|
47
|
+
element :skip, "col-start-2 row-start-1 min-h-11 justify-self-end sm:min-h-0"
|
|
48
|
+
element :next, "col-start-3 row-start-1 min-h-11 justify-self-end sm:min-h-0"
|
|
49
|
+
element :submit, "col-start-3 row-start-1 min-h-11 justify-self-end sm:min-h-0"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<%# Items render as direct children of the radiogroup root (grid gap-3) -
|
|
2
|
+
the roving collection queries [data-poetry-collection-item] in DOM
|
|
3
|
+
order; the label-pairing rows are transparent to it. %>
|
|
4
|
+
<%= content_tag(:div, safe_join(items.map(&:to_s)), **root_attributes.to_attributes) %>
|