simple-calendar-js 2.0.2 → 3.0.0

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.
@@ -0,0 +1,208 @@
1
+ /**
2
+ * SimpleCalendarJs v3.0.0 — Vue 3 Wrapper
3
+ * A clean, modern, and feature-rich JavaScript calendar component with zero dependencies
4
+ *
5
+ * @author Pedro Lopes <simplecalendarjs@gmail.com>
6
+ * @homepage https://www.simplecalendarjs.com
7
+ * @license SEE LICENSE IN LICENSE
8
+ * @repository https://github.com/pclslopes/SimpleCalendarJs
9
+ *
10
+ * Exposed methods (via ref):
11
+ * const calRef = ref();
12
+ * calRef.value.setView('week');
13
+ * calRef.value.navigate(1);
14
+ * calRef.value.goToDate(new Date());
15
+ * calRef.value.goToToday();
16
+ * calRef.value.getInstance(); // Get raw instance
17
+ *
18
+ * Example with theme switching:
19
+ * <script setup>
20
+ * import { ref } from 'vue';
21
+ * import SimpleCalendarJsVue from './simple-calendar-js-vue.js';
22
+ *
23
+ * const isDark = ref(false);
24
+ * const view = ref('month');
25
+ *
26
+ * const fetchEvents = async (start, end) => {
27
+ * // Fetch events...
28
+ * return events;
29
+ * };
30
+ * </script>
31
+ *
32
+ * <template>
33
+ * <button @click="isDark = !isDark">Toggle Theme</button>
34
+ * <SimpleCalendarJsVue
35
+ * :defaultView="view"
36
+ * :darkMode="isDark"
37
+ * :fetchEvents="fetchEvents"
38
+ * />
39
+ * </template>
40
+ */
41
+
42
+ import { ref, onMounted, onUnmounted, watch, defineComponent, h } from 'vue';
43
+
44
+ // Props that require re-mounting when changed
45
+ const INIT_PROPS = [
46
+ 'defaultView',
47
+ 'defaultDate',
48
+ 'weekStartsOn',
49
+ 'locale',
50
+ 'use24Hour',
51
+ 'showTimeInItems',
52
+ 'showGridLines',
53
+ 'showToolbar',
54
+ 'showTodayButton',
55
+ 'showNavigation',
56
+ 'showTitle',
57
+ 'showYearPicker',
58
+ 'showViewSwitcher',
59
+ 'enabledViews',
60
+ ];
61
+
62
+ // Callback props that map to Vue events
63
+ const CALLBACK_PROPS = ['fetchEvents', 'onEventClick', 'onSlotClick', 'onViewChange', 'onNavigate'];
64
+
65
+ export default defineComponent({
66
+ name: 'SimpleCalendarJs',
67
+
68
+ props: {
69
+ // Init-only props
70
+ defaultView: { type: String, default: 'month' },
71
+ defaultDate: { type: Date, default: null },
72
+ weekStartsOn: { type: Number, default: 0 },
73
+ locale: { type: String, default: 'default' },
74
+ use24Hour: { type: Boolean, default: false },
75
+ showTimeInItems: { type: Boolean, default: true },
76
+ showGridLines: { type: Boolean, default: true },
77
+ showToolbar: { type: Boolean, default: true },
78
+ showTodayButton: { type: Boolean, default: true },
79
+ showNavigation: { type: Boolean, default: true },
80
+ showTitle: { type: Boolean, default: true },
81
+ showYearPicker: { type: Boolean, default: true },
82
+ showViewSwitcher: { type: Boolean, default: true },
83
+ enabledViews: { type: Array, default: () => ['month', 'week', 'day'] },
84
+
85
+ // Callback props
86
+ fetchEvents: { type: Function, default: null },
87
+
88
+ // Theme prop
89
+ darkMode: { type: Boolean, default: false },
90
+
91
+ // Custom class
92
+ customClass: { type: String, default: '' },
93
+ },
94
+
95
+ emits: ['eventClick', 'slotClick', 'viewChange', 'navigate'],
96
+
97
+ setup(props, { emit, expose }) {
98
+ const containerRef = ref(null);
99
+ const instanceRef = ref(null);
100
+
101
+ // Helper to resolve SimpleCalendarJs class
102
+ const resolveClass = () => {
103
+ if (typeof SimpleCalendarJs !== 'undefined') return SimpleCalendarJs;
104
+ if (typeof window !== 'undefined' && window.SimpleCalendarJs) return window.SimpleCalendarJs;
105
+ return null;
106
+ };
107
+
108
+ // Create calendar instance
109
+ const createCalendar = () => {
110
+ const Cal = resolveClass();
111
+ if (!Cal) {
112
+ console.error(
113
+ 'SimpleCalendarJsVue: SimpleCalendarJs class not found. ' +
114
+ 'Make sure simple-calendar-js.js is imported or loaded as a script.'
115
+ );
116
+ return;
117
+ }
118
+
119
+ // Destroy previous instance
120
+ if (instanceRef.value) {
121
+ instanceRef.value.destroy();
122
+ instanceRef.value = null;
123
+ }
124
+
125
+ // Build options
126
+ const options = {};
127
+
128
+ // Add init props
129
+ for (const key of INIT_PROPS) {
130
+ if (props[key] !== undefined) options[key] = props[key];
131
+ }
132
+
133
+ // Add callback props with Vue event emitters
134
+ if (props.fetchEvents) {
135
+ options.fetchEvents = props.fetchEvents;
136
+ }
137
+ options.onEventClick = (event) => emit('eventClick', event);
138
+ options.onSlotClick = (date) => emit('slotClick', date);
139
+ options.onViewChange = (view) => emit('viewChange', view);
140
+ options.onNavigate = (start, end) => emit('navigate', start, end);
141
+
142
+ instanceRef.value = new Cal(containerRef.value, options);
143
+
144
+ // Apply dark mode if needed
145
+ if (props.darkMode) {
146
+ instanceRef.value._root.classList.add('uc-dark');
147
+ }
148
+ };
149
+
150
+ // Mount calendar
151
+ onMounted(() => {
152
+ createCalendar();
153
+ });
154
+
155
+ // Cleanup
156
+ onUnmounted(() => {
157
+ instanceRef.value?.destroy();
158
+ instanceRef.value = null;
159
+ });
160
+
161
+ // Watch init props for changes (requires re-mount)
162
+ for (const key of INIT_PROPS) {
163
+ watch(
164
+ () => props[key],
165
+ () => {
166
+ createCalendar();
167
+ }
168
+ );
169
+ }
170
+
171
+ // Watch dark mode (no re-mount needed)
172
+ watch(
173
+ () => props.darkMode,
174
+ (isDark) => {
175
+ if (instanceRef.value) {
176
+ instanceRef.value._root.classList.toggle('uc-dark', isDark);
177
+ }
178
+ }
179
+ );
180
+
181
+ // Watch callback props (update without re-mount)
182
+ watch(
183
+ () => props.fetchEvents,
184
+ (newFn) => {
185
+ if (instanceRef.value && newFn) {
186
+ instanceRef.value._opts.fetchEvents = newFn;
187
+ }
188
+ }
189
+ );
190
+
191
+ // Expose methods
192
+ expose({
193
+ setView: (view) => instanceRef.value?.setView(view),
194
+ navigate: (direction) => instanceRef.value?.navigate(direction),
195
+ goToDate: (date) => instanceRef.value?.goToDate(date),
196
+ goToToday: () => instanceRef.value?.goToToday(),
197
+ getInstance: () => instanceRef.value,
198
+ });
199
+
200
+ // Render function
201
+ return () =>
202
+ h('div', {
203
+ ref: containerRef,
204
+ class: props.customClass,
205
+ style: { height: '100%', minHeight: '500px' },
206
+ });
207
+ },
208
+ });
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "simple-calendar-js",
3
- "version": "2.0.2",
3
+ "version": "3.0.0",
4
4
  "description": "A clean, modern, and feature-rich JavaScript calendar component with zero dependencies. Responsive design and intuitive navigation.",
5
- "main": "dist/SimpleCalendarJs.min.js",
6
- "style": "dist/SimpleCalendarJs.min.css",
5
+ "main": "dist/simple-calendar-js.min.js",
6
+ "style": "dist/simple-calendar-js.min.css",
7
7
  "files": [
8
8
  "dist/",
9
+ "frameworks/",
9
10
  "LICENSE",
10
11
  "README.md"
11
12
  ],
@@ -33,7 +34,10 @@
33
34
  "day-view",
34
35
  "dark-mode",
35
36
  "ui-component",
36
- "framework-agnostic"
37
+ "framework-agnostic",
38
+ "react",
39
+ "vue",
40
+ "angular"
37
41
  ],
38
42
  "author": "Pedro Lopes <simplecalendarjs@gmail.com> (https://www.simplecalendarjs.com)",
39
43
  "license": "SEE LICENSE IN LICENSE",
@@ -1,10 +0,0 @@
1
- /**
2
- * SimpleCalendarJs v2.0.2
3
- * A clean, modern, and feature-rich JavaScript calendar component with zero dependencies
4
- *
5
- * @author Pedro Lopes <simplecalendarjs@gmail.com>
6
- * @homepage https://www.simplecalendarjs.com
7
- * @license SEE LICENSE IN LICENSE
8
- * @repository https://github.com/pclslopes/SimpleCalendarJs
9
- */
10
- .sc-calendar{--sc-bg-primary:#ffffff;--sc-bg-secondary:#f8f9fa;--sc-bg-tertiary:#e9ecef;--sc-text-primary:#212529;--sc-text-secondary:#6c757d;--sc-text-muted:#adb5bd;--sc-border-color:#e9ecef;--sc-border-light:#dee2e6;--sc-accent-color:#4c6f94;--sc-accent-hover:#0056b3;--sc-today-bg:#e3f2fd;--sc-today-text:#dc3545;--sc-hover-bg:#f8f9fa;--sc-shadow:none;--sc-shadow-enabled:0 2px 10px rgba(0, 0, 0, 0.1);font-family:inherit;background:var(--sc-bg-primary);color:var(--sc-text-primary);box-shadow:var(--sc-shadow);overflow:hidden;width:100%;max-width:100%;display:flex;flex-direction:column}.sc-calendar.sc-shadow{--sc-shadow:var(--sc-shadow-enabled)}@media (prefers-color-scheme:dark){.sc-calendar{--sc-bg-primary:#1a1a1a;--sc-bg-secondary:#2d2d2d;--sc-bg-tertiary:#3a3a3a;--sc-text-primary:#ffffff;--sc-text-secondary:#b0b0b0;--sc-text-muted:#888888;--sc-border-color:#444444;--sc-border-light:#555555;--sc-accent-color:#4a90e2;--sc-accent-hover:#357abd;--sc-today-bg:#1a2f4a;--sc-today-text:#ff6b6b;--sc-hover-bg:#2d2d2d;--sc-shadow-enabled:0 2px 10px rgba(0, 0, 0, 0.5)}}[data-theme=dark] .sc-calendar{--sc-bg-primary:#1a1a1a;--sc-bg-secondary:#2d2d2d;--sc-bg-tertiary:#3a3a3a;--sc-text-primary:#ffffff;--sc-text-secondary:#b0b0b0;--sc-text-muted:#888888;--sc-border-color:#444444;--sc-border-light:#555555;--sc-accent-color:#4a90e2;--sc-accent-hover:#357abd;--sc-today-bg:#1a2f4a;--sc-today-text:#ff6b6b;--sc-hover-bg:#2d2d2d;--sc-shadow-enabled:0 2px 10px rgba(0, 0, 0, 0.5)}[data-theme=light] .sc-calendar{--sc-bg-primary:#ffffff;--sc-bg-secondary:#f8f9fa;--sc-bg-tertiary:#e9ecef;--sc-text-primary:#212529;--sc-text-secondary:#6c757d;--sc-text-muted:#adb5bd;--sc-border-color:#e9ecef;--sc-border-light:#dee2e6;--sc-accent-color:#4c6f94;--sc-accent-hover:#0056b3;--sc-today-bg:#e3f2fd;--sc-today-text:#dc3545;--sc-hover-bg:#f8f9fa;--sc-shadow-enabled:0 2px 10px rgba(0, 0, 0, 0.1)}.sc-calendar{background:var(--calendar-bg,var(--sc-bg-primary));color:var(--calendar-text,var(--sc-text-primary));height:100%}.sc-header{background:0 0;padding:16px;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:12px}.sc-borders-both .sc-header,.sc-borders-horizontal .sc-header{border-bottom:1px solid var(--sc-border-color)}.sc-nav{display:flex;align-items:center;gap:8px;justify-self:start}.sc-title-container{position:relative;justify-self:center}.sc-title{margin:0;font-size:1.25rem;font-weight:600;color:var(--sc-text-primary);white-space:nowrap;text-align:center;user-select:none}.sc-title-month,.sc-title-year{cursor:pointer;padding:2px 4px;border-radius:3px;transition:background-color .2s ease}.sc-title-month:hover,.sc-title-year:hover{color:var(--sc-event-color,#4c6f94)}.sc-month-dropdown{position:absolute;top:100%;left:50%;transform:translateX(-50%);background:var(--sc-bg-primary);border:1px solid var(--sc-border-color);border-radius:4px;z-index:1000;min-width:120px;box-shadow:var(--sc-shadow-enabled);max-height:240px;overflow-y:auto}.sc-month-option{padding:4px 8px;font-size:.75rem;color:var(--sc-text-primary);cursor:pointer;transition:background-color .2s ease}.sc-month-option:hover{background:var(--sc-bg-tertiary)}.sc-month-option.sc-current{background:var(--sc-event-color,#4c6f94);color:#fff}.sc-month-option.sc-current:hover{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 85%,#000)}.sc-year-dropdown{position:absolute;top:100%;right:-20px;background:var(--sc-bg-primary);border:1px solid var(--sc-border-color);border-radius:4px;z-index:1000;min-width:100px;box-shadow:var(--sc-shadow-enabled);max-height:240px;overflow-y:auto}.sc-year-option{padding:4px 8px;font-size:.75rem;color:var(--sc-text-primary);cursor:pointer;transition:background-color .2s ease;text-align:center}.sc-year-option:hover{background:var(--sc-bg-tertiary)}.sc-year-option.sc-current{background:var(--sc-event-color,#4c6f94);color:#fff}.sc-year-option.sc-current:hover{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 85%,#000)}.sc-btn{background:0 0;border:1px solid var(--sc-border-light);border-radius:4px;padding:8px 12px;cursor:pointer;color:var(--sc-text-primary);font-size:.875rem;font-weight:500;transition:all .2s ease;min-width:36px;display:flex;align-items:center;justify-content:center}.sc-nav .sc-btn{background:0 0;border:none;font-size:1.2rem;padding:8px;min-width:32px;color:var(--sc-text-primary);font-weight:700}.sc-btn:hover{background:var(--sc-bg-tertiary);border-color:#adb5bd}.sc-nav .sc-btn:hover{background:var(--sc-bg-tertiary);color:var(--sc-text-primary)}.sc-btn:active{background:#dee2e6}.sc-nav .sc-btn:active{background:var(--sc-bg-tertiary)}.sc-view-switcher{display:flex;gap:4px;justify-self:end}.sc-view-btn.sc-active{background:var(--sc-event-color,#4c6f94);color:#fff;border-color:var(--sc-event-color,#4c6f94)}.sc-view-btn.sc-active:hover{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 85%,#000);border-color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 85%,#000)}.sc-content{flex:1;height:100%;display:flex;flex-direction:column}.sc-view-container{flex:1;overflow:hidden;height:100%}.sc-month-view{height:100%;display:flex;flex-direction:column}.sc-weekdays{display:grid;grid-template-columns:repeat(7,1fr);background:var(--sc-bg-secondary)}.sc-weekday{padding:12px 8px;text-align:center;font-weight:600;font-size:.875rem;color:var(--sc-text-secondary)}.sc-borders-both .sc-month-view .sc-weekdays,.sc-borders-vertical .sc-month-view .sc-weekdays{border-left:1px solid var(--sc-border-color);border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-month-view .sc-weekday,.sc-borders-vertical .sc-month-view .sc-weekday{border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-month-view .sc-weekday:nth-child(7n),.sc-borders-vertical .sc-month-view .sc-weekday:nth-child(7n){border-right:none}.sc-days-grid{display:grid;grid-template-columns:repeat(7,1fr);grid-template-rows:repeat(6,1fr);flex:1;overflow:visible}.sc-month-view.sc-fullday .sc-days-grid{grid-template-rows:repeat(6,auto);min-height:auto}.sc-month-view.sc-fullday .sc-day{display:flex;flex-direction:column;position:relative}.sc-day{display:flex;flex-direction:column;min-height:80px;cursor:pointer;transition:background-color .2s ease;overflow:visible}.sc-day:hover{background:var(--sc-hover-bg)}.sc-day-number{text-align:center;font-weight:500;margin-bottom:4px;font-size:.875rem;line-height:1.2}.sc-day.sc-other-month .sc-day-number{color:var(--sc-text-muted)}.sc-day.sc-today{background:var(--sc-today-bg)}.sc-day.sc-today .sc-day-number{color:var(--sc-today-text);font-weight:600}.sc-day-count-mode{display:flex;align-items:center;justify-content:center}.sc-day-content{text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center}.sc-day-count-mode .sc-day-number{font-size:1.125rem;font-weight:600;margin-bottom:4px}.sc-event-count{font-size:.7rem;font-weight:600;line-height:1.2;background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 15%,var(--sc-bg-tertiary));color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 80%,#000);padding:2px 6px;border-radius:10px;display:inline-block;margin-top:2px;text-align:center;box-sizing:border-box}@media (prefers-color-scheme:light){.sc-event-count{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 15%,var(--sc-bg-tertiary));color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 80%,#000)}}[data-theme=light] .sc-event-count{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 15%,var(--sc-bg-tertiary));color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 80%,#000)}@media (prefers-color-scheme:dark){.sc-event-count{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 20%,var(--sc-bg-tertiary));color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 30%,#fff)}}[data-theme=dark] .sc-event-count{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 20%,var(--sc-bg-tertiary));color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 30%,#fff)}.sc-day-count-mode.sc-today .sc-event-count{background:var(--sc-event-color,#4c6f94);color:#fff}@media (prefers-color-scheme:light){.sc-day-count-mode.sc-today .sc-event-count{background:var(--sc-event-color,#4c6f94);color:#fff}}[data-theme=light] .sc-day-count-mode.sc-today .sc-event-count{background:var(--sc-event-color,#4c6f94);color:#fff}@media (prefers-color-scheme:dark){.sc-day-count-mode.sc-today .sc-event-count{background:var(--sc-event-color,#4c6f94);color:#fff}}[data-theme=dark] .sc-day-count-mode.sc-today .sc-event-count{background:var(--sc-event-color,#4c6f94);color:#fff}.sc-day-count-mode.sc-other-month .sc-event-count{background:var(--sc-bg-secondary);color:var(--sc-text-muted)}.sc-day-events{flex:1;display:flex;flex-direction:column;gap:2px;overflow:hidden;padding:4px}.sc-month-view.sc-fullday .sc-day-number{flex-shrink:0;margin-bottom:4px;padding:4px 6px 0 6px}.sc-event-day,.sc-event-month,.sc-event-time,.sc-event-week{background:var(--sc-event-color,#4c6f94);color:#fff;border-radius:3px;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;box-sizing:border-box}.sc-month-view .sc-event-month{padding:2px 6px;font-size:.75rem;height:18px;margin:1px 4px;min-width:0;display:block}.sc-month-view.sc-fullday .sc-event-month{position:relative;padding:2px 6px;border-radius:3px;min-height:18px;display:block;box-sizing:border-box;overflow:visible}.sc-month-view.sc-fullday .sc-event-text{font-size:.75rem;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;position:absolute;display:block;width:100%}.sc-month-view .sc-event-single{border-radius:3px}.sc-month-view .sc-event-start{border-radius:3px 0 0 3px}.sc-month-view .sc-event-middle{border-radius:0}.sc-month-view .sc-event-end{border-radius:0 3px 3px 0}.sc-month-view .sc-event-month.sc-event-start{border-radius:3px 0 0 3px}.sc-month-view .sc-event-month.sc-event-middle{border-radius:0}.sc-month-view .sc-event-month.sc-event-end{border-radius:0 3px 3px 0}.sc-month-view.sc-fullday .sc-event-month.sc-event-start{position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-start::after{content:'';position:absolute;right:-2px;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-month-view.sc-fullday .sc-event-month.sc-event-middle{position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-middle::after,.sc-month-view.sc-fullday .sc-event-month.sc-event-middle::before{content:'';position:absolute;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-month-view.sc-fullday .sc-event-month.sc-event-middle::before{left:-2px}.sc-month-view.sc-fullday .sc-event-month.sc-event-middle::after{right:-2px}.sc-month-view.sc-fullday .sc-event-month.sc-event-end{position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-end::before{content:'';position:absolute;left:-2px;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-month-view.sc-fullday .sc-event-month.sc-event-single{margin:1px 4px!important;border-radius:3px!important;z-index:1;position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-start{margin:1px 0 1px 4px!important;border-radius:3px 0 0 3px!important;z-index:2;position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-middle{margin:1px 0!important;border-radius:0!important;z-index:2;position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-end{margin:1px 4px 1px 0!important;border-radius:0 3px 3px 0!important;z-index:2;position:relative}.sc-event-placeholder{opacity:0!important}.sc-month-view.sc-fullday .sc-event-placeholder{min-height:18px;padding:2px 6px;margin:1px 4px;display:block;box-sizing:border-box}.sc-week-view.sc-fullday .sc-event-placeholder{min-height:18px;padding:2px 6px;margin:1px 4px;display:block;box-sizing:border-box;line-height:.9em}.sc-week-view{height:100%;overflow-y:auto;overflow-x:hidden;display:grid;grid-template-columns:80px repeat(7,1fr);align-content:start}.sc-day-header,.sc-time-column-header{position:sticky;top:0;z-index:20;display:flex;flex-direction:column;justify-content:center;text-align:center;padding:12px 8px;background:var(--sc-bg-secondary);height:60px;box-sizing:border-box}.sc-week-view .sc-day-header.sc-today{background:var(--sc-today-bg)}.sc-day-name{font-size:.75rem;color:var(--sc-text-secondary);font-weight:500;margin-bottom:2px}.sc-day-header .sc-day-number{font-size:1.25rem;font-weight:600;color:var(--sc-text-primary)}.sc-week-view .sc-day-header.sc-today .sc-day-number{color:var(--sc-today-text);font-weight:600}.sc-day-column,.sc-time-slot{height:60px;position:relative;box-sizing:border-box}.sc-time-slot{background:var(--sc-bg-secondary);padding:4px 8px 8px 8px;font-size:.75rem;color:var(--sc-text-secondary);text-align:center;display:flex;align-items:flex-start;justify-content:center}.sc-time-slot:first-child,.sc-time-slot:last-child{background:var(--sc-bg-tertiary);color:#495057;font-weight:500;font-style:italic}.sc-day-column{cursor:pointer;background:var(--sc-bg-primary)}.sc-week-view .sc-day-column.sc-today{background:var(--sc-today-bg)}.sc-day-column:hover{background:var(--sc-hover-bg)}.sc-half-hour-line{position:absolute;top:50%;left:0;right:0;height:0;pointer-events:none;z-index:1}.sc-week-view .sc-event-time{position:absolute;top:4px;left:4px;right:4px;padding:2px 6px;font-size:.75rem;line-height:1.2;border-radius:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;z-index:5;min-height:18px;display:block;box-sizing:border-box;padding-top:3px;padding-bottom:1px}.sc-week-view .sc-event-single{border-radius:3px}.sc-week-view .sc-event-start{border-radius:3px 0 0 3px}.sc-week-view .sc-event-middle{border-radius:0}.sc-week-view .sc-event-end{border-radius:0 3px 3px 0}.sc-week-view.sc-fullday .sc-event{width:100%;margin:2px 4px;padding:2px 6px;font-size:.75rem;height:18px;min-width:0}.sc-week-view.sc-fullday .sc-event-start{margin-right:0}.sc-week-view.sc-fullday .sc-event-middle{margin-left:0;margin-right:0}.sc-week-view.sc-fullday .sc-event-end{margin-left:0}.sc-week-view.sc-fullday{height:auto;display:flex;flex-direction:column}.sc-week-view.sc-fullday .sc-weekdays{display:grid;grid-template-columns:repeat(7,1fr);background:var(--sc-bg-secondary)}.sc-week-view.sc-fullday .sc-weekday{padding:12px 8px;text-align:center;font-weight:600;font-size:.875rem;color:var(--sc-text-secondary)}.sc-borders-horizontal .sc-week-view.sc-fullday .sc-weekdays{border-bottom:1px solid var(--sc-border-color)}.sc-borders-both .sc-week-view.sc-fullday .sc-weekday,.sc-borders-vertical .sc-week-view.sc-fullday .sc-weekday{border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-week-view.sc-fullday .sc-weekday:nth-child(7n),.sc-borders-vertical .sc-week-view.sc-fullday .sc-weekday:nth-child(7n){border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-week-view.sc-fullday .sc-weekday:first-child,.sc-borders-vertical .sc-week-view.sc-fullday .sc-weekday:first-child{border-left:1px solid var(--sc-border-color)}.sc-week-view.sc-fullday .sc-days-grid{display:grid;grid-template-columns:repeat(7,1fr);grid-template-rows:1fr;flex:1;min-height:auto;overflow:visible}.sc-week-view.sc-fullday .sc-day{display:flex;flex-direction:column;min-height:80px;position:relative;cursor:pointer;transition:background-color .2s ease;overflow:visible}.sc-week-view.sc-fullday .sc-day:hover{background:var(--sc-hover-bg)}.sc-week-view.sc-fullday .sc-day-number{text-align:center;font-weight:500;margin-bottom:4px;font-size:.875rem;line-height:1.2;flex-shrink:0;padding:4px 6px 0 6px}.sc-week-view.sc-fullday .sc-day.sc-today{background:var(--sc-today-bg)}.sc-week-view.sc-fullday .sc-day.sc-today .sc-day-number{color:var(--sc-today-text);font-weight:600}.sc-week-view.sc-fullday .sc-event-week{position:relative;border-radius:3px;min-height:18px;padding:2px 6px;display:block;box-sizing:border-box;overflow:visible}.sc-week-view.sc-fullday .sc-event-text{font-size:.75rem;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;position:absolute;display:block;width:100%}.sc-week-view.sc-fullday .sc-event-week.sc-event-single{margin:1px 4px!important;border-radius:3px!important;z-index:1;position:relative}.sc-week-view.sc-fullday .sc-event-week.sc-event-start{margin:1px 0 1px 4px!important;border-radius:3px 0 0 3px!important;z-index:2;position:relative}.sc-week-view.sc-fullday .sc-event-week.sc-event-start::after{content:'';position:absolute;right:-2px;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-week-view.sc-fullday .sc-event-week.sc-event-middle{margin:1px 0!important;border-radius:0!important;z-index:2;position:relative}.sc-week-view.sc-fullday .sc-event-week.sc-event-middle::after,.sc-week-view.sc-fullday .sc-event-week.sc-event-middle::before{content:'';position:absolute;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-week-view.sc-fullday .sc-event-week.sc-event-middle::before{left:-2px}.sc-week-view.sc-fullday .sc-event-week.sc-event-middle::after{right:-2px}.sc-week-view.sc-fullday .sc-event-week.sc-event-end{margin:1px 4px 1px 0!important;border-radius:0 3px 3px 0!important;z-index:2;position:relative}.sc-week-view.sc-fullday .sc-event-week.sc-event-end::before{content:'';position:absolute;left:-2px;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-day-view{height:100%;overflow-y:auto;overflow-x:hidden;max-height:calc(100vh - 200px);display:grid;grid-template-columns:80px 1fr;align-content:start}.sc-day-view.sc-fullday{display:flex;flex-direction:column;padding:0;grid-template-columns:none}.sc-day-view.sc-fullday .sc-day-header{position:sticky;top:0;z-index:10;background:var(--sc-bg-secondary);border-bottom:1px solid var(--sc-border-color);padding:16px;text-align:center;flex-shrink:0}.sc-day-view.sc-fullday .sc-day-name{font-size:.875rem;color:var(--sc-text-secondary);font-weight:500;margin-bottom:4px}.sc-day-view.sc-fullday .sc-day-number{font-size:1.5rem;font-weight:600;color:var(--sc-text-primary)}.sc-day-view.sc-fullday .sc-day-header.sc-today .sc-day-number{color:var(--sc-today-text)}.sc-day-view.sc-fullday .sc-day-events{flex:1;padding:16px;display:flex;flex-direction:column;gap:8px;overflow-y:auto;cursor:pointer}.sc-day-view .sc-event-time{position:absolute;top:4px;left:4px;right:4px;padding:2px 6px;font-size:.75rem;line-height:1.2;border-radius:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;z-index:5;min-height:18px;display:block;box-sizing:border-box;padding-top:3px;padding-bottom:1px}.sc-day-view .sc-event{width:100%;margin:2px 4px;padding:2px 6px;font-size:.75rem;height:18px;min-width:0}.sc-day-view .sc-event-single{border-radius:3px}.sc-day-view .sc-event-start{border-radius:3px 0 0 3px}.sc-day-view .sc-event-middle{border-radius:0}.sc-day-view .sc-event-end{border-radius:0 3px 3px 0}.sc-day-view.sc-fullday .sc-event-start{margin-right:0}.sc-day-view.sc-fullday .sc-event-middle{margin-left:0;margin-right:0}.sc-day-view.sc-fullday .sc-event-end{margin-left:0}.sc-day-view.sc-fullday .sc-event-day{position:relative;padding:2px 6px;border-radius:3px;margin:1px 4px;min-height:18px;box-sizing:border-box;display:block}.sc-day-view.sc-fullday .sc-event-text{font-size:.75rem;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;position:absolute;display:block;width:100%}@media (max-width:768px){.sc-header{grid-template-columns:1fr;grid-template-rows:auto auto auto;gap:8px;justify-items:center}.sc-view-switcher{grid-row:1;justify-self:center}.sc-title-container{grid-row:2;justify-self:center}.sc-title{font-size:1.125rem}.sc-nav{grid-row:3;justify-self:center}.sc-days-grid{min-height:360px;grid-template-rows:repeat(6,minmax(60px,1fr))}.sc-day-number{font-size:.75rem}.sc-day-count-mode .sc-day-number{font-size:1rem}.sc-event-count{font-size:.625rem;padding:1px 4px;min-width:14px}.sc-event-month{font-size:.6rem;padding:1px 3px;height:14px}.sc-day-view .sc-event-time,.sc-week-view .sc-event-time{top:3px;left:3px;right:3px;padding:1px 4px;padding-top:2px;padding-bottom:1px;font-size:.65rem;min-height:16px}.sc-weekday{padding:8px 4px;font-size:.75rem}.sc-week-header{min-height:50px}.sc-week-view{grid-template-columns:60px repeat(7,1fr)}.sc-week-view.sc-fullday{grid-template-columns:repeat(7,1fr)}.sc-day-header{padding:8px 4px}.sc-day-name{font-size:.625rem}.sc-day-header .sc-day-number{font-size:1rem}.sc-day-header.sc-today .sc-day-number{color:var(--sc-today-text);font-weight:600}.sc-time-slot{height:40px;padding:4px;font-size:.625rem}.sc-hour-slot{height:40px}.sc-day-view{grid-template-columns:60px 1fr;max-height:calc(100vh - 160px)}.sc-week-view{max-height:calc(100vh - 160px)}}@media (max-width:480px){.sc-calendar{border-radius:0;box-shadow:none;border:1px solid #e9ecef}.sc-header{padding:12px}.sc-btn{padding:6px 8px;font-size:.75rem;min-width:30px}.sc-title{font-size:1rem}.sc-day{min-height:50px;padding:2px}.sc-days-grid{min-height:300px;grid-template-rows:repeat(6,minmax(50px,1fr))}.sc-day-count-mode .sc-day-number{font-size:.875rem}.sc-event-count{font-size:.5rem;padding:1px 3px;min-width:12px}.sc-weekday{padding:6px 2px;font-size:.625rem}.sc-week-header{min-height:40px}.sc-week-view{grid-template-columns:50px repeat(7,1fr);max-height:calc(100vh - 140px)}.sc-week-view.sc-fullday{grid-template-columns:repeat(7,1fr)}.sc-time-slot{height:30px;padding:2px;font-size:.5rem}.sc-hour-slot{height:30px}.sc-day-view{grid-template-columns:50px 1fr;max-height:calc(100vh - 140px)}.sc-day-view .sc-event-time,.sc-week-view .sc-event-time{top:2px;left:2px;right:2px;padding:1px 3px;padding-top:2px;padding-bottom:0;font-size:.5rem;min-height:14px}}.sc-borders-both .sc-month-view .sc-days-grid{border-left:1px solid var(--sc-border-color);border-bottom:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-month-view .sc-days-grid{border-left:1px solid var(--sc-border-color);border-bottom:none}.sc-borders-horizontal .sc-month-view .sc-days-grid{border-left:none;border-bottom:1px solid var(--sc-border-color)}.sc-borders-none .sc-month-view .sc-days-grid{border-left:none;border-bottom:none}.sc-borders-both .sc-day{border-right:1px solid var(--sc-border-color);border-top:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-day{border-right:1px solid var(--sc-border-color);border-top:none}.sc-borders-horizontal .sc-day{border-right:none;border-top:1px solid var(--sc-border-color)}.sc-borders-none .sc-day{border-right:none;border-top:none}.sc-borders-both .sc-day-column,.sc-borders-both .sc-time-slot{border-right:1px solid var(--sc-border-color);border-bottom:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-day-column,.sc-borders-vertical .sc-time-slot{border-right:1px solid var(--sc-border-color);border-bottom:none}.sc-borders-horizontal .sc-day-column,.sc-borders-horizontal .sc-time-slot{border-right:none;border-bottom:1px solid var(--sc-border-color)}.sc-borders-none .sc-day-column,.sc-borders-none .sc-time-slot{border-right:none;border-bottom:none}.sc-week-view:not(.sc-fullday) .sc-time-slot{border-bottom:1px solid var(--sc-border-color)!important}.sc-week-view:not(.sc-fullday) .sc-day-column{border-bottom:1px solid var(--sc-border-color)!important}.sc-borders-both .sc-week-view.sc-fullday .sc-day{border-right:1px solid var(--sc-border-color);border-bottom:1px solid var(--sc-border-color)}.sc-borders-both .sc-week-view.sc-fullday .sc-day:first-child{border-left:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-week-view.sc-fullday .sc-day{border-right:1px solid var(--sc-border-color);border-bottom:none}.sc-borders-horizontal .sc-week-view.sc-fullday .sc-day{border-right:none;border-bottom:1px solid var(--sc-border-color)}.sc-borders-none .sc-week-view.sc-fullday .sc-day{border-right:none;border-bottom:none}.sc-borders-both .sc-day-header,.sc-borders-both .sc-time-column-header{border-right:1px solid var(--sc-border-color);border-bottom:1px solid var(--sc-border-color)}.sc-borders-both .sc-day-header:first-child,.sc-borders-both .sc-time-column-header:first-child,.sc-borders-vertical .sc-day-header:first-child,.sc-borders-vertical .sc-time-column-header:first-child{border-left:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-day-header,.sc-borders-vertical .sc-time-column-header{border-right:1px solid var(--sc-border-color);border-bottom:none}.sc-borders-horizontal .sc-day-header,.sc-borders-horizontal .sc-time-column-header{border-right:none;border-bottom:1px solid var(--sc-border-color)}.sc-borders-none .sc-day-header,.sc-borders-none .sc-time-column-header{border-right:none;border-bottom:none}.sc-borders-both .sc-week-view:not(.sc-fullday){border-left:1px solid var(--sc-border-color);border-right:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-week-view:not(.sc-fullday){border-left:1px solid var(--sc-border-color);border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-day-header:nth-child(8n),.sc-borders-vertical .sc-day-header:nth-child(8n){border-right:none}.sc-borders-both .sc-half-hour-line,.sc-borders-horizontal .sc-half-hour-line{border-top:1px dotted var(--sc-border-color)}.sc-borders-none .sc-half-hour-line,.sc-borders-vertical .sc-half-hour-line{border-top:none}.sc-week-view:not(.sc-fullday) .sc-half-hour-line{border-top:1px dotted var(--sc-border-color)!important}.sc-day-view:not(.sc-fullday) .sc-time-slot{border-bottom:1px solid var(--sc-border-color)!important}.sc-borders-both .sc-day-view:not(.sc-fullday) .sc-time-slot,.sc-borders-vertical .sc-day-view:not(.sc-fullday) .sc-time-slot{border-left:1px solid var(--sc-border-color)!important}.sc-day-view:not(.sc-fullday) .sc-day-column{border-bottom:1px solid var(--sc-border-color)!important}.sc-day-view:not(.sc-fullday) .sc-half-hour-line{border-top:1px dotted var(--sc-border-color)!important}.sc-borders-both .sc-month-view .sc-days-grid,.sc-borders-vertical .sc-month-view .sc-days-grid{border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-day:nth-child(7n),.sc-borders-vertical .sc-day:nth-child(7n){border-right:none}:root{--sc-event-border-color:#6c757d}.sc-event-borders .sc-event-day:not(.sc-event-placeholder),.sc-event-borders .sc-event-month:not(.sc-event-placeholder),.sc-event-borders .sc-event-time:not(.sc-event-placeholder),.sc-event-borders .sc-event-week:not(.sc-event-placeholder){border:1px solid var(--sc-event-border-color)}.sc-event-borders .sc-event-single:not(.sc-event-placeholder){border:1px solid var(--sc-event-border-color)}.sc-event-borders .sc-event-start:not(.sc-event-placeholder){border-left:1px solid var(--sc-event-border-color);border-top:1px solid var(--sc-event-border-color);border-bottom:1px solid var(--sc-event-border-color)}.sc-event-borders .sc-event-middle:not(.sc-event-placeholder){border-top:1px solid var(--sc-event-border-color);border-bottom:1px solid var(--sc-event-border-color)}.sc-event-borders .sc-event-end:not(.sc-event-placeholder){border-right:1px solid var(--sc-event-border-color);border-top:1px solid var(--sc-event-border-color);border-bottom:1px solid var(--sc-event-border-color)}.sc-week-view .sc-event-week{border-right:none}.sc-event-no-borders .sc-event-day,.sc-event-no-borders .sc-event-month,.sc-event-no-borders .sc-event-time,.sc-event-no-borders .sc-event-week{border:none!important}@media print{.sc-calendar{box-shadow:none;border:1px solid #000}.sc-header{background:#fff!important;-webkit-print-color-adjust:exact;print-color-adjust:exact}.sc-btn{display:none}.sc-view-switcher{display:none}}
@@ -1,10 +0,0 @@
1
- /**
2
- * SimpleCalendarJs v2.0.2
3
- * A clean, modern, and feature-rich JavaScript calendar component with zero dependencies
4
- *
5
- * @author Pedro Lopes <simplecalendarjs@gmail.com>
6
- * @homepage https://www.simplecalendarjs.com
7
- * @license SEE LICENSE IN LICENSE
8
- * @repository https://github.com/pclslopes/SimpleCalendarJs
9
- */
10
- class t{constructor(t,e={}){this.container="string"==typeof t?document.querySelector(t):t;const s=this.mergeColors({background:"#ffffff",backgroundSecondary:"#f8f9fa",backgroundTertiary:"#ecf0f1",text:"#212529",textSecondary:"#6c757d",textMuted:"#adb5bd",border:"#e9ecef",borderLight:"#dee2e6",accent:"#4c6f94",accentHover:"#0056b3",todayBg:"#e3f2fd",todayText:"#dc3545",hoverBg:"#f8f9fa",dark:{background:"#2d2d2d",backgroundSecondary:"#3a3a3a",backgroundTertiary:"#4a4a4a",text:"#ffffff",textSecondary:"#cccccc",textMuted:"#888888",border:"#444444",borderLight:"#555555",accent:"#4a90e2",accentHover:"#357abd",todayBg:"#1a2f4a",todayText:"#ff6b6b",hoverBg:"#2d2d2d"}},e.colors||{});this.options={view:"month",date:new Date,events:[],fulldayMode:!1,startHour:6,endHour:22,timeSlotMinutes:30,dayClick:null,eventClick:null,changeState:null,gridBorders:"both",eventBorders:!1,eventBorderColor:"#6c757d",defaultEventColor:"#4c6f94",showMonthButton:!0,showWeekButton:!0,showDayButton:!0,showNavigation:!0,weekdays:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],showWeekdayChars:null,labels:{month:"Month",week:"Week",day:"Day",events:"events",event:"event",before:"Before",after:"After"},colors:s,...e,colors:s},this.currentDate=new Date(this.options.date),this.view=this.options.view,this.events=this.options.events,this.eventsCallback="function"==typeof this.options.events?this.options.events:null,this.fulldayMode=this.options.fulldayMode,this.fetchedRanges=[],this.allCachedEvents=[],this.init()}mergeColors(t,e){const s={...t};return Object.keys(e).forEach(t=>{"dark"!==t&&void 0!==e[t]&&(s[t]=e[t])}),e.dark&&(s.dark={...t.dark},Object.keys(e.dark).forEach(t=>{void 0!==e.dark[t]&&(s.dark[t]=e.dark[t])})),s}init(){this.container.innerHTML=this.getCalendarTemplate(),this.applyGridBorders(),this.applyEventBorders(),this.applyEventColor(),this.applyViewButtonVisibility(),this.applyNavigationVisibility(),this.applyCustomColors(),this.bindEvents(),this.render()}getWeekdayDisplay(t){const e=this.options.weekdays[t];return null===this.options.showWeekdayChars?e:e.substring(0,this.options.showWeekdayChars)}triggerChangeState(){if(this.options.changeState){const t={view:this.view,date:new Date(this.currentDate),fulldayMode:this.fulldayMode,startHour:this.options.startHour,endHour:this.options.endHour,timeSlotMinutes:this.options.timeSlotMinutes};this.options.changeState(t)}}applyGridBorders(){const t=this.container.querySelector(".sc-calendar");t&&(t.classList.remove("sc-borders-both","sc-borders-vertical","sc-borders-horizontal","sc-borders-none"),t.classList.add(`sc-borders-${this.options.gridBorders}`))}applyEventBorders(){const t=this.container.querySelector(".sc-calendar");t&&(t.classList.remove("sc-event-borders","sc-event-no-borders"),this.options.eventBorders?(t.classList.add("sc-event-borders"),t.style.setProperty("--sc-event-border-color",this.options.eventBorderColor)):t.classList.add("sc-event-no-borders"))}applyEventColor(){const t=this.container.querySelector(".sc-calendar");t&&t.style.setProperty("--sc-event-color",this.options.defaultEventColor)}applyViewButtonVisibility(){const t=this.container.querySelector('[data-view="month"]'),e=this.container.querySelector('[data-view="week"]'),s=this.container.querySelector('[data-view="day"]');t&&(t.style.display=this.options.showMonthButton?"flex":"none"),e&&(e.style.display=this.options.showWeekButton?"flex":"none"),s&&(s.style.display=this.options.showDayButton?"flex":"none");const n=this.container.querySelector(".sc-view-switcher");if(n){const t=this.options.showMonthButton||this.options.showWeekButton||this.options.showDayButton;n.style.display=t?"flex":"none"}}applyNavigationVisibility(){const t=this.container.querySelector(".sc-nav");t&&(t.style.visibility=this.options.showNavigation?"visible":"hidden")}applyCustomColors(){if(!this.container.querySelector(".sc-calendar"))return;const t=this.options.colors,e=(t,e)=>void 0!==e?`--${t}: ${e} !important;`:"",s=`\n .sc-calendar {\n ${e("sc-bg-primary",t.background)}\n ${e("sc-bg-secondary",t.backgroundSecondary)}\n ${e("sc-bg-tertiary",t.backgroundTertiary)}\n ${e("sc-text-primary",t.text)}\n ${e("sc-text-secondary",t.textSecondary)}\n ${e("sc-text-muted",t.textMuted)}\n ${e("sc-border-color",t.border)}\n ${e("sc-border-light",t.borderLight)}\n ${e("sc-accent-color",t.accent)}\n ${e("sc-accent-hover",t.accentHover)}\n ${e("sc-today-bg",t.todayBg)}\n ${e("sc-today-text",t.todayText)}\n ${e("sc-hover-bg",t.hoverBg)}\n }\n\n ${t.dark?`\n [data-theme="dark"] .sc-calendar {\n ${e("sc-bg-primary",t.dark.background)}\n ${e("sc-bg-secondary",t.dark.backgroundSecondary)}\n ${e("sc-bg-tertiary",t.dark.backgroundTertiary)}\n ${e("sc-text-primary",t.dark.text)}\n ${e("sc-text-secondary",t.dark.textSecondary)}\n ${e("sc-text-muted",t.dark.textMuted)}\n ${e("sc-border-color",t.dark.border)}\n ${e("sc-border-light",t.dark.borderLight)}\n ${e("sc-accent-color",t.dark.accent)}\n ${e("sc-accent-hover",t.dark.accentHover)}\n ${e("sc-today-bg",t.dark.todayBg)}\n ${e("sc-today-text",t.dark.todayText)}\n ${e("sc-hover-bg",t.dark.hoverBg)}\n }\n `:""}\n `;let n=document.getElementById("sc-custom-colors");n||(n=document.createElement("style"),n.id="sc-custom-colors",document.head.appendChild(n)),n.textContent=s}getCalendarTemplate(){return`\n <div class="sc-calendar">\n <div class="sc-header">\n <div class="sc-nav">\n <button class="sc-btn sc-prev">←</button>\n <button class="sc-btn sc-next">→</button>\n </div>\n <div class="sc-title-container">\n <h2 class="sc-title">\n <span class="sc-title-month"></span>\n <span class="sc-title-year"></span>\n </h2>\n <div class="sc-month-dropdown" style="display: none;">\n ${this.options.months.map((t,e)=>`<div class="sc-month-option" data-month="${e}">${t}</div>`).join("")}\n </div>\n <div class="sc-year-dropdown" style="display: none;">\n </div>\n </div>\n <div class="sc-view-switcher">\n <button class="sc-btn sc-view-btn ${"month"===this.view?"sc-active":""}" data-view="month">${this.options.labels.month}</button>\n <button class="sc-btn sc-view-btn ${"week"===this.view?"sc-active":""}" data-view="week">${this.options.labels.week}</button>\n <button class="sc-btn sc-view-btn ${"day"===this.view?"sc-active":""}" data-view="day">${this.options.labels.day}</button>\n </div>\n </div>\n <div class="sc-content">\n <div class="sc-view-container"></div>\n </div>\n </div>\n `}bindEvents(){const t=this.container.querySelector(".sc-prev"),e=this.container.querySelector(".sc-next"),s=this.container.querySelectorAll(".sc-view-btn");t.addEventListener("click",()=>this.navigate("prev")),e.addEventListener("click",()=>this.navigate("next")),s.forEach(t=>{t.addEventListener("click",t=>{const e=t.target.dataset.view;this.setView(e)})}),this.container.addEventListener("click",t=>{this.handleCalendarClick(t)}),this.outsideClickBound||(document.addEventListener("click",t=>{t.target.closest(".sc-title-container")||(this.hideMonthDropdown(),this.hideYearDropdown())}),this.outsideClickBound=!0)}bindMonthViewDropdowns(){if("month"!==this.view)return;const t=this.container.querySelector(".sc-title-month"),e=this.container.querySelector(".sc-title-year");if(t){const e=t.cloneNode(!0);t.parentNode.replaceChild(e,t),e.addEventListener("click",t=>{t.stopPropagation(),this.hideYearDropdown(),this.toggleMonthDropdown()})}if(e){const t=e.cloneNode(!0);e.parentNode.replaceChild(t,e),t.addEventListener("click",t=>{t.stopPropagation(),this.hideMonthDropdown(),this.toggleYearDropdown()})}this.generateYearOptions(),this.dropdownOptionsHandler&&this.container.removeEventListener("click",this.dropdownOptionsHandler),this.dropdownOptionsHandler=t=>{if(t.target.classList.contains("sc-month-option")){const e=parseInt(t.target.getAttribute("data-month"));this.setMonth(e),this.hideMonthDropdown()}else if(t.target.classList.contains("sc-year-option")){const e=parseInt(t.target.getAttribute("data-year"));this.setYear(e),this.hideYearDropdown()}},this.container.addEventListener("click",this.dropdownOptionsHandler);this.container.querySelectorAll(".sc-month-option"),this.container.querySelectorAll(".sc-year-option")}calculatePreciseTime(t,e){const s=t.getAttribute("data-time");if(!s)return null;const n=t.getBoundingClientRect(),i=e.clientY-n.top>n.height/2,a=this.parseTimeString(s);return a?(i&&a.setMinutes(a.getMinutes()+30),this.formatTime(a)):s}handleCalendarClick(t){if(t.target.classList.contains("sc-title-month")||t.target.classList.contains("sc-title-year")||t.target.classList.contains("sc-month-option")||t.target.classList.contains("sc-year-option")||t.target.closest(".sc-month-dropdown")||t.target.closest(".sc-year-dropdown"))return;const e=t.target.closest("[data-event-id]");if(e)return t.stopPropagation(),void this.handleEventClick(e,t);const s=t.target.closest("[data-date]");s&&this.handleDayClick(s,t)}handleEventClick(t,e){if(!this.options.eventClick)return;const s=t.getAttribute("data-event-id"),n=t.closest("[data-date]"),i=n?n.getAttribute("data-date"):null;if(s&&i){const t=new Date(i),a=this.getAllEventsForRange().find(t=>t.id==s);if(a){const s={date:t,time:this.calculatePreciseTime(n,e)};this.options.eventClick(a,s,e)}}}handleDayClick(t,e){if(!this.options.dayClick)return;const s=t.getAttribute("data-date");if(s){const n={date:new Date(s),time:this.calculatePreciseTime(t,e)};this.options.dayClick(n,e)}}navigate(t){const e="next"===t?1:-1;switch(this.view){case"month":this.currentDate.setMonth(this.currentDate.getMonth()+e);break;case"week":this.currentDate.setDate(this.currentDate.getDate()+7*e);break;case"day":this.currentDate.setDate(this.currentDate.getDate()+e)}this.render(),this.triggerChangeState()}toggleMonthDropdown(){if("month"!==this.view)return;const t=this.container.querySelector(".sc-month-dropdown");if(t){"none"!==t.style.display?this.hideMonthDropdown():(this.updateMonthDropdownSelection(),t.style.display="block",this.scrollToActiveMonth())}}hideMonthDropdown(){const t=this.container.querySelector(".sc-month-dropdown");t&&(t.style.display="none")}updateMonthDropdownSelection(){const t=this.container.querySelectorAll(".sc-month-option"),e=this.currentDate.getMonth();t.forEach((t,s)=>{t.classList.remove("sc-current"),s===e&&t.classList.add("sc-current")})}toggleYearDropdown(){if("month"!==this.view)return;const t=this.container.querySelector(".sc-year-dropdown");if(t){"none"!==t.style.display?this.hideYearDropdown():(this.generateYearOptions(),this.updateYearDropdownSelection(),t.style.display="block",this.scrollToActiveYear())}}hideYearDropdown(){const t=this.container.querySelector(".sc-year-dropdown");t&&(t.style.display="none")}generateYearOptions(){const t=this.container.querySelector(".sc-year-dropdown"),e=this.currentDate.getFullYear(),s=e+10;if(t){let n="";for(let t=e-10;t<=s;t++)n+=`<div class="sc-year-option" data-year="${t}">${t}</div>`;t.innerHTML=n}}updateYearDropdownSelection(){const t=this.container.querySelectorAll(".sc-year-option"),e=this.currentDate.getFullYear();t.forEach(t=>{t.classList.remove("sc-current"),parseInt(t.getAttribute("data-year"))===e&&t.classList.add("sc-current")})}setYear(t){this.currentDate.setFullYear(t),this.render(),this.triggerChangeState()}scrollToActiveMonth(){const t=this.container.querySelector(".sc-month-dropdown"),e=t?.querySelector(".sc-month-option.sc-current");t&&e&&setTimeout(()=>{t.getBoundingClientRect(),e.getBoundingClientRect(),t.scrollTop;const s=e.offsetTop-t.clientHeight/2+e.offsetHeight/2;t.scrollTop=s},0)}scrollToActiveYear(){const t=this.container.querySelector(".sc-year-dropdown"),e=t?.querySelector(".sc-year-option.sc-current");t&&e&&setTimeout(()=>{t.getBoundingClientRect(),e.getBoundingClientRect(),t.scrollTop;const s=e.offsetTop-t.clientHeight/2+e.offsetHeight/2;t.scrollTop=s},0)}setMonth(t){this.currentDate.setMonth(t),this.render(),this.triggerChangeState()}setView(t){this.view=t,this.container.querySelectorAll(".sc-view-btn").forEach(t=>{t.classList.remove("sc-active")}),this.container.querySelector(`[data-view="${t}"]`).classList.add("sc-active"),this.hideMonthDropdown(),this.hideYearDropdown(),this.render(),this.triggerChangeState()}render(){this.updateTitle(),this.renderView(),"month"===this.view&&this.bindMonthViewDropdowns()}updateTitle(){const t=this.container.querySelector(".sc-title"),e=this.container.querySelector(".sc-title-month"),s=this.container.querySelector(".sc-title-year");switch(this.view){case"month":e&&s?(e.textContent=this.options.months[this.currentDate.getMonth()],s.textContent=this.currentDate.getFullYear()):t.innerHTML=`<span class="sc-title-month">${this.options.months[this.currentDate.getMonth()]}</span> <span class="sc-title-year">${this.currentDate.getFullYear()}</span>`;break;case"week":const n=this.getWeekStart(this.currentDate),i=new Date(n);i.setDate(i.getDate()+6),t.textContent=`${this.options.months[n.getMonth()]} ${n.getDate()} - ${this.options.months[i.getMonth()]} ${i.getDate()}, ${n.getFullYear()}`;break;case"day":t.textContent=`${this.options.weekdays[this.currentDate.getDay()]}, ${this.options.months[this.currentDate.getMonth()]} ${this.currentDate.getDate()}, ${this.currentDate.getFullYear()}`}}renderView(){const t=this.container.querySelector(".sc-view-container");switch(this.view){case"month":t.innerHTML=this.renderMonthView();break;case"week":t.innerHTML=this.renderWeekView();break;case"day":t.innerHTML=this.renderDayView()}}renderMonthView(){const t=this.currentDate.getFullYear(),e=this.currentDate.getMonth(),s=new Date(t,e,1),n=new Date(s);n.setDate(n.getDate()-s.getDay());let i=`\n <div class="sc-month-view ${this.fulldayMode?"sc-fullday":""}">\n <div class="sc-weekdays">\n ${this.options.weekdays.map((t,e)=>`<div class="sc-weekday">${this.getWeekdayDisplay(e)}</div>`).join("")}\n </div>\n <div class="sc-days-grid">\n `;for(let t=0;t<6;t++)for(let s=0;s<7;s++){const a=new Date(n);a.setDate(n.getDate()+7*t+s);const o=a.getMonth()===e,r=this.isToday(a),c=this.getEventsForDate(a);if(this.fulldayMode){const t=this.addEventPlaceholders(c,a);i+=`\n <div class="sc-day ${o?"sc-current-month":"sc-other-month"} ${r?"sc-today":""}" data-date="${a.toISOString()}">\n <div class="sc-day-number">${a.getDate()}</div>\n ${t.map(t=>{if(t.isPlaceholder)return'<div class="sc-event-month sc-event-single sc-event-placeholder">&nbsp;</div>';const e=this.getEventPosition(t.event,a),s=e.isSingle?"sc-event-single":e.isStart?"sc-event-start":e.isEnd?"sc-event-end":"sc-event-middle",n=e.isSingle||e.isStart?t.event.title:"",i=this.applyEventStyles(t.event);return`<div class="sc-event-month ${s}" data-event-id="${t.event.id}" ${i}><span class="sc-event-text">${n}</span></div>`}).join("")}\n </div>\n `}else{const t=c.length,e=0===t?"":1===t?`1 ${this.options.labels.event}`:`${t} ${this.options.labels.events}`;i+=`\n <div class="sc-day sc-day-count-mode ${o?"sc-current-month":"sc-other-month"} ${r?"sc-today":""}" data-date="${a.toISOString()}">\n <div class="sc-day-content">\n <div class="sc-day-number">${a.getDate()}</div>\n ${e?`<div class="sc-event-count">${e}</div>`:""}\n </div>\n </div>\n `}}return i+="\n </div>\n </div>\n ",i}renderWeekView(){const t=this.getWeekStart(this.currentDate),e=[];for(let s=0;s<7;s++){const n=new Date(t);n.setDate(n.getDate()+s),e.push(n)}if(this.fulldayMode)return this.renderWeekFullday(e);const s=this.generateTimeSlots();return`\n <div class="sc-week-view">\n <div class="sc-time-column-header"></div>\n ${e.map(t=>`\n <div class="sc-day-header ${this.isToday(t)?"sc-today":""}">\n <div class="sc-day-name">${this.getWeekdayDisplay(t.getDay())}</div>\n <div class="sc-day-number">${t.getDate()}</div>\n </div>\n `).join("")}\n ${s.map(t=>`\n <div class="sc-time-slot">${t}</div>\n ${e.map(e=>{const s=this.getEventsForTimeSlot(e,t);return`\n <div class="sc-day-column ${this.isToday(e)?"sc-today":""}" data-date="${e.toISOString()}" data-time="${t}">\n <div class="sc-half-hour-line"></div>\n ${s.map(t=>{const e=this.applyEventStyles(t);return`<div class="sc-event-time" data-event-id="${t.id}" ${e}>${t.title}</div>`}).join("")}\n </div>\n `}).join("")}\n `).join("")}\n </div>\n `}renderWeekFullday(t){return`\n <div class="sc-week-view sc-fullday">\n <div class="sc-weekdays">\n ${t.map(t=>`<div class="sc-weekday">${this.getWeekdayDisplay(t.getDay())}</div>`).join("")}\n </div>\n <div class="sc-days-grid">\n ${t.map(t=>{const e=this.isToday(t),s=this.getEventsForDate(t),n=this.addEventPlaceholders(s,t);return`\n <div class="sc-day ${e?"sc-today":""}" data-date="${t.toISOString()}">\n <div class="sc-day-number">${t.getDate()}</div>\n ${n.map(e=>{if(e.isPlaceholder)return'<div class="sc-event-week sc-event-single sc-event-placeholder">&nbsp;</div>';const s=this.getEventPosition(e.event,t),n=s.isSingle?"sc-event-single":s.isStart?"sc-event-start":s.isEnd?"sc-event-end":"sc-event-middle",i=s.isSingle||s.isStart?e.event.title:"",a=this.applyEventStyles(e.event);return`<div class="sc-event-week ${n}" data-event-id="${e.event.id}" ${a}><span class="sc-event-text">${i}</span></div>`}).join("")}\n </div>\n `}).join("")}\n </div>\n </div>\n `}renderDayView(){const t=new Date(this.currentDate);if(this.fulldayMode){const e=this.getEventsForDate(t);return`\n <div class="sc-day-view sc-fullday">\n <div class="sc-day-header ${this.isToday(t)?"sc-today":""}">\n <div class="sc-day-name">${this.options.weekdays[t.getDay()]}</div>\n <div class="sc-day-number">${t.getDate()}</div>\n </div>\n <div class="sc-day-events" data-date="${t.toISOString()}">\n ${e.map(e=>{const s=this.getEventPosition(e,t),n=s.isSingle?"sc-event-single":s.isStart?"sc-event-start":s.isEnd?"sc-event-end":"sc-event-middle",i=e.title,a=this.applyEventStyles(e);return`<div class="sc-event-day ${n}" data-event-id="${e.id}" ${a}><span class="sc-event-text">${i}</span></div>`}).join("")}\n </div>\n </div>\n `}const e=this.generateTimeSlots();return`\n <div class="sc-day-view">\n <div class="sc-time-column-header"></div>\n <div class="sc-day-header ${this.isToday(t)?"sc-today":""}">\n <div class="sc-day-name">${this.options.weekdays[t.getDay()]}</div>\n <div class="sc-day-number">${t.getDate()}</div>\n </div>\n ${e.map(e=>{const s=this.getEventsForTimeSlot(t,e);return`\n <div class="sc-time-slot">${e}</div>\n <div class="sc-day-column ${this.isToday(t)?"sc-today":""}" data-date="${t.toISOString()}" data-time="${e}">\n <div class="sc-half-hour-line"></div>\n ${s.map(t=>{const e=this.applyEventStyles(t);return`<div class="sc-event-time" data-event-id="${t.id}" ${e}>${t.title}</div>`}).join("")}\n </div>\n `}).join("")}\n </div>\n `}generateTimeSlots(){const t=[],e=this.options.startHour,s=this.options.endHour,n=new Date;n.setHours(e,0,0,0),t.push(`${this.options.labels.before} ${this.formatTime(n)}`);for(let n=e;n<=s;n++){const e=new Date;e.setHours(n,0,0,0),t.push(this.formatTime(e))}const i=new Date;return i.setHours(s,0,0,0),t.push(`${this.options.labels.after} ${this.formatTime(i)}`),t}formatTime(t){return t.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}getWeekStart(t){const e=new Date(t);return e.setDate(e.getDate()-e.getDay()),e}getVisibleDateRange(){const t=this.currentDate.getFullYear(),e=this.currentDate.getMonth();switch(this.view){case"month":const s=new Date(t,e,1),n=new Date(s);n.setDate(n.getDate()-s.getDay());const i=new Date(n);return i.setDate(i.getDate()+41),{startDate:n,endDate:i};case"week":const a=this.getWeekStart(this.currentDate),o=new Date(a);return o.setDate(o.getDate()+6),{startDate:a,endDate:o};case"day":const r=new Date(this.currentDate);r.setHours(0,0,0,0);const c=new Date(r);return c.setHours(23,59,59,999),{startDate:r,endDate:c}}}isToday(t){const e=new Date;return t.toDateString()===e.toDateString()}isRangeCovered(t,e){for(const s of this.fetchedRanges)if(s.startDate<=t&&s.endDate>=e)return!0;return!1}getMissingRanges(t,e){const s=[];let n=new Date(t);const i=[...this.fetchedRanges].sort((t,e)=>t.startDate-e.startDate);for(const t of i){if(n<t.startDate&&n<=e){const i=new Date(Math.min(t.startDate.getTime()-864e5,e.getTime()));i>=n&&s.push({startDate:new Date(n),endDate:i})}t.endDate>=n&&(n=new Date(t.endDate.getTime()+864e5))}return n<=e&&s.push({startDate:new Date(n),endDate:new Date(e)}),s}addFetchedRange(t,e,s){const n={startDate:new Date(t),endDate:new Date(e),events:s};this.allCachedEvents=this.allCachedEvents.concat(s),this.fetchedRanges.push(n),this.mergeOverlappingRanges()}mergeOverlappingRanges(){this.fetchedRanges.sort((t,e)=>t.startDate-e.startDate);const t=[];for(const e of this.fetchedRanges)if(0===t.length||t[t.length-1].endDate<e.startDate)t.push(e);else{const s=t[t.length-1];s.endDate=new Date(Math.max(s.endDate.getTime(),e.endDate.getTime())),s.events=s.events.concat(e.events)}this.fetchedRanges=t,this.allCachedEvents=[];for(const t of this.fetchedRanges)this.allCachedEvents=this.allCachedEvents.concat(t.events)}getAllEventsForRange(){if(this.eventsCallback){const t=this.getVisibleDateRange();if(this.isRangeCovered(t.startDate,t.endDate))return this.allCachedEvents;const e=this.getMissingRanges(t.startDate,t.endDate);for(const t of e){const e=this.eventsCallback(t);this.addFetchedRange(t.startDate,t.endDate,e)}return this.allCachedEvents}return this.events||[]}getEventsForDate(t){return this.getAllEventsForRange().filter(e=>{const s=new Date(e.startDate||e.date),n=new Date(e.endDate||e.date),i=new Date(t);return i.setHours(0,0,0,0),s.setHours(0,0,0,0),n.setHours(0,0,0,0),i>=s&&i<=n}).sort((t,e)=>{const s=this.isMultiDayEvent(t),n=this.isMultiDayEvent(e);return s&&!n?-1:!s&&n?1:0})}getEventsForTimeSlot(t,e){if(this.fulldayMode)return this.getEventsForDate(t);return this.getEventsForDate(t).filter(t=>{if(!t.time)return!1;const s=this.parseTimeString(t.time);if(!s)return!1;const n=s.getHours(),i=this.options.startHour,a=this.options.endHour;if(e.startsWith(this.options.labels.before))return n<i;if(e.startsWith(this.options.labels.after))return n>a;const o=this.parseTimeString(e);if(!o)return!1;return n===o.getHours()})}parseTimeString(t){try{const e=new Date,[s,n]=t.split(" "),[i,a]=s.split(":").map(Number);let o=i;return"PM"===n&&12!==i&&(o+=12),"AM"===n&&12===i&&(o=0),e.setHours(o,a||0,0,0),e}catch(t){return null}}isMultiDayEvent(t){const e=new Date(t.startDate||t.date),s=new Date(t.endDate||t.date);return e.setHours(0,0,0,0),s.setHours(0,0,0,0),e.getTime()!==s.getTime()}addEventPlaceholders(t,e){if(!this.fulldayMode)return t.map(t=>({event:t,isPlaceholder:!1}));const s=this.getWeekStart(e),n=[];for(let t=0;t<7;t++){const e=new Date(s);e.setDate(e.getDate()+t),n.push(e)}const i=new Map;n.forEach(t=>{this.getEventsForDate(t).filter(t=>this.isMultiDayEvent(t)).forEach(t=>{i.has(t.id)||i.set(t.id,t)})});const a=Array.from(i.values()).sort((t,e)=>new Date(t.startDate||t.date)-new Date(e.startDate||e.date)),o=[],r=t.filter(t=>this.isMultiDayEvent(t)),c=t.filter(t=>!this.isMultiDayEvent(t));return a.forEach(t=>{const n=r.find(e=>e.id===t.id);if(n)o.push({event:n,isPlaceholder:!1});else{const n=new Date(t.startDate||t.date),i=new Date(t.endDate||t.date);n.setHours(0,0,0,0),i.setHours(0,0,0,0);const a=new Date(e);a.setHours(0,0,0,0);const r=new Date(s);r.setHours(0,0,0,0);const c=new Date(s);c.setDate(c.getDate()+6),c.setHours(0,0,0,0);n<=c&&(i>=r&&i<=c)&&a>i&&o.push({isPlaceholder:!0,eventId:t.id})}}),c.forEach(t=>{o.push({event:t,isPlaceholder:!1})}),o}getEventPosition(t,e){const s=new Date(t.startDate||t.date),n=new Date(t.endDate||t.date),i=new Date(e);s.setHours(0,0,0,0),n.setHours(0,0,0,0),i.setHours(0,0,0,0);return{isStart:i.getTime()===s.getTime(),isEnd:i.getTime()===n.getTime(),isMiddle:i>s&&i<n,isSingle:s.getTime()===n.getTime()}}addEvent(t){this.eventsCallback||(this.events.push(t),this.render())}removeEvent(t){this.eventsCallback||(this.events=this.events.filter(e=>e.id!==t),this.render())}setEvents(t){"function"==typeof t?(this.eventsCallback=t,this.events=[]):(this.events=t||[],this.eventsCallback=null),this.fetchedRanges=[],this.allCachedEvents=[],this.render()}setFulldayMode(t){this.fulldayMode=t,this.render(),this.triggerChangeState()}goToDate(t){this.currentDate=new Date(t),this.render(),this.triggerChangeState()}goToToday(){this.currentDate=new Date,this.render(),this.triggerChangeState()}setGridBorders(t){this.options.gridBorders=t,this.applyGridBorders()}setEventBorders(t){this.options.eventBorders=t,this.applyEventBorders(),this.render()}setEventBorderColor(t){this.options.eventBorderColor=t,this.applyEventBorders(),this.render()}setShowMonthButton(t){this.options.showMonthButton=t,this.applyViewButtonVisibility()}setShowWeekButton(t){this.options.showWeekButton=t,this.applyViewButtonVisibility()}setShowDayButton(t){this.options.showDayButton=t,this.applyViewButtonVisibility()}setViewButtonsVisibility(t,e,s){this.options.showMonthButton=t,this.options.showWeekButton=e,this.options.showDayButton=s,this.applyViewButtonVisibility()}setShowNavigation(t){this.options.showNavigation=t,this.applyNavigationVisibility()}setDefaultEventColor(t){this.options.defaultEventColor=t,this.applyEventColor(),this.render()}getEventBaseCssStyle(t){const e=t.color,s=!0===t.colorIsGradient,n=e?t.color:this.options.defaultEventColor;if(!n)return{};const i=(" "+n).slice(1),a=this.hexToRgb(i);if(!a)return{};const o=this.getOptimalTextColor(a);if(s){const t={r:Math.min(255,a.r+40),g:Math.min(255,a.g+40),b:Math.min(255,a.b+40)};return{"background-image":`linear-gradient(to bottom, ${this.rgbToHex(t)} 0px, ${n} 100%)`,"background-repeat":"repeat-x",color:o,"background-color":n}}return{"background-color":n,color:o}}getOptimalTextColor(t){return this.getLuminance(t)>.5?"#000000":"#ffffff"}getLuminance(t){return.2126*this.getRelativeLuminanceComponent(t.r/255)+.7152*this.getRelativeLuminanceComponent(t.g/255)+.0722*this.getRelativeLuminanceComponent(t.b/255)}getRelativeLuminanceComponent(t){return t<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4)}getContrastRatio(t,e){const s=this.getLuminance(t),n=this.getLuminance(e);return(Math.max(s,n)+.05)/(Math.min(s,n)+.05)}hexToRgb(t){const e=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return e?{r:parseInt(e[1],16),g:parseInt(e[2],16),b:parseInt(e[3],16)}:null}rgbToHex(t){return"#"+(1<<24|t.r<<16|t.g<<8|t.b).toString(16).slice(1)}applyEventStyles(t){const e=this.getEventBaseCssStyle(t),s=Object.entries(e).map(([t,e])=>`${t}: ${e}`).join("; ");return s?`style="${s}"`:""}}