feat: Updates sidebar to use the drawer classes from daisyui, currently doesn't open automatically on large screens like I want.
This commit is contained in:
@@ -9,18 +9,15 @@
|
|||||||
monospace;
|
monospace;
|
||||||
--color-red-500: oklch(63.7% 0.237 25.331);
|
--color-red-500: oklch(63.7% 0.237 25.331);
|
||||||
--color-red-600: oklch(57.7% 0.245 27.325);
|
--color-red-600: oklch(57.7% 0.245 27.325);
|
||||||
|
--color-green-400: oklch(79.2% 0.209 151.711);
|
||||||
--color-indigo-600: oklch(51.1% 0.262 276.966);
|
--color-indigo-600: oklch(51.1% 0.262 276.966);
|
||||||
--color-slate-300: oklch(86.9% 0.022 252.894);
|
--color-slate-300: oklch(86.9% 0.022 252.894);
|
||||||
--color-slate-900: oklch(20.8% 0.042 265.755);
|
--color-slate-900: oklch(20.8% 0.042 265.755);
|
||||||
--color-gray-200: oklch(92.8% 0.006 264.531);
|
--color-gray-200: oklch(92.8% 0.006 264.531);
|
||||||
--color-gray-300: oklch(87.2% 0.01 258.338);
|
|
||||||
--color-gray-400: oklch(70.7% 0.022 261.325);
|
--color-gray-400: oklch(70.7% 0.022 261.325);
|
||||||
--color-gray-800: oklch(27.8% 0.033 256.848);
|
|
||||||
--color-black: #000;
|
--color-black: #000;
|
||||||
--color-white: #fff;
|
--color-white: #fff;
|
||||||
--spacing: 0.25rem;
|
--spacing: 0.25rem;
|
||||||
--text-xs: 0.75rem;
|
|
||||||
--text-xs--line-height: calc(1 / 0.75);
|
|
||||||
--text-sm: 0.875rem;
|
--text-sm: 0.875rem;
|
||||||
--text-sm--line-height: calc(1.25 / 0.875);
|
--text-sm--line-height: calc(1.25 / 0.875);
|
||||||
--text-lg: 1.125rem;
|
--text-lg: 1.125rem;
|
||||||
@@ -4225,21 +4222,12 @@
|
|||||||
--toast-y: 0;
|
--toast-y: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.top-0 {
|
|
||||||
top: calc(var(--spacing) * 0);
|
|
||||||
}
|
|
||||||
.top-2 {
|
.top-2 {
|
||||||
top: calc(var(--spacing) * 2);
|
top: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
.right-2 {
|
.right-2 {
|
||||||
right: calc(var(--spacing) * 2);
|
right: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
.right-4 {
|
|
||||||
right: calc(var(--spacing) * 4);
|
|
||||||
}
|
|
||||||
.right-6 {
|
|
||||||
right: calc(var(--spacing) * 6);
|
|
||||||
}
|
|
||||||
.dock-sm {
|
.dock-sm {
|
||||||
@layer daisyui.l1.l2 {
|
@layer daisyui.l1.l2 {
|
||||||
height: calc(0.25rem * 14);
|
height: calc(0.25rem * 14);
|
||||||
@@ -4297,12 +4285,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.bottom-4 {
|
|
||||||
bottom: calc(var(--spacing) * 4);
|
|
||||||
}
|
|
||||||
.bottom-6 {
|
|
||||||
bottom: calc(var(--spacing) * 6);
|
|
||||||
}
|
|
||||||
.join {
|
.join {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
@@ -5289,6 +5271,9 @@
|
|||||||
.mx-2 {
|
.mx-2 {
|
||||||
margin-inline: calc(var(--spacing) * 2);
|
margin-inline: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
|
.mx-4 {
|
||||||
|
margin-inline: calc(var(--spacing) * 4);
|
||||||
|
}
|
||||||
.file-input-ghost {
|
.file-input-ghost {
|
||||||
@layer daisyui.l1.l2 {
|
@layer daisyui.l1.l2 {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
@@ -5375,6 +5360,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.my-1\.5 {
|
||||||
|
margin-block: calc(var(--spacing) * 1.5);
|
||||||
|
}
|
||||||
.my-2 {
|
.my-2 {
|
||||||
margin-block: calc(var(--spacing) * 2);
|
margin-block: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
@@ -6380,6 +6368,14 @@
|
|||||||
height: var(--size);
|
height: var(--size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.size-4 {
|
||||||
|
width: calc(var(--spacing) * 4);
|
||||||
|
height: calc(var(--spacing) * 4);
|
||||||
|
}
|
||||||
|
.size-7 {
|
||||||
|
width: calc(var(--spacing) * 7);
|
||||||
|
height: calc(var(--spacing) * 7);
|
||||||
|
}
|
||||||
.status-lg {
|
.status-lg {
|
||||||
@layer daisyui.l1.l2 {
|
@layer daisyui.l1.l2 {
|
||||||
width: calc(0.25rem * 3);
|
width: calc(0.25rem * 3);
|
||||||
@@ -6428,6 +6424,9 @@
|
|||||||
.h-screen {
|
.h-screen {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
.min-h-full {
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
.btn-wide {
|
.btn-wide {
|
||||||
@layer daisyui.l1.l2 {
|
@layer daisyui.l1.l2 {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -6559,18 +6558,15 @@
|
|||||||
.w-full {
|
.w-full {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.max-w-\[280px\] {
|
|
||||||
max-width: 280px;
|
|
||||||
}
|
|
||||||
.flex-none {
|
|
||||||
flex: none;
|
|
||||||
}
|
|
||||||
.flex-shrink {
|
.flex-shrink {
|
||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
}
|
}
|
||||||
.flex-grow {
|
.flex-grow {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
.grow {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
.border-collapse {
|
.border-collapse {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
@@ -6755,14 +6751,11 @@
|
|||||||
.flex-col {
|
.flex-col {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.flex-row {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
.flex-wrap {
|
.flex-wrap {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
.items-center {
|
.items-start {
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
.justify-between {
|
.justify-between {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -7095,10 +7088,6 @@
|
|||||||
border-style: var(--tw-border-style);
|
border-style: var(--tw-border-style);
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
}
|
}
|
||||||
.border-r-2 {
|
|
||||||
border-right-style: var(--tw-border-style);
|
|
||||||
border-right-width: 2px;
|
|
||||||
}
|
|
||||||
.border-b {
|
.border-b {
|
||||||
border-bottom-style: var(--tw-border-style);
|
border-bottom-style: var(--tw-border-style);
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
@@ -7375,6 +7364,12 @@
|
|||||||
.bg-base-100 {
|
.bg-base-100 {
|
||||||
background-color: var(--color-base-100);
|
background-color: var(--color-base-100);
|
||||||
}
|
}
|
||||||
|
.bg-base-200 {
|
||||||
|
background-color: var(--color-base-200);
|
||||||
|
}
|
||||||
|
.bg-base-300 {
|
||||||
|
background-color: var(--color-base-300);
|
||||||
|
}
|
||||||
.bg-red-500 {
|
.bg-red-500 {
|
||||||
background-color: var(--color-red-500);
|
background-color: var(--color-red-500);
|
||||||
}
|
}
|
||||||
@@ -7666,9 +7661,6 @@
|
|||||||
.p-4 {
|
.p-4 {
|
||||||
padding: calc(var(--spacing) * 4);
|
padding: calc(var(--spacing) * 4);
|
||||||
}
|
}
|
||||||
.p-6 {
|
|
||||||
padding: calc(var(--spacing) * 6);
|
|
||||||
}
|
|
||||||
.menu-title {
|
.menu-title {
|
||||||
@layer daisyui.l1.l2.l3 {
|
@layer daisyui.l1.l2.l3 {
|
||||||
padding-inline: calc(0.25rem * 3);
|
padding-inline: calc(0.25rem * 3);
|
||||||
@@ -7792,21 +7784,12 @@
|
|||||||
.px-4 {
|
.px-4 {
|
||||||
padding-inline: calc(var(--spacing) * 4);
|
padding-inline: calc(var(--spacing) * 4);
|
||||||
}
|
}
|
||||||
.px-6 {
|
|
||||||
padding-inline: calc(var(--spacing) * 6);
|
|
||||||
}
|
|
||||||
.py-1 {
|
|
||||||
padding-block: calc(var(--spacing) * 1);
|
|
||||||
}
|
|
||||||
.py-1\.5 {
|
.py-1\.5 {
|
||||||
padding-block: calc(var(--spacing) * 1.5);
|
padding-block: calc(var(--spacing) * 1.5);
|
||||||
}
|
}
|
||||||
.py-2 {
|
.py-2 {
|
||||||
padding-block: calc(var(--spacing) * 2);
|
padding-block: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
.py-10 {
|
|
||||||
padding-block: calc(var(--spacing) * 10);
|
|
||||||
}
|
|
||||||
.ps-2 {
|
.ps-2 {
|
||||||
padding-inline-start: calc(var(--spacing) * 2);
|
padding-inline-start: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
@@ -8457,6 +8440,9 @@
|
|||||||
.text-gray-400 {
|
.text-gray-400 {
|
||||||
color: var(--color-gray-400);
|
color: var(--color-gray-400);
|
||||||
}
|
}
|
||||||
|
.text-green-400 {
|
||||||
|
color: var(--color-green-400);
|
||||||
|
}
|
||||||
.text-info {
|
.text-info {
|
||||||
color: var(--color-info);
|
color: var(--color-info);
|
||||||
}
|
}
|
||||||
@@ -9348,13 +9334,6 @@
|
|||||||
border-color: var(--color-red-500);
|
border-color: var(--color-red-500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.hover\:bg-gray-300 {
|
|
||||||
&:hover {
|
|
||||||
@media (hover: hover) {
|
|
||||||
background-color: var(--color-gray-300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.hover\:bg-red-600 {
|
.hover\:bg-red-600 {
|
||||||
&:hover {
|
&:hover {
|
||||||
@media (hover: hover) {
|
@media (hover: hover) {
|
||||||
@@ -9362,13 +9341,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.hover\:text-gray-800 {
|
|
||||||
&:hover {
|
|
||||||
@media (hover: hover) {
|
|
||||||
color: var(--color-gray-800);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.focus\:outline {
|
.focus\:outline {
|
||||||
&:focus {
|
&:focus {
|
||||||
outline-style: var(--tw-outline-style);
|
outline-style: var(--tw-outline-style);
|
||||||
@@ -9385,14 +9357,9 @@
|
|||||||
outline-color: var(--color-indigo-600);
|
outline-color: var(--color-indigo-600);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.data-\[active\=true\]\:bg-gray-300 {
|
.md\:hidden {
|
||||||
&[data-active="true"] {
|
@media (width >= 48rem) {
|
||||||
background-color: var(--color-gray-300);
|
display: none;
|
||||||
}
|
|
||||||
}
|
|
||||||
.data-\[active\=true\]\:text-gray-800 {
|
|
||||||
&[data-active="true"] {
|
|
||||||
color: var(--color-gray-800);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.md\:grid-cols-2 {
|
.md\:grid-cols-2 {
|
||||||
@@ -9400,6 +9367,64 @@
|
|||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.lg\:drawer-open {
|
||||||
|
@media (width >= 64rem) {
|
||||||
|
@layer daisyui.l1.l2.l3 {
|
||||||
|
> .drawer-toggle:checked {
|
||||||
|
~ .drawer-side {
|
||||||
|
scrollbar-color: revert-layer;
|
||||||
|
}
|
||||||
|
:root:has(&) {
|
||||||
|
--page-overflow: revert-layer;
|
||||||
|
--page-scroll-gutter: revert-layer;
|
||||||
|
--page-scroll-bg: revert-layer;
|
||||||
|
--page-scroll-transition: revert-layer;
|
||||||
|
--page-has-backdrop: revert-layer;
|
||||||
|
animation: revert-layer;
|
||||||
|
animation-timeline: revert-layer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@layer daisyui.l1.l2 {
|
||||||
|
> .drawer-side {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
> .drawer-toggle {
|
||||||
|
display: none;
|
||||||
|
~ .drawer-side {
|
||||||
|
pointer-events: auto;
|
||||||
|
visibility: visible;
|
||||||
|
position: sticky;
|
||||||
|
display: block;
|
||||||
|
width: auto;
|
||||||
|
overscroll-behavior: auto;
|
||||||
|
opacity: 100%;
|
||||||
|
> .drawer-overlay {
|
||||||
|
cursor: default;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:checked ~ .drawer-side {
|
||||||
|
pointer-events: auto;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@layer daisyui.l1 {
|
||||||
|
> .drawer-toggle ~ .drawer-side > :not(.drawer-overlay) {
|
||||||
|
translate: 0%;
|
||||||
|
[dir="rtl"] & {
|
||||||
|
translate: 0%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.lg\:hidden {
|
||||||
|
@media (width >= 64rem) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
.lg\:grid-cols-3 {
|
.lg\:grid-cols-3 {
|
||||||
@media (width >= 64rem) {
|
@media (width >= 64rem) {
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
@@ -9410,6 +9435,149 @@
|
|||||||
color: var(--color-white);
|
color: var(--color-white);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.is-drawer-close\:tooltip {
|
||||||
|
&:where(.drawer-toggle:not(:checked) ~ .drawer-side, .drawer-toggle:not(:checked) ~ .drawer-side *) {
|
||||||
|
@layer daisyui.l1.l2.l3 {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
--tt-bg: var(--color-neutral);
|
||||||
|
--tt-off: calc(100% + 0.5rem);
|
||||||
|
--tt-tail: calc(100% + 1px + 0.25rem);
|
||||||
|
& > .tooltip-content, &[data-tip]:before {
|
||||||
|
position: absolute;
|
||||||
|
max-width: 20rem;
|
||||||
|
border-radius: var(--radius-field);
|
||||||
|
padding-inline: calc(0.25rem * 2);
|
||||||
|
padding-block: calc(0.25rem * 1);
|
||||||
|
text-align: center;
|
||||||
|
white-space: normal;
|
||||||
|
color: var(--color-neutral-content);
|
||||||
|
opacity: 0%;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.25;
|
||||||
|
background-color: var(--tt-bg);
|
||||||
|
width: max-content;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 2;
|
||||||
|
--tw-content: attr(data-tip);
|
||||||
|
content: var(--tw-content);
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
opacity: 0%;
|
||||||
|
background-color: var(--tt-bg);
|
||||||
|
content: "";
|
||||||
|
pointer-events: none;
|
||||||
|
width: 0.625rem;
|
||||||
|
height: 0.25rem;
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: -1px 0;
|
||||||
|
--mask-tooltip: url("data:image/svg+xml,%3Csvg width='10' height='4' viewBox='0 0 8 4' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.500009 1C3.5 1 3.00001 4 5.00001 4C7 4 6.5 1 9.5 1C10 1 10 0.499897 10 0H0C-1.99338e-08 0.5 0 1 0.500009 1Z' fill='black'/%3E%3C/svg%3E%0A");
|
||||||
|
mask-image: var(--mask-tooltip);
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
& > .tooltip-content, &[data-tip]:before, &:after {
|
||||||
|
transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1) 75ms, transform 0.2s cubic-bezier(0.4, 0, 0.2, 1) 75ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:is([data-tip]:not([data-tip=""]), :has(.tooltip-content:not(:empty))) {
|
||||||
|
&.tooltip-open, &:hover, &:has(:focus-visible) {
|
||||||
|
& > .tooltip-content, &[data-tip]:before, &:after {
|
||||||
|
opacity: 100%;
|
||||||
|
--tt-pos: 0rem;
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1) 0s, transform 0.2s cubic-bezier(0.4, 0, 0.2, 1) 0s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@layer daisyui.l1.l2 {
|
||||||
|
> .tooltip-content, &[data-tip]:before {
|
||||||
|
transform: translateX(-50%) translateY(var(--tt-pos, 0.25rem));
|
||||||
|
inset: auto auto var(--tt-off) 50%;
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
transform: translateX(-50%) translateY(var(--tt-pos, 0.25rem));
|
||||||
|
inset: auto auto var(--tt-tail) 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.is-drawer-close\:tooltip-right {
|
||||||
|
&:where(.drawer-toggle:not(:checked) ~ .drawer-side, .drawer-toggle:not(:checked) ~ .drawer-side *) {
|
||||||
|
@layer daisyui.l1.l2 {
|
||||||
|
> .tooltip-content, &[data-tip]:before {
|
||||||
|
transform: translateX(calc(var(--tt-pos, -0.25rem) + 0.25rem)) translateY(-50%);
|
||||||
|
inset: 50% auto auto var(--tt-off);
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
transform: translateX(var(--tt-pos, -0.25rem)) translateY(-50%) rotate(90deg);
|
||||||
|
inset: 50% auto auto calc(var(--tt-tail) + 1px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.is-drawer-close\:hidden {
|
||||||
|
&:where(.drawer-toggle:not(:checked) ~ .drawer-side, .drawer-toggle:not(:checked) ~ .drawer-side *) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.is-drawer-close\:w-14 {
|
||||||
|
&:where(.drawer-toggle:not(:checked) ~ .drawer-side, .drawer-toggle:not(:checked) ~ .drawer-side *) {
|
||||||
|
width: calc(var(--spacing) * 14);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.is-drawer-close\:min-w-\[80px\] {
|
||||||
|
&:where(.drawer-toggle:not(:checked) ~ .drawer-side, .drawer-toggle:not(:checked) ~ .drawer-side *) {
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.is-drawer-close\:items-center {
|
||||||
|
&:where(.drawer-toggle:not(:checked) ~ .drawer-side, .drawer-toggle:not(:checked) ~ .drawer-side *) {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.is-drawer-close\:overflow-visible {
|
||||||
|
&:where(.drawer-toggle:not(:checked) ~ .drawer-side, .drawer-toggle:not(:checked) ~ .drawer-side *) {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.is-drawer-close\:text-error {
|
||||||
|
&:where(.drawer-toggle:not(:checked) ~ .drawer-side, .drawer-toggle:not(:checked) ~ .drawer-side *) {
|
||||||
|
color: var(--color-error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.is-drawer-close\:text-green-400 {
|
||||||
|
&:where(.drawer-toggle:not(:checked) ~ .drawer-side, .drawer-toggle:not(:checked) ~ .drawer-side *) {
|
||||||
|
color: var(--color-green-400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.is-drawer-open\:w-64 {
|
||||||
|
&:where(.drawer-toggle:checked ~ .drawer-side, .drawer-toggle:checked ~ .drawer-side *) {
|
||||||
|
width: calc(var(--spacing) * 64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.is-drawer-open\:min-w-\[340px\] {
|
||||||
|
&:where(.drawer-toggle:checked ~ .drawer-side, .drawer-toggle:checked ~ .drawer-side *) {
|
||||||
|
min-width: 340px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.is-drawer-open\:justify-between {
|
||||||
|
&:where(.drawer-toggle:checked ~ .drawer-side, .drawer-toggle:checked ~ .drawer-side *) {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.is-drawer-open\:space-x-4 {
|
||||||
|
&:where(.drawer-toggle:checked ~ .drawer-side, .drawer-toggle:checked ~ .drawer-side *) {
|
||||||
|
:where(& > :not(:last-child)) {
|
||||||
|
--tw-space-x-reverse: 0;
|
||||||
|
margin-inline-start: calc(calc(var(--spacing) * 4) * var(--tw-space-x-reverse));
|
||||||
|
margin-inline-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@layer base {
|
@layer base {
|
||||||
:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light] {
|
:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light] {
|
||||||
|
|||||||
@@ -31,6 +31,13 @@ extension SiteRoute.Api.ProjectRoute {
|
|||||||
case .delete(let id):
|
case .delete(let id):
|
||||||
try await database.projects.delete(id)
|
try await database.projects.delete(id)
|
||||||
return nil
|
return nil
|
||||||
|
case .detail(let id, let route):
|
||||||
|
switch route {
|
||||||
|
case .completedSteps:
|
||||||
|
// FIX:
|
||||||
|
fatalError()
|
||||||
|
|
||||||
|
}
|
||||||
case .get(let id):
|
case .get(let id):
|
||||||
guard let project = try await database.projects.get(id) else {
|
guard let project = try await database.projects.get(id) else {
|
||||||
logger.error("Project not found for id: \(id)")
|
logger.error("Project not found for id: \(id)")
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ extension SiteRoute.View {
|
|||||||
switch self {
|
switch self {
|
||||||
case .project:
|
case .project:
|
||||||
return viewRouteMiddleware
|
return viewRouteMiddleware
|
||||||
case .login, .signup:
|
case .login, .signup, .test:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ extension DatabaseClient {
|
|||||||
public var create: @Sendable (User.ID, Project.Create) async throws -> Project
|
public var create: @Sendable (User.ID, Project.Create) async throws -> Project
|
||||||
public var delete: @Sendable (Project.ID) async throws -> Void
|
public var delete: @Sendable (Project.ID) async throws -> Void
|
||||||
public var get: @Sendable (Project.ID) async throws -> Project?
|
public var get: @Sendable (Project.ID) async throws -> Project?
|
||||||
|
public var getCompletedSteps: @Sendable (Project.ID) async throws -> Project.CompletedSteps
|
||||||
public var getSensibleHeatRatio: @Sendable (Project.ID) async throws -> Double?
|
public var getSensibleHeatRatio: @Sendable (Project.ID) async throws -> Double?
|
||||||
public var fetch: @Sendable (User.ID, PageRequest) async throws -> Page<Project>
|
public var fetch: @Sendable (User.ID, PageRequest) async throws -> Page<Project>
|
||||||
public var update: @Sendable (Project.Update) async throws -> Project
|
public var update: @Sendable (Project.Update) async throws -> Project
|
||||||
@@ -35,6 +36,41 @@ extension DatabaseClient.Projects: TestDependencyKey {
|
|||||||
get: { id in
|
get: { id in
|
||||||
try await ProjectModel.find(id, on: database).map { try $0.toDTO() }
|
try await ProjectModel.find(id, on: database).map { try $0.toDTO() }
|
||||||
},
|
},
|
||||||
|
getCompletedSteps: { id in
|
||||||
|
let roomsCount = try await RoomModel.query(on: database)
|
||||||
|
.with(\.$project)
|
||||||
|
.filter(\.$project.$id == id)
|
||||||
|
.count()
|
||||||
|
|
||||||
|
let equivalentLengths = try await EffectiveLengthModel.query(on: database)
|
||||||
|
.with(\.$project)
|
||||||
|
.filter(\.$project.$id == id)
|
||||||
|
.all()
|
||||||
|
|
||||||
|
var equivalentLengthsCompleted = false
|
||||||
|
|
||||||
|
if equivalentLengths.filter({ $0.type == "supply" }).first != nil,
|
||||||
|
equivalentLengths.filter({ $0.type == "return" }).first != nil
|
||||||
|
{
|
||||||
|
equivalentLengthsCompleted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
let componentLosses = try await ComponentLossModel.query(on: database)
|
||||||
|
.with(\.$project)
|
||||||
|
.filter(\.$project.$id == id)
|
||||||
|
.count()
|
||||||
|
|
||||||
|
let equipmentInfo = try await EquipmentModel.query(on: database)
|
||||||
|
.with(\.$project)
|
||||||
|
.filter(\.$project.$id == id)
|
||||||
|
.first()
|
||||||
|
|
||||||
|
return .init(
|
||||||
|
rooms: roomsCount > 0,
|
||||||
|
equivalentLength: equivalentLengthsCompleted,
|
||||||
|
frictionRate: equipmentInfo != nil && componentLosses > 0
|
||||||
|
)
|
||||||
|
},
|
||||||
getSensibleHeatRatio: { id in
|
getSensibleHeatRatio: { id in
|
||||||
guard let model = try await ProjectModel.find(id, on: database) else {
|
guard let model = try await ProjectModel.find(id, on: database) else {
|
||||||
throw NotFoundError()
|
throw NotFoundError()
|
||||||
|
|||||||
@@ -64,6 +64,19 @@ extension Project {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct CompletedSteps: Codable, Equatable, Sendable {
|
||||||
|
|
||||||
|
public let rooms: Bool
|
||||||
|
public let equivalentLength: Bool
|
||||||
|
public let frictionRate: Bool
|
||||||
|
|
||||||
|
public init(rooms: Bool, equivalentLength: Bool, frictionRate: Bool) {
|
||||||
|
self.rooms = rooms
|
||||||
|
self.equivalentLength = equivalentLength
|
||||||
|
self.frictionRate = frictionRate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct Update: Codable, Equatable, Sendable {
|
public struct Update: Codable, Equatable, Sendable {
|
||||||
|
|
||||||
public let id: Project.ID
|
public let id: Project.ID
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ extension SiteRoute.Api {
|
|||||||
public enum ProjectRoute: Sendable, Equatable {
|
public enum ProjectRoute: Sendable, Equatable {
|
||||||
case create(Project.Create)
|
case create(Project.Create)
|
||||||
case delete(id: Project.ID)
|
case delete(id: Project.ID)
|
||||||
|
case detail(id: Project.ID, route: DetailRoute)
|
||||||
case get(id: Project.ID)
|
case get(id: Project.ID)
|
||||||
case index
|
case index
|
||||||
|
|
||||||
@@ -74,6 +75,31 @@ extension SiteRoute.Api {
|
|||||||
Path { rootPath }
|
Path { rootPath }
|
||||||
Method.get
|
Method.get
|
||||||
}
|
}
|
||||||
|
Route(.case(Self.detail(id:route:))) {
|
||||||
|
Path {
|
||||||
|
rootPath
|
||||||
|
Project.ID.parser()
|
||||||
|
}
|
||||||
|
DetailRoute.router
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SiteRoute.Api.ProjectRoute {
|
||||||
|
public enum DetailRoute: Equatable, Sendable {
|
||||||
|
case completedSteps
|
||||||
|
|
||||||
|
static let rootPath = "details"
|
||||||
|
|
||||||
|
static let router = OneOf {
|
||||||
|
Route(.case(Self.completedSteps)) {
|
||||||
|
Path {
|
||||||
|
rootPath
|
||||||
|
"completed"
|
||||||
|
}
|
||||||
|
Method.get
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,14 @@ extension SiteRoute {
|
|||||||
case login(LoginRoute)
|
case login(LoginRoute)
|
||||||
case signup(SignupRoute)
|
case signup(SignupRoute)
|
||||||
case project(ProjectRoute)
|
case project(ProjectRoute)
|
||||||
|
//FIX: Remove.
|
||||||
|
case test
|
||||||
|
|
||||||
public static let router = OneOf {
|
public static let router = OneOf {
|
||||||
|
Route(.case(Self.test)) {
|
||||||
|
Path { "test" }
|
||||||
|
Method.get
|
||||||
|
}
|
||||||
Route(.case(Self.login)) {
|
Route(.case(Self.login)) {
|
||||||
SiteRoute.View.LoginRoute.router
|
SiteRoute.View.LoginRoute.router
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import Elementary
|
import Elementary
|
||||||
|
|
||||||
|
// TODO: Remove, using svg's.
|
||||||
public struct Icon: HTML, Sendable {
|
public struct Icon: HTML, Sendable {
|
||||||
|
|
||||||
let icon: String
|
let icon: String
|
||||||
|
|||||||
@@ -15,17 +15,33 @@ public struct SVG: HTML, Sendable {
|
|||||||
|
|
||||||
extension SVG {
|
extension SVG {
|
||||||
public enum Key: Sendable {
|
public enum Key: Sendable {
|
||||||
|
case badgeCheck
|
||||||
|
case ban
|
||||||
case chevronRight
|
case chevronRight
|
||||||
case circlePlus
|
case circlePlus
|
||||||
case close
|
case close
|
||||||
|
case doorClosed
|
||||||
case email
|
case email
|
||||||
case key
|
case key
|
||||||
|
case mapPin
|
||||||
|
case rulerDimensionLine
|
||||||
|
case sidebarToggle
|
||||||
|
case squareFunction
|
||||||
case squarePen
|
case squarePen
|
||||||
case trash
|
case trash
|
||||||
case user
|
case user
|
||||||
|
case wind
|
||||||
|
|
||||||
var svg: String {
|
var svg: String {
|
||||||
switch self {
|
switch self {
|
||||||
|
case .badgeCheck:
|
||||||
|
return """
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-check-icon lucide-badge-check"><path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"/><path d="m9 12 2 2 4-4"/></svg>
|
||||||
|
"""
|
||||||
|
case .ban:
|
||||||
|
return """
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-ban-icon lucide-ban"><path d="M4.929 4.929 19.07 19.071"/><circle cx="12" cy="12" r="10"/></svg>
|
||||||
|
"""
|
||||||
case .chevronRight:
|
case .chevronRight:
|
||||||
return """
|
return """
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-right-icon lucide-chevron-right"><path d="m9 18 6-6-6-6"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-right-icon lucide-chevron-right"><path d="m9 18 6-6-6-6"/></svg>
|
||||||
@@ -38,6 +54,10 @@ extension SVG {
|
|||||||
return """
|
return """
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
||||||
"""
|
"""
|
||||||
|
case .doorClosed:
|
||||||
|
return """
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-door-closed-icon lucide-door-closed"><path d="M10 12h.01"/><path d="M18 20V6a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v14"/><path d="M2 20h20"/></svg>
|
||||||
|
"""
|
||||||
case .email:
|
case .email:
|
||||||
return """
|
return """
|
||||||
<svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
<svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
@@ -70,6 +90,22 @@ extension SVG {
|
|||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
"""
|
"""
|
||||||
|
case .mapPin:
|
||||||
|
return """
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin-icon lucide-map-pin"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"/><circle cx="12" cy="10" r="3"/></svg>
|
||||||
|
"""
|
||||||
|
case .rulerDimensionLine:
|
||||||
|
return """
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-ruler-dimension-line-icon lucide-ruler-dimension-line"><path d="M10 15v-3"/><path d="M14 15v-3"/><path d="M18 15v-3"/><path d="M2 8V4"/><path d="M22 6H2"/><path d="M22 8V4"/><path d="M6 15v-3"/><rect x="2" y="12" width="20" height="8" rx="2"/></svg>
|
||||||
|
"""
|
||||||
|
case .sidebarToggle:
|
||||||
|
return """
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round" stroke-width="2" fill="none" stroke="currentColor" class="my-1.5 inline-block"><path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"></path><path d="M9 4v16"></path><path d="M14 10l2 2l-2 2"></path></svg>
|
||||||
|
"""
|
||||||
|
case .squareFunction:
|
||||||
|
return """
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-function-icon lucide-square-function"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><path d="M9 17c2 0 2.8-1 2.8-2.8V10c0-2 1-3.3 3.2-3"/><path d="M9 11.2h5.7"/></svg>
|
||||||
|
"""
|
||||||
case .squarePen:
|
case .squarePen:
|
||||||
return """
|
return """
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-pen-icon lucide-square-pen"><path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-pen-icon lucide-square-pen"><path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z"/></svg>
|
||||||
@@ -94,6 +130,10 @@ extension SVG {
|
|||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
"""
|
"""
|
||||||
|
case .wind:
|
||||||
|
return """
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-wind-icon lucide-wind"><path d="M12.8 19.6A2 2 0 1 0 14 16H2"/><path d="M17.5 8a2.5 2.5 0 1 1 2 4H2"/><path d="M9.8 4.4A2 2 0 1 1 11 8H2"/></svg>
|
||||||
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ extension ViewController.Request {
|
|||||||
@Dependency(\.database) var database
|
@Dependency(\.database) var database
|
||||||
|
|
||||||
switch route {
|
switch route {
|
||||||
|
case .test:
|
||||||
|
return view {
|
||||||
|
TestPage()
|
||||||
|
}
|
||||||
case .login(let route):
|
case .login(let route):
|
||||||
switch route {
|
switch route {
|
||||||
case .index(let next):
|
case .index(let next):
|
||||||
|
|||||||
@@ -22,10 +22,18 @@ struct ProjectView: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some HTML {
|
var body: some HTML {
|
||||||
div {
|
div(.class("h-screen w-full")) {
|
||||||
div(.class("flex flex-row")) {
|
|
||||||
Sidebar(active: activeTab, projectID: projectID)
|
div(.class("drawer lg:drawer-open")) {
|
||||||
main(.class("flex flex-col h-screen w-full px-6 py-10")) {
|
input(.id("my-drawer-1"), .type(.checkbox), .class("drawer-toggle"))
|
||||||
|
|
||||||
|
div(.class("drawer-content p-4")) {
|
||||||
|
label(
|
||||||
|
.for("my-drawer-1"),
|
||||||
|
.class("btn btn-square btn-ghost drawer-button size-7")
|
||||||
|
) {
|
||||||
|
SVG(.sidebarToggle)
|
||||||
|
}
|
||||||
switch self.activeTab {
|
switch self.activeTab {
|
||||||
case .project:
|
case .project:
|
||||||
if let project = try await database.projects.get(projectID) {
|
if let project = try await database.projects.get(projectID) {
|
||||||
@@ -56,6 +64,12 @@ struct ProjectView: HTML, Sendable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try await Sidebar(
|
||||||
|
active: activeTab,
|
||||||
|
projectID: projectID,
|
||||||
|
completedSteps: database.projects.getCompletedSteps(projectID)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,99 +80,164 @@ struct Sidebar: HTML {
|
|||||||
|
|
||||||
let active: SiteRoute.View.ProjectRoute.DetailRoute.Tab
|
let active: SiteRoute.View.ProjectRoute.DetailRoute.Tab
|
||||||
let projectID: Project.ID
|
let projectID: Project.ID
|
||||||
|
let completedSteps: Project.CompletedSteps
|
||||||
|
|
||||||
var body: some HTML {
|
var body: some HTML {
|
||||||
aside(
|
|
||||||
.class(
|
|
||||||
"""
|
|
||||||
h-screen sticky top-0 max-w-[280px] flex-none
|
|
||||||
border-r-2 border-gray-200
|
|
||||||
shadow-lg
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
|
|
||||||
div(.class("flex")) {
|
div(.class("drawer-side is-drawer-close:overflow-visible")) {
|
||||||
// TODO: Move somewhere outside of the sidebar.
|
label(
|
||||||
button(
|
.for("my-drawer-1"), .init(name: "aria-label", value: "close sidebar"),
|
||||||
.class("btn btn-secondary btn-block"),
|
.class("drawer-overlay")
|
||||||
.hx.get(route: .project(.index)),
|
) {}
|
||||||
.hx.target("body"),
|
|
||||||
.hx.pushURL(true),
|
div(
|
||||||
.hx.swap(.outerHTML),
|
.class(
|
||||||
) {
|
"""
|
||||||
"< All Projects"
|
flex min-h-full flex-col items-start bg-base-200
|
||||||
|
is-drawer-close:min-w-[80px] is-drawer-open:min-w-[340px]
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
|
||||||
|
ul(.class("w-full")) {
|
||||||
|
|
||||||
|
li(.class("w-full")) {
|
||||||
|
div(
|
||||||
|
.class("w-full is-drawer-close:tooltip is-drawer-close:tooltip-right"),
|
||||||
|
.data("tip", value: "All Projects")
|
||||||
|
) {
|
||||||
|
a(
|
||||||
|
.class(
|
||||||
|
"""
|
||||||
|
flex btn btn-secondary btn-square btn-block
|
||||||
|
is-drawer-close:items-center
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
.hx.get(route: .project(.index)),
|
||||||
|
.hx.target("body"),
|
||||||
|
.hx.pushURL(true),
|
||||||
|
.hx.swap(.outerHTML),
|
||||||
|
) {
|
||||||
|
div(.class("flex is-drawer-open:space-x-4")) {
|
||||||
|
span { "<" }
|
||||||
|
span(.class("is-drawer-close:hidden")) { "All Projects" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIX: Move to user profile / settings page.
|
||||||
|
li(.class("w-full is-drawer-close:hidden")) {
|
||||||
|
div(.class("flex justify-between p-4")) {
|
||||||
|
Label("Theme")
|
||||||
|
input(.type(.checkbox), .class("toggle theme-controller"), .value("light"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
li(.class("w-full")) {
|
||||||
|
row(
|
||||||
|
title: "Project",
|
||||||
|
icon: .mapPin,
|
||||||
|
route: .project(.detail(projectID, .index(tab: .project))),
|
||||||
|
isComplete: true
|
||||||
|
)
|
||||||
|
.attributes(.class("btn-active"), when: active == .project)
|
||||||
|
}
|
||||||
|
|
||||||
|
li(.class("w-full")) {
|
||||||
|
row(
|
||||||
|
title: "Rooms",
|
||||||
|
icon: .doorClosed,
|
||||||
|
route: .project(.detail(projectID, .rooms(.index))),
|
||||||
|
isComplete: completedSteps.rooms
|
||||||
|
)
|
||||||
|
.attributes(.class("btn-active"), when: active == .rooms)
|
||||||
|
}
|
||||||
|
|
||||||
|
li(.class("w-full")) {
|
||||||
|
row(
|
||||||
|
title: "Equivalent Lengths",
|
||||||
|
icon: .rulerDimensionLine,
|
||||||
|
route: .project(.detail(projectID, .equivalentLength(.index))),
|
||||||
|
isComplete: completedSteps.equivalentLength
|
||||||
|
)
|
||||||
|
.attributes(.class("btn-active"), when: active == .equivalentLength)
|
||||||
|
|
||||||
|
}
|
||||||
|
li(.class("w-full")) {
|
||||||
|
row(
|
||||||
|
title: "Friction Rate",
|
||||||
|
icon: .squareFunction,
|
||||||
|
route: .project(.detail(projectID, .frictionRate(.index))),
|
||||||
|
isComplete: completedSteps.frictionRate
|
||||||
|
)
|
||||||
|
.attributes(.class("btn-active"), when: active == .frictionRate)
|
||||||
|
|
||||||
|
}
|
||||||
|
li(.class("w-full")) {
|
||||||
|
row(
|
||||||
|
title: "Duct Sizes", icon: .wind, href: "#", isComplete: false, hideIsComplete: true
|
||||||
|
)
|
||||||
|
.attributes(.class("btn-active"), when: active == .ductSizing)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
|
||||||
Label("Theme")
|
|
||||||
input(.type(.checkbox), .class("toggle theme-controller"), .value("light"))
|
|
||||||
}
|
|
||||||
.attributes(.class("p-4"))
|
|
||||||
|
|
||||||
row(
|
|
||||||
title: "Project",
|
|
||||||
icon: .mapPin,
|
|
||||||
route: .project(.detail(projectID, .index(tab: .project)))
|
|
||||||
)
|
|
||||||
.attributes(.data("active", value: active == .project ? "true" : "false"))
|
|
||||||
|
|
||||||
row(
|
|
||||||
title: "Rooms",
|
|
||||||
icon: .doorClosed,
|
|
||||||
route: .project(.detail(projectID, .rooms(.index)))
|
|
||||||
)
|
|
||||||
.attributes(.data("active", value: active == .rooms ? "true" : "false"))
|
|
||||||
|
|
||||||
row(
|
|
||||||
title: "Equivalent Lengths",
|
|
||||||
icon: .rulerDimensionLine,
|
|
||||||
route: .project(.detail(projectID, .equivalentLength(.index)))
|
|
||||||
)
|
|
||||||
.attributes(.data("active", value: active == .equivalentLength ? "true" : "false"))
|
|
||||||
|
|
||||||
row(
|
|
||||||
title: "Friction Rate",
|
|
||||||
icon: .squareFunction,
|
|
||||||
route: .project(.detail(projectID, .frictionRate(.index)))
|
|
||||||
)
|
|
||||||
.attributes(.data("active", value: active == .frictionRate ? "true" : "false"))
|
|
||||||
|
|
||||||
row(title: "Duct Sizes", icon: .wind, href: "#")
|
|
||||||
.attributes(.data("active", value: active == .ductSizing ? "true" : "false"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use SiteRoute.View routes as href.
|
// TODO: Use SiteRoute.View routes as href.
|
||||||
private func row(
|
private func row(
|
||||||
title: String,
|
title: String,
|
||||||
icon: Icon.Key,
|
icon: SVG.Key,
|
||||||
href: String
|
href: String,
|
||||||
) -> some HTML<HTMLTag.a> {
|
isComplete: Bool,
|
||||||
a(
|
hideIsComplete: Bool = false
|
||||||
|
) -> some HTML<HTMLTag.div> {
|
||||||
|
div(
|
||||||
.class(
|
.class(
|
||||||
"""
|
"w-full is-drawer-close:tooltip is-drawer-close:tooltip-right"
|
||||||
flex w-full items-center gap-4
|
|
||||||
hover:bg-gray-300 hover:text-gray-800
|
|
||||||
data-[active=true]:bg-gray-300 data-[active=true]:text-gray-800
|
|
||||||
px-4 py-2
|
|
||||||
"""
|
|
||||||
),
|
),
|
||||||
.href(href)
|
.data("tip", value: title)
|
||||||
) {
|
) {
|
||||||
Icon(icon)
|
a(
|
||||||
span(.class("text-xl")) {
|
.class(
|
||||||
title
|
"flex btn btn-soft btn-square btn-block is-drawer-open:justify-between is-drawer-close:items-center"
|
||||||
|
),
|
||||||
|
.href(href)
|
||||||
|
) {
|
||||||
|
div(.class("flex is-drawer-open:space-x-4")) {
|
||||||
|
SVG(icon)
|
||||||
|
span(.class("text-xl is-drawer-close:hidden")) {
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hideIsComplete {
|
||||||
|
div(.class("is-drawer-close:hidden")) {
|
||||||
|
if isComplete {
|
||||||
|
SVG(.badgeCheck)
|
||||||
|
} else {
|
||||||
|
SVG(.ban)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.attributes(.class("text-green-400"), when: isComplete)
|
||||||
|
.attributes(.class("text-error"), when: !isComplete)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
.attributes(.class("is-drawer-close:text-green-400"), when: isComplete)
|
||||||
|
.attributes(.class("is-drawer-close:text-error"), when: !isComplete && !hideIsComplete)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func row(
|
private func row(
|
||||||
title: String,
|
title: String,
|
||||||
icon: Icon.Key,
|
icon: SVG.Key,
|
||||||
route: SiteRoute.View
|
route: SiteRoute.View,
|
||||||
) -> some HTML<HTMLTag.a> {
|
isComplete: Bool,
|
||||||
row(title: title, icon: icon, href: SiteRoute.View.router.path(for: route))
|
hideIsComplete: Bool = false
|
||||||
|
) -> some HTML<HTMLTag.div> {
|
||||||
|
row(
|
||||||
|
title: title, icon: icon, href: SiteRoute.View.router.path(for: route),
|
||||||
|
isComplete: isComplete, hideIsComplete: hideIsComplete
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
51
Sources/ViewController/Views/TestPage.swift
Normal file
51
Sources/ViewController/Views/TestPage.swift
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import Elementary
|
||||||
|
|
||||||
|
struct TestPage: HTML, Sendable {
|
||||||
|
var body: some HTML {
|
||||||
|
HTMLRaw(
|
||||||
|
"""
|
||||||
|
<div class="drawer lg:drawer-open">
|
||||||
|
<input id="my-drawer-4" type="checkbox" class="drawer-toggle" checked/>
|
||||||
|
<div class="drawer-content">
|
||||||
|
<!-- Navbar -->
|
||||||
|
<nav class="navbar w-full bg-base-300">
|
||||||
|
<label for="my-drawer-4" aria-label="open sidebar" class="btn btn-square btn-ghost">
|
||||||
|
<!-- Sidebar toggle icon -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round" stroke-width="2" fill="none" stroke="currentColor" class="my-1.5 inline-block size-4"><path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"></path><path d="M9 4v16"></path><path d="M14 10l2 2l-2 2"></path></svg>
|
||||||
|
</label>
|
||||||
|
<div class="px-4">Navbar Title</div>
|
||||||
|
</nav>
|
||||||
|
<!-- Page content here -->
|
||||||
|
<div class="p-4">Page Content</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-side is-drawer-close:overflow-visible">
|
||||||
|
<label for="my-drawer-4" aria-label="close sidebar" class="drawer-overlay"></label>
|
||||||
|
<div class="flex min-h-full flex-col items-start bg-base-200 is-drawer-close:w-14 is-drawer-open:w-64">
|
||||||
|
<!-- Sidebar content here -->
|
||||||
|
<ul class="menu w-full grow">
|
||||||
|
<!-- List item -->
|
||||||
|
<li>
|
||||||
|
<button class="is-drawer-close:tooltip is-drawer-close:tooltip-right" data-tip="Homepage">
|
||||||
|
<!-- Home icon -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round" stroke-width="2" fill="none" stroke="currentColor" class="my-1.5 inline-block size-4"><path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"></path><path d="M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path></svg>
|
||||||
|
<span class="is-drawer-close:hidden">Homepage</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<!-- List item -->
|
||||||
|
<li>
|
||||||
|
<button class="is-drawer-close:tooltip is-drawer-close:tooltip-right" data-tip="Settings">
|
||||||
|
<!-- Settings icon -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round" stroke-width="2" fill="none" stroke="currentColor" class="my-1.5 inline-block size-4"><path d="M20 7h-9"></path><path d="M14 17H5"></path><circle cx="17" cy="17" r="3"></circle><circle cx="7" cy="7" r="3"></circle></svg>
|
||||||
|
<span class="is-drawer-close:hidden">Settings</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user