diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml index 8463cf8..5647744 100644 --- a/frontend/Cargo.toml +++ b/frontend/Cargo.toml @@ -20,6 +20,19 @@ wasm-bindgen = "0.2" uuid = { version = "1", features = ["v4", "js"] } futures = "0.3" chrono = { version = "0.4", features = ["serde"] } -web-sys = { version = "0.3", features = ["Window", "Storage", "Document", "Element"] } +web-sys = { version = "0.3", features = [ + "HtmlDivElement", + "HtmlUListElement", + "HtmlLiElement", + "HtmlAnchorElement", + "MouseEvent", + "Event", + "Window", + "Document", + "Element", + "DomTokenList", + "CssStyleDeclaration", + "Storage" +] } shared = { path = "../shared" } tailwind_fuse = "0.3.2" diff --git a/frontend/public/tailwind.css b/frontend/public/tailwind.css index 364acde..b7cb82e 100644 --- a/frontend/public/tailwind.css +++ b/frontend/public/tailwind.css @@ -192,6 +192,61 @@ } } @layer utilities { + .modal { + @layer daisyui.l1.l2.l3 { + pointer-events: none; + visibility: hidden; + position: fixed; + inset: calc(0.25rem * 0); + margin: calc(0.25rem * 0); + display: grid; + height: 100%; + max-height: none; + width: 100%; + max-width: none; + align-items: center; + justify-items: center; + background-color: transparent; + padding: calc(0.25rem * 0); + color: inherit; + transition: visibility 0.3s allow-discrete, background-color 0.3s ease-out, opacity 0.1s ease-out; + overflow: clip; + overscroll-behavior: contain; + z-index: 999; + scrollbar-gutter: auto; + &::backdrop { + display: none; + } + } + @layer daisyui.l1.l2 { + &.modal-open, &[open], &:target, .modal-toggle:checked + & { + pointer-events: auto; + visibility: visible; + opacity: 100%; + transition: visibility 0s allow-discrete, background-color 0.3s ease-out, opacity 0.1s ease-out; + background-color: oklch(0% 0 0/ 0.4); + .modal-box { + translate: 0 0; + scale: 1; + opacity: 1; + } + :root:has(&) { + --page-has-backdrop: 1; + --page-overflow: hidden; + --page-scroll-bg: var(--page-scroll-bg-on); + --page-scroll-gutter: stable; + --page-scroll-transition: var(--page-scroll-transition-on); + animation: set-page-has-scroll forwards; + animation-timeline: scroll(); + } + } + @starting-style { + &.modal-open, &[open], &:target, .modal-toggle:checked + & { + opacity: 0%; + } + } + } + } .drawer-side { :where(&) { @layer daisyui.l1.l2.l3 { @@ -591,6 +646,20 @@ } } } + .loading { + @layer daisyui.l1.l2.l3 { + pointer-events: none; + display: inline-block; + aspect-ratio: 1 / 1; + background-color: currentcolor; + vertical-align: middle; + width: calc(var(--size-selector, 0.25rem) * 6); + mask-size: 100%; + mask-repeat: no-repeat; + mask-position: center; + mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); + } + } .\!visible { visibility: visible !important; } @@ -941,6 +1010,20 @@ .inset-0 { inset: calc(var(--spacing) * 0); } + .modal-backdrop { + @layer daisyui.l1.l2.l3 { + grid-column-start: 1; + grid-row-start: 1; + display: grid; + align-self: stretch; + justify-self: stretch; + color: transparent; + z-index: -1; + button { + cursor: pointer; + } + } + } .z-40 { z-index: 40; } @@ -950,6 +1033,27 @@ .z-\[200\] { z-index: 200; } + .modal-box { + @layer daisyui.l1.l2.l3 { + grid-column-start: 1; + grid-row-start: 1; + max-height: 100vh; + width: calc(11/12 * 100%); + max-width: 32rem; + background-color: var(--color-base-100); + padding: calc(0.25rem * 6); + transition: translate 0.3s ease-out, scale 0.3s ease-out, opacity 0.2s ease-out 0.05s, box-shadow 0.3s ease-out; + border-top-left-radius: var(--modal-tl, var(--radius-box)); + border-top-right-radius: var(--modal-tr, var(--radius-box)); + border-bottom-left-radius: var(--modal-bl, var(--radius-box)); + border-bottom-right-radius: var(--modal-br, var(--radius-box)); + scale: 95%; + opacity: 0; + box-shadow: oklch(0% 0 0/ 0.25) 0px 25px 50px -12px; + overflow-y: auto; + overscroll-behavior: contain; + } + } .drawer-content { @layer daisyui.l1.l2.l3 { grid-column-start: 2; @@ -1065,6 +1169,17 @@ border-width: var(--border, 1px) 0 var(--border, 1px) var(--border, 1px); } } + .modal-action { + @layer daisyui.l1.l2.l3 { + margin-top: calc(0.25rem * 6); + display: flex; + justify-content: flex-end; + gap: calc(0.25rem * 2); + } + } + .mt-2 { + margin-top: calc(var(--spacing) * 2); + } .mt-auto { margin-top: auto; } @@ -1207,6 +1322,23 @@ .table { display: table; } + .modal-bottom { + @layer daisyui.l1.l2 { + place-items: end; + .modal-box { + height: auto; + width: 100%; + max-width: none; + max-height: calc(100vh - 5em); + translate: 0 100%; + scale: 1; + --modal-tl: var(--radius-box); + --modal-tr: var(--radius-box); + --modal-bl: 0; + --modal-br: 0; + } + } + } .btn-square { @layer daisyui.l1.l2 { padding-inline: calc(0.25rem * 0); @@ -1413,6 +1545,11 @@ background-color: color-mix(in oklab, var(--color-white) 10%, transparent); } } + .loading-spinner { + @layer daisyui.l1.l2 { + mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); + } + } .stroke-current { stroke: currentcolor; } @@ -1464,6 +1601,9 @@ .py-2\.5 { padding-block: calc(var(--spacing) * 2.5); } + .py-4 { + padding-block: calc(var(--spacing) * 4); + } .text-left { text-align: left; } @@ -1686,6 +1826,12 @@ --size: calc(var(--size-field, 0.25rem) * 6); } } + .btn-primary { + @layer daisyui.l1.l2 { + --btn-color: var(--color-primary); + --btn-fg: var(--color-primary-content); + } + } .select-none { -webkit-user-select: none; user-select: none; @@ -1793,6 +1939,25 @@ opacity: 50%; } } + .sm\:modal-middle { + @media (width >= 40rem) { + @layer daisyui.l1.l2 { + place-items: center; + .modal-box { + height: auto; + width: calc(11/12 * 100%); + max-width: 32rem; + max-height: calc(100vh - 5em); + translate: 0 2%; + scale: 98%; + --modal-tl: var(--radius-box); + --modal-tr: var(--radius-box); + --modal-bl: var(--radius-box); + --modal-br: var(--radius-box); + } + } + } + } .sm\:p-4 { @media (width >= 40rem) { padding: calc(var(--spacing) * 4); diff --git a/frontend/src/components/layout/toolbar.rs b/frontend/src/components/layout/toolbar.rs index 138067d..0313791 100644 --- a/frontend/src/components/layout/toolbar.rs +++ b/frontend/src/components/layout/toolbar.rs @@ -2,6 +2,8 @@ use leptos::*; #[component] pub fn Toolbar() -> impl IntoView { + let (show_add_modal, set_show_add_modal) = create_signal(false); + view! {