From c9e8e24102736aabe10e45ba92475d979af47929 Mon Sep 17 00:00:00 2001 From: Harsh Rao Date: Sat, 8 Feb 2025 14:53:36 +0530 Subject: [PATCH 1/6] refactor --- src/simulator/src/themer/themer.js | 222 ------------------- src/simulator/src/themer/themer.ts | 258 +++++++++++++++++++++++ src/simulator/src/themer/themer.types.ts | 5 + 3 files changed, 263 insertions(+), 222 deletions(-) delete mode 100644 src/simulator/src/themer/themer.js create mode 100644 src/simulator/src/themer/themer.ts create mode 100644 src/simulator/src/themer/themer.types.ts diff --git a/src/simulator/src/themer/themer.js b/src/simulator/src/themer/themer.js deleted file mode 100644 index c514f4ff..00000000 --- a/src/simulator/src/themer/themer.js +++ /dev/null @@ -1,222 +0,0 @@ -import { dots } from '../canvasApi' -import themeOptions from './themes' -import themeCardSvg from './themeCardSvg' -import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' - -/** - * Extracts canvas theme colors from CSS-Variables and returns a JSON Object - * @returns {Object.} - */ -const getCanvasColors = () => { - let colors = {} - colors['hover_select'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--hover-and-sel') - colors['fill'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--fill') - colors['mini_fill'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--mini-map') - colors['mini_stroke'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--mini-map-stroke') - colors['stroke'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--stroke') - colors['stroke_alt'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--secondary-stroke') - colors['input_text'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--input-text') - colors['color_wire_draw'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--wire-draw') - colors['color_wire_con'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--wire-cnt') - colors['color_wire_pow'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--wire-pow') - colors['color_wire_sel'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--wire-sel') - colors['color_wire_lose'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--wire-lose') - colors['color_wire'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--wire-norm') - colors['text'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--text') - colors['node'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--node') - colors['node_norm'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--node-norm') - colors['splitter'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--splitter') - colors['out_rect'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--output-rect') - colors['canvas_stroke'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--canvas-stroke') - colors['canvas_fill'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--canvas-fill') - return colors -} - -/** - * Common canvas theme color object, used for rendering canvas elements - */ -export let colors = getCanvasColors() - -/** - * Updates theme - * 1) Sets CSS Variables for UI elements - * 2) Sets color variable for Canvas elements - */ -export function updateThemeForStyle(themeName) { - const selectedTheme = themeOptions[themeName] - if (selectedTheme === undefined) return - const html = document.getElementsByTagName('html')[0] - Object.keys(selectedTheme).forEach((property, i) => { - html.style.setProperty(property, selectedTheme[property]) - }) - colors = getCanvasColors() -} - -/** - * Theme Preview Card SVG - * Sets the SVG colors according to theme - * @param {string} themeName Name of theme - * @returns {SVG} - */ -export const getThemeCardSvg = (themeName) => { - const colors = themeOptions[themeName] - let svgIcon = $(themeCardSvg) - - // Dynamically set the colors according to the theme - $('.svgText', svgIcon).attr('fill', colors['--text-panel']) - - $('.svgNav', svgIcon).attr('fill', colors['--bg-tab']) - $('.svgNav', svgIcon).attr('stroke', colors['--br-primary']) - - $('.svgGridBG', svgIcon).attr('fill', colors['--canvas-fill']) - $('.svgGrid', svgIcon).attr('fill', colors['--canvas-stroke']) - - $('.svgPanel', svgIcon).attr('fill', colors['--primary']) - $('.svgPanel', svgIcon).attr('stroke', colors['--br-primary']) - - $('.svgChev', svgIcon).attr('stroke', colors['--br-secondary']) - - $('.svgHeader', svgIcon).attr('fill', colors['--primary']) - return svgIcon.prop('outerHTML') -} - -/** - * Generates theme card HTML - * @param {string} themeName Name of theme - * @param {boolean} selected Flag variable for currently selected theme - * @return {string} Theme card html - */ -export const getThemeCard = (themeName, selected) => { - if (themeName === 'Custom Theme') return '
' - let themeId = themeName.replace(' ', '') - let selectedClass = selected ? 'selected set' : '' - // themeSel is the hit area - return ` -
-
- ${getThemeCardSvg(themeName)} - - - - -
- ` -} - -/** - * Create Color Themes Dialog - */ -export const colorThemes = () => { - const simulatorStore = SimulatorStore() - simulatorStore.dialogBox.theme_dialog = true - - // const selectedTheme = localStorage.getItem('theme') - // $('#colorThemesDialog').empty() - // const themes = Object.keys(themeOptions) - // themes.forEach((theme) => { - // if (theme === selectedTheme) { - // $('#colorThemesDialog').append(getThemeCard(theme, true)) - // } else { - // $('#colorThemesDialog').append(getThemeCard(theme, false)) - // } - // }) - - // $('.selected label').trigger('click') - // $('#colorThemesDialog').dialog({ - // resizable: false, - // close() { - // // Rollback to previous theme - // updateThemeForStyle(localStorage.getItem('theme')) - // updateBG() - // }, - // buttons: [ - // { - // text: 'Apply Theme', - // click() { - // // check if any theme is selected or not - // if ($('.selected label').text()) { - // localStorage.removeItem('Custom Theme') - // localStorage.setItem( - // 'theme', - // $('.selected label').text() - // ) - // } - // $('.set').removeClass('set') - // $('.selected').addClass('set') - // $(this).dialog('close') - // }, - // }, - // { - // text: 'Custom Theme', - // click() { - // CustomColorThemes() - // $(this).dialog('close') - // }, - // }, - // ], - // }) - - $('#colorThemesDialog').focus() - $('.ui-dialog[aria-describedby="colorThemesDialog"]').on('click', () => - $('#colorThemesDialog').focus() - ) //hack for losing focus - - $('.themeSel').on('mousedown', (e) => { - e.preventDefault() - $('.selected').removeClass('selected') - let themeCard = $(e.target.parentElement) - themeCard.addClass('selected') - // Extract radio button - var radioButton = themeCard.find('input[type=radio]') - radioButton.trigger('click') // Mark as selected - updateThemeForStyle(themeCard.find('label').text()) // Extract theme name and set - updateBG() - }) -} - -export const updateBG = () => dots(true, false, true) -;(() => { - if (!localStorage.getItem('theme')) - localStorage.setItem('theme', 'Default Theme') - updateThemeForStyle(localStorage.getItem('theme')) -})() diff --git a/src/simulator/src/themer/themer.ts b/src/simulator/src/themer/themer.ts new file mode 100644 index 00000000..1598bf23 --- /dev/null +++ b/src/simulator/src/themer/themer.ts @@ -0,0 +1,258 @@ +import { dots } from '../canvasApi'; +import importedThemeOptions from './themes'; +import{ ThemeOptions } from './themer.types' + +const themeOptions: ThemeOptions = importedThemeOptions; +import themeCardSvg from './themeCardSvg'; +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore'; + +/** + * Extracts canvas theme colors from CSS-Variables and returns a JSON Object + * @returns {Object.} + */ +const getCanvasColors = (): Record => { + const colors: Record = {}; + colors['hover_select'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--hover-and-sel'); + colors['fill'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--fill'); + colors['mini_fill'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--mini-map'); + colors['mini_stroke'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--mini-map-stroke'); + colors['stroke'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--stroke'); + colors['stroke_alt'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--secondary-stroke'); + colors['input_text'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--input-text'); + colors['color_wire_draw'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-draw'); + colors['color_wire_con'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-cnt'); + colors['color_wire_pow'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-pow'); + colors['color_wire_sel'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-sel'); + colors['color_wire_lose'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-lose'); + colors['color_wire'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-norm'); + colors['text'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--text'); + colors['node'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--node'); + colors['node_norm'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--node-norm'); + colors['splitter'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--splitter'); + colors['out_rect'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--output-rect'); + colors['canvas_stroke'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--canvas-stroke'); + colors['canvas_fill'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--canvas-fill'); + return colors; +}; + +/** + * Common canvas theme color object, used for rendering canvas elements + */ +export let colors: Record = getCanvasColors(); + +/** + * Updates theme + * 1) Sets CSS Variables for UI elements + * 2) Sets color variable for Canvas elements + * @param {string} themeName - Name of the theme to apply + */ +export function updateThemeForStyle(themeName: string): void { + const selectedTheme = themeOptions[themeName]; + if (selectedTheme === undefined) return; + const html = document.getElementsByTagName('html')[0]; + Object.keys(selectedTheme).forEach((property) => { + html.style.setProperty(property, selectedTheme[property]); + }); + colors = getCanvasColors(); +} + +/** + * Theme Preview Card SVG + * Sets the SVG colors according to theme + * @param {string} themeName - Name of theme + * @returns {string} - SVG HTML as a string + */ +export const getThemeCardSvg = (themeName: string): string => { + const colors = themeOptions[themeName]; + const parser = new DOMParser(); + const svgDoc = parser.parseFromString(themeCardSvg, 'image/svg+xml'); + const svgElement = svgDoc.documentElement; + + // Dynamically set the colors according to the theme + svgElement.querySelectorAll('.svgText').forEach((el) => { + el.setAttribute('fill', colors['--text-panel']); + }); + svgElement.querySelectorAll('.svgNav').forEach((el) => { + el.setAttribute('fill', colors['--bg-tab']); + el.setAttribute('stroke', colors['--br-primary']); + }); + svgElement.querySelectorAll('.svgGridBG').forEach((el) => { + el.setAttribute('fill', colors['--canvas-fill']); + }); + svgElement.querySelectorAll('.svgGrid').forEach((el) => { + el.setAttribute('fill', colors['--canvas-stroke']); + }); + svgElement.querySelectorAll('.svgPanel').forEach((el) => { + el.setAttribute('fill', colors['--primary']); + el.setAttribute('stroke', colors['--br-primary']); + }); + svgElement.querySelectorAll('.svgChev').forEach((el) => { + el.setAttribute('stroke', colors['--br-secondary']); + }); + svgElement.querySelectorAll('.svgHeader').forEach((el) => { + el.setAttribute('fill', colors['--primary']); + }); + + return svgElement.outerHTML; +}; + +/** + * Generates theme card HTML + * @param {string} themeName - Name of theme + * @param {boolean} selected - Flag variable for currently selected theme + * @return {string} - Theme card HTML as a string + */ +export const getThemeCard = (themeName: string, selected: boolean): string => { + if (themeName === 'Custom Theme') return '
'; + const themeId = themeName.replace(' ', ''); + const selectedClass = selected ? 'selected set' : ''; + // themeSel is the hit area + return ` +
+
+ ${getThemeCardSvg(themeName)} + + + + +
+ `; +}; + +/** + * Create Color Themes Dialog + */ +export const colorThemes = (): void => { + const simulatorStore = SimulatorStore(); + simulatorStore.dialogBox.theme_dialog = true; + + // const selectedTheme = localStorage.getItem('theme'); + // $('#colorThemesDialog').empty(); + // const themes = Object.keys(themeOptions); + // themes.forEach((theme) => { + // if (theme === selectedTheme) { + // $('#colorThemesDialog').append(getThemeCard(theme, true)); + // } else { + // $('#colorThemesDialog').append(getThemeCard(theme, false)); + // } + // }); + + // $('.selected label').trigger('click'); + // $('#colorThemesDialog').dialog({ + // resizable: false, + // close() { + // // Rollback to previous theme + // updateThemeForStyle(localStorage.getItem('theme')); + // updateBG(); + // }, + // buttons: [ + // { + // text: 'Apply Theme', + // click() { + // // check if any theme is selected or not + // if ($('.selected label').text()) { + // localStorage.removeItem('Custom Theme'); + // localStorage.setItem( + // 'theme', + // $('.selected label').text() + // ); + // } + // $('.set').removeClass('set'); + // $('.selected').addClass('set'); + // $(this).dialog('close'); + // }, + // }, + // { + // text: 'Custom Theme', + // click() { + // CustomColorThemes(); + // $(this).dialog('close'); + // }, + // }, + // ], + // }); + + const colorThemesDialog = document.getElementById('colorThemesDialog'); + if (colorThemesDialog) { + colorThemesDialog.focus(); + } + + document.querySelector('.ui-dialog[aria-describedby="colorThemesDialog"]')?.addEventListener('click', () => { + const colorThemesDialog = document.getElementById('colorThemesDialog'); + if (colorThemesDialog) { + colorThemesDialog.focus(); + } + }); // hack for losing focus + + document.querySelectorAll('.themeSel').forEach((element) => { + element.addEventListener('mousedown', (e) => { + e.preventDefault(); + document.querySelectorAll('.selected').forEach((el) => { + el.classList.remove('selected'); + }); + const themeCard = (e.target as HTMLElement).parentElement; + if (themeCard) { + themeCard.classList.add('selected'); + const radioButton = themeCard.querySelector('input[type=radio]') as HTMLInputElement; + if (radioButton) { + radioButton.click(); // Mark as selected + } + const label = themeCard.querySelector('label'); + if (label) { + updateThemeForStyle(label.textContent || ''); // Extract theme name and set + } + updateBG(); + } + }); + }); +}; + +export const updateBG = (): void => dots(true, false, true); + +// Initialize theme on load +(() => { + if (!localStorage.getItem('theme')) { + localStorage.setItem('theme', 'Default Theme'); + } + updateThemeForStyle(localStorage.getItem('theme') || 'Default Theme'); +})(); \ No newline at end of file diff --git a/src/simulator/src/themer/themer.types.ts b/src/simulator/src/themer/themer.types.ts new file mode 100644 index 00000000..4b908706 --- /dev/null +++ b/src/simulator/src/themer/themer.types.ts @@ -0,0 +1,5 @@ +export interface ThemeOptions { + [key: string]: { + [property: string]: string; + }; +} \ No newline at end of file From 4bbc7b49f7c415760bc759edf701cf3fd5bc0cd1 Mon Sep 17 00:00:00 2001 From: Harsh Rao Date: Sat, 8 Feb 2025 16:13:10 +0530 Subject: [PATCH 2/6] resolve --- src/simulator/src/themer/themer.ts | 79 ++++++++++-------------------- 1 file changed, 27 insertions(+), 52 deletions(-) diff --git a/src/simulator/src/themer/themer.ts b/src/simulator/src/themer/themer.ts index 1598bf23..5ee89f82 100644 --- a/src/simulator/src/themer/themer.ts +++ b/src/simulator/src/themer/themer.ts @@ -103,14 +103,27 @@ export function updateThemeForStyle(themeName: string): void { * @returns {string} - SVG HTML as a string */ export const getThemeCardSvg = (themeName: string): string => { + if (!themeOptions[themeName]) { + console.error(`Theme "${themeName}" not found`); + return ''; + } + const colors = themeOptions[themeName]; const parser = new DOMParser(); const svgDoc = parser.parseFromString(themeCardSvg, 'image/svg+xml'); + + // Check for parsing errors + const parserError = svgDoc.querySelector('parsererror'); + if (parserError) { + console.error('Failed to parse SVG:', parserError); + return ''; + } + const svgElement = svgDoc.documentElement; // Dynamically set the colors according to the theme svgElement.querySelectorAll('.svgText').forEach((el) => { - el.setAttribute('fill', colors['--text-panel']); + el.setAttribute('fill', colors['--text-panel'] || '#000000'); }); svgElement.querySelectorAll('.svgNav').forEach((el) => { el.setAttribute('fill', colors['--bg-tab']); @@ -142,9 +155,18 @@ export const getThemeCardSvg = (themeName: string): string => { * @param {boolean} selected - Flag variable for currently selected theme * @return {string} - Theme card HTML as a string */ +const escapeHtml = (unsafe: string): string => { + return unsafe + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + }; + export const getThemeCard = (themeName: string, selected: boolean): string => { if (themeName === 'Custom Theme') return '
'; - const themeId = themeName.replace(' ', ''); + const themeId = escapeHtml(themeName.replace(' ', '')); const selectedClass = selected ? 'selected set' : ''; // themeSel is the hit area return ` @@ -152,8 +174,8 @@ export const getThemeCard = (themeName: string, selected: boolean): string => {
${getThemeCardSvg(themeName)} - - + + `; @@ -164,54 +186,7 @@ export const getThemeCard = (themeName: string, selected: boolean): string => { */ export const colorThemes = (): void => { const simulatorStore = SimulatorStore(); - simulatorStore.dialogBox.theme_dialog = true; - - // const selectedTheme = localStorage.getItem('theme'); - // $('#colorThemesDialog').empty(); - // const themes = Object.keys(themeOptions); - // themes.forEach((theme) => { - // if (theme === selectedTheme) { - // $('#colorThemesDialog').append(getThemeCard(theme, true)); - // } else { - // $('#colorThemesDialog').append(getThemeCard(theme, false)); - // } - // }); - - // $('.selected label').trigger('click'); - // $('#colorThemesDialog').dialog({ - // resizable: false, - // close() { - // // Rollback to previous theme - // updateThemeForStyle(localStorage.getItem('theme')); - // updateBG(); - // }, - // buttons: [ - // { - // text: 'Apply Theme', - // click() { - // // check if any theme is selected or not - // if ($('.selected label').text()) { - // localStorage.removeItem('Custom Theme'); - // localStorage.setItem( - // 'theme', - // $('.selected label').text() - // ); - // } - // $('.set').removeClass('set'); - // $('.selected').addClass('set'); - // $(this).dialog('close'); - // }, - // }, - // { - // text: 'Custom Theme', - // click() { - // CustomColorThemes(); - // $(this).dialog('close'); - // }, - // }, - // ], - // }); - + simulatorStore.dialogBox.theme_dialog = true; const colorThemesDialog = document.getElementById('colorThemesDialog'); if (colorThemesDialog) { colorThemesDialog.focus(); From 60fcd9d435c0fa38ec400a57129dd02d470a96b8 Mon Sep 17 00:00:00 2001 From: Harsh Rao Date: Sat, 8 Feb 2025 16:52:49 +0530 Subject: [PATCH 3/6] resolve --- src/simulator/src/themer/themer.ts | 25 ++++++++++++++++++------ src/simulator/src/themer/themer.types.ts | 8 ++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/simulator/src/themer/themer.ts b/src/simulator/src/themer/themer.ts index 5ee89f82..2ce6ee68 100644 --- a/src/simulator/src/themer/themer.ts +++ b/src/simulator/src/themer/themer.ts @@ -186,21 +186,28 @@ export const getThemeCard = (themeName: string, selected: boolean): string => { */ export const colorThemes = (): void => { const simulatorStore = SimulatorStore(); + const cleanupListeners: (() => void)[] = []; + simulatorStore.dialogBox.theme_dialog = true; const colorThemesDialog = document.getElementById('colorThemesDialog'); if (colorThemesDialog) { colorThemesDialog.focus(); } - - document.querySelector('.ui-dialog[aria-describedby="colorThemesDialog"]')?.addEventListener('click', () => { + + const dialogClickHandler = () => { const colorThemesDialog = document.getElementById('colorThemesDialog'); if (colorThemesDialog) { colorThemesDialog.focus(); } - }); // hack for losing focus - + }; + + const dialog = document.querySelector('.ui-dialog[aria-describedby="colorThemesDialog"]'); + if (dialog) { + dialog.addEventListener('click', dialogClickHandler); + cleanupListeners.push(() => dialog.removeEventListener('click', dialogClickHandler)); + } document.querySelectorAll('.themeSel').forEach((element) => { - element.addEventListener('mousedown', (e) => { + const mousedownHandler = (e: MouseEvent) => { e.preventDefault(); document.querySelectorAll('.selected').forEach((el) => { el.classList.remove('selected'); @@ -218,8 +225,14 @@ export const colorThemes = (): void => { } updateBG(); } - }); + }; + element.addEventListener('mousedown', mousedownHandler as EventListener); + cleanupListeners.push(() => element.removeEventListener('mousedown', mousedownHandler as EventListener)); }); + // Add cleanup method to store + simulatorStore.cleanupThemeDialog = () => { + cleanupListeners.forEach(cleanup => cleanup()); + }; }; export const updateBG = (): void => dots(true, false, true); diff --git a/src/simulator/src/themer/themer.types.ts b/src/simulator/src/themer/themer.types.ts index 4b908706..639e5052 100644 --- a/src/simulator/src/themer/themer.types.ts +++ b/src/simulator/src/themer/themer.types.ts @@ -2,4 +2,12 @@ export interface ThemeOptions { [key: string]: { [property: string]: string; }; +} + +interface Theme { + [key: string]: string; +} + +export interface Themes { + [themeName: string]: Theme; } \ No newline at end of file From 25e35d0cf98fd438fd91f7f6c8d617d34f997b2d Mon Sep 17 00:00:00 2001 From: Harsh Rao Date: Sat, 8 Feb 2025 16:53:51 +0530 Subject: [PATCH 4/6] resolve --- src/simulator/src/themer/themer.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/simulator/src/themer/themer.ts b/src/simulator/src/themer/themer.ts index 2ce6ee68..76b144ce 100644 --- a/src/simulator/src/themer/themer.ts +++ b/src/simulator/src/themer/themer.ts @@ -229,8 +229,9 @@ export const colorThemes = (): void => { element.addEventListener('mousedown', mousedownHandler as EventListener); cleanupListeners.push(() => element.removeEventListener('mousedown', mousedownHandler as EventListener)); }); - // Add cleanup method to store - simulatorStore.cleanupThemeDialog = () => { + + // Add cleanup method to store dynamically + (simulatorStore as any).cleanupThemeDialog = () => { cleanupListeners.forEach(cleanup => cleanup()); }; }; From f800477b516fd3a0abf044ebe8a71bb317103e77 Mon Sep 17 00:00:00 2001 From: Harsh Rao Date: Sat, 8 Feb 2025 17:16:23 +0530 Subject: [PATCH 5/6] resolve --- src/simulator/src/themer/themer.ts | 288 ++++++++++++----------------- 1 file changed, 122 insertions(+), 166 deletions(-) diff --git a/src/simulator/src/themer/themer.ts b/src/simulator/src/themer/themer.ts index 76b144ce..b2997f7f 100644 --- a/src/simulator/src/themer/themer.ts +++ b/src/simulator/src/themer/themer.ts @@ -1,112 +1,83 @@ import { dots } from '../canvasApi'; import importedThemeOptions from './themes'; -import{ ThemeOptions } from './themer.types' - -const themeOptions: ThemeOptions = importedThemeOptions; +import { ThemeOptions } from './themer.types'; import themeCardSvg from './themeCardSvg'; import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore'; +const themeOptions: ThemeOptions = importedThemeOptions; + /** - * Extracts canvas theme colors from CSS-Variables and returns a JSON Object - * @returns {Object.} + * Helper function to set CSS variable values into a colors object. + */ +const setColor = (colors: Record, key: string, cssVar: string): void => { + colors[key] = getComputedStyle(document.documentElement).getPropertyValue(cssVar); +}; + +/** + * Extracts canvas theme colors from CSS variables and returns a JSON object. */ const getCanvasColors = (): Record => { const colors: Record = {}; - colors['hover_select'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--hover-and-sel'); - colors['fill'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--fill'); - colors['mini_fill'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--mini-map'); - colors['mini_stroke'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--mini-map-stroke'); - colors['stroke'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--stroke'); - colors['stroke_alt'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--secondary-stroke'); - colors['input_text'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--input-text'); - colors['color_wire_draw'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--wire-draw'); - colors['color_wire_con'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--wire-cnt'); - colors['color_wire_pow'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--wire-pow'); - colors['color_wire_sel'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--wire-sel'); - colors['color_wire_lose'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--wire-lose'); - colors['color_wire'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--wire-norm'); - colors['text'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--text'); - colors['node'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--node'); - colors['node_norm'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--node-norm'); - colors['splitter'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--splitter'); - colors['out_rect'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--output-rect'); - colors['canvas_stroke'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--canvas-stroke'); - colors['canvas_fill'] = getComputedStyle( - document.documentElement - ).getPropertyValue('--canvas-fill'); + setColor(colors, 'hover_select', '--hover-and-sel'); + setColor(colors, 'fill', '--fill'); + setColor(colors, 'mini_fill', '--mini-map'); + setColor(colors, 'mini_stroke', '--mini-map-stroke'); + setColor(colors, 'stroke', '--stroke'); + setColor(colors, 'stroke_alt', '--secondary-stroke'); + setColor(colors, 'input_text', '--input-text'); + setColor(colors, 'color_wire_draw', '--wire-draw'); + setColor(colors, 'color_wire_con', '--wire-cnt'); + setColor(colors, 'color_wire_pow', '--wire-pow'); + setColor(colors, 'color_wire_sel', '--wire-sel'); + setColor(colors, 'color_wire_lose', '--wire-lose'); + setColor(colors, 'color_wire', '--wire-norm'); + setColor(colors, 'text', '--text'); + setColor(colors, 'node', '--node'); + setColor(colors, 'node_norm', '--node-norm'); + setColor(colors, 'splitter', '--splitter'); + setColor(colors, 'out_rect', '--output-rect'); + setColor(colors, 'canvas_stroke', '--canvas-stroke'); + setColor(colors, 'canvas_fill', '--canvas-fill'); return colors; }; /** - * Common canvas theme color object, used for rendering canvas elements + * Common canvas theme color object, used for rendering canvas elements. */ export let colors: Record = getCanvasColors(); /** - * Updates theme - * 1) Sets CSS Variables for UI elements - * 2) Sets color variable for Canvas elements - * @param {string} themeName - Name of the theme to apply + * Updates theme by setting CSS variables and updating the colors object. */ export function updateThemeForStyle(themeName: string): void { const selectedTheme = themeOptions[themeName]; if (selectedTheme === undefined) return; - const html = document.getElementsByTagName('html')[0]; + + const html = document.documentElement; Object.keys(selectedTheme).forEach((property) => { html.style.setProperty(property, selectedTheme[property]); }); + colors = getCanvasColors(); } /** - * Theme Preview Card SVG - * Sets the SVG colors according to theme - * @param {string} themeName - Name of theme - * @returns {string} - SVG HTML as a string + * Helper function to set attributes for SVG elements. + */ +const setAttributes = (element: Element, attributes: Record): void => { + Object.entries(attributes).forEach(([attr, value]) => { + element.setAttribute(attr, value); + }); +}; + +/** + * Generates a theme preview card SVG with colors based on the selected theme. */ export const getThemeCardSvg = (themeName: string): string => { if (!themeOptions[themeName]) { - console.error(`Theme "${themeName}" not found`); - return ''; - } + console.error(`Theme "${themeName}" not found`); + return ''; + } const colors = themeOptions[themeName]; const parser = new DOMParser(); @@ -118,130 +89,115 @@ export const getThemeCardSvg = (themeName: string): string => { console.error('Failed to parse SVG:', parserError); return ''; } - + const svgElement = svgDoc.documentElement; - // Dynamically set the colors according to the theme - svgElement.querySelectorAll('.svgText').forEach((el) => { - el.setAttribute('fill', colors['--text-panel'] || '#000000'); - }); - svgElement.querySelectorAll('.svgNav').forEach((el) => { - el.setAttribute('fill', colors['--bg-tab']); - el.setAttribute('stroke', colors['--br-primary']); - }); - svgElement.querySelectorAll('.svgGridBG').forEach((el) => { - el.setAttribute('fill', colors['--canvas-fill']); - }); - svgElement.querySelectorAll('.svgGrid').forEach((el) => { - el.setAttribute('fill', colors['--canvas-stroke']); - }); - svgElement.querySelectorAll('.svgPanel').forEach((el) => { - el.setAttribute('fill', colors['--primary']); - el.setAttribute('stroke', colors['--br-primary']); - }); - svgElement.querySelectorAll('.svgChev').forEach((el) => { - el.setAttribute('stroke', colors['--br-secondary']); - }); - svgElement.querySelectorAll('.svgHeader').forEach((el) => { - el.setAttribute('fill', colors['--primary']); - }); + const applyStyles = (selector: string, attributes: Record): void => { + svgElement.querySelectorAll(selector).forEach((el) => setAttributes(el, attributes)); + }; + + applyStyles('.svgText', { fill: colors['--text-panel'] || '#000000' }); + applyStyles('.svgNav', { fill: colors['--bg-tab'], stroke: colors['--br-primary'] }); + applyStyles('.svgGridBG', { fill: colors['--canvas-fill'] }); + applyStyles('.svgGrid', { fill: colors['--canvas-stroke'] }); + applyStyles('.svgPanel', { fill: colors['--primary'], stroke: colors['--br-primary'] }); + applyStyles('.svgChev', { stroke: colors['--br-secondary'] }); + applyStyles('.svgHeader', { fill: colors['--primary'] }); return svgElement.outerHTML; }; /** - * Generates theme card HTML - * @param {string} themeName - Name of theme - * @param {boolean} selected - Flag variable for currently selected theme - * @return {string} - Theme card HTML as a string + * Generates theme card HTML. */ -const escapeHtml = (unsafe: string): string => { - return unsafe - .replace(/&/g, "&") - .replace(//g, ">") - .replace(/"/g, """) - .replace(/'/g, "'"); - }; - export const getThemeCard = (themeName: string, selected: boolean): string => { if (themeName === 'Custom Theme') return '
'; - const themeId = escapeHtml(themeName.replace(' ', '')); + + const themeId = themeName.replace(' ', ''); const selectedClass = selected ? 'selected set' : ''; - // themeSel is the hit area + return ` -
-
- ${getThemeCardSvg(themeName)} - - - - -
- `; +
+
+ ${getThemeCardSvg(themeName)} + + + + +
+ `; }; /** - * Create Color Themes Dialog + * Sets up event listeners for theme selection. */ -export const colorThemes = (): void => { - const simulatorStore = SimulatorStore(); - const cleanupListeners: (() => void)[] = []; - - simulatorStore.dialogBox.theme_dialog = true; - const colorThemesDialog = document.getElementById('colorThemesDialog'); - if (colorThemesDialog) { - colorThemesDialog.focus(); - } - - const dialogClickHandler = () => { - const colorThemesDialog = document.getElementById('colorThemesDialog'); - if (colorThemesDialog) { - colorThemesDialog.focus(); - } - }; - - const dialog = document.querySelector('.ui-dialog[aria-describedby="colorThemesDialog"]'); - if (dialog) { - dialog.addEventListener('click', dialogClickHandler); - cleanupListeners.push(() => dialog.removeEventListener('click', dialogClickHandler)); - } +const setupThemeSelectionHandlers = (cleanupListeners: (() => void)[]): void => { document.querySelectorAll('.themeSel').forEach((element) => { const mousedownHandler = (e: MouseEvent) => { e.preventDefault(); - document.querySelectorAll('.selected').forEach((el) => { - el.classList.remove('selected'); - }); + document.querySelectorAll('.selected').forEach((el) => el.classList.remove('selected')); + const themeCard = (e.target as HTMLElement).parentElement; if (themeCard) { themeCard.classList.add('selected'); const radioButton = themeCard.querySelector('input[type=radio]') as HTMLInputElement; - if (radioButton) { - radioButton.click(); // Mark as selected - } + if (radioButton) radioButton.click(); + const label = themeCard.querySelector('label'); - if (label) { - updateThemeForStyle(label.textContent || ''); // Extract theme name and set - } + if (label) updateThemeForStyle(label.textContent || ''); + updateBG(); } }; + element.addEventListener('mousedown', mousedownHandler as EventListener); cleanupListeners.push(() => element.removeEventListener('mousedown', mousedownHandler as EventListener)); }); - - // Add cleanup method to store dynamically +}; + +/** + * Initializes the color themes dialog. + */ +export const colorThemes = (): void => { + const simulatorStore = SimulatorStore(); + const cleanupListeners: (() => void)[] = []; + + simulatorStore.dialogBox.theme_dialog = true; + + const dialog = document.querySelector('.ui-dialog[aria-describedby="colorThemesDialog"]'); + if (dialog) { + const dialogClickHandler = () => { + const colorThemesDialog = document.getElementById('colorThemesDialog'); + if (colorThemesDialog) colorThemesDialog.focus(); + }; + + dialog.addEventListener('click', dialogClickHandler); + cleanupListeners.push(() => dialog.removeEventListener('click', dialogClickHandler)); + } + + setupThemeSelectionHandlers(cleanupListeners); + + // Add cleanup method to store (simulatorStore as any).cleanupThemeDialog = () => { cleanupListeners.forEach(cleanup => cleanup()); }; }; +/** + * Updates the background of the canvas. + */ export const updateBG = (): void => dots(true, false, true); -// Initialize theme on load -(() => { +/** + * Initializes the theme on load. + */ +const initializeTheme = (): void => { + const theme = localStorage.getItem('theme') || 'Default Theme'; if (!localStorage.getItem('theme')) { - localStorage.setItem('theme', 'Default Theme'); + localStorage.setItem('theme', theme); } - updateThemeForStyle(localStorage.getItem('theme') || 'Default Theme'); -})(); \ No newline at end of file + updateThemeForStyle(theme); +}; + +// Initialize theme on load +initializeTheme(); \ No newline at end of file From 6700f1657b9d2a6efbcf7c5721c75fe198b88575 Mon Sep 17 00:00:00 2001 From: Harsh Rao Date: Sat, 8 Feb 2025 17:33:53 +0530 Subject: [PATCH 6/6] resolve --- src/simulator/src/themer/themer.ts | 47 ++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/simulator/src/themer/themer.ts b/src/simulator/src/themer/themer.ts index b2997f7f..d8597939 100644 --- a/src/simulator/src/themer/themer.ts +++ b/src/simulator/src/themer/themer.ts @@ -129,26 +129,41 @@ export const getThemeCard = (themeName: string, selected: boolean): string => { }; /** - * Sets up event listeners for theme selection. + * Handles theme selection logic. */ -const setupThemeSelectionHandlers = (cleanupListeners: (() => void)[]): void => { - document.querySelectorAll('.themeSel').forEach((element) => { - const mousedownHandler = (e: MouseEvent) => { - e.preventDefault(); - document.querySelectorAll('.selected').forEach((el) => el.classList.remove('selected')); +const handleThemeSelection = (e: MouseEvent): void => { + e.preventDefault(); - const themeCard = (e.target as HTMLElement).parentElement; - if (themeCard) { - themeCard.classList.add('selected'); - const radioButton = themeCard.querySelector('input[type=radio]') as HTMLInputElement; - if (radioButton) radioButton.click(); + // Remove 'selected' class from all theme cards + document.querySelectorAll('.selected').forEach((el) => el.classList.remove('selected')); - const label = themeCard.querySelector('label'); - if (label) updateThemeForStyle(label.textContent || ''); + const themeCard = (e.target as HTMLElement).parentElement; + if (!themeCard) return; - updateBG(); - } - }; + // Add 'selected' class to the clicked theme card + themeCard.classList.add('selected'); + + // Find the radio button and label within the theme card + const radioButton = themeCard.querySelector('input[type=radio]') as HTMLInputElement; + const label = themeCard.querySelector('label'); + + if (radioButton) { + radioButton.click(); // Mark the radio button as selected + } + + if (label) { + updateThemeForStyle(label.textContent || ''); // Update the theme based on the label text + } + + updateBG(); // Update the background +}; + +/** + * Sets up event listeners for theme selection. + */ +const setupThemeSelectionHandlers = (cleanupListeners: (() => void)[]): void => { + document.querySelectorAll('.themeSel').forEach((element) => { + const mousedownHandler = (e: MouseEvent) => handleThemeSelection(e); element.addEventListener('mousedown', mousedownHandler as EventListener); cleanupListeners.push(() => element.removeEventListener('mousedown', mousedownHandler as EventListener));