Skip to content
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
5 changes: 5 additions & 0 deletions apps/builder/app/builder/builder.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@ body {
[data-radix-scroll-area-viewport]::-webkit-scrollbar {
display: none;
}

* {
scrollbar-width: thin;
scrollbar-color: var(--colors-foregroundScrollBar) transparent;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
List,
ListItem,
Text,
rawTheme,
} from "@webstudio-is/design-system";
import {
$openProjectSettings,
Expand Down Expand Up @@ -53,11 +54,11 @@ export const ProjectSettingsView = ({
onOpenChange={onOpenChange}
>
<DialogContent
css={{
width: `calc(${leftPanelWidth} + ${rightPanelWidth})`,
maxWidth: "none",
height: theme.spacing[35],
}}
width={
Number.parseInt(leftPanelWidth, 10) +
Number.parseInt(rightPanelWidth, 10)
}
height={Number.parseInt(rawTheme.spacing[35], 10)}
>
<fieldset style={{ display: "contents" }} disabled={!isDesignMode}>
<Flex grow>
Expand Down
6 changes: 3 additions & 3 deletions apps/builder/app/builder/features/project-settings/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { theme, type CSS } from "@webstudio-is/design-system";
import { rawTheme, theme, type CSS } from "@webstudio-is/design-system";
import { getPagePath, type Pages } from "@webstudio-is/sdk";

export const leftPanelWidth = theme.spacing[26];
export const rightPanelWidth = theme.spacing[35];
export const leftPanelWidth = rawTheme.spacing[26];
export const rightPanelWidth = rawTheme.spacing[35];
export const sectionSpacing: CSS = {
paddingInline: theme.panel.paddingInline,
};
Expand Down
144 changes: 98 additions & 46 deletions apps/builder/app/builder/features/settings-panel/variable-popover.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { z } from "zod";
import { nanoid } from "nanoid";
import { computed } from "nanostores";
import { useStore } from "@nanostores/react";
import { javascript } from "@codemirror/lang-javascript";
import {
type ReactNode,
type Ref,
Expand All @@ -13,13 +15,15 @@ import {
createContext,
useEffect,
useCallback,
useMemo,
} from "react";
import { CopyIcon, RefreshIcon, UpgradeIcon } from "@webstudio-is/icons";
import {
Box,
Button,
Combobox,
DialogClose,
DialogMaximize,
DialogTitle,
DialogTitleActions,
Flex,
Expand Down Expand Up @@ -58,13 +62,19 @@ import {
$userPlanFeatures,
$instances,
$props,
$variableValuesByInstanceSelector,
} from "~/shared/nano-states";
import { $selectedInstance } from "~/shared/awareness";
import {
$selectedInstance,
$selectedInstanceKeyWithRoot,
} from "~/shared/awareness";
import { BindingPopoverProvider } from "~/builder/shared/binding-popover";
import {
EditorContent,
EditorDialog,
EditorDialogButton,
EditorDialogControl,
foldGutterExtension,
} from "~/builder/shared/code-editor-base";
import { updateWebstudioData } from "~/shared/instance-utils";
import {
Expand Down Expand Up @@ -666,8 +676,8 @@ const VariablePanel = forwardRef<
direction="column"
css={{
overflow: "hidden",
padding: theme.panel.padding,
gap: theme.spacing[7],
p: theme.panel.padding,
}}
>
<NameField variable={variable} defaultValue={variable?.name ?? ""} />
Expand Down Expand Up @@ -707,6 +717,40 @@ const areAllFormErrorsVisible = (form: null | HTMLFormElement) => {
return true;
};

const $instanceVariableValues = computed(
[$selectedInstanceKeyWithRoot, $variableValuesByInstanceSelector],
(instanceKey, variableValuesByInstanceSelector) =>
variableValuesByInstanceSelector.get(instanceKey ?? "") ??
new Map<string, unknown>()
);

const VariablePreview = ({ variable }: { variable: DataSource }) => {
const variableValues = useStore($instanceVariableValues);
const extensions = useMemo(() => [javascript({}), foldGutterExtension], []);
const editorProps = {
readOnly: true,
extensions,
// compute value as json lazily only when dialog is open
// by spliting into separate component which is invoked
// only when dialog content is rendered
value: JSON.stringify(variableValues.get(variable.id), null, 2),
onChange: () => {},
onChangeComplete: () => {},
};
return (
<Grid
align="stretch"
css={{
height: "100%",
overflow: "hidden",
boxSizing: "content-box",
}}
>
<EditorContent {...editorProps} />
</Grid>
);
};

export const VariablePopoverTrigger = ({
variable,
children,
Expand All @@ -724,6 +768,9 @@ export const VariablePopoverTrigger = ({

return (
<FloatingPanel
placement="center"
width={variable ? 740 : 320}
height={480}
open={isOpen}
onOpenChange={(newOpen) => {
if (newOpen) {
Expand Down Expand Up @@ -788,6 +835,7 @@ export const VariablePopoverTrigger = ({
</Tooltip>
</>
)}
<DialogMaximize />
<DialogClose />
</DialogTitleActions>
}
Expand All @@ -797,57 +845,61 @@ export const VariablePopoverTrigger = ({
)
}
content={
<ScrollArea
<Grid
css={{
// flex fixes content overflowing artificial scroll area
display: "flex",
flexDirection: "column",
width: theme.spacing[30],
height: "100%",
gridTemplateColumns: "320px 1fr",
}}
>
<form
ref={formRef}
noValidate={true}
// exclude from the flow
style={{ display: "contents" }}
onSubmit={(event) => {
event.preventDefault();
if (isSystemVariable) {
return;
}
const nameElement =
event.currentTarget.elements.namedItem("name");
// make sure only name is valid and allow to save everything else
// to avoid loosing complex configuration when closed accidentally
if (
nameElement instanceof HTMLInputElement &&
nameElement.checkValidity()
) {
const formData = new FormData(event.currentTarget);
panelRef.current?.save(formData);
// close popover whenever new variable is created
// to prevent creating duplicated variable
if (variable === undefined) {
setOpen(false);
}
}
}}
<ScrollArea
// flex fixes content overflowing artificial scroll area
css={{ display: "flex", flexDirection: "column" }}
>
{/* submit is not triggered when press enter on input without submit button */}
<button hidden></button>
<fieldset
<form
ref={formRef}
noValidate={true}
// exclude from the flow
style={{ display: "contents" }}
// forbid editing system variable
disabled={isSystemVariable}
onSubmit={(event) => {
event.preventDefault();
if (isSystemVariable) {
return;
}
const nameElement =
event.currentTarget.elements.namedItem("name");
// make sure only name is valid and allow to save everything else
// to avoid loosing complex configuration when closed accidentally
if (
nameElement instanceof HTMLInputElement &&
nameElement.checkValidity()
) {
const formData = new FormData(event.currentTarget);
panelRef.current?.save(formData);
// close popover whenever new variable is created
// to prevent creating duplicated variable
if (variable === undefined) {
setOpen(false);
}
}
}}
>
<BindingPopoverProvider
value={{ containerRef: bindingPopoverContainerRef }}
{/* submit is not triggered when press enter on input without submit button */}
<button hidden></button>
<fieldset
style={{ display: "contents" }}
// forbid editing system variable
disabled={isSystemVariable}
>
<VariablePanel ref={panelRef} variable={variable} />
</BindingPopoverProvider>
</fieldset>
</form>
</ScrollArea>
<BindingPopoverProvider
value={{ containerRef: bindingPopoverContainerRef }}
>
<VariablePanel ref={panelRef} variable={variable} />
</BindingPopoverProvider>
</fieldset>
</form>
</ScrollArea>
{variable && <VariablePreview variable={variable} />}
</Grid>
}
>
{children}
Expand Down
Loading
Loading