kaze 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +42 -0
  4. data/bin/kaze +11 -0
  5. data/lib/kaze/commands/install_command.rb +63 -0
  6. data/lib/kaze/commands/install_inertia_stacks.rb +151 -0
  7. data/lib/kaze/commands.rb +2 -0
  8. data/lib/kaze/version.rb +3 -0
  9. data/lib/kaze.rb +9 -0
  10. data/stubs/default/Procfile.dev +3 -0
  11. data/stubs/default/app/assets/stylesheets/application.css +1 -0
  12. data/stubs/default/app/assets/stylesheets/application.tailwind.css +3 -0
  13. data/stubs/default/app/controllers/application_controller.rb +5 -0
  14. data/stubs/default/app/controllers/auth/authenticated_session_controller.rb +28 -0
  15. data/stubs/default/app/controllers/auth/new_password_controller.rb +18 -0
  16. data/stubs/default/app/controllers/auth/password_reset_link_controller.rb +17 -0
  17. data/stubs/default/app/controllers/auth/registered_user_controller.rb +19 -0
  18. data/stubs/default/app/controllers/concerns/authenticate.rb +34 -0
  19. data/stubs/default/app/controllers/concerns/handle_inertia_requests.rb +9 -0
  20. data/stubs/default/app/controllers/concerns/verify_csrf_token.rb +24 -0
  21. data/stubs/default/app/controllers/dashboard_controller.rb +5 -0
  22. data/stubs/default/app/controllers/password_controller.rb +11 -0
  23. data/stubs/default/app/controllers/profile_controller.rb +31 -0
  24. data/stubs/default/app/controllers/welcome_controller.rb +10 -0
  25. data/stubs/default/app/forms/application_form.rb +9 -0
  26. data/stubs/default/app/forms/auth/login_form.rb +18 -0
  27. data/stubs/default/app/forms/auth/new_password_form.rb +21 -0
  28. data/stubs/default/app/forms/auth/register_form.rb +7 -0
  29. data/stubs/default/app/forms/auth/send_password_reset_link_form.rb +22 -0
  30. data/stubs/default/app/forms/delete_user_form.rb +5 -0
  31. data/stubs/default/app/forms/update_password_form.rb +6 -0
  32. data/stubs/default/app/forms/update_profile_information_form.rb +6 -0
  33. data/stubs/default/app/mailers/application_mailer.rb +11 -0
  34. data/stubs/default/app/mailers/user_mailer.rb +8 -0
  35. data/stubs/default/app/models/application_record.rb +3 -0
  36. data/stubs/default/app/models/concerns/can_reset_password.rb +5 -0
  37. data/stubs/default/app/models/current.rb +3 -0
  38. data/stubs/default/app/models/user.rb +11 -0
  39. data/stubs/default/app/validators/current_password_validator.rb +5 -0
  40. data/stubs/default/app/validators/email_validator.rb +7 -0
  41. data/stubs/default/app/validators/lowercase_validator.rb +5 -0
  42. data/stubs/default/app/validators/uniqueness_validator.rb +24 -0
  43. data/stubs/default/app/views/layouts/mailer.html.erb +374 -0
  44. data/stubs/default/app/views/layouts/mailer.text.erb +11 -0
  45. data/stubs/default/app/views/user_mailer/reset_password.html.erb +39 -0
  46. data/stubs/default/bin/dev +16 -0
  47. data/stubs/default/bin/vite +27 -0
  48. data/stubs/default/config/routes.rb +27 -0
  49. data/stubs/default/config/vite.json +16 -0
  50. data/stubs/default/db/migrate/20240101000000_create_users.rb +14 -0
  51. data/stubs/default/db/migrate/20240101000001_create_delayed_jobs.rb +22 -0
  52. data/stubs/inertia-react-ts/app/javascript/Components/ApplicationLogo.tsx +12 -0
  53. data/stubs/inertia-react-ts/app/javascript/Components/Checkbox.tsx +14 -0
  54. data/stubs/inertia-react-ts/app/javascript/Components/DangerButton.tsx +17 -0
  55. data/stubs/inertia-react-ts/app/javascript/Components/Dropdown.tsx +99 -0
  56. data/stubs/inertia-react-ts/app/javascript/Components/InputError.tsx +9 -0
  57. data/stubs/inertia-react-ts/app/javascript/Components/InputLabel.tsx +9 -0
  58. data/stubs/inertia-react-ts/app/javascript/Components/Modal.tsx +68 -0
  59. data/stubs/inertia-react-ts/app/javascript/Components/NavLink.tsx +18 -0
  60. data/stubs/inertia-react-ts/app/javascript/Components/PrimaryButton.tsx +17 -0
  61. data/stubs/inertia-react-ts/app/javascript/Components/ResponsiveNavLink.tsx +16 -0
  62. data/stubs/inertia-react-ts/app/javascript/Components/SecondaryButton.tsx +18 -0
  63. data/stubs/inertia-react-ts/app/javascript/Components/TextInput.tsx +30 -0
  64. data/stubs/inertia-react-ts/app/javascript/Layouts/AuthenticatedLayout.tsx +131 -0
  65. data/stubs/inertia-react-ts/app/javascript/Layouts/GuestLayout.tsx +19 -0
  66. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ForgotPassword.tsx +52 -0
  67. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Login.tsx +98 -0
  68. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Register.tsx +118 -0
  69. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ResetPassword.tsx +74 -0
  70. data/stubs/inertia-react-ts/app/javascript/Pages/Dashboard.tsx +22 -0
  71. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Edit.tsx +33 -0
  72. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.tsx +100 -0
  73. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdatePasswordForm.tsx +114 -0
  74. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.tsx +84 -0
  75. data/stubs/inertia-react-ts/app/javascript/Pages/Welcome.tsx +66 -0
  76. data/stubs/inertia-react-ts/app/javascript/entrypoints/application.tsx +34 -0
  77. data/stubs/inertia-react-ts/app/javascript/entrypoints/bootstrap.ts +4 -0
  78. data/stubs/inertia-react-ts/app/javascript/types/global.d.ts +7 -0
  79. data/stubs/inertia-react-ts/app/javascript/types/index.d.ts +12 -0
  80. data/stubs/inertia-react-ts/app/javascript/types/vite-env.d.ts +1 -0
  81. data/stubs/inertia-react-ts/app/views/layouts/application.html.erb +26 -0
  82. data/stubs/inertia-react-ts/config/tailwind.config.js +22 -0
  83. data/stubs/inertia-react-ts/package.json +26 -0
  84. data/stubs/inertia-react-ts/tsconfig.json +19 -0
  85. data/stubs/inertia-react-ts/vite.config.ts +13 -0
  86. data/stubs/inertia-vue-ts/app/javascript/Components/ApplicationLogo.vue +8 -0
  87. data/stubs/inertia-vue-ts/app/javascript/Components/Checkbox.vue +29 -0
  88. data/stubs/inertia-vue-ts/app/javascript/Components/DangerButton.vue +7 -0
  89. data/stubs/inertia-vue-ts/app/javascript/Components/Dropdown.vue +75 -0
  90. data/stubs/inertia-vue-ts/app/javascript/Components/DropdownLink.vue +16 -0
  91. data/stubs/inertia-vue-ts/app/javascript/Components/InputError.vue +13 -0
  92. data/stubs/inertia-vue-ts/app/javascript/Components/InputLabel.vue +12 -0
  93. data/stubs/inertia-vue-ts/app/javascript/Components/Modal.vue +96 -0
  94. data/stubs/inertia-vue-ts/app/javascript/Components/NavLink.vue +21 -0
  95. data/stubs/inertia-vue-ts/app/javascript/Components/PrimaryButton.vue +7 -0
  96. data/stubs/inertia-vue-ts/app/javascript/Components/ResponsiveNavLink.vue +21 -0
  97. data/stubs/inertia-vue-ts/app/javascript/Components/SecondaryButton.vue +19 -0
  98. data/stubs/inertia-vue-ts/app/javascript/Components/TextInput.vue +23 -0
  99. data/stubs/inertia-vue-ts/app/javascript/Layouts/AuthenticatedLayout.vue +155 -0
  100. data/stubs/inertia-vue-ts/app/javascript/Layouts/GuestLayout.vue +20 -0
  101. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ForgotPassword.vue +60 -0
  102. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Login.vue +93 -0
  103. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Register.vue +106 -0
  104. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ResetPassword.vue +89 -0
  105. data/stubs/inertia-vue-ts/app/javascript/Pages/Dashboard.vue +22 -0
  106. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Edit.vue +42 -0
  107. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.vue +98 -0
  108. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdatePasswordForm.vue +108 -0
  109. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.vue +78 -0
  110. data/stubs/inertia-vue-ts/app/javascript/Pages/Welcome.vue +56 -0
  111. data/stubs/inertia-vue-ts/app/javascript/entrypoints/application.ts +34 -0
  112. data/stubs/inertia-vue-ts/app/javascript/entrypoints/bootstrap.ts +4 -0
  113. data/stubs/inertia-vue-ts/app/javascript/types/global.d.ts +13 -0
  114. data/stubs/inertia-vue-ts/app/javascript/types/index.d.ts +12 -0
  115. data/stubs/inertia-vue-ts/app/javascript/types/vite-env.d.ts +1 -0
  116. data/stubs/inertia-vue-ts/app/views/layouts/application.html.erb +25 -0
  117. data/stubs/inertia-vue-ts/config/tailwind.config.js +22 -0
  118. data/stubs/inertia-vue-ts/package.json +24 -0
  119. data/stubs/inertia-vue-ts/tsconfig.json +19 -0
  120. data/stubs/inertia-vue-ts/vite.config.ts +13 -0
  121. metadata +205 -0
@@ -0,0 +1,4 @@
1
+ import axios from 'axios';
2
+ window.axios = axios;
3
+
4
+ window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
@@ -0,0 +1,7 @@
1
+ import { AxiosInstance } from 'axios';
2
+
3
+ declare global {
4
+ interface Window {
5
+ axios: AxiosInstance;
6
+ }
7
+ }
@@ -0,0 +1,12 @@
1
+ export interface User {
2
+ id: number;
3
+ name: string;
4
+ email: string;
5
+ email_verified_at: string;
6
+ }
7
+
8
+ export type PageProps<T extends Record<string, unknown> = Record<string, unknown>> = T & {
9
+ auth: {
10
+ user: User;
11
+ };
12
+ };
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+
7
+ <title><%= content_for(:title) || "Rails" %></title>
8
+
9
+ <%= csrf_meta_tags %>
10
+ <%= csp_meta_tag %>
11
+
12
+ <!-- Fonts -->
13
+ <link rel="preconnect" href="https://fonts.bunny.net">
14
+ <link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
15
+
16
+ <%= vite_client_tag %>
17
+ <%= vite_react_refresh_tag %>
18
+ <%= vite_typescript_tag 'application.tsx', defer: true %>
19
+ </head>
20
+
21
+ <body class="font-sans antialiased">
22
+ <div class="min-h-screen bg-gray-100">
23
+ <%= yield %>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,22 @@
1
+ import defaultTheme from 'tailwindcss/defaultTheme';
2
+ import forms from '@tailwindcss/forms';
3
+
4
+ /** @type {import('tailwindcss').Config} */
5
+ export default {
6
+ content: [
7
+ './public/*.html',
8
+ './app/helpers/**/*.rb',
9
+ './app/views/**/*',
10
+ './app/javascript/**/*.tsx',
11
+ ],
12
+
13
+ theme: {
14
+ extend: {
15
+ fontFamily: {
16
+ sans: ['Figtree', ...defaultTheme.fontFamily.sans],
17
+ },
18
+ },
19
+ },
20
+
21
+ plugins: [forms],
22
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "private": true,
3
+ "type": "module",
4
+ "scripts": {
5
+ "dev": "vite",
6
+ "build": "tsc && bin/rails tailwindcss:build && vite build"
7
+ },
8
+ "devDependencies": {
9
+ "@headlessui/react": "^2.0.4",
10
+ "@inertiajs/react": "^1.1.0",
11
+ "@tailwindcss/forms": "^0.5.7",
12
+ "@types/node": "^18.13.0",
13
+ "@types/react": "^18.0.28",
14
+ "@types/react-dom": "^18.0.10",
15
+ "@vitejs/plugin-react": "^4.2.0",
16
+ "autoprefixer": "^10.4.12",
17
+ "axios": "^1.6.4",
18
+ "postcss": "^8.4.31",
19
+ "react": "^18.2.0",
20
+ "react-dom": "^18.2.0",
21
+ "tailwindcss": "^3.2.1",
22
+ "typescript": "^5.0.2",
23
+ "vite": "^5.2.11",
24
+ "vite-plugin-rails": "^0.5.0"
25
+ }
26
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "allowJs": true,
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "jsx": "react-jsx",
7
+ "strict": true,
8
+ "isolatedModules": true,
9
+ "target": "ESNext",
10
+ "esModuleInterop": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "noEmit": true,
13
+ "skipLibCheck": true,
14
+ "paths": {
15
+ "@/*": ["./app/javascript/*"]
16
+ }
17
+ },
18
+ "include": ["app/javascript/**/*.ts", "app/javascript/**/*.tsx", "app/javascript/**/*.d.ts"]
19
+ }
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from 'vite'
2
+ import Rails from 'vite-plugin-rails'
3
+ import react from '@vitejs/plugin-react'
4
+
5
+ export default defineConfig({
6
+ resolve: {
7
+ dedupe: ['axios']
8
+ },
9
+ plugins: [
10
+ Rails(),
11
+ react(),
12
+ ],
13
+ })
@@ -0,0 +1,8 @@
1
+ <template>
2
+ <svg viewBox="0 -6 32 32" xmlns="http://www.w3.org/2000/svg">
3
+ <g fill="none" fill-rule="evenodd">
4
+ <path d="M0-6h32v32H0z"/>
5
+ <path fill="#c00" fill-rule="nonzero" d="M.985 19.636s.422-4.163 3.375-9.087c2.954-4.924 7.99-8.65 12.083-9.017 8.144-.816 15.46 6.485 15.46 6.485s-.24.168-.494.38C23.42 2.49 18.54 5.274 17.005 6.02c-7.033 3.925-4.91 13.616-4.91 13.616H.987zM24.137 2.32c-.45-.182-.9-.35-1.364-.505l.056-.93c.885.254 1.237.423 1.363.493l-.056.943zM22.8 5.304c.45.028.915.084 1.393.183l-.056.872c-.464-.1-.928-.155-1.392-.17l.056-.885zM17.597.913c-.407 0-.815.015-1.223.058l-.268-.83c.465-.056.915-.084 1.35-.084l.282.858h-.14zm.676 5.178c.35-.154.76-.31 1.237-.45l.31.93c-.41.125-.817.294-1.225.49l-.323-.97zm-6.386-3.7c-.366.184-.718.395-1.083.62l-.647-.985c.38-.225.745-.42 1.097-.604l.633.97zm2.883 6.33c.252-.323.548-.646.87-.942l.634.957c-.31.323-.59.647-.83 1L14.77 8.72zm-2.04 4.53c.112-.506.24-1.027.422-1.547l1.012.802c-.14.548-.24 1.097-.295 1.645l-1.14-.9zM6.57 6.57c-.34.35-.662.73-.958 1.11L4.53 6.752c.323-.352.674-.704 1.04-1.055l1 .872zm-4.25 6.286c-.224.52-.52 1.21-.702 1.688L0 13.954c.14-.38.436-1.084.703-1.69l1.618.592zm10.2 3.967l1.518.548c.084.663.21 1.28.337 1.83l-1.688-.605c-.07-.422-.14-1.027-.168-1.772z"/>
6
+ </g>
7
+ </svg>
8
+ </template>
@@ -0,0 +1,29 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue';
3
+
4
+ const emit = defineEmits(['update:checked']);
5
+
6
+ const props = defineProps<{
7
+ checked: boolean;
8
+ value?: any;
9
+ }>();
10
+
11
+ const proxyChecked = computed({
12
+ get() {
13
+ return props.checked;
14
+ },
15
+
16
+ set(val) {
17
+ emit('update:checked', val);
18
+ },
19
+ });
20
+ </script>
21
+
22
+ <template>
23
+ <input
24
+ type="checkbox"
25
+ :value="value"
26
+ v-model="proxyChecked"
27
+ class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800"
28
+ />
29
+ </template>
@@ -0,0 +1,7 @@
1
+ <template>
2
+ <button
3
+ class="inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150"
4
+ >
5
+ <slot />
6
+ </button>
7
+ </template>
@@ -0,0 +1,75 @@
1
+ <script setup lang="ts">
2
+ import { computed, onMounted, onUnmounted, ref } from 'vue';
3
+
4
+ const props = withDefaults(
5
+ defineProps<{
6
+ align?: 'left' | 'right';
7
+ width?: '48';
8
+ contentClasses?: string;
9
+ }>(),
10
+ {
11
+ align: 'right',
12
+ width: '48',
13
+ contentClasses: 'py-1 bg-white dark:bg-gray-700',
14
+ }
15
+ );
16
+
17
+ const closeOnEscape = (e: KeyboardEvent) => {
18
+ if (open.value && e.key === 'Escape') {
19
+ open.value = false;
20
+ }
21
+ };
22
+
23
+ onMounted(() => document.addEventListener('keydown', closeOnEscape));
24
+ onUnmounted(() => document.removeEventListener('keydown', closeOnEscape));
25
+
26
+ const widthClass = computed(() => {
27
+ return {
28
+ 48: 'w-48',
29
+ }[props.width.toString()];
30
+ });
31
+
32
+ const alignmentClasses = computed(() => {
33
+ if (props.align === 'left') {
34
+ return 'ltr:origin-top-left rtl:origin-top-right start-0';
35
+ } else if (props.align === 'right') {
36
+ return 'ltr:origin-top-right rtl:origin-top-left end-0';
37
+ } else {
38
+ return 'origin-top';
39
+ }
40
+ });
41
+
42
+ const open = ref(false);
43
+ </script>
44
+
45
+ <template>
46
+ <div class="relative">
47
+ <div @click="open = !open">
48
+ <slot name="trigger" />
49
+ </div>
50
+
51
+ <!-- Full Screen Dropdown Overlay -->
52
+ <div v-show="open" class="fixed inset-0 z-40" @click="open = false"></div>
53
+
54
+ <Transition
55
+ enter-active-class="transition ease-out duration-200"
56
+ enter-from-class="opacity-0 scale-95"
57
+ enter-to-class="opacity-100 scale-100"
58
+ leave-active-class="transition ease-in duration-75"
59
+ leave-from-class="opacity-100 scale-100"
60
+ leave-to-class="opacity-0 scale-95"
61
+ >
62
+ <div
63
+ v-show="open"
64
+ class="absolute z-50 mt-2 rounded-md shadow-lg"
65
+ :class="[widthClass, alignmentClasses]"
66
+ style="display: none"
67
+ @click="open = false"
68
+ >
69
+ <div class="rounded-md ring-1 ring-black ring-opacity-5" :class="contentClasses">
70
+ <slot name="content" />
71
+ </div>
72
+ </div>
73
+ </Transition>
74
+ </div>
75
+ </template>
@@ -0,0 +1,16 @@
1
+ <script setup lang="ts">
2
+ import { Link } from '@inertiajs/vue3';
3
+
4
+ defineProps<{
5
+ href: string;
6
+ }>();
7
+ </script>
8
+
9
+ <template>
10
+ <Link
11
+ :href="href"
12
+ class="block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800 transition duration-150 ease-in-out"
13
+ >
14
+ <slot />
15
+ </Link>
16
+ </template>
@@ -0,0 +1,13 @@
1
+ <script setup lang="ts">
2
+ defineProps<{
3
+ message?: string;
4
+ }>();
5
+ </script>
6
+
7
+ <template>
8
+ <div v-show="message">
9
+ <p class="text-sm text-red-600 dark:text-red-400">
10
+ {{ message }}
11
+ </p>
12
+ </div>
13
+ </template>
@@ -0,0 +1,12 @@
1
+ <script setup lang="ts">
2
+ defineProps<{
3
+ value?: string;
4
+ }>();
5
+ </script>
6
+
7
+ <template>
8
+ <label class="block font-medium text-sm text-gray-700 dark:text-gray-300">
9
+ <span v-if="value">{{ value }}</span>
10
+ <span v-else><slot /></span>
11
+ </label>
12
+ </template>
@@ -0,0 +1,96 @@
1
+ <script setup lang="ts">
2
+ import { computed, onMounted, onUnmounted, watch } from 'vue';
3
+
4
+ const props = withDefaults(
5
+ defineProps<{
6
+ show?: boolean;
7
+ maxWidth?: 'sm' | 'md' | 'lg' | 'xl' | '2xl';
8
+ closeable?: boolean;
9
+ }>(),
10
+ {
11
+ show: false,
12
+ maxWidth: '2xl',
13
+ closeable: true,
14
+ }
15
+ );
16
+
17
+ const emit = defineEmits(['close']);
18
+
19
+ watch(
20
+ () => props.show,
21
+ () => {
22
+ if (props.show) {
23
+ document.body.style.overflow = 'hidden';
24
+ } else {
25
+ document.body.style.overflow = 'visible';
26
+ }
27
+ }
28
+ );
29
+
30
+ const close = () => {
31
+ if (props.closeable) {
32
+ emit('close');
33
+ }
34
+ };
35
+
36
+ const closeOnEscape = (e: KeyboardEvent) => {
37
+ if (e.key === 'Escape' && props.show) {
38
+ close();
39
+ }
40
+ };
41
+
42
+ onMounted(() => document.addEventListener('keydown', closeOnEscape));
43
+
44
+ onUnmounted(() => {
45
+ document.removeEventListener('keydown', closeOnEscape);
46
+ document.body.style.overflow = 'visible';
47
+ });
48
+
49
+ const maxWidthClass = computed(() => {
50
+ return {
51
+ sm: 'sm:max-w-sm',
52
+ md: 'sm:max-w-md',
53
+ lg: 'sm:max-w-lg',
54
+ xl: 'sm:max-w-xl',
55
+ '2xl': 'sm:max-w-2xl',
56
+ }[props.maxWidth];
57
+ });
58
+ </script>
59
+
60
+ <template>
61
+ <Teleport to="body">
62
+ <Transition leave-active-class="duration-200">
63
+ <div v-show="show" class="fixed inset-0 overflow-y-auto px-4 py-6 sm:px-0 z-50" scroll-region>
64
+ <Transition
65
+ enter-active-class="ease-out duration-300"
66
+ enter-from-class="opacity-0"
67
+ enter-to-class="opacity-100"
68
+ leave-active-class="ease-in duration-200"
69
+ leave-from-class="opacity-100"
70
+ leave-to-class="opacity-0"
71
+ >
72
+ <div v-show="show" class="fixed inset-0 transform transition-all" @click="close">
73
+ <div class="absolute inset-0 bg-gray-500 dark:bg-gray-900 opacity-75" />
74
+ </div>
75
+ </Transition>
76
+
77
+ <Transition
78
+ enter-active-class="ease-out duration-300"
79
+ enter-from-class="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
80
+ enter-to-class="opacity-100 translate-y-0 sm:scale-100"
81
+ leave-active-class="ease-in duration-200"
82
+ leave-from-class="opacity-100 translate-y-0 sm:scale-100"
83
+ leave-to-class="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
84
+ >
85
+ <div
86
+ v-show="show"
87
+ class="mb-6 bg-white dark:bg-gray-800 rounded-lg overflow-hidden shadow-xl transform transition-all sm:w-full sm:mx-auto"
88
+ :class="maxWidthClass"
89
+ >
90
+ <slot v-if="show" />
91
+ </div>
92
+ </Transition>
93
+ </div>
94
+ </Transition>
95
+ </Teleport>
96
+ </template>
@@ -0,0 +1,21 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue';
3
+ import { Link } from '@inertiajs/vue3';
4
+
5
+ const props = defineProps<{
6
+ href: string;
7
+ active?: boolean;
8
+ }>();
9
+
10
+ const classes = computed(() =>
11
+ props.active
12
+ ? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 dark:border-indigo-600 text-sm font-medium leading-5 text-gray-900 dark:text-gray-100 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out'
13
+ : 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hover:border-gray-300 dark:hover:border-gray-700 focus:outline-none focus:text-gray-700 dark:focus:text-gray-300 focus:border-gray-300 dark:focus:border-gray-700 transition duration-150 ease-in-out'
14
+ );
15
+ </script>
16
+
17
+ <template>
18
+ <Link :href="href" :class="classes">
19
+ <slot />
20
+ </Link>
21
+ </template>
@@ -0,0 +1,7 @@
1
+ <template>
2
+ <button
3
+ class="inline-flex items-center px-4 py-2 bg-gray-800 dark:bg-gray-200 border border-transparent rounded-md font-semibold text-xs text-white dark:text-gray-800 uppercase tracking-widest hover:bg-gray-700 dark:hover:bg-white focus:bg-gray-700 dark:focus:bg-white active:bg-gray-900 dark:active:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150"
4
+ >
5
+ <slot />
6
+ </button>
7
+ </template>
@@ -0,0 +1,21 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue';
3
+ import { Link } from '@inertiajs/vue3';
4
+
5
+ const props = defineProps<{
6
+ href: string;
7
+ active?: boolean;
8
+ }>();
9
+
10
+ const classes = computed(() =>
11
+ props.active
12
+ ? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 dark:border-indigo-600 text-start text-base font-medium text-indigo-700 dark:text-indigo-300 bg-indigo-50 dark:bg-indigo-900/50 focus:outline-none focus:text-indigo-800 dark:focus:text-indigo-200 focus:bg-indigo-100 dark:focus:bg-indigo-900 focus:border-indigo-700 dark:focus:border-indigo-300 transition duration-150 ease-in-out'
13
+ : 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 hover:border-gray-300 dark:hover:border-gray-600 focus:outline-none focus:text-gray-800 dark:focus:text-gray-200 focus:bg-gray-50 dark:focus:bg-gray-700 focus:border-gray-300 dark:focus:border-gray-600 transition duration-150 ease-in-out'
14
+ );
15
+ </script>
16
+
17
+ <template>
18
+ <Link :href="href" :class="classes">
19
+ <slot />
20
+ </Link>
21
+ </template>
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts">
2
+ withDefaults(
3
+ defineProps<{
4
+ type?: 'button' | 'submit' | 'reset';
5
+ }>(),
6
+ {
7
+ type: 'button',
8
+ }
9
+ );
10
+ </script>
11
+
12
+ <template>
13
+ <button
14
+ :type="type"
15
+ class="inline-flex items-center px-4 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-500 rounded-md font-semibold text-xs text-gray-700 dark:text-gray-300 uppercase tracking-widest shadow-sm hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 disabled:opacity-25 transition ease-in-out duration-150"
16
+ >
17
+ <slot />
18
+ </button>
19
+ </template>
@@ -0,0 +1,23 @@
1
+ <script setup lang="ts">
2
+ import { onMounted, ref } from 'vue';
3
+
4
+ const model = defineModel<string>({ required: true });
5
+
6
+ const input = ref<HTMLInputElement | null>(null);
7
+
8
+ onMounted(() => {
9
+ if (input.value?.hasAttribute('autofocus')) {
10
+ input.value?.focus();
11
+ }
12
+ });
13
+
14
+ defineExpose({ focus: () => input.value?.focus() });
15
+ </script>
16
+
17
+ <template>
18
+ <input
19
+ class="border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm"
20
+ v-model="model"
21
+ ref="input"
22
+ />
23
+ </template>