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

experimental: Add System Resource instead of $resources url #3341

Merged
merged 4 commits into from
May 11, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { DataSource, Resource } from "@webstudio-is/sdk";
import {
encodeDataSourceVariable,
isLiteralExpression,
isLocalResource,
sitemapResourceUrl,
} from "@webstudio-is/sdk";
import {
Box,
Expand Down Expand Up @@ -446,10 +446,6 @@ export const ResourceForm = forwardRef<
try {
new URL(evaluatedValue);
} catch {
if (isLocalResource(evaluatedValue, "sitemap.xml")) {
return;
}

return "URL is invalid";
}
},
Expand Down Expand Up @@ -630,3 +626,96 @@ export const ResourceForm = forwardRef<
);
});
ResourceForm.displayName = "ResourceForm";

export const SystemResourceForm = forwardRef<
undefined | PanelApi,
{ variable?: DataSource; nameField: Field<string> }
>(({ variable, nameField }, ref) => {
const resources = useStore($resources);

const resource =
variable?.type === "resource"
? resources.get(variable.resourceId)
: undefined;

const method = "get";

const localResources = [
{
label: "Sitemap",
value: JSON.stringify(sitemapResourceUrl),
description: "Resource that loads the sitemap data of the current site.",
},
];

const [localResource, setLocalResource] = useState(() => {
return (
localResources.find(
(localResource) => localResource.value === resource?.url
) ?? localResources[0]
);
});

const form = composeFields(nameField);

useImperativeHandle(ref, () => ({
...form,
save: () => {
const instanceSelector = $selectedInstanceSelector.get();
if (instanceSelector === undefined) {
return;
}
const [instanceId] = instanceSelector;

const newResource: Resource = {
id: resource?.id ?? nanoid(),
name: nameField.value,
url: localResource.value,
method,
headers: [],
};

const newVariable: DataSource = {
id: variable?.id ?? nanoid(),
// preserve existing instance scope when edit
scopeInstanceId: variable?.scopeInstanceId ?? instanceId,
name: nameField.value,
type: "resource",
resourceId: newResource.id,
};

serverSyncStore.createTransaction(
[$dataSources, $resources],
(dataSources, resources) => {
dataSources.set(newVariable.id, newVariable);
resources.set(newResource.id, newResource);
}
);
},
}));

const resourceId = useId();

return (
<>
<Flex direction="column" css={{ gap: theme.spacing[3] }}>
<Label htmlFor={resourceId}>Resource</Label>
<Select
options={localResources}
getLabel={(option) => option.label}
getValue={(option) => option.value}
getDescription={(option) => {
return (
<Box css={{ width: theme.spacing[25] }}>
{option?.description}
</Box>
);
}}
value={localResource}
onChange={setLocalResource}
/>
</Flex>
</>
);
});
SystemResourceForm.displayName = "ResourceForm";
108 changes: 77 additions & 31 deletions apps/builder/app/builder/features/settings-panel/variable-popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
Tooltip,
theme,
} from "@webstudio-is/design-system";
import { transpileExpression } from "@webstudio-is/sdk";
import { isLocalResource, transpileExpression } from "@webstudio-is/sdk";
import type { DataSource } from "@webstudio-is/sdk";
import {
ExpressionEditor,
Expand Down Expand Up @@ -62,7 +62,7 @@ import {
EditorDialogButton,
EditorDialogControl,
} from "~/builder/shared/code-editor-base";
import { ResourceForm } from "./resource-panel";
import { ResourceForm, SystemResourceForm } from "./resource-panel";
import { generateCurl } from "./curl";

/**
Expand Down Expand Up @@ -108,6 +108,7 @@ type VariableType =
| "boolean"
| "json"
| "resource"
| "system-resource"
| "parameter";

type PanelApi = ComposedFields & {
Expand Down Expand Up @@ -359,6 +360,7 @@ const VariablePanel = forwardRef<
}
>(({ variable }, ref) => {
const { allowDynamicData } = useStore($userPlanFeatures);
const resources = useStore($resources);

const nameField = useField({
initialValue: variable?.name ?? "",
Expand All @@ -383,10 +385,18 @@ const VariablePanel = forwardRef<
</Flex>
);

const [type, setType] = useState<VariableType>(() => {
const [variableType, setVariableType] = useState<VariableType>(() => {
if (
variable?.type === "resource" &&
isLocalResource(JSON.parse(resources.get(variable.resourceId)?.url ?? ""))
) {
return "system-resource";
}

if (variable?.type === "parameter" || variable?.type === "resource") {
return variable.type;
}

if (variable?.type === "variable") {
const type = variable.value.type;
if (type === "string" || type === "number" || type === "boolean") {
Expand Down Expand Up @@ -427,7 +437,20 @@ const VariablePanel = forwardRef<
"A Resource is a configuration for secure data fetching. You can safely use secrets in any field.",
},
],
[
"system-resource",
{
label: (
<Flex direction="row" gap="2" align="center">
System Resource
{allowDynamicData === false && <ProBadge>Pro</ProBadge>}
</Flex>
),
description: "A System Resource is a configuration for Webstudio data.",
},
],
]);

const typeFieldElement = (
<Flex direction="column" gap="1">
<Label>Type</Label>
Expand All @@ -444,21 +467,21 @@ const VariablePanel = forwardRef<
</Box>
);
}}
value={type}
onChange={setType}
value={variableType}
onChange={setVariableType}
/>
</Flex>
);

if (type === "parameter") {
if (variableType === "parameter") {
return (
<>
{nameFieldElement}
<ParameterForm ref={ref} variable={variable} nameField={nameField} />
</>
);
}
if (type === "string") {
if (variableType === "string") {
return (
<>
{nameFieldElement}
Expand All @@ -467,7 +490,7 @@ const VariablePanel = forwardRef<
</>
);
}
if (type === "number") {
if (variableType === "number") {
return (
<>
{nameFieldElement}
Expand All @@ -476,7 +499,7 @@ const VariablePanel = forwardRef<
</>
);
}
if (type === "boolean") {
if (variableType === "boolean") {
return (
<>
{nameFieldElement}
Expand All @@ -485,7 +508,7 @@ const VariablePanel = forwardRef<
</>
);
}
if (type === "json") {
if (variableType === "json") {
return (
<>
{nameFieldElement}
Expand All @@ -494,7 +517,8 @@ const VariablePanel = forwardRef<
</>
);
}
if (type === "resource") {

if (variableType === "resource") {
return (
<>
{nameFieldElement}
Expand All @@ -503,6 +527,22 @@ const VariablePanel = forwardRef<
</>
);
}

if (variableType === "system-resource") {
return (
<>
{nameFieldElement}
{typeFieldElement}
<SystemResourceForm
ref={ref}
variable={variable}
nameField={nameField}
/>
</>
);
}

variableType satisfies never;
});
VariablePanel.displayName = "VariablePanel";

Expand All @@ -522,6 +562,8 @@ export const VariablePopoverTrigger = forwardRef<
const [triggerRef, sideOffsset] = useSideOffset({ isOpen, containerRef });
const bindingPopoverContainerRef = useRef<HTMLDivElement>(null);
const panelRef = useRef<undefined | PanelApi>();
const resources = useStore($resources);

const saveAndClose = () => {
if (panelRef.current) {
if (panelRef.current.allErrorsVisible === false) {
Expand Down Expand Up @@ -597,28 +639,32 @@ export const VariablePopoverTrigger = forwardRef<
) : (
<FloatingPanelPopoverTitle
actions={
variable.type === "resource" && (
variable?.type === "resource" && (
<>
<Tooltip
content="Copy resource as cURL command"
side="bottom"
>
<Button
aria-label="Copy resource as cURL command"
prefix={<CopyIcon />}
color="ghost"
onClick={() => {
const resourceRequest = getComputedResource(
variable.resourceId
);
if (resourceRequest) {
navigator.clipboard.writeText(
generateCurl(resourceRequest)
{isLocalResource(
JSON.parse(resources.get(variable.resourceId)?.url ?? "")
) === false && (
<Tooltip
content="Copy resource as cURL command"
side="bottom"
>
<Button
aria-label="Copy resource as cURL command"
prefix={<CopyIcon />}
color="ghost"
onClick={() => {
const resourceRequest = getComputedResource(
variable.resourceId
);
}
}}
/>
</Tooltip>
if (resourceRequest) {
navigator.clipboard.writeText(
generateCurl(resourceRequest)
);
}
}}
/>
</Tooltip>
)}
<Tooltip content="Refresh resource data" side="bottom">
<Button
aria-label="Refresh resource data"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const TabContent = ({ publish, onSetActiveTab }: TabContentProps) => {

// Only xml category is allowed for xml document type
if (documentType === "xml") {
return category === "xml";
return category === "xml" || category === "data";
}
// Hide xml category for non-xml document types
if (category === "xml") {
Expand All @@ -81,8 +81,16 @@ export const TabContent = ({ publish, onSetActiveTab }: TabContentProps) => {
wrap="wrap"
css={{ px: theme.spacing[9], overflow: "auto" }}
>
{(metaByCategory.get(category) ?? []).map(
(meta: WsComponentMeta, index) => {
{(metaByCategory.get(category) ?? [])
.filter((meta: WsComponentMeta) => {
if (documentType === "xml" && meta.category === "data") {
return (
componentNamesByMeta.get(meta) === "ws:collection"
);
}
return true;
})
.map((meta: WsComponentMeta, index) => {
const component = componentNamesByMeta.get(meta);
if (component === undefined) {
return;
Expand Down Expand Up @@ -123,8 +131,7 @@ export const TabContent = ({ publish, onSetActiveTab }: TabContentProps) => {
/>
</ListItem>
);
}
)}
})}
{dragCard}
</Flex>
</List>
Expand Down
Loading
Loading