Stubs alignment distribution stretch
This commit is contained in:
parent
733acfaf3a
commit
ab50d76e6e
13 changed files with 339 additions and 56 deletions
106
components/style-panel/align-distribute.tsx
Normal file
106
components/style-panel/align-distribute.tsx
Normal file
|
@ -0,0 +1,106 @@
|
|||
import {
|
||||
AlignBottomIcon,
|
||||
AlignCenterHorizontallyIcon,
|
||||
AlignCenterVerticallyIcon,
|
||||
AlignLeftIcon,
|
||||
AlignRightIcon,
|
||||
AlignTopIcon,
|
||||
SpaceEvenlyHorizontallyIcon,
|
||||
SpaceEvenlyVerticallyIcon,
|
||||
StretchHorizontallyIcon,
|
||||
StretchVerticallyIcon,
|
||||
} from "@radix-ui/react-icons"
|
||||
import { IconButton } from "components/shared"
|
||||
import state from "state"
|
||||
import styled from "styles"
|
||||
import { AlignType, DistributeType, StretchType } from "types"
|
||||
|
||||
function alignTop() {
|
||||
state.send("ALIGNED", { type: AlignType.Top })
|
||||
}
|
||||
|
||||
function alignCenterVertical() {
|
||||
state.send("ALIGNED", { type: AlignType.CenterVertical })
|
||||
}
|
||||
|
||||
function alignBottom() {
|
||||
state.send("ALIGNED", { type: AlignType.Bottom })
|
||||
}
|
||||
|
||||
function stretchVertically() {
|
||||
state.send("STRETCHED", { type: StretchType.Vertical })
|
||||
}
|
||||
|
||||
function distributeVertically() {
|
||||
state.send("DISTRIBUTED", { type: DistributeType.Vertical })
|
||||
}
|
||||
|
||||
function alignLeft() {
|
||||
state.send("ALIGNED", { type: AlignType.Left })
|
||||
}
|
||||
|
||||
function alignCenterHorizontal() {
|
||||
state.send("ALIGNED", { type: AlignType.CenterHorizontal })
|
||||
}
|
||||
|
||||
function alignRight() {
|
||||
state.send("ALIGNED", { type: AlignType.Right })
|
||||
}
|
||||
|
||||
function stretchHorizontally() {
|
||||
state.send("STRETCHED", { type: StretchType.Horizontal })
|
||||
}
|
||||
|
||||
function distributeHorizontally() {
|
||||
state.send("DISTRIBUTED", { type: DistributeType.Horizontal })
|
||||
}
|
||||
|
||||
export default function AlignDistribute() {
|
||||
return (
|
||||
<Container>
|
||||
<IconButton onClick={alignTop}>
|
||||
<AlignTopIcon />
|
||||
</IconButton>
|
||||
<IconButton onClick={alignCenterVertical}>
|
||||
<AlignCenterVerticallyIcon />
|
||||
</IconButton>
|
||||
<IconButton onClick={alignBottom}>
|
||||
<AlignBottomIcon />
|
||||
</IconButton>
|
||||
<IconButton onClick={stretchVertically}>
|
||||
<StretchVerticallyIcon />
|
||||
</IconButton>
|
||||
<IconButton onClick={distributeVertically}>
|
||||
<SpaceEvenlyVerticallyIcon />
|
||||
</IconButton>
|
||||
<IconButton onClick={alignLeft}>
|
||||
<AlignLeftIcon />
|
||||
</IconButton>
|
||||
<IconButton onClick={alignCenterHorizontal}>
|
||||
<AlignCenterHorizontallyIcon />
|
||||
</IconButton>
|
||||
<IconButton onClick={alignRight}>
|
||||
<AlignRightIcon />
|
||||
</IconButton>
|
||||
<IconButton onClick={stretchHorizontally}>
|
||||
<StretchHorizontallyIcon />
|
||||
</IconButton>
|
||||
<IconButton onClick={distributeHorizontally}>
|
||||
<SpaceEvenlyHorizontallyIcon />
|
||||
</IconButton>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled("div", {
|
||||
display: "grid",
|
||||
padding: 4,
|
||||
gridTemplateColumns: "repeat(5, auto)",
|
||||
[`& ${IconButton}`]: {
|
||||
color: "$text",
|
||||
},
|
||||
[`& ${IconButton} > svg`]: {
|
||||
fill: "red",
|
||||
stroke: "transparent",
|
||||
},
|
||||
})
|
|
@ -92,15 +92,15 @@ const CurrentColor = styled(DropdownMenu.Trigger, {
|
|||
outline: "none",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
padding: "8px 8px 8px 12px",
|
||||
padding: "4px 6px 4px 12px",
|
||||
|
||||
"&::before": {
|
||||
content: "''",
|
||||
position: "absolute",
|
||||
top: 4,
|
||||
top: 0,
|
||||
left: 4,
|
||||
right: 4,
|
||||
bottom: 4,
|
||||
bottom: 0,
|
||||
pointerEvents: "none",
|
||||
zIndex: -1,
|
||||
},
|
||||
|
|
|
@ -8,6 +8,7 @@ import { deepCompare, deepCompareArrays, getSelectedShapes } from "utils/utils"
|
|||
import { colors } from "state/data"
|
||||
|
||||
import ColorPicker from "./color-picker"
|
||||
import AlignDistribute from "./align-distribute"
|
||||
import { ShapeByType, ShapeStyles } from "types"
|
||||
|
||||
export default function StylePanel() {
|
||||
|
@ -77,34 +78,32 @@ function SelectedShapeStyles({}: {}) {
|
|||
</IconButton>
|
||||
</Panel.ButtonsGroup>
|
||||
</Panel.Header>
|
||||
<Panel.Content>
|
||||
<ColorsRow>
|
||||
<ColorPicker
|
||||
label="Fill"
|
||||
color={shapesStyle.fill}
|
||||
onChange={(color) => state.send("CHANGED_STYLE", { fill: color })}
|
||||
/>
|
||||
<ColorPicker
|
||||
label="Stroke"
|
||||
color={shapesStyle.stroke}
|
||||
onChange={(color) => state.send("CHANGED_STYLE", { stroke: color })}
|
||||
/>
|
||||
</ColorsRow>
|
||||
</Panel.Content>
|
||||
<Content>
|
||||
<ColorPicker
|
||||
label="Fill"
|
||||
color={shapesStyle.fill}
|
||||
onChange={(color) => state.send("CHANGED_STYLE", { fill: color })}
|
||||
/>
|
||||
<ColorPicker
|
||||
label="Stroke"
|
||||
color={shapesStyle.stroke}
|
||||
onChange={(color) => state.send("CHANGED_STYLE", { stroke: color })}
|
||||
/>
|
||||
<AlignDistribute />
|
||||
</Content>
|
||||
</Panel.Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const StylePanelRoot = styled(Panel.Root, {
|
||||
maxWidth: 260,
|
||||
minWidth: 1,
|
||||
width: 184,
|
||||
maxWidth: 184,
|
||||
position: "relative",
|
||||
|
||||
variants: {
|
||||
isOpen: {
|
||||
true: {
|
||||
width: "auto",
|
||||
minWidth: 300,
|
||||
},
|
||||
true: {},
|
||||
false: {
|
||||
height: 34,
|
||||
width: 34,
|
||||
|
@ -113,7 +112,6 @@ const StylePanelRoot = styled(Panel.Root, {
|
|||
},
|
||||
})
|
||||
|
||||
const ColorsRow = styled("div", {
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr 1fr",
|
||||
const Content = styled(Panel.Content, {
|
||||
padding: 8,
|
||||
})
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { useEffect } from "react"
|
||||
import state from "state"
|
||||
import { MoveType } from "types"
|
||||
import { getKeyboardEventInfo, metaKey } from "utils/utils"
|
||||
|
||||
export default function useKeyboardEvents() {
|
||||
|
@ -47,25 +48,37 @@ export default function useKeyboardEvents() {
|
|||
}
|
||||
case "‘": {
|
||||
if (metaKey(e)) {
|
||||
state.send("MOVED_TO_FRONT", getKeyboardEventInfo(e))
|
||||
state.send("MOVED", {
|
||||
...getKeyboardEventInfo(e),
|
||||
type: MoveType.ToFront,
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
case "“": {
|
||||
if (metaKey(e)) {
|
||||
state.send("MOVED_TO_BACK", getKeyboardEventInfo(e))
|
||||
state.send("MOVED", {
|
||||
...getKeyboardEventInfo(e),
|
||||
type: MoveType.ToBack,
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
case "]": {
|
||||
if (metaKey(e)) {
|
||||
state.send("MOVED_FORWARD", getKeyboardEventInfo(e))
|
||||
state.send("MOVED", {
|
||||
...getKeyboardEventInfo(e),
|
||||
type: MoveType.Forward,
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
case "[": {
|
||||
if (metaKey(e)) {
|
||||
state.send("MOVED_BACKWARD", getKeyboardEventInfo(e))
|
||||
state.send("MOVED", {
|
||||
...getKeyboardEventInfo(e),
|
||||
type: MoveType.Backward,
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
"dependencies": {
|
||||
"@monaco-editor/react": "^4.1.3",
|
||||
"@radix-ui/react-dropdown-menu": "^0.0.19",
|
||||
"@radix-ui/react-icons": "^1.0.3",
|
||||
"@state-designer/react": "^1.7.1",
|
||||
"@stitches/react": "^0.1.9",
|
||||
"framer-motion": "^4.1.16",
|
||||
|
|
54
state/commands/align.ts
Normal file
54
state/commands/align.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
import Command from "./command"
|
||||
import history from "../history"
|
||||
import { AlignType, Data } from "types"
|
||||
import { getPage } from "utils/utils"
|
||||
import { getShapeUtils } from "lib/shape-utils"
|
||||
|
||||
export default function alignCommand(data: Data, type: AlignType) {
|
||||
const { currentPageId } = data
|
||||
const initialPoints = Object.fromEntries(
|
||||
Object.entries(getPage(data).shapes).map(([id, shape]) => [
|
||||
id,
|
||||
[...shape.point],
|
||||
])
|
||||
)
|
||||
|
||||
history.execute(
|
||||
data,
|
||||
new Command({
|
||||
name: "aligned",
|
||||
category: "canvas",
|
||||
do(data) {
|
||||
const { shapes } = getPage(data, currentPageId)
|
||||
|
||||
switch (type) {
|
||||
case AlignType.Top: {
|
||||
break
|
||||
}
|
||||
case AlignType.CenterVertical: {
|
||||
break
|
||||
}
|
||||
case AlignType.Bottom: {
|
||||
break
|
||||
}
|
||||
case AlignType.Left: {
|
||||
break
|
||||
}
|
||||
case AlignType.CenterHorizontal: {
|
||||
break
|
||||
}
|
||||
case AlignType.Right: {
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
undo(data) {
|
||||
const { shapes } = getPage(data, currentPageId)
|
||||
for (let id in initialPoints) {
|
||||
const shape = shapes[id]
|
||||
getShapeUtils(shape).translateTo(shape, initialPoints[id])
|
||||
}
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
41
state/commands/distribute.ts
Normal file
41
state/commands/distribute.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import Command from "./command"
|
||||
import history from "../history"
|
||||
import { AlignType, Data, DistributeType } from "types"
|
||||
import { getPage } from "utils/utils"
|
||||
import { getShapeUtils } from "lib/shape-utils"
|
||||
|
||||
export default function distributeCommand(data: Data, type: DistributeType) {
|
||||
const { currentPageId } = data
|
||||
|
||||
const initialPoints = Object.fromEntries(
|
||||
Object.entries(getPage(data).shapes).map(([id, shape]) => [
|
||||
id,
|
||||
[...shape.point],
|
||||
])
|
||||
)
|
||||
|
||||
history.execute(
|
||||
data,
|
||||
new Command({
|
||||
name: "distributed",
|
||||
category: "canvas",
|
||||
do(data) {
|
||||
const { shapes } = getPage(data, currentPageId)
|
||||
|
||||
switch (type) {
|
||||
case DistributeType.Horizontal: {
|
||||
}
|
||||
case DistributeType.Vertical: {
|
||||
}
|
||||
}
|
||||
},
|
||||
undo(data) {
|
||||
const { shapes } = getPage(data, currentPageId)
|
||||
for (let id in initialPoints) {
|
||||
const shape = shapes[id]
|
||||
getShapeUtils(shape).translateTo(shape, initialPoints[id])
|
||||
}
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
|
@ -1,23 +1,29 @@
|
|||
import translate from "./translate"
|
||||
import align from "./align"
|
||||
import deleteSelected from "./delete-selected"
|
||||
import direct from "./direct"
|
||||
import distribute from "./distribute"
|
||||
import generate from "./generate"
|
||||
import move from "./move"
|
||||
import rotate from "./rotate"
|
||||
import stretch from "./stretch"
|
||||
import style from "./style"
|
||||
import transform from "./transform"
|
||||
import transformSingle from "./transform-single"
|
||||
import generate from "./generate"
|
||||
import direct from "./direct"
|
||||
import rotate from "./rotate"
|
||||
import move from "./move"
|
||||
import style from "./style"
|
||||
import deleteSelected from "./delete-selected"
|
||||
import translate from "./translate"
|
||||
|
||||
const commands = {
|
||||
translate,
|
||||
align,
|
||||
deleteSelected,
|
||||
direct,
|
||||
distribute,
|
||||
generate,
|
||||
move,
|
||||
rotate,
|
||||
stretch,
|
||||
style,
|
||||
transform,
|
||||
transformSingle,
|
||||
generate,
|
||||
direct,
|
||||
rotate,
|
||||
move,
|
||||
style,
|
||||
deleteSelected,
|
||||
translate,
|
||||
}
|
||||
|
||||
export default commands
|
||||
|
|
38
state/commands/stretch.ts
Normal file
38
state/commands/stretch.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import Command from "./command"
|
||||
import history from "../history"
|
||||
import { StretchType, Data } from "types"
|
||||
import { getPage } from "utils/utils"
|
||||
import { getShapeUtils } from "lib/shape-utils"
|
||||
|
||||
export default function stretchCommand(data: Data, type: StretchType) {
|
||||
const { currentPageId } = data
|
||||
|
||||
const initialPoints = Object.fromEntries(
|
||||
Object.entries(getPage(data).shapes).map(([id, shape]) => [id, shape.point])
|
||||
)
|
||||
|
||||
history.execute(
|
||||
data,
|
||||
new Command({
|
||||
name: "distributed",
|
||||
category: "canvas",
|
||||
do(data) {
|
||||
const { shapes } = getPage(data, currentPageId)
|
||||
|
||||
switch (type) {
|
||||
case StretchType.Horizontal: {
|
||||
}
|
||||
case StretchType.Vertical: {
|
||||
}
|
||||
}
|
||||
},
|
||||
undo(data) {
|
||||
const { shapes } = getPage(data, currentPageId)
|
||||
for (let id in initialPoints) {
|
||||
const shape = shapes[id]
|
||||
getShapeUtils(shape).translateTo(shape, initialPoints[id])
|
||||
}
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
|
@ -28,6 +28,9 @@ import {
|
|||
CodeControl,
|
||||
MoveType,
|
||||
ShapeStyles,
|
||||
DistributeType,
|
||||
AlignType,
|
||||
StretchType,
|
||||
} from "types"
|
||||
|
||||
const initialData: Data = {
|
||||
|
@ -78,6 +81,8 @@ const state = createState({
|
|||
TOGGLED_STYLE_PANEL_OPEN: "toggleStylePanel",
|
||||
CHANGED_STYLE: ["updateStyles", "applyStylesToSelection"],
|
||||
RESET_CAMERA: "resetCamera",
|
||||
ALIGNED: "alignSelection",
|
||||
DISTRIBUTED: "distributeSelection",
|
||||
ZOOMED_TO_FIT: "zoomCameraToFit",
|
||||
ZOOMED_TO_SELECTION: {
|
||||
if: "hasSelection",
|
||||
|
@ -120,10 +125,7 @@ const state = createState({
|
|||
INCREASED_CODE_FONT_SIZE: "increaseCodeFontSize",
|
||||
DECREASED_CODE_FONT_SIZE: "decreaseCodeFontSize",
|
||||
CHANGED_CODE_CONTROL: "updateControls",
|
||||
MOVED_TO_FRONT: "moveSelectionToFront",
|
||||
MOVED_TO_BACK: "moveSelectionToBack",
|
||||
MOVED_FORWARD: "moveSelectionForward",
|
||||
MOVED_BACKWARD: "moveSelectionBackward",
|
||||
MOVED: "moveSelection",
|
||||
},
|
||||
initial: "notPointing",
|
||||
states: {
|
||||
|
@ -675,17 +677,17 @@ const state = createState({
|
|||
pushPointedIdToSelectedIds(data) {
|
||||
data.selectedIds.add(data.pointedId)
|
||||
},
|
||||
moveSelectionToFront(data) {
|
||||
commands.move(data, MoveType.ToFront)
|
||||
moveSelection(data, payload: { type: MoveType }) {
|
||||
commands.move(data, payload.type)
|
||||
},
|
||||
moveSelectionToBack(data) {
|
||||
commands.move(data, MoveType.ToBack)
|
||||
alignSelection(data, payload: { type: AlignType }) {
|
||||
commands.align(data, payload.type)
|
||||
},
|
||||
moveSelectionForward(data) {
|
||||
commands.move(data, MoveType.Forward)
|
||||
stretchSelection(data, payload: { type: StretchType }) {
|
||||
commands.stretch(data, payload.type)
|
||||
},
|
||||
moveSelectionBackward(data) {
|
||||
commands.move(data, MoveType.Backward)
|
||||
distributeSelection(data, payload: { type: DistributeType }) {
|
||||
commands.distribute(data, payload.type)
|
||||
},
|
||||
|
||||
/* --------------------- Camera --------------------- */
|
||||
|
|
|
@ -15,7 +15,7 @@ const { styled, global, css, theme, getCssString } = createCss({
|
|||
border: "#aaa",
|
||||
panel: "#fefefe",
|
||||
hover: "#efefef",
|
||||
text: "#000",
|
||||
text: "#333",
|
||||
input: "#f3f3f3",
|
||||
inputBorder: "#ddd",
|
||||
},
|
||||
|
|
19
types.ts
19
types.ts
|
@ -226,6 +226,25 @@ export enum MoveType {
|
|||
ToBack,
|
||||
}
|
||||
|
||||
export enum AlignType {
|
||||
Top,
|
||||
CenterVertical,
|
||||
Bottom,
|
||||
Left,
|
||||
CenterHorizontal,
|
||||
Right,
|
||||
}
|
||||
|
||||
export enum StretchType {
|
||||
Horizontal,
|
||||
Vertical,
|
||||
}
|
||||
|
||||
export enum DistributeType {
|
||||
Horizontal,
|
||||
Vertical,
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* Code Editor */
|
||||
/* -------------------------------------------------- */
|
||||
|
|
|
@ -1346,6 +1346,11 @@
|
|||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-use-callback-ref" "0.0.5"
|
||||
|
||||
"@radix-ui/react-icons@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-icons/-/react-icons-1.0.3.tgz#4ef61f1234f44991f7a19e108f77ca37032b4be2"
|
||||
integrity sha512-YbPAUZwTsvF/2H7IU35txaLUB+JNSV8GIhnswlqiFODP/P32t5op5keYUvQWsSj9TA0VLF367J24buUjIprn0w==
|
||||
|
||||
"@radix-ui/react-id@0.0.6":
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-0.0.6.tgz#c4b27d11861805e91ac296e7758ab47e3947b65c"
|
||||
|
|
Loading…
Reference in a new issue