Skip to content

Commit 34c7f1b

Browse files
committed
fix: based on initial testing feedback
1 parent d3451c7 commit 34c7f1b

File tree

8 files changed

+179
-99
lines changed

8 files changed

+179
-99
lines changed

Diff for: packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/useEmbedModal.ts

+36-16
Original file line numberDiff line numberDiff line change
@@ -63,27 +63,44 @@ export function useEmbedModal(ref: MutableRefObject<Quill | null>): ModalReturnT
6363

6464
const customVideoHandler = (value: any): void => {
6565
const selection = ref.current?.getSelection();
66-
if (value === true) {
66+
if (value === true || value.type === "video") {
6767
setDialogConfig({
6868
dialogType: "video",
6969
config: {
70-
onSubmit: (value: VideoFormType) => {
71-
if (Object.hasOwn(value, "src") && (value as videoConfigType).src !== undefined) {
72-
const currentValue = value as videoConfigType;
73-
const delta = new Delta()
74-
.retain(selection?.index ?? 0)
75-
.delete(selection?.length ?? 0)
76-
.insert(
77-
{ video: currentValue },
78-
{ width: currentValue.width, height: currentValue.height }
79-
);
80-
ref.current?.updateContents(delta, Emitter.sources.USER);
70+
onSubmit: (submittedValue: VideoFormType) => {
71+
if (
72+
Object.hasOwn(submittedValue, "src") &&
73+
(submittedValue as videoConfigType).src !== undefined
74+
) {
75+
const currentValue = submittedValue as videoConfigType;
76+
if (value.type === "video") {
77+
const index = selection?.index ?? 0;
78+
const length = selection?.length ?? 1;
79+
const videoConfig = {
80+
width: currentValue.width,
81+
height: currentValue.height
82+
};
83+
// update existing video value
84+
const delta = new Delta().retain(index).retain(length, videoConfig);
85+
ref.current?.updateContents(delta, Emitter.sources.USER);
86+
} else {
87+
// insert new video
88+
const delta = new Delta()
89+
.retain(selection?.index ?? 0)
90+
.delete(selection?.length ?? 0)
91+
.insert(
92+
{ video: currentValue },
93+
{ width: currentValue.width, height: currentValue.height }
94+
);
95+
ref.current?.updateContents(delta, Emitter.sources.USER);
96+
}
8197
} else {
82-
const currentValue = value as videoEmbedConfigType;
98+
const currentValue = submittedValue as videoEmbedConfigType;
8399
const res = ref.current?.clipboard.convert({
84100
html: currentValue.embedcode
85101
});
86102
if (res) {
103+
// insert video via embed code;
87104
const delta = new Delta()
88105
.retain(selection?.index ?? 0)
89106
.delete(selection?.length ?? 0)
@@ -95,7 +112,8 @@ export function useEmbedModal(ref: MutableRefObject<Quill | null>): ModalReturnT
95112
closeDialog();
96113
},
97114
onClose: closeDialog,
98-
selection: ref.current?.getSelection()
115+
selection: ref.current?.getSelection(),
116+
defaultValue: { ...value }
99117
}
100118
});
101119
openDialog();
@@ -114,7 +132,7 @@ export function useEmbedModal(ref: MutableRefObject<Quill | null>): ModalReturnT
114132
onSubmit: (value: viewCodeConfigType) => {
115133
const newDelta = ref.current?.clipboard.convert({ html: value.src });
116134
if (newDelta) {
117-
ref.current?.setContents(newDelta, Quill.sources.USER);
135+
ref.current?.setContents(newDelta, Emitter.sources.USER);
118136
}
119137
closeDialog();
120138
},
@@ -142,9 +160,11 @@ export function useEmbedModal(ref: MutableRefObject<Quill | null>): ModalReturnT
142160
width: value.width,
143161
height: value.height
144162
};
163+
// update existing image attribute
145164
const imageUpdateDelta = new Delta().retain(index).retain(length, imageConfig);
146-
ref.current?.updateContents(imageUpdateDelta);
165+
ref.current?.updateContents(imageUpdateDelta, Emitter.sources.USER);
147166
} else {
167+
// upload new image
148168
if (selection && value.files) {
149169
uploadImage(ref, selection, value);
150170
}

Diff for: packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx

+9-32
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import {
2020
} from "./CustomToolbars/toolbarHandlers";
2121
import { useEmbedModal } from "./CustomToolbars/useEmbedModal";
2222
import Dialog from "./ModalDialog/Dialog";
23-
import type QuillResize from "quill-resize-module";
23+
import { RESIZE_MODULE_CONFIG } from "../utils/formats/resizeModuleConfig";
24+
import { EDIT_DIALOG_EVENT } from "src/utils/helpers";
2425
export interface EditorProps {
2526
defaultValue?: string;
2627
onTextChange?: (...args: [delta: Delta, oldContent: Delta, source: EmitterSource]) => void;
@@ -112,35 +113,7 @@ const Editor = forwardRef((props: EditorProps, ref: MutableRefObject<Quill | nul
112113
}
113114
}
114115
: false,
115-
resize: {
116-
tools: [
117-
{ text: "left" },
118-
{ text: "center" },
119-
{ text: "right" },
120-
{ text: "full" },
121-
{
122-
text: "Alt",
123-
verify(activeEle: HTMLElement) {
124-
return activeEle && activeEle.tagName === "IMG";
125-
},
126-
handler(
127-
this: { quill: Quill; resizer: typeof QuillResize },
128-
_evt: MouseEvent,
129-
_button: HTMLElement,
130-
activeEle: HTMLImageElement
131-
) {
132-
const imageInfo = {
133-
alt: activeEle.alt || "",
134-
src: activeEle.src,
135-
width: activeEle.width,
136-
height: activeEle.height
137-
};
138-
this.resizer.handleEdit();
139-
this.quill.emitter.emit("EDIT-TOOLTIP", imageInfo);
140-
}
141-
}
142-
]
143-
}
116+
resize: RESIZE_MODULE_CONFIG
144117
},
145118
readOnly
146119
};
@@ -152,12 +125,16 @@ const Editor = forwardRef((props: EditorProps, ref: MutableRefObject<Quill | nul
152125
quill.on(Quill.events.SELECTION_CHANGE, (...arg) => {
153126
onSelectionChangeRef.current?.(...arg);
154127
});
155-
quill.on("EDIT-TOOLTIP", (...arg: any[]) => {
128+
quill.on(EDIT_DIALOG_EVENT, (...arg: any[]) => {
156129
if (arg[0]) {
157130
if (arg[0].href) {
158131
customLinkHandler(arg[0]);
159132
} else if (arg[0].src) {
160-
customImageUploadHandler(arg[0]);
133+
if (arg[0].type === "video") {
134+
customVideoHandler(arg[0]);
135+
} else {
136+
customImageUploadHandler(arg[0]);
137+
}
161138
}
162139
}
163140
});

Diff for: packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/ImageDialog.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export default function ImageDialog(props: ImageDialogProps): ReactElement {
3939
<DialogContent className="image-dialog">
4040
<DialogHeader onClose={onClose}>Insert/Edit Image</DialogHeader>
4141
<DialogBody>
42-
<FormControl label="Source Code">
42+
<FormControl label="Source">
4343
{defaultValue?.src ? (
4444
<img src={defaultValue.src} alt={defaultValue.alt} height={50} />
4545
) : (

Diff for: packages/pluggableWidgets/rich-text-web/src/components/ModalDialog/VideoDialog.tsx

+34-20
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export interface VideoDialogProps {
1212
onSubmit(value: VideoFormType): void;
1313
onClose(): void;
1414
selection?: Range | null;
15+
defaultValue?: videoConfigType;
1516
}
1617

1718
export function getValueType(value: VideoFormType): VideoFormType {
@@ -21,11 +22,11 @@ export function getValueType(value: VideoFormType): VideoFormType {
2122
}
2223

2324
function GeneralVideoDialog(props: VideoDialogProps): ReactElement {
24-
const { onSubmit, onClose } = props;
25+
const { onSubmit, onClose, defaultValue } = props;
2526
const [formState, setFormState] = useState<videoConfigType>({
26-
src: "",
27-
width: 560,
28-
height: 314
27+
src: defaultValue?.src ?? "",
28+
width: defaultValue?.width ?? 560,
29+
height: defaultValue?.height ?? 314
2930
});
3031

3132
const onInputChange = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>): void => {
@@ -47,7 +48,17 @@ function GeneralVideoDialog(props: VideoDialogProps): ReactElement {
4748
return (
4849
<Fragment>
4950
<FormControl label="URL">
50-
<input className="form-control" type="url" name="src" onChange={onInputChange} value={formState.src} />
51+
{defaultValue?.src ? (
52+
<span className="mx-text-muted">{defaultValue?.src}</span>
53+
) : (
54+
<input
55+
className="form-control"
56+
type="url"
57+
name="src"
58+
onChange={onInputChange}
59+
value={formState.src}
60+
/>
61+
)}
5162
</FormControl>
5263
<FormControl label="Width">
5364
<input
@@ -100,9 +111,10 @@ function EmbedVideoDialog(props: VideoDialogProps): ReactElement {
100111
}
101112

102113
export default function VideoDialog(props: VideoDialogProps): ReactElement {
103-
const { onClose } = props;
114+
const { onClose, defaultValue } = props;
104115
const [activeTab, setActiveTab] = useState("general");
105-
116+
// disable embed tab if it is about modifying current video
117+
const disableEmbed = defaultValue?.src && defaultValue.src.length > 0;
106118
return (
107119
<DialogContent className="video-dialog">
108120
<DialogHeader onClose={onClose}>{activeTab === "general" ? "Insert/Edit" : "Embed"} Media</DialogHeader>
@@ -118,19 +130,21 @@ export default function VideoDialog(props: VideoDialogProps): ReactElement {
118130
>
119131
<a href="#">General</a>
120132
</li>
121-
<li
122-
role="presentation"
123-
className={classNames({
124-
active: activeTab === "embed"
125-
})}
126-
onClick={(e: React.MouseEvent) => {
127-
setActiveTab("embed");
128-
e.stopPropagation();
129-
e.preventDefault();
130-
}}
131-
>
132-
<a href="#">Embed</a>
133-
</li>
133+
{!disableEmbed && (
134+
<li
135+
role="presentation"
136+
className={classNames({
137+
active: activeTab === "embed"
138+
})}
139+
onClick={(e: React.MouseEvent) => {
140+
setActiveTab("embed");
141+
e.stopPropagation();
142+
e.preventDefault();
143+
}}
144+
>
145+
<a href="#">Embed</a>
146+
</li>
147+
)}
134148
</ul>
135149
</div>
136150
<div>

Diff for: packages/pluggableWidgets/rich-text-web/src/ui/RichTextIcons.scss

-29
Original file line numberDiff line numberDiff line change
@@ -62,35 +62,6 @@ $icons: (
6262

6363
button {
6464
position: relative;
65-
> * {
66-
display: none;
67-
}
68-
69-
&:before {
70-
position: absolute;
71-
left: 50%;
72-
top: 50%;
73-
transform: translate(-50%, -50%);
74-
font-size: 16px;
75-
color: black;
76-
display: block;
77-
}
78-
79-
&:nth-child(1):before {
80-
content: map.get($icons, Text-align-left);
81-
}
82-
&:nth-child(2):before {
83-
content: map.get($icons, Text-align-right);
84-
}
85-
&:nth-child(3):before {
86-
content: map.get($icons, Text-align-center);
87-
}
88-
&:nth-child(4):before {
89-
content: map.get($icons, Text-align-justify);
90-
}
91-
&:nth-child(5):before {
92-
content: map.get($icons, Text-background);
93-
}
9465
}
9566
}
9667
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import Quill from "quill";
2+
import QuillResize from "quill-resize-module";
3+
4+
type ToolbarTool = {
5+
text: string;
6+
className: string;
7+
verify: (activeEle: HTMLElement) => boolean;
8+
handler: (
9+
this: typeof QuillResize.Modules.Base,
10+
_evt: MouseEvent,
11+
_button: HTMLElement,
12+
activeEle: HTMLIFrameElement
13+
) => void;
14+
};
15+
16+
class MxResizeToolbar extends QuillResize.Modules.Toolbar {
17+
_addToolbarButtons() {
18+
const buttons: HTMLButtonElement[] = [];
19+
this.options.tools.forEach((tool: ToolbarTool) => {
20+
if (tool.verify && tool.verify.call(this, this.activeEle) === false) return;
21+
22+
const button = document.createElement("button");
23+
button.className = tool.className;
24+
buttons.push(button);
25+
button.setAttribute("aria-label", tool.text);
26+
button.setAttribute("type", "button");
27+
28+
button.addEventListener("click", evt => {
29+
tool.handler.call(this, evt, button, this.activeEle);
30+
// image may change position; redraw drag handles
31+
this.requestUpdate();
32+
});
33+
this.toolbar.appendChild(button);
34+
});
35+
}
36+
}
37+
38+
export const RESIZE_MODULE_CONFIG = {
39+
modules: ["DisplaySize", MxResizeToolbar, "Resize", "Keyboard"],
40+
tools: [
41+
{
42+
text: "Edit Image",
43+
className: "icons icon-Image",
44+
verify(activeEle: HTMLElement) {
45+
return activeEle && activeEle.tagName === "IMG";
46+
},
47+
handler(
48+
this: { quill: Quill; resizer: typeof QuillResize },
49+
_evt: MouseEvent,
50+
_button: HTMLElement,
51+
activeEle: HTMLImageElement
52+
) {
53+
const imageInfo = {
54+
alt: activeEle.alt || "",
55+
src: activeEle.src,
56+
width: activeEle.width,
57+
height: activeEle.height,
58+
type: "image"
59+
};
60+
this.resizer.handleEdit();
61+
this.quill.emitter.emit("EDIT-TOOLTIP", imageInfo);
62+
}
63+
},
64+
{
65+
text: "Edit Video",
66+
className: "icons icon-Film",
67+
verify(activeEle: HTMLElement) {
68+
return activeEle && activeEle.tagName === "IFRAME" && activeEle.classList.contains("ql-video");
69+
},
70+
handler(
71+
this: typeof QuillResize.Modules.Base,
72+
_evt: MouseEvent,
73+
_button: HTMLElement,
74+
activeEle: HTMLIFrameElement
75+
) {
76+
const videoInfo = {
77+
src: activeEle.src,
78+
width: activeEle.width,
79+
height: activeEle.height,
80+
type: "video"
81+
};
82+
this.resizer.handleEdit();
83+
this.quill.emitter.emit("EDIT-TOOLTIP", videoInfo);
84+
}
85+
}
86+
],
87+
parchment: {
88+
image: {
89+
attribute: ["width", "height"]
90+
},
91+
video: {
92+
attribute: ["width", "height"]
93+
}
94+
}
95+
};

Diff for: packages/pluggableWidgets/rich-text-web/src/utils/helpers.ts

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import Quill from "quill";
33
import { Delta, Op } from "quill/core";
44
import { RichTextContainerProps } from "typings/RichTextProps";
55

6+
export const EDIT_DIALOG_EVENT = "EDIT-DIALOG";
7+
68
function getHeightScale(height: number, heightUnit: "pixels" | "percentageOfParent" | "percentageOfView"): string {
79
return `${height}${heightUnit === "pixels" ? "px" : heightUnit === "percentageOfView" ? "vh" : "%"}`;
810
}

0 commit comments

Comments
 (0)