Skip to content

Commit

Permalink
Misc. refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
elektronaut committed Oct 14, 2024
1 parent 72af081 commit 9048a68
Show file tree
Hide file tree
Showing 29 changed files with 417 additions and 368 deletions.
2 changes: 1 addition & 1 deletion app/assets/builds/pages_core/admin-dist.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions app/assets/builds/pages_core/admin-dist.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions app/javascript/components/Attachments/AttachmentEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ export default function AttachmentEditor(props: Props) {
notice("Embed code copied to clipboard");
};

const save = (evt: MouseEvent) => {
const save = async (evt: MouseEvent) => {
evt.preventDefault();
evt.stopPropagation();

const data = { ...localizations };

void putJson(`/admin/attachments/${attachment.id}`, { attachment: data });
await putJson(`/admin/attachments/${attachment.id}`, { attachment: data });

if (props.onUpdate) {
props.onUpdate(data);
Expand Down
28 changes: 28 additions & 0 deletions app/javascript/components/Attachments/Deleted.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as Attachments from "../../types/Attachments";

type Props = {
attributeName: (record: Attachments.Record) => string;
deleted: Attachments.Record[];
};

export default function Deleted({ attributeName, deleted }: Props) {
return (
<div className="deleted">
{deleted.map((r) => (
<span className="deleted-attachment" key={r.id}>
<input name={`${attributeName(r)}[id]`} type="hidden" value={r.id} />
<input
name={`${attributeName(r)}[attachment_id]`}
type="hidden"
value={(r.attachment && r.attachment.id) || ""}
/>
<input
name={`${attributeName(r)}[_destroy]`}
type="hidden"
value="true"
/>
</span>
))}
</div>
);
}
19 changes: 2 additions & 17 deletions app/javascript/components/Attachments/List.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Attachment from "./Attachment";
import Deleted from "./Deleted";
import Placeholder from "./Placeholder";
import FileUploadButton from "../FileUploadButton";
import { post } from "../../lib/request";
Expand Down Expand Up @@ -146,23 +147,7 @@ export default function List(props: Props) {
return (
<div className={classes.join(" ")} ref={collection.ref} {...listeners}>
<div className="files">{dragOrder.map((d) => attachment(d))}</div>
<div className="deleted">
{deleted.map((r) => (
<span className="deleted-attachment" key={r.id}>
<input name={`${attrName(r)}[id]`} type="hidden" value={r.id} />
<input
name={`${attrName(r)}[attachment_id]`}
type="hidden"
value={(r.attachment && r.attachment.id) || ""}
/>
<input
name={`${attrName(r)}[_destroy]`}
type="hidden"
value="true"
/>
</span>
))}
</div>
<Deleted attributeName={attrName} deleted={deleted} />
<div className="drop-target">
<FileUploadButton
multiple={true}
Expand Down
94 changes: 27 additions & 67 deletions app/javascript/components/ImageCropper.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { useCallback, useState } from "react";

import * as Crop from "../types/Crop";

import Image from "./ImageCropper/Image";
import Toolbar from "./ImageCropper/Toolbar";
import { ImageCropperContext } from "./ImageCropper/useImageCropperContext";
import useContainerSize from "./ImageCropper/useContainerSize";

export { default as useCrop, cropParams } from "./ImageCropper/useCrop";

Expand All @@ -24,73 +23,34 @@ function focalPoint(state: Crop.State): Crop.Position {
}
}

function useContainerSize(): [(node?: HTMLDivElement) => void, Crop.Size] {
const [containerSize, setContainerSize] = useState<Crop.Size>();

const ref = useCallback((node?: HTMLDivElement) => {
const measure = () => {
setContainerSize({
width: node.offsetWidth - 2,
height: node.offsetHeight - 2
});
};
if (node !== null) {
measure();
const observer = new ResizeObserver(measure);
observer.observe(node);
}
}, []);

return [ref, containerSize];
}

export default function ImageCropper(props: Props) {
export default function ImageCropper({
croppedImage,
cropState,
dispatch
}: Props) {
const [containerRef, containerSize] = useContainerSize();

const setAspect = (aspect: number) => {
props.dispatch({ type: "setAspect", payload: aspect });
};

const setCrop = (crop: Crop.CropSize) => {
props.dispatch({ type: "setCrop", payload: crop });
};

const setFocal = (focal: Crop.Position) => {
props.dispatch({ type: "setFocal", payload: focal });
};

const toggleCrop = () => {
if (props.cropState.cropping) {
props.dispatch({ type: "completeCrop" });
} else {
props.dispatch({ type: "startCrop" });
}
};

return (
<div className="visual">
<Toolbar
cropState={props.cropState}
image={props.cropState.image}
setAspect={setAspect}
toggleCrop={toggleCrop}
toggleFocal={() => props.dispatch({ type: "toggleFocal" })}
/>
<div className="image-container" ref={containerRef}>
{!props.croppedImage && (
<div className="loading">Loading image&hellip;</div>
)}
{props.croppedImage && containerSize && (
<Image
cropState={props.cropState}
containerSize={containerSize}
croppedImage={props.croppedImage}
focalPoint={focalPoint(props.cropState)}
setCrop={setCrop}
setFocal={setFocal}
/>
)}
<ImageCropperContext.Provider
value={{
state: cropState,
dispatch: dispatch
}}>
<div className="visual">
<Toolbar />
<div className="image-container" ref={containerRef}>
{!croppedImage && (
<div className="loading">Loading image&hellip;</div>
)}
{croppedImage && containerSize && (
<Image
containerSize={containerSize}
croppedImage={croppedImage}
focalPoint={focalPoint(cropState)}
/>
)}
</div>
</div>
</div>
</ImageCropperContext.Provider>
);
}
8 changes: 4 additions & 4 deletions app/javascript/components/ImageCropper/FocalPoint.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MouseEvent, TouchEvent, useRef, useState } from "react";
import { useRef, useState } from "react";

import * as Crop from "../../types/Crop";

Expand All @@ -8,7 +8,7 @@ type Props = {
onChange: (pos: Crop.Position) => void;
width: number;
height: number;
}
};

function clamp(val: number, min: number, max: number): number {
if (val < min) {
Expand All @@ -32,7 +32,7 @@ export default function FocalPoint(props: Props) {
const containerRef = useRef<HTMLDivElement>();
const pointRef = useRef<HTMLDivElement>();

const dragStart = (evt: MouseEvent | TouchEvent) => {
const dragStart = (evt: React.MouseEvent | React.TouchEvent) => {
evt.preventDefault();
evt.stopPropagation();
if (evt.target == pointRef.current) {
Expand All @@ -47,7 +47,7 @@ export default function FocalPoint(props: Props) {
}
};

const drag = (evt: MouseEvent | TouchEvent) => {
const drag = (evt: React.MouseEvent | React.TouchEvent) => {
if (dragging) {
let x: number, y: number;
const containerSize = containerRef.current.getBoundingClientRect();
Expand Down
46 changes: 29 additions & 17 deletions app/javascript/components/ImageCropper/Image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,41 @@ import ReactCrop from "react-image-crop";
import * as Crop from "../../types/Crop";

import { cropSize } from "./useCrop";
import useImageCropperContext from "./useImageCropperContext";
import FocalPoint from "./FocalPoint";

type Props = {
containerSize: Crop.Size;
croppedImage: string;
cropState: Crop.State;
focalPoint: Crop.Position;
setCrop: (crop: Crop.CropSize) => void;
setFocal: (focal: Crop.Position) => void;
}
};

export default function Image({
containerSize,
croppedImage,
focalPoint
}: Props) {
const { state, dispatch } = useImageCropperContext();

export default function Image(props: Props) {
const imageSize = () => {
const { image, cropping, crop_width, crop_height } = props.cropState;
const { image, cropping, crop_width, crop_height } = state;
if (cropping) {
return { width: image.real_width, height: image.real_height };
} else {
return { width: crop_width, height: crop_height };
}
};

const maxWidth = props.containerSize.width;
const maxHeight = props.containerSize.height;
const setCrop = (crop: Crop.CropSize) => {
dispatch({ type: "setCrop", payload: crop });
};

const setFocal = (focal: Crop.Position) => {
dispatch({ type: "setFocal", payload: focal });
};

const maxWidth = containerSize.width;
const maxHeight = containerSize.height;
const aspect = imageSize().width / imageSize().height;

let width = maxWidth;
Expand All @@ -38,31 +50,31 @@ export default function Image(props: Props) {

const style = { width: `${width}px`, height: `${height}px` };

if (props.cropState.cropping) {
if (state.cropping) {
return (
<div className="image-wrapper" style={style}>
<ReactCrop
src={props.cropState.image.uncropped_url}
crop={cropSize(props.cropState)}
src={state.image.uncropped_url}
crop={cropSize(state)}
minWidth={10}
minHeight={10}
onChange={props.setCrop}
onChange={setCrop}
/>
</div>
);
} else {
return (
<div className="image-wrapper" style={style}>
{props.focalPoint && (
{focalPoint && (
<FocalPoint
width={width}
height={height}
x={props.focalPoint.x}
y={props.focalPoint.y}
onChange={props.setFocal}
x={focalPoint.x}
y={focalPoint.y}
onChange={setFocal}
/>
)}
<img src={props.croppedImage} />
<img src={croppedImage} />
</div>
);
}
Expand Down
Loading

0 comments on commit 9048a68

Please sign in to comment.