diff --git a/apps/builder/app/builder/features/settings-panel/controls/text-content.tsx b/apps/builder/app/builder/features/settings-panel/controls/text-content.tsx index c7a9fb3bd377..be27c01278ae 100644 --- a/apps/builder/app/builder/features/settings-panel/controls/text-content.tsx +++ b/apps/builder/app/builder/features/settings-panel/controls/text-content.tsx @@ -1,24 +1,24 @@ import { useId, useMemo } from "react"; import { useStore } from "@nanostores/react"; import { computed } from "nanostores"; -import { TextArea } from "@webstudio-is/design-system"; +import { Flex, rawTheme, Text, TextArea } from "@webstudio-is/design-system"; import type { Instance } from "@webstudio-is/sdk"; +import { AlertIcon } from "@webstudio-is/icons"; import { $instances } from "~/shared/nano-states"; -import { serverSyncStore } from "~/shared/sync"; import { BindingControl, BindingPopover, } from "~/builder/shared/binding-popover"; +import { updateWebstudioData } from "~/shared/instance-utils"; import { type ControlProps, useLocalValue, VerticalLayout, $selectedInstanceScope, - Label, updateExpressionValue, useBindingState, - humanizeAttribute, } from "../shared"; +import { FieldLabel } from "../property-label"; const useInstance = (instanceId: Instance["id"]) => { const $store = useMemo(() => { @@ -32,22 +32,20 @@ const updateChildren = ( type: "text" | "expression", value: string ) => { - serverSyncStore.createTransaction([$instances], (instances) => { - const instance = instances.get(instanceId); - if (instance === undefined) { - return; + updateWebstudioData((data) => { + const instance = data.instances.get(instanceId); + if (instance) { + instance.children = [{ type, value }]; } - instance.children = [{ type, value }]; }); }; export const TextContent = ({ instanceId, - meta, - propName, computedValue, }: ControlProps<"textContent">) => { const instance = useInstance(instanceId); + const hasChildren = (instance?.children.length ?? 0) > 0; // text content control is rendered only when empty or single child are present const child = instance?.children?.[0] ?? { type: "text", value: "" }; const localValue = useLocalValue(String(computedValue ?? ""), (value) => { @@ -58,7 +56,6 @@ export const TextContent = ({ } }); const id = useId(); - const label = humanizeAttribute(meta.label || propName); const { scope, aliases } = useStore($selectedInstanceScope); let expression: undefined | string; @@ -76,13 +73,37 @@ export const TextContent = ({ return ( + Plain text content that can be bound to either a variable or a + resource value. + {overwritable === false && ( + + + + The value is controlled by an expression and cannot be + changed. + + + )} + + } + resettable={hasChildren} + onReset={() => { + updateWebstudioData((data) => { + const instance = data.instances.get(instanceId); + if (instance) { + instance.children = []; + } + }); + }} > - {label} - + Text Content + } > @@ -102,7 +123,7 @@ export const TextContent = ({ aliases={aliases} validate={(value) => { if (value !== undefined && typeof value !== "string") { - return `${label} expects a string value`; + return `Text Content expects a string value`; } }} variant={variant} diff --git a/apps/builder/app/builder/features/settings-panel/property-label.tsx b/apps/builder/app/builder/features/settings-panel/property-label.tsx index b7eb8faa93ae..a76151ea4cef 100644 --- a/apps/builder/app/builder/features/settings-panel/property-label.tsx +++ b/apps/builder/app/builder/features/settings-panel/property-label.tsx @@ -1,5 +1,5 @@ import { micromark } from "micromark"; -import { useMemo, useState } from "react"; +import { useMemo, useState, type ReactNode } from "react"; import { computed } from "nanostores"; import { useStore } from "@nanostores/react"; import { @@ -182,9 +182,9 @@ export const FieldLabel = ({ children, }: { /** - * Markdown text to show in tooltip + * Markdown text to show in tooltip or react element */ - description?: string; + description?: string | ReactNode; /** * when true means field has value and colored true */ @@ -193,6 +193,18 @@ export const FieldLabel = ({ children: string; }) => { const [isOpen, setIsOpen] = useState(false); + if (typeof description === "string") { + description = ( + *": { marginTop: 0 }, + }} + dangerouslySetInnerHTML={{ __html: micromark(description) }} + > + ); + } else if (description) { + description = {description}; + } return ( {/* prevent label growing */} @@ -221,16 +233,7 @@ export const FieldLabel = ({ {children} - {description && ( - *": { - marginTop: 0, - }, - }} - dangerouslySetInnerHTML={{ __html: micromark(description) }} - > - )} + {description} {resettable && (