mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-09 14:03:33 +09:00
feat: Grid child area picker (#5636)
## Description 1. What is this PR about (link the issue and add a short description) ## Steps for reproduction 1. click button 2. expect xyz ## Code Review - [ ] hi @kof, I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [ ] made a self-review - [ ] added inline comments where things may be not obvious (the "why", not "what") ## Before merging - [ ] tested locally and on preview environment (preview dev login: 0000) - [ ] updated [test cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md) document - [ ] added tests - [ ] if any new env variables are added, added them to `.env` file
This commit is contained in:
1 parent
994a4f787a
commit
8aeb55aec4
4 files changed
+76
-37
No files matched your search
@@ -13,6 +13,7 @@ import {
|
||||
} from "@webstudio-is/design-system";
|
||||
import type { CssProperty, StyleValue } from "@webstudio-is/css-engine";
|
||||
import { toValue } from "@webstudio-is/css-engine";
|
||||
import { parseGridAreas, type AreaInfo } from "@webstudio-is/css-data";
|
||||
import { StyleSection } from "../../shared/style-section";
|
||||
import { PropertyLabel } from "../../property-label";
|
||||
import { ToggleGroupTooltip } from "../../controls/toggle-group/toggle-group-control";
|
||||
@@ -30,8 +31,8 @@ import {
|
||||
deleteProperty,
|
||||
resetEphemeralStyles,
|
||||
} from "../../shared/use-style-data";
|
||||
import { parseGridAreas } from "@webstudio-is/css-data";
|
||||
import { getGridDimensions } from "../layout/shared/grid-areas";
|
||||
import { GridAreaPicker } from "../layout/shared/grid-area-picker";
|
||||
import {
|
||||
GridPositionInputs,
|
||||
type GridPosition,
|
||||
@@ -41,6 +42,7 @@ import { AlignSelfControl, JustifySelfControl } from "../shared/align-self";
|
||||
import { OrderControl } from "../shared/order";
|
||||
import { useStore } from "@nanostores/react";
|
||||
import { $selectedInstancePath, selectInstance } from "~/shared/awareness";
|
||||
import { $isStylePanelGridVisible } from "~/builder/shared/nano-states";
|
||||
import { ExternalLinkIcon } from "@webstudio-is/icons";
|
||||
|
||||
export const properties = [
|
||||
@@ -112,6 +114,19 @@ const derivePositionMode = (
|
||||
return "auto";
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows grid guides on the canvas while this component is mounted.
|
||||
*/
|
||||
const GridChildGuides = () => {
|
||||
useEffect(() => {
|
||||
$isStylePanelGridVisible.set(true);
|
||||
return () => {
|
||||
$isStylePanelGridVisible.set(false);
|
||||
};
|
||||
}, []);
|
||||
return null;
|
||||
};
|
||||
|
||||
export const Section = () => {
|
||||
const [columnStart, columnEnd, rowStart, rowEnd] = useComputedStyles([
|
||||
"grid-column-start",
|
||||
@@ -221,6 +236,7 @@ export const Section = () => {
|
||||
)
|
||||
}
|
||||
>
|
||||
<GridChildGuides />
|
||||
<Flex css={{ flexDirection: "column", gap: theme.spacing[5] }}>
|
||||
<GridChildPositionMode
|
||||
value={positionMode}
|
||||
@@ -645,25 +661,50 @@ const GridChildPositionManual = () => {
|
||||
batch.publish({ isEphemeral: true });
|
||||
};
|
||||
|
||||
const handlePickerChange = (area: AreaInfo) => {
|
||||
const newPosition: GridPosition = {
|
||||
columnStart: area.columnStart,
|
||||
columnEnd: area.columnEnd,
|
||||
rowStart: area.rowStart,
|
||||
rowEnd: area.rowEnd,
|
||||
};
|
||||
localValue.set(newPosition);
|
||||
handleChange(newPosition);
|
||||
localValue.save();
|
||||
resetEphemeralStyles();
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid
|
||||
css={{
|
||||
gridTemplateColumns: "3fr 8fr",
|
||||
alignItems: "start",
|
||||
}}
|
||||
>
|
||||
<div />
|
||||
<GridPositionInputs
|
||||
value={localValue.value}
|
||||
onChange={handleChange}
|
||||
onBlur={() => {
|
||||
localValue.save();
|
||||
resetEphemeralStyles();
|
||||
<Flex direction="column" gap="2">
|
||||
<Grid
|
||||
css={{
|
||||
gridTemplateColumns: "3fr 8fr",
|
||||
alignItems: "start",
|
||||
}}
|
||||
>
|
||||
<div />
|
||||
<GridPositionInputs
|
||||
value={localValue.value}
|
||||
onChange={handleChange}
|
||||
onBlur={() => {
|
||||
localValue.save();
|
||||
resetEphemeralStyles();
|
||||
}}
|
||||
gridColumns={gridColumns}
|
||||
gridRows={gridRows}
|
||||
/>
|
||||
</Grid>
|
||||
<GridAreaPicker
|
||||
value={{
|
||||
name: "",
|
||||
...localValue.value,
|
||||
}}
|
||||
onChange={handlePickerChange}
|
||||
gridColumns={gridColumns}
|
||||
gridRows={gridRows}
|
||||
otherAreas={[]}
|
||||
/>
|
||||
</Grid>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ import { FlexAlignment } from "./shared/flex-alignment";
|
||||
import { GridGenerator } from "./shared/grid-generator";
|
||||
import { GridSettings } from "./shared/grid-settings";
|
||||
import { GridAlignment } from "./shared/grid-alignment";
|
||||
import { $openGridEditorPanel } from "~/builder/shared/nano-states";
|
||||
import { $isStylePanelGridVisible } from "~/builder/shared/nano-states";
|
||||
import { humanizeString } from "~/shared/string-utils";
|
||||
import { DEFAULT_GRID_TRACK_COUNT, DEFAULT_GRID_GAP } from "./shared/constants";
|
||||
|
||||
@@ -573,21 +573,27 @@ const applyDefaultGridStyles = (columnsValue: string, rowsValue: string) => {
|
||||
};
|
||||
|
||||
const LayoutSectionGrid = () => {
|
||||
const openPanel = useStore($openGridEditorPanel);
|
||||
const [openPanel, setOpenPanel] = useState({
|
||||
generator: false,
|
||||
settings: false,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
$isStylePanelGridVisible.set(true);
|
||||
return () => {
|
||||
$isStylePanelGridVisible.set(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="2">
|
||||
<GridGenerator
|
||||
open={openPanel.generator}
|
||||
onOpenChange={(open) =>
|
||||
$openGridEditorPanel.set({ ...openPanel, generator: open })
|
||||
}
|
||||
onOpenChange={(open) => setOpenPanel({ ...openPanel, generator: open })}
|
||||
/>
|
||||
<GridSettings
|
||||
open={openPanel.settings}
|
||||
onOpenChange={(open) =>
|
||||
$openGridEditorPanel.set({ ...openPanel, settings: open })
|
||||
}
|
||||
onOpenChange={(open) => setOpenPanel({ ...openPanel, settings: open })}
|
||||
/>
|
||||
<Grid
|
||||
css={{
|
||||
|
||||
@@ -21,7 +21,7 @@ import { applyScale } from "./outline";
|
||||
import {
|
||||
$clampingRect,
|
||||
$scale,
|
||||
$openGridEditorPanel,
|
||||
$isStylePanelGridVisible,
|
||||
} from "~/builder/shared/nano-states";
|
||||
import { BlockChildHoveredInstanceOutline } from "./outline/block-instance-outline";
|
||||
import { TextEditorContextMenu } from "./block-editor-context-menu";
|
||||
@@ -49,7 +49,7 @@ export const CanvasTools = () => {
|
||||
const instances = useStore($instances);
|
||||
const scale = useStore($scale);
|
||||
const clampingRect = useStore($clampingRect);
|
||||
const openGridEditorPanel = useStore($openGridEditorPanel);
|
||||
const isStylePanelGridVisible = useStore($isStylePanelGridVisible);
|
||||
|
||||
if (!canvasToolsVisible) {
|
||||
return;
|
||||
@@ -86,9 +86,7 @@ export const CanvasTools = () => {
|
||||
<>
|
||||
{isPreviewMode === false && (
|
||||
<>
|
||||
{(openGridEditorPanel.generator || openGridEditorPanel.settings) && (
|
||||
<GridGuides />
|
||||
)}
|
||||
{isStylePanelGridVisible && <GridGuides />}
|
||||
<SelectedInstanceOutline />
|
||||
<HoveredInstanceOutline />
|
||||
<CollaborativeInstanceOutline />
|
||||
|
||||
@@ -168,12 +168,6 @@ export type GridEditingArea = {
|
||||
|
||||
export const $gridEditingArea = atom<GridEditingArea | undefined>(undefined);
|
||||
|
||||
// Which grid editor panels are open.
|
||||
// Used to conditionally show grid guides on the canvas.
|
||||
export const $openGridEditorPanel = atom<{
|
||||
generator: boolean;
|
||||
settings: boolean;
|
||||
}>({
|
||||
generator: false,
|
||||
settings: false,
|
||||
});
|
||||
// Whether a grid-related section is visible in the style panel.
|
||||
// Grid guides are shown on the canvas when true.
|
||||
export const $isStylePanelGridVisible = atom(false);
|
||||
Reference in new issue
Block a user