fix: 🐛 add title in options of wysiwyg image extension
This commit is contained in:
parent
f36d4861da
commit
ce3de272bc
9 changed files with 70 additions and 12 deletions
|
|
@ -257,7 +257,8 @@ return [
|
|||
"position" => "Image position",
|
||||
"format" => "Image format",
|
||||
"image" => "Image",
|
||||
"alt" => "ALT attribute of the image",
|
||||
"attribute_alt" => "Alt attribute of the image",
|
||||
"attribute_title" => "Title associated to the image",
|
||||
"caption" => "Caption of the image",
|
||||
"has_caption" => "Presence of a legend",
|
||||
"landscape" => "Landscape",
|
||||
|
|
|
|||
|
|
@ -258,7 +258,8 @@ return [
|
|||
"position" => "Position de l'image",
|
||||
"format" => "Format de l'image",
|
||||
"image" => "Image",
|
||||
"alt" => "Texte alternatif de l'image",
|
||||
"attribute_alt" => "Texte alternatif de l'image",
|
||||
"attribute_title" => "Titre associé à l'image",
|
||||
"caption" => "Légende de l'image",
|
||||
"has_caption" => "Présence d'une légende",
|
||||
"landscape" => "Paysage",
|
||||
|
|
|
|||
|
|
@ -1667,7 +1667,7 @@ export const ModelSelect = forwardRef<ModelSelectRef, ModelSelectProps>(
|
|||
|
||||
{/* Pagination boutons */}
|
||||
{options.length > 0 && lastPage !== 1 && (
|
||||
<div className="border-t flex flex-col xl:flex-row justify-between items-center gap-1.5 p-1">
|
||||
<div className="border-t flex flex-col lg:flex-row justify-between items-center gap-1.5 p-1">
|
||||
<BtnOptionnalLinkWithTooltip
|
||||
content={
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ const Image = TiptapImage.extend({
|
|||
width: null,
|
||||
height: null,
|
||||
alt: null,
|
||||
title: null,
|
||||
caption: null,
|
||||
style: null,
|
||||
};
|
||||
|
|
@ -44,6 +45,7 @@ const Image = TiptapImage.extend({
|
|||
return {
|
||||
src: img.src,
|
||||
alt: img.getAttribute("alt"),
|
||||
title: img.getAttribute("title"),
|
||||
width: parseInt(
|
||||
img.getAttribute("width") || imageDetails.width,
|
||||
10,
|
||||
|
|
|
|||
|
|
@ -57,10 +57,12 @@ export default function ImageButton({ editor, disabled }: ImageProps) {
|
|||
// Values of inputs text.
|
||||
const [inputText, setInputText] = useState<{
|
||||
alt: string;
|
||||
title: string;
|
||||
caption: string | null;
|
||||
hasCaption: boolean;
|
||||
}>({
|
||||
alt: "",
|
||||
title: "",
|
||||
caption: null,
|
||||
hasCaption: false,
|
||||
});
|
||||
|
|
@ -78,7 +80,8 @@ export default function ImageButton({ editor, disabled }: ImageProps) {
|
|||
.ucFirst()
|
||||
.value();
|
||||
const transImage = useTrans("back.wysiwyg.image.image");
|
||||
const transAlt = useTrans("back.wysiwyg.image.alt");
|
||||
const transAlt = useTrans("back.wysiwyg.image.attribute_alt");
|
||||
const transTitle = useTrans("back.wysiwyg.image.attribute_title");
|
||||
const transCaption = useTrans("back.wysiwyg.image.caption");
|
||||
const transValidation = (attribute: string) => {
|
||||
return {
|
||||
|
|
@ -94,6 +97,7 @@ export default function ImageButton({ editor, disabled }: ImageProps) {
|
|||
};
|
||||
};
|
||||
const transValidationAlt = transValidation(transAlt);
|
||||
const transValidationTitle = transValidation(transTitle);
|
||||
const transValidationCaption = transValidation(transCaption);
|
||||
|
||||
// List of image types.
|
||||
|
|
@ -169,6 +173,7 @@ export default function ImageButton({ editor, disabled }: ImageProps) {
|
|||
upload: "image",
|
||||
inputFile: "image",
|
||||
alt: "alt",
|
||||
title: "title",
|
||||
caption: "caption",
|
||||
};
|
||||
// Manage zod errors.
|
||||
|
|
@ -216,6 +221,7 @@ export default function ImageButton({ editor, disabled }: ImageProps) {
|
|||
setFile(null);
|
||||
setInputText({
|
||||
alt: "",
|
||||
title: "",
|
||||
caption: null,
|
||||
hasCaption: false,
|
||||
});
|
||||
|
|
@ -275,6 +281,7 @@ export default function ImageButton({ editor, disabled }: ImageProps) {
|
|||
const inputTextSchema = imageTextSchema({
|
||||
hasCaption: inputText.hasCaption,
|
||||
transValidationAlt,
|
||||
transValidationTitle,
|
||||
transValidationCaption,
|
||||
});
|
||||
|
||||
|
|
@ -285,6 +292,7 @@ export default function ImageButton({ editor, disabled }: ImageProps) {
|
|||
.parse({
|
||||
inputFile,
|
||||
alt: inputText.alt,
|
||||
title: inputText.title,
|
||||
caption: inputText.caption,
|
||||
});
|
||||
|
||||
|
|
@ -295,6 +303,7 @@ export default function ImageButton({ editor, disabled }: ImageProps) {
|
|||
editor.commands.setImage({
|
||||
src: imageSrc,
|
||||
alt: parsedZod.alt,
|
||||
title: parsedZod.title,
|
||||
caption: parsedZod.caption,
|
||||
width: String(inputType.width),
|
||||
height: String(inputType.height),
|
||||
|
|
@ -336,6 +345,7 @@ export default function ImageButton({ editor, disabled }: ImageProps) {
|
|||
useEffect(() => {
|
||||
setInputText({
|
||||
alt: inputText.alt,
|
||||
title: inputText.title,
|
||||
caption: inputText.hasCaption ? inputText.caption : null,
|
||||
hasCaption: inputText.hasCaption,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import str from "@/hooks/use-string";
|
|||
type ImageInputsProps = {
|
||||
value: {
|
||||
alt: string;
|
||||
title: string;
|
||||
caption: string | null;
|
||||
hasCaption: boolean;
|
||||
};
|
||||
|
|
@ -31,13 +32,30 @@ export default function ImageInputs({
|
|||
{/* Input alt attribute of the image. */}
|
||||
<SimpleString
|
||||
name="alt"
|
||||
label={str(useTrans("back.wysiwyg.image.alt"))
|
||||
label={str(useTrans("back.wysiwyg.image.attribute_alt"))
|
||||
.ucFirst()
|
||||
.value()}
|
||||
placeholder="ex: John Doe"
|
||||
required
|
||||
value={value.alt}
|
||||
setData={(_, v) => onChange({ ...value, alt: v })}
|
||||
setData={(_, currentValue) =>
|
||||
onChange({ ...value, alt: currentValue })
|
||||
}
|
||||
disabled={disabled || loading}
|
||||
parentErrors={errors}
|
||||
/>
|
||||
{/* Input title attribute of the image. */}
|
||||
<SimpleString
|
||||
name="title"
|
||||
label={str(useTrans("back.wysiwyg.image.attribute_title"))
|
||||
.ucFirst()
|
||||
.value()}
|
||||
placeholder="ex: John Doe"
|
||||
required
|
||||
value={value.title}
|
||||
setData={(_, currentValue) =>
|
||||
onChange({ ...value, title: currentValue })
|
||||
}
|
||||
disabled={disabled || loading}
|
||||
parentErrors={errors}
|
||||
/>
|
||||
|
|
@ -52,11 +70,11 @@ export default function ImageInputs({
|
|||
name="hasCaption"
|
||||
checked={value.hasCaption}
|
||||
className="justify-center"
|
||||
setData={(_, v) =>
|
||||
setData={(_, currentValue) =>
|
||||
onChange({
|
||||
...value,
|
||||
hasCaption: v,
|
||||
caption: v ? value.caption : null,
|
||||
hasCaption: currentValue,
|
||||
caption: currentValue ? value.caption : null,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
|
@ -68,7 +86,9 @@ export default function ImageInputs({
|
|||
placeholder="ex: John Doe"
|
||||
required={false}
|
||||
value={value.caption ?? ""}
|
||||
setData={(_, v) => onChange({ ...value, caption: v })}
|
||||
setData={(_, currentValue) =>
|
||||
onChange({ ...value, caption: currentValue })
|
||||
}
|
||||
disabled={disabled || loading}
|
||||
parentErrors={errors}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ const PLACEMENT_STYLES: Record<PlacementValue, string> = {
|
|||
type ImageAttrs = {
|
||||
src: string;
|
||||
alt: string;
|
||||
title: string;
|
||||
width: number;
|
||||
height: number;
|
||||
caption: string | null;
|
||||
|
|
@ -112,10 +113,12 @@ export default function ImageNodeView(props: NodeViewProps) {
|
|||
// Values of inputs text.
|
||||
const [inputText, setInputText] = useState<{
|
||||
alt: string;
|
||||
title: string;
|
||||
caption: string | null;
|
||||
hasCaption: boolean;
|
||||
}>({
|
||||
alt: attrs.alt,
|
||||
title: attrs.title,
|
||||
caption: attrs.caption,
|
||||
hasCaption: !!attrs.caption,
|
||||
});
|
||||
|
|
@ -123,7 +126,8 @@ export default function ImageNodeView(props: NodeViewProps) {
|
|||
const [topSticky, setTopSticky] = useState<number>(0);
|
||||
|
||||
// Translations.
|
||||
const transAlt = useTrans("back.wysiwyg.image.alt");
|
||||
const transAlt = useTrans("back.wysiwyg.image.attribute_alt");
|
||||
const transTitle = useTrans("back.wysiwyg.image.attribute_title");
|
||||
const transCaption = useTrans("back.wysiwyg.image.caption");
|
||||
const transValidation = (attribute: string) => {
|
||||
return {
|
||||
|
|
@ -139,6 +143,7 @@ export default function ImageNodeView(props: NodeViewProps) {
|
|||
};
|
||||
};
|
||||
const transValidationAlt = transValidation(transAlt);
|
||||
const transValidationTitle = transValidation(transTitle);
|
||||
const transValidationCaption = transValidation(transCaption);
|
||||
|
||||
/**
|
||||
|
|
@ -260,6 +265,7 @@ export default function ImageNodeView(props: NodeViewProps) {
|
|||
const fieldErrors: typeof errors = {};
|
||||
const fieldMapping: Record<string, string> = {
|
||||
alt: "alt",
|
||||
title: "title",
|
||||
caption: "caption",
|
||||
};
|
||||
if (err instanceof z.ZodError) {
|
||||
|
|
@ -284,6 +290,7 @@ export default function ImageNodeView(props: NodeViewProps) {
|
|||
const inputTextSchema = imageTextSchema({
|
||||
hasCaption: inputText.hasCaption,
|
||||
transValidationAlt,
|
||||
transValidationTitle,
|
||||
transValidationCaption,
|
||||
});
|
||||
|
||||
|
|
@ -291,12 +298,14 @@ export default function ImageNodeView(props: NodeViewProps) {
|
|||
// Validate data.
|
||||
const parsedZod = inputTextSchema.parse({
|
||||
alt: inputText.alt,
|
||||
title: inputText.title,
|
||||
caption: inputText.caption,
|
||||
});
|
||||
|
||||
// Update data of the image in @tiptap editor.
|
||||
updateAttributes({
|
||||
alt: parsedZod.alt,
|
||||
title: parsedZod.title,
|
||||
caption: parsedZod.caption,
|
||||
});
|
||||
setOpen(false);
|
||||
|
|
@ -374,6 +383,7 @@ export default function ImageNodeView(props: NodeViewProps) {
|
|||
useEffect(() => {
|
||||
setInputText({
|
||||
alt: inputText.alt,
|
||||
title: inputText.title,
|
||||
caption: inputText.hasCaption ? inputText.caption : null,
|
||||
hasCaption: inputText.hasCaption,
|
||||
});
|
||||
|
|
@ -423,6 +433,7 @@ export default function ImageNodeView(props: NodeViewProps) {
|
|||
ref={imgRef}
|
||||
src={attrs.src.replace(url, "")}
|
||||
alt={attrs.alt}
|
||||
title={attrs.title}
|
||||
width={attrs.width}
|
||||
height={attrs.height}
|
||||
draggable={false}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export const imageDetails = {
|
|||
export const imageTextSchema = ({
|
||||
hasCaption,
|
||||
transValidationAlt,
|
||||
transValidationTitle,
|
||||
transValidationCaption,
|
||||
}: {
|
||||
hasCaption: boolean;
|
||||
|
|
@ -25,6 +26,11 @@ export const imageTextSchema = ({
|
|||
max: string;
|
||||
string: string;
|
||||
};
|
||||
transValidationTitle: {
|
||||
min: string;
|
||||
max: string;
|
||||
string: string;
|
||||
};
|
||||
transValidationCaption: {
|
||||
min: string;
|
||||
max: string;
|
||||
|
|
@ -36,15 +42,21 @@ export const imageTextSchema = ({
|
|||
.string({ message: transValidationAlt.string })
|
||||
.min(3, transValidationAlt.min)
|
||||
.max(255, transValidationAlt.max);
|
||||
// Attribute title zod rules.
|
||||
const titleRules = z
|
||||
.string({ message: transValidationTitle.string })
|
||||
.min(3, transValidationTitle.min)
|
||||
.max(255, transValidationTitle.max);
|
||||
// Caption zod rules.
|
||||
const captionRules = z
|
||||
.string({ message: transValidationAlt.string })
|
||||
.string({ message: transValidationCaption.string })
|
||||
.min(3, transValidationCaption.min)
|
||||
.max(255, transValidationCaption.max);
|
||||
|
||||
// Return zod object.
|
||||
return z.object({
|
||||
alt: altRules,
|
||||
title: titleRules,
|
||||
caption: hasCaption ? captionRules : z.null(),
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ declare module "@tiptap/core" {
|
|||
setImage: (attrs: {
|
||||
src: string;
|
||||
alt: string;
|
||||
title: string;
|
||||
caption: string | null;
|
||||
width: string;
|
||||
height: string;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue