Compare commits
54 Commits
features/c
...
features/m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b01ce181d | ||
|
|
acb5c0d92f | ||
|
|
387597755d | ||
|
|
a5f7e1ded9 | ||
|
|
93a03ca4e2 | ||
|
|
a6fe4c7ea3 | ||
|
|
805972bf3c | ||
|
|
c071e9c33b | ||
|
|
b430ab2660 | ||
|
|
a6d388f5da | ||
|
|
a03b7890e6 | ||
|
|
b71e715cf5 | ||
|
|
9ba09c2d2b | ||
|
|
b1ff2ec232 | ||
|
|
093b4e7493 | ||
|
|
7b0bb1e939 | ||
|
|
60bbb6f48f | ||
|
|
f502f80341 | ||
|
|
e634cc8127 | ||
|
|
ee29169f4f | ||
|
|
2d2069cae6 | ||
|
|
f7f12feaec | ||
|
|
5b8fedba7c | ||
|
|
fa872e6914 | ||
|
|
43dd12782b | ||
|
|
f1f140f2bc | ||
|
|
707d4374ef | ||
|
|
048413286f | ||
|
|
9d06040c5d | ||
|
|
15bfc66087 | ||
|
|
e6b2670438 | ||
|
|
e2d2810db6 | ||
|
|
1827b9118f | ||
|
|
086b997845 | ||
|
|
73f5e01244 | ||
|
|
d02bfacd57 | ||
|
|
5370a9df06 | ||
|
|
5bd76495f4 | ||
|
|
7480447420 | ||
|
|
034713961b | ||
|
|
00224417be | ||
|
|
9b4556245e | ||
|
|
ad8120bdd0 | ||
|
|
73bd79c3c5 | ||
|
|
8215791830 | ||
|
|
36456ce377 | ||
|
|
015d0c4db4 | ||
|
|
392d8cd8ec | ||
|
|
473c69c9a1 | ||
|
|
790deeec5d | ||
|
|
687b96cf12 | ||
|
|
3e973dd0e9 | ||
|
|
cc07f898ea | ||
|
|
e5bc5c8a41 |
28
app.vue
28
app.vue
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ConfigProvider } from "radix-vue"
|
||||
import { cn } from "./lib/utils";
|
||||
|
||||
useHead({
|
||||
titleTemplate: (titleChunk) => {
|
||||
@@ -26,6 +27,8 @@ useHead({
|
||||
})
|
||||
|
||||
const useIdFunction = () => useId()
|
||||
|
||||
const { isSidebarOpened } = storeToRefs(useUiStore())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -34,10 +37,18 @@ const useIdFunction = () => useId()
|
||||
|
||||
<NuxtLayout>
|
||||
<ConfigProvider :use-id="useIdFunction">
|
||||
<div class="h-full grid grid-cols-[auto_1fr] dark:bg-black transition-colors">
|
||||
<div
|
||||
class="h-full grid md:grid-cols-[auto_1fr] dark:bg-black shadow-body-light dark:shadow-body-dark transition-colors after:absolute after:transition-colors"
|
||||
:class="cn({
|
||||
'max-md:after:bg-transparent': isSidebarOpened,
|
||||
'has-sidebar max-md:after:bg-black/20 md:after:opacity-0 md:after:pointer-events-none': isSidebarOpened
|
||||
})"
|
||||
>
|
||||
<Sidebar />
|
||||
|
||||
<div class="wrapper shadow-body-light dark:shadow-body-dark transition-all">
|
||||
<div
|
||||
class="wrapper max-h-screen transition-all"
|
||||
>
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</div>
|
||||
@@ -54,4 +65,17 @@ const useIdFunction = () => useId()
|
||||
.wrapper > * {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.has-sidebar {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 30;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
326
assets/_colors.css
Normal file
326
assets/_colors.css
Normal file
@@ -0,0 +1,326 @@
|
||||
:root {
|
||||
--event-red-foreground: var(--color-slate-900);
|
||||
--event-red-background: var(--color-red-500);
|
||||
|
||||
--event-orange-foreground: var(--color-slate-900);
|
||||
--event-orange-background: var(--color-orange-500);
|
||||
|
||||
--event-amber-foreground: var(--color-slate-900);
|
||||
--event-amber-background: var(--color-amber-500);
|
||||
|
||||
--event-yellow-foreground: var(--color-slate-900);
|
||||
--event-yellow-background: var(--color-yellow-500);
|
||||
|
||||
--event-lime-foreground: var(--color-slate-900);
|
||||
--event-lime-background: var(--color-lime-500);
|
||||
|
||||
--event-green-foreground: var(--color-white);
|
||||
--event-green-background: var(--color-green-600);
|
||||
|
||||
--event-emerald-foreground: var(--color-white);
|
||||
--event-emerald-background: var(--color-emerald-600);
|
||||
|
||||
--event-teal-foreground: var(--color-white);
|
||||
--event-teal-background: var(--color-teal-600);
|
||||
|
||||
--event-cyan-foreground: var(--color-white);
|
||||
--event-cyan-background: var(--color-cyan-600);
|
||||
|
||||
--event-sky-foreground: var(--color-white);
|
||||
--event-sky-background: var(--color-sky-600);
|
||||
|
||||
--event-blue-foreground: var(--color-white);
|
||||
--event-blue-background: var(--color-blue-600);
|
||||
|
||||
--event-indigo-foreground: var(--color-white);
|
||||
--event-indigo-background: var(--color-indigo-600);
|
||||
|
||||
--event-violet-foreground: var(--color-white);
|
||||
--event-violet-background: var(--color-violet-600);
|
||||
|
||||
--event-purple-foreground: var(--color-white);
|
||||
--event-purple-background: var(--color-purple-600);
|
||||
|
||||
--event-fuchsia-foreground: var(--color-white);
|
||||
--event-fuchsia-background: var(--color-fuchsia-700);
|
||||
|
||||
--event-pink-foreground: var(--color-white);
|
||||
--event-pink-background: var(--color-pink-700);
|
||||
|
||||
--event-rose-foreground: var(--color-white);
|
||||
--event-rose-background: var(--color-pink-700);
|
||||
|
||||
--event-black-foreground: var(--color-white);
|
||||
--event-black-background: var(--color-stone-500);
|
||||
|
||||
--event-white-foreground: var(--color-slate-900);
|
||||
--event-white-background: var(--color-lime-100);
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-event-red-foreground: var(--event-red-foreground);
|
||||
--color-event-red-background: var(--event-red-background);
|
||||
|
||||
--color-event-orange-foreground: var(--event-orange-foreground);
|
||||
--color-event-orange-background: var(--event-orange-background);
|
||||
|
||||
--color-event-amber-foreground: var(--event-amber-foreground);
|
||||
--color-event-amber-background: var(--event-amber-background);
|
||||
|
||||
--color-event-yellow-foreground: var(--event-yellow-foreground);
|
||||
--color-event-yellow-background: var(--event-yellow-background);
|
||||
|
||||
--color-event-lime-foreground: var(--event-lime-foreground);
|
||||
--color-event-lime-background: var(--event-lime-background);
|
||||
|
||||
--color-event-green-foreground: var(--event-green-foreground);
|
||||
--color-event-green-background: var(--event-green-background);
|
||||
|
||||
--color-event-emerald-foreground: var(--event-emerald-foreground);
|
||||
--color-event-emerald-background: var(--event-emerald-background);
|
||||
|
||||
--color-event-teal-foreground: var(--event-teal-foreground);
|
||||
--color-event-teal-background: var(--event-teal-background);
|
||||
|
||||
--color-event-cyan-foreground: var(--event-cyan-foreground);
|
||||
--color-event-cyan-background: var(--event-cyan-background);
|
||||
|
||||
--color-event-sky-foreground: var(--event-sky-foreground);
|
||||
--color-event-sky-background: var(--event-sky-background);
|
||||
|
||||
--color-event-blue-foreground: var(--event-blue-foreground);
|
||||
--color-event-blue-background: var(--event-blue-background);
|
||||
|
||||
--color-event-indigo-foreground: var(--event-indigo-foreground);
|
||||
--color-event-indigo-background: var(--event-indigo-background);
|
||||
|
||||
--color-event-violet-foreground: var(--event-violet-foreground);
|
||||
--color-event-violet-background: var(--event-violet-background);
|
||||
|
||||
--color-event-purple-foreground: var(--event-purple-foreground);
|
||||
--color-event-purple-background: var(--event-purple-background);
|
||||
|
||||
--color-event-fuchsia-foreground: var(--event-fuchsia-foreground);
|
||||
--color-event-fuchsia-background: var(--event-fuchsia-background);
|
||||
|
||||
--color-event-pink-foreground: var(--event-pink-foreground);
|
||||
--color-event-pink-background: var(--event-pink-background);
|
||||
|
||||
--color-event-rose-foreground: var(--event-rose-foreground);
|
||||
--color-event-rose-background: var(--event-rose-background);
|
||||
|
||||
--color-event-black-foreground: var(--event-black-foreground);
|
||||
--color-event-black-background: var(--event-black-background);
|
||||
|
||||
--color-event-white-foreground: var(--event-white-foreground);
|
||||
--color-event-white-background: var(--event-white-background);
|
||||
}
|
||||
|
||||
.event-button,
|
||||
.event-popover,
|
||||
.event-callout,
|
||||
.card-color {
|
||||
--fg-color: var(--color-foreground);
|
||||
--bg-color: var(--color-background);
|
||||
|
||||
transition-duration: var(--default-transition-duration);
|
||||
transition-timing-function: var(--default-transition-timing-function);
|
||||
}
|
||||
|
||||
.event-callout hr,
|
||||
.card-color hr {
|
||||
transition-property: border-color;
|
||||
transition-duration: var(--default-transition-duration);
|
||||
transition-timing-function: var(--default-transition-timing-function);
|
||||
}
|
||||
|
||||
.event-button {
|
||||
color: var(--fg-color);
|
||||
background-color: var(--bg-color);
|
||||
text-underline-offset: .15rem;
|
||||
transition-property: background-color, color;
|
||||
}
|
||||
.event-button:hover {
|
||||
text-decoration: underline;
|
||||
background-color: color-mix(in srgb, var(--bg-color) 70%, var(--color-background));
|
||||
}
|
||||
.event-button.is-hidden {
|
||||
background-color: transparent;
|
||||
color: var(--color-foreground);
|
||||
border: 1px dashed var(--bg-color);
|
||||
transition-property: background-color, color, border-color;
|
||||
}
|
||||
.event-button.is-hidden:is(:hover, :focus-visible) {
|
||||
background-color: color-mix(in srgb, var(--bg-color) 70%, var(--color-background));
|
||||
color: var(--fg-color);
|
||||
}
|
||||
|
||||
.event-popover {
|
||||
background-color: color-mix(in srgb, var(--bg-color) 10%, var(--color-background));
|
||||
border-color: color-mix(in srgb, var(--bg-color) 50%, var(--color-background));
|
||||
transition-property: background-color, color;
|
||||
|
||||
hr {
|
||||
border-color: var(--bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
.event-callout {
|
||||
position: relative;
|
||||
background-color: color-mix(in srgb, var(--bg-color) 10%, var(--color-background));
|
||||
border-top-right-radius: .2rem;
|
||||
border-bottom-right-radius: .2rem;
|
||||
transition-property: background-color, color;
|
||||
|
||||
hr {
|
||||
border-color: var(--bg-color);
|
||||
}
|
||||
}
|
||||
.event-callout::before {
|
||||
display: block;
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
inset-block: 0;
|
||||
left: -0.5rem;
|
||||
width: .5rem;
|
||||
background-color: color-mix(in srgb, var(--bg-color) 70%, var(--color-background));
|
||||
border-top-left-radius: .2rem;
|
||||
border-bottom-left-radius: .2rem;
|
||||
transition-property: background-color;
|
||||
transition-duration: var(--default-transition-duration);
|
||||
transition-timing-function: var(--default-transition-timing-function);
|
||||
}
|
||||
.event-callout:is(:hover, :focus-visible) {
|
||||
background-color: color-mix(in srgb, var(--bg-color) 30%, var(--color-background));
|
||||
}
|
||||
.event-callout:is(:hover, :focus-visible)::before {
|
||||
background-color: color-mix(in srgb, var(--bg-color) 90%, var(--color-background));
|
||||
}
|
||||
|
||||
.event-callout.is-hidden {
|
||||
border: 1px dashed var(--bg-color);
|
||||
background-color: transparent;
|
||||
}
|
||||
.event-callout.is-hidden::before {
|
||||
inset-block: -2px;
|
||||
height: calc(100% + 3px);
|
||||
background-color: color-mix(in srgb, var(--bg-color) 40%, var(--color-background));
|
||||
}
|
||||
.event-callout.is-hidden:is(:hover, :focus-visible) {
|
||||
background-color: color-mix(in srgb, var(--bg-color) 5%, var(--color-background));
|
||||
}
|
||||
.event-callout.is-hidden:is(:hover, :focus-visible)::before {
|
||||
background-color: color-mix(in srgb, var(--bg-color) 50%, var(--color-background));
|
||||
}
|
||||
|
||||
.card-color {
|
||||
border-color: color-mix(in srgb, var(--bg-color) 30%, var(--color-background));
|
||||
background-color: color-mix(in srgb, var(--bg-color) 10%, var(--color-background));
|
||||
|
||||
hr {
|
||||
border-color: color-mix(in srgb, var(--bg-color) 30%, var(--color-background))
|
||||
}
|
||||
}
|
||||
.card-color:is(:hover, :focus-visible) {
|
||||
border-color: color-mix(in srgb, var(--bg-color) 40%, var(--color-background));
|
||||
background-color: color-mix(in srgb, var(--bg-color) 20%, var(--color-background));
|
||||
|
||||
hr {
|
||||
border-color: color-mix(in srgb, var(--bg-color) 40%, var(--color-background))
|
||||
}
|
||||
}
|
||||
|
||||
.bgc {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.bgc::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: .75rem;
|
||||
aspect-ratio: 1;
|
||||
margin-right: 0.5em;
|
||||
border-radius: .25rem;
|
||||
background-color: var(--bg-color);
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.element-red {
|
||||
--fg-color: var(--color-event-red-foreground);
|
||||
--bg-color: var(--color-event-red-background);
|
||||
}
|
||||
.element-orange {
|
||||
--fg-color: var(--color-event-orange-foreground);
|
||||
--bg-color: var(--color-event-orange-background);
|
||||
}
|
||||
.element-amber {
|
||||
--fg-color: var(--color-event-amber-foreground);
|
||||
--bg-color: var(--color-event-amber-background);
|
||||
}
|
||||
.element-yellow {
|
||||
--fg-color: var(--color-event-yellow-foreground);
|
||||
--bg-color: var(--color-event-yellow-background);
|
||||
}
|
||||
.element-lime {
|
||||
--fg-color: var(--color-event-lime-foreground);
|
||||
--bg-color: var(--color-event-lime-background);
|
||||
}
|
||||
.element-green {
|
||||
--fg-color: var(--color-event-green-foreground);
|
||||
--bg-color: var(--color-event-green-background);
|
||||
}
|
||||
.element-emerald {
|
||||
--fg-color: var(--color-event-emerald-foreground);
|
||||
--bg-color: var(--color-event-emerald-background);
|
||||
}
|
||||
.element-teal {
|
||||
--fg-color: var(--color-event-teal-foreground);
|
||||
--bg-color: var(--color-event-teal-background);
|
||||
}
|
||||
.element-cyan {
|
||||
--fg-color: var(--color-event-cyan-foreground);
|
||||
--bg-color: var(--color-event-cyan-background);
|
||||
}
|
||||
.element-sky {
|
||||
--fg-color: var(--color-event-sky-foreground);
|
||||
--bg-color: var(--color-event-sky-background);
|
||||
}
|
||||
.element-blue {
|
||||
--fg-color: var(--color-event-blue-foreground);
|
||||
--bg-color: var(--color-event-blue-background);
|
||||
}
|
||||
.element-indigo {
|
||||
--fg-color: var(--color-event-indigo-foreground);
|
||||
--bg-color: var(--color-event-indigo-background);
|
||||
}
|
||||
.element-violet {
|
||||
--fg-color: var(--color-event-violet-foreground);
|
||||
--bg-color: var(--color-event-violet-background);
|
||||
}
|
||||
.element-purple {
|
||||
--fg-color: var(--color-event-purple-foreground);
|
||||
--bg-color: var(--color-event-purple-background);
|
||||
}
|
||||
.element-fuchsia {
|
||||
--fg-color: var(--color-event-fuchsia-foreground);
|
||||
--bg-color: var(--color-event-fuchsia-background);
|
||||
}
|
||||
.element-pink {
|
||||
--fg-color: var(--color-event-pink-foreground);
|
||||
--bg-color: var(--color-event-pink-background);
|
||||
}
|
||||
.element-rose {
|
||||
--fg-color: var(--color-event-rose-foreground);
|
||||
--bg-color: var(--color-event-rose-background);
|
||||
}
|
||||
.element-black {
|
||||
--fg-color: var(--color-event-black-foreground);
|
||||
--bg-color: var(--color-event-black-background);
|
||||
}
|
||||
.element-white {
|
||||
--fg-color: var(--color-event-white-foreground);
|
||||
--bg-color: var(--color-event-white-background);
|
||||
}
|
||||
4
assets/_typography.css
Normal file
4
assets/_typography.css
Normal file
@@ -0,0 +1,4 @@
|
||||
@theme inline {
|
||||
--text-2xs: 0.7rem;
|
||||
--text-2xs--line-height: 0.75rem;
|
||||
}
|
||||
433
assets/main.css
433
assets/main.css
@@ -1,416 +1,33 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@import 'tailwindcss';
|
||||
|
||||
@config '../tailwind.config.js';
|
||||
|
||||
@import "./theme.css" layer(theme);
|
||||
@import "./_typography.css" layer(theme);
|
||||
@import "./_colors.css" layer(theme);
|
||||
@import "./_colors.css" layer(utilities);
|
||||
|
||||
/*
|
||||
The default border color has changed to `currentcolor` in Tailwind CSS v4,
|
||||
so we've added these compatibility styles to make sure everything still
|
||||
looks the same as it did with Tailwind CSS v3.
|
||||
|
||||
If we ever want to remove these styles, we need to add an explicit border
|
||||
color utility to any element that depends on these defaults.
|
||||
*/
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
|
||||
--primary: 245 58% 51%;
|
||||
--primary-foreground: 0 0% 100%;
|
||||
|
||||
--secondary: 210 50% 95%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--success: 156 72% 67%;
|
||||
--success-muted: 156 72% 90%;
|
||||
--success-foreground: 222.2 84% 4.9%;
|
||||
|
||||
--ring: 222.2 84% 4.9%;
|
||||
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 180 0 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
|
||||
--muted: 244 47% 20%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
|
||||
--popover: 180 0 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
|
||||
--card: 180 0 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
|
||||
--border: 217 0 10%;
|
||||
--input: 244 47% 20%;
|
||||
|
||||
--primary: 245 58% 51%;
|
||||
--primary-foreground: 0 0% 100%;
|
||||
|
||||
--secondary: 244 47% 20%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
|
||||
--accent: 244 47% 20%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--success: 142 72% 29%;
|
||||
--success-muted: 142 72% 10%;
|
||||
--success-foreground: 210 40% 98%;
|
||||
|
||||
--ring: 212.7 26.8% 83.9%;
|
||||
*,
|
||||
::after,
|
||||
::before,
|
||||
::backdrop,
|
||||
::file-selector-button {
|
||||
border-color: var(--color-border, currentcolor);
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
/* Colors */
|
||||
.event-red:not(.is-hidden) {
|
||||
@apply text-slate-900 bg-red-500 hover:bg-red-600 focus-visible:bg-red-600
|
||||
}
|
||||
.event-orange:not(.is-hidden) {
|
||||
@apply text-slate-900 bg-orange-500 hover:bg-orange-600 focus-visible:bg-orange-600
|
||||
}
|
||||
.event-amber:not(.is-hidden) {
|
||||
@apply text-slate-900 bg-amber-500 hover:bg-amber-600 focus-visible:bg-amber-600
|
||||
}
|
||||
.event-yellow:not(.is-hidden) {
|
||||
@apply text-slate-900 bg-yellow-600 hover:bg-yellow-700 focus-visible:bg-yellow-700
|
||||
}
|
||||
.event-lime:not(.is-hidden) {
|
||||
@apply text-slate-900 bg-lime-500 hover:bg-lime-600 focus-visible:bg-lime-600
|
||||
}
|
||||
.event-green:not(.is-hidden) {
|
||||
@apply text-white bg-green-600 hover:bg-green-700 focus-visible:bg-green-700
|
||||
}
|
||||
.event-emerald:not(.is-hidden) {
|
||||
@apply text-white bg-emerald-600 hover:bg-emerald-700 focus-visible:bg-emerald-700
|
||||
}
|
||||
.event-teal:not(.is-hidden) {
|
||||
@apply text-white bg-teal-600 hover:bg-teal-700 focus-visible:bg-teal-700
|
||||
}
|
||||
.event-cyan:not(.is-hidden) {
|
||||
@apply text-white bg-cyan-600 hover:bg-cyan-700 focus-visible:bg-cyan-700
|
||||
}
|
||||
.event-sky:not(.is-hidden) {
|
||||
@apply text-white bg-sky-600 hover:bg-sky-700 focus-visible:bg-sky-700
|
||||
}
|
||||
.event-blue:not(.is-hidden) {
|
||||
@apply text-white bg-blue-600 hover:bg-blue-700 focus-visible:bg-blue-700
|
||||
}
|
||||
.event-indigo:not(.is-hidden) {
|
||||
@apply text-white bg-indigo-600 hover:bg-indigo-700 focus-visible:bg-indigo-700
|
||||
}
|
||||
.event-violet:not(.is-hidden) {
|
||||
@apply text-white bg-violet-600 hover:bg-violet-700 focus-visible:bg-violet-700
|
||||
}
|
||||
.event-purple:not(.is-hidden) {
|
||||
@apply text-white bg-purple-600 hover:bg-purple-700 focus-visible:bg-purple-700
|
||||
}
|
||||
.event-fuchsia:not(.is-hidden) {
|
||||
@apply text-white bg-fuchsia-600 hover:bg-fuchsia-700 focus-visible:bg-fuchsia-700
|
||||
}
|
||||
.event-pink:not(.is-hidden) {
|
||||
@apply text-white bg-pink-700 hover:bg-pink-800 focus-visible:bg-pink-800
|
||||
}
|
||||
.event-rose:not(.is-hidden) {
|
||||
@apply text-white bg-rose-700 hover:bg-rose-800 focus-visible:bg-rose-800
|
||||
}
|
||||
.event-black:not(.is-hidden) {
|
||||
@apply text-white bg-stone-500 hover:bg-stone-700 focus-visible:bg-stone-700
|
||||
}
|
||||
.event-white:not(.is-hidden) {
|
||||
@apply text-slate-900 bg-white hover:bg-yellow-200 focus-visible:bg-yellow-200 border-[1px] border-slate-300
|
||||
}
|
||||
|
||||
.event.is-hidden {
|
||||
border: 1px dashed transparent;
|
||||
}
|
||||
|
||||
.event-red.is-hidden {
|
||||
@apply text-red-600 border-red-600 hover:bg-red-100 focus-visible:bg-red-100 dark:text-white dark:hover:bg-red-950 dark:focus-visible:bg-red-950
|
||||
}
|
||||
.event-orange.is-hidden {
|
||||
@apply text-orange-600 border-orange-600 hover:bg-orange-100 focus-visible:bg-orange-100 dark:text-white dark:hover:bg-orange-950 dark:focus-visible:bg-orange-950
|
||||
}
|
||||
.event-amber.is-hidden {
|
||||
@apply text-red-600 border-red-600 hover:bg-red-100 focus-visible:bg-red-100 dark:text-white dark:hover:bg-red-950 dark:focus-visible:bg-red-950
|
||||
}
|
||||
.event-yellow.is-hidden {
|
||||
@apply text-yellow-600 border-yellow-600 hover:bg-yellow-100 focus-visible:bg-yellow-100 dark:text-white dark:hover:bg-yellow-950 dark:focus-visible:bg-yellow-950
|
||||
}
|
||||
.event-lime.is-hidden {
|
||||
@apply text-lime-600 border-lime-600 hover:bg-lime-100 focus-visible:bg-lime-100 dark:text-white dark:hover:bg-lime-950 dark:focus-visible:bg-lime-950
|
||||
}
|
||||
.event-green.is-hidden {
|
||||
@apply text-green-600 border-green-600 hover:bg-green-700 focus-visible:bg-green-700 dark:text-white dark:hover:bg-green-950 dark:focus-visible:bg-green-950
|
||||
}
|
||||
.event-emerald.is-hidden {
|
||||
@apply text-emerald-600 border-emerald-600 hover:bg-emerald-700 focus-visible:bg-emerald-700 dark:text-white dark:hover:bg-emerald-950 dark:focus-visible:bg-emerald-950
|
||||
}
|
||||
.event-teal.is-hidden {
|
||||
@apply text-teal-700 border-teal-600 hover:bg-teal-700 focus-visible:bg-teal-700 dark:text-white dark:hover:bg-teal-950 dark:focus-visible:bg-teal-950
|
||||
}
|
||||
.event-cyan.is-hidden {
|
||||
@apply text-cyan-600 border-cyan-600 hover:bg-cyan-100 focus-visible:bg-cyan-100 dark:text-white dark:hover:bg-cyan-950 dark:focus-visible:bg-cyan-950
|
||||
}
|
||||
.event-sky.is-hidden {
|
||||
@apply text-sky-600 border-sky-600 hover:bg-sky-100 focus-visible:bg-sky-100 dark:text-white dark:hover:bg-sky-950 dark:focus-visible:bg-sky-950
|
||||
}
|
||||
.event-blue.is-hidden {
|
||||
@apply text-blue-600 border-blue-600 hover:bg-blue-100 focus-visible:bg-blue-100 dark:text-white dark:hover:bg-blue-950 dark:focus-visible:bg-blue-950
|
||||
}
|
||||
.event-indigo.is-hidden {
|
||||
@apply text-indigo-600 border-indigo-600 hover:bg-indigo-100 focus-visible:bg-indigo-100 dark:text-white dark:hover:bg-indigo-950 dark:focus-visible:bg-indigo-950
|
||||
}
|
||||
.event-violet.is-hidden {
|
||||
@apply text-violet-600 border-violet-600 hover:bg-violet-100 focus-visible:bg-violet-100 dark:text-white dark:hover:bg-violet-950 dark:focus-visible:bg-violet-950
|
||||
}
|
||||
.event-purple.is-hidden {
|
||||
@apply text-purple-600 border-purple-600 hover:bg-purple-100 focus-visible:bg-purple-100 dark:text-white dark:hover:bg-purple-950 dark:focus-visible:bg-purple-950
|
||||
}
|
||||
.event-fuchsia.is-hidden {
|
||||
@apply text-fuchsia-600 border-fuchsia-600 hover:bg-fuchsia-100 focus-visible:bg-fuchsia-100 dark:text-white dark:hover:bg-fuchsia-950 dark:focus-visible:bg-fuchsia-950
|
||||
}
|
||||
.event-pink.is-hidden {
|
||||
@apply text-pink-700 border-pink-700 hover:bg-pink-100 focus-visible:bg-pink-100 dark:text-white dark:hover:bg-pink-950 dark:focus-visible:bg-pink-950
|
||||
}
|
||||
.event-rose.is-hidden {
|
||||
@apply text-rose-700 border-rose-700 hover:bg-rose-100 focus-visible:bg-rose-100 dark:text-white dark:hover:bg-rose-950 dark:focus-visible:bg-rose-950
|
||||
}
|
||||
.event-black.is-hidden {
|
||||
@apply text-stone-700 border-stone-500 hover:bg-stone-100 focus-visible:bg-stone-100 dark:text-white dark:hover:bg-stone-950 dark:focus-visible:bg-stone-950
|
||||
}
|
||||
.event-white.is-hidden {
|
||||
@apply text-slate-900 border-neutral-400 hover:bg-yellow-200 focus-visible:bg-yellow-200 dark:bg-neutral-900 dark:text-white dark:hover:bg-neutral-700 dark:focus-visible:bg-neutral-300
|
||||
}
|
||||
|
||||
.dark .event-details-red {
|
||||
--base-color: var(--color-red-800);
|
||||
}
|
||||
.dark .event-details-orange {
|
||||
--base-color: var(--color-orange-800);
|
||||
}
|
||||
.dark .event-details-amber {
|
||||
--base-color: var(--color-amber-800);
|
||||
}
|
||||
.dark .event-details-yellow {
|
||||
--base-color: var(--color-yellow-800);
|
||||
}
|
||||
.dark .event-details-lime {
|
||||
--base-color: var(--color-lime-800);
|
||||
}
|
||||
.dark .event-details-green {
|
||||
--base-color: var(--color-green-800);
|
||||
}
|
||||
.dark .event-details-emerald {
|
||||
--base-color: var(--color-emerald-800);
|
||||
}
|
||||
.dark .event-details-teal {
|
||||
--base-color: var(--color-teal-800);
|
||||
}
|
||||
.dark .event-details-cyan {
|
||||
--base-color: var(--color-cyan-800);
|
||||
}
|
||||
.dark .event-details-sky {
|
||||
--base-color: var(--color-sky-800);
|
||||
}
|
||||
.dark .event-details-blue {
|
||||
--base-color: var(--color-blue-800);
|
||||
}
|
||||
.dark .event-details-indigo {
|
||||
--base-color: var(--color-indigo-800);
|
||||
}
|
||||
.dark .event-details-violet {
|
||||
--base-color: var(--color-violet-800);
|
||||
}
|
||||
.dark .event-details-purple {
|
||||
--base-color: var(--color-purple-800);
|
||||
}
|
||||
.dark .event-details-fuchsia {
|
||||
--base-color: var(--color-fuchsia-800);
|
||||
}
|
||||
.dark .event-details-pink {
|
||||
--base-color: var(--color-pink-800);
|
||||
}
|
||||
.dark .event-details-rose {
|
||||
--base-color: var(--color-rose-800);
|
||||
}
|
||||
.dark .event-details-black {
|
||||
--base-color: var(--color-stone-800);
|
||||
}
|
||||
.dark .event-details-white {
|
||||
--base-color: var(--color-white);
|
||||
}
|
||||
|
||||
.event-details-red {
|
||||
--base-color: var(--color-red-300);
|
||||
}
|
||||
.event-details-orange {
|
||||
--base-color: var(--color-orange-300);
|
||||
}
|
||||
.event-details-amber {
|
||||
--base-color: var(--color-amber-300);
|
||||
}
|
||||
.event-details-yellow {
|
||||
--base-color: var(--color-yellow-300);
|
||||
}
|
||||
.event-details-lime {
|
||||
--base-color: var(--color-lime-300);
|
||||
}
|
||||
.event-details-green {
|
||||
--base-color: var(--color-green-300);
|
||||
}
|
||||
.event-details-emerald {
|
||||
--base-color: var(--color-emerald-300);
|
||||
}
|
||||
.event-details-teal {
|
||||
--base-color: var(--color-teal-300);
|
||||
}
|
||||
.event-details-cyan {
|
||||
--base-color: var(--color-cyan-300);
|
||||
}
|
||||
.event-details-sky {
|
||||
--base-color: var(--color-sky-300);
|
||||
}
|
||||
.event-details-blue {
|
||||
--base-color: var(--color-blue-300);
|
||||
}
|
||||
.event-details-indigo {
|
||||
--base-color: var(--color-indigo-300);
|
||||
}
|
||||
.event-details-violet {
|
||||
--base-color: var(--color-violet-300);
|
||||
}
|
||||
.event-details-purple {
|
||||
--base-color: var(--color-purple-300);
|
||||
}
|
||||
.event-details-fuchsia {
|
||||
--base-color: var(--color-fuchsia-300);
|
||||
}
|
||||
.event-details-pink {
|
||||
--base-color: var(--color-pink-300);
|
||||
}
|
||||
.event-details-rose {
|
||||
--base-color: var(--color-rose-300);
|
||||
}
|
||||
.event-details-black {
|
||||
--base-color: var(--color-stone-300);
|
||||
}
|
||||
.event-details-white {
|
||||
--base-color: var(--color-white);
|
||||
}
|
||||
|
||||
.event-details {
|
||||
--bg-color: color-mix(in srgb, var(--base-color), var(--color-white) 80%);
|
||||
--border-color: color-mix(in srgb, var(--base-color), var(--color-white) 50%);
|
||||
background-color: var(--bg-color);
|
||||
border-color: var(--border-color);
|
||||
|
||||
hr {
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
}
|
||||
|
||||
.dark .event-details {
|
||||
--bg-color: color-mix(in srgb, var(--base-color), var(--color-slate-950) 80%);
|
||||
--border-color: color-mix(in srgb, var(--base-color), var(--color-slate-800) 50%);
|
||||
}
|
||||
|
||||
.bgc {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.bgc::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: .75rem;
|
||||
aspect-ratio: 1;
|
||||
margin-right: 0.5em;
|
||||
border-radius: .25rem;
|
||||
background-color: red;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.bgc-red::before {
|
||||
@apply bg-red-500;
|
||||
}
|
||||
.bgc-orange::before {
|
||||
@apply bg-orange-500;
|
||||
}
|
||||
.bgc-amber::before {
|
||||
@apply bg-amber-500;
|
||||
}
|
||||
.bgc-yellow::before {
|
||||
@apply bg-yellow-500;
|
||||
}
|
||||
.bgc-lime::before {
|
||||
@apply bg-lime-500;
|
||||
}
|
||||
.bgc-green::before {
|
||||
@apply bg-green-500;
|
||||
}
|
||||
.bgc-emerald::before {
|
||||
@apply bg-emerald-600;
|
||||
}
|
||||
.bgc-teal::before {
|
||||
@apply bg-teal-600;
|
||||
}
|
||||
.bgc-cyan::before {
|
||||
@apply bg-cyan-600;
|
||||
}
|
||||
.bgc-sky::before {
|
||||
@apply bg-sky-600;
|
||||
}
|
||||
.bgc-blue::before {
|
||||
@apply bg-blue-600;
|
||||
}
|
||||
.bgc-indigo::before {
|
||||
@apply bg-indigo-600;
|
||||
}
|
||||
.bgc-violet::before {
|
||||
@apply bg-violet-600;
|
||||
}
|
||||
.bgc-purple::before {
|
||||
@apply bg-purple-600;
|
||||
}
|
||||
.bgc-fuchsia::before {
|
||||
@apply bg-fuchsia-600;
|
||||
}
|
||||
.bgc-pink::before {
|
||||
@apply bg-pink-600;
|
||||
}
|
||||
.bgc-rose::before {
|
||||
@apply bg-rose-600;
|
||||
}
|
||||
.bgc-black::before {
|
||||
@apply bg-black dark:border-[1px] dark:border-slate-300;
|
||||
}
|
||||
.bgc-white::before {
|
||||
@apply bg-white border-[1px] border-slate-700;
|
||||
body {
|
||||
color: var(--color-foreground);
|
||||
background-color: var(--color-background);
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
|
||||
108
assets/theme.css
Normal file
108
assets/theme.css
Normal file
@@ -0,0 +1,108 @@
|
||||
:root {
|
||||
--background: hsl(0 0% 100%);
|
||||
--foreground: hsl(222.2 84% 4.9%);
|
||||
|
||||
--muted: hsl(210 40% 96.1%);
|
||||
--muted-foreground: hsl(215.4 16.3% 46.9%);
|
||||
|
||||
--popover: hsl(0 0% 100%);
|
||||
--popover-foreground: hsl(222.2 84% 4.9%);
|
||||
|
||||
--card: hsl(0 0% 100%);
|
||||
--card-foreground: hsl(222.2 84% 4.9%);
|
||||
|
||||
--border: hsl(214.3 31.8% 91.4%);
|
||||
--input: hsl(214.3 31.8% 91.4%);
|
||||
|
||||
--primary: hsl(245 58% 51%);
|
||||
--primary-foreground: hsl(0 0% 100%);
|
||||
|
||||
--secondary: hsl(210 50% 95%);
|
||||
--secondary-foreground: hsl(222.2 47.4% 11.2%);
|
||||
|
||||
--accent: hsl(210 40% 96.1%);
|
||||
--accent-foreground: hsl(222.2 47.4% 11.2%);
|
||||
|
||||
--destructive: hsl(0 84.2% 60.2%);
|
||||
--destructive-foreground: hsl(210 40% 98%);
|
||||
|
||||
--success: hsl(156 72% 67%);
|
||||
--success-muted: hsl(156 72% 90%);
|
||||
--success-foreground: hsl(222.2 84% 4.9%);
|
||||
|
||||
--ring: hsl(222.2 84% 4.9%);
|
||||
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: hsl(180 0 4.9%);
|
||||
--foreground: hsl(210 40% 98%);
|
||||
|
||||
--muted: hsl(0, 0%, 12%);
|
||||
--muted-foreground: hsl(215 20.2% 65.1%);
|
||||
|
||||
--popover: hsl(180 0 4.9%);
|
||||
--popover-foreground: hsl(210 40% 98%);
|
||||
|
||||
--card: hsl(180 0 4.9%);
|
||||
--card-foreground: hsl(210 40% 98%);
|
||||
|
||||
--border: hsl(0, 0%, 16%);
|
||||
--input: hsl(244 47% 20%);
|
||||
|
||||
--primary: hsl(245 58% 51%);
|
||||
--primary-foreground: hsl(0 0% 100%);
|
||||
|
||||
--secondary: hsl(244 47% 20%);
|
||||
--secondary-foreground: hsl(210 40% 98%);
|
||||
|
||||
--accent: hsl(244 47% 20%);
|
||||
--accent-foreground: hsl(210 40% 98%);
|
||||
|
||||
--destructive: hsl(0 62.8% 30.6%);
|
||||
--destructive-foreground: hsl(210 40% 98%);
|
||||
|
||||
--success: hsl(142 72% 29%);
|
||||
--success-muted: hsl(142 72% 10%);
|
||||
--success-foreground: hsl(210 40% 98%);
|
||||
|
||||
--ring: hsl(212.7 26.8% 83.9%);
|
||||
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
|
||||
--color-destructive: var(--destructive);
|
||||
--color-destructive-foreground: var(--destructive-foreground);
|
||||
|
||||
--color-success: var(--success);
|
||||
--color-success-muted: var(--success-muted);
|
||||
--color-success-foreground: var(--success-foreground);
|
||||
|
||||
--color-ring: var(--ring);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { useCalendar } from "@/stores/CalendarStore"
|
||||
import { useCalendar } from "~/stores/CalendarStore"
|
||||
import { computed, type Component, type ComputedRef } from "vue"
|
||||
|
||||
// import { PhMagnifyingGlass } from '@phosphor-icons/vue'
|
||||
@@ -35,12 +35,12 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<div class="h-full w-full relative">
|
||||
<div class="h-full grid grid-rows-[auto,1fr]">
|
||||
<div class="h-full grid grid-rows-[auto_1fr]">
|
||||
<CalendarMenu />
|
||||
|
||||
<component :is="currentViewComponent" />
|
||||
|
||||
<LazyCalendarSearch />
|
||||
<CalendarSearch />
|
||||
<LazyCalendarDialogCategories v-if="!isReadOnly" />
|
||||
<LazyCalendarDialogUpdateEvent v-if="!isReadOnly" />
|
||||
<LazyCalendarDialogDeleteEvent v-if="!isReadOnly" />
|
||||
|
||||
@@ -9,9 +9,11 @@ const { isReadOnly } = storeToRefs(useCalendar())
|
||||
<UiTooltipProvider :delay-duration="250">
|
||||
<UiTooltip>
|
||||
<UiTooltipTrigger as-child>
|
||||
<UiButton v-if="!isReadOnly" variant="secondary" size="icon" @click="toggleCategoriesModal(true)">
|
||||
<PhTag size="20" weight="light" />
|
||||
</UiButton>
|
||||
<ClientOnly>
|
||||
<UiButton v-if="!isReadOnly" variant="secondary" size="icon" @click="toggleCategoriesModal(true)">
|
||||
<PhTag size="20" weight="light" />
|
||||
</UiButton>
|
||||
</ClientOnly>
|
||||
</UiTooltipTrigger>
|
||||
<UiTooltipContent>
|
||||
<p>
|
||||
|
||||
@@ -3,10 +3,15 @@ import { storeToRefs } from "pinia"
|
||||
import { computed } from "vue"
|
||||
|
||||
import { PhMapPin } from "@phosphor-icons/vue"
|
||||
import { breakpointsTailwind } from "@vueuse/core"
|
||||
|
||||
const { defaultDate, getFormattedDateTitle, getRelativeString, getDifferenceInDays } = useCalendar()
|
||||
const { selectedDate } = storeToRefs(useCalendar())
|
||||
|
||||
const breakpoints = useBreakpoints(
|
||||
breakpointsTailwind
|
||||
)
|
||||
|
||||
const mainDateTitle = computed(() => getFormattedDateTitle(selectedDate.value, true))
|
||||
// const mainDateTitle = computed(() => convertDateToDays(selectedDate.value))
|
||||
|
||||
@@ -16,19 +21,24 @@ const isToday = computed(() => getDifferenceInDays(defaultDate, selectedDate.val
|
||||
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<div class="flex gap-2 items-center">
|
||||
<h1 class="text-2xl font-bold flex items-center gap-1">
|
||||
<PhMapPin size="26" weight="bold" /> {{ mainDateTitle }}
|
||||
<div class="grid md:flex md:gap-2 items-center">
|
||||
<h1 class="text-lg md:text-2xl max-md:leading-tight font-bold flex items-center gap-1">
|
||||
<PhMapPin
|
||||
:size="breakpoints.md.value ? 26 : 18"
|
||||
class="max-md:hidden"
|
||||
weight="light"
|
||||
/>
|
||||
{{ mainDateTitle }}
|
||||
</h1>
|
||||
<h2 v-if="!isToday" class="text-xl italic opacity-75">
|
||||
– {{ dateDifference }}
|
||||
<h2 v-if="!isToday" class="text-sm max-md:leading-tight md:text-xl italic opacity-75">
|
||||
<span class="max-md:hidden">–</span> {{ dateDifference }}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<template #fallback>
|
||||
<div class="flex items-center gap-1">
|
||||
<UiSkeleton class="h-8 w-64" />
|
||||
<UiSkeleton class="h-6 w-28" />
|
||||
<UiSkeleton class="h-8 w-64 max-md:max-w-full" />
|
||||
<UiSkeleton class="max-md:hidden h-6 w-28" />
|
||||
</div>
|
||||
</template>
|
||||
</ClientOnly>
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from "@/lib/utils"
|
||||
import { useCalendar } from "@/stores/CalendarStore"
|
||||
import { PhCalendarBlank, PhCheckCircle, PhGear } from "@phosphor-icons/vue"
|
||||
import { computed } from "vue"
|
||||
import { breakpointsTailwind } from "@vueuse/core"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { useCalendar } from "~/stores/CalendarStore"
|
||||
import { PhCalendarBlank, PhCheckCircle, PhDotsThreeVertical, PhGear, PhTag } from "@phosphor-icons/vue"
|
||||
|
||||
const { currentConfig, viewTypeOptions, getViewTypeTitle, setViewType } = useCalendar()
|
||||
const breakpoints = useBreakpoints(
|
||||
breakpointsTailwind
|
||||
)
|
||||
|
||||
const { currentConfig, viewTypeOptions, getViewTypeTitle, setViewType, toggleCategoriesModal } = useCalendar()
|
||||
const viewTypeTitle = computed(() => getViewTypeTitle(currentConfig.viewType))
|
||||
</script>
|
||||
|
||||
@@ -12,15 +16,29 @@ const viewTypeTitle = computed(() => getViewTypeTitle(currentConfig.viewType))
|
||||
<UiDropdownMenu>
|
||||
<UiDropdownMenuTrigger as-child>
|
||||
<UiButton variant="secondary" size="icon">
|
||||
<PhGear size="20" weight="fill" />
|
||||
<ClientOnly>
|
||||
<PhGear v-if="breakpoints.md.value" size="20" weight="fill" />
|
||||
<PhDotsThreeVertical v-else size="26" weight="bold" />
|
||||
</ClientOnly>
|
||||
</UiButton>
|
||||
</UiDropdownMenuTrigger>
|
||||
|
||||
<UiDropdownMenuContent :side="'bottom'" :align="'start'" :side-offset="10" :align-offset="25" :collision-padding="40" class="text-right">
|
||||
<UiDropdownMenuContent :side="'bottom'" :align="'start'" :side-offset="10" :align-offset="25" :collision-padding="20" class="text-right">
|
||||
<UiDropdownMenuArrow />
|
||||
<UiDropdownMenuLabel>
|
||||
{{ $t('entity.calendar.seeOptions') }}
|
||||
</UiDropdownMenuLabel>
|
||||
|
||||
<UiDropdownMenuItem
|
||||
v-if="!breakpoints.md.value"
|
||||
class="flex gap-[1ch] justify-end items-center"
|
||||
@click="toggleCategoriesModal(true)"
|
||||
>
|
||||
{{ $t('entity.calendar.seeCategories') }}
|
||||
|
||||
<PhTag size="18" />
|
||||
</UiDropdownMenuItem>
|
||||
|
||||
<UiDropdownMenuSub>
|
||||
<UiDropdownMenuSubTrigger arrow-direction="left" class="p-0 rounded-none">
|
||||
<UiDropdownMenuItem class="flex gap-[1ch] justify-end items-center pointer-events-none">
|
||||
|
||||
@@ -1,55 +1,51 @@
|
||||
<script lang="ts" setup>
|
||||
import { PhArchive, PhCalendarDots, PhFile, PhFileDashed, PhFilePlus, PhPencil, PhPencilSimpleLine, PhTrash } from "@phosphor-icons/vue";
|
||||
import { DateTime } from "luxon";
|
||||
import type { Calendar } from "~/models/CalendarConfig";
|
||||
import { cn } from "~/lib/utils"
|
||||
import { PhArchive, PhCalendarDots, PhFile, PhFileDashed, PhFilePlus, PhPencil, PhPencilSimpleLine, PhTrash } from "@phosphor-icons/vue"
|
||||
import { DateTime } from "luxon"
|
||||
import type { Calendar } from "~/models/CalendarConfig"
|
||||
|
||||
const props = defineProps<{
|
||||
calendar: Calendar,
|
||||
gmId?: string,
|
||||
showActions?: boolean,
|
||||
calendar: Calendar
|
||||
gmId?: string
|
||||
showActions?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(["on-edit", "on-delete"])
|
||||
|
||||
const { locale } = useI18n();
|
||||
const cardRef = ref(null)
|
||||
const isCardHovered = useElementHover(cardRef)
|
||||
const { focused: isCardFocused } = useFocusWithin(cardRef)
|
||||
|
||||
const createdAt = DateTime.fromISO(props.calendar.createdAt!).toLocaleString(DateTime.DATETIME_MED, { locale: locale.value });
|
||||
const updatedAt = computed<string>(() => props.calendar.updatedAt ? DateTime.fromISO(props.calendar.updatedAt).toLocaleString(DateTime.DATETIME_MED, { locale: locale.value }) : "");
|
||||
const { locale } = useI18n()
|
||||
|
||||
const user = useSupabaseUser();
|
||||
const isOwner = computed(() => user.value && props.gmId && user.value.id === props.gmId);
|
||||
const calendarLink = computed(() => isOwner.value ? `/my/calendars/${props.calendar.id}` : `/calendars/${props.calendar.shortId}`);
|
||||
const createdAt = DateTime.fromISO(props.calendar.createdAt!).toLocaleString(DateTime.DATETIME_MED, { locale: locale.value })
|
||||
const updatedAt = computed<string>(() => props.calendar.updatedAt ? DateTime.fromISO(props.calendar.updatedAt).toLocaleString(DateTime.DATETIME_MED, { locale: locale.value }) : "")
|
||||
|
||||
const user = useSupabaseUser()
|
||||
const isOwner = computed(() => user.value && props.gmId && user.value.id === props.gmId)
|
||||
const calendarLink = computed(() => isOwner.value ? `/my/calendars/${props.calendar.id}` : `/calendars/${props.calendar.shortId}`)
|
||||
|
||||
const hasActions = computed(() => isOwner.value && props.showActions)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UiCard
|
||||
class="w-full h-full flex flex-col transition-all hover:bg-slate-50 dark:bg-gray-950 dark:hover:bg-indigo-950 dark:focus-within:outline-gray-900"
|
||||
ref="cardRef"
|
||||
class="w-full h-full flex flex-col"
|
||||
:link="calendarLink"
|
||||
:class="{
|
||||
'hover:bg-slate-50 dark:hover:bg-sky-950 dark:focus-within:outline-sky-900': !calendar.color,
|
||||
'bg-red-100 dark:bg-red-950 border-red-200 hover:bg-red-50 dark:hover:bg-red-900 dark:border-red-900 dark:focus-within:outline-red-900': calendar.color === 'red',
|
||||
'bg-orange-100 dark:bg-orange-950 border-orange-200 hover:bg-orange-50 dark:hover:bg-orange-900 dark:border-orange-900 dark:focus-within:outline-orange-900': calendar.color === 'orange',
|
||||
'bg-amber-100 dark:bg-amber-950 border-amber-200 hover:bg-amber-50 dark:hover:bg-amber-900 dark:border-amber-900 dark:focus-within:outline-amber-900': calendar.color === 'amber',
|
||||
'bg-yellow-100 dark:bg-yellow-950 border-yellow-200 hover:bg-yellow-50 dark:hover:bg-yellow-900 dark:border-yellow-900 dark:focus-within:outline-yellow-900': calendar.color === 'yellow',
|
||||
'bg-lime-100 dark:bg-lime-950 border-lime-200 hover:bg-lime-50 dark:hover:bg-lime-900 dark:border-lime-900 dark:focus-within:outline-lime-900': calendar.color === 'lime',
|
||||
' bg-green-100 dark:bg-green-950 border-green-200 hover:bg-green-50 dark:hover:bg-green-900 dark:border-green-900 dark:focus-within:outline-green-900': calendar.color === 'green',
|
||||
'bg-emerald-100 dark:bg-emerald-950 border-emerald-200 hover:bg-emerald-50 dark:hover:bg-emerald-900 dark:border-emerald-900 dark:focus-within:outline-emerald-900': calendar.color === 'emerald',
|
||||
'bg-teal-100 dark:bg-teal-950 border-teal-200 hover:bg-teal-50 dark:hover:bg-teal-900 dark:border-teal-900 dark:focus-within:outline-teal-900': calendar.color === 'teal',
|
||||
'bg-cyan-100 dark:bg-cyan-950 border-cyan-200 hover:bg-cyan-50 dark:hover:bg-cyan-900 dark:border-cyan-900 dark:focus-within:outline-cyan-900': calendar.color === 'cyan',
|
||||
'bg-sky-100 dark:bg-sky-950 border-sky-200 hover:bg-sky-50 dark:hover:bg-sky-900 dark:border-sky-900 dark:focus-within:outline-sky-900': calendar.color === 'sky',
|
||||
'bg-blue-100 dark:bg-blue-950 border-blue-200 hover:bg-blue-50 dark:hover:bg-blue-900 dark:border-blue-900 dark:focus-within:outline-blue-900': calendar.color === 'blue',
|
||||
'bg-indigo-100 dark:bg-indigo-950 border-indigo-200 hover:bg-indigo-50 dark:hover:bg-indigo-900 dark:border-indigo-900 dark:focus-within:outline-indigo-900': calendar.color === 'indigo',
|
||||
'bg-violet-100 dark:bg-violet-950 border-violet-200 hover:bg-violet-50 dark:hover:bg-violet-900 dark:border-violet-900 dark:focus-within:outline-violet-900': calendar.color === 'violet',
|
||||
'bg-purple-100 dark:bg-purple-950 border-purple-200 hover:bg-purple-50 dark:hover:bg-purple-900 dark:border-purple-900 dark:focus-within:outline-purple-900': calendar.color === 'purple',
|
||||
'bg-fuchsia-100 dark:bg-fuchsia-950 border-fuchsia-200 hover:bg-fuchsia-50 dark:hover:bg-fuchsia-900 dark:border-fuchsia-900 dark:focus-within:outline-fuchsia-900': calendar.color === 'fuchsia',
|
||||
'bg-pink-100 dark:bg-pink-950 border-pink-200 hover:bg-pink-50 dark:hover:bg-pink-900 dark:border-pink-900 dark:focus-within:outline-pink-900': calendar.color === 'pink',
|
||||
'bg-pink-100 dark:bg-rose-950 border-rose-200 hover:bg-rose-50 dark:hover:bg-rose-900 dark:border-rose-900 dark:focus-within:outline-rose-900': calendar.color === 'rose',
|
||||
'text-slate-100 bg-slate-900 border-slate-700 hover:bg-slate-700 dark:hover:bg-slate-800 dark:border-slate-900 dark:focus-within:outline-slate-900': calendar.color === 'black',
|
||||
' hover:bg-slate-50 dark:hover:text-slate-900 dark:hover:bg-slate-200 dark:border-slate-700 dark:focus-within:outline-slate-100': calendar.color === 'white',
|
||||
}"
|
||||
:class="cn(
|
||||
calendar.color ? `group card-color element-${calendar.color}` : '',
|
||||
)"
|
||||
>
|
||||
<UiCardHeader class="gap-4">
|
||||
<UiCardTitle class="text-xl pr-12">{{ calendar.name }}</UiCardTitle>
|
||||
<UiCardTitle
|
||||
class="text-xl"
|
||||
:class="cn({
|
||||
'pr-18': hasActions
|
||||
})"
|
||||
>
|
||||
{{ calendar.name }}
|
||||
</UiCardTitle>
|
||||
|
||||
<div v-if="calendar.state === 'published'" class="flex items-center gap-1 text-sm">
|
||||
<PhFile size="20" weight="fill" />
|
||||
@@ -73,20 +69,25 @@ const calendarLink = computed(() => isOwner.value ? `/my/calendars/${props.calen
|
||||
</p>
|
||||
|
||||
<div
|
||||
v-if="isOwner && showActions"
|
||||
class="flex gap-1 absolute top-4 right-4 z-20"
|
||||
v-if="hasActions && (isCardHovered || isCardFocused)"
|
||||
class="flex gap-2 absolute top-4 right-4 z-20"
|
||||
>
|
||||
<UiButton size="icon" variant="ghost" class=" hover:text-white hover:bg-indigo-400 dark:hover:bg-indigo-700" @click="emit('on-edit')">
|
||||
<PhPencil size="16" />
|
||||
</UiButton>
|
||||
|
||||
<UiButton size="icon" variant="ghost" class=" hover:text-white hover:bg-rose-400 dark:hover:bg-rose-700" @click="emit('on-delete')">
|
||||
<PhTrash size="16" />
|
||||
</UiButton>
|
||||
<Transition name="fade" appear>
|
||||
<UiButton size="icon" variant="outline" class="size-8 border-foreground/20 group-hover:border-foreground/30 hover:text-background hover:border-foreground hover:bg-foreground" @click="emit('on-edit')">
|
||||
<PhPencil size="15" weight="fill" />
|
||||
</UiButton>
|
||||
</Transition>
|
||||
<Transition name="fade" appear>
|
||||
<UiButton size="icon" variant="outline" class="size-8 border-foreground/25 group-hover:border-foreground/40 hover:text-background hover:border-rose-500 hover:bg-rose-500" @click="emit('on-delete')">
|
||||
<PhTrash size="15" weight="fill" />
|
||||
</UiButton>
|
||||
</Transition>
|
||||
</div>
|
||||
</UiCardContent>
|
||||
|
||||
<UiCardFooter>
|
||||
<hr>
|
||||
|
||||
<UiCardFooter class="border-t-0">
|
||||
<ul class="grid gap-1 text-sm">
|
||||
<li class="flex gap-1 items-center">
|
||||
<PhFilePlus size="18" />
|
||||
|
||||
28
components/calendar/SearchCTA.vue
Normal file
28
components/calendar/SearchCTA.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core"
|
||||
import { PhMagnifyingGlass } from "@phosphor-icons/vue"
|
||||
|
||||
const { revealAdvancedSearch } = useCalendar()
|
||||
|
||||
const breakpoints = useBreakpoints(
|
||||
breakpointsTailwind
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<Transition name="fade" appear>
|
||||
<UiButton
|
||||
:search-slash="breakpoints.lg.value"
|
||||
:size="breakpoints.lg.value ? 'default' : 'icon'"
|
||||
@click="revealAdvancedSearch()"
|
||||
>
|
||||
<PhMagnifyingGlass size="20" weight="light" />
|
||||
|
||||
<span v-if="breakpoints.lg.value">
|
||||
{{ $t('entity.advancedSearch.title') }}
|
||||
</span>
|
||||
</UiButton>
|
||||
</Transition>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
@@ -2,8 +2,6 @@
|
||||
import type { Category } from "~/models/Category";
|
||||
import { ScrollAreaRoot, ScrollAreaViewport, ScrollAreaScrollbar, ScrollAreaThumb } from "radix-vue"
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { categories } = defineProps<{
|
||||
categories: Category[]
|
||||
}>()
|
||||
@@ -27,7 +25,7 @@ function closeDeleteDialog() {
|
||||
|
||||
<ScrollAreaRoot class="h-36 grow overflow-hidden ">
|
||||
<ScrollAreaViewport class="w-full h-full pr-4" as-child>
|
||||
<div class="[&:last-child]:border-0">
|
||||
<div class="last:border-0">
|
||||
<CalendarCategoryTableRow
|
||||
v-for="item in sortedCategories"
|
||||
:key="item.id"
|
||||
|
||||
@@ -109,7 +109,7 @@ async function submitNew() {
|
||||
ref="inputRef"
|
||||
v-model="categorySkeleton.name"
|
||||
type="text"
|
||||
class="p-1 h-full w-full bg-transparent focus-visible:outline-none italic"
|
||||
class="p-1 h-full w-full bg-transparent focus-visible:outline-hidden italic"
|
||||
>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { PhListBullets } from "@phosphor-icons/vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="grid grid-cols-12 gap-4 px-2 py-4 border-b-[1px] border-b-foreground/10 font-bold mr-4">
|
||||
<header class="grid grid-cols-12 gap-4 px-2 py-4 border-b-[1px] border-b-border font-bold mr-4">
|
||||
<div class="col-span-1 flex items-end">
|
||||
<PhListBullets size="20" />
|
||||
</div>
|
||||
|
||||
@@ -127,7 +127,7 @@ function handleQueryDelete() {
|
||||
ref="inputRef"
|
||||
v-model="categorySkeleton.name"
|
||||
type="text"
|
||||
class="p-1 h-full w-full bg-transparent focus-visible:outline-none italic"
|
||||
class="p-1 h-full w-full bg-transparent focus-visible:outline-hidden italic"
|
||||
>
|
||||
</template>
|
||||
</div>
|
||||
@@ -139,7 +139,7 @@ function handleQueryDelete() {
|
||||
>
|
||||
<span
|
||||
class="bgc"
|
||||
:class="cn(`bgc-${category.color}`)"
|
||||
:class="cn(`element-${category.color}`)"
|
||||
>
|
||||
{{ $t(`ui.colors.${category.color}`) }}
|
||||
</span>
|
||||
|
||||
@@ -41,7 +41,7 @@ function handleClose() {
|
||||
<PhX size="20" />
|
||||
</UiButton>
|
||||
|
||||
<CalendarFormCreate @on-changed-name="onChangedName" @on-close="handleClose" />
|
||||
<CalendarFormCreate v-if="world.id" :world-id="world.id" @on-changed-name="onChangedName" @on-close="handleClose" />
|
||||
</UiAlertDialogContent>
|
||||
</UiAlertDialog>
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import type { RPGDate } from "~/models/Date";
|
||||
|
||||
const { eventSkeleton, operationInProgress } = storeToRefs(useCalendar())
|
||||
const { eventSkeleton, operationInProgress, currentRPGDate } = storeToRefs(useCalendar())
|
||||
const { resetSkeleton } = useCalendar()
|
||||
const popoverOpen = ref(false)
|
||||
|
||||
@@ -39,6 +39,13 @@ function handleClosing() {
|
||||
popoverOpen.value = false
|
||||
resetSkeleton()
|
||||
}
|
||||
|
||||
// If the date changes, the popover should close
|
||||
// Only for months for now (because it's the only view)
|
||||
watch(currentRPGDate, () => {
|
||||
if (!popoverOpen.value) return
|
||||
handleClosing()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -22,26 +22,25 @@ async function handleAction(): Promise<void> {
|
||||
|
||||
isLoading.value = true
|
||||
|
||||
try {
|
||||
await $fetch(`/api/calendars/${props.calendar.id}`, { method: "DELETE" })
|
||||
emit("on-close")
|
||||
const { error } = await tryCatch($fetch(`/api/calendars/${props.calendar.id}`, { method: "DELETE" }))
|
||||
|
||||
if (error) {
|
||||
toast({
|
||||
title: t("entity.calendar.deletedToast.title", { calendar: props.calendar.name }),
|
||||
variant: "success",
|
||||
duration: ToastLifetime.SHORT
|
||||
title: error.message,
|
||||
variant: "destructive"
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
if (err instanceof Error) {
|
||||
toast({
|
||||
title: err.message,
|
||||
variant: "destructive"
|
||||
})
|
||||
}
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
toast({
|
||||
title: t("entity.calendar.deletedToast.title", { calendar: props.calendar.name }),
|
||||
variant: "success",
|
||||
duration: ToastLifetime.SHORT
|
||||
})
|
||||
|
||||
emit("on-close")
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core"
|
||||
import { PhPlus } from "@phosphor-icons/vue";
|
||||
import { VisuallyHidden } from "radix-vue";
|
||||
|
||||
const isDialogOpen = ref<boolean>(false);
|
||||
const { resetSkeleton } = useCalendar();
|
||||
@@ -15,26 +17,46 @@ function toggleDialog() {
|
||||
function handleClosing() {
|
||||
setTimeout(() => resetSkeleton(), 100)
|
||||
}
|
||||
|
||||
const breakpoints = useBreakpoints(
|
||||
breakpointsTailwind
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UiButton @click="toggleDialog">
|
||||
<PhPlus size="18" weight="bold" />
|
||||
<ClientOnly>
|
||||
<Transition name="fade" appear>
|
||||
<UiButton
|
||||
class="max-md:fixed max-md:bottom-8 max-md:right-8 max-md:z-50 max-md:size-14 max-md:rounded-xl"
|
||||
:size="breakpoints.md.value ? 'default' : 'icon'"
|
||||
@click="toggleDialog"
|
||||
>
|
||||
<PhPlus :size="breakpoints.md.value ? 18 : 24" weight="bold" />
|
||||
|
||||
<strong class="font-semibold">
|
||||
{{ $t("entity.calendar.event.newEvent") }}
|
||||
</strong>
|
||||
</UiButton>
|
||||
<strong v-if="breakpoints.md.value" class="font-semibold">
|
||||
{{ $t("entity.calendar.event.newEvent") }}
|
||||
</strong>
|
||||
</UiButton>
|
||||
</Transition>
|
||||
</ClientOnly>
|
||||
|
||||
<UiDialog v-model:open="isDialogOpen">
|
||||
<UiDialogContent
|
||||
class="border-indigo-200 dark:bg-slate-950 dark:border-indigo-950"
|
||||
@escape-key-down.prevent="handleClosing"
|
||||
class="max-md:translate-0 max-md:inset-0 max-md:w-full max-md:block"
|
||||
:trap-focus="true"
|
||||
@escape-key-down="handleClosing"
|
||||
@pointer-down-outside.prevent="handleClosing"
|
||||
>
|
||||
<UiDialogTitle>
|
||||
<UiDialogTitle class="max-md:mb-8">
|
||||
{{ $t("entity.calendar.event.addSingle") }}
|
||||
</UiDialogTitle>
|
||||
|
||||
<VisuallyHidden>
|
||||
<UiDialogDescription>
|
||||
{{ $t("entity.calendar.event.addSingleDescription") }}
|
||||
</UiDialogDescription>
|
||||
</VisuallyHidden>
|
||||
|
||||
<CalendarFormCreateEvent @event-created="toggleDialog" />
|
||||
</UiDialogContent>
|
||||
</UiDialog>
|
||||
|
||||
@@ -25,13 +25,13 @@ function handleClosing() {
|
||||
<UiDialogContent
|
||||
:disable-outside-pointer-events="true"
|
||||
:trap-focus="true"
|
||||
class="pl-3 min-w-96 border-indigo-200 dark:bg-slate-950 dark:border-indigo-950"
|
||||
class="pl-3 md:min-w-96 max-md:translate-0 max-md:inset-0 max-md:w-full max-md:block"
|
||||
@escape-key-down="handleClosing"
|
||||
@focus-outside="handleClosing"
|
||||
@interact-outside="handleClosing"
|
||||
@pointer-down-outside="(e) => e.preventDefault()"
|
||||
>
|
||||
<header class="pl-8 grid gap-y-2">
|
||||
<header class="pl-8 grid gap-y-2 max-md:mb-8">
|
||||
<UiDialogTitle>
|
||||
{{ $t('entity.calendar.event.editDialog.title') }}
|
||||
</UiDialogTitle>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from "@/lib/utils"
|
||||
import type { RPGDate } from "@/models/Date"
|
||||
import type { CalendarEvent } from "@/models/CalendarEvent"
|
||||
import { useCalendar } from "@/stores/CalendarStore"
|
||||
import { cn } from "~/lib/utils"
|
||||
import type { RPGDate } from "~/models/Date"
|
||||
import type { CalendarEvent } from "~/models/CalendarEvent"
|
||||
import { useCalendar } from "~/stores/CalendarStore"
|
||||
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core"
|
||||
|
||||
import {
|
||||
PhHourglassMedium,
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
PhDotsThreeOutlineVertical,
|
||||
PhEye
|
||||
} from "@phosphor-icons/vue"
|
||||
import type { CollisionPadding } from "~/models/Popover"
|
||||
|
||||
const { defaultDate, getFormattedDateTitle, jumpToDate, getRelativeString, revealEditEventModal, revealDeleteEventModal } = useCalendar()
|
||||
const { lastActiveEvent, isReadOnly } = storeToRefs(useCalendar())
|
||||
@@ -24,6 +26,22 @@ const props = defineProps<{
|
||||
isEndEvent?: boolean
|
||||
}>()
|
||||
|
||||
const breakpoints = useBreakpoints(
|
||||
breakpointsTailwind
|
||||
)
|
||||
const collisionPadLg: CollisionPadding = {
|
||||
top: 50,
|
||||
bottom: 50,
|
||||
left: 50,
|
||||
right: 50,
|
||||
}
|
||||
const collisionPad: CollisionPadding = {
|
||||
top: 80,
|
||||
bottom: 80,
|
||||
left: 12.5,
|
||||
right: 12.5,
|
||||
}
|
||||
|
||||
// Ref for the popover
|
||||
const eventDetails = ref<HTMLElement>()
|
||||
|
||||
@@ -62,17 +80,14 @@ function deployDeleteModal() {
|
||||
|
||||
<template>
|
||||
<UiPopoverContent
|
||||
class="w-96"
|
||||
class="max-w-full w-96 event-popover"
|
||||
:align="'center'"
|
||||
:align-offset="50"
|
||||
:side="'left'"
|
||||
:collision-padding="60"
|
||||
:hide-when-detached="true"
|
||||
:align-offset="breakpoints.lg.value ? 50 : 25"
|
||||
:side-offset="8"
|
||||
:side="breakpoints.lg.value ? 'left' : 'top'"
|
||||
:collision-padding="breakpoints.lg.value ? collisionPadLg : collisionPad"
|
||||
:class="cn(
|
||||
event.category ? `event-details-${event.category.color}` : '',
|
||||
{
|
||||
'event-details': event.category
|
||||
}
|
||||
event.category ? `element-${event.category.color}` : '',
|
||||
)"
|
||||
@focus-outside.prevent
|
||||
>
|
||||
@@ -143,7 +158,7 @@ function deployDeleteModal() {
|
||||
</template>
|
||||
|
||||
<template v-if="event.description">
|
||||
<hr class="border-slate-500 mt-2" >
|
||||
<hr class="border-border mt-2" >
|
||||
|
||||
<div class="max-h-48 overflow-y-auto mt-2 text-sm text-slate-600 dark:text-slate-300">
|
||||
{{ event.description }}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from "@/lib/utils"
|
||||
import type { RPGDate } from "@/models/Date"
|
||||
import { cn } from "~/lib/utils"
|
||||
import type { RPGDate } from "~/models/Date"
|
||||
import type { CalendarEvent } from "~/models/CalendarEvent"
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -55,10 +55,10 @@ onMounted(() => {
|
||||
<UiPopover v-model:open="isPopoverDetailsOpen">
|
||||
<UiPopoverTrigger as-child>
|
||||
<button
|
||||
class="event text-xs px-2 py-1 block w-full text-left rounded-sm transition-colors outline-offset-1"
|
||||
class="event-button text-2xs md:text-xs px-[5px] py-[5px] md:px-2 md:py-1 block w-full text-left rounded-sm transition-colors outline-offset-1 cursor-pointer"
|
||||
:class="
|
||||
cn(
|
||||
event.category ? `event-${event.category.color}` : '',
|
||||
`element-${event.category?.color}`,
|
||||
{
|
||||
'is-hidden': event.hidden,
|
||||
'rounded-r-none': isStartEvent,
|
||||
@@ -75,7 +75,7 @@ onMounted(() => {
|
||||
</button>
|
||||
</UiPopoverTrigger>
|
||||
|
||||
<LazyCalendarEventDetails
|
||||
<CalendarEventDetails
|
||||
:event
|
||||
:spans-multiple-days
|
||||
:is-start-event
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
import type { Calendar } from "~/models/CalendarConfig";
|
||||
import { PhAlarm, PhCalendarDots, PhCircleNotch, PhWrench } from "@phosphor-icons/vue";
|
||||
|
||||
const defaultSkeleton: Calendar = { name: "", today: { day: 1, month: 0, year: 0 }, months: [], events: [], state: "draft", color: "white" }
|
||||
const props = defineProps<{
|
||||
worldId: number
|
||||
}>()
|
||||
|
||||
const defaultSkeleton: Calendar = { name: "", today: { day: 1, month: 0, year: 0 }, months: [], events: [], categories: [], state: "draft", color: "white" }
|
||||
const calendarSkeleton = ref<Calendar>({ ...defaultSkeleton })
|
||||
|
||||
onMounted(() => {
|
||||
@@ -38,16 +42,18 @@ const validSkeleton = computed(() => validSkeletonGeneral.value && validSkeleton
|
||||
const isCreatingCalendar = ref<boolean>(false)
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
isCreatingCalendar.value = true
|
||||
await $fetch("/api/calendars/create", { method: "POST", body: { ...calendarSkeleton.value, worldId: 1 } })
|
||||
isCreatingCalendar.value = true
|
||||
|
||||
emit("on-close")
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
} finally {
|
||||
const { error } = await tryCatch($fetch("/api/calendars/create", { method: "POST", body: { ...calendarSkeleton.value, worldId: props.worldId } }))
|
||||
|
||||
if (error) {
|
||||
console.log(error.message)
|
||||
isCreatingCalendar.value = false
|
||||
return
|
||||
}
|
||||
|
||||
emit("on-close")
|
||||
isCreatingCalendar.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,7 +111,7 @@ function handleFormCancel() {
|
||||
name="new-calendar-name"
|
||||
required
|
||||
:placeholder="$t('common.title')"
|
||||
class="w-full py-2 -mx-1 px-1 text-xl border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"
|
||||
class="w-full py-2 -mx-1 px-1 text-xl border-b-[1px] bg-transparent focus-visible:outline-hidden focus-visible:border-blue-600"
|
||||
@input="handleNameChange"
|
||||
>
|
||||
|
||||
|
||||
@@ -29,17 +29,16 @@ async function handleSubmit() {
|
||||
|
||||
isLoading.value = true
|
||||
|
||||
try {
|
||||
await submitSkeleton()
|
||||
const { error } = await tryCatch(submitSkeleton())
|
||||
|
||||
emit("event-created")
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
formErrors.message = err.message
|
||||
}
|
||||
} finally {
|
||||
if (error) {
|
||||
formErrors.message = error.message
|
||||
isLoading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
emit("event-created")
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +66,7 @@ function handleCancel() {
|
||||
:placeholder="$t('entity.calendar.event.title')"
|
||||
:maxlength="120"
|
||||
pattern="([A-Za-zÀ-ÖØ-öø-ÿ0-9\s\&\-\~]+){3,120}"
|
||||
class="w-full -my-1 py-1 -mx-1 px-1 text-lg border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600 invalid:border-red-500"
|
||||
class="w-full -my-1 py-1 -mx-1 px-1 text-lg border-b-[1px] bg-transparent focus-visible:outline-hidden focus-visible:border-blue-600 invalid:border-red-500"
|
||||
>
|
||||
<div class="mt-2 mb-1 text-xs opacity-50">
|
||||
{{ t('entity.calendar.event.patterns.title') }}
|
||||
@@ -81,7 +80,7 @@ function handleCancel() {
|
||||
name="new-event-description"
|
||||
:placeholder="$t('entity.addDescription')"
|
||||
:maxlength="1200"
|
||||
class="w-full -my-1 py-1 -mx-1 px-1 min-h-24 max-h-36 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600 invalid:border-red-500"
|
||||
class="w-full -my-1 py-1 -mx-1 px-1 min-h-24 max-h-36 text-sm border-b-[1px] bg-transparent focus-visible:outline-hidden focus-visible:border-blue-600 invalid:border-red-500"
|
||||
/>
|
||||
<div class="mt-2 mb-1 text-xs opacity-50">
|
||||
{{ t('entity.calendar.event.patterns.description') }}
|
||||
@@ -141,7 +140,7 @@ function handleCancel() {
|
||||
:placeholder="$t('entity.calendar.event.addLocation')"
|
||||
:maxlength="160"
|
||||
pattern="([A-Za-zÀ-ÖØ-öø-ÿ0-9\s\&\-\~]+){3,160}"
|
||||
class="w-full -my-1 py-2 px-2 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"
|
||||
class="w-full -my-1 py-2 px-2 text-sm border-b-[1px] bg-transparent focus-visible:outline-hidden focus-visible:border-blue-600"
|
||||
>
|
||||
<div class="mt-2 mb-1 text-xs opacity-50">
|
||||
{{ t('entity.calendar.event.patterns.location') }}
|
||||
|
||||
@@ -22,21 +22,20 @@ async function handleAction(): Promise<void> {
|
||||
|
||||
const categoryName = categorySkeleton.value?.name
|
||||
|
||||
try {
|
||||
await deleteCategoryFromSkeleton()
|
||||
const { error } = await tryCatch(deleteCategoryFromSkeleton())
|
||||
|
||||
toast({
|
||||
title: t("entity.category.deletedToast.title", { category: categoryName }),
|
||||
variant: "success",
|
||||
duration: ToastLifetime.MEDIUM
|
||||
})
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
formErrors.message = err.message
|
||||
}
|
||||
} finally {
|
||||
if (error) {
|
||||
formErrors.message = error.message
|
||||
isLoading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
toast({
|
||||
title: t("entity.category.deletedToast.title", { category: categoryName }),
|
||||
variant: "success",
|
||||
duration: ToastLifetime.MEDIUM
|
||||
})
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,24 +22,23 @@ async function handleAction(): Promise<void> {
|
||||
|
||||
const eventTitle = eventSkeleton.value.title
|
||||
|
||||
try {
|
||||
await deleteEventFromSkeleton()
|
||||
const { error } = await tryCatch(deleteEventFromSkeleton())
|
||||
|
||||
isDeleteEventModalOpen.value = false
|
||||
resetSkeleton()
|
||||
|
||||
toast({
|
||||
title: t("entity.calendar.event.deletedToast.title", { event: eventTitle }),
|
||||
variant: "success",
|
||||
duration: ToastLifetime.MEDIUM
|
||||
})
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
formErrors.message = err.message
|
||||
}
|
||||
} finally {
|
||||
if (error) {
|
||||
formErrors.message = error.message
|
||||
isLoading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
isDeleteEventModalOpen.value = false
|
||||
resetSkeleton()
|
||||
|
||||
toast({
|
||||
title: t("entity.calendar.event.deletedToast.title", { event: eventTitle }),
|
||||
variant: "success",
|
||||
duration: ToastLifetime.MEDIUM
|
||||
})
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,16 +34,22 @@ const validSkeleton = computed(() => validSkeletonGeneral.value && validSkeleton
|
||||
const isUpdatingCalendar = ref<boolean>(false)
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
isUpdatingCalendar.value = true
|
||||
await $fetch(`/api/calendars/${calendarSkeleton.value.id}`, { method: "PATCH", body: { ...calendarSkeleton.value, worldId: props.world?.id } })
|
||||
isUpdatingCalendar.value = true
|
||||
|
||||
emit("on-close")
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
} finally {
|
||||
const fetchBody = { ...calendarSkeleton.value, worldId: props.world?.id }
|
||||
|
||||
const { error } = await tryCatch(
|
||||
$fetch(`/api/calendars/${calendarSkeleton.value.id}`, { method: "PATCH", body: fetchBody })
|
||||
)
|
||||
|
||||
if (error) {
|
||||
console.log(error.message)
|
||||
isUpdatingCalendar.value = false
|
||||
return
|
||||
}
|
||||
|
||||
emit("on-close")
|
||||
isUpdatingCalendar.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +107,7 @@ function handleFormCancel() {
|
||||
name="new-calendar-name"
|
||||
required
|
||||
:placeholder="$t('common.title')"
|
||||
class="w-full py-2 -mx-1 px-1 text-xl border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"
|
||||
class="w-full py-2 -mx-1 px-1 text-xl border-b-[1px] bg-transparent focus-visible:outline-hidden focus-visible:border-blue-600"
|
||||
@input="handleNameChange"
|
||||
>
|
||||
|
||||
|
||||
@@ -23,19 +23,11 @@ async function handleAction() {
|
||||
|
||||
isLoading.value = true
|
||||
|
||||
try {
|
||||
await updateEventFromSkeleton()
|
||||
const { error } = await tryCatch(updateEventFromSkeleton())
|
||||
|
||||
emit("event-updated")
|
||||
|
||||
toast({
|
||||
title: t("entity.calendar.event.updatedToast.title", { event: eventSkeleton.value.title }),
|
||||
variant: "success",
|
||||
duration: ToastLifetime.SHORT
|
||||
})
|
||||
} catch (err) {
|
||||
if (error) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const apiError = (err as any).data as APIError
|
||||
const apiError = (error as any).data as APIError
|
||||
|
||||
apiError.data.errors.forEach((error) => {
|
||||
toast({
|
||||
@@ -45,10 +37,21 @@ async function handleAction() {
|
||||
duration: ToastLifetime.MEDIUM,
|
||||
})
|
||||
})
|
||||
} finally {
|
||||
resetSkeleton()
|
||||
|
||||
isLoading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
emit("event-updated")
|
||||
|
||||
toast({
|
||||
title: t("entity.calendar.event.updatedToast.title", { event: eventSkeleton.value.title }),
|
||||
variant: "success",
|
||||
duration: ToastLifetime.SHORT
|
||||
})
|
||||
|
||||
isLoading.value = false
|
||||
resetSkeleton()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,7 +82,7 @@ function handleCancel() {
|
||||
:placeholder="$t('entity.calendar.event.title')"
|
||||
:minlength="3"
|
||||
:maxlength="120"
|
||||
class="w-full -my-1 py-1 -mx-1 px-1 text-lg border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600 invalid:border-red-500"
|
||||
class="w-full -my-1 py-1 -mx-1 px-1 text-lg border-b-[1px] bg-transparent focus-visible:outline-hidden focus-visible:border-blue-600 invalid:border-red-500"
|
||||
>
|
||||
<div class="mt-2 mb-1 text-xs opacity-50">
|
||||
{{ t('entity.calendar.event.patterns.title') }}
|
||||
@@ -95,7 +98,7 @@ function handleCancel() {
|
||||
name="new-event-description"
|
||||
:placeholder="$t('entity.addDescription')"
|
||||
:maxlength="1200"
|
||||
class="w-full -my-1 py-1 -mx-1 px-1 min-h-24 max-h-36 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600 invalid:border-red-500"
|
||||
class="w-full -my-1 py-1 -mx-1 px-1 min-h-24 max-h-36 text-sm border-b-[1px] bg-transparent focus-visible:outline-hidden focus-visible:border-blue-600 invalid:border-red-500"
|
||||
/>
|
||||
<div class="mt-2 mb-1 text-xs opacity-50">
|
||||
{{ t('entity.calendar.event.patterns.description') }}
|
||||
@@ -159,7 +162,7 @@ function handleCancel() {
|
||||
:placeholder="$t('entity.calendar.event.addLocation')"
|
||||
:minlength="3"
|
||||
:maxlength="160"
|
||||
class="w-full -my-1 py-2 px-2 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600 invalid:border-red-500"
|
||||
class="w-full -my-1 py-2 px-2 text-sm border-b-[1px] bg-transparent focus-visible:outline-hidden focus-visible:border-blue-600 invalid:border-red-500"
|
||||
>
|
||||
|
||||
<div class="mt-2 mb-1 text-xs opacity-50">
|
||||
|
||||
@@ -50,7 +50,7 @@ const filteredCategories = computed(() =>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<PhCaretDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<PhCaretDown class="ml-2 size-4 shrink-0 opacity-50" />
|
||||
</UiButton>
|
||||
</UiPopoverTrigger>
|
||||
<UiPopoverContent
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { PhCaretDown } from "@phosphor-icons/vue";
|
||||
import { cn } from "~/lib/utils";
|
||||
import type { Category } from "~/models/Category";
|
||||
|
||||
const isPopoverOpen = ref<boolean>(false)
|
||||
@@ -39,10 +40,15 @@ const filteredCategories = computed(() =>
|
||||
{{ props.placeholder }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ model.name }}
|
||||
<span
|
||||
class="bgc"
|
||||
:class="cn(`element-${model.color}`)"
|
||||
>
|
||||
{{ capitalize(model.name) }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<PhCaretDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<PhCaretDown class="ml-2 size-4 shrink-0 opacity-50" />
|
||||
</UiButton>
|
||||
</UiPopoverTrigger>
|
||||
<UiPopoverContent
|
||||
@@ -51,7 +57,7 @@ const filteredCategories = computed(() =>
|
||||
:collision-padding="50"
|
||||
class="w-fit h-[33vh] p-0"
|
||||
>
|
||||
<UiCommand v-model="model" v-model:searchTerm="searchTerm">
|
||||
<UiCommand v-model="model" v-model:search-term="searchTerm">
|
||||
<UiCommandInput :placeholder="$t('entity.category.search')" />
|
||||
<UiCommandEmpty>{{ $t('entity.category.notFoundAny') }}</UiCommandEmpty>
|
||||
<UiCommandList>
|
||||
@@ -63,7 +69,12 @@ const filteredCategories = computed(() =>
|
||||
class="cursor-pointer"
|
||||
@select="handleCatSelect"
|
||||
>
|
||||
{{ category.name }}
|
||||
<span
|
||||
class="bgc"
|
||||
:class="cn(`element-${category.color}`)"
|
||||
>
|
||||
{{ capitalize(category.name) }}
|
||||
</span>
|
||||
</UiCommandItem>
|
||||
</UiCommandGroup>
|
||||
</UiCommandList>
|
||||
|
||||
@@ -91,19 +91,19 @@ useSortable(monthSortableList, model.value, { animation: 150, handle: ".handle"
|
||||
/>
|
||||
</div>
|
||||
<div class="md:col-span-1">
|
||||
<UiButton size="icon" class="rounded-full h-8 w-8" :disabled="!validNewMonth" @click.prevent="addMonthToModel">
|
||||
<UiButton size="icon" class="rounded-full size-8" :disabled="!validNewMonth" @click.prevent="addMonthToModel">
|
||||
<PhPlus size="17"/>
|
||||
</UiButton>
|
||||
</div>
|
||||
<div class="md:col-span-full">
|
||||
<div
|
||||
class="border-[1px] dark:border-slate-800 p-4 rounded-sm max-h-80 overflow-y-auto"
|
||||
class="border-[1px] border-border p-4 rounded-sm max-h-80 overflow-y-auto"
|
||||
:class="model.length ? 'md:grid md:grid-cols-12 md:gap-4 md:items-center' : ''"
|
||||
>
|
||||
<div v-if="model.length" class="hidden md:block col-span-1">
|
||||
<ul class="grid gap-y-4 justify-center">
|
||||
<li v-for="(m, i) in model" :key="`num-${m.name}`">
|
||||
<UiButton size="icon" variant="secondary" class="h-8 w-8 rounded-full">
|
||||
<UiButton size="icon" variant="secondary" class="size-8 rounded-full">
|
||||
<span class="font-bold text-sm">{{ i + 1 }}</span>
|
||||
</UiButton>
|
||||
</li>
|
||||
@@ -115,7 +115,7 @@ useSortable(monthSortableList, model.value, { animation: 150, handle: ".handle"
|
||||
<template v-if="model.length">
|
||||
<li v-for="(m, i) in model" :key="m.name" class="grid md:grid-cols-12 gap-4 md:items-center text-slate-900 bg-slate-200 rounded-md">
|
||||
<div class="md:col-span-1 text-right duration-200 ease-out transition transform origin-top-right">
|
||||
<UiButton type="button" variant="ghost" size="icon" class="handle rounded-full h-8 w-8">
|
||||
<UiButton type="button" variant="ghost" size="icon" class="handle rounded-full size-8">
|
||||
<PhList size="17" />
|
||||
</UiButton>
|
||||
</div>
|
||||
@@ -145,7 +145,7 @@ useSortable(monthSortableList, model.value, { animation: 150, handle: ".handle"
|
||||
<UiTooltipProvider>
|
||||
<UiTooltip>
|
||||
<UiTooltipTrigger as-child>
|
||||
<UiButton type="button" variant="ghost" size="icon" class="rounded-full h-8 w-8" @click="removeMonthFromModel(i)">
|
||||
<UiButton type="button" variant="ghost" size="icon" class="rounded-full size-8" @click="removeMonthFromModel(i)">
|
||||
<PhTrash size="17" />
|
||||
</UiButton>
|
||||
</UiTooltipTrigger>
|
||||
|
||||
@@ -1,46 +1,45 @@
|
||||
<script lang="ts" setup>
|
||||
import { useCalendar } from "@/stores/CalendarStore"
|
||||
import { useCalendar } from "~/stores/CalendarStore"
|
||||
import { breakpointsTailwind } from "@vueuse/core"
|
||||
|
||||
import { PhMagnifyingGlass } from "@phosphor-icons/vue"
|
||||
const breakpoints = useBreakpoints(
|
||||
breakpointsTailwind
|
||||
)
|
||||
|
||||
const { revealAdvancedSearch } = useCalendar()
|
||||
const { isReadOnly } = storeToRefs(useCalendar())
|
||||
const { isReadOnly, defaultDate } = storeToRefs(useCalendar())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="mt-2 grid gap-4 border-slate-200 contrast-more:border-slate-500 dark:border-slate-700 border-b-[1px]">
|
||||
<div class="px-8 flex items-center justify-between gap-2">
|
||||
<menu class="flex items-center gap-2">
|
||||
<li v-if="!isReadOnly">
|
||||
<LazyCalendarDialogQuickCreateEvent />
|
||||
</li>
|
||||
<header class="md:mt-2 grid gap-3 md:gap-4 border-border border-b-[1px] transition-colors">
|
||||
<div class="px-5 md:px-8 flex items-center justify-between gap-2">
|
||||
<menu class="flex items-center md:gap-2">
|
||||
<li>
|
||||
<CalendarMenuToday />
|
||||
<LazyCalendarDialogQuickCreateEvent v-if="!isReadOnly" />
|
||||
</li>
|
||||
<li class="ml-4">
|
||||
<li class="max-md:hidden">
|
||||
<LazyCalendarMenuToday v-if="defaultDate" />
|
||||
</li>
|
||||
<li class="ml-2 md:ml-4">
|
||||
<CalendarCurrentDate />
|
||||
</li>
|
||||
</menu>
|
||||
|
||||
<menu class="flex items-center gap-2">
|
||||
<li>
|
||||
<UiButton search-slash @click="revealAdvancedSearch()">
|
||||
<PhMagnifyingGlass size="20" weight="light" />
|
||||
<span>
|
||||
{{ $t('entity.advancedSearch.title') }}
|
||||
</span>
|
||||
</UiButton>
|
||||
</li>
|
||||
<li>
|
||||
<CalendarCategoriesCTA />
|
||||
<CalendarSearchCTA />
|
||||
</li>
|
||||
<ClientOnly>
|
||||
<li v-if="breakpoints.md.value">
|
||||
<CalendarCategoriesCTA />
|
||||
</li>
|
||||
</ClientOnly>
|
||||
<li>
|
||||
<CalendarOptionsCTA />
|
||||
</li>
|
||||
</menu>
|
||||
</div>
|
||||
|
||||
<div class="ml-8">
|
||||
<div class="ml-4 md:ml-8">
|
||||
<CalendarMenuSubnav />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -139,7 +139,7 @@ function toFutureFar(): void {
|
||||
|
||||
<template>
|
||||
<div class="flex gap-2">
|
||||
<div class="grid items-end w-40 px-4 py-2 border-slate-200 bg-white dark:bg-black dark:border-slate-700 border-x-[1px] border-t-[1px] rounded-t-sm text-sm transition-colors">
|
||||
<div class="grid items-center w-40 px-3 md:px-4 py-2 bg-white dark:bg-black border-border border-x-[1px] border-t-[1px] rounded-t-sm text-sm max-md:text-xs transition-colors">
|
||||
<ClientOnly>
|
||||
<span>{{ currentDate.currentDateTitle }}</span>
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue"
|
||||
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core"
|
||||
import { PhCalendar } from "@phosphor-icons/vue"
|
||||
|
||||
const { defaultDate, jumpToDefaultDate, getFormattedDateTitle, currentDate } = useCalendar()
|
||||
|
||||
@@ -8,15 +9,29 @@ const defaultDateFormatted: string = getFormattedDateTitle(defaultDate, true)
|
||||
const buttonDisabledState: ComputedRef<boolean> = computed<boolean>(() => {
|
||||
return currentDate.currentMonth === defaultDate.month && currentDate.currentYear === defaultDate.year
|
||||
})
|
||||
|
||||
const breakpoints = useBreakpoints(
|
||||
breakpointsTailwind
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UiTooltipProvider :delay-duration="250">
|
||||
<UiTooltip>
|
||||
<UiTooltipTrigger as-child>
|
||||
<UiButton size="sm" variant="secondary" :disabled="buttonDisabledState" @click="jumpToDefaultDate">
|
||||
{{ $t('entity.calendar.date.today') }}
|
||||
</UiButton>
|
||||
<ClientOnly>
|
||||
<UiButton
|
||||
:size="breakpoints.md.value ? 'default' : 'icon'"
|
||||
variant="secondary"
|
||||
:disabled="buttonDisabledState" @click="jumpToDefaultDate"
|
||||
>
|
||||
<PhCalendar v-if="!breakpoints.md.value" size="20" weight="fill" />
|
||||
|
||||
<span v-if="breakpoints.md.value">
|
||||
{{ $t('entity.calendar.date.today') }}
|
||||
</span>
|
||||
</UiButton>
|
||||
</ClientOnly>
|
||||
</UiTooltipTrigger>
|
||||
<UiTooltipContent>
|
||||
<p>{{ defaultDateFormatted }}</p>
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
import {
|
||||
isCharacter,
|
||||
type Character,
|
||||
} from "@/models/Characters"
|
||||
import type { RPGDateOrder } from "@/models/Date"
|
||||
} from "~/models/Characters"
|
||||
import type { RPGDateOrder } from "~/models/Date"
|
||||
import {
|
||||
isCalendarEvent,
|
||||
type CalendarEvent,
|
||||
} from "~/models/CalendarEvent"
|
||||
import { capitalize } from "@/utils/Strings"
|
||||
import { capitalize } from "~/utils/Strings"
|
||||
import { useMagicKeys, useScroll, useStorage, whenever } from "@vueuse/core"
|
||||
import { computed, ref, watch } from "vue"
|
||||
import { searchUnifier, type SearchMode } from "../SearchMode"
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
|
||||
import SearchList from "./lists/SearchList.vue"
|
||||
import type { Category } from "~/models/Category"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const { isAdvancedSearchOpen, allEvents, categories } = storeToRefs(useCalendar())
|
||||
const { characters } = storeToRefs(useCharacters())
|
||||
@@ -264,10 +265,10 @@ function handleCategoryUnselect(e: Category) {
|
||||
<template>
|
||||
<UiDialog v-model:open="isAdvancedSearchOpen" @update:open="resetSearch()">
|
||||
<UiDialogContent
|
||||
class="flex flex-col flex-nowrap top-16 -translate-y-0 data-[state=closed]:slide-out-to-top-[5%] data-[state=open]:slide-in-from-top-[5%]"
|
||||
:class="{
|
||||
'bottom-16': searchResults.length > 0
|
||||
}"
|
||||
class="flex flex-col flex-nowrap top-10 -translate-y-0 data-[state=closed]:slide-out-to-top-[5%] data-[state=open]:slide-in-from-top-[5%] max-md:rounded-none"
|
||||
:class="cn({
|
||||
'max-md:w-full bottom-0 md:bottom-10 top-0 md:top-10': searchResults.length > 0
|
||||
})"
|
||||
>
|
||||
<VisuallyHidden>
|
||||
<UiDialogTitle> {{ $t('entity.advancedSearch.title') }} </UiDialogTitle>
|
||||
@@ -296,7 +297,7 @@ function handleCategoryUnselect(e: Category) {
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-8">
|
||||
<div>
|
||||
<div class="hidden">
|
||||
<UiToggleGroup
|
||||
v-model="selectedEntity"
|
||||
type="single"
|
||||
@@ -314,10 +315,16 @@ function handleCategoryUnselect(e: Category) {
|
||||
</div>
|
||||
|
||||
<div class="grow flex justify-end items-center gap-1">
|
||||
<UiTagsInput class="grow px-0 gap-y-1 w-80">
|
||||
<UiTagsInput class="grow px-0 gap-y-1 md:w-80">
|
||||
<div v-if="selectedCategories.length > 0" class="flex gap-2 flex-wrap items-center px-3">
|
||||
<UiTagsInputItem v-for="item in selectedCategories" :key="item.id" :value="item.name">
|
||||
<UiTagsInputItemText class="capitalize cursor-pointer" @click="handleCategoryUnselect(item)" />
|
||||
<UiTagsInputItem v-for="cat in selectedCategories" :key="cat.id" :value="cat.name">
|
||||
<button
|
||||
class="bgc px-2 capitalize cursor-pointer"
|
||||
:class="cn(`element-${cat.color}`)"
|
||||
@click="handleCategoryUnselect(cat)"
|
||||
>
|
||||
{{ capitalize(cat.name) }}
|
||||
</button>
|
||||
</UiTagsInputItem>
|
||||
</div>
|
||||
|
||||
@@ -345,7 +352,7 @@ function handleCategoryUnselect(e: Category) {
|
||||
<ComboboxPortal :to="'#searchForm'">
|
||||
<UiCommandList
|
||||
position="popper"
|
||||
class="w-[--radix-popper-anchor-width] rounded-md mt-2 border bg-popover text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50"
|
||||
class="w-(--radix-popper-anchor-width) rounded-md mt-2 border bg-popover text-popover-foreground shadow-md outline-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50"
|
||||
:dismissable="true"
|
||||
>
|
||||
<UiCommandEmpty />
|
||||
@@ -356,7 +363,12 @@ function handleCategoryUnselect(e: Category) {
|
||||
:value="cat"
|
||||
@select.prevent="handleCategorySelect(cat)"
|
||||
>
|
||||
{{ capitalize(cat.name) }}
|
||||
<span
|
||||
class="bgc"
|
||||
:class="cn(`element-${cat.color}`)"
|
||||
>
|
||||
{{ capitalize(cat.name) }}
|
||||
</span>
|
||||
</UiCommandItem>
|
||||
</UiCommandGroup>
|
||||
</UiCommandList>
|
||||
@@ -401,7 +413,7 @@ function handleCategoryUnselect(e: Category) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr >
|
||||
<hr>
|
||||
|
||||
<div v-if="searchResults.length > 0" ref="searchResultsRef" class="grow overflow-y-auto">
|
||||
<SearchList
|
||||
@@ -435,7 +447,7 @@ function handleCategoryUnselect(e: Category) {
|
||||
as-child
|
||||
>
|
||||
<UiButton
|
||||
class="w-10 h-10 p-0"
|
||||
class="size-8 md:size-10 p-0"
|
||||
:variant="item.value === currentPage ? 'default' : 'outline'"
|
||||
>
|
||||
{{ item.value }}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Character } from "@/models/Characters"
|
||||
import type { RPGDate } from "@/models/Date"
|
||||
import { useCalendar } from "@/stores/CalendarStore"
|
||||
import type { Character } from "~/models/Characters"
|
||||
import type { RPGDate } from "~/models/Date"
|
||||
import { useCalendar } from "~/stores/CalendarStore"
|
||||
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "~/components/ui/tooltip"
|
||||
import { Button } from "~/components/ui/button"
|
||||
import { PhArrowSquareOut, PhPlant, PhSkull } from "@phosphor-icons/vue"
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -21,7 +21,7 @@ console.log(props.character.birth)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="block w-full text-left py-3 px-4 border-[1px] border-slate-700 rounded-sm">
|
||||
<div class="block w-full text-left py-3 px-4 border-[1px] border-border rounded-sm">
|
||||
<div class="grid gap-2">
|
||||
<div class="flex gap-2">
|
||||
<h2 class="font-bold">
|
||||
@@ -37,7 +37,7 @@ console.log(props.character.birth)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<menu class="flex gap-2 border-[1px] border-slate-700 rounded-sm w-fit">
|
||||
<menu class="flex gap-2 border-[1px] border-border rounded-sm w-fit">
|
||||
<li v-if="character.birth">
|
||||
<TooltipProvider :delay-duration="100">
|
||||
<Tooltip>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from "@/lib/utils"
|
||||
import type { RPGDate } from "@/models/Date"
|
||||
import type { CalendarEvent } from "@/models/CalendarEvent"
|
||||
import { cn } from "~/lib/utils"
|
||||
import type { RPGDate } from "~/models/Date"
|
||||
import type { CalendarEvent } from "~/models/CalendarEvent"
|
||||
|
||||
import { PhArrowSquareOut, PhHourglassMedium, PhAlarm, PhMapPinArea, PhEye } from "@phosphor-icons/vue"
|
||||
|
||||
@@ -21,16 +21,16 @@ const dateDuration = computed<string | null>(() => props.event.endDate ? getRela
|
||||
|
||||
<template>
|
||||
<button
|
||||
class="event group relative block w-full text-left py-3 px-4 rounded-sm transition-colors"
|
||||
class="event-callout group relative block w-full text-left py-2 px-3 md:py-3 md:px-4 cursor-pointer transition-colors"
|
||||
:class="cn(
|
||||
event.category ? `event-${event.category.color}` : '',
|
||||
event.category ? `element-${event.category.color}` : '',
|
||||
{
|
||||
'pt-4 is-hidden': event.hidden,
|
||||
})"
|
||||
@click="$emit('query:date-jump', event.startDate)"
|
||||
>
|
||||
<div class="flex gap-2 items-center mb-1">
|
||||
<h2 class="font-bold text-lg underline-offset-4 group-hover:underline">
|
||||
<h2 class="font-semibold md:font-bold md:text-lg underline-offset-4 group-hover:underline">
|
||||
{{ event.title }}
|
||||
</h2>
|
||||
<div v-if="event.wiki">
|
||||
@@ -43,7 +43,7 @@ const dateDuration = computed<string | null>(() => props.event.endDate ? getRela
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 items-center justify-between mb-1">
|
||||
<div class="flex gap-2 items-center justify-between mb-2 md:mb-1">
|
||||
<template v-if="!event.endDate">
|
||||
<p class="font-semibold text-sm opacity-75">
|
||||
{{ getFormattedDateTitle(event.startDate, true) }}
|
||||
@@ -81,15 +81,15 @@ const dateDuration = computed<string | null>(() => props.event.endDate ? getRela
|
||||
|
||||
<div class="mb-1 flex gap-x-2 items-center">
|
||||
<template v-if="event.location">
|
||||
<p class="w-fit text-sm italic opacity-75 flex items-center gap-1">
|
||||
<p class="w-fit text-xs md:text-sm italic opacity-75 flex items-center gap-1">
|
||||
<PhMapPinArea size="16" weight="fill" /> {{ event.location }}
|
||||
</p>
|
||||
</template>
|
||||
<p class="w-fit text-sm italic opacity-75 flex items-center gap-1">
|
||||
<p class="w-fit text-xs md:text-sm italic opacity-75 flex items-center gap-1">
|
||||
<PhAlarm size="16" weight="fill" /> {{ dateDifference }}
|
||||
</p>
|
||||
<template v-if="dateDuration">
|
||||
<p class="w-fit text-sm italic opacity-75 flex items-center gap-1">
|
||||
<p class="w-fit text-xs md:text-sm italic opacity-75 flex items-center gap-1">
|
||||
<PhHourglassMedium size="16" weight="fill" /> {{ $t('entity.calendar.date.while', { duration: dateDuration } )}}
|
||||
</p>
|
||||
</template>
|
||||
@@ -111,8 +111,8 @@ const dateDuration = computed<string | null>(() => props.event.endDate ? getRela
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-if="event.description" class="text-sm">
|
||||
<hr class="my-2 border-white opacity-50" >
|
||||
<div v-if="event.description" class="text-xs md:text-sm">
|
||||
<hr class="my-3 md:my-2 border-white opacity-50" >
|
||||
<span class="opacity-75">
|
||||
{{ event.description }}
|
||||
</span>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { isCharacter, type Character } from "@/models/Characters"
|
||||
import type { RPGDate, RPGDateOrder } from "@/models/Date"
|
||||
import { useCalendar } from "@/stores/CalendarStore"
|
||||
import { isCharacter, type Character } from "~/models/Characters"
|
||||
import type { RPGDate, RPGDateOrder } from "~/models/Date"
|
||||
import { useCalendar } from "~/stores/CalendarStore"
|
||||
import { computed } from "vue"
|
||||
import { isCalendarEvent, type CalendarEvent } from "~/models/CalendarEvent"
|
||||
import type { SearchMode } from "../../SearchMode"
|
||||
@@ -60,7 +60,7 @@ const pagedResults = computed(() => sortedResults.value.slice(props.startAt, pro
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul class="grid gap-4 pl-8 pr-12">
|
||||
<ul class="grid gap-3 md:gap-4 pl-4 pr-6 md:pl-8 md:pr-12">
|
||||
<li v-for="r in pagedResults" :key="isCalendarEvent(r) ? r.title : r.name">
|
||||
<EventCallout v-if="isCalendarEvent(r)" :event="r" @query:date-jump="handleJumpToDate" />
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import type { RPGDate } from "@/models/Date"
|
||||
import type { CalendarEvent } from "@/models/CalendarEvent"
|
||||
import { useElementBounding } from "@vueuse/core"
|
||||
import type { RPGDate } from "~/models/Date"
|
||||
import type { CalendarEvent } from "~/models/CalendarEvent"
|
||||
import { breakpointsTailwind, useElementBounding } from "@vueuse/core"
|
||||
import { storeToRefs } from "pinia"
|
||||
import { computed, ref, type ComputedRef } from "vue"
|
||||
|
||||
@@ -12,12 +12,20 @@ const props = defineProps<{
|
||||
faded?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "on-open-create-dialog", date: RPGDate): void
|
||||
}>()
|
||||
|
||||
const calendarTile = ref()
|
||||
const calendarEventsList = ref()
|
||||
|
||||
const { defaultDate, selectDate, areDatesIdentical } = useCalendar()
|
||||
const { defaultDate, selectDate, areDatesIdentical, getFormattedDateTitle } = useCalendar()
|
||||
const { selectedDate, currentEvents, isReadOnly } = storeToRefs(useCalendar())
|
||||
|
||||
const breakpoints = useBreakpoints(
|
||||
breakpointsTailwind
|
||||
)
|
||||
|
||||
/**
|
||||
* All events with a startDate / endDate that starts or ends on the tile
|
||||
*/
|
||||
@@ -73,19 +81,19 @@ const eventsNotDisplayed: ComputedRef<number> = computed<number>(() => eventsFo
|
||||
<template>
|
||||
<div
|
||||
ref="calendarTile"
|
||||
class="tile relative text-xs p-2"
|
||||
class="tile relative p-1 md:p-2 transition-colors"
|
||||
:class="{
|
||||
'text-slate-300 dark:text-slate-500': props.faded,
|
||||
'text-slate-500 dark:text-slate-300': !props.faded
|
||||
}"
|
||||
>
|
||||
<button
|
||||
class="relative z-10 group block w-full text-center cursor-pointer"
|
||||
class="relative z-10 group block w-full text-2xs md:text-xs text-center cursor-pointer"
|
||||
@click="selectDate(date)"
|
||||
>
|
||||
<ClientOnly>
|
||||
<span
|
||||
class="inline-flex w-8 h-8 aspect-square items-center justify-center rounded-full border-2 border-transparent font-bold transition-colors group-hover:border-indigo-300 dark:group-hover:border-indigo-700"
|
||||
class="inline-flex size-7 md:size-8 aspect-square items-center justify-center rounded-full border-1 md:border-2 border-transparent font-bold transition-colors group-hover:border-indigo-300 dark:group-hover:border-indigo-700"
|
||||
:class="{
|
||||
'text-white bg-slate-600 dark:bg-slate-800': isDefaultDate && !isSelectedDate,
|
||||
'text-white bg-indigo-500': isSelectedDate
|
||||
@@ -99,7 +107,7 @@ const eventsNotDisplayed: ComputedRef<number> = computed<number>(() => eventsFo
|
||||
<ClientOnly>
|
||||
<ul
|
||||
ref="calendarEventsList"
|
||||
class="absolute top-12 bottom-2 inset-x-2 grid auto-rows-min gap-1 z-10 pointer-events-none transition-opacity"
|
||||
class="absolute top-9 md:top-12 bottom-1 md:bottom-2 inset-x-2 grid auto-rows-min gap-1 z-10 pointer-events-none transition-opacity"
|
||||
:class="{
|
||||
'opacity-40': props.faded && !isSelectedDate
|
||||
}"
|
||||
@@ -114,17 +122,23 @@ const eventsNotDisplayed: ComputedRef<number> = computed<number>(() => eventsFo
|
||||
<UiPopover>
|
||||
<UiPopoverTrigger as-child>
|
||||
<button
|
||||
class="text-xs px-2 py-1 block w-full text-left font-bold rounded-sm whitespace-nowrap overflow-hidden text-ellipsis cursor-pointer transition-colors hover:bg-slate-800"
|
||||
class="text-2xs md:text-xs px-[5px] py-[5px] md:px-2 md:py-1 block w-full text-left font-bold rounded-sm whitespace-nowrap overflow-hidden text-ellipsis cursor-pointer transition-colors hover:text-foreground hover:bg-foreground/10"
|
||||
>
|
||||
{{ eventsNotDisplayed }} autre{{ eventsNotDisplayed > 1 ? 's' : '' }}
|
||||
</button>
|
||||
</UiPopoverTrigger>
|
||||
<UiPopoverContent class="w-80" :align="'center'" :side="'right'">
|
||||
<div class="text-center mb-4">
|
||||
<UiPopoverContent
|
||||
class="w-80"
|
||||
:align="'center'"
|
||||
:align-offset="breakpoints.lg.value ? 50 : 25"
|
||||
:collision-padding="breakpoints.lg.value ? 50 : 25"
|
||||
:side="breakpoints.lg.value ? 'left' : 'bottom'"
|
||||
>
|
||||
<div class="text-center mb-2">
|
||||
<span
|
||||
class="inline-flex w-12 h-12 aspect-square items-center justify-center text-lg font-semibold text-slate-300 bg-slate-800 rounded-full"
|
||||
class="text-lg font-semibold rounded-full"
|
||||
>
|
||||
{{ date.day }}
|
||||
{{ getFormattedDateTitle(date, true) }}
|
||||
</span>
|
||||
</div>
|
||||
<ul class="grid auto-rows-min gap-1 z-10 pointer-events-none transition-opacity">
|
||||
@@ -143,14 +157,17 @@ const eventsNotDisplayed: ComputedRef<number> = computed<number>(() => eventsFo
|
||||
</ClientOnly>
|
||||
|
||||
<ClientOnly>
|
||||
<LazyCalendarDialogCreateEvent v-if="!isReadOnly" :date btn-class="absolute inset-0 w-full h-full cursor-default z-0" />
|
||||
<LazyCalendarDialogCreateEvent v-if="!isReadOnly && breakpoints.lg.value" :date btn-class="absolute inset-0 w-full h-full cursor-default z-0" />
|
||||
<button
|
||||
v-else-if="!isReadOnly && !breakpoints.lg.value"
|
||||
class="absolute inset-0 w-full h-full cursor-default z-0"
|
||||
@click="emit('on-open-create-dialog', props.date)"
|
||||
/>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// .tile are the direct children of the grid
|
||||
// We need to get, between each element, a 1px solid line to separate them, for columns and rows
|
||||
.tile {
|
||||
border-right-width: 1px;
|
||||
border-bottom-width: 1px;
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
import { useCalendar } from "@/stores/CalendarStore"
|
||||
import { useCalendar } from "~/stores/CalendarStore"
|
||||
import { useThrottleFn } from "@vueuse/core"
|
||||
import type { RPGDate } from "~/models/Date"
|
||||
import { VisuallyHidden } from "radix-vue"
|
||||
|
||||
const { currentDate, decrementViewMonth, incrementViewMonth } = useCalendar()
|
||||
const { currentMonthData } = storeToRefs(useCalendar())
|
||||
const { currentDate, decrementViewMonth, incrementViewMonth, resetSkeleton } = useCalendar()
|
||||
const { currentMonthData, operationInProgress, eventSkeleton } = storeToRefs(useCalendar())
|
||||
|
||||
function handleWheel(e: WheelEvent) {
|
||||
// Prevent scrolling
|
||||
e.preventDefault()
|
||||
|
||||
const isMovingUp = e.deltaY < 0
|
||||
if (isMovingUp) {
|
||||
moveCalendarLeft()
|
||||
@@ -21,10 +26,43 @@ const moveCalendarLeft = useThrottleFn(() => {
|
||||
const moveCalendarRight = useThrottleFn(() => {
|
||||
incrementViewMonth()
|
||||
}, 100)
|
||||
|
||||
const isDialogOpen = ref<boolean>(false);
|
||||
|
||||
/**
|
||||
* Opens event creation's popover
|
||||
*/
|
||||
function handleDialogOpen(date: RPGDate) {
|
||||
// If another operation is in progress, whether it's another create popup or a modal, don't bother opening it
|
||||
if (operationInProgress.value) {
|
||||
isDialogOpen.value = false
|
||||
return
|
||||
}
|
||||
|
||||
resetSkeleton()
|
||||
|
||||
isDialogOpen.value = true
|
||||
|
||||
// Set skeleton initial startDate if it's known
|
||||
if (date) {
|
||||
eventSkeleton.value.startDate = date
|
||||
}
|
||||
}
|
||||
|
||||
function toggleDialog() {
|
||||
isDialogOpen.value = !isDialogOpen.value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prevents the modal from closing if's still loading
|
||||
*/
|
||||
function handleClosing() {
|
||||
setTimeout(() => resetSkeleton(), 100)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="grid grid-cols-10" @wheel="handleWheel">
|
||||
<div class="grid grid-cols-5 md:grid-cols-7 lg:grid-cols-10" @wheel="handleWheel">
|
||||
<template v-if="currentMonthData">
|
||||
<CalendarStateMonthlyDayTile
|
||||
v-for="day in currentMonthData?.days"
|
||||
@@ -34,7 +72,29 @@ const moveCalendarRight = useThrottleFn(() => {
|
||||
month: currentDate.currentMonth,
|
||||
year: currentDate.currentYear
|
||||
}"
|
||||
@on-open-create-dialog="handleDialogOpen"
|
||||
/>
|
||||
|
||||
<UiDialog v-model:open="isDialogOpen">
|
||||
<UiDialogContent
|
||||
class="max-md:translate-0 max-md:inset-0 max-md:w-full max-md:block"
|
||||
:trap-focus="true"
|
||||
@escape-key-down="handleClosing"
|
||||
@pointer-down-outside.prevent="null"
|
||||
>
|
||||
<UiDialogTitle class="max-md:mb-8">
|
||||
{{ $t("entity.calendar.event.addSingle") }}
|
||||
</UiDialogTitle>
|
||||
|
||||
<VisuallyHidden>
|
||||
<UiDialogDescription>
|
||||
{{ $t("entity.calendar.event.addSingleDescription") }}
|
||||
</UiDialogDescription>
|
||||
</VisuallyHidden>
|
||||
|
||||
<CalendarFormCreateEvent @event-created="toggleDialog" />
|
||||
</UiDialogContent>
|
||||
</UiDialog>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { RPGDate } from "@/models/Date"
|
||||
import type { RPGDate } from "~/models/Date"
|
||||
import { storeToRefs } from "pinia"
|
||||
import { computed, type ComputedRef } from "vue"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { useCalendar } from "@/stores/CalendarStore"
|
||||
import { useCalendar } from "~/stores/CalendarStore"
|
||||
import { useThrottleFn } from "@vueuse/core"
|
||||
|
||||
const { decrementViewYear, incrementViewYear } = useCalendar()
|
||||
|
||||
@@ -15,46 +15,52 @@ defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UiBreadcrumb>
|
||||
<UiBreadcrumbList>
|
||||
<UiBreadcrumbItem v-if="route.path !== '/my'">
|
||||
<UiBreadcrumbLink as-child>
|
||||
<NuxtLink to="/my">
|
||||
{{ $t("breadcrumbs.profile") }}
|
||||
</NuxtLink>
|
||||
</UiBreadcrumbLink>
|
||||
</UiBreadcrumbItem>
|
||||
<UiBreadcrumbItem v-else>
|
||||
<UiBreadcrumbPage>
|
||||
{{ $t("breadcrumbs.profile") }}
|
||||
</UiBreadcrumbPage>
|
||||
</UiBreadcrumbItem>
|
||||
<div class="flex gap-3 items-center">
|
||||
<div class="md:hidden">
|
||||
<SidebarToggle />
|
||||
</div>
|
||||
|
||||
<template v-for="(item, index) in items" :key="index">
|
||||
<UiBreadcrumbSeparator>
|
||||
<PhCaretRight />
|
||||
</UiBreadcrumbSeparator>
|
||||
<UiBreadcrumbItem>
|
||||
<UiBreadcrumbLink v-if="item.to" as-child>
|
||||
<NuxtLink :to="item.to">
|
||||
<UiBreadcrumb>
|
||||
<UiBreadcrumbList>
|
||||
<UiBreadcrumbItem v-if="route.path !== '/my'">
|
||||
<UiBreadcrumbLink as-child>
|
||||
<NuxtLink to="/my">
|
||||
{{ $t("breadcrumbs.profile") }}
|
||||
</NuxtLink>
|
||||
</UiBreadcrumbLink>
|
||||
</UiBreadcrumbItem>
|
||||
<UiBreadcrumbItem v-else>
|
||||
<UiBreadcrumbPage>
|
||||
{{ $t("breadcrumbs.profile") }}
|
||||
</UiBreadcrumbPage>
|
||||
</UiBreadcrumbItem>
|
||||
|
||||
<template v-for="(item, index) in items" :key="index">
|
||||
<UiBreadcrumbSeparator>
|
||||
<PhCaretRight />
|
||||
</UiBreadcrumbSeparator>
|
||||
<UiBreadcrumbItem>
|
||||
<UiBreadcrumbLink v-if="item.to" as-child>
|
||||
<NuxtLink :to="item.to">
|
||||
<template v-if="item.label">
|
||||
{{ item.label }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $t(`breadcrumbs.${item.translateKey}`) }}
|
||||
</template>
|
||||
</NuxtLink>
|
||||
</UiBreadcrumbLink>
|
||||
<UiBreadcrumbPage v-else>
|
||||
<template v-if="item.label">
|
||||
{{ item.label }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $t(`breadcrumbs.${item.translateKey}`) }}
|
||||
</template>
|
||||
</NuxtLink>
|
||||
</UiBreadcrumbLink>
|
||||
<UiBreadcrumbPage v-else>
|
||||
<template v-if="item.label">
|
||||
{{ item.label }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $t(`breadcrumbs.${item.translateKey}`) }}
|
||||
</template>
|
||||
</UiBreadcrumbPage>
|
||||
</UiBreadcrumbItem>
|
||||
</template>
|
||||
</UiBreadcrumbList>
|
||||
</UiBreadcrumb>
|
||||
</UiBreadcrumbPage>
|
||||
</UiBreadcrumbItem>
|
||||
</template>
|
||||
</UiBreadcrumbList>
|
||||
</UiBreadcrumb>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "~/lib/utils";
|
||||
import { type RPGColor, rpgColors } from "~/models/Color";
|
||||
|
||||
const { id, theme = "normal", position = "popper" } = defineProps<{
|
||||
@@ -17,7 +17,7 @@ const model = defineModel<RPGColor>({ default: "white" });
|
||||
<UiSelectValue
|
||||
:placeholder="$t('ui.colors.selectOne')"
|
||||
class="bgc"
|
||||
:class="cn(`bgc-${model}`)"
|
||||
:class="cn(`element-${model}`)"
|
||||
/>
|
||||
</UiSelectTrigger>
|
||||
<UiSelectContent :position>
|
||||
@@ -29,7 +29,7 @@ const model = defineModel<RPGColor>({ default: "white" });
|
||||
class="bgc"
|
||||
:class="
|
||||
cn(
|
||||
`bgc-${color}`,
|
||||
`element-${color}`,
|
||||
)
|
||||
"
|
||||
>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
import { PhCompass, PhGlobeHemisphereEast, PhHurricane, PhList } from "@phosphor-icons/vue"
|
||||
import { PhCompass, PhGlobeHemisphereEast, PhHurricane, PhX } from "@phosphor-icons/vue"
|
||||
import type { SidebarMenuActionType, SidebarMenuIcon } from "./SidebarProps";
|
||||
import { cn } from "~/lib/utils";
|
||||
import { breakpointsTailwind } from "@vueuse/core"
|
||||
|
||||
const { revealAdvancedSearch } = useCalendar()
|
||||
const { currentMenu } = storeToRefs(useUiStore())
|
||||
const { toggleSidebar } = useUiStore()
|
||||
const { currentMenu, isSidebarOpened } = storeToRefs(useUiStore())
|
||||
|
||||
function handleMenuItemAction(actionType: SidebarMenuActionType) {
|
||||
if (actionType === "event-search") {
|
||||
@@ -21,26 +24,68 @@ function computeMenuItemIcon(iconString: SidebarMenuIcon) {
|
||||
return PhCompass
|
||||
}
|
||||
}
|
||||
|
||||
const breakpoints = useBreakpoints(
|
||||
breakpointsTailwind
|
||||
)
|
||||
|
||||
// const sidebarRef = ref(null)
|
||||
|
||||
// onClickOutside(sidebarRef, () => {
|
||||
// isSidebarOpened.value = false
|
||||
// })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav class="w-16 py-6 border-r-[1px] bg-indigo-700 dark:bg-black text-white border-r-indigo-700 dark:border-r-indigo-950 grid grid-rows-[1fr_auto] justify-center transition-colors after:opacity-50 after:contrast-125 dark:after:opacity-75 dark:after:contrast-175 after:-hue-rotate-60">
|
||||
<menu class="flex flex-col gap-4">
|
||||
<li class="mb-12">
|
||||
<UiButton variant="ghost" size="icon" class="rounded-full" @click="console.log">
|
||||
<PhList size="27" />
|
||||
<nav
|
||||
ref="sidebarRef"
|
||||
:class="cn(
|
||||
['md:relative md:isolate w-16 py-6 grid gap-4 grid-rows-[1fr_auto] justify-center transition-all'], // Base appearance
|
||||
['after:opacity-50 after:contrast-125 dark:after:opacity-75 dark:after:contrast-175 after:-hue-rotate-60'], // After styling
|
||||
['border-r-[1px] bg-indigo-700 dark:bg-black text-white border-r-indigo-700 dark:border-r-indigo-950 shadow-navbar-light dark:shadow-navbar-dark'], // Colours
|
||||
['max-md:justify-stretch max-md:px-4 max-md:py-4 max-md:absolute max-md:left-0 max-md:inset-0 max-md:z-50 max-md:w-30 max-md:max-w-full'], // Responsive behaviours
|
||||
{
|
||||
'max-md:-translate-x-30': !isSidebarOpened,
|
||||
'max-md:-translate-x-0 shadow-navbar-dark dark:bg-slate-950': isSidebarOpened
|
||||
}
|
||||
)"
|
||||
>
|
||||
<menu class="flex flex-col gap-4 max-md:items-center">
|
||||
<li class="mb-12 mt-4 max-md:self-start">
|
||||
<UiButton
|
||||
variant="outline"
|
||||
size="icon"
|
||||
class="md:hidden size-9 border-background/30"
|
||||
@click="toggleSidebar"
|
||||
>
|
||||
<PhX size="19" />
|
||||
</UiButton>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<UiTooltipProvider :delay-duration="50">
|
||||
<li class="max-md:self-start">
|
||||
<UiTooltipProvider :delay-duration="50" :disabled="!breakpoints.md.value">
|
||||
<UiTooltip>
|
||||
<UiTooltipTrigger as-child>
|
||||
<UiButton variant="ghost" size="icon" class="rounded-full" as-child>
|
||||
<UiButton
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="rounded-full max-md:hidden"
|
||||
as-child
|
||||
>
|
||||
<RouterLink to="/explore">
|
||||
<PhCompass size="24" weight="fill" />
|
||||
</RouterLink>
|
||||
</UiButton>
|
||||
<RouterLink
|
||||
to="/explore"
|
||||
class="md:hidden flex items-center gap-[.6ch] underline-offset-4 hover:underline"
|
||||
>
|
||||
<PhCompass size="22" weight="fill" />
|
||||
|
||||
<span class="text-[.9em]">
|
||||
{{ $t('pages.explore.menuLabel') }}
|
||||
</span>
|
||||
</RouterLink>
|
||||
</UiTooltipTrigger>
|
||||
<UiTooltipContent :side="'right'" :side-offset="6">
|
||||
<p>
|
||||
@@ -92,9 +137,6 @@ function computeMenuItemIcon(iconString: SidebarMenuIcon) {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
nav {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
content: '';
|
||||
|
||||
15
components/global/sidebar/SidebarToggle.vue
Normal file
15
components/global/sidebar/SidebarToggle.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<script lang="ts" setup>
|
||||
import { PhList } from "@phosphor-icons/vue"
|
||||
|
||||
const { toggleSidebar } = useUiStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UiButton
|
||||
size="icon"
|
||||
variant="outline"
|
||||
@click="toggleSidebar"
|
||||
>
|
||||
<PhList size="19" weight="light" />
|
||||
</UiButton>
|
||||
</template>
|
||||
@@ -21,29 +21,27 @@ function closeMenu() {
|
||||
watch(user, closeMenu)
|
||||
|
||||
async function handleGoogleLogin() {
|
||||
try {
|
||||
auth.signInWithOAuth({
|
||||
provider: "google",
|
||||
options: {
|
||||
queryParams: {
|
||||
access_type: "offline",
|
||||
prompt: "consent"
|
||||
},
|
||||
redirectTo: profileUrl
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
const { error } = await auth.signInWithOAuth({
|
||||
provider: "google",
|
||||
options: {
|
||||
queryParams: {
|
||||
access_type: "offline",
|
||||
prompt: "consent"
|
||||
},
|
||||
redirectTo: profileUrl
|
||||
}
|
||||
})
|
||||
|
||||
if (error) {
|
||||
console.log(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLogout() {
|
||||
try {
|
||||
const { error } = await auth.signOut()
|
||||
const { error } = await auth.signOut()
|
||||
|
||||
if (error) throw error
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
if (error) {
|
||||
console.log(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import { AlertDialogAction, type AlertDialogActionProps } from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from "@/components/ui/button/index"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { buttonVariants } from "~/components/ui/button/index"
|
||||
|
||||
const props = defineProps<AlertDialogActionProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import { AlertDialogCancel, type AlertDialogCancelProps } from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from "@/components/ui/button/index"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { buttonVariants } from "~/components/ui/button/index"
|
||||
|
||||
const props = defineProps<AlertDialogCancelProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
AlertDialogPortal,
|
||||
useForwardPropsEmits,
|
||||
} from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<AlertDialogContentProps & { class?: HTMLAttributes["class"] }>()
|
||||
const emits = defineEmits<AlertDialogContentEmits>()
|
||||
@@ -31,7 +31,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'fixed left-1/2 top-1/2 z-50 grid w-full max-w-3xl -translate-x-1/2 -translate-y-1/2 gap-4 border border-indigo-200 bg-background dark:bg-slate-950 dark:border-indigo-950 p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||
'fixed left-1/2 top-1/2 z-50 grid w-[calc(100%-2rem)] max-w-3xl -translate-x-1/2 -translate-y-1/2 gap-4 border border-indigo-200 bg-background dark:bg-slate-950 dark:border-indigo-950 p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-50% data-[state=closed]:slide-out-to-top-48% data-[state=open]:slide-in-from-left-50% data-[state=open]:slide-in-from-top-48% rounded-lg',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
AlertDialogDescription,
|
||||
type AlertDialogDescriptionProps,
|
||||
} from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<AlertDialogDescriptionProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import { AlertDialogTitle, type AlertDialogTitleProps } from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<AlertDialogTitleProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { AvatarRoot } from "radix-vue"
|
||||
import { type AvatarVariants, avatarVariant } from "."
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { type BadgeVariants, badgeVariants } from "."
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
variant?: BadgeVariants["variant"]
|
||||
|
||||
@@ -3,7 +3,7 @@ import { type VariantProps, cva } from "class-variance-authority"
|
||||
export { default as Badge } from "./Badge.vue"
|
||||
|
||||
export const badgeVariants = cva(
|
||||
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { MoreHorizontal } from "lucide-vue-next"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { PhArrowsHorizontal } from "@phosphor-icons/vue";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
@@ -15,7 +15,7 @@ const props = defineProps<{
|
||||
:class="cn('flex h-9 w-9 items-center justify-center', props.class)"
|
||||
>
|
||||
<slot>
|
||||
<MoreHorizontal class="h-4 w-4" />
|
||||
<PhArrowsHorizontal class="size-4" />
|
||||
</slot>
|
||||
<span class="sr-only">More</span>
|
||||
</span>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import type { PrimitiveProps } from "radix-vue"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { Primitive } from "radix-vue"
|
||||
|
||||
const props = withDefaults(defineProps<PrimitiveProps & { class?: HTMLAttributes["class"] }>(), {
|
||||
@@ -13,7 +13,7 @@ const props = withDefaults(defineProps<PrimitiveProps & { class?: HTMLAttributes
|
||||
<Primitive
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn('transition-colors hover:text-foreground', props.class)"
|
||||
:class="cn('transition-colors rounded-[1px] hover:text-primary focus-visible:text-primary underline-offset-4 hover:underline focus-visible:underline focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-4', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { ChevronRight } from "lucide-vue-next"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { PhCaretRight } from "@phosphor-icons/vue";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
@@ -15,7 +15,7 @@ const props = defineProps<{
|
||||
:class="cn('[&>svg]:w-3 [&>svg]:h-3', props.class)"
|
||||
>
|
||||
<slot>
|
||||
<ChevronRight />
|
||||
<PhCaretRight />
|
||||
</slot>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { Primitive, type PrimitiveProps } from "radix-vue"
|
||||
import { type ButtonVariants, buttonVariants } from "."
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
variant?: ButtonVariants["variant"]
|
||||
@@ -20,13 +20,13 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
<Primitive
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn(buttonVariants({ variant, size }), props.class, 'group')"
|
||||
:class="cn(buttonVariants({ variant, size }), props.class, 'group cursor-pointer')"
|
||||
>
|
||||
<slot />
|
||||
|
||||
<span
|
||||
v-if="props.searchSlash"
|
||||
class="h-4 p-1 ml-1 grid place-items-center text-[0.7em] leading-none font-semibold text-slate-500 bg-slate-100 border-slate-300 border-[1px] rounded-[3px] shadow-sm group-hover:text-slate-600 group-hover:bg-slate-200 group-hover:border-slate-400 transition-colors"
|
||||
class="h-4 p-1 ml-1 grid place-items-center text-[0.7em] leading-none font-semibold text-slate-500 bg-slate-100 border-slate-300 border-[1px] rounded-[3px] shadow-xs group-hover:text-slate-600 group-hover:bg-slate-200 group-hover:border-slate-400 transition-colors"
|
||||
>
|
||||
<kbd class="-mt-[2px]">CTRL + :</kbd>
|
||||
</span>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { type VariantProps, cva } from "class-variance-authority"
|
||||
export { default as Button } from "./Button.vue"
|
||||
|
||||
export const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-30",
|
||||
"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-30",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
@@ -14,22 +14,23 @@ const emit = defineEmits(["on-click"])
|
||||
<template>
|
||||
<div
|
||||
:class="
|
||||
cn('rounded-lg border bg-card text-card-foreground shadow-sm transition-all isolate', props.class, {
|
||||
'relative outline outline-2 outline-offset-4 outline-transparent hover:-translate-y-[.2rem]':
|
||||
cn('rounded-lg border bg-card text-card-foreground shadow-xs transition-all isolate', props.class, {
|
||||
'relative hover:-translate-y-[.2rem]':
|
||||
props.link || props.hasClick
|
||||
})
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
|
||||
<NuxtLink
|
||||
v-if="props.link"
|
||||
:to="props.link"
|
||||
class="absolute inset-0 z-10 focus-visible:outline-none"
|
||||
class="rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 absolute inset-0 z-10 cursor-pointer"
|
||||
/>
|
||||
<button
|
||||
v-if="props.hasClick"
|
||||
class="absolute inset-0 z-10 focus-visible:outline-none"
|
||||
@click="emit('on-click')" />
|
||||
class="rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 absolute inset-0 z-10 cursor-pointer"
|
||||
@click="emit('on-click')"
|
||||
/>
|
||||
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
@@ -8,7 +8,7 @@ const props = defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('flex items-center p-6 border-t-2 border-t-slate-900', props.class)">
|
||||
<div :class="cn('flex items-center p-6 border-t-2 border-t-border', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import type { CheckboxRootEmits, CheckboxRootProps } from "radix-vue"
|
||||
import { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from "radix-vue"
|
||||
import { Check } from "lucide-vue-next"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { PhCheck } from "@phosphor-icons/vue";
|
||||
|
||||
const props = defineProps<CheckboxRootProps & { class?: HTMLAttributes["class"] }>()
|
||||
const emits = defineEmits<CheckboxRootEmits>()
|
||||
@@ -21,12 +21,12 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
<CheckboxRoot
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn('peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
|
||||
cn('peer size-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
|
||||
props.class)"
|
||||
>
|
||||
<CheckboxIndicator class="flex h-full w-full items-center justify-center text-current">
|
||||
<slot>
|
||||
<Check class="h-4 w-4" />
|
||||
<PhCheck class="size-4" />
|
||||
</slot>
|
||||
</CheckboxIndicator>
|
||||
</CheckboxRoot>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import type { ComboboxRootEmits, ComboboxRootProps } from "radix-vue"
|
||||
import { ComboboxRoot, useForwardPropsEmits } from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = withDefaults(defineProps<ComboboxRootProps & { class?: HTMLAttributes["class"] }>(), {
|
||||
open: true,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { useForwardPropsEmits } from "radix-vue"
|
||||
import type { DialogRootEmits, DialogRootProps } from "radix-vue"
|
||||
import Command from "./Command.vue"
|
||||
import { Dialog, DialogContent } from "@/components/ui/dialog"
|
||||
import { Dialog, DialogContent } from "~/components/ui/dialog"
|
||||
|
||||
const props = defineProps<DialogRootProps>()
|
||||
const emits = defineEmits<DialogRootEmits>()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import type { ComboboxEmptyProps } from "radix-vue"
|
||||
import { ComboboxEmpty } from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<ComboboxEmptyProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import type { ComboboxGroupProps } from "radix-vue"
|
||||
import { ComboboxGroup, ComboboxLabel } from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<
|
||||
ComboboxGroupProps & {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import { Search } from "lucide-vue-next"
|
||||
import { ComboboxInput, type ComboboxInputProps, useForwardProps } from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { PhMagnifyingGlass } from "@phosphor-icons/vue"
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false
|
||||
@@ -25,13 +25,13 @@ const forwardedProps = useForwardProps(delegatedProps)
|
||||
|
||||
<template>
|
||||
<div class="flex items-center border-b px-3" cmdk-input-wrapper>
|
||||
<Search class="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<PhMagnifyingGlass class="mr-2 size-4 shrink-0 opacity-50" />
|
||||
<ComboboxInput
|
||||
v-bind="{ ...forwardedProps, ...$attrs }"
|
||||
auto-focus
|
||||
:class="
|
||||
cn(
|
||||
'flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import type { ComboboxItemEmits, ComboboxItemProps } from "radix-vue"
|
||||
import { ComboboxItem, useForwardPropsEmits } from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<ComboboxItemProps & { class?: HTMLAttributes["class"] }>()
|
||||
const emits = defineEmits<ComboboxItemEmits>()
|
||||
@@ -21,7 +21,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden data-highlighted:bg-accent data-disabled:pointer-events-none data-disabled:opacity-50',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import type { ComboboxContentEmits, ComboboxContentProps } from "radix-vue"
|
||||
import { ComboboxContent, useForwardPropsEmits } from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<ComboboxContentProps & { class?: HTMLAttributes["class"] }>(),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import type { ComboboxSeparatorProps } from "radix-vue"
|
||||
import { ComboboxSeparator } from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<ComboboxSeparatorProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
DialogPortal,
|
||||
useForwardPropsEmits
|
||||
} from "radix-vue"
|
||||
import { X } from "lucide-vue-next"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { PhX } from "@phosphor-icons/vue";
|
||||
|
||||
const props = defineProps<DialogContentProps & { class?: HTMLAttributes["class"] }>()
|
||||
const emits = defineEmits<DialogContentEmits>()
|
||||
@@ -33,7 +33,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'fixed left-1/2 top-1/2 z-50 grid w-[calc(100%-2rem)] max-w-4xl -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background border-indigo-200 dark:bg-slate-950 dark:border-indigo-950 p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||
'fixed left-1/2 top-1/2 z-50 grid w-[calc(100%-2rem)] max-w-4xl -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background border-indigo-200 dark:bg-slate-950 dark:border-indigo-950 p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-50% data-[state=closed]:slide-out-to-top-48% data-[state=open]:slide-in-from-left-50% data-[state=open]:slide-in-from-top-48% rounded-lg',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
@@ -41,9 +41,9 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
<slot />
|
||||
|
||||
<DialogClose
|
||||
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
|
||||
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
|
||||
>
|
||||
<X class="w-4 h-4" />
|
||||
<PhX class="w-4 h-4" />
|
||||
<span class="sr-only">Close</span>
|
||||
</DialogClose>
|
||||
</DialogContent>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import { DialogDescription, type DialogDescriptionProps, useForwardProps } from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<DialogDescriptionProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{ class?: HTMLAttributes["class"] }>()
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
DialogPortal,
|
||||
useForwardPropsEmits
|
||||
} from "radix-vue"
|
||||
import { X } from "lucide-vue-next"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { PhX } from "@phosphor-icons/vue";
|
||||
|
||||
const props = defineProps<DialogContentProps & { class?: HTMLAttributes["class"] }>()
|
||||
const emits = defineEmits<DialogContentEmits>()
|
||||
@@ -32,7 +32,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
<DialogContent
|
||||
:class="
|
||||
cn(
|
||||
'relative z-50 grid w-full max-w-lg my-8 gap-4 border border-border bg-background p-6 shadow-lg duration-200 sm:rounded-lg md:w-full',
|
||||
'relative z-50 grid w-full max-w-lg my-8 gap-4 border border-border bg-background p-6 shadow-lg duration-200 rounded-lg md:w-full',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
@@ -55,7 +55,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
<DialogClose
|
||||
class="absolute top-3 right-3 p-0.5 transition-colors rounded-md hover:bg-secondary"
|
||||
>
|
||||
<X class="w-4 h-4" />
|
||||
<PhX class="w-4 h-4" />
|
||||
<span class="sr-only">Close</span>
|
||||
</DialogClose>
|
||||
</DialogContent>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import { DialogTitle, type DialogTitleProps, useForwardProps } from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<DialogTitleProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
DropdownMenuItemIndicator,
|
||||
useForwardPropsEmits
|
||||
} from "radix-vue"
|
||||
import { Check } from "lucide-vue-next"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { PhCheck } from "@phosphor-icons/vue";
|
||||
|
||||
const props = defineProps<DropdownMenuCheckboxItemProps & { class?: HTMLAttributes["class"] }>()
|
||||
const emits = defineEmits<DropdownMenuCheckboxItemEmits>()
|
||||
@@ -27,14 +27,14 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<DropdownMenuItemIndicator>
|
||||
<Check class="w-4 h-4" />
|
||||
<PhCheck class="size-4" />
|
||||
</DropdownMenuItemIndicator>
|
||||
</span>
|
||||
<slot />
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
DropdownMenuPortal,
|
||||
useForwardPropsEmits
|
||||
} from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<DropdownMenuContentProps & { class?: HTMLAttributes["class"] }>(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import { DropdownMenuItem, type DropdownMenuItemProps, useForwardProps } from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<
|
||||
DropdownMenuItemProps & { class?: HTMLAttributes["class"]; inset?: boolean }
|
||||
@@ -21,7 +21,7 @@ const forwardedProps = useForwardProps(delegatedProps)
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50',
|
||||
inset && 'pl-8',
|
||||
props.class
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import { DropdownMenuLabel, type DropdownMenuLabelProps, useForwardProps } from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<
|
||||
DropdownMenuLabelProps & { class?: HTMLAttributes["class"]; inset?: boolean }
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
type DropdownMenuRadioItemProps,
|
||||
useForwardPropsEmits
|
||||
} from "radix-vue"
|
||||
import { Circle } from "lucide-vue-next"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { PhCircle } from "@phosphor-icons/vue";
|
||||
|
||||
const props = defineProps<DropdownMenuRadioItemProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
@@ -28,14 +28,14 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<span class="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuItemIndicator>
|
||||
<Circle class="h-2 w-2 fill-current" />
|
||||
<PhCircle class="size-2 text-current" />
|
||||
</DropdownMenuItemIndicator>
|
||||
</span>
|
||||
<slot />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import { DropdownMenuSeparator, type DropdownMenuSeparatorProps } from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<
|
||||
DropdownMenuSeparatorProps & {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
type DropdownMenuSubContentProps,
|
||||
useForwardPropsEmits
|
||||
} from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<DropdownMenuSubContentProps & { class?: HTMLAttributes["class"] }>()
|
||||
const emits = defineEmits<DropdownMenuSubContentEmits>()
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
type DropdownMenuSubTriggerProps,
|
||||
useForwardProps
|
||||
} from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { PhCaretLeft, PhCaretRight } from "@phosphor-icons/vue";
|
||||
|
||||
const props = defineProps<DropdownMenuSubTriggerProps & { class?: HTMLAttributes["class"], arrowDirection?: "left" | "right" }>()
|
||||
@@ -24,13 +24,13 @@ const forwardedProps = useForwardProps(delegatedProps)
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent',
|
||||
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden focus:bg-accent data-[state=open]:bg-accent',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<template v-if="props.arrowDirection === 'left'">
|
||||
<PhCaretLeft class="mr-auto h-4 w-4" />
|
||||
<PhCaretLeft class="mr-auto size-4" />
|
||||
|
||||
<span class="inline-block">
|
||||
<slot />
|
||||
@@ -39,7 +39,7 @@ const forwardedProps = useForwardProps(delegatedProps)
|
||||
<template v-else>
|
||||
<slot />
|
||||
|
||||
<PhCaretRight class="ml-auto h-4 w-4" />
|
||||
<PhCaretRight class="ml-auto size-4" />
|
||||
</template>
|
||||
</DropdownMenuSubTrigger>
|
||||
</template>
|
||||
|
||||
@@ -7,7 +7,7 @@ const forwardedProps = useForwardProps(props)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuTrigger class="outline-none" v-bind="forwardedProps">
|
||||
<DropdownMenuTrigger class="outline-hidden" v-bind="forwardedProps">
|
||||
<slot />
|
||||
</DropdownMenuTrigger>
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { useVModel } from "@vueuse/core"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
defaultValue?: string | number
|
||||
@@ -24,7 +24,7 @@ const modelValue = useVModel(props, "modelValue", emits, {
|
||||
v-model="modelValue"
|
||||
:class="
|
||||
cn(
|
||||
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import { Label, type LabelProps } from "radix-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const props = defineProps<LabelProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from "vue"
|
||||
import { PaginationEllipsis, type PaginationEllipsisProps } from "radix-vue"
|
||||
import { MoreHorizontal } from "lucide-vue-next"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { PhArrowsHorizontal } from "@phosphor-icons/vue";
|
||||
|
||||
const props = defineProps<PaginationEllipsisProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
@@ -16,10 +16,10 @@ const delegatedProps = computed(() => {
|
||||
<template>
|
||||
<PaginationEllipsis
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('w-9 h-9 flex items-center justify-center', props.class)"
|
||||
:class="cn('size-8 md:size-10 flex items-center justify-center', props.class)"
|
||||
>
|
||||
<slot>
|
||||
<MoreHorizontal />
|
||||
<PhArrowsHorizontal />
|
||||
</slot>
|
||||
</PaginationEllipsis>
|
||||
</template>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user