Skip to content
This repository has been archived by the owner on Apr 24, 2024. It is now read-only.

Commit

Permalink
feat(#1122): fix types and make text editable
Browse files Browse the repository at this point in the history
  • Loading branch information
danielsteinkogler committed Apr 11, 2024
1 parent bef4ece commit ef9ee61
Show file tree
Hide file tree
Showing 17 changed files with 561 additions and 322 deletions.
4 changes: 4 additions & 0 deletions backend/src/model/dto/drawings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ pub enum FillPatternType {
Fill,
#[serde(rename = "none")]
None,
#[serde(rename = "hatch")]
Hatch,
#[serde(rename = "crosshatch")]
CrossHatch,
}

/// Used to change the `add_date` of a drawing.
Expand Down
2 changes: 1 addition & 1 deletion doc/decisions/backend_orm_crate.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Backend ORM/SQL Crate
p# Backend ORM/SQL Crate

## Problem

Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/Form/DebouncedFillPatternInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
SubmitHandler,
useFormContext,
} from 'react-hook-form';
import { FillPatternType } from '@/api_types/definitions';
import { DrawingLayerFillPatterns } from '@/features/map_planning/layers/drawing/DrawingLayerFillPatterns';
import { useDebouncedSubmit } from '@/hooks/useDebouncedSubmit';
import CheckIcon from '@/svg/icons/check.svg?react';
Expand Down Expand Up @@ -59,7 +60,7 @@ export function DebouncedFillPatternSelector<T extends FieldValues>({
<DrawingLayerFillPatterns
{...{ id, ...rest }}
{...field}
selectedPattern={watch(id) as string}
selectedPattern={watch(id) as FillPatternType}
></DrawingLayerFillPatterns>
)}
/>
Expand Down
58 changes: 58 additions & 0 deletions frontend/src/components/Form/DebouncedSimpleFormTextArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { ComponentPropsWithoutRef } from 'react';
import {
FieldValues,
Path,
SubmitErrorHandler,
SubmitHandler,
useFormContext,
} from 'react-hook-form';
import { useDebouncedSubmit } from '@/hooks/useDebouncedSubmit';
import CheckIcon from '@/svg/icons/check.svg?react';
import CircleDottedIcon from '@/svg/icons/circle-dotted.svg?react';
import SimpleFormTextArea from './SimpleFormTextArea';

type DebouncedSimpleFormTextAreatProps<T extends FieldValues = FieldValues> = {
/** A function that takes the from values.
* It is called if the settled value of the input is valid */
onValid: SubmitHandler<T>;
/** A function that takes the form error.
* It is called if the settled value of the input is invalid */
onInvalid?: SubmitErrorHandler<T> | undefined;
/** The elements unique id.
* Gets passed to react-hook-form to identify which field the input belongs to. */
id: Path<T>;
/** The test id for the input */
'data-testid'?: string;
} & Omit<SimpleFormInputProps, 'aria-invalid' | 'id' | 'register'>;

type SimpleFormInputProps = ComponentPropsWithoutRef<typeof SimpleFormTextArea>;

export function DebouncedSimpleFormTextArea<T extends FieldValues>({
onValid,
onInvalid,
id,
'data-testid': testId,
...rest
}: DebouncedSimpleFormTextAreatProps<T>) {
const { handleSubmit, register, watch } = useFormContext<T>();

const submitState = useDebouncedSubmit<T>(watch(id), handleSubmit, onValid, onInvalid);

return (
<div data-testid={testId} className="flex gap-2">
<SimpleFormTextArea {...{ id, register, ...rest }} aria-invalid={submitState === 'error'} />
{submitState === 'loading' && (
<CircleDottedIcon
className="mb-3 mt-auto h-5 w-5 flex-shrink-0 animate-spin text-secondary-400"
data-testid="form-input-loading"
/>
)}
{submitState === 'idle' && (
<CheckIcon
className="mb-3 mt-auto h-5 w-5 flex-shrink-0 text-primary-400"
data-testid="form-input-idle"
/>
)}
</div>
);
}
1 change: 1 addition & 0 deletions frontend/src/config/i18n/de/drawings.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"add_date": "Hinzugefügt am",
"remove_date": "Entfernt am",
"color": "Farbe",
"text": "Text",
"strokeWidth": "Stärke",
"fillEnabled": "Form füllen",
"error_invalid_add_remove_date": "Das Entfernungsdatum muss nach dem Hinzufügungsdatum liegen.",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/config/i18n/en/drawings.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"add_date": "Added on",
"remove_date": "Removed on",
"color": "Color",
"text": "Text",
"strokeWidth": "Stroke Width",
"fillEnabled": "Fill Enabled",
"error_invalid_add_remove_date": "Remove date must be after add date.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import IconButton from '@/components/Button/IconButton';
import SimpleButton, { ButtonVariant } from '@/components/Button/SimpleButton';
import { DebouncedFillPatternSelector } from '@/components/Form/DebouncedFillPatternInput';
import { DebouncedSimpleFormInput } from '@/components/Form/DebouncedSimpleFormInput';
import { DebouncedSimpleFormTextArea } from '@/components/Form/DebouncedSimpleFormTextArea';
import EditIcon from '@/svg/icons/edit.svg?react';
import EraserIcon from '@/svg/icons/eraser.svg?react';
import useMapStore from '../../store/MapStore';
Expand All @@ -18,9 +19,13 @@ const DrawingAttributeEditFormSchema = z
.object({
addDate: z.nullable(z.string()).transform((value) => value || undefined),
removeDate: z.nullable(z.string()).transform((value) => value || undefined),
color: z.string(),
strokeWidth: z.number().nullable(),
fillPattern: z.nullable(z.string()).transform((value) => value || undefined),
text: z.nullable(z.string()).transform((value) => value || undefined),
color: z.nullable(z.string()).transform((value) => value || undefined),
strokeWidth: z.number().optional().nullable(),
fillPattern: z
.nullable(z.string())
.optional()
.transform((value) => value || undefined),
})
.refine((schema) => !schema.removeDate || !schema.addDate || schema.addDate < schema.removeDate, {
path: ['dateRelation'],
Expand All @@ -30,42 +35,63 @@ export type EditDrawingAttributesProps = {
onAddDateChange: (addDate: DrawingFormData) => void;
onRemoveDateChange: (removeDate: DrawingFormData) => void;
onColorChange: (color: DrawingFormData) => void;
onTextChange: (text: DrawingFormData) => void;
onStrokeWidthChange: (strokeWidth: DrawingFormData) => void;
onFillPatternChange: (fillPattern: DrawingFormData) => void;
onDeleteClick: () => void;
isReadOnlyMode: boolean;
};

export type DrawingFormData =
| Pick<DrawingDto, 'addDate' | 'removeDate' | 'scaleX' | 'scaleY'> & {
color?: string;
strokeWidth?: number;
fillPattern?: string;
text?: string;
};

export type DrawingFromElement = {
id: string;
type: DrawingShapeType;
addDate?: string;
removeDate?: string;
color?: string;
strokeWidth?: number;
fillPattern?: string;
text?: string;
};

export type EditSingleDrawingProps = EditDrawingAttributesProps & {
drawing: DrawingDto;
drawing: DrawingFromElement;
};

export type EditMultipleDrawingsProps = EditDrawingAttributesProps & {
drawings: DrawingDto[];
drawings: DrawingFromElement[];
};

export type DrawingAttributeEditFormProps = EditDrawingAttributesProps & {
drawingId?: string;
addDateDefaultValue: string;
removeDateDefaultValue: string;
colorDefaultValue: string;
colorDefaultValue?: string;
textDefaultValue?: string;
strokeWidthDefaultValue?: number;
fillPatternDefaultValue?: string;
multipleDrawings?: boolean;
showShapeEditButton: boolean;
showColor: boolean;
showStrokeWidth: boolean;
showFillPattern: boolean;
showText: boolean;
};

export type DrawingFormData = Pick<
DrawingDto,
'addDate' | 'removeDate' | 'scaleX' | 'scaleY' | 'color' | 'strokeWidth' | 'fillPattern'
>;

export function SingleDrawingAttributeForm({
drawing,
onAddDateChange,
onRemoveDateChange,
onDeleteClick,
onColorChange,
onTextChange,
onStrokeWidthChange,
onFillPatternChange,
isReadOnlyMode,
Expand All @@ -75,18 +101,24 @@ export function SingleDrawingAttributeForm({
<DrawingAttributeEditForm
addDateDefaultValue={drawing.addDate ?? ''}
removeDateDefaultValue={drawing.removeDate ?? ''}
colorDefaultValue={drawing.properties.color ?? ''}
strokeWidthDefaultValue={drawing.properties.strokeWidth ?? 0}
fillPatternDefaultValue={drawing.properties.fillPattern}
colorDefaultValue={drawing.color ?? ''}
strokeWidthDefaultValue={drawing.strokeWidth ?? 0}
fillPatternDefaultValue={drawing.fillPattern}
textDefaultValue={drawing.text}
onAddDateChange={onAddDateChange}
onRemoveDateChange={onRemoveDateChange}
onDeleteClick={onDeleteClick}
onColorChange={onColorChange}
onTextChange={onTextChange}
showColor={drawing.color !== undefined}
showStrokeWidth={drawing.strokeWidth !== undefined}
showFillPattern={drawing.fillPattern !== undefined}
showText={drawing.type === DrawingShapeType.LabelText}
onStrokeWidthChange={onStrokeWidthChange}
onFillPatternChange={onFillPatternChange}
isReadOnlyMode={isReadOnlyMode}
drawingId={drawing.id}
showShapeEditButton={drawing.shapeType === DrawingShapeType.BezierPolygon}
showShapeEditButton={drawing.type === DrawingShapeType.BezierPolygon}
/>
</div>
);
Expand Down Expand Up @@ -114,25 +146,36 @@ export function MultipleDrawingAttributeForm({
return existsCommonDate ? comparisonDate : '';
};

const hasAnyShapeColor = drawings.some((drawing) => drawing.color !== undefined);
const hasAnyShapeStrokeWidth = drawings.some((drawing) => drawing.strokeWidth !== undefined);
const hasAnyShapeFillPattern = drawings.some((drawing) => drawing.fillPattern !== undefined);

const getCommonColor = () => {
const color = drawings[0].properties?.color;
const existsCommonColor = drawings.every((drawing) => drawing.properties.color === color);
return existsCommonColor ? color : '#000000';
const color = drawings[0].color;
const existsCommonColor = drawings.every((drawing) => drawing.color === color);
return existsCommonColor ? color : undefined;
};

const getCommonText = () => {
const text = drawings[0].text;
const existsCommonText = drawings.every((drawing) => drawing.text === text);
return existsCommonText ? text : undefined;
};

const getCommonStrokeWidth = () => {
const strokeWidth = drawings[0].properties?.strokeWidth;
const strokeWidth = drawings[0].strokeWidth;

const existsCommonStrokeWidth = drawings.every(
(drawing) => drawing.properties.strokeWidth === strokeWidth,
(drawing) => drawing.strokeWidth === strokeWidth,
);
return existsCommonStrokeWidth ? strokeWidth : 0;

return existsCommonStrokeWidth ? strokeWidth : undefined;
};

const getCommonFillPattern = () => {
const fillPattern = drawings[0].properties?.fillPattern;

const fillPattern = drawings[0].fillPattern;
const existsCommonFillPattern = drawings.every(
(drawing) => drawing.properties.fillPattern === fillPattern,
(drawing) => drawing.fillPattern === fillPattern,
);

return existsCommonFillPattern ? fillPattern : undefined;
Expand All @@ -146,10 +189,16 @@ export function MultipleDrawingAttributeForm({
colorDefaultValue={getCommonColor()}
strokeWidthDefaultValue={getCommonStrokeWidth()}
fillPatternDefaultValue={getCommonFillPattern()}
textDefaultValue={getCommonText()}
onAddDateChange={onAddDateChange}
onRemoveDateChange={onRemoveDateChange}
onDeleteClick={onDeleteClick}
onColorChange={onColorChange}
onTextChange={onColorChange}
showColor={hasAnyShapeColor}
showStrokeWidth={hasAnyShapeStrokeWidth}
showFillPattern={hasAnyShapeFillPattern}
showText={drawings.every((drawing) => drawing.type === DrawingShapeType.LabelText)}
onStrokeWidthChange={onStrokeWidthChange}
onFillPatternChange={onFillPatternChange}
isReadOnlyMode={isReadOnlyMode}
Expand All @@ -165,34 +214,38 @@ export function DrawingAttributeEditForm({
addDateDefaultValue,
removeDateDefaultValue,
colorDefaultValue,
textDefaultValue,
strokeWidthDefaultValue,
fillPatternDefaultValue,
onAddDateChange,
onRemoveDateChange,
onDeleteClick,
onColorChange,
onTextChange,
onStrokeWidthChange,
onFillPatternChange,
isReadOnlyMode,
multipleDrawings = false,
showShapeEditButton,
showColor,
showStrokeWidth,
showFillPattern,
showText,
}: DrawingAttributeEditFormProps) {
const { t } = useTranslation(['drawings']);

const formInfo = useForm<DrawingFormData>({
defaultValues: {
addDate: addDateDefaultValue,
removeDate: removeDateDefaultValue,
color: colorDefaultValue,
strokeWidth: strokeWidthDefaultValue,
text: textDefaultValue ?? '',
color: colorDefaultValue ?? '',
strokeWidth: strokeWidthDefaultValue ?? 0,
fillPattern: fillPatternDefaultValue,
},
resolver: zodResolver(DrawingAttributeEditFormSchema),
});

const showStrokeWidth = strokeWidthDefaultValue !== undefined && strokeWidthDefaultValue > 0;
const showFillPattern = fillPatternDefaultValue !== undefined;
const showColor = colorDefaultValue !== undefined && colorDefaultValue.length > 0;
const showProperties = showStrokeWidth || showFillPattern || showColor;

const drawingLayerSetEditMode = useMapStore((state) => state.drawingLayerSetEditMode);
Expand All @@ -204,14 +257,16 @@ export function DrawingAttributeEditForm({
formInfo.reset({
addDate: addDateDefaultValue,
removeDate: removeDateDefaultValue,
color: colorDefaultValue,
strokeWidth: strokeWidthDefaultValue,
color: colorDefaultValue ?? '',
text: textDefaultValue ?? '',
strokeWidth: strokeWidthDefaultValue ?? 0,
fillPattern: fillPatternDefaultValue,
});
}, [
addDateDefaultValue,
removeDateDefaultValue,
colorDefaultValue,
textDefaultValue,
strokeWidthDefaultValue,
fillPatternDefaultValue,
formInfo,
Expand Down Expand Up @@ -249,6 +304,19 @@ export function DrawingAttributeEditForm({

{showProperties && <hr className="my-4 border-neutral-700" />}

{showText && (
<div className="flex gap-2">
<DebouncedSimpleFormTextArea
id="text"
type="text"
labelText={t('drawings:text')}
className="h-32 w-36"
disabled={isReadOnlyMode}
onValid={onTextChange}
/>
</div>
)}

{showColor && (
<div className="flex gap-2">
<DebouncedSimpleFormInput
Expand Down
Loading

0 comments on commit ef9ee61

Please sign in to comment.