@axium/calendar 0.3.0 → 0.3.2

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 {
@@ -108,7 +107,7 @@
108
107
  }
109
108
 
110
109
  &.today {
111
- border: 1px solid var(--border-accent);
110
+ border: var(--border-accent);
112
111
  }
113
112
 
114
113
  &.selected {
@@ -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.0",
3
+ "version": "0.3.2",
4
4
  "author": "James Prevett <axium@jamespre.dev>",
5
5
  "description": "Calendar for Axium",
6
6
  "funding": {
@@ -35,7 +35,7 @@
35
35
  "build": "tsc"
36
36
  },
37
37
  "peerDependencies": {
38
- "@axium/client": ">=0.14.3",
38
+ "@axium/client": ">=0.17.0",
39
39
  "@axium/core": ">=0.20.0",
40
40
  "@axium/server": ">=0.36.0",
41
41
  "@sveltejs/kit": "^2.27.3",
@@ -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,38 @@
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 day-number today" onclick={() => start.setTime(today.getTime())}>{today.getDate()}</button>
121
+ <button class="reset mobile-only icon-text" command="show-modal" commandfor="event-init"><Icon i="plus" /></button>
108
122
  </div>
109
- <div id="cal-list">
123
+ <div id="cal-sidebar" bind:this={calSidebar}>
110
124
  <Cal.Select bind:start bind:end />
111
- <div class="cal-list-header">
125
+ <div class="cal-sidebar-header">
112
126
  <h4>My Calendars</h4>
113
127
  <button style:display="contents" command="show-modal" commandfor="add-calendar">
114
128
  <Icon i="plus" />
@@ -116,7 +130,7 @@
116
130
  </div>
117
131
  {#each calendars.filter(cal => cal.userId == user.id) as cal (cal.id)}
118
132
  <div
119
- class="cal-list-item"
133
+ class="cal-sidebar-item"
120
134
  {@attach contextMenu(
121
135
  { i: 'pencil', text: 'Rename', action: () => dialogs['rename:' + cal.id].showModal() },
122
136
  { i: 'user-group', text: 'Share', action: () => dialogs['share:' + cal.id].showModal() },
@@ -161,7 +175,7 @@
161
175
  </div>
162
176
  {/each}
163
177
  {#if calendars.some(cal => cal.userId != user.id)}
164
- <div class="cal-list-header">
178
+ <div class="cal-sidebar-header">
165
179
  <h4>Shared Calendars</h4>
166
180
  </div>
167
181
  {#each calendars.filter(cal => cal.userId != user.id) as cal (cal.id)}
@@ -173,7 +187,7 @@
173
187
  {/if}
174
188
  </div>
175
189
  <div id="cal">
176
- <div class="hours subtle">
190
+ <div id="hours" class="subtle">
177
191
  {#each { length: 24 }, i}
178
192
  {#if !i}
179
193
  <span class="hour">{tz}</span>
@@ -369,207 +383,3 @@
369
383
  <input name="name" type="text" required />
370
384
  </div>
371
385
  </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: 1px solid 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: 1px solid 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: 1px solid 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: 5em;
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
+ flex-grow: 1;
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.5rem + 3cqw, 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
+ }