diff --git a/index.html b/index.html index af0f8ec..63f70e6 100644 --- a/index.html +++ b/index.html @@ -7,41 +7,93 @@ -
-
- - -
-
-
-
- - -
+ + + + +
+ +
+
+ Enero + Febrero + Marzo + Abril + Mayo + Junio + Julio + Agosto + Septiembre + Octubre + Noviembre + Diciembre +
+
+ L + M + X + J + V + S + D +
+
diff --git a/photoCalendar.js b/photoCalendar.js index aecf661..185707a 100644 --- a/photoCalendar.js +++ b/photoCalendar.js @@ -13,15 +13,33 @@ class PhotoCalendar { img: null, photoOffsetX: 0, photoOffsetY: 0, - year: 2024, + year: null, centerMonths: false, }; constructor() { + this.initDarkModeSwitch(); this.initControls(); this.onYearChange(); } + initDarkModeSwitch() { + function setDarkMode(darkModeSelected) { + if (darkModeSelected) { + document.documentElement.classList.add("darkMode"); + } else { + document.documentElement.classList.remove("darkMode"); + } + } + const darkModekCheckbox = document.getElementById("darkModeSwitch_checkbox"); + darkModekCheckbox.checked = localStorage.getItem("darkMode") || false; + setDarkMode(darkModekCheckbox.checked); + darkModekCheckbox.addEventListener("change", (e) => { + localStorage.setItem("darkMode", e.target.checked); + setDarkMode(e.target.checked); + }); + } + overPhotoImg(x, y) { return y >= 0 && y <= this.options.calendarStartY; } @@ -34,7 +52,7 @@ class PhotoCalendar { } } - getMousePositionInCanvas(x,y) { + getMousePositionInCanvas(x, y) { const canvas = this.getCanvas(); const rect = canvas.getBoundingClientRect(); const scaleX = canvas.width / rect.width; @@ -48,7 +66,7 @@ class PhotoCalendar { let x = this.getEventLocation(e).x; let y = this.getEventLocation(e).y; - const mousePosition = this.getMousePositionInCanvas(x,y); + const mousePosition = this.getMousePositionInCanvas(x, y); if (this.overPhotoImg(mousePosition.x, mousePosition.y)) { this.dragOptions.isDragging = true; this.dragOptions.x = x - this.options.photoOffsetX; @@ -101,6 +119,8 @@ class PhotoCalendar { initControls() { const self = this; + this.options.year = new Date().getFullYear() + 1; + document.getElementById("year").value = this.options.year; document.getElementById("year").addEventListener("change", (e) => { self.options.year = e.target.value; self.onYearChange(); @@ -252,10 +272,18 @@ class PhotoCalendar { ctx.fillText(text, centerX, y); } + getMonthNames() { + return Array.from(document.querySelectorAll("[data-months] span")).map((x) => x.innerHTML); + } + + getWeekdayNames() { + return Array.from(document.querySelectorAll("[data-weekdays] span")).map((x) => x.innerHTML); + } + renderMonth(calendar, month, canvas, x, y, w) { const ctx = canvas.getContext("2d"); - const monthNames = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"]; - const dayInitials = ["L", "M", "X", "J", "V", "S", "D"]; + const monthNames = this.getMonthNames(); //["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"]; + const dayInitials = this.getWeekdayNames(); //["L", "M", "X", "J", "V", "S", "D"]; const cellWidth = w / 8; const cellHeight = 30; const headerHeight = 30; @@ -311,6 +339,63 @@ class PhotoCalendar { } } +function translate(lang, firstTime = false) { + const en = { + "Año:": "Year:", + "Dividir meses": "Split months", + "Centrar meses": "Center months", + "Seleccionar Foto": "Select image", + "Descargar foto-calendario": "Download photo-calendar", + "Buy me a coffe": "Buy me a coffe", + "Support my work": "Support my work", + L: "M", + M: "T", + X: "W", + J: "T", + V: "F", + S: "S", + D: "S", + Enero: "January", + Febrero: "February", + Marzo: "March", + Abril: "April", + Mayo: "May", + June: "June", + Julio: "July", + Agosto: "August", + Septiembre: "September", + Octubre: "October", + Noviembre: "November", + Diciembre: "December", + }; + Array.from(document.querySelectorAll("[data-translate]")).forEach((x) => { + if (firstTime) { + x.dataset.translate = x.innerHTML; + } + if (en.hasOwnProperty(x.dataset.translate)) { + if (lang == "es") { + x.innerHTML = x.dataset.translate; + } else { + x.innerHTML = en[x.dataset.translate]; + } + } + }); + const languageSelector = document.getElementById("language"); + languageSelector.value = lang; +} + document.addEventListener("DOMContentLoaded", () => { const app = new PhotoCalendar(); + + const userLanguage = navigator.language || navigator.userLanguage; + const languageCode = userLanguage.split("-")[0]; + if (languageCode != "es") { + console.log("Language Code", languageCode); + translate(languageCode, true); + app.onYearChange(); + } + document.getElementById("language").addEventListener("change", (e) => { + translate(e.target.value); + app.onYearChange(); + }); }); diff --git a/style.css b/style.css index c7d6116..de1468b 100644 --- a/style.css +++ b/style.css @@ -1,13 +1,494 @@ +:root { + --body-bg: #fff; + --body-color: #202124; + --border-color: #fff; + + --header-bg: #192d40; + --header-border-color: #cecece; + + --menu-bg: var(--body-bg); + --menu-color: var(--body-color); + --menu-bg-hover: #f1f3f4; +} + +:root.darkMode { + --body-bg: #212529; + --body-color: #dee2e6; + --border-color: #495057; + + --header-bg: var(--body-bg); + --header-border-color: var(--border-color); + + --menu-bg: var(--body-bg); + --menu-color: var(--body-color); + --menu-bg-hover: #374549; +} + +html,body { + margin:0; + padding:0; +} + body { + background-color: var(--body-bg); overflow: hidden; font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; font-size: 1rem; } + +/* + _ _ ___ + | \| |__ ___ _| _ ) __ _ _ _ + | .` / _` \ V / _ \/ _` | '_| + |_|\_\__,_|\_/|___/\__,_|_| + +*/ + +#header.scroll-up, +#header:focus-within { + top: 0; +} +#header.scroll-down { + top: -100%; +} +#header { + position: sticky; + ----top: 0; + transition: top 500ms ease-in-out; + + width: 100%; + z-index: 101; + height: 2.5em; + background-color: var(--header-bg); + color: #fff; + border-bottom: 1px solid var(--header-border-color); +} +#header a { + color: #fff; + text-decoration: none; +} + +#header-left { + height: 100%; +} +#header-left > div { + float: left; + height: 100%; +} + +/* + __ __ _ __ _ + | \/ |___ _ _ _ _ ___ | |___ / _| |_ + | |\/| / -_) ' \ || | |___| | / -_) _| _| + |_| |_\___|_||_\_,_| |_\___|_| \__| +*/ +#menu-checkbox { + display: none; +} +#menu-checkbox:checked ~ #menu-overlay { + display: block; +} +#menu-checkbox:checked ~ #menu { + visibility: visible; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} + +#menu-overlay { + display: none; + width: 100%; + height: 100%; + position: fixed; + top: 0; + left: 0; + z-index: 100; +} + +#menu { + position: fixed; + top: 2.5em; + height: calc(100% - 2.5em); + + z-index: 101; + visibility: hidden; + width: 300px; + background: var(--menu-bg); + color: var(--menu-color); + -webkit-transition: all 0.2s; + transition: all 0.2s; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + + padding: .5rem; + overflow-y: auto; + + -webkit-box-shadow: 0 0 1em rgba(0, 0, 0, 0.28); + box-shadow: 0 0 1em rgba(0, 0, 0, 0.28); + border-right: 1px solid var(--border-color); +} +#menu ul { + margin: 0; + padding: 0; + list-style: none; +} +#menu ul li { + padding: 0.25em 0 0.25em 0; +} +#menu ul li img { + vertical-align: middle; + width: 32px; + padding: 0 0.25em 0 0.25em; +} +#menu a { + color: var(--menu-color); + text-decoration: none; + display: block; + font-size: 12pt; +} + +#menu li:hover { + background-color: var(--menu-bg-hover); +} + +#menu h2 { + color: var(--menu-color); + border-bottom: 2px solid var(--menu-bg-hover); + padding-left: 10px; + display: block; + font-size: 1em; + font-weight: bold; +} + +/* + _ _ ___ _ _ + | \| |__ ___ _| _ ) __ _ _ _ ___ | |_ __ _ _ __ | |__ _ _ _ _ __ _ _ _ ___ _ _ + | .` / _` \ V / _ \/ _` | '_| |___| | ' \/ _` | ' \| '_ \ || | '_/ _` | || / -_) '_| + |_|\_\__,_|\_/|___/\__,_|_| |_||_\__,_|_|_|_|_.__/\_,_|_| \__, |\_,_\___|_| + |___/ +*/ +#menu-checkbox:checked ~ #header .hamburger:hover, +.hamburger:hover { + opacity: 0.7; +} +#menu-checkbox:checked ~ #header .hamburger--arrow .hamburger-inner:before { + transform: translate3d(-10px, 0, 0) rotate(-45deg) scaleX(0.7); + top: -0.4rem; +} +#menu-checkbox:checked ~ #header .hamburger--arrow .hamburger-inner:after { + transform: translate3d(-10px, 0, 0) rotate(45deg) scaleX(0.7); + bottom: -0.4rem; +} + +.hamburger { + /* float: left; */ + overflow: visible; + margin: 0; + padding: 0.75em 0.5em 0px 0.5em; + cursor: pointer; + transition-timing-function: linear; + transition-duration: 0.15s; + transition-property: opacity, filter; + text-transform: none; + color: inherit; + border: 0; + background-color: transparent; +} +.hamburger-box { + position: relative; + display: inline-block; + width: 2rem; + height: 1rem; +} +.hamburger-inner { + top: 50%; + display: block; + margin-top: -0.125em; +} +.hamburger-inner, +.hamburger-inner:after, +.hamburger-inner:before { + position: absolute; + width: 2rem; + height: 0.25rem; + + transition-timing-function: ease; + transition-duration: 0.15s; + transition-property: transform; + border-radius: 0.25em; + background-color: #fff; +} +.hamburger-inner:after, +.hamburger-inner:before { + display: block; + content: ""; +} +.hamburger-inner:before { + top: -0.6rem; +} +.hamburger-inner:after { + bottom: -0.6rem; +} + +/* + _ _ ___ _ + | \| |__ ___ _| _ ) __ _ _ _ ___ | | ___ __ _ ___ + | .` / _` \ V / _ \/ _` | '_| |___| | |__/ _ \/ _` / _ \ + |_|\_\__,_|\_/|___/\__,_|_| |____\___/\__, \___/ + |___/ +*/ + + +#title{ + width: 100%; + text-align: center; + position: absolute; + margin: .75em 0 .75em 0; + z-index: -999; +} + + +/* toggle ________________________________*/ + +.control.toggle { + align-items: center; + border-radius: 100px; + display: flex; + font-size: 1em; + --margin-bottom: 16px; + margin-top:1.25rem; +} +.control.toggle:last-of-type { + margin: 0; + margin-top:1.25rem; +} + +.control .toggle__input { + clip: rect(0 0 0 0); + clip-path: inset(50%); + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; + width: 1px; +} +.control .toggle__input:not([disabled]):active + .toggle-track, .control .toggle__input:not([disabled]):focus + .toggle-track { + border-color: var(--control-focus-border); + outline: none; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.0125), 0 0 8px rgba(34,139,230,0.5); + box-shadow: inset 0 1px 1px rgba(0,0,0,0.0125), 0 0 8px rgba(34,139,230,0.5); +} +.control .toggle__input:disabled + .toggle-track { + cursor: not-allowed; + opacity: 0.5; +} + +.control .toggle-track { + background: var(--control-bg); + border: 1px solid var(--control-border); + border-radius: 100px; + cursor: pointer; + display: flex; + height: 30px; + margin-right: .5em; + position: relative; + width: 60px; +} + +.control .toggle-indicator { + align-items: center; + background: var(--control-border); + border-radius: 24px; + bottom: 2px; + display: flex; + height: 24px; + justify-content: center; + left: 2px; + outline: solid 2px transparent; + position: absolute; + transition: 0.25s; + width: 24px; +} + + +.control.toggle:has(input:disabled) { + color: var(--control-disabled-color); +} + + +.control .checkMark { + fill: var(--control-bg); + height: 20px; + width: 20px; + opacity: 0; + transition: opacity 0.25s ease-in-out; +} +.darkMode .control .checkMark { + fill: white; +} + +.control .toggle__input:checked + .toggle-track .toggle-indicator { + background: var(--control-btn-bg); + transform: translateX(30px); +} +.control .toggle__input:checked + .toggle-track .toggle-indicator .checkMark { + opacity: 1; + transition: opacity 0.25s ease-in-out; +} + +@media screen and (-ms-high-contrast: active) { + .control .toggle-track { + border-radius: 0; + } +} + +/* Dark Mode Switch */ +#darkModeSwitch { + --border-color: transparent; +} +.darkMode #darkModeSwitch { + --border-color: white; +} + +#darkModeSwitch input +{ + display: none; +} + +#darkModeSwitch label +{ + display: block; + top: 50%; + right: 0; + left: 0; + width: 72px; + height: 32px; + margin: 0 auto; + background-color: #77b5fe; + border-radius: 56px; + transform: translateY(0%); + cursor: pointer; + transition: 0.3s ease background-color; + overflow: hidden; + border: 1px solid var(--border-color); +} + +#darkModeSwitch .starContainer +{ + position: absolute; + top: 3px; + left: 13px; + width: 20px; + height: 20px; + transform: scale(1); + transition: 0.3s ease top, 0.3s ease left, 0.3s ease transform, 0.3s ease background-color; + z-index: 1; +} + +#darkModeSwitch .star-1 +{ + position: relative; +} + +#darkModeSwitch .star-2 +{ + position: absolute; + transform: rotateZ(36deg); +} + +#darkModeSwitch .star +{ + top: 0; + left: -7px; + font-size: 36px; + line-height: 24px; + color: #fafd0f; + transition: 0.3s ease color; +} + +#darkModeSwitch .moon +{ + position: absolute; + bottom: -52px; + right: 8px; + width: 20px; + height: 20px; + background-color: #fff; + border-radius: 50%; + transition: 0.3s ease bottom; +} + +#darkModeSwitch .moon:before +{ + content: ""; + position: absolute; + top: -12px; + left: -17px; + width: 30px; + height: 30px; + background-color:#03a9f4; + border-radius: 50%; + transition: 0.3s ease background-color; +} + +#darkModeSwitch_checkbox:checked + label +{ + background-color: #000; +} + +#darkModeSwitch_checkbox:checked + label .starContainer +{ + top: 0; + left: 40px; + transform: scale(0.3); +} + +#darkModeSwitch_checkbox:checked + label .star +{ + color: yellow; +} + +#darkModeSwitch_checkbox:checked + label .moon +{ + bottom: 4px; +} + +#darkModeSwitch_checkbox:checked + label .moon:before +{ + background-color: #000; +} + + + +/* + + + + + + + + + + + + + + + + + + + + + + +*/ .control { padding-bottom: 0.5rem; + display:flex; } .preview canvas { - border: 1px solid black; + ---border: 1px solid black; } .control.file input[type="file"] { @@ -16,9 +497,27 @@ body { .control.file label { border-radius: 4px; border: 1px solid black; - -display: block; - padding: 0.25rem; + display: block; + padding: 0.20rem; text-align: center; + background: #f0f0f0; + color: black; + width:100%; +} + +#menu a:hover, +.control.checkbox label:hover, +.control.select:hover { + color: #77b5fe; +} +.control.select:hover input { + border: 2px solid #77b5fe; + border-radius: 4px; +} + +.control.file label:hover, +.control.button button:hover { + background: #77b5fe; } .control.button button { @@ -27,45 +526,41 @@ body { display: block; padding: 0.25rem; font-size: 1rem; + width:100%; +} + +.control.select label, +.control.select select, +.control.select input { + width: 50%; } @media (min-aspect-ratio: 1/1) { /* Horizontal */ + .preview { + text-align: center; + } .preview canvas { - position: absolute; - height: 100%; - top: 0; - right: 0; - } - - .row { - display: block; - } - .col50 { - width: 50%; + height: calc(100% - 2.5rem); + border-left:1px solid lightgray; + border-right:1px solid lightgray; } } @media (max-aspect-ratio: 1/1) { /* Vertical */ .preview canvas { + margin: 0; position: absolute; + top: 50%; + -ms-transform: translateY(-50%); + transform: translateY(-50%); + width: 100%; bottom: 0; left: 0; - } - .control.file label { - width: 90%; - } - .control.button button { - width: 90%; - } - - .row { - width: 100%; - display: flex; - } - .col50 { - width: 50%; + + border-top:1px solid lightgray; + border-bottom:1px solid lightgray; } }