Skip to content

Commit 080168e

Browse files
authored
feat: show inherited data variables (#4774)
Ref #4768 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 4. inherited variables can only be inspected <img width="244" alt="image" src="https://github.com/user-attachments/assets/bf4648e6-b5fc-48cb-a6c3-a6d5e8b234cd" />
1 parent 6d2c297 commit 080168e

File tree

6 files changed

+64
-42
lines changed

6 files changed

+64
-42
lines changed

apps/builder/app/builder/features/settings-panel/variables-section.tsx

+49-35
Original file line numberDiff line numberDiff line change
@@ -48,25 +48,30 @@ import {
4848
import {
4949
$selectedInstance,
5050
$selectedInstanceKey,
51+
$selectedInstancePath,
5152
$selectedPage,
5253
} from "~/shared/awareness";
5354

5455
/**
5556
* find variables defined specifically on this selected instance
5657
*/
57-
const $instanceVariables = computed(
58-
[$selectedInstance, $dataSources],
59-
(instance, dataSources) => {
60-
const matchedVariables: DataSource[] = [];
61-
if (instance === undefined) {
62-
return matchedVariables;
58+
const $availableVariables = computed(
59+
[$selectedInstancePath, $dataSources],
60+
(instancePath, dataSources) => {
61+
if (instancePath === undefined) {
62+
return [];
6363
}
64-
for (const dataSource of dataSources.values()) {
65-
if (instance.id === dataSource.scopeInstanceId) {
66-
matchedVariables.push(dataSource);
64+
const availableVariables = new Map<DataSource["name"], DataSource>();
65+
// order from ancestor to descendant
66+
// so descendants can override ancestor variables
67+
for (const { instance } of instancePath.slice().reverse()) {
68+
for (const dataSource of dataSources.values()) {
69+
if (dataSource.scopeInstanceId === instance.id) {
70+
availableVariables.set(dataSource.name, dataSource);
71+
}
6772
}
6873
}
69-
return matchedVariables;
74+
return Array.from(availableVariables.values());
7075
}
7176
);
7277

@@ -181,27 +186,33 @@ const EmptyVariables = () => {
181186

182187
const VariablesItem = ({
183188
variable,
189+
source,
184190
index,
185191
value,
186192
usageCount,
187193
}: {
188194
variable: DataSource;
195+
source: "local" | "remote";
189196
index: number;
190197
value: unknown;
191198
usageCount: number;
192199
}) => {
193-
const label =
194-
value === undefined
195-
? variable.name
196-
: `${variable.name}: ${formatValuePreview(value)}`;
200+
const labelValue =
201+
value === undefined ? "" : `: ${formatValuePreview(value)}`;
197202
const [inspectDialogOpen, setInspectDialogOpen] = useState(false);
198203
const [isMenuOpen, setIsMenuOpen] = useState(false);
199204
return (
200205
<VariablePopoverTrigger key={variable.id} variable={variable}>
201206
<CssValueListItem
202207
id={variable.id}
203208
index={index}
204-
label={<Label truncate>{label}</Label>}
209+
label={
210+
<Flex align="center">
211+
<Label color={source}>{variable.name}</Label>
212+
{labelValue}
213+
</Flex>
214+
}
215+
disabled={source === "remote"}
205216
data-state={isMenuOpen ? "open" : undefined}
206217
buttons={
207218
<>
@@ -234,13 +245,15 @@ const VariablesItem = ({
234245
<DropdownMenuItem onSelect={() => setInspectDialogOpen(true)}>
235246
Inspect
236247
</DropdownMenuItem>
237-
<DropdownMenuItem
238-
// allow to delete only unused variables
239-
disabled={variable.type === "parameter" || usageCount > 0}
240-
onSelect={() => deleteVariable(variable.id)}
241-
>
242-
Delete {usageCount > 0 && `(${usageCount} bindings)`}
243-
</DropdownMenuItem>
248+
{source === "local" && (
249+
<DropdownMenuItem
250+
// allow to delete only unused variables
251+
disabled={variable.type === "parameter" || usageCount > 0}
252+
onSelect={() => deleteVariable(variable.id)}
253+
>
254+
Delete {usageCount > 0 && `(${usageCount} bindings)`}
255+
</DropdownMenuItem>
256+
)}
244257
</DropdownMenuContent>
245258
</DropdownMenuPortal>
246259
</DropdownMenu>
@@ -252,7 +265,8 @@ const VariablesItem = ({
252265
};
253266

254267
const VariablesList = () => {
255-
const availableVariables = useStore($instanceVariables);
268+
const instance = useStore($selectedInstance);
269+
const availableVariables = useStore($availableVariables);
256270
const variableValues = useStore($instanceVariableValues);
257271
const usedVariables = useStore($usedVariables);
258272

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

263277
return (
264278
<CssValueListArrowFocus>
265-
{availableVariables.map((variable, index) => {
266-
const value = variableValues.get(variable.id);
267-
return (
268-
<VariablesItem
269-
key={variable.id}
270-
value={value}
271-
variable={variable}
272-
index={index}
273-
usageCount={usedVariables.get(variable.id) ?? 0}
274-
/>
275-
);
276-
})}
279+
{availableVariables.map((variable, index) => (
280+
<VariablesItem
281+
key={variable.id}
282+
source={
283+
instance?.id === variable.scopeInstanceId ? "local" : "remote"
284+
}
285+
value={variableValues.get(variable.id)}
286+
variable={variable}
287+
index={index}
288+
usageCount={usedVariables.get(variable.id) ?? 0}
289+
/>
290+
))}
277291
</CssValueListArrowFocus>
278292
);
279293
};

apps/builder/app/builder/shared/binding-popover.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
createContext,
99
type ReactNode,
1010
} from "react";
11+
import { useStore } from "@nanostores/react";
1112
import {
1213
DotIcon,
1314
InfoCircleIcon,
@@ -47,7 +48,6 @@ import {
4748
$isDesignMode,
4849
computeExpression,
4950
} from "~/shared/nano-states";
50-
import { useStore } from "@nanostores/react";
5151

5252
export const evaluateExpressionWithinScope = (
5353
expression: string,

apps/builder/app/shared/instance-utils.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1087,14 +1087,16 @@ export const insertWebstudioFragmentCopy = ({
10871087
dataSources.set(newDataSourceId, {
10881088
...dataSource,
10891089
id: newDataSourceId,
1090-
scopeInstanceId: newInstanceIds.get(scopeInstanceId),
1090+
scopeInstanceId:
1091+
newInstanceIds.get(scopeInstanceId) ?? scopeInstanceId,
10911092
resourceId: newResourceId,
10921093
});
10931094
} else {
10941095
dataSources.set(newDataSourceId, {
10951096
...dataSource,
10961097
id: newDataSourceId,
1097-
scopeInstanceId: newInstanceIds.get(scopeInstanceId),
1098+
scopeInstanceId:
1099+
newInstanceIds.get(scopeInstanceId) ?? scopeInstanceId,
10981100
});
10991101
}
11001102
}

apps/builder/app/shared/nano-states/props.test.ts

+6
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,14 @@ test("compute expression prop values", () => {
9393
toMap([
9494
{
9595
id: "var1",
96+
scopeInstanceId: "box",
9697
type: "variable",
9798
name: "",
9899
value: { type: "number", value: 1 },
99100
},
100101
{
101102
id: "var2",
103+
scopeInstanceId: "box",
102104
type: "variable",
103105
name: "",
104106
value: { type: "string", value: "Hello" },
@@ -162,6 +164,7 @@ test("generate action prop callbacks", () => {
162164
toMap([
163165
{
164166
id: "var",
167+
scopeInstanceId: "box",
165168
type: "variable",
166169
name: "",
167170
value: { type: "number", value: 1 },
@@ -292,6 +295,7 @@ test("compute expression from collection items", () => {
292295
toMap([
293296
{
294297
id: "itemId",
298+
scopeInstanceId: "list",
295299
type: "parameter",
296300
name: "item",
297301
},
@@ -362,6 +366,7 @@ test("access parameter value from variables values", () => {
362366
toMap([
363367
{
364368
id: "parameterId",
369+
scopeInstanceId: "body",
365370
type: "parameter",
366371
name: "paramName",
367372
},
@@ -400,6 +405,7 @@ test("compute props bound to resource variables", () => {
400405
toMap([
401406
{
402407
id: "resourceVariableId",
408+
scopeInstanceId: "body",
403409
type: "resource",
404410
name: "paramName",
405411
resourceId: "resourceId",

packages/design-system/src/components/css-value-list-item.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ export const CssValueListItem = forwardRef(
180180
{...listItemAttributes}
181181
{...rest}
182182
hidden={hidden}
183-
disabled={hidden === true}
183+
disabled={hidden === true || rest.disabled}
184184
>
185185
<DragHandleIconStyled />
186186

packages/sdk/src/schema/data-sources.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,20 @@ export const DataSource = z.union([
3030
z.object({
3131
type: z.literal("variable"),
3232
id: DataSourceId,
33-
scopeInstanceId: z.optional(z.string()),
33+
scopeInstanceId: z.string(),
3434
name: z.string(),
3535
value: DataSourceVariableValue,
3636
}),
3737
z.object({
3838
type: z.literal("parameter"),
3939
id: DataSourceId,
40-
scopeInstanceId: z.optional(z.string()),
40+
scopeInstanceId: z.string(),
4141
name: z.string(),
4242
}),
4343
z.object({
4444
type: z.literal("resource"),
4545
id: DataSourceId,
46-
scopeInstanceId: z.optional(z.string()),
46+
scopeInstanceId: z.string(),
4747
name: z.string(),
4848
resourceId: z.string(),
4949
}),

0 commit comments

Comments
 (0)