kaze 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +42 -0
- data/bin/kaze +11 -0
- data/lib/kaze/commands/install_command.rb +63 -0
- data/lib/kaze/commands/install_inertia_stacks.rb +151 -0
- data/lib/kaze/commands.rb +2 -0
- data/lib/kaze/version.rb +3 -0
- data/lib/kaze.rb +9 -0
- data/stubs/default/Procfile.dev +3 -0
- data/stubs/default/app/assets/stylesheets/application.css +1 -0
- data/stubs/default/app/assets/stylesheets/application.tailwind.css +3 -0
- data/stubs/default/app/controllers/application_controller.rb +5 -0
- data/stubs/default/app/controllers/auth/authenticated_session_controller.rb +28 -0
- data/stubs/default/app/controllers/auth/new_password_controller.rb +18 -0
- data/stubs/default/app/controllers/auth/password_reset_link_controller.rb +17 -0
- data/stubs/default/app/controllers/auth/registered_user_controller.rb +19 -0
- data/stubs/default/app/controllers/concerns/authenticate.rb +34 -0
- data/stubs/default/app/controllers/concerns/handle_inertia_requests.rb +9 -0
- data/stubs/default/app/controllers/concerns/verify_csrf_token.rb +24 -0
- data/stubs/default/app/controllers/dashboard_controller.rb +5 -0
- data/stubs/default/app/controllers/password_controller.rb +11 -0
- data/stubs/default/app/controllers/profile_controller.rb +31 -0
- data/stubs/default/app/controllers/welcome_controller.rb +10 -0
- data/stubs/default/app/forms/application_form.rb +9 -0
- data/stubs/default/app/forms/auth/login_form.rb +18 -0
- data/stubs/default/app/forms/auth/new_password_form.rb +21 -0
- data/stubs/default/app/forms/auth/register_form.rb +7 -0
- data/stubs/default/app/forms/auth/send_password_reset_link_form.rb +22 -0
- data/stubs/default/app/forms/delete_user_form.rb +5 -0
- data/stubs/default/app/forms/update_password_form.rb +6 -0
- data/stubs/default/app/forms/update_profile_information_form.rb +6 -0
- data/stubs/default/app/mailers/application_mailer.rb +11 -0
- data/stubs/default/app/mailers/user_mailer.rb +8 -0
- data/stubs/default/app/models/application_record.rb +3 -0
- data/stubs/default/app/models/concerns/can_reset_password.rb +5 -0
- data/stubs/default/app/models/current.rb +3 -0
- data/stubs/default/app/models/user.rb +11 -0
- data/stubs/default/app/validators/current_password_validator.rb +5 -0
- data/stubs/default/app/validators/email_validator.rb +7 -0
- data/stubs/default/app/validators/lowercase_validator.rb +5 -0
- data/stubs/default/app/validators/uniqueness_validator.rb +24 -0
- data/stubs/default/app/views/layouts/mailer.html.erb +374 -0
- data/stubs/default/app/views/layouts/mailer.text.erb +11 -0
- data/stubs/default/app/views/user_mailer/reset_password.html.erb +39 -0
- data/stubs/default/bin/dev +16 -0
- data/stubs/default/bin/vite +27 -0
- data/stubs/default/config/routes.rb +27 -0
- data/stubs/default/config/vite.json +16 -0
- data/stubs/default/db/migrate/20240101000000_create_users.rb +14 -0
- data/stubs/default/db/migrate/20240101000001_create_delayed_jobs.rb +22 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ApplicationLogo.tsx +12 -0
- data/stubs/inertia-react-ts/app/javascript/Components/Checkbox.tsx +14 -0
- data/stubs/inertia-react-ts/app/javascript/Components/DangerButton.tsx +17 -0
- data/stubs/inertia-react-ts/app/javascript/Components/Dropdown.tsx +99 -0
- data/stubs/inertia-react-ts/app/javascript/Components/InputError.tsx +9 -0
- data/stubs/inertia-react-ts/app/javascript/Components/InputLabel.tsx +9 -0
- data/stubs/inertia-react-ts/app/javascript/Components/Modal.tsx +68 -0
- data/stubs/inertia-react-ts/app/javascript/Components/NavLink.tsx +18 -0
- data/stubs/inertia-react-ts/app/javascript/Components/PrimaryButton.tsx +17 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ResponsiveNavLink.tsx +16 -0
- data/stubs/inertia-react-ts/app/javascript/Components/SecondaryButton.tsx +18 -0
- data/stubs/inertia-react-ts/app/javascript/Components/TextInput.tsx +30 -0
- data/stubs/inertia-react-ts/app/javascript/Layouts/AuthenticatedLayout.tsx +131 -0
- data/stubs/inertia-react-ts/app/javascript/Layouts/GuestLayout.tsx +19 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ForgotPassword.tsx +52 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Login.tsx +98 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Register.tsx +118 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ResetPassword.tsx +74 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Dashboard.tsx +22 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Edit.tsx +33 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.tsx +100 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdatePasswordForm.tsx +114 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.tsx +84 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Welcome.tsx +66 -0
- data/stubs/inertia-react-ts/app/javascript/entrypoints/application.tsx +34 -0
- data/stubs/inertia-react-ts/app/javascript/entrypoints/bootstrap.ts +4 -0
- data/stubs/inertia-react-ts/app/javascript/types/global.d.ts +7 -0
- data/stubs/inertia-react-ts/app/javascript/types/index.d.ts +12 -0
- data/stubs/inertia-react-ts/app/javascript/types/vite-env.d.ts +1 -0
- data/stubs/inertia-react-ts/app/views/layouts/application.html.erb +26 -0
- data/stubs/inertia-react-ts/config/tailwind.config.js +22 -0
- data/stubs/inertia-react-ts/package.json +26 -0
- data/stubs/inertia-react-ts/tsconfig.json +19 -0
- data/stubs/inertia-react-ts/vite.config.ts +13 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ApplicationLogo.vue +8 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/Checkbox.vue +29 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/DangerButton.vue +7 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/Dropdown.vue +75 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/DropdownLink.vue +16 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/InputError.vue +13 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/InputLabel.vue +12 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/Modal.vue +96 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/NavLink.vue +21 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/PrimaryButton.vue +7 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ResponsiveNavLink.vue +21 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/SecondaryButton.vue +19 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/TextInput.vue +23 -0
- data/stubs/inertia-vue-ts/app/javascript/Layouts/AuthenticatedLayout.vue +155 -0
- data/stubs/inertia-vue-ts/app/javascript/Layouts/GuestLayout.vue +20 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ForgotPassword.vue +60 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Login.vue +93 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Register.vue +106 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ResetPassword.vue +89 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Dashboard.vue +22 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Edit.vue +42 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.vue +98 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdatePasswordForm.vue +108 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.vue +78 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Welcome.vue +56 -0
- data/stubs/inertia-vue-ts/app/javascript/entrypoints/application.ts +34 -0
- data/stubs/inertia-vue-ts/app/javascript/entrypoints/bootstrap.ts +4 -0
- data/stubs/inertia-vue-ts/app/javascript/types/global.d.ts +13 -0
- data/stubs/inertia-vue-ts/app/javascript/types/index.d.ts +12 -0
- data/stubs/inertia-vue-ts/app/javascript/types/vite-env.d.ts +1 -0
- data/stubs/inertia-vue-ts/app/views/layouts/application.html.erb +25 -0
- data/stubs/inertia-vue-ts/config/tailwind.config.js +22 -0
- data/stubs/inertia-vue-ts/package.json +24 -0
- data/stubs/inertia-vue-ts/tsconfig.json +19 -0
- data/stubs/inertia-vue-ts/vite.config.ts +13 -0
- metadata +205 -0
@@ -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,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,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>
|