Skip to content

Commit

Permalink
feat: show inherited data variables
Browse files Browse the repository at this point in the history
Here made a few changes to align data variables with css variables.

1. inherited variables are also shown in "Data Variables" section in
   Settings
2. local and remote variables have blue and orange labels
3. local variables with the same name "mask" inherited variables in the
   list
  • Loading branch information
TrySound committed Jan 23, 2025
1 parent 6d2c297 commit 0da0d56
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,30 @@ import {
import {
$selectedInstance,
$selectedInstanceKey,
$selectedInstancePath,
$selectedPage,
} from "~/shared/awareness";

/**
* find variables defined specifically on this selected instance
*/
const $instanceVariables = computed(
[$selectedInstance, $dataSources],
(instance, dataSources) => {
const matchedVariables: DataSource[] = [];
if (instance === undefined) {
return matchedVariables;
const $availableVariables = computed(
[$selectedInstancePath, $dataSources],
(instancePath, dataSources) => {
if (instancePath === undefined) {
return [];
}
for (const dataSource of dataSources.values()) {
if (instance.id === dataSource.scopeInstanceId) {
matchedVariables.push(dataSource);
const availableVariables = new Map<DataSource["name"], DataSource>();
// order from ancestor to descendant
// so descendants can override ancestor variables
for (const { instance } of instancePath.slice().reverse()) {
for (const dataSource of dataSources.values()) {
if (dataSource.scopeInstanceId === instance.id) {
availableVariables.set(dataSource.name, dataSource);
}
}
}
return matchedVariables;
return Array.from(availableVariables.values());
}
);

Expand Down Expand Up @@ -181,27 +186,33 @@ const EmptyVariables = () => {

const VariablesItem = ({
variable,
source,
index,
value,
usageCount,
}: {
variable: DataSource;
source: "local" | "remote";
index: number;
value: unknown;
usageCount: number;
}) => {
const label =
value === undefined
? variable.name
: `${variable.name}: ${formatValuePreview(value)}`;
const labelValue =
value === undefined ? "" : `: ${formatValuePreview(value)}`;
const [inspectDialogOpen, setInspectDialogOpen] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
return (
<VariablePopoverTrigger key={variable.id} variable={variable}>
<CssValueListItem
id={variable.id}
index={index}
label={<Label truncate>{label}</Label>}
label={
<Flex align="center">
<Label color={source}>{variable.name}</Label>
{labelValue}
</Flex>
}
disabled={source === "remote"}
data-state={isMenuOpen ? "open" : undefined}
buttons={
<>
Expand Down Expand Up @@ -234,13 +245,15 @@ const VariablesItem = ({
<DropdownMenuItem onSelect={() => setInspectDialogOpen(true)}>
Inspect
</DropdownMenuItem>
<DropdownMenuItem
// allow to delete only unused variables
disabled={variable.type === "parameter" || usageCount > 0}
onSelect={() => deleteVariable(variable.id)}
>
Delete {usageCount > 0 && `(${usageCount} bindings)`}
</DropdownMenuItem>
{source === "local" && (
<DropdownMenuItem
// allow to delete only unused variables
disabled={variable.type === "parameter" || usageCount > 0}
onSelect={() => deleteVariable(variable.id)}
>
Delete {usageCount > 0 && `(${usageCount} bindings)`}
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenuPortal>
</DropdownMenu>
Expand All @@ -252,7 +265,8 @@ const VariablesItem = ({
};

const VariablesList = () => {
const availableVariables = useStore($instanceVariables);
const instance = useStore($selectedInstance);
const availableVariables = useStore($availableVariables);
const variableValues = useStore($instanceVariableValues);
const usedVariables = useStore($usedVariables);

Expand All @@ -262,18 +276,18 @@ const VariablesList = () => {

return (
<CssValueListArrowFocus>
{availableVariables.map((variable, index) => {
const value = variableValues.get(variable.id);
return (
<VariablesItem
key={variable.id}
value={value}
variable={variable}
index={index}
usageCount={usedVariables.get(variable.id) ?? 0}
/>
);
})}
{availableVariables.map((variable, index) => (
<VariablesItem
key={variable.id}
source={
instance?.id === variable.scopeInstanceId ? "local" : "remote"
}
value={variableValues.get(variable.id)}
variable={variable}
index={index}
usageCount={usedVariables.get(variable.id) ?? 0}
/>
))}
</CssValueListArrowFocus>
);
};
Expand Down
2 changes: 1 addition & 1 deletion apps/builder/app/builder/shared/binding-popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
createContext,
type ReactNode,
} from "react";
import { useStore } from "@nanostores/react";
import {
DotIcon,
InfoCircleIcon,
Expand Down Expand Up @@ -47,7 +48,6 @@ import {
$isDesignMode,
computeExpression,
} from "~/shared/nano-states";
import { useStore } from "@nanostores/react";

export const evaluateExpressionWithinScope = (
expression: string,
Expand Down
6 changes: 4 additions & 2 deletions apps/builder/app/shared/instance-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1087,14 +1087,16 @@ export const insertWebstudioFragmentCopy = ({
dataSources.set(newDataSourceId, {
...dataSource,
id: newDataSourceId,
scopeInstanceId: newInstanceIds.get(scopeInstanceId),
scopeInstanceId:
newInstanceIds.get(scopeInstanceId) ?? scopeInstanceId,
resourceId: newResourceId,
});
} else {
dataSources.set(newDataSourceId, {
...dataSource,
id: newDataSourceId,
scopeInstanceId: newInstanceIds.get(scopeInstanceId),
scopeInstanceId:
newInstanceIds.get(scopeInstanceId) ?? scopeInstanceId,
});
}
}
Expand Down
6 changes: 6 additions & 0 deletions apps/builder/app/shared/nano-states/props.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,14 @@ test("compute expression prop values", () => {
toMap([
{
id: "var1",
scopeInstanceId: "box",
type: "variable",
name: "",
value: { type: "number", value: 1 },
},
{
id: "var2",
scopeInstanceId: "box",
type: "variable",
name: "",
value: { type: "string", value: "Hello" },
Expand Down Expand Up @@ -162,6 +164,7 @@ test("generate action prop callbacks", () => {
toMap([
{
id: "var",
scopeInstanceId: "box",
type: "variable",
name: "",
value: { type: "number", value: 1 },
Expand Down Expand Up @@ -292,6 +295,7 @@ test("compute expression from collection items", () => {
toMap([
{
id: "itemId",
scopeInstanceId: "list",
type: "parameter",
name: "item",
},
Expand Down Expand Up @@ -362,6 +366,7 @@ test("access parameter value from variables values", () => {
toMap([
{
id: "parameterId",
scopeInstanceId: "body",
type: "parameter",
name: "paramName",
},
Expand Down Expand Up @@ -400,6 +405,7 @@ test("compute props bound to resource variables", () => {
toMap([
{
id: "resourceVariableId",
scopeInstanceId: "body",
type: "resource",
name: "paramName",
resourceId: "resourceId",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export const CssValueListItem = forwardRef(
{...listItemAttributes}
{...rest}
hidden={hidden}
disabled={hidden === true}
disabled={hidden === true || rest.disabled}
>
<DragHandleIconStyled />

Expand Down
6 changes: 3 additions & 3 deletions packages/sdk/src/schema/data-sources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,20 @@ export const DataSource = z.union([
z.object({
type: z.literal("variable"),
id: DataSourceId,
scopeInstanceId: z.optional(z.string()),
scopeInstanceId: z.string(),
name: z.string(),
value: DataSourceVariableValue,
}),
z.object({
type: z.literal("parameter"),
id: DataSourceId,
scopeInstanceId: z.optional(z.string()),
scopeInstanceId: z.string(),
name: z.string(),
}),
z.object({
type: z.literal("resource"),
id: DataSourceId,
scopeInstanceId: z.optional(z.string()),
scopeInstanceId: z.string(),
name: z.string(),
resourceId: z.string(),
}),
Expand Down

0 comments on commit 0da0d56

Please sign in to comment.