From c7c641f6480d2adc8205ec7752857ece21572d3f Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Fri, 18 Jul 2025 13:54:05 -0600 Subject: [PATCH 1/2] Update sizing --- .../MetricStylingApp/StylingAppColors/StylingAppColors.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/src/controllers/MetricController/MetricViewChart/MetricEditController/MetricStylingApp/StylingAppColors/StylingAppColors.tsx b/apps/web/src/controllers/MetricController/MetricViewChart/MetricEditController/MetricStylingApp/StylingAppColors/StylingAppColors.tsx index dc8912745..f6d8718f5 100644 --- a/apps/web/src/controllers/MetricController/MetricViewChart/MetricEditController/MetricStylingApp/StylingAppColors/StylingAppColors.tsx +++ b/apps/web/src/controllers/MetricController/MetricViewChart/MetricEditController/MetricStylingApp/StylingAppColors/StylingAppColors.tsx @@ -22,12 +22,12 @@ export const StylingAppColors: React.FC<{ }); return ( -
+
{/*
*/} -
+
Date: Fri, 18 Jul 2025 14:12:12 -0600 Subject: [PATCH 2/2] save should trigger close --- .../AddCustomThemeBase.tsx | 24 +++------- .../AddThemeProviderWrapper.tsx | 8 +++- .../EditCustomThemeMenu.tsx | 39 ++++++++-------- .../DefaultThemeSelector/NewThemePopup.tsx | 46 ++++++++++++++++--- .../features/colors/ThemeList/ThemeList.tsx | 11 ++--- 5 files changed, 79 insertions(+), 49 deletions(-) diff --git a/apps/web/src/components/features/colors/DefaultThemeSelector/AddCustomThemeBase.tsx b/apps/web/src/components/features/colors/DefaultThemeSelector/AddCustomThemeBase.tsx index 83d3d9ec1..5b02e2b2b 100644 --- a/apps/web/src/components/features/colors/DefaultThemeSelector/AddCustomThemeBase.tsx +++ b/apps/web/src/components/features/colors/DefaultThemeSelector/AddCustomThemeBase.tsx @@ -1,10 +1,9 @@ -import React, { useRef, type PropsWithChildren } from 'react'; +import React, { useRef } from 'react'; import { ThemeList, type IColorPalette } from '../ThemeList'; import { Button } from '@/components/ui/buttons'; import { Plus } from '../../../ui/icons'; import { NewThemePopup } from './NewThemePopup'; import { useMemoizedFn } from '@/hooks/useMemoizedFn'; -import { Popover } from '../../../ui/popover'; import { EditCustomThemeMenu } from './EditCustomThemeMenu'; import { AddThemeProviderWrapper, useAddTheme } from './AddThemeProviderWrapper'; @@ -59,25 +58,16 @@ const AddCustomThemeButton: React.FC = React.memo(({}) => { const { createCustomTheme } = useAddTheme(); const buttonRef = useRef(null); - const closePopover = useMemoizedFn(() => { - buttonRef.current?.click(); - }); - - const onSave = useMemoizedFn(async (theme: IColorPalette) => { - await createCustomTheme(theme); - closePopover(); - }); - return ( - } - trigger="click" - className="max-w-[320px] p-0" - sideOffset={12}> + - + ); }); diff --git a/apps/web/src/components/features/colors/DefaultThemeSelector/AddThemeProviderWrapper.tsx b/apps/web/src/components/features/colors/DefaultThemeSelector/AddThemeProviderWrapper.tsx index 484351f1b..d1e7efbd2 100644 --- a/apps/web/src/components/features/colors/DefaultThemeSelector/AddThemeProviderWrapper.tsx +++ b/apps/web/src/components/features/colors/DefaultThemeSelector/AddThemeProviderWrapper.tsx @@ -27,5 +27,11 @@ export const AddThemeProviderWrapper: React.FC> }; export const useAddTheme = () => { - return React.useContext(AddThemeProvider); + const context = React.useContext(AddThemeProvider); + + if (!context) { + throw new Error('useAddTheme must be used within an AddThemeProvider'); + } + + return context; }; diff --git a/apps/web/src/components/features/colors/DefaultThemeSelector/EditCustomThemeMenu.tsx b/apps/web/src/components/features/colors/DefaultThemeSelector/EditCustomThemeMenu.tsx index 47ac2e91e..0503cfa37 100644 --- a/apps/web/src/components/features/colors/DefaultThemeSelector/EditCustomThemeMenu.tsx +++ b/apps/web/src/components/features/colors/DefaultThemeSelector/EditCustomThemeMenu.tsx @@ -1,27 +1,30 @@ -import React from 'react'; +import React, { type PropsWithChildren } from 'react'; import type { IColorPalette } from '../ThemeList'; import { useMemoizedFn } from '@/hooks'; import { NewThemePopup } from './NewThemePopup'; import { useAddTheme } from './AddThemeProviderWrapper'; -export const EditCustomThemeMenu: React.FC<{ theme: IColorPalette }> = React.memo(({ theme }) => { - const { deleteCustomTheme, modifyCustomTheme } = useAddTheme(); +export const EditCustomThemeMenu: React.FC> = + React.memo(({ theme, children }) => { + const { deleteCustomTheme, modifyCustomTheme } = useAddTheme(); - const onSave = useMemoizedFn(async (theme: IColorPalette) => { - await modifyCustomTheme(theme.id, theme); + const onSave = useMemoizedFn((theme: IColorPalette) => { + return modifyCustomTheme(theme.id, theme); + }); + + const onDelete = useMemoizedFn((themeId: string) => { + return deleteCustomTheme(themeId); + }); + + const onUpdate = useMemoizedFn((theme: IColorPalette) => { + return modifyCustomTheme(theme.id, theme); + }); + + return ( + + {children} + + ); }); - const onDelete = useMemoizedFn(async (themeId: string) => { - await deleteCustomTheme(themeId); - }); - - const onUpdate = useMemoizedFn(async (theme: IColorPalette) => { - await modifyCustomTheme(theme.id, theme); - }); - - return ( - - ); -}); - EditCustomThemeMenu.displayName = 'EditCustomThemeMenu'; diff --git a/apps/web/src/components/features/colors/DefaultThemeSelector/NewThemePopup.tsx b/apps/web/src/components/features/colors/DefaultThemeSelector/NewThemePopup.tsx index 7d4a7c4d5..d366820ca 100644 --- a/apps/web/src/components/features/colors/DefaultThemeSelector/NewThemePopup.tsx +++ b/apps/web/src/components/features/colors/DefaultThemeSelector/NewThemePopup.tsx @@ -1,14 +1,15 @@ import { Button } from '@/components/ui/buttons'; import { Input } from '@/components/ui/inputs'; import { Text } from '@/components/ui/typography'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import type { IColorPalette } from '../ThemeList'; import { useMemoizedFn } from '@/hooks'; import { v4 as uuidv4 } from 'uuid'; -import { Plus, Trash } from '../../../ui/icons'; +import { Plus, Trash, FloppyDisk } from '../../../ui/icons'; import { ColorPickButton } from './DraggableColorPicker'; import { inputHasText } from '@/lib/text'; import { DEFAULT_CHART_THEME } from '@buster/server-shared/metrics'; +import { Popover } from '../../../ui/popover'; interface NewThemePopupProps { selectedTheme?: IColorPalette; @@ -17,8 +18,16 @@ interface NewThemePopupProps { onUpdate?: (theme: IColorPalette) => Promise; } -export const NewThemePopup = React.memo( - ({ selectedTheme, onDelete, onUpdate, onSave }: NewThemePopupProps) => { +const NewThemePopupContent = React.memo( + ({ + selectedTheme, + onDelete, + onUpdate, + onSave, + triggerRef + }: NewThemePopupProps & { + triggerRef: React.RefObject; + }) => { const [title, setTitle] = useState(''); const [colors, setColors] = useState(DEFAULT_CHART_THEME); const [id, setId] = useState(uuidv4()); @@ -32,8 +41,13 @@ export const NewThemePopup = React.memo( setId(uuidv4()); }); + const closePopover = useMemoizedFn(() => { + triggerRef.current?.click(); + }); + const onDeleteClick = useMemoizedFn(async () => { if (selectedTheme) await onDelete?.(id); + closePopover(); setTimeout(() => { reset(); }, 350); @@ -41,6 +55,7 @@ export const NewThemePopup = React.memo( const onSaveClick = useMemoizedFn(async () => { await onSave({ id, name: title, colors }); + closePopover(); setTimeout(() => { reset(); }, 350); @@ -48,6 +63,7 @@ export const NewThemePopup = React.memo( const onUpdateClick = useMemoizedFn(async () => { await onUpdate?.({ id, name: title, colors }); + closePopover(); setTimeout(() => { reset(); }, 350); @@ -89,7 +105,7 @@ export const NewThemePopup = React.memo( block disabled={disableCreateTheme} onClick={isNewTheme ? onSaveClick : onUpdateClick} - prefix={}> + prefix={isNewTheme ? : }> {isNewTheme || !onUpdate ? 'Create theme' : 'Update theme'}
@@ -98,4 +114,22 @@ export const NewThemePopup = React.memo( } ); -NewThemePopup.displayName = 'NewThemePopup'; +NewThemePopupContent.displayName = 'NewThemePopupContent'; + +export const NewThemePopup = ({ + children, + ...props +}: NewThemePopupProps & { children: React.ReactNode }) => { + const triggerRef = useRef(null); + return ( + } + trigger="click" + className="max-w-[320px] p-0" + sideOffset={12}> + + {children} + + + ); +}; diff --git a/apps/web/src/components/features/colors/ThemeList/ThemeList.tsx b/apps/web/src/components/features/colors/ThemeList/ThemeList.tsx index 35ff8cca0..5a333cfc6 100644 --- a/apps/web/src/components/features/colors/ThemeList/ThemeList.tsx +++ b/apps/web/src/components/features/colors/ThemeList/ThemeList.tsx @@ -11,7 +11,7 @@ export const ThemeList: React.FC<{ themes: IColorPalette[]; className?: string; onChangeColorTheme: (theme: IColorPalette) => void; - themeThreeDotsMenu?: React.FC<{ theme: IColorPalette }>; + themeThreeDotsMenu?: React.FC<{ theme: IColorPalette; children: React.ReactNode }>; }> = ({ themes, className, themeThreeDotsMenu, onChangeColorTheme }) => { return (
; + threeDotMenu?: React.FC<{ theme: IColorPalette; children: React.ReactNode }>; onChangeColorTheme: (theme: IColorPalette) => void; }> = React.memo(({ theme, selected = false, threeDotMenu, onChangeColorTheme }) => { const { name, colors } = theme; @@ -65,17 +65,14 @@ const ColorOption: React.FC<{ {shouldShowMenu && (
e.stopPropagation()}> - } - trigger="click"> +
)}