Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: unset variables when create or rename data variables #4808

Merged
merged 5 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 30 additions & 32 deletions apps/builder/app/builder/features/settings-panel/resource-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import {
import { TrashIcon, InfoCircleIcon, PlusIcon } from "@webstudio-is/icons";
import { isFeatureEnabled } from "@webstudio-is/feature-flags";
import { humanizeString } from "~/shared/string-utils";
import { serverSyncStore } from "~/shared/sync";
import {
$dataSources,
$resources,
Expand All @@ -55,10 +54,12 @@ import {
} from "~/builder/shared/code-editor-base";
import { parseCurl, type CurlRequest } from "./curl";
import {
$selectedInstance,
$selectedInstanceKey,
$selectedInstancePath,
$selectedPage,
} from "~/shared/awareness";
import { updateWebstudioData } from "~/shared/instance-utils";
import { restoreTreeVariablesMutable } from "~/shared/data-variables";

const validateUrl = (value: string, scope: Record<string, unknown>) => {
const evaluatedValue = evaluateExpressionWithinScope(value, scope);
Expand Down Expand Up @@ -582,10 +583,11 @@ export const ResourceForm = forwardRef<

useImperativeHandle(ref, () => ({
save: (formData) => {
const instanceId = $selectedInstance.get()?.id;
if (instanceId === undefined) {
const instancePath = $selectedInstancePath.get();
if (instancePath === undefined) {
return;
}
const [{ instance }] = instancePath;
const name = z.string().parse(formData.get("name"));
const newResource: Resource = {
id: resource?.id ?? nanoid(),
Expand All @@ -598,18 +600,16 @@ export const ResourceForm = forwardRef<
const newVariable: DataSource = {
id: variable?.id ?? nanoid(),
// preserve existing instance scope when edit
scopeInstanceId: variable?.scopeInstanceId ?? instanceId,
scopeInstanceId: variable?.scopeInstanceId ?? instance.id,
name,
type: "resource",
resourceId: newResource.id,
};
serverSyncStore.createTransaction(
[$dataSources, $resources],
(dataSources, resources) => {
dataSources.set(newVariable.id, newVariable);
resources.set(newResource.id, newResource);
}
);
updateWebstudioData((data) => {
data.dataSources.set(newVariable.id, newVariable);
data.resources.set(newResource.id, newResource);
restoreTreeVariablesMutable({ instancePath, ...data });
});
},
}));

Expand Down Expand Up @@ -715,10 +715,11 @@ export const SystemResourceForm = forwardRef<

useImperativeHandle(ref, () => ({
save: (formData) => {
const instanceId = $selectedInstance.get()?.id;
if (instanceId === undefined) {
const instancePath = $selectedInstancePath.get();
if (instancePath === undefined) {
return;
}
const [{ instance }] = instancePath;
const name = z.string().parse(formData.get("name"));
const newResource: Resource = {
id: resource?.id ?? nanoid(),
Expand All @@ -731,18 +732,16 @@ export const SystemResourceForm = forwardRef<
const newVariable: DataSource = {
id: variable?.id ?? nanoid(),
// preserve existing instance scope when edit
scopeInstanceId: variable?.scopeInstanceId ?? instanceId,
scopeInstanceId: variable?.scopeInstanceId ?? instance.id,
name,
type: "resource",
resourceId: newResource.id,
};
serverSyncStore.createTransaction(
[$dataSources, $resources],
(dataSources, resources) => {
dataSources.set(newVariable.id, newVariable);
resources.set(newResource.id, newResource);
}
);
updateWebstudioData((data) => {
data.dataSources.set(newVariable.id, newVariable);
data.resources.set(newResource.id, newResource);
restoreTreeVariablesMutable({ instancePath, ...data });
});
},
}));

Expand Down Expand Up @@ -825,10 +824,11 @@ export const GraphqlResourceForm = forwardRef<

useImperativeHandle(ref, () => ({
save: (formData) => {
const instanceId = $selectedInstance.get()?.id;
if (instanceId === undefined) {
const instancePath = $selectedInstancePath.get();
if (instancePath === undefined) {
return;
}
const [{ instance }] = instancePath;
const name = z.string().parse(formData.get("name"));
const body = generateObjectExpression(
new Map([
Expand All @@ -848,18 +848,16 @@ export const GraphqlResourceForm = forwardRef<
const newVariable: DataSource = {
id: variable?.id ?? nanoid(),
// preserve existing instance scope when edit
scopeInstanceId: variable?.scopeInstanceId ?? instanceId,
scopeInstanceId: variable?.scopeInstanceId ?? instance.id,
name,
type: "resource",
resourceId: newResource.id,
};
serverSyncStore.createTransaction(
[$dataSources, $resources],
(dataSources, resources) => {
dataSources.set(newVariable.id, newVariable);
resources.set(newResource.id, newResource);
}
);
updateWebstudioData((data) => {
data.dataSources.set(newVariable.id, newVariable);
data.resources.set(newResource.id, newResource);
restoreTreeVariablesMutable({ instancePath, ...data });
});
},
}));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { CopyIcon, RefreshIcon, UpgradeIcon } from "@webstudio-is/icons";
import {
Box,
Button,
Combobox,
DialogClose,
DialogTitle,
DialogTitleActions,
Expand Down Expand Up @@ -55,9 +56,10 @@ import {
invalidateResource,
getComputedResource,
$userPlanFeatures,
$instances,
$props,
} from "~/shared/nano-states";
import { serverSyncStore } from "~/shared/sync";
import { $selectedInstance } from "~/shared/awareness";
import { $selectedInstance, $selectedInstancePath } from "~/shared/awareness";
import { BindingPopoverProvider } from "~/builder/shared/binding-popover";
import {
EditorDialog,
Expand All @@ -70,6 +72,11 @@ import {
SystemResourceForm,
} from "./resource-panel";
import { generateCurl } from "./curl";
import { updateWebstudioData } from "~/shared/instance-utils";
import {
findUnsetVariableNames,
restoreTreeVariablesMutable,
} from "~/shared/data-variables";

const $variablesByName = computed(
[$selectedInstance, $dataSources],
Expand All @@ -84,6 +91,22 @@ const $variablesByName = computed(
}
);

const $unsetVariableNames = computed(
[$selectedInstancePath, $instances, $props, $dataSources, $resources],
(instancePath, instances, props, dataSources, resources) => {
if (instancePath === undefined) {
return [];
}
return findUnsetVariableNames({
instancePath,
instances,
props,
dataSources,
resources,
});
}
);

const NameField = ({
variableId,
defaultValue,
Expand All @@ -95,6 +118,7 @@ const NameField = ({
const [error, setError] = useState("");
const nameId = useId();
const variablesByName = useStore($variablesByName);
const unsetVariableNames = useStore($unsetVariableNames);
const validateName = useCallback(
(value: string) => {
if (
Expand All @@ -113,10 +137,13 @@ const NameField = ({
useEffect(() => {
ref.current?.setCustomValidity(validateName(defaultValue));
}, [defaultValue, validateName]);
const [value, setValue] = useState(defaultValue);
return (
<Grid gap={1}>
<Label htmlFor={nameId}>Name</Label>
<InputErrorsTooltip errors={error ? [error] : undefined}>
{/* @todo autocomplete unset variables */}
{/*
<InputField
inputRef={ref}
name="name"
Expand All @@ -131,6 +158,28 @@ const NameField = ({
onBlur={() => ref.current?.checkValidity()}
onInvalid={(event) => setError(event.currentTarget.validationMessage)}
/>
*/}
<Combobox<string>
inputRef={ref}
name="name"
id={nameId}
color={error ? "error" : undefined}
itemToString={(item) => item ?? ""}
getItems={() => unsetVariableNames}
value={value}
onItemSelect={(newValue) => {
ref.current?.setCustomValidity(validateName(newValue));
setValue(newValue);
setError("");
}}
onChange={(newValue = "") => {
ref.current?.setCustomValidity(validateName(newValue));
setValue(newValue);
setError("");
}}
onBlur={() => ref.current?.checkValidity()}
onInvalid={(event) => setError(event.currentTarget.validationMessage)}
/>
</InputErrorsTooltip>
</Grid>
);
Expand Down Expand Up @@ -247,13 +296,18 @@ const ParameterForm = forwardRef<
>(({ variable }, ref) => {
useImperativeHandle(ref, () => ({
save: (formData) => {
const instancePath = $selectedInstancePath.get();
if (instancePath === undefined) {
return;
}
// only existing parameter variables can be renamed
if (variable === undefined) {
return;
}
const name = z.string().parse(formData.get("name"));
serverSyncStore.createTransaction([$dataSources], (dataSources) => {
dataSources.set(variable.id, { ...variable, name });
updateWebstudioData((data) => {
data.dataSources.set(variable.id, { ...variable, name });
restoreTreeVariablesMutable({ instancePath, ...data });
});
},
}));
Expand All @@ -272,30 +326,29 @@ const useValuePanelRef = ({
}) => {
useImperativeHandle(ref, () => ({
save: (formData) => {
const instanceId = $selectedInstance.get()?.id;
if (instanceId === undefined) {
const instancePath = $selectedInstancePath.get();
if (instancePath === undefined) {
return;
}
const [{ instance: selectedInstance }] = instancePath;
const dataSourceId = variable?.id ?? nanoid();
// preserve existing instance scope when edit
const scopeInstanceId = variable?.scopeInstanceId ?? instanceId;
const scopeInstanceId = variable?.scopeInstanceId ?? selectedInstance.id;
const name = z.string().parse(formData.get("name"));
serverSyncStore.createTransaction(
[$dataSources, $resources],
(dataSources, resources) => {
// cleanup resource when value variable is set
if (variable?.type === "resource") {
resources.delete(variable.resourceId);
}
dataSources.set(dataSourceId, {
id: dataSourceId,
scopeInstanceId,
name,
type: "variable",
value: variableValue,
});
updateWebstudioData((data) => {
// cleanup resource when value variable is set
if (variable?.type === "resource") {
data.resources.delete(variable.resourceId);
}
);
data.dataSources.set(dataSourceId, {
id: dataSourceId,
scopeInstanceId,
name,
type: "variable",
value: variableValue,
});
restoreTreeVariablesMutable({ instancePath, ...data });
});
},
}));
};
Expand Down
Loading