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,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Select
|
|
6
|
+
# The Select preview matrix: the ported select-demo composition, the
|
|
7
|
+
# scrollable grouped list (scroll-button case), placeholder vs
|
|
8
|
+
# valued, both trigger sizes, disabled, invalid (the Field
|
|
9
|
+
# aria-invalid chain), RTL, and the server-rendered open state.
|
|
10
|
+
class Preview < Poetry::Core::Preview::Base
|
|
11
|
+
# The select-demo port: grouped fruit, placeholder shown until a
|
|
12
|
+
# commit writes the native select + the value display together.
|
|
13
|
+
def default
|
|
14
|
+
render_component(placeholder: "Select a fruit", "aria-label": "Fruit") do |select|
|
|
15
|
+
select.with_group(label: "Fruits") do |group|
|
|
16
|
+
group.with_item(value: "apple") { "Apple" }
|
|
17
|
+
group.with_item(value: "banana") { "Banana" }
|
|
18
|
+
group.with_item(value: "blueberry") { "Blueberry" }
|
|
19
|
+
group.with_item(value: "grapes", disabled: true) { "Grapes" }
|
|
20
|
+
group.with_item(value: "pineapple") { "Pineapple" }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# select-scrollable parity: grouped timezones behind max-h - the
|
|
26
|
+
# scroll buttons appear per-direction at the extremes.
|
|
27
|
+
def scrollable
|
|
28
|
+
render_component(placeholder: "Select a timezone", "aria-label": "Timezone") do |select|
|
|
29
|
+
select.with_group(label: "North America") do |group|
|
|
30
|
+
group.with_item(value: "est") { "Eastern Standard Time (EST)" }
|
|
31
|
+
group.with_item(value: "cst") { "Central Standard Time (CST)" }
|
|
32
|
+
group.with_item(value: "mst") { "Mountain Standard Time (MST)" }
|
|
33
|
+
group.with_item(value: "pst") { "Pacific Standard Time (PST)" }
|
|
34
|
+
end
|
|
35
|
+
select.with_separator
|
|
36
|
+
select.with_group(label: "Europe & Africa") do |group|
|
|
37
|
+
group.with_item(value: "gmt") { "Greenwich Mean Time (GMT)" }
|
|
38
|
+
group.with_item(value: "cet") { "Central European Time (CET)" }
|
|
39
|
+
group.with_item(value: "eet") { "Eastern European Time (EET)" }
|
|
40
|
+
group.with_item(value: "west") { "Western European Summer Time (WEST)" }
|
|
41
|
+
end
|
|
42
|
+
select.with_separator
|
|
43
|
+
select.with_group(label: "Asia") do |group|
|
|
44
|
+
group.with_item(value: "ist") { "India Standard Time (IST)" }
|
|
45
|
+
group.with_item(value: "cst_china") { "China Standard Time (CST)" }
|
|
46
|
+
group.with_item(value: "jst") { "Japan Standard Time (JST)" }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# The server-rendered value: display label + native option selected
|
|
52
|
+
# + aria-selected/data-selected on the option, all in one pass.
|
|
53
|
+
def valued
|
|
54
|
+
render_component(value: "banana", placeholder: "Select a fruit", "aria-label": "Fruit") do |select|
|
|
55
|
+
select.with_item(value: "apple") { "Apple" }
|
|
56
|
+
select.with_item(value: "banana") { "Banana" }
|
|
57
|
+
select.with_item(value: "blueberry") { "Blueberry" }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# data-size=sm - h-8, matching Input's dense form rows.
|
|
62
|
+
def small
|
|
63
|
+
render_component(size: :sm, placeholder: "Per page", "aria-label": "Per page") do |select|
|
|
64
|
+
select.with_item(value: "10") { "10" }
|
|
65
|
+
select.with_item(value: "25") { "25" }
|
|
66
|
+
select.with_item(value: "50") { "50" }
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Disables the trigger AND the native select together.
|
|
71
|
+
def disabled
|
|
72
|
+
render_component(disabled: true, placeholder: "Select a fruit", "aria-label": "Fruit") do |select|
|
|
73
|
+
select.with_item(value: "apple") { "Apple" }
|
|
74
|
+
select.with_item(value: "banana") { "Banana" }
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# The Field error chain lands aria-invalid on the trigger - the
|
|
79
|
+
# source aria-invalid: classes fire with zero Select-side code.
|
|
80
|
+
def invalid
|
|
81
|
+
render_component(placeholder: "Choose department", required: true,
|
|
82
|
+
"aria-label": "Department", "aria-invalid": "true") do |select|
|
|
83
|
+
select.with_item(value: "eng") { "Engineering" }
|
|
84
|
+
select.with_item(value: "design") { "Design" }
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# dir=rtl flips popper alignment via the direction helper; the
|
|
89
|
+
# listbox itself is flat (Left/Right stay no-ops).
|
|
90
|
+
def rtl
|
|
91
|
+
render_component(dir: :rtl, value: "ar", "aria-label": "اللغة") do |select|
|
|
92
|
+
select.with_item(value: "ar") { "العربية" }
|
|
93
|
+
select.with_item(value: "he") { "עברית" }
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Server-rendered open state (controllable-state: the attributes
|
|
98
|
+
# are the store; the controller reconciles on connect).
|
|
99
|
+
def open
|
|
100
|
+
render_component(open: true, value: "apple", placeholder: "Select a fruit",
|
|
101
|
+
"aria-label": "Fruit") do |select|
|
|
102
|
+
select.with_group(label: "Fruits") do |group|
|
|
103
|
+
group.with_item(value: "apple") { "Apple" }
|
|
104
|
+
group.with_item(value: "banana") { "Banana" }
|
|
105
|
+
end
|
|
106
|
+
select.with_separator
|
|
107
|
+
select.with_item(value: "other") { "Other" }
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Select
|
|
6
|
+
# Style dictionary for the Select family.
|
|
7
|
+
class Style < Poetry::Core::Style
|
|
8
|
+
element :trigger, "cn-select-trigger flex w-fit items-center justify-between whitespace-nowrap " \
|
|
9
|
+
"outline-none disabled:cursor-not-allowed disabled:opacity-50 " \
|
|
10
|
+
"*:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex " \
|
|
11
|
+
"*:data-[slot=select-value]:items-center " \
|
|
12
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0"
|
|
13
|
+
|
|
14
|
+
element :content, "cn-select-content cn-menu-translucent relative z-50 max-h-(--available-height) " \
|
|
15
|
+
"origin-(--transform-origin) overflow-x-hidden overflow-y-auto " \
|
|
16
|
+
"isolate w-(--anchor-width)"
|
|
17
|
+
|
|
18
|
+
# Padding/scroll-margin are theme-owned via cn-select-viewport
|
|
19
|
+
# (sera runs p-1.5). Sizing vars stay structural.
|
|
20
|
+
# min-h on purpose, never a hard h: a hard h collapses the whole
|
|
21
|
+
# popup to trigger height (62px for a five-item list) the moment
|
|
22
|
+
# the trigger-height var is fed (goldens never see open popups, so
|
|
23
|
+
# only a human catches it).
|
|
24
|
+
element :viewport, "cn-select-viewport min-h-[var(--radix-select-trigger-height)] w-full " \
|
|
25
|
+
"min-w-[var(--radix-select-trigger-width)]"
|
|
26
|
+
|
|
27
|
+
element :item, "cn-select-item relative flex w-full cursor-default items-center outline-hidden " \
|
|
28
|
+
"select-none [&_svg]:pointer-events-none [&_svg]:shrink-0 data-disabled:opacity-50 " \
|
|
29
|
+
"data-disabled:pointer-events-none"
|
|
30
|
+
|
|
31
|
+
element :label, "cn-select-label"
|
|
32
|
+
|
|
33
|
+
element :separator, "cn-select-separator pointer-events-none"
|
|
34
|
+
|
|
35
|
+
element :scroll_button, ""
|
|
36
|
+
|
|
37
|
+
# The indicator wrapper span - the RIGHT-2 gutter, the mirror image
|
|
38
|
+
# of dropdown-menu's left gutter.
|
|
39
|
+
element :item_indicator, "cn-select-item-indicator absolute flex items-center justify-center"
|
|
40
|
+
|
|
41
|
+
# The check stays in the DOM always; the parent item's bare
|
|
42
|
+
# data-selected drives visibility (unselected = attribute ABSENCE -
|
|
43
|
+
# no data-unselected exists), so the controller's
|
|
44
|
+
# aria-selected/data-selected twin-flip is the whole toggle.
|
|
45
|
+
element :item_indicator_state, "[:not([data-selected])>&]:hidden"
|
|
46
|
+
|
|
47
|
+
# The form bubble: visually hidden but PAINTED (sr-only clips, never
|
|
48
|
+
# display:none - browser autofill heuristics skip unpainted controls).
|
|
49
|
+
element :native, "sr-only"
|
|
50
|
+
|
|
51
|
+
# The source's inline lucide icon classes, named per part.
|
|
52
|
+
element :trigger_icon, "cn-select-trigger-icon"
|
|
53
|
+
element :indicator_check, "size-4"
|
|
54
|
+
element :scroll_icon, ""
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<%# InputGroup anatomy: the real input (inert + transparent while masked),
|
|
2
|
+
the aria-hidden mask overlay (dots swapping to the reveal hint on
|
|
3
|
+
hover/focus), the trailing eye (revealed-only) and copy: affordance,
|
|
4
|
+
and the sr hint the masked group is described by. %>
|
|
5
|
+
<%= content_tag(:div, **root_attributes.to_attributes) do %>
|
|
6
|
+
<%= content_tag(:div, **group_attributes.to_attributes) do %>
|
|
7
|
+
<%= tag.input(**input_attributes.to_attributes) %>
|
|
8
|
+
<%= content_tag(:span, **mask_attributes.to_attributes) do %>
|
|
9
|
+
<span class="relative">
|
|
10
|
+
<span class="<%= css(:mask_dots) %>">••••••••</span>
|
|
11
|
+
<span class="<%= css(:mask_reveal) %>"><%= t("poetry.sensitive_input.reveal_hint") %></span>
|
|
12
|
+
</span>
|
|
13
|
+
<% end %>
|
|
14
|
+
<%= content_tag(:div, **Poetry::Core::HTML::Attributes.new(addon_attributes).to_attributes) do %>
|
|
15
|
+
<%= render toggle_button do %>
|
|
16
|
+
<%= render Poetry::Ui::Icon::Component.new(name: :"eye-off") %>
|
|
17
|
+
<% end %>
|
|
18
|
+
<% if copy %>
|
|
19
|
+
<%= render copy_button do %>
|
|
20
|
+
<span class="<%= css(:icon_stack) %>">
|
|
21
|
+
<%= render Poetry::Ui::Icon::Component.new(name: :copy, class: css(:icon_copy)) %>
|
|
22
|
+
<%= render Poetry::Ui::Icon::Component.new(name: :check, class: css(:icon_check)) %>
|
|
23
|
+
</span>
|
|
24
|
+
<% end %>
|
|
25
|
+
<% end %>
|
|
26
|
+
<% end %>
|
|
27
|
+
<% end %>
|
|
28
|
+
<%= content_tag(:span, t("poetry.sensitive_input.hint"), **hint_attributes.to_attributes) %>
|
|
29
|
+
<% end %>
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
# A secret field that stays masked until deliberately revealed.
|
|
6
|
+
module SensitiveInput
|
|
7
|
+
# A secret field - API keys, tokens, credentials - shown masked
|
|
8
|
+
# until deliberately revealed. Three states ride data-state on the
|
|
9
|
+
# root: masked (the value hidden behind an overlay that IS the
|
|
10
|
+
# reveal button, while the real input stays rendered for layout but
|
|
11
|
+
# goes inert), revealed (type=text, editable, the eye re-masks), and
|
|
12
|
+
# empty (a plain password input; the first character typed
|
|
13
|
+
# auto-reveals so composition happens visibly). Re-mask via Escape
|
|
14
|
+
# (focus returns to the group), leaving the component, or the eye.
|
|
15
|
+
# copy: adds a copy button - a real keyboard tab stop - that copies
|
|
16
|
+
# the value WITHOUT revealing it. The no-JS story is a plain
|
|
17
|
+
# password input that still submits.
|
|
18
|
+
#
|
|
19
|
+
# @example An API key with copy-without-reveal
|
|
20
|
+
# render Poetry::Ui::SensitiveInput::Component.new(name: "api_key", label: "API key",
|
|
21
|
+
# value: token, copy: true)
|
|
22
|
+
class Component < Poetry::Core::Component
|
|
23
|
+
include Poetry::Ui::InputGroupField
|
|
24
|
+
|
|
25
|
+
# Projected into the registry, llms.txt, and the agent surface.
|
|
26
|
+
AGENT_RULES = [
|
|
27
|
+
"Secrets shown-on-demand are a SensitiveInput (poetry_sensitive_input) - never a bare " \
|
|
28
|
+
"password Input with a hand-rolled eye; the masked-container contract (role=button, " \
|
|
29
|
+
"focus discipline, blur re-mask) rides the controller.",
|
|
30
|
+
"label: feeds the masked announcement (\"{label}, masked.\") - pair with a Label/Field " \
|
|
31
|
+
"for the visible caption.",
|
|
32
|
+
"copy: true adds copy-without-revealing; leave it off for password-change forms.",
|
|
33
|
+
"Values re-mask on blur BY DESIGN - do not fight it with reveal-state persistence."
|
|
34
|
+
].freeze
|
|
35
|
+
|
|
36
|
+
# Both engines declare on the root; the clipboard-text controller
|
|
37
|
+
# rides along only when copy: is set.
|
|
38
|
+
use_stimulus do
|
|
39
|
+
on :root do
|
|
40
|
+
controller :sensitive_input do
|
|
41
|
+
register
|
|
42
|
+
value :masked_label
|
|
43
|
+
value :hidden_message, from: :hidden_message_text
|
|
44
|
+
value :read_only, "true", if: :readonly
|
|
45
|
+
action :blurred, on: :focusout
|
|
46
|
+
end
|
|
47
|
+
controller :clipboard_text, if: :copy do
|
|
48
|
+
register
|
|
49
|
+
value :message, from: :copied_message_text
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
# Clicks anywhere on the bordered surface reveal (the mask's own
|
|
53
|
+
# clicks bubble here); Enter/Space ride the mask button.
|
|
54
|
+
on :group do
|
|
55
|
+
controller(:sensitive_input) { action :reveal, on: :click }
|
|
56
|
+
end
|
|
57
|
+
on :mask do
|
|
58
|
+
controller :sensitive_input do
|
|
59
|
+
target :mask
|
|
60
|
+
action :maskKeydown, on: :keydown
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
on :input do
|
|
64
|
+
controller :sensitive_input do
|
|
65
|
+
target :input
|
|
66
|
+
action :changed, on: :input
|
|
67
|
+
action :inputKeydown, on: :keydown
|
|
68
|
+
end
|
|
69
|
+
controller(:clipboard_text, if: :copy) { target :input }
|
|
70
|
+
end
|
|
71
|
+
on :toggle do
|
|
72
|
+
controller :sensitive_input do
|
|
73
|
+
target :toggle
|
|
74
|
+
action :toggle, on: :click
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
on :copy_button do
|
|
78
|
+
controller(:clipboard_text) { action :copy, on: :click }
|
|
79
|
+
end
|
|
80
|
+
# The sr hint the masked group is described by.
|
|
81
|
+
on :hint do
|
|
82
|
+
controller(:sensitive_input) { target :hint }
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
option :name, :string, required: true, doc: "The form field name on the real input."
|
|
87
|
+
option :value, :string,
|
|
88
|
+
doc: "The secret's current value; present = first paint is masked, blank = the empty state."
|
|
89
|
+
option :id, :string, doc: "The input's DOM id - what a Field label's for: must reference."
|
|
90
|
+
option :label, :string,
|
|
91
|
+
doc: "The accessible name - feeds the input's aria-label and the masked announcement (\"{label}, " \
|
|
92
|
+
"masked.\"); pair with a visible Label/Field caption."
|
|
93
|
+
option :placeholder, :string, doc: "Hint text shown while the field is empty."
|
|
94
|
+
option :described_by, :string, doc: "aria-describedby on the input - Field hint/error wiring."
|
|
95
|
+
option :copy, :boolean, default: false, doc: "Adds the copy-without-revealing button in the trailing cell."
|
|
96
|
+
option :disabled, :boolean, default: false, doc: "Disables the input and drops the masked group's tab stop."
|
|
97
|
+
option :readonly, :boolean, default: false, doc: "The value can be revealed and copied but not edited."
|
|
98
|
+
option :required, :boolean, default: false, doc: "Marks the real input required."
|
|
99
|
+
option :invalid, :boolean, default: false,
|
|
100
|
+
doc: "aria-invalid on the input - set by Field/FormBuilder from model errors."
|
|
101
|
+
|
|
102
|
+
part "sensitive-input", "Root - the state machine rides here",
|
|
103
|
+
states: {
|
|
104
|
+
"data-state" => { condition: "always - masked (value hidden, group is the reveal " \
|
|
105
|
+
"button), revealed, or empty",
|
|
106
|
+
values: %w[masked revealed empty] },
|
|
107
|
+
"data-copied" => "copy: only - stamped for a beat after a successful copy " \
|
|
108
|
+
"(the clipboard-text engine)",
|
|
109
|
+
"data-disabled" => "disabled: - the masked group loses its tab stop and pointer " \
|
|
110
|
+
"affordances"
|
|
111
|
+
}
|
|
112
|
+
part "sensitive-input-group", "The bordered field surface (InputGroup's chrome) - " \
|
|
113
|
+
"clicks anywhere on it reveal; wears the focus ring when " \
|
|
114
|
+
"the mask button inside holds focus"
|
|
115
|
+
part "input-group-control", "The real input - rendered in every state for layout " \
|
|
116
|
+
"stability; inert while masked (aria-hidden, tabindex -1, " \
|
|
117
|
+
"readonly, transparent); type=password unless revealed"
|
|
118
|
+
part "sensitive-input-mask", "The overlay painting the masked state - while masked it " \
|
|
119
|
+
"IS the reveal button (role=button, tabindex 0, " \
|
|
120
|
+
"\"{label}, masked.\", described by the sr hint; only text " \
|
|
121
|
+
"spans inside, so no nested-interactive); bullet dots swap " \
|
|
122
|
+
"to the reveal hint on hover/focus with no layout shift"
|
|
123
|
+
part "input-group-addon", "The trailing cell holding the eye (and copy: affordance)",
|
|
124
|
+
states: {
|
|
125
|
+
"data-align" => { condition: "always - inline-end holds the actions",
|
|
126
|
+
values: %w[inline-end] }
|
|
127
|
+
}
|
|
128
|
+
# The eye and copy affordances render as composed ghost Buttons
|
|
129
|
+
# carrying data-slot=sensitive-input-toggle / clipboard-text-copy
|
|
130
|
+
# on Button's root - part ownership attributes them to Button. The
|
|
131
|
+
# eye exists ONLY while revealed (hidden otherwise; the masked
|
|
132
|
+
# group is the reveal path), re-masks and hands focus back to the
|
|
133
|
+
# group; copy is a real tab stop wired to the clipboard-text
|
|
134
|
+
# engine.
|
|
135
|
+
|
|
136
|
+
# @api private
|
|
137
|
+
def hint_id
|
|
138
|
+
"#{control_id}-hint"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# @api private
|
|
142
|
+
def state
|
|
143
|
+
value.present? ? "masked" : "empty"
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# @api private
|
|
147
|
+
def masked?
|
|
148
|
+
state == "masked"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# @api private
|
|
152
|
+
def root_attributes
|
|
153
|
+
attrs = {
|
|
154
|
+
"data-slot" => "sensitive-input",
|
|
155
|
+
"data-state" => state,
|
|
156
|
+
"class" => css
|
|
157
|
+
}.merge(component_data_attributes)
|
|
158
|
+
attrs["data-disabled"] = "" if disabled
|
|
159
|
+
html_attributes.merge_if_not_set(attrs.merge(stimulus_attributes_for(:root)))
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# @api private
|
|
163
|
+
def group_attributes
|
|
164
|
+
attrs = Poetry::Core::HTML::Attributes.new(
|
|
165
|
+
"data-slot" => "sensitive-input-group",
|
|
166
|
+
"class" => InputGroup::Style.css(class: css(:group))
|
|
167
|
+
)
|
|
168
|
+
attrs.merge!(stimulus_attributes_for(:group))
|
|
169
|
+
attrs
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Fixed inline-end - the reveal/copy cell.
|
|
173
|
+
# @api private
|
|
174
|
+
def addon_attributes
|
|
175
|
+
super(:end)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# The mask overlay IS the reveal button while masked (never the
|
|
179
|
+
# group: a role=button ancestor around the inert input trips axe
|
|
180
|
+
# nested-interactive). Display:none in every other state keeps it
|
|
181
|
+
# out of the a11y tree.
|
|
182
|
+
# @api private
|
|
183
|
+
def mask_attributes
|
|
184
|
+
attrs = Poetry::Core::HTML::Attributes.new(
|
|
185
|
+
"data-slot" => "sensitive-input-mask",
|
|
186
|
+
"class" => css(:mask)
|
|
187
|
+
)
|
|
188
|
+
if masked?
|
|
189
|
+
attrs["role"] = "button"
|
|
190
|
+
attrs["tabindex"] = disabled ? "-1" : "0"
|
|
191
|
+
attrs["aria-label"] = masked_label
|
|
192
|
+
attrs["aria-describedby"] = hint_id
|
|
193
|
+
end
|
|
194
|
+
attrs.merge!(stimulus_attributes_for(:mask))
|
|
195
|
+
attrs
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# @api private
|
|
199
|
+
def input_attributes
|
|
200
|
+
attrs = Poetry::Core::HTML::Attributes.new(
|
|
201
|
+
"type" => "password",
|
|
202
|
+
"name" => name,
|
|
203
|
+
"id" => control_id,
|
|
204
|
+
"data-slot" => "input-group-control",
|
|
205
|
+
"class" => "#{Input::Style.css(class: InputGroup::Style.css(:control_input))} #{css(:input)}",
|
|
206
|
+
"autocomplete" => "off",
|
|
207
|
+
"spellcheck" => "false"
|
|
208
|
+
)
|
|
209
|
+
attrs["value"] = value if value.present?
|
|
210
|
+
attrs["placeholder"] = placeholder if placeholder.present?
|
|
211
|
+
attrs["aria-label"] = label if label.present?
|
|
212
|
+
attrs["aria-invalid"] = "true" if invalid && !disabled
|
|
213
|
+
attrs["aria-describedby"] = described_by if described_by.present?
|
|
214
|
+
attrs["disabled"] = "" if disabled
|
|
215
|
+
attrs["required"] = "" if required
|
|
216
|
+
if masked?
|
|
217
|
+
attrs["aria-hidden"] = "true"
|
|
218
|
+
attrs["tabindex"] = "-1"
|
|
219
|
+
attrs["readonly"] = ""
|
|
220
|
+
elsif readonly
|
|
221
|
+
attrs["readonly"] = ""
|
|
222
|
+
end
|
|
223
|
+
attrs.merge!(stimulus_attributes_for(:input))
|
|
224
|
+
attrs
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# The eye: exists only while revealed (the masked group is the
|
|
228
|
+
# reveal path), so the server always renders it hidden.
|
|
229
|
+
# @api private
|
|
230
|
+
def toggle_button
|
|
231
|
+
group_tool_button(slot: "sensitive-input-toggle",
|
|
232
|
+
label: t("poetry.sensitive_input.hide"),
|
|
233
|
+
tabindex: nil, extra: { "hidden" => "" },
|
|
234
|
+
wiring: stimulus_attributes_for(:toggle))
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# A REAL tab stop (no tabindex -1): copy must be keyboard-reachable.
|
|
238
|
+
# @api private
|
|
239
|
+
def copy_button
|
|
240
|
+
group_tool_button(slot: "clipboard-text-copy",
|
|
241
|
+
label: t("poetry.clipboard_text.copy"),
|
|
242
|
+
tabindex: nil,
|
|
243
|
+
wiring: stimulus_attributes_for(:copy_button))
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# @api private
|
|
247
|
+
def masked_label
|
|
248
|
+
if label.present?
|
|
249
|
+
t("poetry.sensitive_input.masked_label", label: label)
|
|
250
|
+
else
|
|
251
|
+
t("poetry.sensitive_input.masked_fallback")
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# The sr hint the masked group is described by (always rendered;
|
|
256
|
+
# only referenced while masked).
|
|
257
|
+
# @api private
|
|
258
|
+
def hint_attributes
|
|
259
|
+
attrs = Poetry::Core::HTML::Attributes.new("id" => hint_id, "class" => "sr-only")
|
|
260
|
+
attrs.merge!(stimulus_attributes_for(:hint))
|
|
261
|
+
attrs
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
private
|
|
265
|
+
|
|
266
|
+
def hidden_message_text = t("poetry.sensitive_input.hidden")
|
|
267
|
+
def copied_message_text = t("poetry.clipboard_text.copied")
|
|
268
|
+
|
|
269
|
+
private :hint_id, :state, :masked?, :root_attributes, :group_attributes, :addon_attributes, :mask_attributes
|
|
270
|
+
private :input_attributes, :toggle_button, :copy_button, :masked_label, :hint_attributes
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module SensitiveInput
|
|
6
|
+
class Preview < Poetry::Core::Preview::Base
|
|
7
|
+
# Masked: the group IS the reveal affordance (click/Enter/Space);
|
|
8
|
+
# the value re-masks on blur, Escape, or the eye.
|
|
9
|
+
def default
|
|
10
|
+
render_component(name: "api_key", label: "API key",
|
|
11
|
+
value: "sk_demo_4eC39HqLyjWDarjtT1zdp7dc")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# copy: adds copy-WITHOUT-revealing (the clipboard-text engine).
|
|
15
|
+
def with_copy
|
|
16
|
+
render_component(name: "api_key", label: "API key",
|
|
17
|
+
value: "sk_demo_4eC39HqLyjWDarjtT1zdp7dc", copy: true)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# readonly: reveal and copy still work; editing never does.
|
|
21
|
+
def readonly
|
|
22
|
+
render_component(name: "api_key", label: "API key", readonly: true,
|
|
23
|
+
value: "sk_demo_4eC39HqLyjWDarjtT1zdp7dc", copy: true)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Empty: a plain password field; the first character typed
|
|
27
|
+
# auto-reveals so composition happens visibly.
|
|
28
|
+
def empty
|
|
29
|
+
render_component(name: "new_secret", label: "New secret",
|
|
30
|
+
placeholder: "Paste the token…")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def disabled
|
|
34
|
+
render_component(name: "api_key", label: "API key",
|
|
35
|
+
value: "rotated-out", disabled: true)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module SensitiveInput
|
|
6
|
+
# InputGroup + Input + Button wear the chrome (the SearchField
|
|
7
|
+
# composition); the machine's rendering is structural utilities keyed
|
|
8
|
+
# on the root's data-state. The one theme-owned name is the group's
|
|
9
|
+
# OWN focus ring (cn-sensitive-input-group): while masked the group
|
|
10
|
+
# is the focusable button, and every theme keys its input ring on the
|
|
11
|
+
# CONTROL's focus-visible - the group needs each theme's treatment in
|
|
12
|
+
# that theme's own vocabulary (sera underlines, mira rings at 2).
|
|
13
|
+
class Style < Poetry::Core::Style
|
|
14
|
+
base "flex w-full flex-col"
|
|
15
|
+
|
|
16
|
+
# group/sensitive powers the mask's hover swap; disabled kills the
|
|
17
|
+
# pointer affordances at the root.
|
|
18
|
+
element :group, "cn-sensitive-input-group group/sensitive relative " \
|
|
19
|
+
"[[data-state=masked]:not([data-disabled])_&]:cursor-pointer " \
|
|
20
|
+
"[[data-disabled]_&]:pointer-events-none"
|
|
21
|
+
|
|
22
|
+
# The real input goes transparent (not hidden - it defines the
|
|
23
|
+
# width) and inert while masked.
|
|
24
|
+
element :input, "[[data-state=masked]_&]:pointer-events-none " \
|
|
25
|
+
"[[data-state=masked]_&]:text-transparent " \
|
|
26
|
+
"[[data-state=masked]_&]:select-none"
|
|
27
|
+
|
|
28
|
+
# The overlay paints the masked state only - and while masked it IS
|
|
29
|
+
# the reveal button (pointer target + focusable); display:none in
|
|
30
|
+
# every other state keeps it out of the a11y tree. Both texts render
|
|
31
|
+
# stacked so the hover swap never shifts layout.
|
|
32
|
+
element :mask, "pointer-events-none absolute inset-y-0 start-0 hidden items-center " \
|
|
33
|
+
"px-3 text-base outline-none md:text-sm [[data-state=masked]_&]:flex " \
|
|
34
|
+
"[[data-state=masked]:not([data-disabled])_&]:pointer-events-auto " \
|
|
35
|
+
"[[data-state=masked]:not([data-disabled])_&]:cursor-pointer"
|
|
36
|
+
element :mask_dots, "group-focus-within/sensitive:invisible group-hover/sensitive:invisible"
|
|
37
|
+
element :mask_reveal, "invisible absolute start-0 top-0 whitespace-nowrap " \
|
|
38
|
+
"text-muted-foreground group-focus-within/sensitive:visible " \
|
|
39
|
+
"group-hover/sensitive:visible"
|
|
40
|
+
|
|
41
|
+
element :icon_stack, "relative flex size-4 items-center justify-center"
|
|
42
|
+
element :icon_copy, "absolute inset-0 m-auto transition-all duration-200 " \
|
|
43
|
+
"[[data-copied]_&]:scale-0 [[data-copied]_&]:opacity-0"
|
|
44
|
+
element :icon_check, "absolute inset-0 m-auto scale-0 opacity-0 transition-all duration-200 " \
|
|
45
|
+
"[[data-copied]_&]:scale-100 [[data-copied]_&]:opacity-100"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
# A thin divider line between content regions.
|
|
6
|
+
module Separator
|
|
7
|
+
# A thin divider. Decorative by default (aria-hidden;
|
|
8
|
+
# it separates visually but adds nothing for AT). Set decorative: false
|
|
9
|
+
# for a semantic boundary (role=separator with the orientation), e.g.
|
|
10
|
+
# between toolbar groups.
|
|
11
|
+
#
|
|
12
|
+
# @example Horizontal divider between sections
|
|
13
|
+
# render Poetry::Ui::Separator::Component.new
|
|
14
|
+
class Component < Poetry::Core::Component
|
|
15
|
+
# Projected into the registry, llms.txt, and the agent surface.
|
|
16
|
+
AGENT_RULES = [
|
|
17
|
+
"A purely visual divider stays decorative (the default): aria-hidden, role absent.",
|
|
18
|
+
"Set decorative: false only when the divide is semantically meaningful (role=separator)."
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
21
|
+
option :orientation, :symbol, default: :horizontal, doc: "The divider's axis."
|
|
22
|
+
option :decorative, :boolean, default: true,
|
|
23
|
+
doc: "Whether the divide is purely visual (aria-hidden) or a semantic boundary " \
|
|
24
|
+
"(role=separator)."
|
|
25
|
+
|
|
26
|
+
validates :orientation, inclusion: { in: %i[horizontal vertical] }
|
|
27
|
+
|
|
28
|
+
part "separator", "The divider itself - decorative (aria-hidden) by default, " \
|
|
29
|
+
"role=separator when decorative: false",
|
|
30
|
+
states: {
|
|
31
|
+
"data-orientation" => { condition: "always - the resolved orientation",
|
|
32
|
+
values: %w[horizontal vertical] }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# @api private
|
|
36
|
+
def call
|
|
37
|
+
content_tag(:div, nil, **root_attributes.to_attributes)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @api private
|
|
41
|
+
def root_attributes
|
|
42
|
+
attrs = { "data-slot" => "separator", "data-orientation" => orientation }
|
|
43
|
+
if decorative
|
|
44
|
+
attrs["aria-hidden"] = "true"
|
|
45
|
+
else
|
|
46
|
+
attrs["role"] = "separator"
|
|
47
|
+
# ARIA default orientation is horizontal; only mark the exception.
|
|
48
|
+
attrs["aria-orientation"] = "vertical" if orientation == :vertical
|
|
49
|
+
end
|
|
50
|
+
html_attributes.merge_if_not_set(attrs.merge(component_data_attributes))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private :root_attributes
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Separator
|
|
6
|
+
# The Separator preview: a decorative horizontal rule, and a semantic
|
|
7
|
+
# vertical divider (role=separator).
|
|
8
|
+
class Preview < Poetry::Core::Preview::Base
|
|
9
|
+
def horizontal
|
|
10
|
+
render_component(class: "my-2")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def vertical_semantic
|
|
14
|
+
render_component(orientation: :vertical, decorative: false, class: "h-8")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Separator
|
|
6
|
+
# Style dictionary for the Separator family. The thickness/length
|
|
7
|
+
# flips on data-horizontal / data-vertical, driven by the rendered
|
|
8
|
+
# data-orientation.
|
|
9
|
+
class Style < Poetry::Core::Style
|
|
10
|
+
base "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full " \
|
|
11
|
+
"data-vertical:w-px data-vertical:self-stretch"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|