@axium/calendar 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/Select.svelte CHANGED
@@ -19,7 +19,7 @@
19
19
  const sameMonth = (d: Date) => view.getFullYear() == d.getFullYear() && view.getMonth() == d.getMonth();
20
20
  </script>
21
21
 
22
- <div class="CaldendarSelect">
22
+ <div class="CalendarSelect">
23
23
  <div class="bar">
24
24
  <span class="label">{view.toLocaleString('default', { month: 'long', year: 'numeric' })}</span>
25
25
  <button style:display="contents" onclick={() => view.setMonth(view.getMonth() - 1)}><Icon i="chevron-left" /></button>
@@ -70,8 +70,7 @@
70
70
  <style>
71
71
  .CalendarSelect {
72
72
  display: flex;
73
- align-items: center;
74
- justify-content: space-between;
73
+ flex-direction: column;
75
74
  }
76
75
 
77
76
  .bar {
@@ -124,4 +123,23 @@
124
123
  }
125
124
  }
126
125
  }
126
+
127
+ @media (width < 700px) {
128
+ .CalendarSelect {
129
+ width: 100%;
130
+ }
131
+
132
+ .month-grid {
133
+ width: 100%;
134
+ grid-template-columns: repeat(8, minmax(0, 1fr));
135
+ grid-template-rows: repeat(7, minmax(0, 1fr));
136
+ }
137
+
138
+ .month-grid > div {
139
+ width: 100%;
140
+ aspect-ratio: 1;
141
+ height: auto;
142
+ min-width: 0;
143
+ }
144
+ }
127
145
  </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axium/calendar",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "author": "James Prevett <axium@jamespre.dev>",
5
5
  "description": "Calendar for Axium",
6
6
  "funding": {
@@ -21,6 +21,7 @@
21
21
  import { SvelteDate } from 'svelte/reactivity';
22
22
  import { _throw } from 'utilium';
23
23
  import * as z from 'zod';
24
+ import './cal.css';
24
25
 
25
26
  const { data } = $props();
26
27
 
@@ -42,6 +43,7 @@
42
43
  $effect(() => {
43
44
  for (const cal of calendars) cal.color ||= encodeColor(colorHashHex(cal.name));
44
45
  getEvents(calendars, { start: new Date(start.getTime()), end: new Date(end.getTime()) }).then(e => (events = e));
46
+ calSidebar?.classList.remove('active');
45
47
  });
46
48
 
47
49
  const tz = new Date().toLocaleString('en', { timeStyle: 'long' }).split(' ').slice(-1)[0];
@@ -80,6 +82,8 @@
80
82
  );
81
83
 
82
84
  const defaultEventColor = $derived((eventInit.calendar || calendars[0])?.color || encodeColor(colorHashHex(user.name)));
85
+
86
+ let calSidebar = $state<HTMLDivElement>();
83
87
  </script>
84
88
 
85
89
  <svelte:head>
@@ -87,28 +91,40 @@
87
91
  </svelte:head>
88
92
 
89
93
  <div id="cal-app">
90
- <button class="event-init icon-text" command="show-modal" commandfor="event-init"><Icon i="plus" /> New Event</button>
94
+ <button class="event-init icon-text mobile-hide" command="show-modal" commandfor="event-init"><Icon i="plus" /> New Event</button>
91
95
  <div class="bar">
92
- <button onclick={() => start.setTime(today.getTime())}>Today</button>
96
+ <!-- desktop -->
97
+ <button class="mobile-hide" onclick={() => start.setTime(today.getTime())}>Today</button>
93
98
  <button
94
- style:display="contents"
99
+ class="reset mobile-hide"
95
100
  onclick={() => {
96
101
  start.setDate(start.getDate() - spanDays);
97
102
  end.setDate(end.getDate() - spanDays);
98
103
  }}><Icon i="chevron-left" /></button
99
104
  >
100
105
  <button
101
- style:display="contents"
106
+ class="reset mobile-hide"
102
107
  onclick={() => {
103
108
  start.setDate(start.getDate() + spanDays);
104
109
  end.setDate(end.getDate() + spanDays);
105
110
  }}><Icon i="chevron-right" /></button
106
111
  >
112
+
113
+ <!-- mobile -->
114
+ <button class="reset mobile-only icon-text" onclick={() => calSidebar?.classList.toggle('active')}><Icon i="bars" /></button>
115
+
116
+ <!-- shared -->
107
117
  <span class="label">{weekDays[0].toLocaleString('default', { month: 'long', year: 'numeric' })}</span>
118
+
119
+ <!-- mobile -->
120
+ <button class="reset mobile-only" onclick={() => start.setTime(today.getTime())}>
121
+ <span class="day-number today">{today.getDate()}</span>
122
+ </button>
123
+ <button class="reset mobile-only icon-text" command="show-modal" commandfor="event-init"><Icon i="plus" /></button>
108
124
  </div>
109
- <div id="cal-list">
125
+ <div id="cal-sidebar" bind:this={calSidebar}>
110
126
  <Cal.Select bind:start bind:end />
111
- <div class="cal-list-header">
127
+ <div class="cal-sidebar-header">
112
128
  <h4>My Calendars</h4>
113
129
  <button style:display="contents" command="show-modal" commandfor="add-calendar">
114
130
  <Icon i="plus" />
@@ -116,7 +132,7 @@
116
132
  </div>
117
133
  {#each calendars.filter(cal => cal.userId == user.id) as cal (cal.id)}
118
134
  <div
119
- class="cal-list-item"
135
+ class="cal-sidebar-item"
120
136
  {@attach contextMenu(
121
137
  { i: 'pencil', text: 'Rename', action: () => dialogs['rename:' + cal.id].showModal() },
122
138
  { i: 'user-group', text: 'Share', action: () => dialogs['share:' + cal.id].showModal() },
@@ -161,7 +177,7 @@
161
177
  </div>
162
178
  {/each}
163
179
  {#if calendars.some(cal => cal.userId != user.id)}
164
- <div class="cal-list-header">
180
+ <div class="cal-sidebar-header">
165
181
  <h4>Shared Calendars</h4>
166
182
  </div>
167
183
  {#each calendars.filter(cal => cal.userId != user.id) as cal (cal.id)}
@@ -173,7 +189,7 @@
173
189
  {/if}
174
190
  </div>
175
191
  <div id="cal">
176
- <div class="hours subtle">
192
+ <div id="hours" class="subtle">
177
193
  {#each { length: 24 }, i}
178
194
  {#if !i}
179
195
  <span class="hour">{tz}</span>
@@ -369,207 +385,3 @@
369
385
  <input name="name" type="text" required />
370
386
  </div>
371
387
  </FormDialog>
372
-
373
- <style>
374
- :global(body) {
375
- top: 5em;
376
- }
377
-
378
- #cal-app {
379
- display: grid;
380
- grid-template-rows: 3em 1fr;
381
- grid-template-columns: 15em 1fr;
382
- inset: 0;
383
- position: absolute;
384
- }
385
-
386
- button.event-init {
387
- margin: 0.5em;
388
- background-color: var(--bg-alt);
389
- text-align: center;
390
- justify-content: center;
391
- }
392
-
393
- :global {
394
- form {
395
- div:has(label ~ input),
396
- div:has(label ~ textarea),
397
- div:has(label ~ select) {
398
- display: flex;
399
- flex-direction: row;
400
- gap: 0.5em;
401
- }
402
- }
403
- }
404
-
405
- div.event-times-container,
406
- div.attendees-container {
407
- display: flex;
408
- flex-direction: row;
409
- gap: 0.5em;
410
- }
411
-
412
- div.event-times,
413
- div.attendees {
414
- display: flex;
415
- flex-direction: column;
416
- gap: 0.5em;
417
- flex-grow: 1;
418
- }
419
-
420
- h3,
421
- h4 {
422
- margin: 0;
423
- }
424
-
425
- :global {
426
- #event-init form {
427
- .event-time-options {
428
- display: flex;
429
- align-items: center;
430
- gap: 0.5em;
431
-
432
- .spacing,
433
- select {
434
- flex: auto;
435
- }
436
-
437
- label {
438
- flex: none;
439
- }
440
- }
441
-
442
- input,
443
- select,
444
- textarea {
445
- flex-grow: 1;
446
- }
447
-
448
- textarea {
449
- padding: 0.5em;
450
- font-size: 16px;
451
- }
452
- }
453
- }
454
-
455
- .bar {
456
- display: flex;
457
- gap: 1em;
458
- align-items: center;
459
- font-weight: bold;
460
- }
461
-
462
- #cal-list {
463
- display: flex;
464
- flex-direction: column;
465
- gap: 1em;
466
- grid-column: 1;
467
- padding: 1em;
468
-
469
- .cal-list-header,
470
- .cal-list-item {
471
- display: flex;
472
- align-items: center;
473
- justify-content: space-between;
474
- }
475
- }
476
-
477
- #cal {
478
- display: flex;
479
- width: 100%;
480
- height: 100%;
481
- padding: 1em;
482
- border-radius: 1em;
483
- background-color: var(--bg-menu);
484
-
485
- .hours {
486
- display: flex;
487
- flex-direction: column;
488
- align-items: stretch;
489
- text-align: right;
490
- justify-content: space-around;
491
- overflow-x: visible;
492
- margin-top: 5em;
493
- }
494
-
495
- .hour {
496
- position: relative;
497
- padding-right: 1em;
498
- }
499
-
500
- .hour:not(.empty)::after {
501
- content: '';
502
- position: absolute;
503
- left: calc(100% - 0.5em);
504
- width: calc(100vw - 22em);
505
- border-bottom: var(--border-accent);
506
- top: 50%;
507
- z-index: 0;
508
- pointer-events: none;
509
- }
510
-
511
- .cal-content {
512
- display: flex;
513
- width: 100%;
514
- height: 100%;
515
- justify-content: space-around;
516
- }
517
-
518
- .day {
519
- width: 100%;
520
- height: 100%;
521
- border-left: var(--border-accent);
522
- display: flex;
523
- flex-direction: column;
524
-
525
- .day-header {
526
- display: flex;
527
- text-align: center;
528
- align-items: center;
529
- justify-content: center;
530
- gap: 0.5em;
531
- user-select: none;
532
- height: 5.9em;
533
- flex-shrink: 0;
534
-
535
- .day-number {
536
- border-radius: 0.3em;
537
- width: 2em;
538
- height: 2em;
539
- display: inline-flex;
540
- align-items: center;
541
- justify-content: center;
542
-
543
- &.today {
544
- border: var(--border-accent);
545
- color: var(--fg-accent);
546
- }
547
- }
548
- }
549
-
550
- .day-content {
551
- flex-grow: 1;
552
- position: relative;
553
-
554
- .now {
555
- position: absolute;
556
- width: 100%;
557
- border-bottom: 1px solid hsl(0 33 var(--fg-light));
558
- z-index: 9;
559
- pointer-events: none;
560
-
561
- &::before {
562
- content: '';
563
- position: absolute;
564
- left: -0.25em;
565
- top: -0.25em;
566
- width: 0.5em;
567
- height: 0.5em;
568
- border-radius: 50%;
569
- background-color: hsl(0 33 var(--fg-light));
570
- }
571
- }
572
- }
573
- }
574
- }
575
- </style>
@@ -0,0 +1,255 @@
1
+ body:has(#cal-app) {
2
+ top: 4em;
3
+ }
4
+
5
+ #cal-app {
6
+ display: grid;
7
+ grid-template-rows: 3em 1fr;
8
+ grid-template-columns: 15em 1fr;
9
+ inset: 0;
10
+ position: absolute;
11
+
12
+ h3,
13
+ h4 {
14
+ margin: 0;
15
+ }
16
+ }
17
+
18
+ button.event-init {
19
+ margin: 0.5em;
20
+ background-color: var(--bg-alt);
21
+ text-align: center;
22
+ justify-content: center;
23
+ }
24
+
25
+ form {
26
+ div:has(label ~ input),
27
+ div:has(label ~ textarea),
28
+ div:has(label ~ select) {
29
+ display: flex;
30
+ flex-direction: row;
31
+ gap: 0.5em;
32
+ }
33
+ }
34
+
35
+ div.event-times-container,
36
+ div.attendees-container {
37
+ display: flex;
38
+ flex-direction: row;
39
+ gap: 0.5em;
40
+ }
41
+
42
+ div.event-times,
43
+ div.attendees {
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: 0.5em;
47
+ flex-grow: 1;
48
+ }
49
+
50
+ #event-init form {
51
+ .event-time-options {
52
+ display: flex;
53
+ align-items: center;
54
+ gap: 0.5em;
55
+
56
+ .spacing,
57
+ select {
58
+ flex: auto;
59
+ }
60
+
61
+ label {
62
+ flex: none;
63
+ }
64
+ }
65
+
66
+ input,
67
+ select,
68
+ textarea {
69
+ width: 100%;
70
+ }
71
+
72
+ textarea {
73
+ padding: 0.5em;
74
+ font-size: 16px;
75
+ }
76
+ }
77
+
78
+ .bar {
79
+ display: flex;
80
+ gap: 1em;
81
+ align-items: center;
82
+ font-weight: bold;
83
+
84
+ @media (width < 700px) {
85
+ margin: 0 1em;
86
+
87
+ .label {
88
+ flex-grow: 1;
89
+ }
90
+ }
91
+ }
92
+
93
+ :where(#cal-sidebar) {
94
+ display: flex;
95
+ flex-direction: column;
96
+ gap: 1em;
97
+ grid-column: 1;
98
+ padding: 1em;
99
+ border-radius: 1em;
100
+
101
+ .cal-sidebar-header,
102
+ .cal-sidebar-item {
103
+ display: flex;
104
+ align-items: center;
105
+ justify-content: space-between;
106
+ }
107
+ }
108
+
109
+ #cal {
110
+ display: flex;
111
+ width: 100%;
112
+ height: 100%;
113
+ padding: 1em;
114
+ border-radius: 1em;
115
+ background-color: var(--bg-menu);
116
+
117
+ .cal-content {
118
+ display: flex;
119
+ width: 100%;
120
+ height: 100%;
121
+ justify-content: space-around;
122
+ }
123
+ }
124
+
125
+ #hours {
126
+ display: flex;
127
+ flex-direction: column;
128
+ align-items: stretch;
129
+ text-align: right;
130
+ justify-content: space-around;
131
+ overflow-x: visible;
132
+ margin-top: 5em;
133
+
134
+ .hour {
135
+ position: relative;
136
+ padding-right: 1em;
137
+ }
138
+
139
+ .hour:not(.empty)::after {
140
+ content: '';
141
+ position: absolute;
142
+ left: calc(100% - 0.5em);
143
+ width: calc(100vw - 22em);
144
+ border-bottom: var(--border-accent);
145
+ top: 50%;
146
+ z-index: 0;
147
+ pointer-events: none;
148
+
149
+ @media (width < 700px) {
150
+ width: calc(100vw - 5em);
151
+ }
152
+ }
153
+ }
154
+
155
+ .day {
156
+ width: 100%;
157
+ height: 100%;
158
+ border-left: var(--border-accent);
159
+ display: flex;
160
+ flex-direction: column;
161
+ }
162
+
163
+ .day-header {
164
+ display: flex;
165
+ text-align: center;
166
+ align-items: center;
167
+ justify-content: center;
168
+ gap: 0.5em;
169
+ user-select: none;
170
+ height: 5.9em;
171
+ flex-shrink: 0;
172
+
173
+ @media (width < 700px) {
174
+ flex-direction: column;
175
+ }
176
+ }
177
+
178
+ .day-number {
179
+ border-radius: 0.3em;
180
+ width: 2em;
181
+ height: 2em;
182
+ display: inline-flex;
183
+ align-items: center;
184
+ justify-content: center;
185
+
186
+ &.today {
187
+ border: var(--border-accent);
188
+ color: var(--fg-accent);
189
+ }
190
+ }
191
+
192
+ .day-content {
193
+ flex-grow: 1;
194
+ position: relative;
195
+
196
+ .now {
197
+ position: absolute;
198
+ width: 100%;
199
+ border-bottom: 1px solid hsl(0 33 var(--fg-light));
200
+ z-index: 9;
201
+ pointer-events: none;
202
+
203
+ &::before {
204
+ content: '';
205
+ position: absolute;
206
+ left: -0.25em;
207
+ top: -0.25em;
208
+ width: 0.5em;
209
+ height: 0.5em;
210
+ border-radius: 50%;
211
+ background-color: hsl(0 33 var(--fg-light));
212
+ }
213
+ }
214
+ }
215
+
216
+ @media (width < 700px) {
217
+ #cal-app {
218
+ grid-template-columns: 1fr;
219
+ }
220
+
221
+ #cal-sidebar {
222
+ container-type: inline-size;
223
+ position: absolute;
224
+ inset: 3em 0 0;
225
+ z-index: 10;
226
+ background-color: var(--bg-menu);
227
+ font-size: clamp(0.95rem, 0.75rem + 2.5cqw, 1.35rem);
228
+ translate: -100% 0;
229
+ opacity: 0;
230
+ visibility: hidden;
231
+ pointer-events: none;
232
+
233
+ .CalendarSelect {
234
+ width: 100%;
235
+ }
236
+
237
+ &.active {
238
+ translate: 0 0;
239
+ opacity: 1;
240
+ visibility: visible;
241
+ pointer-events: auto;
242
+ }
243
+
244
+ @media not (prefers-reduced-motion) {
245
+ transition:
246
+ translate 0.25s ease,
247
+ opacity 0.25s ease,
248
+ visibility 0s linear 0.25s;
249
+
250
+ &.active {
251
+ transition-delay: 0s;
252
+ }
253
+ }
254
+ }
255
+ }