calendar progress
authorPhiTux <redacted>
Fri, 27 Dec 2024 12:24:11 +0000 (13:24 +0100)
committerPhiTux <redacted>
Fri, 27 Dec 2024 12:24:11 +0000 (13:24 +0100)
frontend/src/routes/+layout.svelte
frontend/src/routes/+page.svelte
frontend/src/routes/Datepicker.svelte [new file with mode: 0644]
frontend/src/routes/Sidenav.svelte [new file with mode: 0644]

index 3adc9d110cd2bb3093b0a1c1f423066e9fa77d17..bd0a3a279744c4dbaf318b118daa5eee0cb3e5d1 100644 (file)
 <main class="d-flex flex-column">
        <nav class="navbar navbar-expand-lg bg-body-tertiary">
                <div class="container-fluid">
+                       <button
+                               class="btn d-md-none"
+                               type="button"
+                               data-bs-toggle="offcanvas"
+                               data-bs-target="#sidenav"
+                               aria-controls="sidenav">menü</button
+                       >
                        <a class="nav-item" href="/">Navbar</a>
                        <a class="nav-item" href="/login">Navbar</a>
                        <div class="dropdown">
@@ -39,7 +46,7 @@
                                        Dropdown button
                                </button>
                                <ul class="dropdown-menu dropdown-menu-end">
-                                       <li><button class="dropdown-item">Settings</button></li>
+                                       <li><a class="dropdown-item" href="/settings">Settings</a></li>
                                        <li><button class="dropdown-item" onclick={logout}>Logout</button></li>
                                </ul>
                        </div>
index b22af18f463ece2861c2893e1a0cb43383c3d265..7387a9ab8186becc1891ae3f458bde1ab1f472e3 100644 (file)
@@ -3,6 +3,7 @@
        //import * as bootstrap from 'bootstrap';
        import { Tooltip } from 'bootstrap';
        import { goto } from '$app/navigation';
+       import Sidenav from './Sidenav.svelte';
 
        //on mount
        import { onMount } from 'svelte';
        });
 </script>
 
-<h1>Welcome to SvelteKit</h1>
-<p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p>
-<button
-       type="button"
-       class="btn btn-primary"
-       data-bs-toggle="tooltip"
-       data-bs-title="Default tooltip">Primary</button
->
+<!-- shown on small Screen, when triggered -->
+<div class="offcanvas-md d-md-none offcanvas-start p-3" id="sidenav" tabindex="-1">
+       <div class="offcanvas-header">
+               <button
+                       type="button"
+                       class="btn-close"
+                       data-bs-dismiss="offcanvas"
+                       data-bs-target="#sidenav"
+                       aria-label="Close"
+               ></button>
+       </div>
+       <Sidenav sidenav="true" />
+</div>
+
+<div class="d-flex flex-row justify-content-between">
+       <!-- shown on large Screen -->
+       <div class="d-md-block d-none sidenav p-3">
+               <Sidenav />
+       </div>
+
+       <!-- Center -->
+       <div class="d-flex flex-column mt-4 mx-4 flex-fill">
+               <!-- Input-Area -->
+               <div class="d-flex flex-column">
+                       <div class="d-flex flex-row">
+                               <div class="flex-fill">Datum</div>
+                               <div class="flex-fill">written at</div>
+                               <div>history</div>
+                               <div>delete</div>
+                       </div>
+                       <textarea class="form-control" rows="10"></textarea>
+               </div>
+       </div>
+
+       <div id="right">Right</div>
+</div>
+
+<style>
+       textarea {
+               resize: vertical;
+               width: 100%;
+       }
+
+       #right {
+               width: 300px;
+       }
+</style>
diff --git a/frontend/src/routes/Datepicker.svelte b/frontend/src/routes/Datepicker.svelte
new file mode 100644 (file)
index 0000000..10d1d65
--- /dev/null
@@ -0,0 +1,279 @@
+<script>
+       import { onMount } from 'svelte';
+       import { fly } from 'svelte/transition';
+
+       let { currentlySelectedDate = new Date(), dateSelected } = $props();
+       let shownDate = $state(currentlySelectedDate);
+       let days = $state([]);
+       let markedDays = {
+               '2024-12-25': { type: 'background', color: '#28a745' }, // green instead of red
+               '2024-12-31': { type: 'dot', color: '#28a745' } // green instead of blue
+       };
+       let currentMonth = $state(shownDate.toLocaleString('default', { month: 'long' }));
+       let currentYear = $state(shownDate.getFullYear());
+
+       let animationDirection = $state(1); // Für die Animationsrichtung
+
+       const updateCalendar = (date) => {
+               currentMonth = date.toLocaleString('default', { month: 'long' });
+               currentYear = date.getFullYear();
+
+               const month = date.getMonth();
+               const year = date.getFullYear();
+               const firstDay = new Date(year, month, 1);
+               const lastDay = new Date(year, month + 1, 0);
+
+               let tempDays = [];
+               // monday is first day
+               let firstDayIndex = firstDay.getDay() - 1;
+               if (firstDayIndex === -1) firstDayIndex = 6; // sunday gets 6
+
+               for (let i = 0; i < firstDayIndex; i++) {
+                       tempDays.push(null); // Fill empty slots before the first day
+               }
+
+               for (let i = 1; i <= lastDay.getDate(); i++) {
+                       const dayKey = `${year}-${(month + 1).toString().padStart(2, '0')}-${i
+                               .toString()
+                               .padStart(2, '0')}`;
+                       tempDays.push({ date: new Date(year, month, i), mark: markedDays[dayKey] });
+               }
+
+               return tempDays;
+       };
+
+       const changeMonth = (increment) => {
+               animationDirection = increment;
+               shownDate.setMonth(shownDate.getMonth() + increment);
+               days = updateCalendar(shownDate);
+       };
+
+       const onDateClick = (date) => {
+               currentlySelectedDate = date;
+               dateSelected(date);
+       };
+
+       onMount(() => {
+               days = updateCalendar(shownDate);
+       });
+
+       let months = Array.from({ length: 12 }, (_, i) =>
+               new Date(2000, i).toLocaleString('default', { month: 'long' })
+       );
+
+       let years = Array.from({ length: 201 }, (_, i) => 1900 + i);
+
+       const onMonthSelect = (event) => {
+               shownDate.setMonth(months.indexOf(event.target.value));
+               days = updateCalendar(shownDate);
+       };
+
+       const onYearSelect = (event) => {
+               shownDate.setFullYear(parseInt(event.target.value));
+               days = updateCalendar(shownDate);
+       };
+
+       // weekdays
+       const weekDays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
+</script>
+
+<div class="datepicker">
+       <div class="datepicker-header">
+               <button type="button" class="btn btnLeftRight" onclick={() => changeMonth(-1)}>&lt;</button>
+               <div class="date-selectors">
+                       <select value={currentMonth} onchange={onMonthSelect}>
+                               {#each months as month}
+                                       <option value={month}>{month}</option>
+                               {/each}
+                       </select>
+                       <select value={currentYear} onchange={onYearSelect}>
+                               {#each years as year}
+                                       <option value={year}>{year}</option>
+                               {/each}
+                       </select>
+               </div>
+               <button type="button" class="btn btnLeftRight" onclick={() => changeMonth(1)}>&gt;</button>
+       </div>
+       <div class="calendar-container">
+               {#key days}
+                       <div
+                               class="datepicker-grid"
+                               in:fly={{ x: animationDirection > 0 ? 100 : -100, duration: 200 }}
+                               out:fly={{ x: animationDirection > 0 ? -100 : 100, duration: 200 }}
+                       >
+                               {#each weekDays as day}
+                                       <div class="day-header">{day}</div>
+                               {/each}
+                               {#each days as day (day ? day.date : Math.random())}
+                                       {#if day}
+                                               <!-- svelte-ignore a11y_click_events_have_key_events -->
+                                               <!-- svelte-ignore a11y_no_static_element_interactions -->
+                                               <div
+                                                       in:fly={{ y: 100, duration: 200 }}
+                                                       out:fly={{ y: -100, duration: 200 }}
+                                                       class="day
+                                                               {day.mark?.type === 'background' ? 'mark-background' : ''} 
+                                                               {day.mark?.type === 'dot' ? 'mark-dot' : ''} 
+                                                               {currentlySelectedDate.toDateString() === day.date.toDateString() ? 'selected' : ''}"
+                                                       style="--color: {day.mark?.color || 'transparent'}"
+                                                       onclick={() => onDateClick(day.date)}
+                                               >
+                                                       {day.date.getDate()}
+                                               </div>
+                                       {:else}
+                                               <div class="day empty-slot"></div>
+                                       {/if}
+                               {/each}
+                       </div>
+               {/key}
+       </div>
+</div>
+
+<style>
+       .btnLeftRight {
+               color: white;
+       }
+       .btnLeftRight:hover {
+               background-color: #ffffff31;
+       }
+       .btnLeftRight:active {
+               border-color: rgba(255, 255, 255, 0);
+       }
+       .datepicker {
+               display: inline-block;
+               font-family: Arial, sans-serif;
+               border: 1px solid #ccc;
+               border-radius: 8px;
+               overflow: hidden;
+               width: 300px;
+               box-sizing: border-box;
+       }
+       .datepicker-header {
+               display: flex;
+               justify-content: space-between;
+               background: #007bff;
+               color: white;
+               padding: 8px 16px;
+               font-size: 16px;
+       }
+       .calendar-container {
+               position: relative;
+               overflow: hidden;
+               min-height: 320px;
+       }
+       .datepicker-grid {
+               display: grid;
+               grid-template-columns: repeat(7, 1fr);
+               padding: 8px;
+               gap: 2px;
+               text-align: center;
+               position: absolute;
+               width: 100%;
+       }
+       .datepicker-grid:not(.slide-left):not(.slide-right) {
+               transform: translateX(0);
+               opacity: 1;
+       }
+       .day-header {
+               font-weight: bold;
+               padding: 8px 0;
+               font-size: 0.9em;
+               color: #666;
+       }
+       .day {
+               height: 32px;
+               width: 32px;
+               min-width: 32px;
+               display: flex;
+               align-items: center;
+               justify-content: center;
+               cursor: pointer;
+               position: relative;
+               border-radius: 50%;
+               margin: 2px auto;
+               user-select: none;
+       }
+       .day:hover {
+               background: #f0f0f0;
+       }
+       .day.mark-background {
+               background-color: var(--color);
+               color: white;
+               aspect-ratio: 1;
+       }
+       .day.mark-dot::after {
+               content: '';
+               width: 6px;
+               height: 6px;
+               background-color: var(--color);
+               border-radius: 50%;
+               position: absolute;
+               bottom: 2px;
+               aspect-ratio: 1;
+       }
+       .empty-slot {
+               visibility: hidden;
+       }
+       .date-selectors {
+               display: flex;
+               gap: 4px;
+               flex: 1;
+               justify-content: center;
+       }
+
+       .date-selectors select {
+               background: transparent;
+               color: white;
+               border: none;
+               font-size: 16px;
+               cursor: pointer;
+               padding: 2px 12px;
+               border-radius: 4px;
+               max-width: 100px;
+               text-align: center;
+               text-align-last: center;
+               -webkit-appearance: none;
+               -moz-appearance: none;
+               appearance: none;
+       }
+
+       .date-selectors select:hover {
+               background: rgba(255, 255, 255, 0.1);
+       }
+
+       .date-selectors select option {
+               background: white;
+               color: black;
+       }
+
+       .date-selectors select:after {
+               content: '▼';
+               position: absolute;
+               right: 5px;
+       }
+
+       .day.selected {
+               background-color: #007bff;
+               color: white;
+       }
+
+       .day.selected:hover {
+               background-color: #0056b3;
+       }
+
+       /* Ensure selected state takes precedence */
+       .day.mark-background:not(.selected) {
+               background-color: var(--color);
+               color: white;
+       }
+
+       .day.mark-dot:not(.selected)::after {
+               content: '';
+               width: 6px;
+               height: 6px;
+               background-color: var(--color);
+               border-radius: 50%;
+               position: absolute;
+               bottom: 2px;
+       }
+</style>
diff --git a/frontend/src/routes/Sidenav.svelte b/frontend/src/routes/Sidenav.svelte
new file mode 100644 (file)
index 0000000..90de620
--- /dev/null
@@ -0,0 +1,16 @@
+<script>
+       import Datepicker from './Datepicker.svelte';
+       let { sidenav } = {};
+       let selectedDate = $state(new Date());
+</script>
+
+{sidenav}
+<!-- {#if sidenav} -->
+
+<!-- {/if} -->
+
+<Datepicker dateSelected={(date) => (selectedDate = date)} />
+<br />
+{selectedDate.toDateString()}
+<br />
+Search
git clone https://git.99rst.org/PROJECT