Improves event display on the calendar, gets closer to a v1 of the calendar design, updates guest layout to match theme, adds additional css components for accordion, calendar, checkbox, indicator, and input.
This commit is contained in:
		
							parent
							
								
									643ac833ba
								
							
						
					
					
						commit
						7efcf5cf55
					
				@ -41,7 +41,8 @@ class CalendarController extends Controller
 | 
			
		||||
                'calendars.id',
 | 
			
		||||
                'ci.displayname',
 | 
			
		||||
                'ci.calendarcolor',
 | 
			
		||||
                'meta.color as meta_color'
 | 
			
		||||
                'meta.color as meta_color',
 | 
			
		||||
                'meta.color_fg as meta_color_fg'
 | 
			
		||||
            )
 | 
			
		||||
            ->join('calendarinstances as ci', 'ci.calendarid', '=', 'calendars.id')
 | 
			
		||||
            ->leftJoin('calendar_meta as meta', 'meta.calendar_id', '=', 'calendars.id')
 | 
			
		||||
@ -63,12 +64,18 @@ class CalendarController extends Controller
 | 
			
		||||
        $payload = [
 | 
			
		||||
            'view' => $view,
 | 
			
		||||
            'range' => $range,
 | 
			
		||||
            'active' => [
 | 
			
		||||
                'year'  => $range['start']->format('Y'),
 | 
			
		||||
                'month' => $range['start']->format("F"),
 | 
			
		||||
                'day' => $range['start']->format("d"),
 | 
			
		||||
            ],
 | 
			
		||||
            'calendars' => $calendars->keyBy('id')->map(function ($cal) {
 | 
			
		||||
                return [
 | 
			
		||||
                    'id' => $cal->id,
 | 
			
		||||
                    'name' => $cal->displayname,
 | 
			
		||||
                    'color' => $cal->meta_color ?? $cal->calendarcolor ?? '#999',
 | 
			
		||||
                    'on' => true,         // default to visible; the UI can toggle this
 | 
			
		||||
                    'color' => $cal->meta_color ?? $cal->calendarcolor ?? '#1a1a1a', // clean this up @todo
 | 
			
		||||
                    'color_fg'  => $cal->meta_color_fg ?? '#ffffff', // clean this up
 | 
			
		||||
                    'on' => true, // default to visible; the UI can toggle this
 | 
			
		||||
                ];
 | 
			
		||||
            }),
 | 
			
		||||
            'events' => $events->map(function ($e) { // just the events map
 | 
			
		||||
@ -83,7 +90,9 @@ class CalendarController extends Controller
 | 
			
		||||
                    'calendar_id' => $e->calendarid,
 | 
			
		||||
                    'title'       => $e->meta->title ?? '(no title)',
 | 
			
		||||
                    'start'       => $start->format('c'),
 | 
			
		||||
                    'start_ui'    => $start->format('g:ia'),
 | 
			
		||||
                    'end'         => optional($end)->format('c'),
 | 
			
		||||
                    'end_ui'      => optional($end)->format('g:ia')
 | 
			
		||||
                ];
 | 
			
		||||
            }),
 | 
			
		||||
            'grid' => $grid,
 | 
			
		||||
@ -129,7 +138,8 @@ class CalendarController extends Controller
 | 
			
		||||
        // update calendar meta
 | 
			
		||||
        $instance->meta()->create([
 | 
			
		||||
            'calendar_id' => $instanceId,
 | 
			
		||||
            'color'       => $data['color'] ?? null,
 | 
			
		||||
            'color'       => $data['color'] ?? '#1a1a1a',
 | 
			
		||||
            'color_fg'    => contrast_text_color($data['color'] ?? '#1a1a1a'),
 | 
			
		||||
            'created_at'  => now(),
 | 
			
		||||
            'updated_at'  => now(),
 | 
			
		||||
        ]);
 | 
			
		||||
@ -212,8 +222,9 @@ class CalendarController extends Controller
 | 
			
		||||
 | 
			
		||||
        // update calendar meta (our table)
 | 
			
		||||
        $calendar->meta()->updateOrCreate([], [
 | 
			
		||||
            'color' => $data['color'] ?? null]
 | 
			
		||||
        );
 | 
			
		||||
            'color'    => $data['color'] ?? '#1a1a1a',
 | 
			
		||||
            'color_fg' => contrast_text_color($data['color'] ?? '#1a1a1a')
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        return redirect()
 | 
			
		||||
            ->route('calendar.show', $calendar)
 | 
			
		||||
@ -313,7 +324,9 @@ class CalendarController extends Controller
 | 
			
		||||
                    'calendar_id' => $ev->calendarid,
 | 
			
		||||
                    'title'       => $ev->meta->title ?? '(no title)',
 | 
			
		||||
                    'start'       => $start->format('c'),
 | 
			
		||||
                    'end'         => $end->format('c'),
 | 
			
		||||
                    'start_ui'    => $start->format('g:ia'),
 | 
			
		||||
                    'end'         => optional($end)->format('c'),
 | 
			
		||||
                    'end_ui'      => optional($end)->format('g:ia')
 | 
			
		||||
                ];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@ class CalendarMeta extends Model
 | 
			
		||||
    protected $fillable = [
 | 
			
		||||
        'calendar_id',
 | 
			
		||||
        'color',
 | 
			
		||||
        'color_fg',
 | 
			
		||||
        'created_at',
 | 
			
		||||
        'edited_at',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										67
									
								
								app/Support/helpers.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								app/Support/helpers.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,67 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
if (! function_exists('format_event_url')) {
 | 
			
		||||
    /**
 | 
			
		||||
     * format an event url with a given calendar ID and event ID
 | 
			
		||||
     */
 | 
			
		||||
    function format_event_url(string $eid, string $cid): string
 | 
			
		||||
    {
 | 
			
		||||
        return 'calendar/'.$cid.'/event/'.$eid;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (! function_exists('contrast_text_color')) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Choose an accessible foreground (#fff or #000) against a HEX background.
 | 
			
		||||
     *
 | 
			
		||||
     * Rule set:
 | 
			
		||||
     *   1. Calculate WCAG contrast ratios for both black and white.
 | 
			
		||||
     *   2. Prefer the colour with the *higher* ratio.
 | 
			
		||||
     *   3. Override: if black wins but ratio < 5.5 AND white > 4, use white.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string $hex        Background colour (3- or 6-digit hex, with or without #)
 | 
			
		||||
     * @param  string $light      Return value for “white”   (default '#ffffff')
 | 
			
		||||
     * @param  string $dark       Return value for “black”   (default '#000000')
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    function contrast_text_color(string $hex, string $light = '#ffffff', string $dark = '#000000'): string
 | 
			
		||||
    {
 | 
			
		||||
        // --- normalise ----------------------------------------------------
 | 
			
		||||
        $hex = ltrim($hex, '#');
 | 
			
		||||
        if (strlen($hex) === 3) {                      // #abc → #aabbcc
 | 
			
		||||
            $hex = preg_replace('/./', '$0$0', $hex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [$r, $g, $b] = [
 | 
			
		||||
            hexdec(substr($hex, 0, 2)) / 255,
 | 
			
		||||
            hexdec(substr($hex, 2, 2)) / 255,
 | 
			
		||||
            hexdec(substr($hex, 4, 2)) / 255,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        // --- convert sRGB → linear RGB -----------------------------------
 | 
			
		||||
        $linear = function (float $c): float {
 | 
			
		||||
            return $c <= 0.04045 ? $c / 12.92 : pow(($c + 0.055) / 1.055, 2.4);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        $R = $linear($r);
 | 
			
		||||
        $G = $linear($g);
 | 
			
		||||
        $B = $linear($b);
 | 
			
		||||
 | 
			
		||||
        // --- relative luminance (ITU-R BT.709) ----------------------------
 | 
			
		||||
        $L_bg = 0.2126 * $R + 0.7152 * $G + 0.0722 * $B;       // 0–1
 | 
			
		||||
 | 
			
		||||
        // --- contrast ratios vs black (L=0) and white (L=1) ---------------
 | 
			
		||||
        $contrast_black = ($L_bg + 0.05) / 0.05;               // bg lighter than black
 | 
			
		||||
        $contrast_white = 1.05 / ($L_bg + 0.05);               // white vs bg
 | 
			
		||||
 | 
			
		||||
        // --- pick the winner ---------------------------------------------
 | 
			
		||||
        $useDark = $contrast_black >= $contrast_white;
 | 
			
		||||
 | 
			
		||||
        // override rule if dark is true but white "looks better"
 | 
			
		||||
        if ($useDark && $contrast_black < 5.5 && $contrast_white > 4) {
 | 
			
		||||
            $useDark = false; // switch to white
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $useDark ? $dark : $light;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -28,7 +28,10 @@
 | 
			
		||||
            "App\\": "app/",
 | 
			
		||||
            "Database\\Factories\\": "database/factories/",
 | 
			
		||||
            "Database\\Seeders\\": "database/seeders/"
 | 
			
		||||
        }
 | 
			
		||||
        },
 | 
			
		||||
        "files": [
 | 
			
		||||
            "app/Support/helpers.php"
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    "autoload-dev": {
 | 
			
		||||
        "psr-4": {
 | 
			
		||||
 | 
			
		||||
@ -9,9 +9,10 @@ return new class extends Migration
 | 
			
		||||
    public function up(): void
 | 
			
		||||
    {
 | 
			
		||||
        Schema::create('calendar_meta', function (Blueprint $table) {
 | 
			
		||||
            $table->unsignedInteger('calendar_id')->primary();  // FK = PK
 | 
			
		||||
            $table->string('title')->nullable();       // UI override
 | 
			
		||||
            $table->string('color', 7)->nullable();    // e.g. #FFAA00
 | 
			
		||||
            $table->unsignedInteger('calendar_id')->primary(); // FK = PK
 | 
			
		||||
            $table->string('title')->nullable(); // ui override
 | 
			
		||||
            $table->string('color', 7)->nullable(); // bg color
 | 
			
		||||
            $table->string('color_fg', 7)->nullable(); // fg color
 | 
			
		||||
            $table->boolean('is_shared')->default(false);
 | 
			
		||||
            $table->json('settings')->nullable();      // arbitrary JSON
 | 
			
		||||
            $table->timestamps();
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,12 @@
 | 
			
		||||
/** kithkin */
 | 
			
		||||
@import './etc/layout.css';
 | 
			
		||||
@import './etc/type.css';
 | 
			
		||||
@import './lib/accordion.css';
 | 
			
		||||
@import './lib/button.css';
 | 
			
		||||
@import './lib/calendar.css';
 | 
			
		||||
@import './lib/checkbox.css';
 | 
			
		||||
@import './lib/indicator.css';
 | 
			
		||||
@import './lib/input.css';
 | 
			
		||||
@import './lib/mini.css';
 | 
			
		||||
 | 
			
		||||
/** plugins */
 | 
			
		||||
 | 
			
		||||
@ -91,14 +91,23 @@ main {
 | 
			
		||||
 | 
			
		||||
    /* main content title and actions */
 | 
			
		||||
    > header {
 | 
			
		||||
        @apply flex flex-row items-center justify-between px-6 2xl:px-8;
 | 
			
		||||
        @apply grid items-center;
 | 
			
		||||
        grid-template-columns: minmax(20rem, 20dvw) repeat(3, 1fr);
 | 
			
		||||
 | 
			
		||||
        h1 {
 | 
			
		||||
            @apply h-12 max-h-12;
 | 
			
		||||
            @apply flex items-center pl-6 2xl:pl-8;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        h2 {
 | 
			
		||||
            @apply col-span-1 flex flex-row gap-1 items-center justify-start relative top-px;
 | 
			
		||||
 | 
			
		||||
            > span {
 | 
			
		||||
                @apply text-gray-700;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        menu {
 | 
			
		||||
            @apply flex flex-row items-center justify-end gap-2 h-12 max-h-12;
 | 
			
		||||
            @apply col-span-2 flex flex-row items-center justify-end gap-2 pr-6 2xl:pr-8;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -125,7 +134,7 @@ main {
 | 
			
		||||
@media (width >= 96rem) { /* 2xl */
 | 
			
		||||
    main {
 | 
			
		||||
        body#app & {
 | 
			
		||||
            grid-template-rows: 6rem auto;
 | 
			
		||||
            grid-template-rows: 5.5rem auto;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -23,8 +23,25 @@
 | 
			
		||||
  --color-cyan-400: oklch(92.6% 0.117 195.31);
 | 
			
		||||
  --color-cyan-500: oklch(90.54% 0.155 194.76); /* 00ffff */
 | 
			
		||||
  --color-cyan-550: oklch(82% 0.2812 194.769);  /* 00e3e3 */
 | 
			
		||||
  --color-cyan-600: oklch(80.43% 0.137 194.76); /* todo below */
 | 
			
		||||
  --color-cyan-700: oklch(70.28% 0.12 194.76);
 | 
			
		||||
  --color-cyan-800: oklch(59.15% 0.101 194.76);
 | 
			
		||||
  --color-cyan-900: oklch(49.05% 0.084 194.76);
 | 
			
		||||
  --color-cyan-950: oklch(43.96% 0.075 194.76);
 | 
			
		||||
  --color-magenta-50: oklch(96.93% 0.027 325.87);
 | 
			
		||||
  --color-magenta-100: oklch(93.76% 0.056 326.06);
 | 
			
		||||
  --color-magenta-200: oklch(87.58% 0.117 326.54);
 | 
			
		||||
  --color-magenta-300: oklch(81.59% 0.181 327.09);
 | 
			
		||||
  --color-magenta-400: oklch(75.66% 0.251 327.72);
 | 
			
		||||
  --color-magenta-500: oklch(70.17% 0.322 328.37);
 | 
			
		||||
  --color-magenta-550: oklch(0.666 0.3061 328.36); /* ee00ee */
 | 
			
		||||
  --color-magenta-600: oklch(62.55% 0.287 328.37);
 | 
			
		||||
  --color-magenta-700: oklch(55.14% 0.253 328.37);
 | 
			
		||||
  --color-magenta-800: oklch(47.69% 0.219 328.37);
 | 
			
		||||
  --color-magenta-900: oklch(40.42% 0.186 328.37);
 | 
			
		||||
  --color-magenta-950: oklch(36.79% 0.169 328.37);
 | 
			
		||||
 | 
			
		||||
  --border-width-1.5: 1.5px;
 | 
			
		||||
  --border-width-md: 1.5px;
 | 
			
		||||
 | 
			
		||||
  --radius-xs: 0.25rem;
 | 
			
		||||
  --radius-sm: 0.375rem;
 | 
			
		||||
@ -36,12 +53,18 @@
 | 
			
		||||
  --radius-4xl: 3rem;
 | 
			
		||||
  --radius-blob: 80% 65% 90% 50% / 90% 80% 75% 75%;
 | 
			
		||||
 | 
			
		||||
  --shadow-drop:  2.5px 2.5px 0 0 var(--color-primary);
 | 
			
		||||
  --shadow-drop: 2.5px 2.5px 0 0 var(--color-primary);
 | 
			
		||||
  --shadow-input: inset 0 0.25rem 0 0 var(--color-gray-100);
 | 
			
		||||
 | 
			
		||||
  --spacing-md: 1.5px;
 | 
			
		||||
  --spacing-2px: 2px;
 | 
			
		||||
 | 
			
		||||
  --text-2xs: 0.625rem;
 | 
			
		||||
  --text-2xs--line-height: 1.2;
 | 
			
		||||
  --text-2xl: 1.75rem;
 | 
			
		||||
  --text-2xl--line-height: 1.333;
 | 
			
		||||
  --text-3xl: 2rem;
 | 
			
		||||
  --text-3xl--line-height: calc(2.25 / 1.875);
 | 
			
		||||
  --text-3xl--line-height: 1.2;
 | 
			
		||||
  --text-4xl: 3rem;
 | 
			
		||||
  --text-4xl--line-height: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,25 @@
 | 
			
		||||
    font-style: normal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* app name */
 | 
			
		||||
h1 {
 | 
			
		||||
   @apply font-serif text-3xl font-extrabold leading-tight;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* page header */
 | 
			
		||||
h2 {
 | 
			
		||||
    @apply font-serif text-2xl font-extrabold leading-tight text-primary;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* links */
 | 
			
		||||
a {
 | 
			
		||||
    &.text {
 | 
			
		||||
        @apply underline decoration-inherit underline-offset-2 text-magenta-600;
 | 
			
		||||
        text-decoration-thickness: 1.5px;
 | 
			
		||||
        transition: color 125ms ease-in-out;
 | 
			
		||||
 | 
			
		||||
        &:hover {
 | 
			
		||||
            @apply text-magenta-700;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										42
									
								
								resources/css/lib/accordion.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								resources/css/lib/accordion.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
			
		||||
details {
 | 
			
		||||
 | 
			
		||||
    summary {
 | 
			
		||||
        @apply relative flex items-center cursor-pointer list-none h-8 font-semibold z-0;
 | 
			
		||||
 | 
			
		||||
        &::-webkit-details-marker {
 | 
			
		||||
            @apply hidden;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &::before {
 | 
			
		||||
            @apply block absolute top-0 left-0 -ml-3 -mt-1 bg-transparent rounded-md;
 | 
			
		||||
            content: '';
 | 
			
		||||
            height: calc(100% + 0.5rem);
 | 
			
		||||
            transition: background-color 100ms ease-in-out;
 | 
			
		||||
            width: calc(100% + 1.5rem);
 | 
			
		||||
            z-index: -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &::after {
 | 
			
		||||
            @apply block w-8 h-8 absolute right-0 top-0 bg-no-repeat bg-center;
 | 
			
		||||
            background-image: url("data:image/svg+xml,%3Csvg 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'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
 | 
			
		||||
            content: '';
 | 
			
		||||
            transition: rotate 100ms ease-in-out;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &:hover {
 | 
			
		||||
            &::before {
 | 
			
		||||
                @apply bg-gray-100;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &[open] {
 | 
			
		||||
        summary::after {
 | 
			
		||||
            @apply rotate-180;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    > .content {
 | 
			
		||||
        @apply mt-2;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,13 +1,13 @@
 | 
			
		||||
button,
 | 
			
		||||
.button {
 | 
			
		||||
    @apply relative inline-flex items-center cursor-pointer gap-2 rounded-md h-11 px-4 text-lg font-medium;
 | 
			
		||||
    transition: background-color 100ms ease-in-out;
 | 
			
		||||
    transition: background-color 125ms ease-in-out;
 | 
			
		||||
    --button-border: var(--color-primary);
 | 
			
		||||
    --button-accent: var(--color-primary-hover);
 | 
			
		||||
 | 
			
		||||
    &.button--primary {
 | 
			
		||||
        @apply bg-cyan-300;
 | 
			
		||||
        border: 1.5px solid var(--button-border);
 | 
			
		||||
        @apply bg-cyan-300 border-md border-solid;
 | 
			
		||||
        border-color: var(--button-border);
 | 
			
		||||
        box-shadow: 2.5px 2.5px 0 0 var(--button-border);
 | 
			
		||||
 | 
			
		||||
        &:hover {
 | 
			
		||||
@ -31,3 +31,32 @@ button,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.button-group {
 | 
			
		||||
    @apply relative flex flex-row items-center p-0 m-0 h-11 max-h-11;
 | 
			
		||||
 | 
			
		||||
    > label {
 | 
			
		||||
        @apply relative flex items-center justify-center h-full pl-3.5 pr-3 cursor-pointer;
 | 
			
		||||
        @apply border-md border-primary font-medium;
 | 
			
		||||
        box-shadow: 1.5px 2.5px 0 0 var(--color-primary);
 | 
			
		||||
 | 
			
		||||
        > input[type="radio"] {
 | 
			
		||||
            @apply hidden absolute top-0 left-0 w-0 h-0 max-w-0 max-h-0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &:first-child {
 | 
			
		||||
            @apply rounded-l-md;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &:last-child {
 | 
			
		||||
            @apply border-r-md rounded-r-md;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &:has(input:checked) {
 | 
			
		||||
            @apply bg-cyan-300 border-t-2;
 | 
			
		||||
            box-shadow: inset 0 0.25rem 0 0 var(--color-cyan-400);
 | 
			
		||||
            left: 1.5px;
 | 
			
		||||
            top: 2.5px;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										64
									
								
								resources/css/lib/calendar.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								resources/css/lib/calendar.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,64 @@
 | 
			
		||||
.calendar {
 | 
			
		||||
    @apply grid col-span-3 pr-6 2xl:pr-8 pb-6 2xl:pb-8 pt-2;
 | 
			
		||||
    grid-template-rows: 2rem 1fr;
 | 
			
		||||
 | 
			
		||||
    hgroup {
 | 
			
		||||
        @apply grid grid-cols-7 w-full gap-1;
 | 
			
		||||
 | 
			
		||||
        > span {
 | 
			
		||||
            @apply uppercase text-right pr-4 font-bold;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ol {
 | 
			
		||||
        @apply grid grid-cols-7 w-full gap-1;
 | 
			
		||||
        contain: paint;
 | 
			
		||||
        grid-auto-rows: 1fr;
 | 
			
		||||
 | 
			
		||||
        li {
 | 
			
		||||
            @apply relative px-1 pt-8 border-t-md border-primary;
 | 
			
		||||
 | 
			
		||||
            &::before {
 | 
			
		||||
                @apply absolute top-0 right-px w-auto h-8 flex items-center justify-end pr-4 text-sm font-medium;
 | 
			
		||||
                content: attr(data-day-number);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            &.day--outside {
 | 
			
		||||
                @apply bg-gray-50 text-gray-700;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            &.day--today {
 | 
			
		||||
                @apply bg-cyan-100;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            &:nth-child(-n+7) {
 | 
			
		||||
                @apply border-t-2;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            &:last-child {
 | 
			
		||||
                @apply rounded-br-lg;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            .event {
 | 
			
		||||
                @apply flex items-center text-xs gap-1 px-1 py-px font-medium truncate rounded-sm bg-transparent;
 | 
			
		||||
                transition: background-color 125ms ease-in-out;
 | 
			
		||||
 | 
			
		||||
                .indicator {
 | 
			
		||||
                    --indicator-bg: var(--event-color);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                .title {
 | 
			
		||||
                    @apply grow;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                time {
 | 
			
		||||
                    @apply text-2xs;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                &:hover {
 | 
			
		||||
                    background-color: color-mix(in srgb, var(--event-color) 25%, #fff 100%);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								resources/css/lib/checkbox.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								resources/css/lib/checkbox.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
input[type="checkbox"] {
 | 
			
		||||
    @apply border-md rounded-sm w-5 h-5 ring-0;
 | 
			
		||||
    transition: border 150ms ease-in-out,
 | 
			
		||||
                outline 150ms ease-in-out,
 | 
			
		||||
                background 150ms ease-in-out,
 | 
			
		||||
                box-shadow 200ms ease-out;
 | 
			
		||||
    color: var(--checkbox-color);
 | 
			
		||||
    border-color: var(--checkbox-color);
 | 
			
		||||
    --checkbox-color: var(--color-primary);
 | 
			
		||||
 | 
			
		||||
    &:focus {
 | 
			
		||||
        outline: 2px solid transparent;
 | 
			
		||||
        outline-offset: 2px;
 | 
			
		||||
        box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--checkbox-color), var(--tw-shadow);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								resources/css/lib/indicator.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								resources/css/lib/indicator.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
i.indicator {
 | 
			
		||||
    @apply inline-flex w-2.5 h-2.5 min-w-2.5 min-h-2.5 rounded-full font-normal;
 | 
			
		||||
    background-color: var(--indicator-bg);
 | 
			
		||||
    --indicator-bg: var(--color-magenta-500); /* default color */
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								resources/css/lib/input.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								resources/css/lib/input.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
input[type="email"],
 | 
			
		||||
input[type="text"],
 | 
			
		||||
input[type="password"],
 | 
			
		||||
input[type="search"] {
 | 
			
		||||
    @apply border-md border-gray-800 bg-white rounded-md shadow-input;
 | 
			
		||||
    @apply focus:border-primary focus:ring-2 focus:ring-offset-2 focus:ring-cyan-600;
 | 
			
		||||
    transition: box-shadow 125ms ease-in-out,
 | 
			
		||||
                border-color 125ms ease-in-out;
 | 
			
		||||
}
 | 
			
		||||
@ -12,10 +12,10 @@
 | 
			
		||||
 | 
			
		||||
    /* days wrapper */
 | 
			
		||||
    figure {
 | 
			
		||||
        @apply border-1.5 border-primary shadow-drop rounded-md;
 | 
			
		||||
        @apply border-md border-primary shadow-drop rounded-md;
 | 
			
		||||
 | 
			
		||||
        /* weekdays */
 | 
			
		||||
        figcaption {
 | 
			
		||||
        hgroup {
 | 
			
		||||
             @apply grid grid-cols-7 p-2 pt-3 pb-0;
 | 
			
		||||
 | 
			
		||||
            span {
 | 
			
		||||
@ -55,7 +55,7 @@
 | 
			
		||||
 | 
			
		||||
            &.day--with-events {
 | 
			
		||||
                &::after {
 | 
			
		||||
                    @apply absolute bottom-0 left-1/2 -translate-x-1/2 h-1 rounded-full w-4 bg-yellow-500;
 | 
			
		||||
                    @apply absolute bottom-0 left-1/2 -translate-x-1/2 h-1 rounded-full w-4 bg-magenta-500;
 | 
			
		||||
                    content: '';
 | 
			
		||||
                }
 | 
			
		||||
                &[data-event-count='1']::after {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								resources/svg/icons/chevron-down.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								resources/svg/icons/chevron-down.svg
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
<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-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 271 B  | 
							
								
								
									
										1
									
								
								resources/svg/icons/chevron-left.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								resources/svg/icons/chevron-left.svg
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
<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-left-icon lucide-chevron-left"><path d="m15 18-6-6 6-6"/></svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 273 B  | 
							
								
								
									
										1
									
								
								resources/svg/icons/chevron-right.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								resources/svg/icons/chevron-right.svg
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
<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>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 274 B  | 
							
								
								
									
										1
									
								
								resources/svg/icons/chevron-up.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								resources/svg/icons/chevron-up.svg
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
<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-up-icon lucide-chevron-up"><path d="m18 15-6-6-6 6"/></svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 269 B  | 
@ -26,15 +26,15 @@
 | 
			
		||||
 | 
			
		||||
        <!-- Remember Me -->
 | 
			
		||||
        <div class="block mt-4">
 | 
			
		||||
            <label for="remember_me" class="inline-flex items-center">
 | 
			
		||||
                <input id="remember_me" type="checkbox" class="rounded-sm border-gray-300 text-indigo-600 shadow-xs focus:ring-indigo-500" name="remember">
 | 
			
		||||
                <span class="ms-2 text-sm text-gray-600">{{ __('Remember me') }}</span>
 | 
			
		||||
            <label for="remember_me" class="inline-flex items-center gap-2">
 | 
			
		||||
                <input id="remember_me" type="checkbox" name="remember">
 | 
			
		||||
                <span>{{ __('Remember me') }}</span>
 | 
			
		||||
            </label>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="flex items-center justify-between mt-4 gap-4">
 | 
			
		||||
            @if (Route::has('password.request'))
 | 
			
		||||
                <a class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" href="{{ route('password.request') }}">
 | 
			
		||||
                <a href="{{ route('password.request') }}" href="text">
 | 
			
		||||
                    {{ __('Forgot your password?') }}
 | 
			
		||||
                </a>
 | 
			
		||||
            @endif
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,18 @@
 | 
			
		||||
        <h1>
 | 
			
		||||
            {{ __('Calendar') }}
 | 
			
		||||
        </h1>
 | 
			
		||||
        <h2>
 | 
			
		||||
            <strong>{{ $active['month'] }}</strong>
 | 
			
		||||
            <span>{{ $active['year'] }}</span>
 | 
			
		||||
        </h2>
 | 
			
		||||
        <menu>
 | 
			
		||||
            <li>
 | 
			
		||||
                <form class="button-group button-group--primary" method="get" action="/">
 | 
			
		||||
                    <x-button.group-button>Day</x-button.group-button>
 | 
			
		||||
                    <x-button.group-button>Week</x-button.group-button>
 | 
			
		||||
                    <x-button.group-button active="true">Month</x-button.group-button>
 | 
			
		||||
                    <x-button.group-button>3-Up</x-button.group-button>
 | 
			
		||||
                </form>
 | 
			
		||||
            <li>
 | 
			
		||||
                <a class="button button--primary" href="{{ route('calendar.create') }}">
 | 
			
		||||
                    <x-icon-plus-circle /> Create
 | 
			
		||||
@ -18,19 +29,23 @@
 | 
			
		||||
    </x-slot>
 | 
			
		||||
    <x-slot name="article">
 | 
			
		||||
        <aside>
 | 
			
		||||
            <div>
 | 
			
		||||
            @foreach ($calendars as $cal)
 | 
			
		||||
                <label class="flex items-center space-x-2">
 | 
			
		||||
                    <input type="checkbox"
 | 
			
		||||
                           wire:model="visibleCalendars"
 | 
			
		||||
                           value="{{ $cal['id'] }}"
 | 
			
		||||
                           checked>
 | 
			
		||||
                    <span class="w-3 h-3 rounded-sm" style="background: {{ $cal['color'] }}"></span>
 | 
			
		||||
                    <span>{{ $cal['name'] }}</span>
 | 
			
		||||
                </label>
 | 
			
		||||
            @endforeach
 | 
			
		||||
            <div class="flex flex-col gap-4">
 | 
			
		||||
                <details open>
 | 
			
		||||
                    <summary>{{ __('My Calendars') }}</summary>
 | 
			
		||||
                    <ul class="content">
 | 
			
		||||
                    @foreach ($calendars as $cal)
 | 
			
		||||
                        <li>
 | 
			
		||||
                            <label class="flex items-center space-x-2">
 | 
			
		||||
                                <input type="checkbox"
 | 
			
		||||
                                       value="{{ $cal['id'] }}"
 | 
			
		||||
                                       style="--checkbox-color: {{ $cal['color'] }}"
 | 
			
		||||
                                       checked>
 | 
			
		||||
                                <span>{{ $cal['name'] }}</span>
 | 
			
		||||
                            </label>
 | 
			
		||||
                        </li>
 | 
			
		||||
                    @endforeach
 | 
			
		||||
                </ul>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <x-calendar.mini>
 | 
			
		||||
                @foreach ($grid['weeks'] as $week)
 | 
			
		||||
                    @foreach ($week as $day)
 | 
			
		||||
@ -39,5 +54,6 @@
 | 
			
		||||
                @endforeach
 | 
			
		||||
            </x-calendar.mini>
 | 
			
		||||
        </aside>
 | 
			
		||||
        <x-calendar.full class="month" :grid="$grid" :calendars="$calendars" />
 | 
			
		||||
    </x-slot>
 | 
			
		||||
</x-app-layout>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								resources/views/components/button/group-button.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								resources/views/components/button/group-button.blade.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
@props([
 | 
			
		||||
    'type' => 'submit',
 | 
			
		||||
    'name' => 'button-group',
 | 
			
		||||
    'value' => '1',
 | 
			
		||||
    'class' => '',
 | 
			
		||||
    'active' => false ])
 | 
			
		||||
 | 
			
		||||
<label class="{{ $class }}">
 | 
			
		||||
    <input type="radio" name="{{ $name }}" value="{{ $value }}" @checked($active)>
 | 
			
		||||
    {{ $slot }}
 | 
			
		||||
</label>
 | 
			
		||||
							
								
								
									
										26
									
								
								resources/views/components/calendar/day.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								resources/views/components/calendar/day.blade.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
@props([
 | 
			
		||||
    'day',              // required
 | 
			
		||||
    'calendars' => [],  // calendar palette keyed by id
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
<li
 | 
			
		||||
    data-day-number="{{ $day['label'] }}"
 | 
			
		||||
    data-event-count="{{ count($day['events'] ?? []) }}"
 | 
			
		||||
    @class([
 | 
			
		||||
        'day',
 | 
			
		||||
        'day--with-events' => !empty($day['events']),
 | 
			
		||||
        'day--current'     =>  $day['in_month'],
 | 
			
		||||
        'day--outside'     => !$day['in_month'],
 | 
			
		||||
        'day--today'       =>  $day['is_today'],
 | 
			
		||||
])>
 | 
			
		||||
    @foreach ($day['events'] as $event)
 | 
			
		||||
        @php
 | 
			
		||||
            $bg = $calendars[(string) $event['calendar_id']]['color'] ?? '#999';
 | 
			
		||||
        @endphp
 | 
			
		||||
    <a class="event" href="{{ format_event_url($event['id'], $event['calendar_id']) }}" style="--event-color: {{ $bg }}">
 | 
			
		||||
        <i class="indicator" aria-label="Calendar indicator"></i>
 | 
			
		||||
        <span class="title">{{ $event['title'] }}</span>
 | 
			
		||||
        <time>{{ $event['start_ui'] }}</time>
 | 
			
		||||
    </a>
 | 
			
		||||
    @endforeach
 | 
			
		||||
</li>
 | 
			
		||||
							
								
								
									
										24
									
								
								resources/views/components/calendar/full.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								resources/views/components/calendar/full.blade.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
@props([
 | 
			
		||||
    'grid' => ['weeks' => []],
 | 
			
		||||
    'calendars' => [],
 | 
			
		||||
    'class' => ''
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
<section class="calendar {{ $class }}">
 | 
			
		||||
    <hgroup>
 | 
			
		||||
        <span>Mon</span>
 | 
			
		||||
        <span>Tue</span>
 | 
			
		||||
        <span>Wed</span>
 | 
			
		||||
        <span>Thu</span>
 | 
			
		||||
        <span>Fri</span>
 | 
			
		||||
        <span>Sat</span>
 | 
			
		||||
        <span>Sun</span>
 | 
			
		||||
    </hgroup>
 | 
			
		||||
    <ol data-weeks="{{ count($grid['weeks']) }}">
 | 
			
		||||
        @foreach ($grid['weeks'] as $week)
 | 
			
		||||
            @foreach ($week as $day)
 | 
			
		||||
        <x-calendar.day :day="$day" :calendars="$calendars" />
 | 
			
		||||
            @endforeach
 | 
			
		||||
        @endforeach
 | 
			
		||||
    </ol>
 | 
			
		||||
</section>
 | 
			
		||||
@ -6,7 +6,7 @@
 | 
			
		||||
        <menu>Controls</menu>
 | 
			
		||||
    </header>
 | 
			
		||||
    <figure>
 | 
			
		||||
        <figcaption>
 | 
			
		||||
        <hgroup>
 | 
			
		||||
            <span>U</span>
 | 
			
		||||
            <span>M</span>
 | 
			
		||||
            <span>T</span>
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
            <span>R</span>
 | 
			
		||||
            <span>F</span>
 | 
			
		||||
            <span>S</span>
 | 
			
		||||
        </figcaption>
 | 
			
		||||
        </hgroup>
 | 
			
		||||
        <form action="/" method="get">
 | 
			
		||||
            {{ $slot }}
 | 
			
		||||
        </form>
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,3 @@
 | 
			
		||||
@props(['disabled' => false])
 | 
			
		||||
 | 
			
		||||
<input @disabled($disabled) {{ $attributes->merge(['class' => 'border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-xs']) }}>
 | 
			
		||||
<input @disabled($disabled) {{ $attributes->merge(['class' => '']) }}>
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
               <h1>{{ config('app.name', 'Kithkin') }}</h1>
 | 
			
		||||
            </a>
 | 
			
		||||
        </header>
 | 
			
		||||
        <main>
 | 
			
		||||
        <main class="border-md border-primary shadow-drop">
 | 
			
		||||
            {{ $slot }}
 | 
			
		||||
        </main>
 | 
			
		||||
    </body>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user