kaze 1.3.1 → 2.0.0
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 +4 -4
- data/CHANGELOG.md +72 -1
- data/README.md +1 -1
- data/lib/kaze/commands/app/hotwire_stack.rb +2 -0
- data/lib/kaze/commands/app/inertia_react_stack.rb +5 -1
- data/lib/kaze/commands/app/inertia_vue_stack.rb +5 -1
- data/lib/kaze/commands/app.rb +9 -2
- data/lib/kaze/version.rb +1 -1
- data/stubs/default/config/routes.rb +11 -4
- data/stubs/default/test/integration/appearance_test.rb +11 -0
- data/stubs/default/test/integration/password_update_test.rb +9 -1
- data/stubs/hotwire/app/assets/tailwind/application.css +123 -0
- data/stubs/hotwire/app/components/breadcrumbs_component.rb +32 -0
- data/stubs/hotwire/app/components/danger_button_component.rb +1 -1
- data/stubs/hotwire/app/components/dropdown_component.html.erb +2 -2
- data/stubs/hotwire/app/components/dropdown_component.rb +5 -1
- data/stubs/hotwire/app/components/dropdown_link_component.rb +1 -1
- data/stubs/hotwire/app/components/heading_component.rb +19 -0
- data/stubs/hotwire/app/components/icon_component.rb +30 -0
- data/stubs/hotwire/app/components/input_error_component.rb +1 -1
- data/stubs/hotwire/app/components/input_label_component.rb +3 -3
- data/stubs/hotwire/app/components/modal_component.html.erb +2 -2
- data/stubs/hotwire/app/components/primary_button_component.rb +1 -1
- data/stubs/hotwire/app/components/secondary_button_component.rb +1 -1
- data/stubs/hotwire/app/components/sidebar_menu_button_component.rb +25 -0
- data/stubs/hotwire/app/components/text_input_component.rb +1 -1
- data/stubs/hotwire/app/components/user_avatar_component.rb +27 -0
- data/stubs/hotwire/app/controllers/application_controller.rb +1 -0
- data/stubs/hotwire/app/controllers/concerns/sidebar_state.rb +14 -0
- data/stubs/hotwire/app/controllers/settings/appearance_controller.rb +7 -0
- data/stubs/hotwire/app/controllers/settings/password_controller.rb +19 -0
- data/stubs/hotwire/app/controllers/{profile_controller.rb → settings/profile_controller.rb} +3 -4
- data/stubs/hotwire/app/views/auth/login.html.erb +1 -1
- data/stubs/hotwire/app/views/dashboard/index.html.erb +6 -11
- data/stubs/hotwire/app/views/layouts/_appearance.html.erb +18 -0
- data/stubs/hotwire/app/views/layouts/_settings.html.erb +26 -0
- data/stubs/hotwire/app/views/layouts/_sidebar.html.erb +56 -0
- data/stubs/hotwire/app/views/layouts/_sidebar_header.html.erb +11 -0
- data/stubs/hotwire/app/views/layouts/_sidebar_user.html.erb +34 -0
- data/stubs/hotwire/app/views/layouts/application.html.erb +25 -15
- data/stubs/hotwire/app/views/layouts/guest.html.erb +1 -0
- data/stubs/hotwire/app/views/settings/appearance/_update_appearance_form.html.erb +28 -0
- data/stubs/hotwire/app/views/settings/appearance/edit.html.erb +10 -0
- data/stubs/hotwire/app/views/{profile/partials → settings/password}/_update_password_form.html.erb +2 -10
- data/stubs/hotwire/app/views/settings/password/edit.html.erb +10 -0
- data/stubs/hotwire/app/views/{profile/partials → settings/profile}/_confirm_user_deletion_modal.html.erb +2 -2
- data/stubs/hotwire/app/views/settings/profile/_delete_user_form.html.erb +6 -0
- data/stubs/hotwire/app/views/{profile/partials → settings/profile}/_update_profile_information_form.html.erb +4 -12
- data/stubs/hotwire/app/views/{profile → settings/profile}/destroy.turbo_stream.erb +1 -1
- data/stubs/hotwire/app/views/settings/profile/edit.html.erb +14 -0
- data/stubs/hotwire/app/views/welcome/index.html.erb +1 -0
- data/stubs/inertia-common/app/controllers/concerns/handle_inertia_requests.rb +4 -1
- data/stubs/inertia-common/app/controllers/settings/appearance_controller.rb +7 -0
- data/stubs/inertia-common/app/controllers/settings/password_controller.rb +17 -0
- data/stubs/inertia-common/app/controllers/{profile_controller.rb → settings/profile_controller.rb} +2 -2
- data/stubs/inertia-common/test/integration/appearance_test.rb +11 -0
- data/stubs/inertia-common/test/integration/password_update_test.rb +10 -2
- data/stubs/inertia-react-ts/app/assets/tailwind/application.css +124 -0
- data/stubs/inertia-react-ts/app/javascript/Components/AppLogo.tsx +17 -0
- data/stubs/inertia-react-ts/app/javascript/Components/AppShell.tsx +9 -0
- data/stubs/inertia-react-ts/app/javascript/Components/AppSidebar.tsx +65 -0
- data/stubs/inertia-react-ts/app/javascript/Components/AppSidebarHeader.tsx +14 -0
- data/stubs/inertia-react-ts/app/javascript/Components/Breadcrumbs.tsx +43 -0
- data/stubs/inertia-react-ts/app/javascript/Components/Heading.tsx +19 -0
- data/stubs/inertia-react-ts/app/javascript/Components/InputError.tsx +1 -1
- data/stubs/inertia-react-ts/app/javascript/Components/NavFooter.tsx +32 -0
- data/stubs/inertia-react-ts/app/javascript/Components/NavMain.tsx +32 -0
- data/stubs/inertia-react-ts/app/javascript/Components/NavUser.tsx +39 -0
- data/stubs/inertia-react-ts/app/javascript/Components/UserInfo.tsx +23 -0
- data/stubs/inertia-react-ts/app/javascript/Components/UserMenuContent.tsx +57 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/avatar.tsx +51 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/breadcrumb.tsx +109 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/button.tsx +49 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/checkbox.tsx +27 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/dialog.tsx +109 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/dropdown-menu.tsx +217 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/input.tsx +21 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/label.tsx +19 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/separator.tsx +26 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/sheet.tsx +137 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/sidebar.tsx +719 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/skeleton.tsx +13 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/tooltip.tsx +55 -0
- data/stubs/inertia-react-ts/app/javascript/Layouts/AuthenticatedLayout.tsx +19 -127
- data/stubs/inertia-react-ts/app/javascript/Layouts/GuestLayout.tsx +2 -2
- data/stubs/inertia-react-ts/app/javascript/Layouts/SettingsLayout.tsx +57 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ForgotPassword.tsx +6 -6
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Login.tsx +17 -12
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Register.tsx +14 -14
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ResetPassword.tsx +10 -10
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/VerifyEmail.tsx +2 -2
- data/stubs/inertia-react-ts/app/javascript/Pages/Dashboard.tsx +13 -8
- data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Appearance.tsx +33 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Partials/DeleteUserForm.tsx +107 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Partials/UpdateAppearanceForm.tsx +41 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/{Profile → Settings}/Partials/UpdatePasswordForm.tsx +12 -20
- data/stubs/inertia-react-ts/app/javascript/Pages/{Profile → Settings}/Partials/UpdateProfileInformationForm.tsx +13 -21
- data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Password.tsx +33 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Profile.tsx +35 -0
- data/stubs/inertia-react-ts/app/javascript/entrypoints/application.tsx +6 -1
- data/stubs/inertia-react-ts/app/javascript/hooks/use-initials.tsx +23 -0
- data/stubs/inertia-react-ts/app/javascript/hooks/use-mobile-navigation.tsx +11 -0
- data/stubs/inertia-react-ts/app/javascript/hooks/use-mobile.tsx +36 -0
- data/stubs/inertia-react-ts/app/javascript/lib/appearance.ts +22 -0
- data/stubs/inertia-react-ts/app/javascript/lib/utils.ts +6 -0
- data/stubs/inertia-react-ts/app/javascript/types/global.d.ts +2 -2
- data/stubs/inertia-react-ts/app/javascript/types/index.d.ts +14 -0
- data/stubs/inertia-react-ts/app/views/layouts/application.html.erb +19 -1
- data/stubs/inertia-react-ts/components.json +21 -0
- data/stubs/inertia-react-ts/package.json +13 -0
- data/stubs/inertia-vue-ts/app/assets/tailwind/application.css +124 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/AppLogo.vue +17 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/AppShell.vue +12 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/AppSidebar.vue +65 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/AppSidebarHeader.vue +20 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/Breadcrumbs.vue +32 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/Heading.vue +15 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/InputError.vue +1 -1
- data/stubs/inertia-vue-ts/app/javascript/Components/NavFooter.vue +33 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/NavMain.vue +32 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/NavUser.vue +40 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/UserInfo.vue +25 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/UserMenuContent.vue +47 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/avatar/Avatar.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/avatar/AvatarFallback.vue +21 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/avatar/AvatarImage.vue +16 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/avatar/index.ts +3 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/Breadcrumb.vue +17 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbEllipsis.vue +23 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbItem.vue +17 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbLink.vue +21 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbList.vue +17 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbPage.vue +20 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbSeparator.vue +22 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/index.ts +7 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/button/Button.vue +31 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/button/index.ts +34 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/checkbox/Checkbox.vue +35 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/checkbox/index.ts +1 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/Dialog.vue +15 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogClose.vue +12 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogContent.vue +52 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogDescription.vue +23 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogFooter.vue +25 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogHeader.vue +14 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogOverlay.vue +26 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogTitle.vue +23 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogTrigger.vue +12 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/index.ts +9 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenu.vue +15 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuContent.vue +37 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuGroup.vue +12 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuItem.vue +41 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuTrigger.vue +14 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/index.ts +7 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/input/Input.vue +50 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/input/index.ts +1 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/label/Label.vue +26 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/label/index.ts +1 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/separator/Separator.vue +29 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/separator/index.ts +1 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/Sheet.vue +19 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetClose.vue +15 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetContent.vue +62 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetDescription.vue +21 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetFooter.vue +16 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetHeader.vue +15 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetOverlay.vue +21 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetTitle.vue +21 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetTrigger.vue +15 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/index.ts +8 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/Sidebar.vue +96 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarContent.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarFooter.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarGroup.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarGroupAction.vue +27 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarGroupContent.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarGroupLabel.vue +25 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarHeader.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarInput.vue +22 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarInset.vue +21 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenu.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuAction.vue +35 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuBadge.vue +26 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuButton.vue +48 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuButtonChild.vue +36 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuItem.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuSkeleton.vue +35 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuSub.vue +22 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuSubButton.vue +36 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarProvider.vue +82 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarRail.vue +33 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarSeparator.vue +19 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarTrigger.vue +28 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/index.ts +60 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/utils.ts +19 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/skeleton/Skeleton.vue +17 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/skeleton/index.ts +1 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/tooltip/Tooltip.vue +19 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/tooltip/TooltipContent.vue +34 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/tooltip/TooltipProvider.vue +14 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/tooltip/TooltipTrigger.vue +15 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/tooltip/index.ts +4 -0
- data/stubs/inertia-vue-ts/app/javascript/Layouts/AuthenticatedLayout.vue +17 -147
- data/stubs/inertia-vue-ts/app/javascript/Layouts/SettingsLayout.vue +57 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ForgotPassword.vue +7 -7
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Login.vue +10 -12
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Register.vue +12 -14
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ResetPassword.vue +8 -10
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/VerifyEmail.vue +3 -3
- data/stubs/inertia-vue-ts/app/javascript/Pages/Dashboard.vue +15 -10
- data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Appearance.vue +33 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Partials/DeleteUserForm.vue +96 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Partials/UpdateAppearanceForm.vue +42 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/{Profile → Settings}/Partials/UpdatePasswordForm.vue +12 -20
- data/stubs/inertia-vue-ts/app/javascript/Pages/{Profile → Settings}/Partials/UpdateProfileInformationForm.vue +12 -20
- data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Password.vue +33 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Profile.vue +40 -0
- data/stubs/inertia-vue-ts/app/javascript/composables/useInitials.ts +29 -0
- data/stubs/inertia-vue-ts/app/javascript/lib/appearance.ts +22 -0
- data/stubs/inertia-vue-ts/app/javascript/lib/utils.ts +6 -0
- data/stubs/inertia-vue-ts/app/javascript/types/index.d.ts +14 -0
- data/stubs/inertia-vue-ts/app/javascript/types/vue-shims.d.ts +10 -0
- data/stubs/inertia-vue-ts/app/views/layouts/application.html.erb +19 -1
- data/stubs/inertia-vue-ts/components.json +20 -0
- data/stubs/inertia-vue-ts/package.json +8 -1
- data/stubs/inertia-vue-ts/vite.config.ts +25 -1
- metadata +174 -43
- data/stubs/hotwire/app/components/nav_link_component.rb +0 -15
- data/stubs/hotwire/app/components/responsive_nav_link_component.rb +0 -15
- data/stubs/hotwire/app/controllers/password_controller.rb +0 -13
- data/stubs/hotwire/app/views/layouts/_navigation.html.erb +0 -82
- data/stubs/hotwire/app/views/profile/edit.html.erb +0 -24
- data/stubs/hotwire/app/views/profile/partials/_delete_user_form.html.erb +0 -14
- data/stubs/inertia-common/app/controllers/password_controller.rb +0 -13
- data/stubs/inertia-react-ts/app/javascript/Components/Checkbox.tsx +0 -11
- data/stubs/inertia-react-ts/app/javascript/Components/DangerButton.tsx +0 -20
- data/stubs/inertia-react-ts/app/javascript/Components/Dropdown.tsx +0 -100
- data/stubs/inertia-react-ts/app/javascript/Components/InputLabel.tsx +0 -14
- data/stubs/inertia-react-ts/app/javascript/Components/Modal.tsx +0 -66
- data/stubs/inertia-react-ts/app/javascript/Components/NavLink.tsx +0 -21
- data/stubs/inertia-react-ts/app/javascript/Components/PrimaryButton.tsx +0 -20
- data/stubs/inertia-react-ts/app/javascript/Components/ResponsiveNavLink.tsx +0 -21
- data/stubs/inertia-react-ts/app/javascript/Components/SecondaryButton.tsx +0 -22
- data/stubs/inertia-react-ts/app/javascript/Components/TextInput.tsx +0 -32
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Edit.tsx +0 -32
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.tsx +0 -102
- data/stubs/inertia-vue-ts/app/javascript/Components/Checkbox.vue +0 -29
- data/stubs/inertia-vue-ts/app/javascript/Components/DangerButton.vue +0 -7
- data/stubs/inertia-vue-ts/app/javascript/Components/Dropdown.vue +0 -75
- data/stubs/inertia-vue-ts/app/javascript/Components/DropdownLink.vue +0 -16
- data/stubs/inertia-vue-ts/app/javascript/Components/InputLabel.vue +0 -12
- data/stubs/inertia-vue-ts/app/javascript/Components/Modal.vue +0 -109
- data/stubs/inertia-vue-ts/app/javascript/Components/NavLink.vue +0 -21
- data/stubs/inertia-vue-ts/app/javascript/Components/PrimaryButton.vue +0 -7
- data/stubs/inertia-vue-ts/app/javascript/Components/ResponsiveNavLink.vue +0 -21
- data/stubs/inertia-vue-ts/app/javascript/Components/SecondaryButton.vue +0 -19
- data/stubs/inertia-vue-ts/app/javascript/Components/TextInput.vue +0 -23
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Edit.vue +0 -38
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.vue +0 -99
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 858087e59b76bb15ec6cc06701b2ba201c4aacb12cf592cdfbc37f1304b805c1
|
|
4
|
+
data.tar.gz: ed23682c9b61226b8201240b0b43e41a660f1745d986c2f4e1b6644aa4d26003
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 888b37a89b65ee26046232a7f9dffcbe028500726c7082911ed74eea0dd4fe27f78d640661e6847d2ace7921544dfe8df67b4c28688ef6dfb00f3f7b140094a8
|
|
7
|
+
data.tar.gz: db51173dd6dc0e73686a13df9e4bc0af2afdb50fdde496c43c98860e818d64a0171b9d50142b1d824aa8a89bec4a77657642cc80edbfe072f0274a01436ec736
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,75 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [2.0.0] - 2026-10-22
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- The frontend is rebuilt on [shadcn/ui](https://ui.shadcn.com), following Laravel's [react](https://github.com/laravel/react-starter-kit) and [vue](https://github.com/laravel/vue-starter-kit) starter kits ([#27](https://github.com/cuonggt/kaze/pull/27), [#28](https://github.com/cuonggt/kaze/pull/28), [#29](https://github.com/cuonggt/kaze/pull/29), [#30](https://github.com/cuonggt/kaze/pull/30), [#31](https://github.com/cuonggt/kaze/pull/31), [#32](https://github.com/cuonggt/kaze/pull/32), [#33](https://github.com/cuonggt/kaze/pull/33), [#34](https://github.com/cuonggt/kaze/pull/34)). shadcn copies source into the project rather than shipping a package, which is how Kaze already works, so the primitives are vendored into `Components/ui` and are yours to edit.
|
|
14
|
+
|
|
15
|
+
A design token layer comes with it. Colour is now `bg-background`, `text-muted-foreground`, `border-sidebar-border` and so on, resolving through oklch variables defined once per stylesheet, instead of `bg-white dark:bg-gray-800` repeated per element. A `components.json` records the aliases so `npx shadcn@latest add <component>` lands in the right place — note that Kaze uses a capitalised `@/Components`, which that file already reflects.
|
|
16
|
+
|
|
17
|
+
Thirteen primitives for each Inertia stack: button, input, label, checkbox, dialog, dropdown-menu, separator, skeleton, sheet, tooltip, avatar, breadcrumb and sidebar, plus a `cn` helper in `lib/utils`. React builds on Radix, Vue on reka-ui.
|
|
18
|
+
|
|
19
|
+
- A collapsible sidebar replaces the top navigation bar in all three stacks. It collapses to icons, becomes a drawer below `md`, and remembers its state in a `sidebar_state` cookie that the server reads back, so the first paint matches the last choice rather than flashing open.
|
|
20
|
+
|
|
21
|
+
There is no shadcn for Hotwire, so its sidebar is hand-rolled in Alpine and ERB against the same tokens, with the lucide icons inlined through a new `IconComponent` since Hotwire has no npm.
|
|
22
|
+
|
|
23
|
+
- A settings area at `/settings/profile`, `/settings/password` and `/settings/appearance`, replacing the single profile page ([#35](https://github.com/cuonggt/kaze/pull/35), [#36](https://github.com/cuonggt/kaze/pull/36)). `/settings` redirects to the profile page. Each has its own breadcrumb and page title, and the delete-account form stays on the profile page.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **The profile page has moved and its routes with it.** `/profile` is now `/settings/profile`, and password updates post to `/settings/password` rather than to the profile page. The route helper names are unchanged — `profile_edit_path` still exists, it just points somewhere new — so an application that uses the helpers rather than hard-coded paths needs no edit. `password_edit_path` and `appearance_edit_path` are new. `ProfileController` and `PasswordController` become `Settings::ProfileController`, `Settings::PasswordController` and `Settings::AppearanceController`.
|
|
28
|
+
|
|
29
|
+
- The `dark` variant is redefined, in all three stylesheets:
|
|
30
|
+
|
|
31
|
+
```css
|
|
32
|
+
/* was */ @custom-variant dark (&:where(.dark, .dark *));
|
|
33
|
+
/* now */ @custom-variant dark (&:is(.dark *));
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`:where()` contributes no specificity, so a `dark:` utility tied with its light counterpart and won or lost on the order Tailwind happened to emit them in — `dark:text-black` lost to `text-white` on the same element. `:is()` gives the variant the higher specificity, which is what Laravel's kits use. If you have written a `dark:` override that was silently losing, it will now apply; if you wrote one that depended on losing, it will not.
|
|
37
|
+
|
|
38
|
+
- The Vue stack pins `typescript` to `^5.9.3`, down from `^7.0.2` ([#30](https://github.com/cuonggt/kaze/pull/30)). `@vue/compiler-sfc` needs filesystem access to resolve prop types that cross a package boundary, which the vendored primitives do, and the resolver it uses is not compatible with TypeScript 7. The React stack stays on 7.
|
|
39
|
+
|
|
40
|
+
### Removed
|
|
41
|
+
|
|
42
|
+
- The hand-rolled components the shadcn primitives replace: `PrimaryButton`, `SecondaryButton`, `DangerButton`, `TextInput`, `InputLabel`, `Checkbox`, `Modal`, `Dropdown`, `NavLink` and `ResponsiveNavLink` from both Inertia stacks, `DropdownLink` from the Vue stack, and `NavLinkComponent` and `ResponsiveNavLinkComponent` from Hotwire. The buttons, inputs and labels map onto `Button`, `Input` and `Label` with a variant; the navigation links have no successor, because the sidebar replaced what used them.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- `kaze install` exits non-zero when it fails. Every step is guarded, so the installer already stopped at the first failure and withheld its success message — but the process still exited 0, so a script or CI job saw a green step over a half-installed application. This is the same class of silent failure as [#11](https://github.com/cuonggt/kaze/issues/11), in the exit code rather than the output. Anything that runs `kaze install` unattended and currently passes over a failure will now fail, which is the point.
|
|
47
|
+
|
|
48
|
+
### Upgrading
|
|
49
|
+
|
|
50
|
+
Kaze scaffolds rather than vendors, so nothing changes under an existing application on its own. Re-running `kaze install` over one built with 1.4.0 or earlier does **not** remove what this release replaced — the installer copies files in and never deletes, deliberately, since by then they are yours and may be edited. The replaced files are left behind for you to delete:
|
|
51
|
+
|
|
52
|
+
| stack | left behind |
|
|
53
|
+
|---|---|
|
|
54
|
+
| all | `app/controllers/profile_controller.rb`, `app/controllers/password_controller.rb` |
|
|
55
|
+
| react, vue | `app/javascript/Components/{PrimaryButton,SecondaryButton,DangerButton,TextInput,InputLabel,Checkbox,Modal,Dropdown,NavLink,ResponsiveNavLink}`, `DropdownLink` on vue, and the whole of `app/javascript/Pages/Profile/` |
|
|
56
|
+
| hotwire | `app/components/{nav_link,responsive_nav_link}_component.rb`, `app/views/layouts/_navigation.html.erb`, and the whole of `app/views/profile/` |
|
|
57
|
+
|
|
58
|
+
On the React stack this is not optional: `tsc` typechecks every file under `app/javascript`, and the stale `Pages/Profile/Edit.tsx` still passes a `header` prop that the layout no longer takes, so the build fails until that directory is gone. Vue and Hotwire build and run with the stale files in place — they are dead weight rather than a break, since nothing routes to them any more.
|
|
59
|
+
|
|
60
|
+
## [1.4.0] - 2026-10-21
|
|
61
|
+
|
|
62
|
+
### Added
|
|
63
|
+
|
|
64
|
+
- An appearance preference — light, dark or system — on the profile page of all three stacks ([#26](https://github.com/cuonggt/kaze/pull/26)). The stubs already carried 315 `dark:` classes, but nothing could reach them: `dark:` followed `prefers-color-scheme`, so an application took whatever the operating system said. The choice is stored in `localStorage`, and system follows the device and tracks it live.
|
|
65
|
+
|
|
66
|
+
Each stylesheet now redefines the variant, since Tailwind v4 no longer configures dark mode in JavaScript:
|
|
67
|
+
|
|
68
|
+
```css
|
|
69
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The layouts apply the resulting `dark` class in an inline `<head>` script before first paint, so the page never flashes the wrong theme. A consequence worth knowing: the theme now depends on JavaScript, so with scripting disabled an application always renders light where it previously followed the operating system.
|
|
73
|
+
|
|
74
|
+
### Fixed
|
|
75
|
+
|
|
76
|
+
- Dark mode gaps that were already broken for anyone whose operating system was set to dark, and which the preference control made obvious. React's `AuthenticatedLayout` carried one `dark:` class where Vue's had fifteen, leaving a white navigation bar and header above a dark page; the missing variants are ported from the Vue layout. Four wrappers lacked `dark:bg-gray-900`, leaving a light grey page behind dark cards — hotwire's application layout, React's `AuthenticatedLayout` and `GuestLayout`, and the Inertia ERB wrappers for both stacks.
|
|
77
|
+
|
|
9
78
|
## [1.3.1] - 2026-10-21
|
|
10
79
|
|
|
11
80
|
### Changed
|
|
@@ -108,7 +177,9 @@ Together these take a freshly scaffolded Inertia application from seven npm advi
|
|
|
108
177
|
|
|
109
178
|
Releases before 1.0.0 are not catalogued here. See the [tags](https://github.com/cuonggt/kaze/tags) for their history.
|
|
110
179
|
|
|
111
|
-
[Unreleased]: https://github.com/cuonggt/kaze/compare/
|
|
180
|
+
[Unreleased]: https://github.com/cuonggt/kaze/compare/v2.0.0...HEAD
|
|
181
|
+
[2.0.0]: https://github.com/cuonggt/kaze/compare/v1.4.0...v2.0.0
|
|
182
|
+
[1.4.0]: https://github.com/cuonggt/kaze/compare/v1.3.1...v1.4.0
|
|
112
183
|
[1.3.1]: https://github.com/cuonggt/kaze/compare/v1.3.0...v1.3.1
|
|
113
184
|
[1.3.0]: https://github.com/cuonggt/kaze/compare/v1.2.1...v1.3.0
|
|
114
185
|
[1.2.1]: https://github.com/cuonggt/kaze/compare/v1.2.0...v1.2.1
|
data/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Heavily inspired by [Laravel Breeze](https://github.com/laravel/breeze), this gem offers authentication and application starter kits to give you a head start building your new [Rails](https://rubyonrails.org) application. These kits automatically scaffold your application with the routes, controllers, and views you need to register and authenticate your application's users.
|
|
6
6
|
|
|
7
|
-
[Kaze](https://github.com/cuonggt/kaze) is a opinionated minimal, simple implementation of all authentication features that a modern web application should have, including login, registration, password reset, email verification. In addition, Kaze includes a
|
|
7
|
+
[Kaze](https://github.com/cuonggt/kaze) is a opinionated minimal, simple implementation of all authentication features that a modern web application should have, including login, registration, password reset, email verification. In addition, Kaze includes a settings area where the user may update their name and email address, change their password, choose an appearance, and delete their account.
|
|
8
8
|
|
|
9
9
|
Kaze provides scaffolding options based on [Hotwire](https://hotwired.dev) or [Inertia](https://inertiajs.com), with the choice of using Vue or React for the Inertia-based scaffolding.
|
|
10
10
|
|
|
@@ -56,6 +56,7 @@ class Kaze::Commands::App::InertiaReactStack < Kaze::Commands::App::BaseStack
|
|
|
56
56
|
FileUtils.copy_file("#{stubs_path}/inertia-common/config/vite.json", "#{Dir.pwd}/config/vite.json")
|
|
57
57
|
ensure_directory_exists("#{Dir.pwd}/config/initializers")
|
|
58
58
|
FileUtils.copy_file("#{stubs_path}/inertia-common/config/initializers/inertia_rails.rb", "#{Dir.pwd}/config/initializers/inertia_rails.rb")
|
|
59
|
+
FileUtils.copy_file("#{stubs_path}/inertia-react-ts/components.json", "#{Dir.pwd}/components.json")
|
|
59
60
|
FileUtils.copy_file("#{stubs_path}/inertia-react-ts/tsconfig.json", "#{Dir.pwd}/tsconfig.json")
|
|
60
61
|
FileUtils.copy_file("#{stubs_path}/inertia-react-ts/vite.config.ts", "#{Dir.pwd}/vite.config.ts")
|
|
61
62
|
FileUtils.copy_file("#{stubs_path}/default/bin/dev", "#{Dir.pwd}/bin/dev")
|
|
@@ -63,7 +64,8 @@ class Kaze::Commands::App::InertiaReactStack < Kaze::Commands::App::BaseStack
|
|
|
63
64
|
FileUtils.copy_file("#{stubs_path}/default/Procfile.dev", "#{Dir.pwd}/Procfile.dev")
|
|
64
65
|
replace_in_file('#vite: bin/vite dev', 'vite: bin/vite dev', "#{Dir.pwd}/Procfile.dev")
|
|
65
66
|
run_command("#{Dir.pwd}/bin/rails generate js_routes:middleware")
|
|
66
|
-
|
|
67
|
+
# The CSS is built by `npm run build` below, once node_modules exists --
|
|
68
|
+
# the stylesheet imports tw-animate-css, which cannot resolve before then.
|
|
67
69
|
|
|
68
70
|
say ''
|
|
69
71
|
say 'Installing and building Node dependencies.', :magenta
|
|
@@ -83,5 +85,7 @@ class Kaze::Commands::App::InertiaReactStack < Kaze::Commands::App::BaseStack
|
|
|
83
85
|
|
|
84
86
|
say ''
|
|
85
87
|
say 'Kaze scaffolding installed successfully.', :green
|
|
88
|
+
|
|
89
|
+
true
|
|
86
90
|
end
|
|
87
91
|
end
|
|
@@ -56,6 +56,7 @@ class Kaze::Commands::App::InertiaVueStack < Kaze::Commands::App::BaseStack
|
|
|
56
56
|
FileUtils.copy_file("#{stubs_path}/inertia-common/config/vite.json", "#{Dir.pwd}/config/vite.json")
|
|
57
57
|
ensure_directory_exists("#{Dir.pwd}/config/initializers")
|
|
58
58
|
FileUtils.copy_file("#{stubs_path}/inertia-common/config/initializers/inertia_rails.rb", "#{Dir.pwd}/config/initializers/inertia_rails.rb")
|
|
59
|
+
FileUtils.copy_file("#{stubs_path}/inertia-vue-ts/components.json", "#{Dir.pwd}/components.json")
|
|
59
60
|
FileUtils.copy_file("#{stubs_path}/inertia-vue-ts/tsconfig.json", "#{Dir.pwd}/tsconfig.json")
|
|
60
61
|
FileUtils.copy_file("#{stubs_path}/inertia-vue-ts/vite.config.ts", "#{Dir.pwd}/vite.config.ts")
|
|
61
62
|
FileUtils.copy_file("#{stubs_path}/default/bin/dev", "#{Dir.pwd}/bin/dev")
|
|
@@ -63,7 +64,8 @@ class Kaze::Commands::App::InertiaVueStack < Kaze::Commands::App::BaseStack
|
|
|
63
64
|
FileUtils.copy_file("#{stubs_path}/default/Procfile.dev", "#{Dir.pwd}/Procfile.dev")
|
|
64
65
|
replace_in_file('#vite: bin/vite dev', 'vite: bin/vite dev', "#{Dir.pwd}/Procfile.dev")
|
|
65
66
|
run_command("#{Dir.pwd}/bin/rails generate js_routes:middleware")
|
|
66
|
-
|
|
67
|
+
# The CSS is built by `npm run build` below, once node_modules exists --
|
|
68
|
+
# the stylesheet imports tw-animate-css, which cannot resolve before then.
|
|
67
69
|
|
|
68
70
|
say ''
|
|
69
71
|
say 'Installing and building Node dependencies.', :magenta
|
|
@@ -83,5 +85,7 @@ class Kaze::Commands::App::InertiaVueStack < Kaze::Commands::App::BaseStack
|
|
|
83
85
|
|
|
84
86
|
say ''
|
|
85
87
|
say 'Kaze scaffolding installed successfully.', :green
|
|
88
|
+
|
|
89
|
+
true
|
|
86
90
|
end
|
|
87
91
|
end
|
data/lib/kaze/commands/app.rb
CHANGED
|
@@ -7,11 +7,18 @@ class Kaze::Commands::App < Thor
|
|
|
7
7
|
|
|
8
8
|
desc 'install [STACK]', 'Install the Kaze controllers and resources. Supported stacks: hotwire, react, vue.'
|
|
9
9
|
def install(stack = 'hotwire')
|
|
10
|
-
|
|
10
|
+
unless File.exist?("#{Dir.pwd}/bin/rails")
|
|
11
|
+
say 'Kaze must be run in a new Rails application.', :red
|
|
12
|
+
exit 1
|
|
13
|
+
end
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
# The stack installers stop at the first step that fails, so a falsy return
|
|
16
|
+
# means the application is half-installed. Exiting non-zero keeps that from
|
|
17
|
+
# passing silently in a script or a CI job.
|
|
18
|
+
exit 1 unless StackFactory.make(stack.to_sym).install
|
|
13
19
|
rescue => e
|
|
14
20
|
say e.message, :red
|
|
21
|
+
exit 1
|
|
15
22
|
end
|
|
16
23
|
|
|
17
24
|
desc 'version', 'Show Kaze version'
|
data/lib/kaze/version.rb
CHANGED
|
@@ -23,9 +23,16 @@ Rails.application.routes.draw do
|
|
|
23
23
|
|
|
24
24
|
get 'dashboard', to: 'dashboard#index', as: :dashboard
|
|
25
25
|
|
|
26
|
-
get '
|
|
27
|
-
patch 'profile', to: 'profile#update', as: :profile_update
|
|
28
|
-
delete 'profile', to: 'profile#destroy', as: :profile_destroy
|
|
26
|
+
get 'settings', to: redirect('/settings/profile')
|
|
29
27
|
|
|
30
|
-
|
|
28
|
+
scope 'settings', module: 'settings' do
|
|
29
|
+
get 'profile', to: 'profile#edit', as: :profile_edit
|
|
30
|
+
patch 'profile', to: 'profile#update', as: :profile_update
|
|
31
|
+
delete 'profile', to: 'profile#destroy', as: :profile_destroy
|
|
32
|
+
|
|
33
|
+
get 'password', to: 'password#edit', as: :password_edit
|
|
34
|
+
put 'password', to: 'password#update', as: :password_update
|
|
35
|
+
|
|
36
|
+
get 'appearance', to: 'appearance#edit', as: :appearance_edit
|
|
37
|
+
end
|
|
31
38
|
end
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
require 'test_helper'
|
|
2
2
|
|
|
3
3
|
class PasswordUpdateTest < ActionDispatch::IntegrationTest
|
|
4
|
+
test 'password page is displayed' do
|
|
5
|
+
user = FactoryBot.create(:user)
|
|
6
|
+
|
|
7
|
+
acting_as(user).get password_edit_path
|
|
8
|
+
|
|
9
|
+
assert_response :success
|
|
10
|
+
end
|
|
11
|
+
|
|
4
12
|
test 'password can be updated' do
|
|
5
13
|
user = FactoryBot.create(:user)
|
|
6
14
|
|
|
@@ -10,7 +18,7 @@ class PasswordUpdateTest < ActionDispatch::IntegrationTest
|
|
|
10
18
|
password_confirmation: 'new-password'
|
|
11
19
|
}
|
|
12
20
|
|
|
13
|
-
assert_redirected_to
|
|
21
|
+
assert_redirected_to password_edit_path
|
|
14
22
|
assert BCrypt::Password.new(user.reload.password_digest).is_password?('new-password')
|
|
15
23
|
end
|
|
16
24
|
|
|
@@ -1,5 +1,128 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
2
|
|
|
3
|
+
/* Drive `dark:` from a `dark` class rather than prefers-color-scheme, so the
|
|
4
|
+
appearance preference can override the operating system. The layouts apply
|
|
5
|
+
the class before first paint. */
|
|
6
|
+
@custom-variant dark (&:is(.dark *));
|
|
7
|
+
|
|
8
|
+
@theme inline {
|
|
9
|
+
--color-background: var(--background);
|
|
10
|
+
--color-foreground: var(--foreground);
|
|
11
|
+
|
|
12
|
+
--color-card: var(--card);
|
|
13
|
+
--color-card-foreground: var(--card-foreground);
|
|
14
|
+
|
|
15
|
+
--color-popover: var(--popover);
|
|
16
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
17
|
+
|
|
18
|
+
--color-primary: var(--primary);
|
|
19
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
20
|
+
|
|
21
|
+
--color-secondary: var(--secondary);
|
|
22
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
23
|
+
|
|
24
|
+
--color-muted: var(--muted);
|
|
25
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
26
|
+
|
|
27
|
+
--color-accent: var(--accent);
|
|
28
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
29
|
+
|
|
30
|
+
--color-destructive: var(--destructive);
|
|
31
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
32
|
+
|
|
33
|
+
--color-border: var(--border);
|
|
34
|
+
--color-input: var(--input);
|
|
35
|
+
--color-ring: var(--ring);
|
|
36
|
+
|
|
37
|
+
--color-sidebar: var(--sidebar);
|
|
38
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
39
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
40
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
41
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
42
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
43
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
44
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
45
|
+
|
|
46
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
47
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
48
|
+
--radius-lg: var(--radius);
|
|
49
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
:root {
|
|
53
|
+
--radius: 0.625rem;
|
|
54
|
+
|
|
55
|
+
--background: oklch(1 0 0);
|
|
56
|
+
--foreground: oklch(0.145 0 0);
|
|
57
|
+
--card: oklch(1 0 0);
|
|
58
|
+
--card-foreground: oklch(0.145 0 0);
|
|
59
|
+
--popover: oklch(1 0 0);
|
|
60
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
61
|
+
--primary: oklch(0.205 0 0);
|
|
62
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
63
|
+
--secondary: oklch(0.97 0 0);
|
|
64
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
65
|
+
--muted: oklch(0.97 0 0);
|
|
66
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
67
|
+
--accent: oklch(0.97 0 0);
|
|
68
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
69
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
70
|
+
--destructive-foreground: oklch(0.985 0 0);
|
|
71
|
+
--border: oklch(0.922 0 0);
|
|
72
|
+
--input: oklch(0.922 0 0);
|
|
73
|
+
--ring: oklch(0.87 0 0);
|
|
74
|
+
|
|
75
|
+
--sidebar: oklch(0.985 0 0);
|
|
76
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
77
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
78
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
79
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
80
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
81
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
82
|
+
--sidebar-ring: oklch(0.87 0 0);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.dark {
|
|
86
|
+
--background: oklch(0.145 0 0);
|
|
87
|
+
--foreground: oklch(0.985 0 0);
|
|
88
|
+
--card: oklch(0.145 0 0);
|
|
89
|
+
--card-foreground: oklch(0.985 0 0);
|
|
90
|
+
--popover: oklch(0.145 0 0);
|
|
91
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
92
|
+
--primary: oklch(0.985 0 0);
|
|
93
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
94
|
+
--secondary: oklch(0.269 0 0);
|
|
95
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
96
|
+
--muted: oklch(0.269 0 0);
|
|
97
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
98
|
+
--accent: oklch(0.269 0 0);
|
|
99
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
100
|
+
--destructive: oklch(0.396 0.141 25.723);
|
|
101
|
+
--destructive-foreground: oklch(0.985 0 0);
|
|
102
|
+
--border: oklch(0.269 0 0);
|
|
103
|
+
--input: oklch(0.269 0 0);
|
|
104
|
+
--ring: oklch(0.439 0 0);
|
|
105
|
+
|
|
106
|
+
--sidebar: oklch(0.205 0 0);
|
|
107
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
108
|
+
--sidebar-primary: oklch(0.985 0 0);
|
|
109
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
110
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
111
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
112
|
+
--sidebar-border: oklch(0.269 0 0);
|
|
113
|
+
--sidebar-ring: oklch(0.439 0 0);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@layer base {
|
|
117
|
+
* {
|
|
118
|
+
@apply border-border outline-ring/50;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
body {
|
|
122
|
+
@apply bg-background text-foreground;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
3
126
|
@plugin "@tailwindcss/forms";
|
|
4
127
|
|
|
5
128
|
@source "../../../public/*.html";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# The trail in the sidebar header. Takes [{ title:, href: }], where the last
|
|
2
|
+
# entry is the current page and is not a link.
|
|
3
|
+
class BreadcrumbsComponent < ViewComponent::Base
|
|
4
|
+
erb_template <<~ERB
|
|
5
|
+
<nav aria-label="breadcrumb">
|
|
6
|
+
<ol class="flex flex-wrap items-center gap-1.5 text-sm break-words text-muted-foreground sm:gap-2.5">
|
|
7
|
+
<% @items.each_with_index do |item, index| %>
|
|
8
|
+
<li class="inline-flex items-center gap-1.5">
|
|
9
|
+
<% if index == @items.length - 1 %>
|
|
10
|
+
<span class="font-normal text-foreground" aria-current="page"><%= item[:title] %></span>
|
|
11
|
+
<% else %>
|
|
12
|
+
<a href="<%= item[:href] %>" class="transition-colors hover:text-foreground"><%= item[:title] %></a>
|
|
13
|
+
<% end %>
|
|
14
|
+
</li>
|
|
15
|
+
<% unless index == @items.length - 1 %>
|
|
16
|
+
<li role="presentation" aria-hidden="true">
|
|
17
|
+
<%= render(IconComponent.new('chevron-right', classes: 'size-3.5')) %>
|
|
18
|
+
</li>
|
|
19
|
+
<% end %>
|
|
20
|
+
<% end %>
|
|
21
|
+
</ol>
|
|
22
|
+
</nav>
|
|
23
|
+
ERB
|
|
24
|
+
|
|
25
|
+
def initialize(items)
|
|
26
|
+
@items = items
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def render?
|
|
30
|
+
@items.present?
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -7,7 +7,7 @@ class DangerButtonComponent < ViewComponent::Base
|
|
|
7
7
|
|
|
8
8
|
def initialize(attributes = {})
|
|
9
9
|
attributes[:type] = attributes[:type] || 'submit'
|
|
10
|
-
attributes[:class] = "inline-flex items-center
|
|
10
|
+
attributes[:class] = "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] h-9 px-4 py-2 bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40#{" #{attributes[:class]}" if attributes[:class]}"
|
|
11
11
|
@attributes = attributes.map { |key, attribute| "#{key}=\"#{attribute}\"" }
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
x-transition:leave="transition ease-in duration-75"
|
|
10
10
|
x-transition:leave-start="opacity-100 scale-100"
|
|
11
11
|
x-transition:leave-end="opacity-0 scale-95"
|
|
12
|
-
class="absolute z-50
|
|
12
|
+
class="absolute z-50 <%= @side_classes %> <%= @width %> rounded-md shadow-lg <%= @alignment_classes %>"
|
|
13
13
|
style="display: none;"
|
|
14
14
|
x-on:click="open = false">
|
|
15
|
-
<div class="rounded-md
|
|
15
|
+
<div class="rounded-md border shadow-md <%= @content_classes %>">
|
|
16
16
|
<%= content %>
|
|
17
17
|
</div>
|
|
18
18
|
</div>
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
class DropdownComponent < ViewComponent::Base
|
|
2
2
|
renders_one :trigger
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
# `side` is which way the menu opens off the trigger. The sidebar's user row
|
|
5
|
+
# sits at the foot of the viewport, so it opens upwards; everything else
|
|
6
|
+
# opens downwards.
|
|
7
|
+
def initialize(align: 'right', side: 'bottom', width: '48', content_classes: 'p-1 bg-popover text-popover-foreground')
|
|
5
8
|
if align == 'left'
|
|
6
9
|
@alignment_classes = 'ltr:origin-top-left rtl:origin-top-right start-0'
|
|
7
10
|
elsif align == 'top'
|
|
@@ -9,6 +12,7 @@ class DropdownComponent < ViewComponent::Base
|
|
|
9
12
|
else
|
|
10
13
|
@alignment_classes = 'ltr:origin-top-right rtl:origin-top-left end-0'
|
|
11
14
|
end
|
|
15
|
+
@side_classes = side == 'top' ? 'bottom-full mb-2' : 'mt-2'
|
|
12
16
|
@width = width == '48' ? 'w-48' : width
|
|
13
17
|
@content_classes = content_classes
|
|
14
18
|
end
|
|
@@ -6,7 +6,7 @@ class DropdownLinkComponent < ViewComponent::Base
|
|
|
6
6
|
ERB
|
|
7
7
|
|
|
8
8
|
def initialize(attributes = {})
|
|
9
|
-
attributes[:class] = "
|
|
9
|
+
attributes[:class] = "relative flex w-full cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-start text-sm outline-hidden select-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground#{" #{attributes[:class]}" if attributes[:class]}"
|
|
10
10
|
@attributes = attributes.map { |key, attribute| "#{key}=\"#{attribute}\"" }
|
|
11
11
|
end
|
|
12
12
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# A section heading with an optional description. `small` is the variant the
|
|
2
|
+
# settings pages use above each form.
|
|
3
|
+
class HeadingComponent < ViewComponent::Base
|
|
4
|
+
erb_template <<~ERB
|
|
5
|
+
<header class="<%= @header_classes %>">
|
|
6
|
+
<h2 class="<%= @title_classes %>"><%= @title %></h2>
|
|
7
|
+
<% if @description %>
|
|
8
|
+
<p class="text-sm text-muted-foreground"><%= @description %></p>
|
|
9
|
+
<% end %>
|
|
10
|
+
</header>
|
|
11
|
+
ERB
|
|
12
|
+
|
|
13
|
+
def initialize(title:, description: nil, variant: 'default')
|
|
14
|
+
@title = title
|
|
15
|
+
@description = description
|
|
16
|
+
@header_classes = variant == 'small' ? '' : 'mb-8 space-y-0.5'
|
|
17
|
+
@title_classes = variant == 'small' ? 'mb-0.5 text-base font-medium' : 'text-xl font-semibold tracking-tight'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Inlines the lucide icons the layouts use. The Inertia stacks get these from
|
|
2
|
+
# the lucide package; there is no npm here, so the paths are carried by hand.
|
|
3
|
+
# Each entry is the body of lucide's 24x24 outline svg.
|
|
4
|
+
class IconComponent < ViewComponent::Base
|
|
5
|
+
PATHS = {
|
|
6
|
+
"book-open" => '<path d="M12 5v16" /><path d="M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z" />',
|
|
7
|
+
"chevron-right" => '<path d="m9 18 6-6-6-6" />',
|
|
8
|
+
"chevrons-up-down" => '<path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" />',
|
|
9
|
+
"folder-git-2" => '<path d="M18 19a5 5 0 0 1-5-5v8" /><path d="M9 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v5" /><circle cx="13" cy="12" r="2" /><circle cx="20" cy="19" r="2" />',
|
|
10
|
+
"layout-grid" => '<rect width="7" height="7" x="3" y="3" rx="1" /><rect width="7" height="7" x="14" y="3" rx="1" /><rect width="7" height="7" x="14" y="14" rx="1" /><rect width="7" height="7" x="3" y="14" rx="1" />',
|
|
11
|
+
"log-out" => '<path d="m16 17 5-5-5-5" /><path d="M21 12H9" /><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />',
|
|
12
|
+
"monitor" => '<rect width="20" height="14" x="2" y="3" rx="2" /><line x1="8" x2="16" y1="21" y2="21" /><line x1="12" x2="12" y1="17" y2="21" />',
|
|
13
|
+
"moon" => '<path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401" />',
|
|
14
|
+
"panel-left-close" => '<rect width="18" height="18" x="3" y="3" rx="2" /><path d="M9 3v18" /><path d="m16 15-3-3 3-3" />',
|
|
15
|
+
"panel-left-open" => '<rect width="18" height="18" x="3" y="3" rx="2" /><path d="M9 3v18" /><path d="m14 9 3 3-3 3" />',
|
|
16
|
+
"settings" => '<path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /><circle cx="12" cy="12" r="3" />',
|
|
17
|
+
"sun" => '<circle cx="12" cy="12" r="4" /><path d="M12 2v2" /><path d="M12 20v2" /><path d="m4.93 4.93 1.41 1.41" /><path d="m17.66 17.66 1.41 1.41" /><path d="M2 12h2" /><path d="M20 12h2" /><path d="m6.34 17.66-1.41 1.41" /><path d="m19.07 4.93-1.41 1.41" />'
|
|
18
|
+
}.freeze
|
|
19
|
+
|
|
20
|
+
erb_template <<~ERB
|
|
21
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
|
22
|
+
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
23
|
+
class="<%= @classes %>" aria-hidden="true"><%= @path %></svg>
|
|
24
|
+
ERB
|
|
25
|
+
|
|
26
|
+
def initialize(name, classes: "size-4")
|
|
27
|
+
@path = PATHS.fetch(name) { raise ArgumentError, "unknown icon: #{name}" }.html_safe
|
|
28
|
+
@classes = classes
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
class InputLabelComponent < ViewComponent::Base
|
|
2
2
|
erb_template <<~ERB
|
|
3
|
-
<
|
|
3
|
+
<label <%= sanitize @attributes.join(" ") %>>
|
|
4
4
|
<%= @value ? @value : content %>
|
|
5
|
-
</
|
|
5
|
+
</label>
|
|
6
6
|
ERB
|
|
7
7
|
|
|
8
8
|
def initialize(attributes = {})
|
|
9
9
|
@value = attributes[:value] || nil
|
|
10
|
-
attributes[:class] = "
|
|
10
|
+
attributes[:class] = "flex items-center gap-2 text-sm leading-none font-medium select-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50#{" #{attributes[:class]}" if attributes[:class]}"
|
|
11
11
|
@attributes = attributes.without(:value).map { |key, attribute| "#{key}=\"#{attribute}\"" }
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -42,11 +42,11 @@
|
|
|
42
42
|
x-transition:leave="ease-in duration-200"
|
|
43
43
|
x-transition:leave-start="opacity-100"
|
|
44
44
|
x-transition:leave-end="opacity-0">
|
|
45
|
-
<div class="absolute inset-0 bg-
|
|
45
|
+
<div class="absolute inset-0 bg-black/80"></div>
|
|
46
46
|
</div>
|
|
47
47
|
<div
|
|
48
48
|
x-show="show"
|
|
49
|
-
class="mb-6 bg-
|
|
49
|
+
class="mb-6 bg-background border rounded-lg overflow-hidden shadow-lg transform transition-all sm:w-full <%= @max_width %> sm:mx-auto"
|
|
50
50
|
x-transition:enter="ease-out duration-300"
|
|
51
51
|
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
52
52
|
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
|
|
@@ -7,7 +7,7 @@ class PrimaryButtonComponent < ViewComponent::Base
|
|
|
7
7
|
|
|
8
8
|
def initialize(attributes = {})
|
|
9
9
|
attributes[:type] = attributes[:type] || 'submit'
|
|
10
|
-
attributes[:class] = "inline-flex items-center
|
|
10
|
+
attributes[:class] = "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] h-9 px-4 py-2 bg-primary text-primary-foreground shadow-xs hover:bg-primary/90#{" #{attributes[:class]}" if attributes[:class]}"
|
|
11
11
|
@attributes = attributes.map { |key, attribute| "#{key}=\"#{attribute}\"" }
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -7,7 +7,7 @@ class SecondaryButtonComponent < ViewComponent::Base
|
|
|
7
7
|
|
|
8
8
|
def initialize(attributes = {})
|
|
9
9
|
attributes[:type] = attributes[:type] || 'button'
|
|
10
|
-
attributes[:class] = "inline-flex items-center
|
|
10
|
+
attributes[:class] = "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] h-9 px-4 py-2 border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground#{" #{attributes[:class]}" if attributes[:class]}"
|
|
11
11
|
@attributes = attributes.map { |key, attribute| "#{key}=\"#{attribute}\"" }
|
|
12
12
|
end
|
|
13
13
|
end
|