feat: enhance click handling for color inputs and update event bus cleanup

This commit is contained in:
alexsparkes
2026-02-04 21:15:12 +00:00
parent 7a390b91ca
commit 8dd6cf2655
18 changed files with 188 additions and 112 deletions

View File

@@ -22,7 +22,7 @@ const useAppSetup = () => {
loadSettings();
const refreshHandler = (data) => {
if (data === 'other') {
if (data === 'other' || data === 'greeting' || data === 'clock' || data === 'quote') {
loadSettings(true);
}
};

View File

@@ -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) &&

View File

@@ -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) &&

View File

@@ -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) &&

View File

@@ -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 &&

View File

@@ -141,7 +141,7 @@ const Greeting = () => {
EventBus.on('refresh', handleRefresh);
return () => {
EventBus.off('refresh');
EventBus.off('refresh', handleRefresh);
if (timerRef.current) {
clearTimeout(timerRef.current);
}

View File

@@ -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') },
]}

View File

@@ -26,7 +26,7 @@ const Message = () => {
EventBus.on('refresh', handleRefresh);
return () => {
EventBus.off('refresh');
EventBus.off('refresh', handleRefresh);
};
}, []);

View File

@@ -63,7 +63,7 @@ const Widgets = () => {
EventBus.on('refresh', handleRefresh);
return () => {
EventBus.off('refresh');
EventBus.off('refresh', handleRefresh);
};
}, []);

View File

@@ -67,7 +67,7 @@ const Navbar = ({ openModal }) => {
EventBus.on('refresh', handleRefresh);
return () => {
EventBus.off('refresh');
EventBus.off('refresh', handleRefresh);
};
}, []);

View File

@@ -28,7 +28,7 @@ const Notes = ({ notesRef, floatRef, position, xPosition, yPosition }) => {
EventBus.on('refresh', handleRefresh);
return () => {
EventBus.off('refresh');
EventBus.off('refresh', handleRefresh);
};
}, []);

View File

@@ -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') },
]}

View File

@@ -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) {

View File

@@ -54,7 +54,7 @@ const Autocomplete = ({
EventBus.on('refresh', handleRefresh);
return () => {
EventBus.off('refresh');
EventBus.off('refresh', handleRefresh);
};
}, []);

View File

@@ -135,7 +135,7 @@ const Clock = () => {
EventBus.on('refresh', handleRefresh);
return () => {
EventBus.off('refresh');
EventBus.off('refresh', handleRefresh);
if (timerRef.current) {
clearTimeout(timerRef.current);
}

View File

@@ -143,7 +143,7 @@ const DateWidget = () => {
EventBus.on('refresh', handleRefresh);
return () => {
EventBus.off('refresh');
EventBus.off('refresh', handleRefresh);
};
}, []);

View File

@@ -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') },
]}

View File

@@ -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',
`
<style id='customgreetingfont'>
${googleFontUrl}
.greeting {
font-family: '${greetingFont}', 'Lexend Deca', 'Inter', sans-serif !important;
font-weight: ${greetingFontWeight};
font-style: ${greetingFontStyle};
color: ${greetingColor} !important;
}
</style>
`,
);
} else if (localStorage.getItem('greetingColor')) {
const greetingColor = localStorage.getItem('greetingColor');
document.head.insertAdjacentHTML(
'beforeend',
`
<style id='customgreetingfont'>
.greeting {
color: ${greetingColor} !important;
}
</style>
`,
`<style id='customgreetingfont'>${styleContent}</style>`,
);
}
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',
`
<style id='customclockfont'>
${googleFontUrl}
.clock, .date {
font-family: '${clockFont}', 'Lexend Deca', 'Inter', sans-serif !important;
font-weight: ${clockFontWeight};
font-style: ${clockFontStyle};
color: ${clockColor} !important;
}
</style>
`,
);
} else if (localStorage.getItem('clockColor')) {
const clockColor = localStorage.getItem('clockColor');
document.head.insertAdjacentHTML(
'beforeend',
`
<style id='customclockfont'>
.clock, .date {
color: ${clockColor} !important;
}
</style>
`,
`<style id='customclockfont'>${styleContent}</style>`,
);
}
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',
`
<style id='customquotefont'>
${googleFontUrl}
.quote, .quoteauthor {
font-family: '${quoteFont}', 'Lexend Deca', 'Inter', sans-serif !important;
font-weight: ${quoteFontWeight};
font-style: ${quoteFontStyle};
color: ${quoteColor} !important;
}
</style>
`,
);
} else if (localStorage.getItem('quoteColor')) {
const quoteColor = localStorage.getItem('quoteColor');
document.head.insertAdjacentHTML(
'beforeend',
`
<style id='customquotefont'>
.quote, .quoteauthor {
color: ${quoteColor} !important;
}
</style>
`,
`<style id='customquotefont'>${styleContent}</style>`,
);
}
@@ -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 ██
██ ██