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,526 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
# Command family: the filterable command palette and its dialog form.
|
|
6
|
+
module Command
|
|
7
|
+
# The engine pieces below (ItemSet, Helpers, Item, Group) build the
|
|
8
|
+
# palette's anatomy; Combobox reuses the same markup contract.
|
|
9
|
+
|
|
10
|
+
# The server-side item registry: every rendered item (DOM order)
|
|
11
|
+
# lands here so option ids are server-stable ("#{id}-item-<n>" - the
|
|
12
|
+
# aria-activedescendant contract) and the initial highlight (the
|
|
13
|
+
# value: option, else the first enabled item) is decided in render
|
|
14
|
+
# order before the input derives its activedescendant. Duplicate or
|
|
15
|
+
# blank values raise at render.
|
|
16
|
+
#
|
|
17
|
+
# @api private
|
|
18
|
+
class ItemSet
|
|
19
|
+
attr_reader :highlighted_id, :count
|
|
20
|
+
|
|
21
|
+
def initialize(base_id:, highlight_value: nil)
|
|
22
|
+
@base_id = base_id
|
|
23
|
+
@highlight_value = highlight_value.presence
|
|
24
|
+
@seen = Set.new
|
|
25
|
+
@count = 0
|
|
26
|
+
@highlighted_id = nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Registers one item; returns [item_id, highlighted].
|
|
30
|
+
def register(value:, disabled: false)
|
|
31
|
+
key = value.to_s
|
|
32
|
+
raise ArgumentError, "Command item requires a non-blank value:" if key.blank?
|
|
33
|
+
unless @seen.add?(key)
|
|
34
|
+
raise ArgumentError, "duplicate Command item value #{key.inspect} - item values must be " \
|
|
35
|
+
"unique within their command"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
item_id = "#{@base_id}-item-#{@count}"
|
|
39
|
+
@count += 1
|
|
40
|
+
highlighted = highlight?(key, disabled)
|
|
41
|
+
@highlighted_id = item_id if highlighted
|
|
42
|
+
[item_id, highlighted]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# value: given -> only that item (unless disabled; the controller's
|
|
48
|
+
# connect-seat falls back client-side); no value -> first enabled.
|
|
49
|
+
def highlight?(value, disabled)
|
|
50
|
+
return false if @highlighted_id || disabled
|
|
51
|
+
|
|
52
|
+
@highlight_value ? value == @highlight_value : true
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Shared part builders for the item union - mixed into the root
|
|
57
|
+
# Component and the nested Group so both levels render the same
|
|
58
|
+
# anatomy through the same Builder. Hosts must expose #item_set.
|
|
59
|
+
#
|
|
60
|
+
# @api private
|
|
61
|
+
module Helpers
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
# Hosts must also expose #item_wiring (the root Component computes
|
|
65
|
+
# it from its :item declaration; Group receives it at construction)
|
|
66
|
+
# so every item renders the same declared wiring.
|
|
67
|
+
def item_component(**)
|
|
68
|
+
Item.new(item_set: item_set, item_wiring: item_wiring, **)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# role=separator, server-rendered VISIBLE - the controller hides
|
|
72
|
+
# every separator whenever the query is non-empty (a filtered
|
|
73
|
+
# list has no stable sections).
|
|
74
|
+
def separator_part(**options)
|
|
75
|
+
attrs = {
|
|
76
|
+
# Decorative inside role=listbox (only option/group children
|
|
77
|
+
# are valid - axe aria-required-children, the Select precedent).
|
|
78
|
+
"data-slot" => "command-separator", "aria-hidden" => "true",
|
|
79
|
+
"class" => Style.css(:separator, class: options.delete(:class))
|
|
80
|
+
}
|
|
81
|
+
content_tag(:div, nil, attrs.merge(options))
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# One role=option div (APG editable combobox, activedescendant
|
|
86
|
+
# flavor). A part component ON PURPOSE (not an eager lambda):
|
|
87
|
+
# rendering happens in DOM order, so the shared ItemSet assigns
|
|
88
|
+
# server-stable ids and the initial highlight in exactly the order
|
|
89
|
+
# the controller's collection walks. NO tabindex (options are never
|
|
90
|
+
# DOM-focused - the activedescendant design) and NO aria-selected
|
|
91
|
+
# (reserved for committed values - a bare palette has none; the
|
|
92
|
+
# highlight is data-highlighted + the input's activedescendant).
|
|
93
|
+
#
|
|
94
|
+
# @api private
|
|
95
|
+
class Item < Poetry::Core::Component
|
|
96
|
+
internal_component!
|
|
97
|
+
include Helpers
|
|
98
|
+
|
|
99
|
+
attr_reader :item_set, :item_wiring
|
|
100
|
+
|
|
101
|
+
def initialize(item_set:, value:, item_wiring: {}, **options)
|
|
102
|
+
super(options)
|
|
103
|
+
@item_set = item_set
|
|
104
|
+
@item_wiring = item_wiring
|
|
105
|
+
@value = value.to_s
|
|
106
|
+
@disabled = options.delete(:disabled) || false
|
|
107
|
+
@keywords = Array(options.delete(:keywords)).map(&:to_s)
|
|
108
|
+
@filter_value = options.delete(:filter_value)
|
|
109
|
+
@always_render = options.delete(:always_render) || false
|
|
110
|
+
@shortcut = options.delete(:shortcut)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def call
|
|
114
|
+
item_id, highlighted = item_set.register(value: @value, disabled: @disabled)
|
|
115
|
+
attrs = {
|
|
116
|
+
"id" => item_id, "data-slot" => "command-item", "role" => "option",
|
|
117
|
+
"data-poetry-collection-item" => "", "data-value" => @value,
|
|
118
|
+
"class" => Style.css(:item, class: html_attributes.delete(:class))
|
|
119
|
+
}.merge(@item_wiring)
|
|
120
|
+
attrs["data-highlighted"] = "" if highlighted
|
|
121
|
+
if @disabled
|
|
122
|
+
attrs["aria-disabled"] = "true"
|
|
123
|
+
attrs["data-disabled"] = ""
|
|
124
|
+
end
|
|
125
|
+
attrs["data-keywords"] = @keywords.join(" ") if @keywords.any?
|
|
126
|
+
attrs["data-filter-value"] = @filter_value if @filter_value
|
|
127
|
+
attrs["data-always-render"] = "" if @always_render
|
|
128
|
+
content_tag(:div, Poetry::Core::HTML::Attributes.merged(attrs, html_attributes)) do
|
|
129
|
+
safe_join([label_part, shortcut_part].compact)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
# The item's LABEL - the filter/typematch text source (shortcuts
|
|
136
|
+
# excluded; icons contribute no textContent; icon-rich content
|
|
137
|
+
# overrides via filter_value:/keywords:).
|
|
138
|
+
def label_part
|
|
139
|
+
content_tag(:span, content, "data-slot" => "command-item-text",
|
|
140
|
+
"class" => Style.css(:item_text))
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Presentational only - Command never binds the hinted key (the
|
|
144
|
+
# host recipe does); excluded from the filter text.
|
|
145
|
+
def shortcut_part
|
|
146
|
+
return if @shortcut.blank?
|
|
147
|
+
|
|
148
|
+
content_tag(:span, @shortcut, "data-slot" => "command-shortcut",
|
|
149
|
+
"class" => Style.css(:shortcut))
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# role=group labelled by its heading part (aria-labelledby wired) -
|
|
154
|
+
# the same item union one level down, registering its items into the
|
|
155
|
+
# PARENT's item set so ids and the initial highlight stay in DOM
|
|
156
|
+
# order. Kept internal ON PURPOSE: nested parts are anatomy, not
|
|
157
|
+
# registered components.
|
|
158
|
+
#
|
|
159
|
+
# @api private
|
|
160
|
+
class Group < Poetry::Core::Component
|
|
161
|
+
internal_component!
|
|
162
|
+
include Helpers
|
|
163
|
+
|
|
164
|
+
attr_reader :item_set, :item_wiring
|
|
165
|
+
|
|
166
|
+
renders_many :items,
|
|
167
|
+
doc: "The group's members: with_item actions and with_separator dividers, interleaved in " \
|
|
168
|
+
"declaration order.",
|
|
169
|
+
types: {
|
|
170
|
+
item: { renders: ->(**options) { item_component(**options) }, as: :item },
|
|
171
|
+
separator: { renders: ->(**options) { separator_part(**options) }, as: :separator }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
def initialize(item_set:, heading:, item_wiring: {}, always_render: false, **extra_attributes)
|
|
175
|
+
raise ArgumentError, "Command group requires heading: (the group's accessible name)" if heading.blank?
|
|
176
|
+
|
|
177
|
+
super(extra_attributes)
|
|
178
|
+
@item_set = item_set
|
|
179
|
+
@item_wiring = item_wiring
|
|
180
|
+
@heading_text = heading
|
|
181
|
+
@always_render = always_render
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def before_render
|
|
185
|
+
raise ArgumentError, "Command group requires at least one item" unless items?
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def call
|
|
189
|
+
attrs = {
|
|
190
|
+
"data-slot" => "command-group", "role" => "group", "aria-labelledby" => heading_id,
|
|
191
|
+
"class" => Style.css(:group, class: html_attributes.delete(:class))
|
|
192
|
+
}
|
|
193
|
+
attrs["data-always-render"] = "" if @always_render
|
|
194
|
+
content_tag(:div, Poetry::Core::HTML::Attributes.merged(attrs, html_attributes)) do
|
|
195
|
+
safe_join([heading_part, *items])
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
private
|
|
200
|
+
|
|
201
|
+
def group_id
|
|
202
|
+
@group_id ||= poetry_instance_id("poetry-command-group")
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def heading_id
|
|
206
|
+
"#{group_id}-heading"
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# A styled heading, no ARIA role - the group points at it via
|
|
210
|
+
# aria-labelledby.
|
|
211
|
+
def heading_part
|
|
212
|
+
content_tag(:div, @heading_text, "data-slot" => "command-group-heading", "id" => heading_id,
|
|
213
|
+
"class" => Style.css(:heading))
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# A command palette: an always-visible search input over a listbox
|
|
218
|
+
# of actions, filtered client-side as you type. Real focus stays
|
|
219
|
+
# pinned to the input for the whole session; the highlighted option
|
|
220
|
+
# carries data-highlighted and its server-stable id in the input's
|
|
221
|
+
# aria-activedescendant, and options never take focus themselves.
|
|
222
|
+
#
|
|
223
|
+
# The filter is deterministic substring matching with ranked bands,
|
|
224
|
+
# and HIDE-ONLY: the server renders ALL items and the controller
|
|
225
|
+
# hides non-matches, never reordering the DOM. Activating an item
|
|
226
|
+
# fires a cancelable poetry:command:select event - the host (or
|
|
227
|
+
# Combobox, which wraps this engine) owns the consequences.
|
|
228
|
+
# filter: false leaves filtering to the server (the Turbo-frame
|
|
229
|
+
# async recipe).
|
|
230
|
+
#
|
|
231
|
+
# @example
|
|
232
|
+
# render Poetry::Ui::Command::Component.new("aria-label": "Command menu") do |command|
|
|
233
|
+
# command.with_item(value: "new-file") { "New file" }
|
|
234
|
+
# command.with_item(value: "search") { "Search" }
|
|
235
|
+
# end
|
|
236
|
+
class Component < Poetry::Core::Component
|
|
237
|
+
include Helpers
|
|
238
|
+
|
|
239
|
+
# Projected into the registry, llms.txt, and the agent surface.
|
|
240
|
+
AGENT_RULES = [
|
|
241
|
+
"Use poetry_command - never hand-roll a filterable listbox with an input + a list and ad-hoc JS.",
|
|
242
|
+
"Command items DO things; they carry no form value. Picking a value for a form is Combobox " \
|
|
243
|
+
"(which wraps this) - never bind a hidden input to a bare Command.",
|
|
244
|
+
"Every item needs a unique value: (ArgumentError) and gets a server id - never strip item ids " \
|
|
245
|
+
"(aria-activedescendant depends on them).",
|
|
246
|
+
"Never put tabindex or focus on options; never write aria-selected in a bare Command - " \
|
|
247
|
+
"highlight is data-highlighted + activedescendant only.",
|
|
248
|
+
"Filtering is hide-only: never reorder, remove, or re-append items to 'sort' results - " \
|
|
249
|
+
"DOM order is the contract.",
|
|
250
|
+
"The poetry:command:select event is the ONLY activation surface - act in a listener (or item " \
|
|
251
|
+
"data-action); don't patch the controller to navigate.",
|
|
252
|
+
"Long/async data: filter: false + a Turbo frame (the recipe) - don't render 5,000 items and hope.",
|
|
253
|
+
"Icon-rich labels: set filter_value:/keywords: rather than stuffing hidden text into items."
|
|
254
|
+
].freeze
|
|
255
|
+
|
|
256
|
+
# The input-bound ARIA surface: aria-label/labelledby/describedby
|
|
257
|
+
# passed to the component land on the INPUT - the combobox is the
|
|
258
|
+
# interactive control the label must reach - never on the root div.
|
|
259
|
+
INPUT_ARIA_KEYS = %w[label labelledby describedby].freeze
|
|
260
|
+
|
|
261
|
+
# The accessible-name requirement stated statically: an id or an
|
|
262
|
+
# aria name must be given, checkable at write time.
|
|
263
|
+
REQUIRES_ANY = [
|
|
264
|
+
{ hint: "the input's accessible name",
|
|
265
|
+
options: %w[id aria-label aria-labelledby aria] }
|
|
266
|
+
].freeze
|
|
267
|
+
|
|
268
|
+
renders_one :empty, doc: "Custom zero-results content (defaults to t('poetry.command.empty'))."
|
|
269
|
+
renders_one :loading, doc: "Custom pending content (a spinner); the HOST toggles visibility (Turbo frame " \
|
|
270
|
+
"events) - Command renders the part, never sets it."
|
|
271
|
+
|
|
272
|
+
renders_many :items,
|
|
273
|
+
doc: "The item UNION: item | group (heading + items) | separator - one ordered collection " \
|
|
274
|
+
"(interleaving preserved; items and groups are part COMPONENTS so id assignment follows " \
|
|
275
|
+
"render/DOM order).",
|
|
276
|
+
types: {
|
|
277
|
+
item: { renders: ->(**options) { item_component(**options) }, as: :item },
|
|
278
|
+
group: { renders: lambda { |**options|
|
|
279
|
+
Group.new(item_set: item_set, item_wiring: item_wiring, **options)
|
|
280
|
+
}, as: :group },
|
|
281
|
+
separator: { renders: ->(**options) { separator_part(**options) }, as: :separator }
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
use_stimulus do
|
|
285
|
+
on :root do
|
|
286
|
+
controller :command do
|
|
287
|
+
register
|
|
288
|
+
value :filter
|
|
289
|
+
value :loop
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
on :input do
|
|
293
|
+
controller :command do
|
|
294
|
+
action :filter_input, on: :input
|
|
295
|
+
action :keydown, on: :keydown
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
# Every item (root-level and grouped) wears this - the anatomy
|
|
299
|
+
# classes receive it as item_wiring at construction.
|
|
300
|
+
on :item do
|
|
301
|
+
controller :command do
|
|
302
|
+
action :activate, on: :click
|
|
303
|
+
action :pointer_highlight, on: :pointermove
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
option :filter, :boolean, default: true, doc: "Client-side filtering; false leaves the list server-driven."
|
|
309
|
+
option :loop, :boolean, default: false, doc: "Wraps arrow-key highlight movement past either end of the list."
|
|
310
|
+
option :placeholder, :string, doc: "The filter input's placeholder text."
|
|
311
|
+
option :list_label, :string, default: -> { I18n.t("poetry.command.list_label") },
|
|
312
|
+
doc: "The listbox's accessible name."
|
|
313
|
+
option :value, :string, doc: "Seats the initial highlight on the item with this value."
|
|
314
|
+
option :disabled, :boolean, default: false, doc: "Disables the filter input."
|
|
315
|
+
option :id, :string, doc: "The base DOM id; the input, list, and item ids derive from it."
|
|
316
|
+
|
|
317
|
+
part "command", "Root of the palette - the input row over the listbox, carrying the " \
|
|
318
|
+
"engine controller"
|
|
319
|
+
part "command-input-wrapper", "The input row - search icon + filter input above the list"
|
|
320
|
+
part "command-search-icon", "Decorative search glyph beside the input"
|
|
321
|
+
part "command-input", "The role=combobox filter input - real focus stays pinned here " \
|
|
322
|
+
"for the whole session; the highlight rides aria-activedescendant"
|
|
323
|
+
part "command-list", "The role=listbox holding empty/loading/items - the input's " \
|
|
324
|
+
"aria-controls target"
|
|
325
|
+
part "command-empty", "Zero-matches message - rendered hidden; the controller unhides " \
|
|
326
|
+
"it when the filter pass leaves no visible items"
|
|
327
|
+
part "command-loading", "Pending affordance (role=status) - rendered hidden; the HOST " \
|
|
328
|
+
"toggles it (Turbo frame events), Command never does"
|
|
329
|
+
part "command-group", "role=group labelled by its heading - hidden by the controller " \
|
|
330
|
+
"when every member item is filtered out",
|
|
331
|
+
states: {
|
|
332
|
+
"data-always-render" => "always_render: is set - the group survives every filter pass"
|
|
333
|
+
}
|
|
334
|
+
part "command-group-heading", "The group heading - styled, no ARIA role (the group " \
|
|
335
|
+
"points at it via aria-labelledby)"
|
|
336
|
+
part "command-item", "One role=option action row - highlight, filtering, and " \
|
|
337
|
+
"disablement ride here (never aria-selected in a bare Command)",
|
|
338
|
+
states: {
|
|
339
|
+
"data-value" => "always - the item's unique value (its server-stable id follows " \
|
|
340
|
+
"registration order)",
|
|
341
|
+
"data-highlighted" => "the item holds the highlight (bare; the controller " \
|
|
342
|
+
"twin-writes it with the input's aria-activedescendant)",
|
|
343
|
+
"data-disabled" => "disabled: is set (aria-disabled rides along)",
|
|
344
|
+
"data-keywords" => "keywords: given - extra filter terms beyond the label",
|
|
345
|
+
"data-always-render" => "always_render: is set - the item survives every filter pass",
|
|
346
|
+
"data-hidden" => "the filter scored the item zero (the controller pairs it with " \
|
|
347
|
+
"hidden; never rendered server-side)"
|
|
348
|
+
}
|
|
349
|
+
part "command-item-text", "The item's label span - the filter/typematch text source " \
|
|
350
|
+
"(shortcuts and icons excluded)"
|
|
351
|
+
part "command-shortcut", "Presentational keyboard hint - excluded from the filter text; " \
|
|
352
|
+
"Command never binds the hinted key"
|
|
353
|
+
part "command-separator", "Decorative divider (aria-hidden) - hidden by the controller " \
|
|
354
|
+
"whenever the query is non-empty"
|
|
355
|
+
part "command-status", "The sr-only polite result-count live region - the controller " \
|
|
356
|
+
"writes the debounced count from the localized templates",
|
|
357
|
+
states: {
|
|
358
|
+
"data-zero" => "always - the localized zero-results template",
|
|
359
|
+
"data-one" => "always - the localized one-result template",
|
|
360
|
+
"data-other" => "always - the localized many-results template (a literal count " \
|
|
361
|
+
"placeholder the controller interpolates)"
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
# Pulls the input-bound aria-* out of the root attributes.
|
|
365
|
+
# @api private
|
|
366
|
+
def initialize(attributes = {})
|
|
367
|
+
super
|
|
368
|
+
@input_aria = extract_input_aria!
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
# The input row's trailing content - the seam the CommandDialog
|
|
372
|
+
# seats its close button in (IN the row, never laid over the input).
|
|
373
|
+
# A lambda the owner supplies, called at render time in the OWNER's
|
|
374
|
+
# view context so it can render components. Never a helper kwarg:
|
|
375
|
+
# this is composition between two components of the gem.
|
|
376
|
+
# @api private
|
|
377
|
+
attr_writer :input_trailing
|
|
378
|
+
|
|
379
|
+
# The rendered trailing content of the input row, or nil.
|
|
380
|
+
# @api private
|
|
381
|
+
def input_trailing_html
|
|
382
|
+
@input_trailing&.call
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
# Enforces the accessible-name requirement.
|
|
386
|
+
# @api private
|
|
387
|
+
def before_render
|
|
388
|
+
return if named?
|
|
389
|
+
|
|
390
|
+
raise ArgumentError, "Command requires an accessible name for its input - pass 'aria-label' " \
|
|
391
|
+
"(or id: plus a visible label[for: \"<id>-input\"])"
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
# Stable server ids: "#{id}-input" / "#{id}-list" / "#{id}-item-<n>"
|
|
395
|
+
# (aria-activedescendant NEEDS server-stable option ids).
|
|
396
|
+
# @api private
|
|
397
|
+
def base_id
|
|
398
|
+
@base_id ||= id.presence || poetry_instance_id("poetry-command")
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
# The filter input's id.
|
|
402
|
+
# @api private
|
|
403
|
+
def input_id
|
|
404
|
+
"#{base_id}-input"
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
# The listbox's id - the input's aria-controls target.
|
|
408
|
+
# @api private
|
|
409
|
+
def list_id
|
|
410
|
+
"#{base_id}-list"
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
# The shared item registry for this render.
|
|
414
|
+
# @api private
|
|
415
|
+
def item_set
|
|
416
|
+
@item_set ||= ItemSet.new(base_id: base_id, highlight_value: value)
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
# Attributes for the palette root.
|
|
420
|
+
# @api private
|
|
421
|
+
def root_attributes
|
|
422
|
+
html_attributes.merge_if_not_set(
|
|
423
|
+
{ "id" => base_id, "data-slot" => "command" }
|
|
424
|
+
.merge(stimulus_attributes_for(:root))
|
|
425
|
+
.merge(component_data_attributes)
|
|
426
|
+
)
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
# THE combobox: aria-expanded is STATICALLY true - the listbox is
|
|
430
|
+
# always rendered and visible in bare Command (Combobox flips it on
|
|
431
|
+
# its trigger instead). The activedescendant is server-rendered
|
|
432
|
+
# from the ItemSet (populated by the template's capture pass).
|
|
433
|
+
# @api private
|
|
434
|
+
def input_attributes
|
|
435
|
+
attrs = {
|
|
436
|
+
"type" => "text", "id" => input_id, "data-slot" => "command-input",
|
|
437
|
+
"role" => "combobox", "aria-expanded" => "true", "aria-controls" => list_id,
|
|
438
|
+
"aria-autocomplete" => "list", "autocomplete" => "off", "autocorrect" => "off",
|
|
439
|
+
"spellcheck" => "false", "class" => css(:input)
|
|
440
|
+
}
|
|
441
|
+
attrs["placeholder"] = placeholder if placeholder.present?
|
|
442
|
+
attrs["disabled"] = true if disabled
|
|
443
|
+
attrs["aria-activedescendant"] = item_set.highlighted_id if item_set.highlighted_id
|
|
444
|
+
attrs.merge!(stimulus_attributes_for(:input))
|
|
445
|
+
attrs.merge!(input_aria_attributes)
|
|
446
|
+
attrs
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# Attributes for the role=listbox list.
|
|
450
|
+
# @api private
|
|
451
|
+
def list_attributes
|
|
452
|
+
{
|
|
453
|
+
"id" => list_id, "data-slot" => "command-list", "role" => "listbox",
|
|
454
|
+
"tabindex" => "-1", "aria-label" => list_label, "class" => css(:list)
|
|
455
|
+
}
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
# Zero-matches message - rendered hidden; the controller unhides it
|
|
459
|
+
# when the filter pass leaves no visible items. No role: the sr
|
|
460
|
+
# story rides the status live region.
|
|
461
|
+
# @api private
|
|
462
|
+
def empty_part
|
|
463
|
+
content_tag(:div, empty? ? empty : t("poetry.command.empty"),
|
|
464
|
+
"data-slot" => "command-empty", "hidden" => true, "class" => css(:empty))
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
# Pending affordance (role=status) - rendered hidden; the HOST
|
|
468
|
+
# toggles it.
|
|
469
|
+
# @api private
|
|
470
|
+
def loading_part
|
|
471
|
+
content_tag(:div, "data-slot" => "command-loading", "role" => "status",
|
|
472
|
+
"hidden" => true, "class" => css(:loading)) do
|
|
473
|
+
safe_join([content_tag(:span, t("poetry.command.loading"), class: css(:sr_only)),
|
|
474
|
+
loading].compact)
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
# The sr-only polite result-count region - the controller writes
|
|
479
|
+
# the debounced count from the LOCALIZED templates carried as
|
|
480
|
+
# data attributes (data-other keeps a literal %{count}
|
|
481
|
+
# placeholder), so the engine stays i18n-free.
|
|
482
|
+
# @api private
|
|
483
|
+
def status_part
|
|
484
|
+
content_tag(:span, nil,
|
|
485
|
+
"data-slot" => "command-status", "role" => "status", "aria-live" => "polite",
|
|
486
|
+
"class" => css(:status),
|
|
487
|
+
"data-zero" => t("poetry.command.results", count: 0),
|
|
488
|
+
"data-one" => t("poetry.command.results", count: 1),
|
|
489
|
+
"data-other" => t("poetry.command.results.other", count: "%{count}")) # rubocop:disable Style/FormatStringToken
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
private
|
|
493
|
+
|
|
494
|
+
def named?
|
|
495
|
+
id.present? || @input_aria["label"].present? || @input_aria["labelledby"].present?
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
# Pull the input-bound aria-* out of the root's html attributes
|
|
499
|
+
# (both flat "aria-label" and nested aria: {label:} spellings) so
|
|
500
|
+
# the accessible name wires the combobox, not the wrapper.
|
|
501
|
+
def extract_input_aria!
|
|
502
|
+
aria = {}.with_indifferent_access
|
|
503
|
+
nested = @html_attributes.delete("aria")
|
|
504
|
+
nested.each { |nested_key, nested_value| aria[nested_key] = nested_value } if nested.is_a?(Hash)
|
|
505
|
+
@html_attributes.keys.grep(/\Aaria-/).each do |flat|
|
|
506
|
+
aria[flat.delete_prefix("aria-")] = @html_attributes.delete(flat)
|
|
507
|
+
end
|
|
508
|
+
aria
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
def input_aria_attributes
|
|
512
|
+
INPUT_ARIA_KEYS.each_with_object({}) do |key, attrs|
|
|
513
|
+
attrs["aria-#{key}"] = @input_aria[key] unless @input_aria[key].nil?
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
def item_wiring
|
|
518
|
+
@item_wiring ||= stimulus_attributes_for(:item)
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
private :base_id, :input_id, :list_id, :item_set, :root_attributes, :input_attributes, :list_attributes
|
|
522
|
+
private :empty_part, :loading_part, :status_part
|
|
523
|
+
end
|
|
524
|
+
end
|
|
525
|
+
end
|
|
526
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<%= content_tag(:div, **root_attributes.to_attributes) do %>
|
|
2
|
+
<%= trigger %>
|
|
3
|
+
<%= content_tag(:dialog, **Poetry::Core::HTML::Attributes.new(dialog_attributes).to_attributes) do %>
|
|
4
|
+
<%# sr-only header (source-exact): the dialog stays labelled/described
|
|
5
|
+
while the palette owns the visible surface. %>
|
|
6
|
+
<div data-slot="dialog-header" class="sr-only">
|
|
7
|
+
<h2 id="<%= title_id %>" data-slot="dialog-title"><%= title %></h2>
|
|
8
|
+
<p id="<%= description_id %>" data-slot="dialog-description"><%= description %></p>
|
|
9
|
+
</div>
|
|
10
|
+
<%# The close X (when shown) rides INSIDE the command's input row - the
|
|
11
|
+
embedded Command renders it as the row's trailing item. %>
|
|
12
|
+
<%= render(command) %>
|
|
13
|
+
<% end %>
|
|
14
|
+
<% end %>
|