Skip to content

Commit

Permalink
fix: sidebar loses focus on input (#100)
Browse files Browse the repository at this point in the history
creates a ref for the themeHistory state, which allows access to the
latest values inside the callback without regenerating the components

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
benlife5 and github-actions[bot] authored Oct 22, 2024
1 parent c5e80f4 commit d2049ec
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 65 deletions.
34 changes: 22 additions & 12 deletions src/internal/components/InternalThemeEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Puck, Config, InitialHistory } from "@measured/puck";
import React from "react";
import React, { useCallback, useEffect, useRef } from "react";
import { useState } from "react";
import { TemplateMetadata } from "../types/templateMetadata.ts";
import { EntityFieldProvider } from "../../components/EntityField.tsx";
Expand Down Expand Up @@ -45,6 +45,11 @@ export const InternalThemeEditor = ({
buildThemeLocalStorageKey,
}: InternalThemeEditorProps) => {
const [canEdit, setCanEdit] = useState<boolean>(false); // helps sync puck preview and save state
const themeHistoriesRef = useRef(themeHistories);

useEffect(() => {
themeHistoriesRef.current = themeHistories;
}, [themeHistories]);

const handlePublishTheme = async () => {
devLogger.logFunc("saveThemeData");
Expand All @@ -69,21 +74,22 @@ export const InternalThemeEditor = ({
};

const handleThemeChange = (topLevelKey: string, newValue: any) => {
if (!themeHistories || !themeConfig) {
if (!themeHistoriesRef.current || !themeConfig) {
return;
}

const newThemeValues = {
...themeHistories.histories[themeHistories.index]?.data,
...themeHistoriesRef.current.histories[themeHistoriesRef.current.index]
?.data,
...generateCssVariablesFromPuckFields(newValue, topLevelKey),
};

const newHistory = {
histories: [
...themeHistories.histories,
...themeHistoriesRef.current.histories,
{ id: uuidv4(), data: newThemeValues },
] as ThemeHistory[],
index: themeHistories.histories.length,
index: themeHistoriesRef.current.histories.length,
};

window.localStorage.setItem(
Expand Down Expand Up @@ -124,6 +130,16 @@ export const InternalThemeEditor = ({
}
};

const fieldsOverride = useCallback(() => {
return (
<ThemeSidebar
themeHistoriesRef={themeHistoriesRef}
themeConfig={themeConfig}
onThemeChange={handleThemeChange}
/>
);
}, []);

return (
<EntityFieldProvider>
<Puck
Expand Down Expand Up @@ -152,13 +168,7 @@ export const InternalThemeEditor = ({
),
actionBar: () => <></>,
components: () => <></>,
fields: () => (
<ThemeSidebar
themeConfig={themeConfig}
themeHistory={themeHistories!.histories}
onThemeChange={handleThemeChange}
/>
),
fields: fieldsOverride,
}}
/>
</EntityFieldProvider>
Expand Down
4 changes: 2 additions & 2 deletions src/internal/components/ThemeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,9 @@ export const ThemeEditor = (props: ThemeEditorProps) => {
buildThemeLocalStorageKey,
]);

// Log THEME_INITIAL_HISTORY on load and update theme in editor to reflect save state
// Log THEME_HISTORIES on load and update theme in editor to reflect save state
useEffect(() => {
devLogger.logData("THEME_INITIAL_HISTORY", themeHistories);
devLogger.logData("THEME_HISTORIES", themeHistories);
if (themeHistories && themeConfig) {
updateThemeInEditor(
themeHistories.histories[themeHistories.index]?.data as SavedTheme,
Expand Down
75 changes: 31 additions & 44 deletions src/internal/puck/components/ColorSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,39 @@
import React, { useState } from "react";
import { Field, FieldLabel } from "@measured/puck";
import { FieldLabel } from "@measured/puck";
import { RenderProps } from "../../utils/renderEntityFields.ts";
import { Color, ColorResult, SketchPicker } from "react-color";

export type ColorSelectorProps = {
label: string;
};

export const ColorSelector = (props: ColorSelectorProps): Field => {
return {
type: "custom",
label: props.label,
render: ({ field, value, onChange }: RenderProps) => {
const [isOpen, setIsOpen] = useState(false);
export const ColorSelector = ({ field, value, onChange }: RenderProps) => {
const [isOpen, setIsOpen] = useState(false);

const fieldStyles = colorPickerStyles(value);
return (
<>
<FieldLabel
label={field.label || "Label is undefined"}
className="ve-mt-2.5"
>
<div
style={fieldStyles.swatch}
onClick={() => setIsOpen((current) => !current)}
>
<div style={fieldStyles.color} />
</div>
{isOpen && (
<div style={fieldStyles.popover}>
<div
style={fieldStyles.cover}
onClick={() => setIsOpen(false)}
/>
<SketchPicker
disableAlpha={true}
color={value}
onChange={(colorResult: ColorResult) => {
onChange(colorResult.hex);
}}
/>
</div>
)}
</FieldLabel>
</>
);
},
};
const fieldStyles = colorPickerStyles(value);
return (
<>
<FieldLabel
label={field.label || "Label is undefined"}
className="ve-mt-2.5"
>
<div
style={fieldStyles.swatch}
onClick={() => setIsOpen((current) => !current)}
>
<div style={fieldStyles.color} />
</div>
{isOpen && (
<div style={fieldStyles.popover}>
<div style={fieldStyles.cover} onClick={() => setIsOpen(false)} />
<SketchPicker
disableAlpha={true}
color={value}
onChange={(colorResult: ColorResult) => {
onChange(colorResult.hex);
}}
/>
</div>
)}
</FieldLabel>
</>
);
};

const colorPickerStyles = (color: Color) => {
Expand Down
16 changes: 12 additions & 4 deletions src/internal/puck/components/ThemeSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@ import {
constructThemePuckFields,
constructThemePuckValues,
} from "../../utils/constructThemePuckFields.ts";
import { ThemeHistory } from "../../types/themeData.ts";
import { ThemeHistories } from "../../types/themeData.ts";

type ThemeSidebarProps = {
themeHistoriesRef: React.MutableRefObject<ThemeHistories | undefined>;
themeConfig?: ThemeConfig;
themeHistory: ThemeHistory[];
onThemeChange: (parentStyleKey: string, value: Record<string, any>) => void;
};

const ThemeSidebar = (props: ThemeSidebarProps) => {
const { themeConfig, themeHistory, onThemeChange } = props;
const { themeConfig, themeHistoriesRef, onThemeChange } = props;

if (!themeHistoriesRef.current) {
return;
}

if (!themeConfig) {
return (
<div>
Expand All @@ -28,6 +33,9 @@ const ThemeSidebar = (props: ThemeSidebarProps) => {
);
}

const themeData =
themeHistoriesRef.current?.histories[themeHistoriesRef.current?.index].data;

return (
<div>
<Alert>
Expand All @@ -39,7 +47,7 @@ const ThemeSidebar = (props: ThemeSidebarProps) => {
{Object.entries(themeConfig).map(([parentStyleKey, parentStyle]) => {
const field = constructThemePuckFields(parentStyle);
const values = constructThemePuckValues(
themeHistory[themeHistory.length - 1]?.data,
themeData,
parentStyle,
parentStyleKey
);
Expand Down
13 changes: 11 additions & 2 deletions src/internal/utils/constructThemePuckFields.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { ObjectField, SelectField, NumberField } from "@measured/puck";
import {
ObjectField,
SelectField,
NumberField,
CustomField,
} from "@measured/puck";
import { ParentStyle, SavedTheme, Style } from "../../utils/themeResolver.ts";
import { ColorSelector } from "../puck/components/ColorSelector.tsx";

Expand Down Expand Up @@ -36,7 +41,11 @@ export const convertStyleToPuckField = (style: Style) => {
options: style.options,
} as SelectField;
case "color":
return ColorSelector({ label: style.label });
return {
label: style.label,
type: "custom",
render: ColorSelector,
} as CustomField;
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/utils/devLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export type DevLoggerPrefix =
| "THEME_SAVE_STATE"
| "VISUAL_CONFIGURATION_DATA"
| "THEME_DATA"
| "THEME_INITIAL_HISTORY"
| "THEME_HISTORIES"
| "DOCUMENT"
| "PUCK_INDEX"
| "PUCK_HISTORY"
Expand Down

0 comments on commit d2049ec

Please sign in to comment.