├── 0 ├── .gitignore ├── LICENSE ├── MMM-Multimonth.css ├── MMM-Multimonth.js ├── README.md ├── horz-noweeknum.png ├── horz-week.png ├── package.json ├── screenshot-horizontal.png ├── screenshot-vertical-norepeat.png ├── screenshot-vertical-repeat.png ├── screenshot.png ├── vert-noweek-norep.png ├── vert-noweek-rep.png ├── vert-week-norep.png └── vert-week-rep.png /0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BKeyport/MMM-Multimonth/609ba2a2b5d40283b7870b9086e82463f4751567/0 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Brendan Keyport (BKeyport) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MMM-Multimonth.css: -------------------------------------------------------------------------------- 1 | /* CSS for Multimonth Calendar - (C) Brendan Keyport 2024 */ 2 | 3 | /* Settings */ 4 | .MMM-Multimonth .settings { 5 | --background-header: Green; 6 | --background-dow: var(--color-background); 7 | --background-dowBlank: rgba(255, 255, 255, 0); 8 | --background-weekno: grey; 9 | --background-event: var(--color-background); 10 | 11 | --background-dimmed: var(--color-background); 12 | --background-weekday: var(--color-background); 13 | --background-weekend: cornflowerblue; 14 | --background-today: Yellow; 15 | 16 | 17 | --color-header: var(--color-text-bright); 18 | --color-dow: var(--color-text); 19 | --color-dowBlank: rgba(255, 255, 255, 0); 20 | --color-weekno: var(--color-text); 21 | 22 | --color-dimmed: var(--color-text-dimmed); 23 | --color-weekday: var(--color-text); 24 | --color-weekend: var(--color-background); 25 | --color-today: var(--color-background); 26 | 27 | --back-rounding: 8px; 28 | --alignment: center; 29 | --fontsize: var(--font-size-xsmall); 30 | --smallEventMarker: 3px; 31 | } 32 | 33 | /* Calendar */ 34 | .MMM-Multimonth .calendar { 35 | display: flex; 36 | align-items: stretch; 37 | white-space: nowrap; 38 | border: 0; 39 | font-size: var(--fontsize); 40 | line-height: var(--fontsize) + 5; 41 | text-align: var(--alignment); 42 | } 43 | 44 | /* Layout */ 45 | .MMM-Multimonth .vertical { 46 | flex-direction: column; 47 | } 48 | 49 | .MMM-Multimonth .horizontal { 50 | flex-direction: row; 51 | } 52 | 53 | /* Month */ 54 | .MMM-Multimonth .month { 55 | flex-basis: 100%; 56 | padding: 0px; 57 | display: flex; 58 | flex-direction: column; 59 | } 60 | 61 | /* Month Header */ 62 | .MMM-Multimonth .month-header { 63 | background-color: var(--background-header); 64 | color: var(--color-header); 65 | font-size: var(--fontsize); 66 | line-height: var(--fontsize) + 5; 67 | border-radius: var(--back-rounding); 68 | } 69 | 70 | /* Days of Week Container */ 71 | .MMM-Multimonth .dowContainer { 72 | display: flex; 73 | flex-direction: row; 74 | align-items: stretch; 75 | } 76 | 77 | /* Days of Week */ 78 | .MMM-Multimonth .dow { 79 | color: var(--color-dow); 80 | border-radius: var(--back-rounding); 81 | background-color: var(--background-dow); 82 | flex: 1 1 0px; 83 | min-width: 0; 84 | padding-left: 4px; 85 | padding-right: 4px; 86 | } 87 | 88 | .MMM-Multimonth .dowBlank { 89 | color: var(--color-dowBlank); 90 | border-radius: var(--back-rounding); 91 | background-color: var(--background-dowBlank); 92 | flex: 1 1 0px; 93 | min-width: 0; 94 | padding-left: 4px; 95 | padding-right: 4px; 96 | } 97 | 98 | /* Week Container */ 99 | .MMM-Multimonth .weekContainer { 100 | display: flex; 101 | flex-direction: row; 102 | align-items: stretch; 103 | flex: 1 1 0px; 104 | min-width: 0; 105 | padding: 0 0; 106 | margin: 0 0; 107 | } 108 | 109 | /* Week Number */ 110 | .MMM-Multimonth .weekNumSmall, 111 | .MMM-Multimonth .weekNumBig { 112 | color: var(--color-weekno); 113 | border-radius: var(--back-rounding); 114 | background-color: var(--background-weekno); 115 | flex: 1 0; 116 | min-width: inherit; 117 | padding: 0 0; 118 | margin: 0 0; 119 | } 120 | 121 | /* Day Container */ 122 | .MMM-Multimonth .dayContainer { 123 | display: flex; 124 | flex-flow: row wrap; 125 | flex: 1 0; 126 | } 127 | 128 | 129 | /* Day */ 130 | .MMM-Multimonth .day { 131 | background-color: var(--background-weekday); 132 | border-radius: var(--back-rounding); 133 | color: var(--color-weekday); 134 | flex: 1 1 100%; 135 | } 136 | 137 | 138 | /* Dimmed Day */ 139 | .MMM-Multimonth .dim { 140 | color: var(--color-dimmed); 141 | border-radius: var(--back-rounding); 142 | background-color: var(--background-dimmed); 143 | } 144 | 145 | /* No Display */ 146 | .MMM-Multimonth .noDisplay { 147 | display: none; 148 | } 149 | 150 | /* Weekend */ 151 | .MMM-Multimonth .weekend { 152 | color: var(--color-weekend); 153 | border-radius: var(--back-rounding); 154 | background-color: var(--background-weekend); 155 | } 156 | 157 | /* Today */ 158 | .MMM-Multimonth .today { 159 | border-style: hidden; 160 | background-color: var(--background-today); 161 | color: var(--color-today); 162 | } 163 | 164 | 165 | /* Events */ 166 | 167 | .MMM-Multimonth .events { 168 | display: flex; 169 | flex-flow: row wrap; 170 | flex: 1 1 100%; 171 | } 172 | 173 | .MMM-Multimonth .event, 174 | .MMM-Multimonth .bigEvent { 175 | border-radius: var(--back-rounding); 176 | min-width: inherit; 177 | padding: 0 0; 178 | margin: 0 0; 179 | } 180 | 181 | /* Small Event */ 182 | .MMM-Multimonth .event { 183 | padding: 0 0; 184 | margin: 0 0; 185 | font-size: var(--font-size); 186 | line-height: calc(var(--font-size-medium) - 20px); 187 | } 188 | 189 | /* Big Event */ 190 | .MMM-Multimonth .bigEvent { 191 | display: inline-block; 192 | white-space: wrap; 193 | background-color: var(--background-event); 194 | } 195 | -------------------------------------------------------------------------------- /MMM-Multimonth.js: -------------------------------------------------------------------------------- 1 | Module.register("MMM-Multimonth", { 2 | // Default module config. 3 | defaults: { 4 | startMonth: -1, 5 | monthCount: 3, 6 | monthsVertical: true, 7 | repeatWeekdaysVertical: false, 8 | weekNumbers: false, 9 | weekNumbersISO: false, 10 | highlightWeekend: false, 11 | headerType: 'short', 12 | otherMonths: false, 13 | startWeek: 0, 14 | weekend1: 0, 15 | weekend2: 6, 16 | eventsOn: true, 17 | eventsCount: true, 18 | calNames: [], 19 | instanceID: "", 20 | bigCalendar: false, 21 | }, 22 | 23 | // CSS Add 24 | getStyles: function () { 25 | return [this.data.path + "/MMM-Multimonth.css"]; 26 | }, 27 | 28 | // Update at midnight 29 | 30 | start: function () { 31 | this.storedEvents = []; 32 | 33 | function scheduleMidnightUpdate() { 34 | const now = new Date(); 35 | const nextMidnight = new Date(now); 36 | 37 | // Set the time to midnight 38 | nextMidnight.setHours(24, 0, 0, 0); 39 | 40 | // Calculate the time remaining until the next midnight 41 | const timeUntilMidnight = nextMidnight - now; 42 | 43 | // Schedule the updateDom method to be called at midnight 44 | setTimeout(() => { 45 | this.updateDom(); 46 | 47 | // Reschedule the update for the next midnight 48 | scheduleMidnightUpdate.call(this); 49 | }, timeUntilMidnight); 50 | } 51 | 52 | // Call the function to start the first schedule 53 | scheduleMidnightUpdate.call(this); 54 | }, 55 | 56 | notificationReceived: function (notification, payload, sender) { 57 | if (notification === 'CALENDAR_EVENTS') { 58 | this.storedEvents = JSON.parse(JSON.stringify(payload)) 59 | this.updateDom(); 60 | } 61 | }, 62 | 63 | 64 | 65 | // Override dom generator. 66 | getDom: function () { 67 | // Functions 68 | const firstDay = (dateObject, index) => { 69 | const dayOfWeek = dateObject.getDay(), 70 | firstDay = new Date(dateObject), 71 | diff = dayOfWeek >= index ? dayOfWeek - index : 6 - dayOfWeek; 72 | firstDay.setDate(dateObject.getDate() - diff); 73 | firstDay.setHours(0, 0, 0, 0); 74 | return firstDay; 75 | } 76 | 77 | const lastDay = (dateObject, index) => { 78 | const dayOfWeek = dateObject.getDay(), 79 | lastDay = new Date(dateObject), 80 | diff = (index + (7 - dateObject.getDay())) % 7; 81 | lastDay.setDate(dateObject.getDate() + diff); 82 | lastDay.setHours(0, 0, 0, 0); 83 | return lastDay; 84 | } 85 | 86 | const weekNames = (dateObject, index) => { 87 | sDate = firstDay(dateObject, 0); 88 | weekdaysTemp = []; 89 | var weekdaysHeader = ""; 90 | for (tday = 0; tday < 7; tday++) { 91 | weekdaysTemp.push(sDate.toLocaleDateString(config.language, { weekday: this.config.headerType })); 92 | sDate.setDate(sDate.getDate() + 1); 93 | } 94 | for (tday = 0; tday < 7; tday++) { 95 | offset = tday + index 96 | if (offset >= 7) offset = offset - 7; 97 | weekdaysHeader += `
${weekdaysTemp[offset]}
`; 98 | } 99 | return weekdaysHeader; 100 | } 101 | 102 | const weekNumber = (dateObject) => { 103 | const target = new Date(dateObject); 104 | const jan1 = new Date(target.getFullYear(), 0, 1); 105 | const daysDiff = Math.floor((target - jan1) / (24 * 60 * 60 * 1000)); 106 | let weekResult = Math.ceil((daysDiff + jan1.getDay() + 1) / 7); 107 | // Check if January 1st is part of the week 108 | const adjTarget = new Date(dateObject); 109 | adjTarget.setDate(adjTarget.getDate() + 6); 110 | const newJan1 = new Date(adjTarget.getFullYear(), 0, 1); 111 | if (newJan1.getTime() === jan1.getTime()) { 112 | result = weekResult 113 | } else { 114 | result = 1 115 | } 116 | return result; 117 | }; 118 | 119 | const weekNumberISO = (dateObject) => { 120 | const mondayDate = new Date(dateObject.getFullYear(), dateObject.getMonth(), dateObject.getDate() + 1); 121 | const oneJan = new Date(mondayDate.getFullYear(), 0, 1); 122 | const numberOfDays = Math.floor((mondayDate - oneJan) / (24 * 60 * 60 * 1000)); 123 | const result = Math.ceil((mondayDate.getDay() + 1 + numberOfDays) / 7); 124 | return result; 125 | }; 126 | 127 | const matchName = (cn) => { 128 | result = true; 129 | if (this.config.calNames.length > 0) { 130 | result = false; 131 | for (let ev = 0; ev < this.config.calNames.length; ev++) { 132 | if (this.config.calNames[ev] == cn) { 133 | result = true; 134 | } 135 | } 136 | } 137 | return result; 138 | }; 139 | 140 | 141 | //Variables 142 | date = new Date(); 143 | month = date.getMonth(); 144 | day = date.getDate(); 145 | year = date.getFullYear(); 146 | var wrapper = document.createElement("div"); 147 | var lastMonth = this.config.startMonth + this.config.monthCount - 1; 148 | 149 | // pre-calculate the header line containing the week days - no need to repeat this for every month 150 | var weekdaysHeader = `
`; 151 | if (this.config.weekNumbers && !this.config.bigCalendar) { 152 | weekdaysHeader += `
 
`; 153 | } 154 | weekdaysHeader += weekNames(date, this.config.startWeek); 155 | weekdaysHeader += "
"; 156 | 157 | // set calendar main container depending on calendar orientation 158 | if (this.config.monthsVertical) { 159 | output = `
`; 160 | } else { 161 | output = `
`; 162 | } 163 | 164 | // iterate through months to display 165 | for (currentMonth = this.config.startMonth; currentMonth <= lastMonth; currentMonth++) { 166 | output += `
`; 167 | 168 | // add the month headers 169 | titleTemp = new Date(year, month + currentMonth, 1); 170 | monthTitle = titleTemp.toLocaleString(config.language, { month: 'long', year: 'numeric' }); 171 | output += `
` + monthTitle + "
"; 172 | 173 | // add day of week headers 174 | if (!this.config.monthsVertical || this.config.repeatWeekdaysVertical || currentMonth == this.config.startMonth) { 175 | output += weekdaysHeader; 176 | } 177 | firstDayOfMonth = new Date(year, month + currentMonth, 1, 0, 0, 0, 0); 178 | lastDayOfMonth = new Date(year, month + currentMonth + 1, 0, 0, 0, 0, 0); 179 | gridDay = firstDay(firstDayOfMonth, this.config.startWeek); 180 | gridEnd = lastDay(lastDayOfMonth, this.config.startWeek - 1); 181 | 182 | // Week grid builder 183 | do { 184 | output += `
`; 185 | 186 | // Week Number Calculator 187 | const weekNum = this.config.weekNumbersISO ? weekNumberISO(gridDay) : weekNumber(gridDay); 188 | weekNumResultSmall = `
${weekNum}
`; 189 | weekNumResultBig = `
Wk ${weekNum}
`; 190 | 191 | // Week Loop (for Days) 192 | 193 | // Week Number to the left of the week if not a big calendar 194 | if (this.config.weekNumbers && !this.config.bigCalendar) { 195 | output += weekNumResultSmall; 196 | } 197 | 198 | // build the day container 199 | const eventBodge = new Date(gridDay); 200 | 201 | for (dow = 0; dow <= 6; dow++) { 202 | output += "
"; 203 | 204 | // Internal week number - if big calendar 205 | if (dow === 0 && this.config.weekNumbers && this.config.bigCalendar) { 206 | output += weekNumResultBig; 207 | } 208 | 209 | // the day item 210 | output += "
" + gridDay.getDate() + "
"; 222 | output += "
"; 223 | 224 | // Object to store counts for each unique calendarName and its symbol 225 | const eventCounts = {}; 226 | 227 | // Count events for each calendarName 228 | for (let ev = 0; ev < this.storedEvents.length; ev++) { 229 | const calendarName = this.storedEvents[ev].calendarName; 230 | const symbol = this.storedEvents[ev].symbol; 231 | const color = this.storedEvents[ev].color; 232 | const match = matchName(calendarName); 233 | const orig = new Date(Number(this.storedEvents[ev].startDate)); 234 | const modi = orig.setHours(0, 0, 0, 0); 235 | if (modi == gridDay.getTime() && this.config.eventsOn && match) { 236 | if (eventCounts[calendarName]) { 237 | eventCounts[calendarName].count++; 238 | } else { 239 | eventCounts[calendarName] = { count: 1, symbol: symbol, color: color }; 240 | } 241 | } 242 | } 243 | 244 | // Display event counts for each calendarName 245 | if (this.config.bigCalendar && this.config.eventCounts) { 246 | output += `
` 247 | for (const calendarName in eventCounts) { 248 | output += ` ${eventCounts[calendarName].count} x `; 249 | } 250 | output += `
` 251 | } else if (this.config.bigCalendar && !this.config.eventCounts) { 252 | 253 | output += `
` 254 | for (const calendarName in eventCounts) { 255 | output += ``; 256 | } 257 | output += `
` 258 | } else { 259 | 260 | for (const calendarName in eventCounts) { 261 | output += `
`; 262 | 263 | } 264 | 265 | } 266 | 267 | 268 | } else { 269 | if (this.config.otherMonths) { 270 | output += "dim " + this.config.instanceID; 271 | } else { 272 | output += "noDisplay " + this.config.instanceID; 273 | } 274 | output += "'>"; 275 | } 276 | 277 | 278 | output += "
"; 279 | // End of the day container 280 | output += "
"; 281 | 282 | gridDay.setDate(gridDay.getDate() + 1); 283 | } 284 | 285 | output += "
"; // end of week 286 | } while (gridDay < gridEnd); 287 | output += "
"; // end of month 288 | } 289 | output += "
"; // end of calendar 290 | wrapper.innerHTML = output; 291 | return wrapper; 292 | } 293 | }); 294 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### NOTE: THIS MODULE IS TO BE HOSTED ONLY AT - Any other hosting location is invalid. 2 | 3 | If you fork and change any code, please let me know, if it's useful for the general public, I'd love to add it! 4 | 5 | If you find this module useful, and would like to contribute to the project, I appreciate the thought. Instead of giving to me, please donate to L'Arche Tahoma Hope 6 | at - in honor of Nancy Tyson. (Dedicate my donation checkbox). Nancy means the world to me. You don't have to notify me you 7 | have done so. 8 | 9 | THANK YOU for your consideration. 10 | 11 | # MagicMirror Module to display a calendar with multiple months (user configurable) 12 | 13 | ![screenshot](screenshot.png?raw=true "Screenshot (vertical mode, no week numbers, single weekday line)") 14 | 15 | This is a module for the [MagicMirror²](https://github.com/MagicMirrorOrg/MagicMirror) project. 16 | 17 | This will create a mini-calendar with as many months as the user wants, assuming screen space is available. Screenshots are with config examples. 18 | 19 | ## Installation 20 | 21 | Clone this repository in your ~/MagicMirror/modules/ folder ( $ cd ~MagicMirror/modules/ ): 22 | 23 | ```shell 24 | git clone https://github.com/BKeyport/MMM-Multimonth 25 | ``` 26 | 27 | **Note: NPM INSTALL is no longer required. This module is proud to no longer use any external dependencies!** 28 | 29 | ## Using the module 30 | 31 | To use this module, add it to the modules array in the config/config.js file - shown options are defaults: 32 | 33 | ```js 34 | { 35 | module: 'MMM-Multimonth', 36 | position: 'top_left', // can be any of the postions 37 | config: { // Optional - will default to 3 months, with one previous and one next, vertical orientation. 38 | } 39 | }, 40 | ``` 41 | 42 | **Full config options:** 43 | 44 | | Option | Default | Description | 45 | | ---------------------- | ------- | ------------------------------------------------------------ | 46 | | startMonth | -1 | Starting month relative to the current month | 47 | | monthCount | 3 | How many months do you want to display? | 48 | | monthsVertical | false | Display months on a vertical line | 49 | | repeatWeekdaysVertical | false | Repeat the weekday names? (Vertical Calendar Only) | 50 | | weekNumbers | false | Show the week numbers (How many weeks in the year) | 51 | | weekNumbersISO | false | Use ISO calculation or US/CA calculation (ignored if WeekNumbers is false) True = ISO, False = US/Canada. | 52 | | highlightWeekend | false | highlight the weekend (See Below) | 53 | | headerType | 'short' | How do you want the days displayed? In US - 'short' = "Sun, Mon, Tue" In US - 'narrow' = "S, M, T" | 54 | | otherMonths | false | Display the previous and next month dimmed in the month grid. | 55 | | eventsOn | true | Turn on and off Event monitoring | 56 | | calNames | [] | List of calendar names to trigger underline. Empty will do all of them. | 57 | | bigCalendar | false | Enable alternate big calendar mode, See below | 58 | | instanceID | | If you'd like to have changes per instance, use a string here and specify in CSS. | 59 | | startWeek | 0 | Starting day of your week | 60 | | weekend1 | 0 | 1st Day of your weekend | 61 | | weekend2 | 6 | 2nd day of your weekend | 62 | | eventsCount | true | Big calendar mode - show number of events per icon | 63 | 64 | 65 | for the last three items: 0 = Sunday, 1 = Monday, 2 = Tuesday, 3 = Wednesday, 4 = Thursday, 5 = Friday, 6 = Saturday. 66 | 67 | Remember the format for options: 68 | 69 | `option: value,` Example: `startMonth: -1,` 70 | 71 | Important: calNames is an array, please specify as such `["Main", "Sportsball", "Utility"]` case is IMPORTANT. 72 | 73 | instanceID: You can specify in custom.css different css for each instance. For example, if you add `instanceID: "test",` to your code, you can overide that instance with: 74 | 75 | ```css 76 | .MMM-Multimonth .month-header.test { 77 | background-color: blue; 78 | color: var(--color-header); 79 | font-size: var(--font-size-small); 80 | line-height: var(--font-size-small)+5; 81 | border-radius: var(--back-rounding); 82 | } 83 | ``` 84 | 85 | To get events to feed to module, add the calendar module into your `config.js`. 86 | 87 | Suggested settings at minimum: 88 | 89 | ```js 90 | { 91 | module: "calendar", 92 | config: { 93 | broadcastPastEvents: true, 94 | calendars: [ 95 | { 96 | url: , 97 | name: , 98 | }, 99 | //.... As many as you'd like .... 100 | ], 101 | } 102 | }, 103 | ``` 104 | 105 | ## New Features: 106 | 107 | ### Event Colors: 108 | 109 | The module now can use the colors from the calendar module, default is the system color set (white) 110 | 111 | ### Event Symbols: 112 | 113 | The module now can use the symbols from the calendar module in big calendar mode. 114 | 115 | Example (uses public calendar from the University of Washington): 116 | 117 | ``` js 118 | { 119 | url: "https://gohuskies.com/calendar.ashx/calendar.ics", 120 | name: "Huskies", 121 | symbol: "paw", 122 | color: "#4B2E83", 123 | }, 124 | ``` 125 | 126 | 127 | 128 | ### Big Calendar Mode: 129 | 130 | I wanted a big calendar for the current month, and the solutions out there didn't do what I wanted, so here we are. 131 | 132 | Big calendar mode will change two things in code. 133 | 134 | The Week Number (if used) will become part of the first day of the week's spot. 135 | 136 | Events will be summarized into a count and a symbol, and display below each day of the month. 137 | 138 | To keep the CSS slim, I ***do not*** format anything differently, and it ***will*** break the constraints of the mini mode. Don't use if you're inexperienced in CSS. You will have to design your display yourself. If you have any questions, ask in the magic mirror forums. Myself or any of the CSS wizards will be happy to help you. 139 | 140 | 141 | 142 | ## Updating from prior to 08 Jun 2024: 143 | 144 | CSS is changed - please check all customizations. **Old Elements, values, etc. have been deleted**. 145 | 146 | The following CSS code is all you need in `custom.css` if all you're changing is the colors and/or background edge rounding: 147 | 148 | (Key changes here: "current" has been changed to "today", "background-week, color-week have been removed as redundant") 149 | 150 | ```css 151 | /* Defaults are shown */ 152 | .MMM-Multimonth .settings { 153 | --background-header: rgb(0, 128, 0); /* for the month and year line, use green background */ 154 | --background-dow: var(--color-background); /* For the days of the week, use global background */ 155 | --background-dowBlank: rgba(255, 255, 255, 0); /* For the blank square if you use Week numbers */ 156 | --background-weekno: #808080; /* for the week number, use a grey background */ 157 | --background-event: var(--color-background); /* Background for events, used in big mode only */ 158 | 159 | --background-dimmed: var(--color-background); /* For dimmed (previous/next month in current grid), use the global background color */ 160 | --background-weekday: var(--color-background); /* For normal days, use global background */ 161 | --background-weekend: cornflowerblue; /* For the days defined as your weekend, change the background to cornflowerblue */ 162 | --background-today: Yellow; /* for the current day, change the background to yellow */ 163 | 164 | --color-header: var(--color-text-bright); /* for the header lines, use the global bright text color */ 165 | --color-dow: var(--color-text); /* for the days of the week header, use the global text color */ 166 | --color-dowBlank: rgba(255, 255, 255, 0); /* For the blank square if you use Week numbers */ 167 | --color-weekno: var(--color-text); /* for the week numbers, use the global bright text color */ 168 | 169 | --color-dimmed: var(--color-text-dimmed); /* For dimmed (previous/next month in current grid), use the global dimmed text color */ 170 | --color-weekday: var(--color-text); /* For normal days, use global normal text */ 171 | --color-weekend: var(--color-background); /* For the days defined as your weekend, change the text to the background color */ 172 | --color-today: var(--color-background); /* for the current day, change the text to the background color */ 173 | 174 | --back-rounding: 8px; /* Set the radius of the background rounded edges. See documentation for border-radius elsewhere */ 175 | --alignment: center; /* Adjust the text alignment */ 176 | --fontsize: var(--font-size-xsmall); /* Set font size */ 177 | --smallEventMarker: 3px; /* Event Marker size in pixels */ 178 | } 179 | 180 | ``` 181 | 182 | Notes on the colors above: You can use any colors you'd like. https://www.w3schools.com/cssref/css_colors.php is a list of accepted color names, or if you wanna make your own, https://www.w3schools.com/cssref/css_colors_legal.php. The above list is intended to be a sample, so I use different ways of doing legal colors. the "var" statements refer back to the master color list in Magic Mirror. You'll find a full list in main.css. 183 | 184 | 185 | 186 | 187 | | Screenshot | How to get | 188 | | ------------------------------------------------------------ | ------------------------------------------------------------ | 189 | | ![screenshot](vert-noweek-norep.png?raw=true "Screenshot (vertical mode, no week numbers, single weekday line)") | monthsVertical: true,
repeatWeekdaysVertical: false,
weekNumbers: false, | 190 | | ![screenshot](vert-noweek-rep.png?raw=true "Screenshot (vertical mode, no week numbers, repeat weekday line for every month)") | monthsVertical: true,
repeatWeekdaysVertical: true,
weekNumbers: false, | 191 | | ![screenshot](vert-week-norep.png?raw=true "Screenshot (vertical mode, no week numbers, single weekday line)") | monthsVertical: true,
repeatWeekdaysVertical: false,
weekNumbers: true, | 192 | | ![screenshot](vert-week-rep.png?raw=true "Screenshot (vertical mode, no week numbers, repeat weekday line for every month)") | monthsVertical: true,
repeatWeekdaysVertical: true,
weekNumbers: true, | 193 | | ![screenshot](horz-noweeknum.png?raw=true "Screenshot (horizontal mode)") | monthsVertical: false,
weekNumbers: false, | 194 | | ![screenshot](horz-week.png?raw=true "Screenshot (horizontal mode)") | monthsVertical: false,
weekNumbers: true, | 195 | 196 | Many thanks to kirash for the inspiration with the monthly_calendar module, sdetweil and lavolp3 for the help in coding and CSS work. Without your help, this would just be a pipe dream. 197 | 198 | Please, if you have any suggestions for improvement, let me know, I'm learning JS and CSS as I write this, and I dream of much bigger things. 199 | 200 | Language Localization is controlled by the master language of MagicMirror. 201 | 202 | ## Update: 203 | 204 | Simply issue the following command in the MMM-Multimonth folder: 205 | 206 | ```shell 207 | git pull 208 | ``` 209 | then restart your mirror. 210 | 211 | 212 | ## Versioning 213 | 214 | 0.1 - Initial Release 215 | 216 | 0.2 - Change code to improve functionality, allow for some localization - will not move start of week yet, but will change languages with system. 217 | 218 | 0.25 - Fix code with temporary bodge so to refresh every hour to assure day changes sometime between midnight and 1 am. 219 | 220 | 0.3 - Removed bodge in 0.25, replaced with a simple refresh at midnight, no animation, as I find it annoying. 221 | 222 | 0.9 - Start of week now moves per moment.js. Note: at this point, module is almost everything I wanted. If I don't get any bug reports that I can control, I'm going to bump it up to 1.0 and call it good. 223 | 224 | 0.95 - Fix start of week issue. Had a calculation backwards. - Suggestion was made to optionally add week numbers to the system, still working on that. 225 | 226 | 0.99 - Major rewrite by [Volker Wegert](https://github.com/vwegert) (Danke sehr!) - Implements everything I want, and I even understand the wizard level code he wrote. 227 | 228 | 1.00 - Minor changes to code/css to standardize look. 229 | 230 | 1.10 - Added leading and trailing months when 1 month is selected per user request. 231 | 232 | 1.20 - Added weekend highlight, default is off. 233 | 234 | 1.21 - Added weekend highlight for current day, so user could specify a different look for current day on weekend. 235 | 236 | 1.50 - Minor fixes 237 | 238 | 1.90 - Minor fixes/Remove EOL - Begin work on moving to a life without moment.js - Goal, no dependencies. 239 | 240 | 7 Sep 2022 - changing to date based versioning. 241 | 242 | 1 Oct 2022 - Completely removed moment.js from project. **Returned to Beta Status due to lack of internationalization and start of week adjustments** 243 | 244 | 4 Oct 2022 - Internationalization and Start of Week added back in. -- Note: I'm aware of a bug where the week numbers are showing an extra week. I'm redesigning the layout. 245 | 246 | 5 Oct 2022 - Fix bug in week numbers, fix extra week error. 247 | 248 | 12 Oct 2022 - Fix bug causing last day of month to fall off calendar if it's the same day as start of week. 249 | 250 | 24 Oct 2022 - Add config option for previous/next month display rather than forcing based on use. 251 | 252 | 29 Oct 2022 - Start Implementing streamlined CSS, add feature to move weekend around, documentation improvements. 253 | 254 | 4 Nov 2022 - Correct US/CA Week number calculation, add ISO calculation. 255 | 256 | 11 Nov 2022 - Changes: 257 | 258 | * Temporary disabled US/CA week numbers, routine isn't consistent. I'm gonna rework it. Regardless of how it's set, you will use ISO week numbers. 259 | * Rewrote javascript to match new knowledge, saving several lines of code and making more CSS sense. 260 | * added classes for future features, prepped more things for major future planned feature. 261 | * Streamlined CSS classes some more. 262 | * Due to changes, css class "current_day_weekend" is no longer supported. It does nothing. 263 | 264 | 26 Nov 2022 - Changes: 265 | 266 | * Added support for events from default calendar module. 267 | * Changed the following css elements: day, dim, weekend, current, event, settings. 268 | * Added new varable "--color-event" - which sets the color of the underline marking event on that day. 269 | 270 | 28 Nov 2022 - Added config option "eventsOn" to control event monitoring. 271 | 272 | 17 May 2023 - Added calNames - a way to control which calendars do what. Please see above. 273 | 274 | 16 Jul 2023 - Adjusted CSS to follow master font sizing from MagicMirror. Does not affect custom.css if used for this module. 275 | 276 | 18 Oct 2023 - Added CSS class "instanceID" to allow for more complex CSS. Yes, I'm still working on the module - just been busy. Added fontsize variable to defaults. 277 | 278 | 1 Dec 2023 - Basic changes to system to allow for a new feature (currently undocumented, need to go back through code to document due to being lazy and not writing down changes) 279 | 280 | 5 May 2024 - Fixed US Weeknumbers, among other bug fixes. 281 | 282 | 9 May 2024 - Adjusted README.md for new 3rd party list compatibility, plus capturing other changes. 283 | 284 | 8 Jun 2024 - MAJOR CSS re-write. Some values have changed, Some have been eliminated, some new. 285 | 286 | 18 Jun 2024 - Adjusted JS and CSS to make things line up better, found lots of little CSS bugs. Fully enabled color event handling. 287 | 288 | 11 Aug 2024 - Adjusted startup code to refresh properly at midnight, other minor changes. 289 | 290 | 20 Aug 2024 - Fixing errors caught by github user tgtechy. Thank you. 291 | 292 | 18 Apr 2025 - Change Event visual on small screen to "Jewels" - the ∙ symbol colored to match calendar, add option to not show event count in big mode. 293 | 294 | 08 May 2025 - Remove duplicate code, clean up. 295 | 296 | -------------------------------------------------------------------------------- /horz-noweeknum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BKeyport/MMM-Multimonth/609ba2a2b5d40283b7870b9086e82463f4751567/horz-noweeknum.png -------------------------------------------------------------------------------- /horz-week.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BKeyport/MMM-Multimonth/609ba2a2b5d40283b7870b9086e82463f4751567/horz-week.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mmm-multimonth", 3 | "version": "2024.5.9", 4 | "description": "Multiple month Calendar for MagicMirror².", 5 | "main": "MMM-Multimonth.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/BKeyport/MMM-Multimonth.git" 12 | }, 13 | "keywords": [ 14 | "MagicMirror", 15 | "calendar" 16 | ], 17 | "author": "Brendan Keyport", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/BKeyport/MMM-Multimonth/issues" 21 | }, 22 | "homepage": "https://github.com/BKeyport/MMM-Multimonth#readme", 23 | "devDependencies": { 24 | "@eslint/js": "^9.2.0", 25 | "eslint": "^9.2.0", 26 | "globals": "^15.2.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /screenshot-horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BKeyport/MMM-Multimonth/609ba2a2b5d40283b7870b9086e82463f4751567/screenshot-horizontal.png -------------------------------------------------------------------------------- /screenshot-vertical-norepeat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BKeyport/MMM-Multimonth/609ba2a2b5d40283b7870b9086e82463f4751567/screenshot-vertical-norepeat.png -------------------------------------------------------------------------------- /screenshot-vertical-repeat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BKeyport/MMM-Multimonth/609ba2a2b5d40283b7870b9086e82463f4751567/screenshot-vertical-repeat.png -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BKeyport/MMM-Multimonth/609ba2a2b5d40283b7870b9086e82463f4751567/screenshot.png -------------------------------------------------------------------------------- /vert-noweek-norep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BKeyport/MMM-Multimonth/609ba2a2b5d40283b7870b9086e82463f4751567/vert-noweek-norep.png -------------------------------------------------------------------------------- /vert-noweek-rep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BKeyport/MMM-Multimonth/609ba2a2b5d40283b7870b9086e82463f4751567/vert-noweek-rep.png -------------------------------------------------------------------------------- /vert-week-norep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BKeyport/MMM-Multimonth/609ba2a2b5d40283b7870b9086e82463f4751567/vert-week-norep.png -------------------------------------------------------------------------------- /vert-week-rep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BKeyport/MMM-Multimonth/609ba2a2b5d40283b7870b9086e82463f4751567/vert-week-rep.png --------------------------------------------------------------------------------