diff --git a/packages/pluggableWidgets/rich-text-web/CHANGELOG.md b/packages/pluggableWidgets/rich-text-web/CHANGELOG.md index f4ed467b37..4f423e0b1d 100644 --- a/packages/pluggableWidgets/rich-text-web/CHANGELOG.md +++ b/packages/pluggableWidgets/rich-text-web/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added + +- We added support to change the form orientation of the link, image and video modals + ## [4.8.1] - 2025-07-29 ### Fixed diff --git a/packages/pluggableWidgets/rich-text-web/src/RichText.xml b/packages/pluggableWidgets/rich-text-web/src/RichText.xml index 94e21433e3..964c0cc67b 100644 --- a/packages/pluggableWidgets/rich-text-web/src/RichText.xml +++ b/packages/pluggableWidgets/rich-text-web/src/RichText.xml @@ -55,6 +55,14 @@ Read panel + + Form orientation + The form orientation for modals (Insert link, Insert image, Insert video). + + Horizontal + Vertical + + diff --git a/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.spec.tsx b/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.spec.tsx index a43372e9d4..fc3c12307e 100644 --- a/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.spec.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.spec.tsx @@ -45,7 +45,8 @@ describe("Rich Text", () => { minHeight: 75, OverflowY: "auto", customFonts: [], - enableDefaultUpload: true + enableDefaultUpload: true, + formOrientation: "vertical" }; }); diff --git a/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/useEmbedModal.ts b/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/useEmbedModal.ts index 1dc0dec0c8..45326639ff 100644 --- a/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/useEmbedModal.ts +++ b/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/useEmbedModal.ts @@ -12,6 +12,7 @@ import { type VideoFormType } from "../ModalDialog/VideoDialog"; import { Delta } from "quill/core"; import { IMG_MIME_TYPES } from "./constants"; import Emitter from "quill/core/emitter"; +import { RichTextContainerProps } from "typings/RichTextProps"; type ModalReturnType = { showDialog: boolean; @@ -23,7 +24,13 @@ type ModalReturnType = { customImageUploadHandler(value: any): void; }; -export function useEmbedModal(ref: MutableRefObject): ModalReturnType { +export function useEmbedModal( + ref: MutableRefObject, + props: Pick< + RichTextContainerProps, + "imageSource" | "imageSourceContent" | "enableDefaultUpload" | "formOrientation" + > +): ModalReturnType { const [showDialog, setShowDialog] = useState(false); const [dialogConfig, setDialogConfig] = useState({}); const openDialog = (): void => { @@ -51,7 +58,8 @@ export function useEmbedModal(ref: MutableRefObject): ModalReturnT closeDialog(); }, onClose: closeDialog, - defaultValue: { ...value, text } + defaultValue: { ...value, text }, + formOrientation: props.formOrientation } }); openDialog(); @@ -113,7 +121,8 @@ export function useEmbedModal(ref: MutableRefObject): ModalReturnT }, onClose: closeDialog, selection: ref.current?.getSelection(), - defaultValue: { ...value } + defaultValue: { ...value }, + formOrientation: props.formOrientation } }); openDialog(); @@ -136,7 +145,8 @@ export function useEmbedModal(ref: MutableRefObject): ModalReturnT } closeDialog(); }, - onClose: closeDialog + onClose: closeDialog, + formOrientation: props.formOrientation } }); openDialog(); @@ -186,7 +196,11 @@ export function useEmbedModal(ref: MutableRefObject): ModalReturnT closeDialog(); }, onClose: closeDialog, - defaultValue: { ...value } + defaultValue: { ...value }, + formOrientation: props.formOrientation, + imageSource: props.imageSource, + imageSourceContent: props.imageSourceContent, + enableDefaultUpload: props.enableDefaultUpload } }); openDialog(); diff --git a/packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx b/packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx index fbf7a6a4a9..0e4a927e84 100644 --- a/packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx @@ -38,6 +38,7 @@ export interface EditorProps defaultValue?: string; onTextChange?: (...args: [delta: Delta, oldContent: Delta, source: EmitterSource]) => void; onSelectionChange?: (...args: [range: Range, oldRange: Range, source: EmitterSource]) => void; + formOrientation: "horizontal" | "vertical"; theme: string; style?: CSSProperties; className?: string; @@ -65,7 +66,7 @@ const Editor = forwardRef((props: EditorProps, ref: MutableRefObject setShowDialog(open)} parentNode={modalRef.current?.ownerDocument.body} - imageSource={props.imageSource} - imageSourceContent={props.imageSourceContent} - enableDefaultUpload={props.enableDefaultUpload} {...dialogConfig} > diff --git a/packages/pluggableWidgets/rich-text-web/src/components/EditorWrapper.tsx b/packages/pluggableWidgets/rich-text-web/src/components/EditorWrapper.tsx index 02a1957fbb..a078a92282 100644 --- a/packages/pluggableWidgets/rich-text-web/src/components/EditorWrapper.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/components/EditorWrapper.tsx @@ -52,7 +52,8 @@ function EditorWrapperInner(props: EditorWrapperProps): ReactElement { tabIndex, imageSource, imageSourceContent, - enableDefaultUpload + enableDefaultUpload, + formOrientation } = props; const globalState = useContext(EditorContext); @@ -215,6 +216,7 @@ function EditorWrapperInner(props: EditorWrapperProps): ReactElement { imageSource={imageSource} imageSourceContent={imageSourceContent} enableDefaultUpload={enableDefaultUpload} + formOrientation={formOrientation} /> {enableStatusBar && ( diff --git a/packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/Dialog.tsx b/packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/Dialog.tsx index 1256ec9fb4..0c3641d11f 100644 --- a/packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/Dialog.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/Dialog.tsx @@ -9,13 +9,13 @@ import { useRole } from "@floating-ui/react"; import { If } from "@mendix/widget-plugin-component-kit/If"; +import classNames from "classnames"; import { createElement, Fragment, ReactElement } from "react"; import LinkDialog, { LinkDialogProps } from "./LinkDialog"; import VideoDialog, { VideoDialogProps } from "./VideoDialog"; import ViewCodeDialog, { ViewCodeDialogProps } from "./ViewCodeDialog"; import ImageDialog, { ImageDialogProps } from "./ImageDialog"; import "./Dialog.scss"; -import { RichTextContainerProps } from "../../../typings/RichTextProps"; interface BaseDialogProps { isOpen: boolean; @@ -49,15 +49,13 @@ export type ChildDialogProps = | ViewCodeDialogBaseProps | ImageDialogBaseProps; -export type DialogProps = BaseDialogProps & - ChildDialogProps & - Pick; +export type DialogProps = BaseDialogProps & ChildDialogProps; /** * Dialog components that will be shown on toolbar's button */ export default function Dialog(props: DialogProps): ReactElement { - const { isOpen, onOpenChange, dialogType, config, imageSource, imageSourceContent, enableDefaultUpload } = props; + const { isOpen, onOpenChange, dialogType, config } = props; const { refs, context } = useFloating({ open: isOpen, onOpenChange @@ -81,7 +79,9 @@ export default function Dialog(props: DialogProps): ReactElement { >
- +
diff --git a/packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/DialogContent.tsx b/packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/DialogContent.tsx index 1bdb66946c..06a2709aec 100644 --- a/packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/DialogContent.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/DialogContent.tsx @@ -36,24 +36,50 @@ export function DialogHeader(props: DialogHeaderProps): ReactElement { ); } -export function DialogBody(props: PropsWithChildrenWithClass): ReactElement { - const { children, className } = props; +export function DialogBody( + props: PropsWithChildrenWithClass & { formOrientation: "horizontal" | "vertical" } +): ReactElement { + const { children, className, formOrientation } = props; - return
{children}
; + return ( +
+ {children} +
+ ); } export interface FormControlProps extends PropsWithChildrenWithClass { label?: string; + formOrientation: "horizontal" | "vertical"; + inputId?: string; } export function FormControl(props: FormControlProps): ReactElement { - const { children, className, label } = props; + const { children, className, label, formOrientation, inputId } = props; return ( -
- {label && } -
{children}
+
+ {label && ( + + )} + {formOrientation === "vertical" ? ( + children + ) : ( +
{children}
+ )}
); diff --git a/packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/ImageDialog.tsx b/packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/ImageDialog.tsx index d6dba5215d..67ba980aae 100644 --- a/packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/ImageDialog.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/ImageDialog.tsx @@ -20,15 +20,19 @@ interface CustomEvent extends Event { initCustomEvent(typeArg: string, canBubbleArg: boolean, cancelableArg: boolean, detailArg: T): void; } -export interface ImageDialogProps extends Pick { +export interface ImageDialogProps + extends Pick< + RichTextContainerProps, + "imageSource" | "imageSourceContent" | "enableDefaultUpload" | "formOrientation" + > { onSubmit(value: imageConfigType): void; onClose(): void; defaultValue?: imageConfigType; - enableDefaultUpload?: boolean; } export default function ImageDialog(props: ImageDialogProps): ReactElement { - const { onClose, defaultValue, onSubmit, imageSource, imageSourceContent, enableDefaultUpload } = props; + const { onClose, defaultValue, onSubmit, imageSource, imageSourceContent, enableDefaultUpload, formOrientation } = + props; const [activeTab, setActiveTab] = useState("general"); const [selectedImageEntity, setSelectedImageEntity] = useState(); const imageUploadElementRef = useRef(null); @@ -127,9 +131,9 @@ export default function ImageDialog(props: ImageDialogProps): ReactElement { }, [imageUploadElementRef.current]); return ( - + {activeTab === "general" ? "Insert/Edit" : "Embed"} Images - +
{!disableEmbed && (
@@ -161,7 +165,11 @@ export default function ImageDialog(props: ImageDialogProps): ReactElement { )}
- + {defaultValue?.src ? ( ) : enableDefaultUpload ? ( ) : undefined} - + - +
- + + Insert/Edit Link - - + + - + - + - - diff --git a/packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/VideoDialog.tsx b/packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/VideoDialog.tsx index a3671ea30e..e24f15c0a2 100644 --- a/packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/VideoDialog.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/VideoDialog.tsx @@ -13,6 +13,7 @@ export interface VideoDialogProps { onClose(): void; selection?: Range | null; defaultValue?: videoConfigType; + formOrientation: "horizontal" | "vertical"; } export function getValueType(value: VideoFormType): VideoFormType { @@ -22,7 +23,7 @@ export function getValueType(value: VideoFormType): VideoFormType { } function GeneralVideoDialog(props: VideoDialogProps): ReactElement { - const { onSubmit, onClose, defaultValue } = props; + const { onSubmit, onClose, defaultValue, formOrientation } = props; const [formState, setFormState] = useState({ src: defaultValue?.src ?? "", width: defaultValue?.width ?? 560, @@ -47,11 +48,12 @@ function GeneralVideoDialog(props: VideoDialogProps): ReactElement { return ( - + {defaultValue?.src ? ( {defaultValue?.src} ) : ( )} - + - + ({ embedcode: "" }); @@ -95,9 +99,10 @@ function EmbedVideoDialog(props: VideoDialogProps): ReactElement { return ( - + {" "}