Skip to content

Commit

Permalink
Merge pull request #16 from miyanokomiya/feat/line_attachment
Browse files Browse the repository at this point in the history
Line attachment
  • Loading branch information
miyanokomiya authored Nov 4, 2024
2 parents 1468af8 + 978f882 commit b7c7f9b
Show file tree
Hide file tree
Showing 39 changed files with 2,217 additions and 149 deletions.
7 changes: 5 additions & 2 deletions src/components/atoms/BlockField.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
interface Props {
label: string;
fullBody?: boolean;
children?: React.ReactNode;
}

export const BlockField: React.FC<Props> = ({ label, children }) => {
export const BlockField: React.FC<Props> = ({ label, fullBody, children }) => {
const bodyClass = fullBody ? "ml-2 w-full" : "ml-auto";

return (
<div className="flex flex-col gap-1">
<span>{label}:</span>
<div className="ml-auto">{children}</div>
<div className={bodyClass}>{children}</div>
</div>
);
};
82 changes: 82 additions & 0 deletions src/components/shapeInspectorPanel/AttachmentInspector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { useCallback } from "react";
import { BlockGroupField } from "../atoms/BlockGroupField";
import { InlineField } from "../atoms/InlineField";
import { ToggleInput } from "../atoms/inputs/ToggleInput";
import { Shape } from "../../models";
import { SliderInput } from "../atoms/inputs/SliderInput";
import { BlockField } from "../atoms/BlockField";

interface Props {
targetShape: Shape;
updateTargetShape: (patch: Partial<Shape>) => void;
}

export const AttachmentInspector: React.FC<Props> = ({ targetShape, updateTargetShape }) => {
const attachment = targetShape.attachment;

const handleAnchorRateChange = useCallback(
(val: number) => {
if (!attachment) return;
updateTargetShape({ attachment: { ...attachment, to: { x: val, y: attachment.to.y } } });
},
[attachment, updateTargetShape],
);

const handleRelativeRotationChange = useCallback(
(val: boolean) => {
if (!attachment) return;
updateTargetShape({ attachment: { ...attachment, rotationType: val ? "relative" : "absolute" } });
},
[attachment, updateTargetShape],
);

const handleAnchorXChange = useCallback(
(val: number) => {
if (!attachment) return;
updateTargetShape({ attachment: { ...attachment, anchor: { x: val, y: attachment.anchor.y } } });
},
[attachment, updateTargetShape],
);
const handleAnchorYChange = useCallback(
(val: number) => {
if (!attachment) return;
updateTargetShape({ attachment: { ...attachment, anchor: { x: attachment.anchor.x, y: val } } });
},
[attachment, updateTargetShape],
);

if (!attachment) return;

return (
<BlockGroupField label="Attachment" accordionKey="attachment-inspector">
<InlineField label="Rate" fullBody>
<SliderInput value={attachment.to.x} onChanged={handleAnchorRateChange} min={0} max={1} step={0.01} showValue />
</InlineField>
<InlineField label="Relative rotation">
<ToggleInput value={attachment.rotationType === "relative"} onChange={handleRelativeRotationChange} />
</InlineField>
<BlockField label="Anchor" fullBody>
<InlineField label="Left" fullBody>
<SliderInput
value={attachment.anchor.x}
onChanged={handleAnchorXChange}
min={0}
max={1}
step={0.01}
showValue
/>
</InlineField>
<InlineField label="Top" fullBody>
<SliderInput
value={attachment.anchor.y}
onChanged={handleAnchorYChange}
min={0}
max={1}
step={0.01}
showValue
/>
</InlineField>
</BlockField>
</BlockGroupField>
);
};
16 changes: 14 additions & 2 deletions src/components/shapeInspectorPanel/ConventionalShapeInspector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { InlineField } from "../atoms/InlineField";
import { useShapeComposite, useShapeCompositeWithoutTmpInfo } from "../../hooks/storeHooks";
import { resizeShapeTrees } from "../../composables/shapeResizing";
import { BlockGroupField } from "../atoms/BlockGroupField";
import { getAttachmentByUpdatingRotation } from "../../shapes";

interface Props {
targetShape: Shape;
Expand Down Expand Up @@ -80,9 +81,20 @@ export const ConventionalShapeInspector: React.FC<Props> = ({
const handleChangeRotation = useCallback(
(val: number, draft = false) => {
const affine = getRotateToAffine(subShapeComposite, targetShape, (val * Math.PI) / 180);
handleResize(affine, draft);
if (draft) {
readyState();

const patch = resizeShapeTrees(shapeComposite, [targetShape.id], affine);
const attachment = getAttachmentByUpdatingRotation(targetShape, patch[targetShape.id].rotation);
if (attachment) {
patch[targetShape.id].attachment = attachment;
}
updateTmpShapes(patch);
} else {
commit();
}
},
[targetShape, subShapeComposite, handleResize],
[targetShape, subShapeComposite, commit, readyState, updateTmpShapes, shapeComposite],
);

return (
Expand Down
26 changes: 26 additions & 0 deletions src/components/shapeInspectorPanel/ShapeInspectorPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { GroupShape, isGroupShape } from "../../shapes/group";
import { ClipInspector } from "./ClipInspector";
import { AlphaField } from "./AlphaField";
import { HighlightShapeMeta } from "../../composables/states/appCanvas/core";
import { AttachmentInspector } from "./AttachmentInspector";

export const ShapeInspectorPanel: React.FC = () => {
const targetShape = useSelectedShape();
Expand Down Expand Up @@ -147,6 +148,30 @@ const ShapeInspectorPanelWithShape: React.FC<ShapeInspectorPanelWithShapeProps>
[targetShapes, getShapeComposite, patchShapes],
);

// Only shapes already having "attachment" will be updated.
const updateAttachmentBySamePatch = useCallback(
(patch: Partial<Shape>, draft = false) => {
const shapeComposite = getShapeComposite();

const layoutPatch = getPatchByLayouts(shapeComposite, {
update: targetShapes.reduce<{ [id: string]: Partial<Shape> }>((p, s) => {
if (s.attachment) {
p[s.id] = patch;
}
return p;
}, {}),
});

if (draft) {
setTmpShapeMap(layoutPatch);
} else {
setTmpShapeMap({});
patchShapes(layoutPatch);
}
},
[targetShapes, getShapeComposite, patchShapes, setTmpShapeMap],
);

const alphaField = <AlphaField targetTmpShape={targetTmpShape} updateTargetShape={updateTargetShapesBySamePatch} />;

return (
Expand Down Expand Up @@ -194,6 +219,7 @@ const ShapeInspectorPanelWithShape: React.FC<ShapeInspectorPanelWithShapeProps>
updateTargetGroupShape={updateGroupShapesBySamePatch}
/>
) : undefined}
<AttachmentInspector targetShape={targetShape} updateTargetShape={updateAttachmentBySamePatch} />
<button type="submit" className="hidden" />
</form>
);
Expand Down
Loading

0 comments on commit b7c7f9b

Please sign in to comment.