diff --git a/src/App.jsx b/src/App.jsx index 61b260b2..023f5579 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -22,7 +22,7 @@ const useAppSetup = () => { loadSettings(); const refreshHandler = (data) => { - if (data === 'other') { + if (data === 'other' || data === 'greeting' || data === 'clock' || data === 'quote') { loadSettings(true); } }; diff --git a/src/components/Form/Settings/ChipSelect/ChipSelect.jsx b/src/components/Form/Settings/ChipSelect/ChipSelect.jsx index 9d76eab0..65ad3c1b 100644 --- a/src/components/Form/Settings/ChipSelect/ChipSelect.jsx +++ b/src/components/Form/Settings/ChipSelect/ChipSelect.jsx @@ -29,6 +29,26 @@ function ChipSelect({ label, options, onChange, name }) { useEffect(() => { const handleClickOutside = (event) => { + // Ignore clicks on color inputs to prevent closing when native color picker opens + if (event.target.type === 'color') { + return; + } + + // Also ignore clicks on color input labels and wrappers + const target = event.target; + if (target.tagName === 'LABEL' && target.htmlFor) { + const associatedInput = document.getElementById(target.htmlFor) || document.querySelector(`input[name="${target.htmlFor}"]`); + if (associatedInput && associatedInput.type === 'color') { + return; + } + } + + // Check if clicking within a color input wrapper + const colorInputWrapper = target.closest('.colourInput'); + if (colorInputWrapper && colorInputWrapper.querySelector('input[type="color"]')) { + return; + } + if ( containerRef.current && !containerRef.current.contains(event.target) && diff --git a/src/components/Form/Settings/DatePicker/DatePicker.jsx b/src/components/Form/Settings/DatePicker/DatePicker.jsx index 75f0add4..add10dec 100644 --- a/src/components/Form/Settings/DatePicker/DatePicker.jsx +++ b/src/components/Form/Settings/DatePicker/DatePicker.jsx @@ -23,6 +23,26 @@ const DatePicker = memo((props) => { useEffect(() => { const handleClickOutside = (event) => { + // Ignore clicks on color inputs to prevent closing when native color picker opens + if (event.target.type === 'color') { + return; + } + + // Also ignore clicks on color input labels and wrappers + const target = event.target; + if (target.tagName === 'LABEL' && target.htmlFor) { + const associatedInput = document.getElementById(target.htmlFor) || document.querySelector(`input[name="${target.htmlFor}"]`); + if (associatedInput && associatedInput.type === 'color') { + return; + } + } + + // Check if clicking within a color input wrapper + const colorInputWrapper = target.closest('.colourInput'); + if (colorInputWrapper && colorInputWrapper.querySelector('input[type="color"]')) { + return; + } + if ( containerRef.current && !containerRef.current.contains(event.target) && diff --git a/src/components/Form/Settings/Dropdown/Dropdown.jsx b/src/components/Form/Settings/Dropdown/Dropdown.jsx index e0c1eb98..7c8832b5 100644 --- a/src/components/Form/Settings/Dropdown/Dropdown.jsx +++ b/src/components/Form/Settings/Dropdown/Dropdown.jsx @@ -35,6 +35,26 @@ const Dropdown = memo((props) => { useEffect(() => { const handleClickOutside = (event) => { + // Ignore clicks on color inputs to prevent closing when native color picker opens + if (event.target.type === 'color') { + return; + } + + // Also ignore clicks on color input labels and wrappers + const target = event.target; + if (target.tagName === 'LABEL' && target.htmlFor) { + const associatedInput = document.getElementById(target.htmlFor) || document.querySelector(`input[name="${target.htmlFor}"]`); + if (associatedInput && associatedInput.type === 'color') { + return; + } + } + + // Check if clicking within a color input wrapper + const colorInputWrapper = target.closest('.colourInput'); + if (colorInputWrapper && colorInputWrapper.querySelector('input[type="color"]')) { + return; + } + if ( containerRef.current && !containerRef.current.contains(event.target) && diff --git a/src/components/Form/Settings/LocationSearch/LocationSearch.jsx b/src/components/Form/Settings/LocationSearch/LocationSearch.jsx index 2b27160a..7ffcdb1c 100644 --- a/src/components/Form/Settings/LocationSearch/LocationSearch.jsx +++ b/src/components/Form/Settings/LocationSearch/LocationSearch.jsx @@ -52,6 +52,26 @@ const LocationSearch = memo((props) => { useEffect(() => { const handleClickOutside = (event) => { + // Ignore clicks on color inputs to prevent closing when native color picker opens + if (event.target.type === 'color') { + return; + } + + // Also ignore clicks on color input labels and wrappers + const target = event.target; + if (target.tagName === 'LABEL' && target.htmlFor) { + const associatedInput = document.getElementById(target.htmlFor) || document.querySelector(`input[name="${target.htmlFor}"]`); + if (associatedInput && associatedInput.type === 'color') { + return; + } + } + + // Check if clicking within a color input wrapper + const colorInputWrapper = target.closest('.colourInput'); + if (colorInputWrapper && colorInputWrapper.querySelector('input[type="color"]')) { + return; + } + const t = useT(); if ( containerRef.current && diff --git a/src/features/greeting/Greeting.jsx b/src/features/greeting/Greeting.jsx index 46bbe9a8..e933b4db 100644 --- a/src/features/greeting/Greeting.jsx +++ b/src/features/greeting/Greeting.jsx @@ -141,7 +141,7 @@ const Greeting = () => { EventBus.on('refresh', handleRefresh); return () => { - EventBus.off('refresh'); + EventBus.off('refresh', handleRefresh); if (timerRef.current) { clearTimeout(timerRef.current); } diff --git a/src/features/greeting/options/GreetingOptions.jsx b/src/features/greeting/options/GreetingOptions.jsx index 912b2c64..ed6dcacb 100644 --- a/src/features/greeting/options/GreetingOptions.jsx +++ b/src/features/greeting/options/GreetingOptions.jsx @@ -273,12 +273,12 @@ const GreetingOptions = ({ currentSubSection, onSubSectionChange, sectionName }) name="greetingFontWeight" category="greeting" items={[ - { value: '400', text: t(fontWeight + '.normal') }, + { value: '600', text: t(fontWeight + '.semi_bold') }, { value: '100', text: t(fontWeight + '.thin') }, { value: '200', text: t(fontWeight + '.extra_light') }, { value: '300', text: t(fontWeight + '.light') }, + { value: '400', text: t(fontWeight + '.normal') }, { value: '500', text: t(fontWeight + '.medium') }, - { value: '600', text: t(fontWeight + '.semi_bold') }, { value: '700', text: t(fontWeight + '.bold') }, { value: '800', text: t(fontWeight + '.extra_bold') }, ]} diff --git a/src/features/message/Message.jsx b/src/features/message/Message.jsx index d7dd5070..f5933d7b 100644 --- a/src/features/message/Message.jsx +++ b/src/features/message/Message.jsx @@ -26,7 +26,7 @@ const Message = () => { EventBus.on('refresh', handleRefresh); return () => { - EventBus.off('refresh'); + EventBus.off('refresh', handleRefresh); }; }, []); diff --git a/src/features/misc/views/Widgets.jsx b/src/features/misc/views/Widgets.jsx index 07ba8764..48d883b9 100644 --- a/src/features/misc/views/Widgets.jsx +++ b/src/features/misc/views/Widgets.jsx @@ -63,7 +63,7 @@ const Widgets = () => { EventBus.on('refresh', handleRefresh); return () => { - EventBus.off('refresh'); + EventBus.off('refresh', handleRefresh); }; }, []); diff --git a/src/features/navbar/Navbar.jsx b/src/features/navbar/Navbar.jsx index dd7388b4..ef639158 100644 --- a/src/features/navbar/Navbar.jsx +++ b/src/features/navbar/Navbar.jsx @@ -67,7 +67,7 @@ const Navbar = ({ openModal }) => { EventBus.on('refresh', handleRefresh); return () => { - EventBus.off('refresh'); + EventBus.off('refresh', handleRefresh); }; }, []); diff --git a/src/features/navbar/components/Notes.jsx b/src/features/navbar/components/Notes.jsx index abb13122..76335624 100644 --- a/src/features/navbar/components/Notes.jsx +++ b/src/features/navbar/components/Notes.jsx @@ -28,7 +28,7 @@ const Notes = ({ notesRef, floatRef, position, xPosition, yPosition }) => { EventBus.on('refresh', handleRefresh); return () => { - EventBus.off('refresh'); + EventBus.off('refresh', handleRefresh); }; }, []); diff --git a/src/features/quote/options/QuoteOptions.jsx b/src/features/quote/options/QuoteOptions.jsx index a62e8d7c..0d77d172 100644 --- a/src/features/quote/options/QuoteOptions.jsx +++ b/src/features/quote/options/QuoteOptions.jsx @@ -232,12 +232,12 @@ const QuoteOptions = ({ currentSubSection, onSubSectionChange, sectionName }) => name="quoteFontWeight" category="quote" items={[ - { value: '400', text: t(fontWeight + '.normal') }, + { value: '600', text: t(fontWeight + '.semi_bold') }, { value: '100', text: t(fontWeight + '.thin') }, { value: '200', text: t(fontWeight + '.extra_light') }, { value: '300', text: t(fontWeight + '.light') }, + { value: '400', text: t(fontWeight + '.normal') }, { value: '500', text: t(fontWeight + '.medium') }, - { value: '600', text: t(fontWeight + '.semi_bold') }, { value: '700', text: t(fontWeight + '.bold') }, { value: '800', text: t(fontWeight + '.extra_bold') }, ]} diff --git a/src/features/search/Search.jsx b/src/features/search/Search.jsx index dc81df98..b4b05cf1 100644 --- a/src/features/search/Search.jsx +++ b/src/features/search/Search.jsx @@ -81,11 +81,16 @@ function Search() { } } - EventBus.on('refresh', (data) => { + const handleRefresh = (data) => { if (data === 'search') { init(); } - }); + }; + + EventBus.on('refresh', handleRefresh); + return () => { + EventBus.off('refresh', handleRefresh); + }; }, [init]); function searchButton(e) { diff --git a/src/features/search/components/autocomplete/Autocomplete.jsx b/src/features/search/components/autocomplete/Autocomplete.jsx index 85114296..0a541a56 100644 --- a/src/features/search/components/autocomplete/Autocomplete.jsx +++ b/src/features/search/components/autocomplete/Autocomplete.jsx @@ -54,7 +54,7 @@ const Autocomplete = ({ EventBus.on('refresh', handleRefresh); return () => { - EventBus.off('refresh'); + EventBus.off('refresh', handleRefresh); }; }, []); diff --git a/src/features/time/Clock.jsx b/src/features/time/Clock.jsx index eff7c869..d1a1d17e 100644 --- a/src/features/time/Clock.jsx +++ b/src/features/time/Clock.jsx @@ -135,7 +135,7 @@ const Clock = () => { EventBus.on('refresh', handleRefresh); return () => { - EventBus.off('refresh'); + EventBus.off('refresh', handleRefresh); if (timerRef.current) { clearTimeout(timerRef.current); } diff --git a/src/features/time/Date.jsx b/src/features/time/Date.jsx index ce306089..b9bca448 100644 --- a/src/features/time/Date.jsx +++ b/src/features/time/Date.jsx @@ -143,7 +143,7 @@ const DateWidget = () => { EventBus.on('refresh', handleRefresh); return () => { - EventBus.off('refresh'); + EventBus.off('refresh', handleRefresh); }; }, []); diff --git a/src/features/time/options/TimeOptions.jsx b/src/features/time/options/TimeOptions.jsx index 87b42b78..db4d0686 100644 --- a/src/features/time/options/TimeOptions.jsx +++ b/src/features/time/options/TimeOptions.jsx @@ -253,12 +253,12 @@ const TimeOptions = ({ currentSubSection, onSubSectionChange, sectionName }) => name="clockFontWeight" category="clock" items={[ - { value: '400', text: t(fontWeight + '.normal') }, + { value: '600', text: t(fontWeight + '.semi_bold') }, { value: '100', text: t(fontWeight + '.thin') }, { value: '200', text: t(fontWeight + '.extra_light') }, { value: '300', text: t(fontWeight + '.light') }, + { value: '400', text: t(fontWeight + '.normal') }, { value: '500', text: t(fontWeight + '.medium') }, - { value: '600', text: t(fontWeight + '.semi_bold') }, { value: '700', text: t(fontWeight + '.bold') }, { value: '800', text: t(fontWeight + '.extra_bold') }, ]} diff --git a/src/utils/settings/load.js b/src/utils/settings/load.js index 07cf9138..09ca611e 100644 --- a/src/utils/settings/load.js +++ b/src/utils/settings/load.js @@ -143,113 +143,104 @@ export function loadSettings(hotreload) { // Per-widget fonts (override global widget font) const greetingFont = localStorage.getItem('greetingFont'); - if (greetingFont) { - const greetingFontWeight = localStorage.getItem('greetingFontWeight') || '400'; - const greetingFontStyle = localStorage.getItem('greetingFontStyle') || 'normal'; - const greetingColor = localStorage.getItem('greetingColor') || '#ffffff'; + const greetingFontWeight = localStorage.getItem('greetingFontWeight'); + const greetingFontStyle = localStorage.getItem('greetingFontStyle'); + const greetingColor = localStorage.getItem('greetingColor'); - const fontFamily = greetingFont.replace(/ /g, '+'); - const googleFontUrl = `@import url('https://fonts.googleapis.com/css2?family=${fontFamily}:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800&display=swap');`; + if (greetingFont || greetingFontWeight || greetingFontStyle || greetingColor) { + let styleContent = ''; + + if (greetingFont) { + const fontFamily = greetingFont.replace(/ /g, '+'); + styleContent += `@import url('https://fonts.googleapis.com/css2?family=${fontFamily}:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800&display=swap');\n`; + } + + styleContent += '.greeting {'; + if (greetingFont) { + styleContent += `font-family: '${greetingFont}', 'Lexend Deca', 'Inter', sans-serif !important;`; + } + if (greetingFontWeight) { + styleContent += `font-weight: ${greetingFontWeight} !important;`; + } + if (greetingFontStyle) { + styleContent += `font-style: ${greetingFontStyle} !important;`; + } + if (greetingColor) { + styleContent += `color: ${greetingColor} !important;`; + } + styleContent += '}'; document.head.insertAdjacentHTML( 'beforeend', - ` - - `, - ); - } else if (localStorage.getItem('greetingColor')) { - const greetingColor = localStorage.getItem('greetingColor'); - document.head.insertAdjacentHTML( - 'beforeend', - ` - - `, + ``, ); } const clockFont = localStorage.getItem('clockFont'); - if (clockFont) { - const clockFontWeight = localStorage.getItem('clockFontWeight') || '400'; - const clockFontStyle = localStorage.getItem('clockFontStyle') || 'normal'; - const clockColor = localStorage.getItem('clockColor') || '#ffffff'; + const clockFontWeight = localStorage.getItem('clockFontWeight'); + const clockFontStyle = localStorage.getItem('clockFontStyle'); + const clockColor = localStorage.getItem('clockColor'); - const fontFamily = clockFont.replace(/ /g, '+'); - const googleFontUrl = `@import url('https://fonts.googleapis.com/css2?family=${fontFamily}:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800&display=swap');`; + if (clockFont || clockFontWeight || clockFontStyle || clockColor) { + let styleContent = ''; + + if (clockFont) { + const fontFamily = clockFont.replace(/ /g, '+'); + styleContent += `@import url('https://fonts.googleapis.com/css2?family=${fontFamily}:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800&display=swap');\n`; + } + + styleContent += '.clock, .date {'; + if (clockFont) { + styleContent += `font-family: '${clockFont}', 'Lexend Deca', 'Inter', sans-serif !important;`; + } + if (clockFontWeight) { + styleContent += `font-weight: ${clockFontWeight} !important;`; + } + if (clockFontStyle) { + styleContent += `font-style: ${clockFontStyle} !important;`; + } + if (clockColor) { + styleContent += `color: ${clockColor} !important;`; + } + styleContent += '}'; document.head.insertAdjacentHTML( 'beforeend', - ` - - `, - ); - } else if (localStorage.getItem('clockColor')) { - const clockColor = localStorage.getItem('clockColor'); - document.head.insertAdjacentHTML( - 'beforeend', - ` - - `, + ``, ); } const quoteFont = localStorage.getItem('quoteFont'); - if (quoteFont) { - const quoteFontWeight = localStorage.getItem('quoteFontWeight') || '400'; - const quoteFontStyle = localStorage.getItem('quoteFontStyle') || 'normal'; - const quoteColor = localStorage.getItem('quoteColor') || '#ffffff'; + const quoteFontWeight = localStorage.getItem('quoteFontWeight'); + const quoteFontStyle = localStorage.getItem('quoteFontStyle'); + const quoteColor = localStorage.getItem('quoteColor'); - const fontFamily = quoteFont.replace(/ /g, '+'); - const googleFontUrl = `@import url('https://fonts.googleapis.com/css2?family=${fontFamily}:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800&display=swap');`; + if (quoteFont || quoteFontWeight || quoteFontStyle || quoteColor) { + let styleContent = ''; + + if (quoteFont) { + const fontFamily = quoteFont.replace(/ /g, '+'); + styleContent += `@import url('https://fonts.googleapis.com/css2?family=${fontFamily}:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800&display=swap');\n`; + } + + styleContent += '.quote, .quoteauthor {'; + if (quoteFont) { + styleContent += `font-family: '${quoteFont}', 'Lexend Deca', 'Inter', sans-serif !important;`; + } + if (quoteFontWeight) { + styleContent += `font-weight: ${quoteFontWeight} !important;`; + } + if (quoteFontStyle) { + styleContent += `font-style: ${quoteFontStyle} !important;`; + } + if (quoteColor) { + styleContent += `color: ${quoteColor} !important;`; + } + styleContent += '}'; document.head.insertAdjacentHTML( 'beforeend', - ` - - `, - ); - } else if (localStorage.getItem('quoteColor')) { - const quoteColor = localStorage.getItem('quoteColor'); - document.head.insertAdjacentHTML( - 'beforeend', - ` - - `, + ``, ); } @@ -272,15 +263,15 @@ export function loadSettings(hotreload) { // easter egg console.log(` - █████████████████████████████████████████████████████████████ + █████████████████████████████████████████████████████████████ + ██ ██ + ██ ███ ███ ██ ██ ███████ ██ + ██ ████ ████ ██ ██ ██ ██ + ██ ██ ████ ██ ██ ██ █████ ██ + ██ ██ ██ ██ ██ ██ ██ ██ + ██ ██ ██ ██████ ███████ ██ + ██ ██ ██ ██ - ██ ███ ███ ██ ██ ███████ ██ - ██ ████ ████ ██ ██ ██ ██ - ██ ██ ████ ██ ██ ██ █████ ██ - ██ ██ ██ ██ ██ ██ ██ ██ - ██ ██ ██ ██████ ███████ ██ - ██ ██ - ██ ██ ██ Copyright 2018-${new Date().getFullYear()} The Mue Authors ██ ██ GitHub: https://github.com/mue/mue ██ ██ ██