velvetkit 0.1.0.beta
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/CHANGELOG.md +16 -0
- data/LICENSE.txt +21 -0
- data/README.md +197 -0
- data/lib/generators/velvetkit/add/add_generator.rb +111 -0
- data/lib/generators/velvetkit/ai/ai_generator.rb +78 -0
- data/lib/generators/velvetkit/helpers.rb +64 -0
- data/lib/generators/velvetkit/install/install_generator.rb +108 -0
- data/lib/velvetkit/namespace.rb +57 -0
- data/lib/velvetkit/reference.rb +108 -0
- data/lib/velvetkit/registry.rb +162 -0
- data/lib/velvetkit/version.rb +5 -0
- data/lib/velvetkit.rb +28 -0
- data/registry/app/assets/tailwind/velvetkit.css +342 -0
- data/registry/app/components/ui/accordion/content.html.erb +5 -0
- data/registry/app/components/ui/accordion/content.rb +34 -0
- data/registry/app/components/ui/accordion/item.html.erb +1 -0
- data/registry/app/components/ui/accordion/item.rb +40 -0
- data/registry/app/components/ui/accordion/trigger.html.erb +1 -0
- data/registry/app/components/ui/accordion/trigger.rb +37 -0
- data/registry/app/components/ui/accordion.html.erb +7 -0
- data/registry/app/components/ui/accordion.rb +34 -0
- data/registry/app/components/ui/alert/description.html.erb +1 -0
- data/registry/app/components/ui/alert/description.rb +12 -0
- data/registry/app/components/ui/alert/title.html.erb +1 -0
- data/registry/app/components/ui/alert/title.rb +12 -0
- data/registry/app/components/ui/alert.html.erb +16 -0
- data/registry/app/components/ui/alert.rb +55 -0
- data/registry/app/components/ui/avatar/fallback.html.erb +1 -0
- data/registry/app/components/ui/avatar/fallback.rb +12 -0
- data/registry/app/components/ui/avatar/image.rb +20 -0
- data/registry/app/components/ui/avatar.html.erb +1 -0
- data/registry/app/components/ui/avatar.rb +56 -0
- data/registry/app/components/ui/avatar_group.html.erb +1 -0
- data/registry/app/components/ui/avatar_group.rb +35 -0
- data/registry/app/components/ui/badge.html.erb +1 -0
- data/registry/app/components/ui/badge.rb +38 -0
- data/registry/app/components/ui/banner.html.erb +10 -0
- data/registry/app/components/ui/banner.rb +47 -0
- data/registry/app/components/ui/base.rb +53 -0
- data/registry/app/components/ui/breadcrumb/auto_ellipsis.html.erb +23 -0
- data/registry/app/components/ui/breadcrumb/auto_ellipsis.rb +12 -0
- data/registry/app/components/ui/breadcrumb/ellipsis.html.erb +18 -0
- data/registry/app/components/ui/breadcrumb/ellipsis.rb +8 -0
- data/registry/app/components/ui/breadcrumb/item.html.erb +1 -0
- data/registry/app/components/ui/breadcrumb/item.rb +12 -0
- data/registry/app/components/ui/breadcrumb/item_slot.html.erb +10 -0
- data/registry/app/components/ui/breadcrumb/item_slot.rb +47 -0
- data/registry/app/components/ui/breadcrumb/list.html.erb +1 -0
- data/registry/app/components/ui/breadcrumb/list.rb +12 -0
- data/registry/app/components/ui/breadcrumb/separator.html.erb +1 -0
- data/registry/app/components/ui/breadcrumb/separator.rb +12 -0
- data/registry/app/components/ui/breadcrumb.html.erb +30 -0
- data/registry/app/components/ui/breadcrumb.rb +83 -0
- data/registry/app/components/ui/button.html.erb +1 -0
- data/registry/app/components/ui/button.rb +81 -0
- data/registry/app/components/ui/button_to.html.erb +1 -0
- data/registry/app/components/ui/button_to.rb +33 -0
- data/registry/app/components/ui/card/content.html.erb +1 -0
- data/registry/app/components/ui/card/content.rb +12 -0
- data/registry/app/components/ui/card/description.html.erb +1 -0
- data/registry/app/components/ui/card/description.rb +12 -0
- data/registry/app/components/ui/card/footer.html.erb +1 -0
- data/registry/app/components/ui/card/footer.rb +12 -0
- data/registry/app/components/ui/card/header.html.erb +1 -0
- data/registry/app/components/ui/card/header.rb +15 -0
- data/registry/app/components/ui/card/title.html.erb +1 -0
- data/registry/app/components/ui/card/title.rb +12 -0
- data/registry/app/components/ui/card.html.erb +1 -0
- data/registry/app/components/ui/card.rb +18 -0
- data/registry/app/components/ui/checkbox.html.erb +18 -0
- data/registry/app/components/ui/checkbox.rb +133 -0
- data/registry/app/components/ui/data_grid.html.erb +2 -0
- data/registry/app/components/ui/data_grid.rb +84 -0
- data/registry/app/components/ui/date_picker.html.erb +70 -0
- data/registry/app/components/ui/date_picker.rb +111 -0
- data/registry/app/components/ui/dialog/close.html.erb +7 -0
- data/registry/app/components/ui/dialog/close.rb +53 -0
- data/registry/app/components/ui/dialog/content.html.erb +26 -0
- data/registry/app/components/ui/dialog/content.rb +102 -0
- data/registry/app/components/ui/dialog/description.html.erb +1 -0
- data/registry/app/components/ui/dialog/description.rb +16 -0
- data/registry/app/components/ui/dialog/footer.html.erb +1 -0
- data/registry/app/components/ui/dialog/footer.rb +14 -0
- data/registry/app/components/ui/dialog/header.html.erb +1 -0
- data/registry/app/components/ui/dialog/header.rb +16 -0
- data/registry/app/components/ui/dialog/title.html.erb +1 -0
- data/registry/app/components/ui/dialog/title.rb +16 -0
- data/registry/app/components/ui/dialog/trigger.html.erb +5 -0
- data/registry/app/components/ui/dialog/trigger.rb +75 -0
- data/registry/app/components/ui/dialog.html.erb +3 -0
- data/registry/app/components/ui/dialog.rb +41 -0
- data/registry/app/components/ui/divider.html.erb +9 -0
- data/registry/app/components/ui/divider.rb +41 -0
- data/registry/app/components/ui/icon.html.erb +1 -0
- data/registry/app/components/ui/icon.rb +41 -0
- data/registry/app/components/ui/image.html.erb +1 -0
- data/registry/app/components/ui/image.rb +74 -0
- data/registry/app/components/ui/input.html.erb +9 -0
- data/registry/app/components/ui/input.rb +127 -0
- data/registry/app/components/ui/input_group/addon.html.erb +6 -0
- data/registry/app/components/ui/input_group/addon.rb +51 -0
- data/registry/app/components/ui/input_group/field.rb +26 -0
- data/registry/app/components/ui/input_group.html.erb +5 -0
- data/registry/app/components/ui/input_group.rb +55 -0
- data/registry/app/components/ui/label.html.erb +2 -0
- data/registry/app/components/ui/label.rb +48 -0
- data/registry/app/components/ui/link.html.erb +1 -0
- data/registry/app/components/ui/link.rb +64 -0
- data/registry/app/components/ui/menu/content.html.erb +5 -0
- data/registry/app/components/ui/menu/content.rb +106 -0
- data/registry/app/components/ui/menu/group.html.erb +1 -0
- data/registry/app/components/ui/menu/group.rb +19 -0
- data/registry/app/components/ui/menu/item.html.erb +1 -0
- data/registry/app/components/ui/menu/item.rb +68 -0
- data/registry/app/components/ui/menu/label.html.erb +1 -0
- data/registry/app/components/ui/menu/label.rb +33 -0
- data/registry/app/components/ui/menu/separator.html.erb +1 -0
- data/registry/app/components/ui/menu/separator.rb +20 -0
- data/registry/app/components/ui/menu/trigger.html.erb +1 -0
- data/registry/app/components/ui/menu/trigger.rb +29 -0
- data/registry/app/components/ui/menu.html.erb +3 -0
- data/registry/app/components/ui/menu.rb +23 -0
- data/registry/app/components/ui/native_select.html.erb +16 -0
- data/registry/app/components/ui/native_select.rb +109 -0
- data/registry/app/components/ui/pagination.html.erb +34 -0
- data/registry/app/components/ui/pagination.rb +62 -0
- data/registry/app/components/ui/password_input.html.erb +20 -0
- data/registry/app/components/ui/password_input.rb +94 -0
- data/registry/app/components/ui/popover/content.html.erb +12 -0
- data/registry/app/components/ui/popover/content.rb +83 -0
- data/registry/app/components/ui/popover/trigger.html.erb +1 -0
- data/registry/app/components/ui/popover/trigger.rb +29 -0
- data/registry/app/components/ui/popover.html.erb +3 -0
- data/registry/app/components/ui/popover.rb +24 -0
- data/registry/app/components/ui/radio_group/item.html.erb +18 -0
- data/registry/app/components/ui/radio_group/item.rb +97 -0
- data/registry/app/components/ui/radio_group.html.erb +1 -0
- data/registry/app/components/ui/radio_group.rb +35 -0
- data/registry/app/components/ui/select.html.erb +60 -0
- data/registry/app/components/ui/select.rb +181 -0
- data/registry/app/components/ui/sheet/close.rb +4 -0
- data/registry/app/components/ui/sheet/content.html.erb +24 -0
- data/registry/app/components/ui/sheet/content.rb +97 -0
- data/registry/app/components/ui/sheet/trigger.rb +4 -0
- data/registry/app/components/ui/sheet.html.erb +3 -0
- data/registry/app/components/ui/sheet.rb +37 -0
- data/registry/app/components/ui/sidebar/content.html.erb +1 -0
- data/registry/app/components/ui/sidebar/content.rb +18 -0
- data/registry/app/components/ui/sidebar/footer.html.erb +3 -0
- data/registry/app/components/ui/sidebar/footer.rb +16 -0
- data/registry/app/components/ui/sidebar/group.html.erb +1 -0
- data/registry/app/components/ui/sidebar/group.rb +19 -0
- data/registry/app/components/ui/sidebar/group_label.html.erb +1 -0
- data/registry/app/components/ui/sidebar/group_label.rb +18 -0
- data/registry/app/components/ui/sidebar/header.html.erb +1 -0
- data/registry/app/components/ui/sidebar/header.rb +16 -0
- data/registry/app/components/ui/sidebar/inset.html.erb +1 -0
- data/registry/app/components/ui/sidebar/inset.rb +17 -0
- data/registry/app/components/ui/sidebar/menu.html.erb +1 -0
- data/registry/app/components/ui/sidebar/menu.rb +18 -0
- data/registry/app/components/ui/sidebar/menu_button.html.erb +1 -0
- data/registry/app/components/ui/sidebar/menu_button.rb +30 -0
- data/registry/app/components/ui/sidebar/menu_item.html.erb +1 -0
- data/registry/app/components/ui/sidebar/menu_item.rb +23 -0
- data/registry/app/components/ui/sidebar/trigger.html.erb +1 -0
- data/registry/app/components/ui/sidebar/trigger.rb +24 -0
- data/registry/app/components/ui/sidebar/wrapper.html.erb +1 -0
- data/registry/app/components/ui/sidebar/wrapper.rb +40 -0
- data/registry/app/components/ui/sidebar.html.erb +11 -0
- data/registry/app/components/ui/sidebar.rb +29 -0
- data/registry/app/components/ui/spinner.html.erb +1 -0
- data/registry/app/components/ui/spinner.rb +20 -0
- data/registry/app/components/ui/switch.html.erb +12 -0
- data/registry/app/components/ui/switch.rb +109 -0
- data/registry/app/components/ui/table/cell.html.erb +1 -0
- data/registry/app/components/ui/table/cell.rb +16 -0
- data/registry/app/components/ui/table/column.html.erb +1 -0
- data/registry/app/components/ui/table/column.rb +18 -0
- data/registry/app/components/ui/table/head.html.erb +1 -0
- data/registry/app/components/ui/table/head.rb +13 -0
- data/registry/app/components/ui/table/row.html.erb +1 -0
- data/registry/app/components/ui/table/row.rb +26 -0
- data/registry/app/components/ui/table.html.erb +4 -0
- data/registry/app/components/ui/table.rb +45 -0
- data/registry/app/components/ui/tabs/content.html.erb +1 -0
- data/registry/app/components/ui/tabs/content.rb +43 -0
- data/registry/app/components/ui/tabs/list.html.erb +1 -0
- data/registry/app/components/ui/tabs/list.rb +28 -0
- data/registry/app/components/ui/tabs/trigger.html.erb +1 -0
- data/registry/app/components/ui/tabs/trigger.rb +91 -0
- data/registry/app/components/ui/tabs.html.erb +3 -0
- data/registry/app/components/ui/tabs.rb +27 -0
- data/registry/app/components/ui/text.html.erb +1 -0
- data/registry/app/components/ui/text.rb +48 -0
- data/registry/app/components/ui/theme_toggle.html.erb +9 -0
- data/registry/app/components/ui/theme_toggle.rb +63 -0
- data/registry/app/components/ui/tooltip/content.html.erb +1 -0
- data/registry/app/components/ui/tooltip/content.rb +65 -0
- data/registry/app/components/ui/tooltip/trigger.html.erb +1 -0
- data/registry/app/components/ui/tooltip/trigger.rb +33 -0
- data/registry/app/components/ui/tooltip.html.erb +3 -0
- data/registry/app/components/ui/tooltip.rb +24 -0
- data/registry/app/components/ui/typography/blockquote.html.erb +1 -0
- data/registry/app/components/ui/typography/blockquote.rb +15 -0
- data/registry/app/components/ui/typography/code.html.erb +1 -0
- data/registry/app/components/ui/typography/code.rb +25 -0
- data/registry/app/components/ui/typography/h1.rb +7 -0
- data/registry/app/components/ui/typography/h2.rb +7 -0
- data/registry/app/components/ui/typography/h3.rb +7 -0
- data/registry/app/components/ui/typography/h4.rb +7 -0
- data/registry/app/components/ui/typography/large.rb +7 -0
- data/registry/app/components/ui/typography/lead.rb +7 -0
- data/registry/app/components/ui/typography/muted.rb +7 -0
- data/registry/app/components/ui/typography/small.rb +7 -0
- data/registry/app/javascript/ag_grid.ts +26 -0
- data/registry/app/javascript/cally.ts +8 -0
- data/registry/app/javascript/controllers/ui/_internal/use_dismiss.ts +88 -0
- data/registry/app/javascript/controllers/ui/_internal/use_floating.ts +112 -0
- data/registry/app/javascript/controllers/ui/accordion_controller.ts +100 -0
- data/registry/app/javascript/controllers/ui/alert_controller.ts +7 -0
- data/registry/app/javascript/controllers/ui/banner_controller.ts +7 -0
- data/registry/app/javascript/controllers/ui/data_grid_controller.ts +198 -0
- data/registry/app/javascript/controllers/ui/date_picker_controller.ts +141 -0
- data/registry/app/javascript/controllers/ui/dialog_controller.ts +70 -0
- data/registry/app/javascript/controllers/ui/menu_controller.ts +201 -0
- data/registry/app/javascript/controllers/ui/password_visibility_controller.ts +43 -0
- data/registry/app/javascript/controllers/ui/popover_controller.ts +108 -0
- data/registry/app/javascript/controllers/ui/select_controller.ts +274 -0
- data/registry/app/javascript/controllers/ui/sidebar_controller.ts +64 -0
- data/registry/app/javascript/controllers/ui/tabs_controller.ts +74 -0
- data/registry/app/javascript/controllers/ui/theme_controller.ts +101 -0
- data/registry/app/javascript/controllers/ui/tooltip_controller.ts +102 -0
- data/registry/app/javascript/velvetkit_icons.ts +58 -0
- data/registry/app/views/layouts/_theme_script.html.erb +19 -0
- data/registry/components.yml +102 -0
- data/registry/config/locales/velvetkit.en.yml +56 -0
- data/registry/docs/agents.md +62 -0
- data/registry/test/components/previews/ui/accordion_preview/faq_style.html.erb +20 -0
- data/registry/test/components/previews/ui/accordion_preview/multiple.html.erb +27 -0
- data/registry/test/components/previews/ui/accordion_preview/single.html.erb +22 -0
- data/registry/test/components/previews/ui/accordion_preview.rb +31 -0
- data/registry/test/components/previews/ui/alert_preview/title_only.html.erb +12 -0
- data/registry/test/components/previews/ui/alert_preview/variants.html.erb +22 -0
- data/registry/test/components/previews/ui/alert_preview/with_icon.html.erb +12 -0
- data/registry/test/components/previews/ui/alert_preview/with_link.html.erb +18 -0
- data/registry/test/components/previews/ui/alert_preview.rb +34 -0
- data/registry/test/components/previews/ui/avatar_group_preview/default.html.erb +11 -0
- data/registry/test/components/previews/ui/avatar_group_preview/overflow.html.erb +23 -0
- data/registry/test/components/previews/ui/avatar_group_preview/small.html.erb +12 -0
- data/registry/test/components/previews/ui/avatar_group_preview.rb +23 -0
- data/registry/test/components/previews/ui/avatar_preview/colors.html.erb +8 -0
- data/registry/test/components/previews/ui/avatar_preview/shapes.html.erb +10 -0
- data/registry/test/components/previews/ui/avatar_preview/sizes.html.erb +8 -0
- data/registry/test/components/previews/ui/avatar_preview/with_image.html.erb +10 -0
- data/registry/test/components/previews/ui/avatar_preview.rb +24 -0
- data/registry/test/components/previews/ui/badge_preview/sizes.html.erb +5 -0
- data/registry/test/components/previews/ui/badge_preview/variants.html.erb +8 -0
- data/registry/test/components/previews/ui/badge_preview/with_icon.html.erb +14 -0
- data/registry/test/components/previews/ui/badge_preview.rb +20 -0
- data/registry/test/components/previews/ui/banner_preview/introduction.html.erb +9 -0
- data/registry/test/components/previews/ui/banner_preview/promotion.html.erb +9 -0
- data/registry/test/components/previews/ui/banner_preview/with_illustration.html.erb +12 -0
- data/registry/test/components/previews/ui/banner_preview.rb +25 -0
- data/registry/test/components/previews/ui/breadcrumb_preview/collapsed.html.erb +50 -0
- data/registry/test/components/previews/ui/breadcrumb_preview/default.html.erb +8 -0
- data/registry/test/components/previews/ui/breadcrumb_preview/long_path.html.erb +8 -0
- data/registry/test/components/previews/ui/breadcrumb_preview/medium.html.erb +7 -0
- data/registry/test/components/previews/ui/breadcrumb_preview/with_avatar.html.erb +8 -0
- data/registry/test/components/previews/ui/breadcrumb_preview.rb +66 -0
- data/registry/test/components/previews/ui/button_preview/disabled.html.erb +39 -0
- data/registry/test/components/previews/ui/button_preview/icon_only.html.erb +17 -0
- data/registry/test/components/previews/ui/button_preview/loading.html.erb +12 -0
- data/registry/test/components/previews/ui/button_preview/sizes.html.erb +6 -0
- data/registry/test/components/previews/ui/button_preview/variants.html.erb +8 -0
- data/registry/test/components/previews/ui/button_preview/with_both_icons.html.erb +12 -0
- data/registry/test/components/previews/ui/button_preview/with_leading_icon.html.erb +22 -0
- data/registry/test/components/previews/ui/button_preview/with_trailing_icon.html.erb +14 -0
- data/registry/test/components/previews/ui/button_preview.rb +65 -0
- data/registry/test/components/previews/ui/button_to_preview/states.html.erb +8 -0
- data/registry/test/components/previews/ui/button_to_preview/variants.html.erb +5 -0
- data/registry/test/components/previews/ui/button_to_preview/with_icon.html.erb +11 -0
- data/registry/test/components/previews/ui/button_to_preview.rb +21 -0
- data/registry/test/components/previews/ui/card_preview/complete.html.erb +31 -0
- data/registry/test/components/previews/ui/card_preview/default.html.erb +7 -0
- data/registry/test/components/previews/ui/card_preview/settings_form.html.erb +25 -0
- data/registry/test/components/previews/ui/card_preview/with_footer.html.erb +13 -0
- data/registry/test/components/previews/ui/card_preview/with_header.html.erb +11 -0
- data/registry/test/components/previews/ui/card_preview.rb +44 -0
- data/registry/test/components/previews/ui/checkbox_preview/in_form.html.erb +9 -0
- data/registry/test/components/previews/ui/checkbox_preview/states.html.erb +22 -0
- data/registry/test/components/previews/ui/checkbox_preview/with_label.html.erb +5 -0
- data/registry/test/components/previews/ui/checkbox_preview.rb +27 -0
- data/registry/test/components/previews/ui/data_grid_preview.rb +18 -0
- data/registry/test/components/previews/ui/date_picker_preview/default.html.erb +3 -0
- data/registry/test/components/previews/ui/date_picker_preview/states.html.erb +7 -0
- data/registry/test/components/previews/ui/date_picker_preview.rb +13 -0
- data/registry/test/components/previews/ui/dialog_preview/confirmation.html.erb +11 -0
- data/registry/test/components/previews/ui/dialog_preview/custom_trigger.html.erb +16 -0
- data/registry/test/components/previews/ui/dialog_preview/default.html.erb +15 -0
- data/registry/test/components/previews/ui/dialog_preview/long_content.html.erb +16 -0
- data/registry/test/components/previews/ui/dialog_preview/playground.html.erb +12 -0
- data/registry/test/components/previews/ui/dialog_preview/scrollable_body.html.erb +29 -0
- data/registry/test/components/previews/ui/dialog_preview/sizes.html.erb +46 -0
- data/registry/test/components/previews/ui/dialog_preview/without_close_button.html.erb +11 -0
- data/registry/test/components/previews/ui/dialog_preview/without_title.html.erb +13 -0
- data/registry/test/components/previews/ui/dialog_preview.rb +38 -0
- data/registry/test/components/previews/ui/divider_preview/horizontal.html.erb +5 -0
- data/registry/test/components/previews/ui/divider_preview/vertical.html.erb +9 -0
- data/registry/test/components/previews/ui/divider_preview/with_label.html.erb +5 -0
- data/registry/test/components/previews/ui/divider_preview.rb +19 -0
- data/registry/test/components/previews/ui/icon_preview/common.html.erb +8 -0
- data/registry/test/components/previews/ui/icon_preview/sizes.html.erb +7 -0
- data/registry/test/components/previews/ui/icon_preview.rb +20 -0
- data/registry/test/components/previews/ui/image_preview/radii.html.erb +8 -0
- data/registry/test/components/previews/ui/image_preview/sizing.html.erb +9 -0
- data/registry/test/components/previews/ui/image_preview.rb +16 -0
- data/registry/test/components/previews/ui/input_group_preview/addons.html.erb +29 -0
- data/registry/test/components/previews/ui/input_group_preview/playground.html.erb +12 -0
- data/registry/test/components/previews/ui/input_group_preview.rb +17 -0
- data/registry/test/components/previews/ui/input_preview/sizes.html.erb +5 -0
- data/registry/test/components/previews/ui/input_preview/states.html.erb +7 -0
- data/registry/test/components/previews/ui/input_preview/with_labels.html.erb +10 -0
- data/registry/test/components/previews/ui/input_preview.rb +33 -0
- data/registry/test/components/previews/ui/label_preview/sizes.html.erb +6 -0
- data/registry/test/components/previews/ui/label_preview/states.html.erb +6 -0
- data/registry/test/components/previews/ui/label_preview/weights.html.erb +5 -0
- data/registry/test/components/previews/ui/label_preview.rb +22 -0
- data/registry/test/components/previews/ui/link_preview/sizes.html.erb +5 -0
- data/registry/test/components/previews/ui/link_preview/variants.html.erb +8 -0
- data/registry/test/components/previews/ui/link_preview/with_icons.html.erb +11 -0
- data/registry/test/components/previews/ui/link_preview.rb +20 -0
- data/registry/test/components/previews/ui/menu_preview/positions.html.erb +122 -0
- data/registry/test/components/previews/ui/menu_preview/variants.html.erb +218 -0
- data/registry/test/components/previews/ui/menu_preview.rb +26 -0
- data/registry/test/components/previews/ui/native_select_preview/sizes.html.erb +6 -0
- data/registry/test/components/previews/ui/native_select_preview/states.html.erb +6 -0
- data/registry/test/components/previews/ui/native_select_preview.rb +23 -0
- data/registry/test/components/previews/ui/pagination_preview/states.html.erb +15 -0
- data/registry/test/components/previews/ui/pagination_preview.rb +15 -0
- data/registry/test/components/previews/ui/password_input_preview/states.html.erb +19 -0
- data/registry/test/components/previews/ui/password_input_preview.rb +21 -0
- data/registry/test/components/previews/ui/popover_preview/default.html.erb +7 -0
- data/registry/test/components/previews/ui/popover_preview/positions.html.erb +30 -0
- data/registry/test/components/previews/ui/popover_preview/with_close_button.html.erb +8 -0
- data/registry/test/components/previews/ui/popover_preview/with_rich_content.html.erb +17 -0
- data/registry/test/components/previews/ui/popover_preview/without_title.html.erb +9 -0
- data/registry/test/components/previews/ui/popover_preview.rb +32 -0
- data/registry/test/components/previews/ui/radio_group_preview/default.html.erb +10 -0
- data/registry/test/components/previews/ui/radio_group_preview/disabled.html.erb +17 -0
- data/registry/test/components/previews/ui/radio_group_preview/with_required.html.erb +10 -0
- data/registry/test/components/previews/ui/radio_group_preview.rb +25 -0
- data/registry/test/components/previews/ui/select_preview/default.html.erb +4 -0
- data/registry/test/components/previews/ui/select_preview/long_list.html.erb +5 -0
- data/registry/test/components/previews/ui/select_preview/multiple.html.erb +6 -0
- data/registry/test/components/previews/ui/select_preview/sizes.html.erb +5 -0
- data/registry/test/components/previews/ui/select_preview/states.html.erb +6 -0
- data/registry/test/components/previews/ui/select_preview.rb +45 -0
- data/registry/test/components/previews/ui/sheet_preview/bottom.html.erb +12 -0
- data/registry/test/components/previews/ui/sheet_preview/left.html.erb +10 -0
- data/registry/test/components/previews/ui/sheet_preview/playground.html.erb +15 -0
- data/registry/test/components/previews/ui/sheet_preview/right.html.erb +11 -0
- data/registry/test/components/previews/ui/sheet_preview/top.html.erb +8 -0
- data/registry/test/components/previews/ui/sheet_preview.rb +22 -0
- data/registry/test/components/previews/ui/sidebar_preview/_brand.html.erb +14 -0
- data/registry/test/components/previews/ui/sidebar_preview/_shell.html.erb +60 -0
- data/registry/test/components/previews/ui/sidebar_preview/collapsible.html.erb +55 -0
- data/registry/test/components/previews/ui/sidebar_preview/default.html.erb +17 -0
- data/registry/test/components/previews/ui/sidebar_preview/with_dropdown_header.html.erb +33 -0
- data/registry/test/components/previews/ui/sidebar_preview/with_groups.html.erb +22 -0
- data/registry/test/components/previews/ui/sidebar_preview.rb +15 -0
- data/registry/test/components/previews/ui/spinner_preview/in_buttons.html.erb +7 -0
- data/registry/test/components/previews/ui/spinner_preview/sizes.html.erb +7 -0
- data/registry/test/components/previews/ui/spinner_preview.rb +19 -0
- data/registry/test/components/previews/ui/switch_preview/default.html.erb +4 -0
- data/registry/test/components/previews/ui/switch_preview/sizes.html.erb +5 -0
- data/registry/test/components/previews/ui/switch_preview/states.html.erb +7 -0
- data/registry/test/components/previews/ui/switch_preview.rb +26 -0
- data/registry/test/components/previews/ui/table_preview/customers.html.erb +15 -0
- data/registry/test/components/previews/ui/table_preview/empty.html.erb +8 -0
- data/registry/test/components/previews/ui/table_preview/with_caption.html.erb +14 -0
- data/registry/test/components/previews/ui/table_preview.rb +12 -0
- data/registry/test/components/previews/ui/tabs_preview/default.html.erb +39 -0
- data/registry/test/components/previews/ui/tabs_preview/pill_variant.html.erb +18 -0
- data/registry/test/components/previews/ui/tabs_preview/settings_form.html.erb +42 -0
- data/registry/test/components/previews/ui/tabs_preview/with_icons.html.erb +34 -0
- data/registry/test/components/previews/ui/tabs_preview.rb +33 -0
- data/registry/test/components/previews/ui/text_preview/sizes.html.erb +10 -0
- data/registry/test/components/previews/ui/text_preview/tones.html.erb +5 -0
- data/registry/test/components/previews/ui/text_preview/weights.html.erb +6 -0
- data/registry/test/components/previews/ui/text_preview.rb +26 -0
- data/registry/test/components/previews/ui/theme_toggle_preview/in_a_header.html.erb +14 -0
- data/registry/test/components/previews/ui/theme_toggle_preview/palette.html.erb +47 -0
- data/registry/test/components/previews/ui/theme_toggle_preview.rb +15 -0
- data/registry/test/components/previews/ui/tooltip_preview/default.html.erb +8 -0
- data/registry/test/components/previews/ui/tooltip_preview/positions.html.erb +34 -0
- data/registry/test/components/previews/ui/tooltip_preview/with_button.html.erb +8 -0
- data/registry/test/components/previews/ui/tooltip_preview/with_icon.html.erb +10 -0
- data/registry/test/components/previews/ui/tooltip_preview.rb +29 -0
- data/registry/test/components/previews/ui/typography_preview/article.html.erb +11 -0
- data/registry/test/components/previews/ui/typography_preview/code_and_quotes.html.erb +6 -0
- data/registry/test/components/previews/ui/typography_preview/headings.html.erb +6 -0
- data/registry/test/components/previews/ui/typography_preview/text_styles.html.erb +7 -0
- data/registry/test/components/previews/ui/typography_preview.rb +23 -0
- data/registry/test/components/ui/accordion/content_test.rb +64 -0
- data/registry/test/components/ui/accordion/item_test.rb +61 -0
- data/registry/test/components/ui/accordion/trigger_test.rb +80 -0
- data/registry/test/components/ui/accordion_test.rb +96 -0
- data/registry/test/components/ui/alert/description_test.rb +37 -0
- data/registry/test/components/ui/alert/title_test.rb +38 -0
- data/registry/test/components/ui/alert_test.rb +184 -0
- data/registry/test/components/ui/avatar_group_test.rb +79 -0
- data/registry/test/components/ui/avatar_test.rb +129 -0
- data/registry/test/components/ui/badge_test.rb +112 -0
- data/registry/test/components/ui/banner_test.rb +116 -0
- data/registry/test/components/ui/base_test.rb +98 -0
- data/registry/test/components/ui/breadcrumb/ellipsis_test.rb +51 -0
- data/registry/test/components/ui/breadcrumb/item_test.rb +39 -0
- data/registry/test/components/ui/breadcrumb/list_test.rb +34 -0
- data/registry/test/components/ui/breadcrumb/separator_test.rb +36 -0
- data/registry/test/components/ui/breadcrumb_test.rb +276 -0
- data/registry/test/components/ui/button_test.rb +276 -0
- data/registry/test/components/ui/button_to_test.rb +74 -0
- data/registry/test/components/ui/card/content_test.rb +41 -0
- data/registry/test/components/ui/card/description_test.rb +32 -0
- data/registry/test/components/ui/card/footer_test.rb +34 -0
- data/registry/test/components/ui/card/header_test.rb +57 -0
- data/registry/test/components/ui/card/title_test.rb +34 -0
- data/registry/test/components/ui/card_test.rb +67 -0
- data/registry/test/components/ui/checkbox_test.rb +236 -0
- data/registry/test/components/ui/data_grid_test.rb +88 -0
- data/registry/test/components/ui/date_picker_test.rb +168 -0
- data/registry/test/components/ui/dialog/content_test.rb +156 -0
- data/registry/test/components/ui/dialog/trigger_test.rb +74 -0
- data/registry/test/components/ui/dialog_test.rb +54 -0
- data/registry/test/components/ui/divider_test.rb +69 -0
- data/registry/test/components/ui/icon_test.rb +82 -0
- data/registry/test/components/ui/image_test.rb +74 -0
- data/registry/test/components/ui/input_group_test.rb +143 -0
- data/registry/test/components/ui/input_test.rb +251 -0
- data/registry/test/components/ui/label_test.rb +96 -0
- data/registry/test/components/ui/link_test.rb +95 -0
- data/registry/test/components/ui/menu/content_test.rb +233 -0
- data/registry/test/components/ui/menu/group_test.rb +24 -0
- data/registry/test/components/ui/menu/item_test.rb +76 -0
- data/registry/test/components/ui/menu/label_test.rb +43 -0
- data/registry/test/components/ui/menu/separator_test.rb +32 -0
- data/registry/test/components/ui/menu/trigger_test.rb +109 -0
- data/registry/test/components/ui/menu_test.rb +152 -0
- data/registry/test/components/ui/native_select_test.rb +118 -0
- data/registry/test/components/ui/pagination_test.rb +92 -0
- data/registry/test/components/ui/password_input_test.rb +98 -0
- data/registry/test/components/ui/popover/content_test.rb +226 -0
- data/registry/test/components/ui/popover/trigger_test.rb +83 -0
- data/registry/test/components/ui/popover_test.rb +101 -0
- data/registry/test/components/ui/radio_group/item_test.rb +14 -0
- data/registry/test/components/ui/radio_group_test.rb +139 -0
- data/registry/test/components/ui/select_test.rb +181 -0
- data/registry/test/components/ui/sheet/content_test.rb +135 -0
- data/registry/test/components/ui/sheet/trigger_test.rb +23 -0
- data/registry/test/components/ui/sheet_test.rb +43 -0
- data/registry/test/components/ui/sidebar/menu_button_test.rb +43 -0
- data/registry/test/components/ui/sidebar/trigger_test.rb +17 -0
- data/registry/test/components/ui/sidebar/wrapper_test.rb +15 -0
- data/registry/test/components/ui/sidebar_test.rb +43 -0
- data/registry/test/components/ui/spinner_test.rb +63 -0
- data/registry/test/components/ui/switch_test.rb +206 -0
- data/registry/test/components/ui/table_test.rb +106 -0
- data/registry/test/components/ui/tabs/content_test.rb +93 -0
- data/registry/test/components/ui/tabs/list_test.rb +65 -0
- data/registry/test/components/ui/tabs/trigger_test.rb +159 -0
- data/registry/test/components/ui/tabs_test.rb +88 -0
- data/registry/test/components/ui/text_test.rb +84 -0
- data/registry/test/components/ui/theme_toggle_test.rb +88 -0
- data/registry/test/components/ui/tooltip/content_test.rb +135 -0
- data/registry/test/components/ui/tooltip/trigger_test.rb +52 -0
- data/registry/test/components/ui/tooltip_test.rb +97 -0
- data/registry/test/components/ui/typography/code_blockquote_test.rb +57 -0
- data/registry/test/components/ui/typography/heading_test.rb +71 -0
- data/registry/test/components/ui/typography/text_variants_test.rb +71 -0
- metadata +540 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Velvetkit
|
|
4
|
+
# What it takes to use one component: what it is, how to add it, its
|
|
5
|
+
# arguments and slots, its notes and working examples.
|
|
6
|
+
#
|
|
7
|
+
# Read from the registry, so it can't drift from what `add` copies, and
|
|
8
|
+
# written in the app's namespace, so an AI agent reading it writes
|
|
9
|
+
# `Kit::Button` in an app that has Kit::Button. The docs site renders it as
|
|
10
|
+
# HTML; `velvetkit:ai` and llms.txt hand it to agents as Markdown.
|
|
11
|
+
class Reference
|
|
12
|
+
Example = Data.define(:key, :title, :source)
|
|
13
|
+
|
|
14
|
+
attr_reader :name, :namespace
|
|
15
|
+
|
|
16
|
+
def initialize(name, namespace: Namespace.new(Namespace::CANONICAL), registry: Velvetkit.registry)
|
|
17
|
+
@name = name
|
|
18
|
+
@namespace = namespace
|
|
19
|
+
@registry = registry
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def title = name.humanize
|
|
23
|
+
def description = registry.description(name)
|
|
24
|
+
def add_command = "bin/rails generate velvetkit:add #{name}"
|
|
25
|
+
|
|
26
|
+
def class_name
|
|
27
|
+
constant = "#{namespace.name}::#{name.camelize}"
|
|
28
|
+
File.exist?(source_path) ? constant : "#{constant}::*"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Other components `add` brings along.
|
|
32
|
+
def dependencies
|
|
33
|
+
registry.resolve([ name ]) - [ "base", name ]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def stimulus_controllers
|
|
37
|
+
registry.stimulus_controllers(name).map do |file|
|
|
38
|
+
"#{namespace.dom}--#{File.basename(file, "_controller.ts").dasherize}"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def packages = registry.packages(name) - [ "@hotwired/stimulus" ]
|
|
43
|
+
def gems = registry.gems(name).keys
|
|
44
|
+
|
|
45
|
+
# The comment above the class, where a component explains itself.
|
|
46
|
+
def notes
|
|
47
|
+
return unless File.exist?(source_path)
|
|
48
|
+
|
|
49
|
+
lines = File.readlines(source_path).drop_while { |line| !line.start_with?("#") || line.include?("frozen_string_literal") }
|
|
50
|
+
comment = lines.take_while { |line| line.start_with?("#") }.map { |line| line.delete_prefix("#").delete_prefix(" ") }
|
|
51
|
+
namespace.rewrite(comment.join).strip.presence
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def signature
|
|
55
|
+
return unless File.exist?(source_path)
|
|
56
|
+
|
|
57
|
+
args = File.read(source_path)[/^ def initialize\((.*?)\)\n/m, 1]&.squish
|
|
58
|
+
args && "#{class_name}.new(#{args})"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def slots
|
|
62
|
+
files = Dir[File.join(registry.root, Registry::COMPONENTS, "#{name}{.rb,/**/*.rb}")]
|
|
63
|
+
files.flat_map do |file|
|
|
64
|
+
source = File.read(file)
|
|
65
|
+
declared = source.scan(/^\s*renders_(one|many) :(\w+)/).map { |kind, slot| "with_#{kind == "many" ? slot.singularize : slot}" }
|
|
66
|
+
declared + source.scan(/^\s*(?:alias_method :|def )(with_\w+)/).flatten
|
|
67
|
+
end.uniq
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Preview templates, in the order the preview class lists them.
|
|
71
|
+
# Playgrounds take Lookbook params, so they only work inside Lookbook.
|
|
72
|
+
def examples
|
|
73
|
+
labels = preview_labels
|
|
74
|
+
Dir[File.join(previews_root, "#{name}_preview", "[^_]*.html.erb")]
|
|
75
|
+
.map { |file| File.basename(file, ".html.erb") }
|
|
76
|
+
.reject { |key| key == "playground" }
|
|
77
|
+
.sort_by { |key| labels.keys.index(key) || labels.size }
|
|
78
|
+
.map { |key| Example.new(key, labels.fetch(key, key.humanize), namespace.rewrite(File.read(File.join(previews_root, "#{name}_preview", "#{key}.html.erb")))) }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def to_markdown
|
|
82
|
+
parts = [ "## #{title} (`#{class_name}`)", description ]
|
|
83
|
+
parts << "Add it: `#{add_command}`"
|
|
84
|
+
parts << "Also adds: #{dependencies.map { |d| "`#{d}`" }.join(", ")}" if dependencies.any?
|
|
85
|
+
parts << "Stimulus: #{stimulus_controllers.map { |c| "`#{c}`" }.join(", ")}" if stimulus_controllers.any?
|
|
86
|
+
parts << "Installs: #{(packages + gems).map { |p| "`#{p}`" }.join(", ")}" if (packages + gems).any?
|
|
87
|
+
parts << "### Arguments\n\n```ruby\n#{signature}\n```" if signature
|
|
88
|
+
parts << "Slots: #{slots.map { |s| "`#{s}`" }.join(", ")}" if slots.any?
|
|
89
|
+
parts << "### Notes\n\n#{notes}" if notes
|
|
90
|
+
examples.each { |example| parts << "### Example: #{example.title}\n\n```erb\n#{example.source.strip}\n```" }
|
|
91
|
+
parts.join("\n\n") + "\n"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
attr_reader :registry
|
|
97
|
+
|
|
98
|
+
def source_path = File.join(registry.root, Registry::COMPONENTS, "#{name}.rb")
|
|
99
|
+
def previews_root = File.join(registry.root, Registry::PREVIEWS)
|
|
100
|
+
|
|
101
|
+
def preview_labels
|
|
102
|
+
preview = File.join(previews_root, "#{name}_preview.rb")
|
|
103
|
+
return {} unless File.exist?(preview)
|
|
104
|
+
|
|
105
|
+
File.read(preview).scan(/# @label (.+)\n(?:\s*#.*\n)*\s*def (\w+)/).to_h { |label, method| [ method, label.strip ] }
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
require "active_support/core_ext/string/inflections"
|
|
5
|
+
|
|
6
|
+
module Velvetkit
|
|
7
|
+
# The components the gem can copy into an app, and everything each one needs.
|
|
8
|
+
#
|
|
9
|
+
# Paths are relative to the registry root and written against the canonical
|
|
10
|
+
# `UI` namespace; Velvetkit::Namespace rewrites them on the way out.
|
|
11
|
+
class Registry
|
|
12
|
+
COMPONENTS = "app/components/ui"
|
|
13
|
+
CONTROLLERS = "app/javascript/controllers/ui"
|
|
14
|
+
TESTS = "test/components/ui"
|
|
15
|
+
PREVIEWS = "test/components/previews/ui"
|
|
16
|
+
|
|
17
|
+
# Installed by `velvetkit:install` rather than `velvetkit:add`.
|
|
18
|
+
FOUNDATION = %w[
|
|
19
|
+
config/locales/velvetkit.en.yml
|
|
20
|
+
app/assets/tailwind/velvetkit.css
|
|
21
|
+
app/javascript/velvetkit_icons.ts
|
|
22
|
+
].freeze
|
|
23
|
+
FOUNDATION_PACKAGES = %w[@hotwired/stimulus lucide tw-animate-css].freeze
|
|
24
|
+
|
|
25
|
+
attr_reader :root
|
|
26
|
+
|
|
27
|
+
def initialize(root)
|
|
28
|
+
@root = root
|
|
29
|
+
@catalog = YAML.load_file(File.join(root, "components.yml"))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def names
|
|
33
|
+
@catalog.fetch("components").keys
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def description(name)
|
|
37
|
+
fetch(name).fetch("description")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def package_version(package)
|
|
41
|
+
@catalog.fetch("packages").fetch(package) do
|
|
42
|
+
raise Error, "#{package} is imported but has no version in components.yml"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# The requested components plus every component they render, base first.
|
|
47
|
+
def resolve(requested)
|
|
48
|
+
requested.each { |name| fetch(name) }
|
|
49
|
+
|
|
50
|
+
resolved = []
|
|
51
|
+
visiting = Set.new
|
|
52
|
+
visit = lambda do |name|
|
|
53
|
+
# A component already placed, or one whose dependencies are being
|
|
54
|
+
# walked right now: two components may render each other.
|
|
55
|
+
next if resolved.include?(name) || !visiting.add?(name)
|
|
56
|
+
|
|
57
|
+
dependencies(name).each(&visit)
|
|
58
|
+
resolved << name
|
|
59
|
+
end
|
|
60
|
+
([ "base" ] + requested).each(&visit)
|
|
61
|
+
resolved
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def dependencies(name)
|
|
65
|
+
sources = component_files(name) + test_files(name)
|
|
66
|
+
(references(sources) - [ name ]).sort
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Ruby, templates and any extra files listed in the catalog.
|
|
70
|
+
def component_files(name)
|
|
71
|
+
files = glob("#{COMPONENTS}/#{name}.*", "#{COMPONENTS}/#{name}/**/*")
|
|
72
|
+
files + fetch(name).fetch("files", [])
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def test_files(name)
|
|
76
|
+
glob("#{TESTS}/#{name}_test.rb", "#{TESTS}/#{name}/**/*")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def preview_files(name)
|
|
80
|
+
glob("#{PREVIEWS}/#{name}_preview.rb", "#{PREVIEWS}/#{name}_preview/**/*")
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Components a preview renders that are not in `installed`. A preview with
|
|
84
|
+
# any is skipped rather than shipped broken.
|
|
85
|
+
def preview_requires(name, installed)
|
|
86
|
+
references(preview_files(name)) - installed - [ name ]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Stimulus controllers, their shared helpers and lazily-loaded entry points.
|
|
90
|
+
def javascript_files(name)
|
|
91
|
+
sources = component_files(name)
|
|
92
|
+
controllers = scan(sources, /\bui--([a-z]+(?:-[a-z]+)*)/).filter_map { |id| controller_for(id) }
|
|
93
|
+
entry_points = scan(sources, /asset_path\(["']([a-z_]+)\.js["']\)/).map { |entry| "app/javascript/#{entry}.ts" }
|
|
94
|
+
js = (controllers + entry_points).uniq
|
|
95
|
+
internals = scan(js, %r{from ['"]\./_internal/([a-z_]+)\.js['"]}).map { |f| "#{CONTROLLERS}/_internal/#{f}.ts" }
|
|
96
|
+
js + internals.uniq
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def stimulus_controllers(name)
|
|
100
|
+
javascript_files(name).select { |f| f.start_with?(CONTROLLERS) && f.end_with?("_controller.ts") }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def packages(name)
|
|
104
|
+
scan(javascript_files(name), /(?:from|import)\s+['"]([^.'"][^'"]*)['"]/)
|
|
105
|
+
.map { |spec| spec.start_with?("@") ? spec.split("/").first(2).join("/") : spec.split("/").first }
|
|
106
|
+
.uniq.sort
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Gems the component needs, with the version to add to the Gemfile.
|
|
110
|
+
def gems(name)
|
|
111
|
+
fetch(name).fetch("gems", []).to_h { |gem| [ gem, @catalog.fetch("gems").fetch(gem) ] }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def read(file)
|
|
115
|
+
File.read(File.join(root, file))
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
def fetch(name)
|
|
121
|
+
@catalog.fetch("components").fetch(name.to_s) do
|
|
122
|
+
raise Error, "Unknown component #{name.inspect}. Available: #{names.join(', ')}"
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Components the files render. Comments don't count: a component's notes
|
|
127
|
+
# may point at another without needing it.
|
|
128
|
+
def references(files)
|
|
129
|
+
files.flat_map { |file| code(file).scan(/\bUI::([A-Z][A-Za-z0-9]*)/).flatten }
|
|
130
|
+
.map(&:underscore).select { |n| names.include?(n) }.uniq
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def code(file)
|
|
134
|
+
read(file).gsub(/^\s*#.*$/, "").gsub(/<%#.*?%>/m, "")
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# `ui--data-grid-url-value` names the data-grid controller; drop trailing
|
|
138
|
+
# words until what is left matches a controller file.
|
|
139
|
+
def controller_for(attribute)
|
|
140
|
+
words = attribute.split("-")
|
|
141
|
+
words.size.downto(1) do |n|
|
|
142
|
+
file = "#{CONTROLLERS}/#{words.first(n).join('_')}_controller.ts"
|
|
143
|
+
return file if exist?(file)
|
|
144
|
+
end
|
|
145
|
+
nil
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def scan(files, pattern)
|
|
149
|
+
files.flat_map { |file| read(file).scan(pattern).flatten }.uniq
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def glob(*patterns)
|
|
153
|
+
patterns.flat_map { |pattern| Dir.glob(pattern, base: root) }
|
|
154
|
+
.select { |f| File.file?(File.join(root, f)) }
|
|
155
|
+
.sort
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def exist?(file)
|
|
159
|
+
File.exist?(File.join(root, file))
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
data/lib/velvetkit.rb
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "velvetkit/version"
|
|
4
|
+
require "velvetkit/namespace"
|
|
5
|
+
require "velvetkit/registry"
|
|
6
|
+
require "velvetkit/reference"
|
|
7
|
+
|
|
8
|
+
# VelvetKit copies ViewComponent UI components into a Rails app, one at a time
|
|
9
|
+
# and under the namespace the app chooses. Nothing runs from the gem at
|
|
10
|
+
# runtime: once copied, the components are the app's own code.
|
|
11
|
+
module Velvetkit
|
|
12
|
+
class Error < StandardError; end
|
|
13
|
+
|
|
14
|
+
CONFIG_FILE = "config/velvetkit.yml"
|
|
15
|
+
|
|
16
|
+
def self.registry
|
|
17
|
+
@registry ||= Registry.new(File.expand_path("../registry", __dir__))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Everything an AI agent needs to build UI in an app: the rules, then a
|
|
21
|
+
# reference for each component, all in the app's namespace.
|
|
22
|
+
def self.agent_guide(components, namespace: Namespace.new(Namespace::CANONICAL))
|
|
23
|
+
guide = File.read(File.join(registry.root, "docs/agents.md"))
|
|
24
|
+
.sub("{{available}}", (registry.names - [ "base" ]).join(", "))
|
|
25
|
+
references = (components - [ "base" ]).map { |name| Reference.new(name, namespace: namespace).to_markdown }
|
|
26
|
+
([ namespace.rewrite(guide) ] + references).join("\n")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/* VelvetKit design tokens.
|
|
2
|
+
*
|
|
3
|
+
* Import this once from your Tailwind entry point, after Tailwind itself:
|
|
4
|
+
*
|
|
5
|
+
* @import "tailwindcss";
|
|
6
|
+
* @import "./velvetkit.css";
|
|
7
|
+
*
|
|
8
|
+
* Every component styles itself from these variables, so retheming the kit
|
|
9
|
+
* means changing the values below — never the components.
|
|
10
|
+
*/
|
|
11
|
+
@import 'tw-animate-css';
|
|
12
|
+
|
|
13
|
+
@custom-variant dark (&:is(.dark *));
|
|
14
|
+
|
|
15
|
+
:root {
|
|
16
|
+
line-height: 1.5;
|
|
17
|
+
font-weight: 400;
|
|
18
|
+
|
|
19
|
+
color-scheme: light;
|
|
20
|
+
|
|
21
|
+
font-synthesis: none;
|
|
22
|
+
text-rendering: optimizeLegibility;
|
|
23
|
+
-webkit-font-smoothing: antialiased;
|
|
24
|
+
-moz-osx-font-smoothing: grayscale;
|
|
25
|
+
--radius: 0.65rem;
|
|
26
|
+
--background: oklch(1 0 0);
|
|
27
|
+
--foreground: oklch(0.141 0.005 285.823);
|
|
28
|
+
--card: oklch(1 0 0);
|
|
29
|
+
--card-foreground: oklch(0.141 0.005 285.823);
|
|
30
|
+
--popover: oklch(1 0 0);
|
|
31
|
+
--popover-foreground: oklch(0.141 0.005 285.823);
|
|
32
|
+
--primary: oklch(0.357 0.158 301.6); /* #4D1A7F, velvet */
|
|
33
|
+
--primary-bolder: oklch(0.42 0.17 301.6);
|
|
34
|
+
--primary-hovered: oklch(0.42 0.17 301.6);
|
|
35
|
+
--primary-foreground: oklch(0.985 0.008 301.6);
|
|
36
|
+
--secondary: oklch(0.967 0.001 286.375);
|
|
37
|
+
--secondary-foreground: oklch(0.21 0.006 285.885);
|
|
38
|
+
--muted: oklch(0.967 0.001 286.375);
|
|
39
|
+
--muted-foreground: oklch(0.53 0.016 285.938);
|
|
40
|
+
--accent: oklch(0.967 0.001 286.375);
|
|
41
|
+
--accent-foreground: oklch(0.21 0.006 285.885);
|
|
42
|
+
--accent-orange-subtle: oklch(0.8178 0.1249 59.29);
|
|
43
|
+
--accent-green-subtle: oklch(0.8968 0.1735 157.95);
|
|
44
|
+
--accent-yellow-subtle: oklch(0.9453 0.1627 104.19);
|
|
45
|
+
--accent-purple-subtle: oklch(0.634 0.1797 274.43);
|
|
46
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
47
|
+
--border: oklch(0.92 0.004 286.32);
|
|
48
|
+
--input: oklch(0.92 0.004 286.32);
|
|
49
|
+
--ring: var(--primary);
|
|
50
|
+
--chart-pie-active: oklch(0.9137 0.1511 150.96);
|
|
51
|
+
--chart-pie-inactive: oklch(0.3533 0.0096 164.42);
|
|
52
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
53
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
54
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
55
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
56
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
57
|
+
--sidebar: oklch(0.985 0.001 286.32);
|
|
58
|
+
--sidebar-foreground: oklch(0.141 0.005 285.823);
|
|
59
|
+
/* Accent for the active nav item; lighter in dark so it clears AA against
|
|
60
|
+
the dark panel, where --primary itself would only reach 3.08:1. */
|
|
61
|
+
--sidebar-primary: oklch(0.357 0.158 301.6);
|
|
62
|
+
--sidebar-primary-foreground: var(--primary-foreground);
|
|
63
|
+
--sidebar-accent: oklch(0.97 0.001 286.375);
|
|
64
|
+
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
|
65
|
+
--sidebar-border: oklch(0.922 0.004 286.32);
|
|
66
|
+
--sidebar-ring: var(--primary);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.dark {
|
|
70
|
+
/* Native scrollbars, date pickers and other UA widgets follow the theme. */
|
|
71
|
+
color-scheme: dark;
|
|
72
|
+
|
|
73
|
+
--background: oklch(0.141 0.005 285.823);
|
|
74
|
+
--foreground: oklch(0.985 0 0);
|
|
75
|
+
--card: oklch(0.21 0.006 285.885);
|
|
76
|
+
--card-foreground: oklch(0.985 0 0);
|
|
77
|
+
--popover: oklch(0.21 0.006 285.885);
|
|
78
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
79
|
+
/* Velvet (hue 301.6), lifted from the light theme's #4D1A7F, which would
|
|
80
|
+
sit at 1.7:1 against this page. Here white text clears WCAG AA (5.2:1)
|
|
81
|
+
and the button still separates from the page (3.8:1). */
|
|
82
|
+
--primary: oklch(0.55 0.17 301.6);
|
|
83
|
+
--primary-foreground: oklch(0.985 0.008 301.6);
|
|
84
|
+
--secondary: oklch(0.274 0.006 286.033);
|
|
85
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
86
|
+
--muted: oklch(0.274 0.006 286.033);
|
|
87
|
+
--muted-foreground: oklch(0.705 0.015 286.067);
|
|
88
|
+
--accent: oklch(0.274 0.006 286.033);
|
|
89
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
90
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
91
|
+
--border: oklch(1 0 0 / 10%);
|
|
92
|
+
--input: oklch(1 0 0 / 15%);
|
|
93
|
+
--ring: var(--primary);
|
|
94
|
+
--chart-pie-active: oklch(0.696 0.17 162.48);
|
|
95
|
+
--chart-pie-inactive: oklch(0.556 0 0);
|
|
96
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
97
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
98
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
99
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
100
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
101
|
+
--sidebar: oklch(0.21 0.006 285.885);
|
|
102
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
103
|
+
--sidebar-primary: oklch(0.7 0.15 301.6);
|
|
104
|
+
--sidebar-primary-foreground: var(--primary-foreground);
|
|
105
|
+
--sidebar-accent: oklch(0.274 0.006 286.033);
|
|
106
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
107
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
108
|
+
--sidebar-ring: var(--primary);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@theme inline {
|
|
112
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
113
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
114
|
+
--radius-lg: var(--radius);
|
|
115
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
116
|
+
--color-background: var(--background);
|
|
117
|
+
--color-foreground: var(--foreground);
|
|
118
|
+
--color-card: var(--card);
|
|
119
|
+
--color-card-foreground: var(--card-foreground);
|
|
120
|
+
--color-popover: var(--popover);
|
|
121
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
122
|
+
--color-primary: var(--primary);
|
|
123
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
124
|
+
--color-secondary: var(--secondary);
|
|
125
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
126
|
+
--color-muted: var(--muted);
|
|
127
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
128
|
+
--color-accent: var(--accent);
|
|
129
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
130
|
+
--color-destructive: var(--destructive);
|
|
131
|
+
--color-border: var(--border);
|
|
132
|
+
--color-input: var(--input);
|
|
133
|
+
--color-ring: var(--ring);
|
|
134
|
+
--chart-pie-active: oklch(0.696 0.17 162.48);
|
|
135
|
+
--chart-pie-inactive: oklch(0.556 0 0);
|
|
136
|
+
--color-chart-1: var(--chart-1);
|
|
137
|
+
--color-chart-2: var(--chart-2);
|
|
138
|
+
--color-chart-3: var(--chart-3);
|
|
139
|
+
--color-chart-4: var(--chart-4);
|
|
140
|
+
--color-chart-5: var(--chart-5);
|
|
141
|
+
--color-sidebar: var(--sidebar);
|
|
142
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
143
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
144
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
145
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
146
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
147
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
148
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@layer base {
|
|
152
|
+
* {
|
|
153
|
+
@apply border-border outline-ring/50;
|
|
154
|
+
/* A ring with an offset paints the gap in this colour. Tailwind's default
|
|
155
|
+
is white, which showed as a white line inside rings in dark mode. */
|
|
156
|
+
--tw-ring-offset-color: var(--background);
|
|
157
|
+
}
|
|
158
|
+
body {
|
|
159
|
+
@apply bg-background text-foreground;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
::selection {
|
|
164
|
+
background: var(--primary);
|
|
165
|
+
color: var(--primary-foreground);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* Chrome paints autofilled fields with its own pale-blue background and
|
|
169
|
+
ignores `background-color`, which also left InputGroup two-tone: the fill
|
|
170
|
+
covered the input but not the addon. Clipping the UA background to the text
|
|
171
|
+
hides it without touching `box-shadow`, so Tailwind's focus ring (which is
|
|
172
|
+
a box-shadow) still works. The long transition defers the fill Chrome
|
|
173
|
+
re-applies on interaction. */
|
|
174
|
+
input:-webkit-autofill,
|
|
175
|
+
input:-webkit-autofill:hover,
|
|
176
|
+
input:-webkit-autofill:focus,
|
|
177
|
+
input:-webkit-autofill:active,
|
|
178
|
+
textarea:-webkit-autofill,
|
|
179
|
+
select:-webkit-autofill {
|
|
180
|
+
-webkit-background-clip: text;
|
|
181
|
+
background-clip: text;
|
|
182
|
+
-webkit-text-fill-color: var(--foreground);
|
|
183
|
+
caret-color: var(--foreground);
|
|
184
|
+
transition: background-color 100000s ease-in-out 0s;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* The date picker calendar.
|
|
188
|
+
*
|
|
189
|
+
* Cally renders in a shadow root, so it is reachable only through the custom
|
|
190
|
+
* properties and ::part() names it publishes. All of them are pointed at the
|
|
191
|
+
* kit's tokens, which is what makes the calendar follow dark mode with no
|
|
192
|
+
* JavaScript.
|
|
193
|
+
*
|
|
194
|
+
* Layout: ‹ [May ▾] [2012 ▾] › in one header row. The selects sit in Cally's
|
|
195
|
+
* heading slot (see date_picker.html.erb), so its own heading and the month's
|
|
196
|
+
* heading would only repeat them.
|
|
197
|
+
*/
|
|
198
|
+
calendar-date,
|
|
199
|
+
calendar-range {
|
|
200
|
+
--color-accent: var(--primary);
|
|
201
|
+
--color-text-on-accent: var(--primary-foreground);
|
|
202
|
+
font-family: inherit;
|
|
203
|
+
font-size: 0.875rem;
|
|
204
|
+
color: var(--popover-foreground);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
calendar-date::part(header),
|
|
208
|
+
calendar-range::part(header) {
|
|
209
|
+
display: flex;
|
|
210
|
+
align-items: center;
|
|
211
|
+
justify-content: space-between;
|
|
212
|
+
gap: 0.25rem;
|
|
213
|
+
margin-bottom: 0.5rem;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* The arrows: ghost icon buttons. */
|
|
217
|
+
calendar-date::part(button),
|
|
218
|
+
calendar-range::part(button) {
|
|
219
|
+
display: inline-grid;
|
|
220
|
+
place-items: center;
|
|
221
|
+
width: 2rem;
|
|
222
|
+
height: 2rem;
|
|
223
|
+
padding: 0;
|
|
224
|
+
border: 0;
|
|
225
|
+
border-radius: var(--radius-md);
|
|
226
|
+
background: transparent;
|
|
227
|
+
color: var(--muted-foreground);
|
|
228
|
+
cursor: pointer;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
calendar-date::part(button):hover,
|
|
232
|
+
calendar-range::part(button):hover {
|
|
233
|
+
background: var(--accent);
|
|
234
|
+
color: var(--accent-foreground);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
calendar-date::part(button):focus-visible,
|
|
238
|
+
calendar-range::part(button):focus-visible {
|
|
239
|
+
outline: 2px solid var(--ring);
|
|
240
|
+
outline-offset: 1px;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
calendar-date::part(disabled),
|
|
244
|
+
calendar-range::part(disabled) {
|
|
245
|
+
opacity: 0.35;
|
|
246
|
+
cursor: default;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
calendar-month::part(heading) {
|
|
250
|
+
display: none;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
calendar-month::part(th) {
|
|
254
|
+
padding-bottom: 0.25rem;
|
|
255
|
+
font-size: 0.75rem;
|
|
256
|
+
font-weight: 500;
|
|
257
|
+
color: var(--muted-foreground);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/* Days. */
|
|
261
|
+
calendar-month::part(button) {
|
|
262
|
+
width: 2.25rem;
|
|
263
|
+
height: 2.25rem;
|
|
264
|
+
border: 0;
|
|
265
|
+
border-radius: var(--radius-md);
|
|
266
|
+
background: transparent;
|
|
267
|
+
color: var(--popover-foreground);
|
|
268
|
+
font-size: 0.875rem;
|
|
269
|
+
cursor: pointer;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
calendar-month::part(button):hover {
|
|
273
|
+
background: var(--accent);
|
|
274
|
+
color: var(--accent-foreground);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
calendar-month::part(button):focus-visible {
|
|
278
|
+
outline: 2px solid var(--ring);
|
|
279
|
+
outline-offset: 1px;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
calendar-month::part(outside) {
|
|
283
|
+
color: var(--muted-foreground);
|
|
284
|
+
opacity: 0.6;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/* Today: a quiet tint rather than a hard box. */
|
|
288
|
+
calendar-month::part(today) {
|
|
289
|
+
outline: none;
|
|
290
|
+
background: var(--accent);
|
|
291
|
+
color: var(--primary);
|
|
292
|
+
font-weight: 600;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/* The chosen day, after today so it wins when they're the same. */
|
|
296
|
+
calendar-month::part(selected) {
|
|
297
|
+
background: var(--primary);
|
|
298
|
+
color: var(--primary-foreground);
|
|
299
|
+
font-weight: 600;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/* Month and year: the reason this library was chosen, since reaching a birth
|
|
303
|
+
year is one click rather than a hundred and seventy. Styled like the kit's
|
|
304
|
+
fields; the open list is the platform's own. */
|
|
305
|
+
calendar-select-month::part(select),
|
|
306
|
+
calendar-select-year::part(select) {
|
|
307
|
+
appearance: none;
|
|
308
|
+
height: 2rem;
|
|
309
|
+
padding: 0 1.75rem 0 0.625rem;
|
|
310
|
+
border: 1px solid var(--input);
|
|
311
|
+
border-radius: var(--radius-md);
|
|
312
|
+
background: var(--background)
|
|
313
|
+
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238a8a93' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")
|
|
314
|
+
no-repeat right 0.5rem center / 0.875rem;
|
|
315
|
+
color: var(--foreground);
|
|
316
|
+
font: inherit;
|
|
317
|
+
font-size: 0.875rem;
|
|
318
|
+
font-weight: 500;
|
|
319
|
+
cursor: pointer;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
calendar-select-month::part(select):hover,
|
|
323
|
+
calendar-select-year::part(select):hover {
|
|
324
|
+
background-color: var(--accent);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
calendar-select-month::part(select):focus-visible,
|
|
328
|
+
calendar-select-year::part(select):focus-visible {
|
|
329
|
+
outline: 2px solid var(--ring);
|
|
330
|
+
outline-offset: 1px;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* The "Month" and "Year" labels stay for screen readers only. */
|
|
334
|
+
calendar-select-month::part(label),
|
|
335
|
+
calendar-select-year::part(label) {
|
|
336
|
+
position: absolute;
|
|
337
|
+
width: 1px;
|
|
338
|
+
height: 1px;
|
|
339
|
+
overflow: hidden;
|
|
340
|
+
clip-path: inset(50%);
|
|
341
|
+
white-space: nowrap;
|
|
342
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class UI::Accordion::Content < UI::Base
|
|
4
|
+
def initialize(trigger_id: nil, class: nil, **html_attrs)
|
|
5
|
+
@trigger_id = trigger_id
|
|
6
|
+
@user_class = binding.local_variable_get(:class)
|
|
7
|
+
@html_attrs = html_attrs
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def wrapper_classes
|
|
11
|
+
cn(
|
|
12
|
+
"grid grid-rows-[0fr] transition-[grid-template-rows] duration-200",
|
|
13
|
+
"data-[state=open]:grid-rows-[1fr]",
|
|
14
|
+
@user_class
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def content_id
|
|
19
|
+
@html_attrs[:id] || "ui-accordion-content-#{SecureRandom.hex(4)}"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def html_options
|
|
23
|
+
opts = @html_attrs.dup
|
|
24
|
+
opts[:id] = content_id
|
|
25
|
+
opts[:role] = "region"
|
|
26
|
+
opts[:class] = wrapper_classes
|
|
27
|
+
opts[:"aria-labelledby"] = @trigger_id if @trigger_id
|
|
28
|
+
opts[:data] = {
|
|
29
|
+
"ui--accordion-target": "content",
|
|
30
|
+
state: "closed"
|
|
31
|
+
}.merge(opts[:data] || {})
|
|
32
|
+
opts
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= tag.div(**html_options) do %><%= trigger if trigger? %><%= accordion_content if accordion_content? %><% end %>
|