@rh-support/components 2.5.222 → 2.5.225
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/esm/ConfirmationModal/ConfirmationDialog.js +1 -1
- package/lib/esm/CustomDatePicker/CustomDatePicker.d.ts +18 -0
- package/lib/esm/CustomDatePicker/CustomDatePicker.d.ts.map +1 -0
- package/lib/esm/CustomDatePicker/CustomDatePicker.js +27 -0
- package/lib/esm/CustomDatePicker/customDatePicker.css +52 -0
- package/lib/esm/CustomDatePicker/index.d.ts +2 -0
- package/lib/esm/CustomDatePicker/index.d.ts.map +1 -0
- package/lib/esm/CustomDatePicker/index.js +1 -0
- package/lib/esm/Functional/CaseContactsSelectorExternal.d.ts +2 -0
- package/lib/esm/Functional/CaseContactsSelectorExternal.d.ts.map +1 -1
- package/lib/esm/Functional/CaseContactsSelectorExternal.js +156 -82
- package/lib/esm/InlineEdit/NewInlineEdit.d.ts +2 -0
- package/lib/esm/InlineEdit/NewInlineEdit.d.ts.map +1 -1
- package/lib/esm/InlineEdit/NewInlineEdit.js +7 -2
- package/lib/esm/OwnerTypeaheadDropdown/OwnerTypeaheadDropdown.d.ts +2 -0
- package/lib/esm/OwnerTypeaheadDropdown/OwnerTypeaheadDropdown.d.ts.map +1 -1
- package/lib/esm/OwnerTypeaheadDropdown/OwnerTypeaheadDropdown.js +8 -3
- package/lib/esm/PhoneInput/PhoneInput.d.ts.map +1 -1
- package/lib/esm/PhoneInput/PhoneInput.js +38 -1
- package/lib/esm/RichTextEditor/RichTextEditor.d.ts +45 -0
- package/lib/esm/RichTextEditor/RichTextEditor.d.ts.map +1 -0
- package/lib/esm/RichTextEditor/RichTextEditor.js +463 -0
- package/lib/esm/RichTextEditor/index.d.ts +4 -0
- package/lib/esm/RichTextEditor/index.d.ts.map +1 -0
- package/lib/esm/RichTextEditor/index.js +2 -0
- package/lib/esm/RichTextEditor/richTextEditor.css +153 -0
- package/lib/esm/RichTextEditor/useQuillEditor.d.ts +20 -0
- package/lib/esm/RichTextEditor/useQuillEditor.d.ts.map +1 -0
- package/lib/esm/RichTextEditor/useQuillEditor.js +81 -0
- package/lib/esm/Table/TablePagination.d.ts +1 -0
- package/lib/esm/Table/TablePagination.d.ts.map +1 -1
- package/lib/esm/Table/TablePagination.js +1 -1
- package/lib/esm/helperFunctions/errorMessage.d.ts +2 -1
- package/lib/esm/helperFunctions/errorMessage.d.ts.map +1 -1
- package/lib/esm/helperFunctions/errorMessage.js +19 -5
- package/lib/esm/hooks/useCopyToClipboard.d.ts +1 -1
- package/lib/esm/hooks/useCopyToClipboard.d.ts.map +1 -1
- package/lib/esm/hooks/useCopyToClipboard.js +3 -1
- package/lib/esm/index.d.ts +2 -1
- package/lib/esm/index.d.ts.map +1 -1
- package/lib/esm/index.js +3 -1
- package/package.json +9 -7
- package/lib/esm/MarkdownEditor/MarkdownEditor.d.ts +0 -49
- package/lib/esm/MarkdownEditor/MarkdownEditor.d.ts.map +0 -1
- package/lib/esm/MarkdownEditor/MarkdownEditor.js +0 -601
- package/lib/esm/MarkdownEditor/index.d.ts +0 -2
- package/lib/esm/MarkdownEditor/index.d.ts.map +0 -1
- package/lib/esm/MarkdownEditor/index.js +0 -1
- package/lib/esm/MarkdownEditor/markdownEditor.css +0 -223
|
@@ -107,7 +107,11 @@ const formatNumber = (fullPhoneNumber, localFormat) => {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
const isPatternWithPrefix = (_a = pattern === null || pattern === void 0 ? void 0 : pattern.replace(/\s+/g, '')) === null || _a === void 0 ? void 0 : _a.startsWith(PREFIX);
|
|
110
|
+
// TODO: Temporary — return pattern without forcing + prefix for portal phone without country code.
|
|
111
|
+
pattern = isPatternWithPrefix ? pattern : pattern;
|
|
112
|
+
/* TODO: Revert to previous logic after phone format handling is finalized.
|
|
110
113
|
pattern = isPatternWithPrefix ? pattern : !isEmpty(isPatternWithPrefix) ? PREFIX + pattern : '';
|
|
114
|
+
*/
|
|
111
115
|
return pattern;
|
|
112
116
|
};
|
|
113
117
|
// return phoneline and countrycode for any given phone
|
|
@@ -140,7 +144,11 @@ export function PhoneInput(props) {
|
|
|
140
144
|
const setLocalFormat = (phoneNumber) => {
|
|
141
145
|
const localPhone = removeAllChars(phoneNumber);
|
|
142
146
|
if (shouldNotSetCountryCode) {
|
|
147
|
+
// TODO: Temporary — portal phone without country code; use mask without + prefix.
|
|
148
|
+
return DEFAULT_MASK;
|
|
149
|
+
/* TODO: Revert to previous logic after phone format handling is finalized.
|
|
143
150
|
return DEFAULT_FORMAT; // default format for portal phone without country code
|
|
151
|
+
*/
|
|
144
152
|
}
|
|
145
153
|
let localFormat;
|
|
146
154
|
const exactCountry = getExactCountry(localPhone);
|
|
@@ -169,12 +177,23 @@ export function PhoneInput(props) {
|
|
|
169
177
|
const localFormat = setLocalFormat(phone);
|
|
170
178
|
const _formattedNumber = formatNumber(phone, localFormat);
|
|
171
179
|
setFormattedNumber(_formattedNumber);
|
|
172
|
-
|
|
180
|
+
// TODO: Temporary — do not prepend + when shouldNotSetCountryCode is true.
|
|
181
|
+
if (!shouldNotSetCountryCode &&
|
|
182
|
+
!(phone === null || phone === void 0 ? void 0 : phone.trim().startsWith(PREFIX)) &&
|
|
183
|
+
regex.test(phone) &&
|
|
184
|
+
!isEqual(phone, '-')) {
|
|
173
185
|
onPhoneValueChange(PREFIX + (phone === null || phone === void 0 ? void 0 : phone.trim()));
|
|
174
186
|
}
|
|
175
187
|
else {
|
|
176
188
|
onPhoneValueChange(phone === null || phone === void 0 ? void 0 : phone.slice(0, localFormat === null || localFormat === void 0 ? void 0 : localFormat.length));
|
|
177
189
|
}
|
|
190
|
+
/* TODO: Revert to previous logic after phone format handling is finalized.
|
|
191
|
+
if (!phone?.trim().startsWith(PREFIX) && regex.test(phone) && !isEqual(phone, '-')) {
|
|
192
|
+
onPhoneValueChange(PREFIX + phone?.trim());
|
|
193
|
+
} else {
|
|
194
|
+
onPhoneValueChange(phone?.slice(0, localFormat?.length));
|
|
195
|
+
}
|
|
196
|
+
*/
|
|
178
197
|
};
|
|
179
198
|
const onCountrySelect = (_event, itemId) => {
|
|
180
199
|
var _a, _b;
|
|
@@ -233,10 +252,18 @@ export function PhoneInput(props) {
|
|
|
233
252
|
onPhoneValueChange('');
|
|
234
253
|
}
|
|
235
254
|
else {
|
|
255
|
+
// TODO: Temporary — strip leading + when shouldNotSetCountryCode is true.
|
|
256
|
+
const normalizedPhone = shouldNotSetCountryCode ? (phoneValue === null || phoneValue === void 0 ? void 0 : phoneValue.replace(/^\++/, '')) || '' : phoneValue;
|
|
257
|
+
onPhoneValueChange(normalizedPhone);
|
|
258
|
+
const localFormat = setLocalFormat(normalizedPhone);
|
|
259
|
+
const _formattedNumber = formatNumber(normalizedPhone, localFormat);
|
|
260
|
+
setFormattedNumber(_formattedNumber);
|
|
261
|
+
/* TODO: Revert to previous logic after phone format handling is finalized.
|
|
236
262
|
onPhoneValueChange(phoneValue);
|
|
237
263
|
const localFormat = setLocalFormat(phoneValue);
|
|
238
264
|
const _formattedNumber = formatNumber(phoneValue, localFormat);
|
|
239
265
|
setFormattedNumber(_formattedNumber);
|
|
266
|
+
*/
|
|
240
267
|
}
|
|
241
268
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
242
269
|
}, [phoneValue]);
|
|
@@ -251,10 +278,20 @@ export function PhoneInput(props) {
|
|
|
251
278
|
else {
|
|
252
279
|
onCountryCodeChange && onCountryCodeChange(PREFIX + countryCode);
|
|
253
280
|
}
|
|
281
|
+
// TODO: Temporary — strip leading + when shouldNotSetCountryCode is true.
|
|
282
|
+
const normalizedPhone = shouldNotSetCountryCode
|
|
283
|
+
? removeAllSpaces((phoneValue === null || phoneValue === void 0 ? void 0 : phoneValue.replace(/^\++/, '')) || '')
|
|
284
|
+
: removeAllSpaces(phoneValue);
|
|
285
|
+
onPhoneValueChange && onPhoneValueChange(normalizedPhone);
|
|
286
|
+
const localFormat = setLocalFormat(normalizedPhone);
|
|
287
|
+
const _formattedNumber = formatNumber(normalizedPhone, localFormat);
|
|
288
|
+
setFormattedNumber(_formattedNumber);
|
|
289
|
+
/* TODO: Revert to previous logic after phone format handling is finalized.
|
|
254
290
|
onPhoneValueChange && onPhoneValueChange(removeAllSpaces(phoneValue)); // removing trailing and in between spaces
|
|
255
291
|
const localFormat = setLocalFormat(phoneValue);
|
|
256
292
|
const _formattedNumber = formatNumber(phoneValue, localFormat);
|
|
257
293
|
setFormattedNumber(_formattedNumber);
|
|
294
|
+
*/
|
|
258
295
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
259
296
|
}, [shouldNotSetCountryCode]);
|
|
260
297
|
const toggle = (React.createElement(MenuToggle, { ref: toggleRef, onClick: () => setIsOpen(!isOpen), isExpanded: isOpen, className: "phone-number-select", "data-tracking-id": "case-phone-number-country-code", isDisabled: props.isDisabled, "aria-label": "Country Code" }, iso2 && phoneValue !== '' ? placeholder : t('Country Code')));
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import './richTextEditor.css';
|
|
2
|
+
import Quill from 'quill';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
export interface IRichTextEditorProps {
|
|
5
|
+
/** HTML string value */
|
|
6
|
+
value: string;
|
|
7
|
+
/** Called with HTML string on change */
|
|
8
|
+
onChange: (html: string) => void;
|
|
9
|
+
/** Emit plain text for char counting or previews */
|
|
10
|
+
onChangePlainText?: (text: string) => void;
|
|
11
|
+
/** Placeholder text when editor is empty */
|
|
12
|
+
placeholder?: string;
|
|
13
|
+
/** Disable the editor */
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
/** CSS class for the outer container */
|
|
16
|
+
className?: string;
|
|
17
|
+
/** Unique ID */
|
|
18
|
+
id?: string;
|
|
19
|
+
/** Hide the formatting toolbar */
|
|
20
|
+
hideToolbar?: boolean;
|
|
21
|
+
/** Character limit (counted against plain text) */
|
|
22
|
+
charLimit?: number;
|
|
23
|
+
/** Threshold to show character counter */
|
|
24
|
+
minCharsForCounter?: number;
|
|
25
|
+
/** Callback when character limit is exceeded */
|
|
26
|
+
onCharLimitExceeded?: (exceeded: boolean) => void;
|
|
27
|
+
/** Editor height */
|
|
28
|
+
height?: string | number;
|
|
29
|
+
/** Focus handler */
|
|
30
|
+
onFocus?: () => void;
|
|
31
|
+
/** Blur handler */
|
|
32
|
+
onBlur?: () => void;
|
|
33
|
+
/** Ref binding for programmatic access to the Quill instance */
|
|
34
|
+
bindEditor?: (editor: Quill) => void;
|
|
35
|
+
/** Accessible label */
|
|
36
|
+
'aria-label'?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Async callback invoked when the user selects an image file (via toolbar or paste).
|
|
39
|
+
* Must upload the file and return the image URL to embed.
|
|
40
|
+
*/
|
|
41
|
+
onImageUpload?: (file: File) => Promise<string>;
|
|
42
|
+
}
|
|
43
|
+
export declare function RichTextEditor(props: Readonly<IRichTextEditorProps>): React.JSX.Element;
|
|
44
|
+
export default RichTextEditor;
|
|
45
|
+
//# sourceMappingURL=RichTextEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RichTextEditor.d.ts","sourceRoot":"","sources":["../../../src/RichTextEditor/RichTextEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA+B9B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAmD,MAAM,OAAO,CAAC;AAMxE,MAAM,WAAW,oBAAoB;IACjC,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yBAAyB;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kCAAkC;IAClC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gDAAgD;IAChD,mBAAmB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,oBAAoB;IACpB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,oBAAoB;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,mBAAmB;IACnB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,gEAAgE;IAChE,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,KAAK,IAAI,CAAC;IACrC,uBAAuB;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACnD;AAQD,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,oBAAoB,CAAC,qBAgvBnE;AAED,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import './richTextEditor.css';
|
|
11
|
+
import { Button, ButtonVariant, Dropdown, DropdownItem, DropdownList, FormGroup, MenuToggle, Modal, ModalBody, ModalFooter, ModalHeader, ModalVariant, TextInput, Toolbar, ToolbarGroup, } from '@patternfly/react-core';
|
|
12
|
+
import AlignCenterIcon from '@patternfly/react-icons/dist/js/icons/align-center-icon';
|
|
13
|
+
import AlignJustifyIcon from '@patternfly/react-icons/dist/js/icons/align-justify-icon';
|
|
14
|
+
import AlignLeftIcon from '@patternfly/react-icons/dist/js/icons/align-left-icon';
|
|
15
|
+
import AlignRightIcon from '@patternfly/react-icons/dist/js/icons/align-right-icon';
|
|
16
|
+
import CaretDownIcon from '@patternfly/react-icons/dist/js/icons/caret-down-icon';
|
|
17
|
+
import IndentIcon from '@patternfly/react-icons/dist/js/icons/indent-icon';
|
|
18
|
+
import LinkIcon from '@patternfly/react-icons/dist/js/icons/link-icon';
|
|
19
|
+
import ListOlIcon from '@patternfly/react-icons/dist/js/icons/list-ol-icon';
|
|
20
|
+
import ListUlIcon from '@patternfly/react-icons/dist/js/icons/list-ul-icon';
|
|
21
|
+
import OutdentIcon from '@patternfly/react-icons/dist/js/icons/outdent-icon';
|
|
22
|
+
import RemoveFormatIcon from '@patternfly/react-icons/dist/js/icons/remove-format-icon';
|
|
23
|
+
import StrikethroughIcon from '@patternfly/react-icons/dist/js/icons/strikethrough-icon';
|
|
24
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
25
|
+
import { useTranslation } from 'react-i18next';
|
|
26
|
+
import { ToastNotification } from '../ToastNotification';
|
|
27
|
+
import { useQuillEditor } from './useQuillEditor';
|
|
28
|
+
const defaultCharLimit = 32000;
|
|
29
|
+
const defaultMinCharsForCounter = 31800;
|
|
30
|
+
const ALLOWED_IMAGE_TYPES = new Set(['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml']);
|
|
31
|
+
const IMAGE_SIZE_LIMIT = 2 * 1024 * 1024; // 2 MB
|
|
32
|
+
export function RichTextEditor(props) {
|
|
33
|
+
const { t } = useTranslation();
|
|
34
|
+
const { value, onChange, onChangePlainText, placeholder = '', disabled = false, className = '', id = 'rich-text-editor', hideToolbar = false, charLimit = defaultCharLimit, minCharsForCounter = defaultMinCharsForCounter, onCharLimitExceeded, height, onFocus, onBlur, bindEditor, 'aria-label': ariaLabel, onImageUpload, } = props;
|
|
35
|
+
const [isHeadingDropdownOpen, setIsHeadingDropdownOpen] = useState(false);
|
|
36
|
+
const [charCount, setCharCount] = useState(0);
|
|
37
|
+
const charLimitExceededRef = useRef(false);
|
|
38
|
+
const [isLinkModalOpen, setIsLinkModalOpen] = useState(false);
|
|
39
|
+
const [linkUrl, setLinkUrl] = useState('');
|
|
40
|
+
const [linkText, setLinkText] = useState('');
|
|
41
|
+
const savedSelectionRef = useRef(null);
|
|
42
|
+
const [isImageModalOpen, setIsImageModalOpen] = useState(false);
|
|
43
|
+
const [imageUrl, setImageUrl] = useState('');
|
|
44
|
+
const [imageUrlError, setImageUrlError] = useState('');
|
|
45
|
+
const savedImageSelectionRef = useRef(null);
|
|
46
|
+
const [, setIsImageUploading] = useState(false);
|
|
47
|
+
const fileInputRef = useRef(null);
|
|
48
|
+
const { editorRef, quillRef, activeFormat, setActiveFormat, setContents, setReadOnly } = useQuillEditor({
|
|
49
|
+
placeholder,
|
|
50
|
+
readOnly: disabled,
|
|
51
|
+
onChange: (html) => {
|
|
52
|
+
onChange(html);
|
|
53
|
+
},
|
|
54
|
+
onChangePlainText: (text) => {
|
|
55
|
+
onChangePlainText === null || onChangePlainText === void 0 ? void 0 : onChangePlainText(text);
|
|
56
|
+
setCharCount(text.length);
|
|
57
|
+
},
|
|
58
|
+
onFocus,
|
|
59
|
+
onBlur,
|
|
60
|
+
});
|
|
61
|
+
// Sync external value into Quill when it changes externally
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
setContents(value);
|
|
64
|
+
}, [value, setContents]);
|
|
65
|
+
// Sync disabled state
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
setReadOnly(disabled);
|
|
68
|
+
}, [disabled, setReadOnly]);
|
|
69
|
+
// Bind editor ref externally
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (quillRef.current && bindEditor) {
|
|
72
|
+
bindEditor(quillRef.current);
|
|
73
|
+
}
|
|
74
|
+
}, [bindEditor, quillRef]);
|
|
75
|
+
// Character limit exceeded callback
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
const exceeded = charCount > charLimit;
|
|
78
|
+
if (exceeded !== charLimitExceededRef.current) {
|
|
79
|
+
charLimitExceededRef.current = exceeded;
|
|
80
|
+
onCharLimitExceeded === null || onCharLimitExceeded === void 0 ? void 0 : onCharLimitExceeded(exceeded);
|
|
81
|
+
}
|
|
82
|
+
}, [charCount, charLimit, onCharLimitExceeded]);
|
|
83
|
+
const onImageUploadRef = useRef(onImageUpload);
|
|
84
|
+
onImageUploadRef.current = onImageUpload;
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
const editorEl = editorRef.current;
|
|
87
|
+
if (!editorEl || !onImageUploadRef.current)
|
|
88
|
+
return;
|
|
89
|
+
const handlePaste = (e) => __awaiter(this, void 0, void 0, function* () {
|
|
90
|
+
var _a;
|
|
91
|
+
if (!((_a = e.clipboardData) === null || _a === void 0 ? void 0 : _a.items))
|
|
92
|
+
return;
|
|
93
|
+
const imageItem = Array.from(e.clipboardData.items).find((item) => item.type.startsWith('image/') && ALLOWED_IMAGE_TYPES.has(item.type));
|
|
94
|
+
if (!imageItem)
|
|
95
|
+
return;
|
|
96
|
+
const file = imageItem.getAsFile();
|
|
97
|
+
if (!file)
|
|
98
|
+
return;
|
|
99
|
+
if (file.size > IMAGE_SIZE_LIMIT) {
|
|
100
|
+
e.preventDefault();
|
|
101
|
+
e.stopPropagation();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
e.preventDefault();
|
|
105
|
+
e.stopPropagation();
|
|
106
|
+
const quill = quillRef.current;
|
|
107
|
+
if (!quill)
|
|
108
|
+
return;
|
|
109
|
+
const range = quill.getSelection() || { index: quill.getLength() - 1, length: 0 };
|
|
110
|
+
setIsImageUploading(true);
|
|
111
|
+
try {
|
|
112
|
+
const url = yield onImageUploadRef.current(file);
|
|
113
|
+
if (url) {
|
|
114
|
+
quill.insertEmbed(range.index, 'image', url);
|
|
115
|
+
quill.setSelection(range.index + 1, 0, 'silent');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (_b) {
|
|
119
|
+
// Error handled by the consumer's upload implementation
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
setIsImageUploading(false);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
editorEl.addEventListener('paste', handlePaste);
|
|
126
|
+
return () => {
|
|
127
|
+
editorEl.removeEventListener('paste', handlePaste);
|
|
128
|
+
};
|
|
129
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
130
|
+
}, [editorRef, quillRef]);
|
|
131
|
+
const formatToggle = useCallback((format, value) => {
|
|
132
|
+
const quill = quillRef.current;
|
|
133
|
+
if (!quill)
|
|
134
|
+
return;
|
|
135
|
+
const selection = quill.getSelection();
|
|
136
|
+
if (!selection)
|
|
137
|
+
return;
|
|
138
|
+
const newValue = value !== null && value !== void 0 ? value : !activeFormat[format];
|
|
139
|
+
quill.format(format, newValue);
|
|
140
|
+
setActiveFormat((prev) => {
|
|
141
|
+
const next = Object.assign({}, prev);
|
|
142
|
+
if (newValue) {
|
|
143
|
+
next[format] = newValue;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
delete next[format];
|
|
147
|
+
}
|
|
148
|
+
return next;
|
|
149
|
+
});
|
|
150
|
+
}, [activeFormat, quillRef, setActiveFormat]);
|
|
151
|
+
const onLinkClick = useCallback(() => {
|
|
152
|
+
const quill = quillRef.current;
|
|
153
|
+
if (!quill)
|
|
154
|
+
return;
|
|
155
|
+
const range = quill.getSelection();
|
|
156
|
+
if (!range)
|
|
157
|
+
return;
|
|
158
|
+
if (activeFormat.link) {
|
|
159
|
+
quill.format('link', false);
|
|
160
|
+
setActiveFormat((prev) => {
|
|
161
|
+
const next = Object.assign({}, prev);
|
|
162
|
+
delete next.link;
|
|
163
|
+
return next;
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
savedSelectionRef.current = { index: range.index, length: range.length };
|
|
168
|
+
setLinkText(range.length > 0 ? quill.getText(range.index, range.length).trim() : '');
|
|
169
|
+
setLinkUrl('');
|
|
170
|
+
setIsLinkModalOpen(true);
|
|
171
|
+
}
|
|
172
|
+
}, [activeFormat, quillRef, setActiveFormat]);
|
|
173
|
+
const onLinkInsert = useCallback(() => {
|
|
174
|
+
const quill = quillRef.current;
|
|
175
|
+
const saved = savedSelectionRef.current;
|
|
176
|
+
const url = linkUrl.trim();
|
|
177
|
+
const text = linkText.trim() || url;
|
|
178
|
+
if (!quill || !saved || !url)
|
|
179
|
+
return;
|
|
180
|
+
if (saved.length > 0) {
|
|
181
|
+
quill.setSelection(saved.index, saved.length, 'silent');
|
|
182
|
+
quill.format('link', url);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
quill.insertText(saved.index, text, 'link', url, 'user');
|
|
186
|
+
quill.setSelection(saved.index + text.length, 0, 'silent');
|
|
187
|
+
}
|
|
188
|
+
setActiveFormat((prev) => (Object.assign(Object.assign({}, prev), { link: url })));
|
|
189
|
+
savedSelectionRef.current = null;
|
|
190
|
+
setIsLinkModalOpen(false);
|
|
191
|
+
setLinkUrl('');
|
|
192
|
+
setLinkText('');
|
|
193
|
+
}, [linkUrl, linkText, quillRef, setActiveFormat]);
|
|
194
|
+
const onLinkCancel = useCallback(() => {
|
|
195
|
+
savedSelectionRef.current = null;
|
|
196
|
+
setIsLinkModalOpen(false);
|
|
197
|
+
setLinkText('');
|
|
198
|
+
setLinkUrl('');
|
|
199
|
+
}, []);
|
|
200
|
+
const handleFileInputChange = useCallback((e) => __awaiter(this, void 0, void 0, function* () {
|
|
201
|
+
var _a;
|
|
202
|
+
const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
203
|
+
if (fileInputRef.current)
|
|
204
|
+
fileInputRef.current.value = '';
|
|
205
|
+
if (!file || !onImageUpload)
|
|
206
|
+
return;
|
|
207
|
+
if (!ALLOWED_IMAGE_TYPES.has(file.type)) {
|
|
208
|
+
ToastNotification.addDangerMessage(t('Invalid file type. Only JPEG, PNG, GIF, and SVG images are allowed.'));
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (file.size > IMAGE_SIZE_LIMIT) {
|
|
212
|
+
ToastNotification.addDangerMessage(t('File is too large. Maximum size is 2 MB.'));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const quill = quillRef.current;
|
|
216
|
+
const saved = savedImageSelectionRef.current;
|
|
217
|
+
if (!quill || !saved)
|
|
218
|
+
return;
|
|
219
|
+
setIsImageUploading(true);
|
|
220
|
+
try {
|
|
221
|
+
const url = yield onImageUpload(file);
|
|
222
|
+
if (url) {
|
|
223
|
+
quill.focus();
|
|
224
|
+
quill.setSelection(saved.index, saved.length, 'silent');
|
|
225
|
+
quill.insertEmbed(saved.index, 'image', url);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch (_b) {
|
|
229
|
+
ToastNotification.addDangerMessage(t('Failed to upload image.'));
|
|
230
|
+
}
|
|
231
|
+
finally {
|
|
232
|
+
setIsImageUploading(false);
|
|
233
|
+
savedImageSelectionRef.current = null;
|
|
234
|
+
}
|
|
235
|
+
}), [onImageUpload, quillRef, t]);
|
|
236
|
+
const onImageInsert = useCallback(() => {
|
|
237
|
+
const quill = quillRef.current;
|
|
238
|
+
const saved = savedImageSelectionRef.current;
|
|
239
|
+
if (!quill || !saved || !imageUrl.trim())
|
|
240
|
+
return;
|
|
241
|
+
const url = imageUrl.trim();
|
|
242
|
+
try {
|
|
243
|
+
const parsed = new URL(url);
|
|
244
|
+
if (parsed.hostname !== 'redhat.com' && !parsed.hostname.endsWith('.redhat.com')) {
|
|
245
|
+
setImageUrlError(t('Only *.redhat.com URLs are allowed'));
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
catch (_a) {
|
|
250
|
+
setImageUrlError(t('Only *.redhat.com URLs are allowed'));
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
quill.setSelection(saved.index, saved.length, 'silent');
|
|
254
|
+
quill.insertEmbed(saved.index, 'image', url);
|
|
255
|
+
savedImageSelectionRef.current = null;
|
|
256
|
+
setIsImageModalOpen(false);
|
|
257
|
+
setImageUrl('');
|
|
258
|
+
setImageUrlError('');
|
|
259
|
+
}, [imageUrl, quillRef, t]);
|
|
260
|
+
const onImageCancel = useCallback(() => {
|
|
261
|
+
savedImageSelectionRef.current = null;
|
|
262
|
+
setIsImageModalOpen(false);
|
|
263
|
+
setImageUrl('');
|
|
264
|
+
setImageUrlError('');
|
|
265
|
+
}, []);
|
|
266
|
+
const onImageUrlChange = useCallback((_e, val) => {
|
|
267
|
+
setImageUrl(val);
|
|
268
|
+
if (val.trim()) {
|
|
269
|
+
try {
|
|
270
|
+
const parsed = new URL(val.trim());
|
|
271
|
+
if (parsed.hostname !== 'redhat.com' && !parsed.hostname.endsWith('.redhat.com')) {
|
|
272
|
+
setImageUrlError(t('Only *.redhat.com URLs are allowed'));
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
setImageUrlError('');
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
catch (_a) {
|
|
279
|
+
setImageUrlError(t('Only *.redhat.com URLs are allowed'));
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
setImageUrlError('');
|
|
284
|
+
}
|
|
285
|
+
}, [t]);
|
|
286
|
+
const onImageUrlKeyDown = useCallback((e) => {
|
|
287
|
+
if (e.key === 'Enter') {
|
|
288
|
+
e.preventDefault();
|
|
289
|
+
onImageInsert();
|
|
290
|
+
}
|
|
291
|
+
}, [onImageInsert]);
|
|
292
|
+
const onLinkTextChange = useCallback((_e, val) => {
|
|
293
|
+
setLinkText(val);
|
|
294
|
+
}, []);
|
|
295
|
+
const onLinkUrlChange = useCallback((_e, val) => {
|
|
296
|
+
setLinkUrl(val);
|
|
297
|
+
}, []);
|
|
298
|
+
const onLinkUrlKeyDown = useCallback((e) => {
|
|
299
|
+
if (e.key === 'Enter') {
|
|
300
|
+
e.preventDefault();
|
|
301
|
+
onLinkInsert();
|
|
302
|
+
}
|
|
303
|
+
}, [onLinkInsert]);
|
|
304
|
+
const onToolbarMouseDown = useCallback((e) => {
|
|
305
|
+
e.preventDefault();
|
|
306
|
+
}, []);
|
|
307
|
+
const onBoldClick = useCallback(() => formatToggle('bold'), [formatToggle]);
|
|
308
|
+
const onItalicClick = useCallback(() => formatToggle('italic'), [formatToggle]);
|
|
309
|
+
const onUnderlineClick = useCallback(() => formatToggle('underline'), [formatToggle]);
|
|
310
|
+
const onStrikethroughClick = useCallback(() => formatToggle('strike'), [formatToggle]);
|
|
311
|
+
const onIndentClick = useCallback(() => {
|
|
312
|
+
const quill = quillRef.current;
|
|
313
|
+
if (!quill)
|
|
314
|
+
return;
|
|
315
|
+
const range = quill.getSelection();
|
|
316
|
+
if (!range)
|
|
317
|
+
return;
|
|
318
|
+
const current = quill.getFormat(range).indent || 0;
|
|
319
|
+
quill.format('indent', current + 1);
|
|
320
|
+
setActiveFormat((prev) => (Object.assign(Object.assign({}, prev), { indent: current + 1 })));
|
|
321
|
+
}, [quillRef, setActiveFormat]);
|
|
322
|
+
const onOutdentClick = useCallback(() => {
|
|
323
|
+
const quill = quillRef.current;
|
|
324
|
+
if (!quill)
|
|
325
|
+
return;
|
|
326
|
+
const range = quill.getSelection();
|
|
327
|
+
if (!range)
|
|
328
|
+
return;
|
|
329
|
+
const current = quill.getFormat(range).indent || 0;
|
|
330
|
+
if (current > 0) {
|
|
331
|
+
quill.format('indent', current - 1 || false);
|
|
332
|
+
setActiveFormat((prev) => (Object.assign(Object.assign({}, prev), { indent: current - 1 || false })));
|
|
333
|
+
}
|
|
334
|
+
}, [quillRef, setActiveFormat]);
|
|
335
|
+
const onAlignClick = useCallback((alignment) => {
|
|
336
|
+
const quill = quillRef.current;
|
|
337
|
+
if (!quill)
|
|
338
|
+
return;
|
|
339
|
+
const range = quill.getSelection();
|
|
340
|
+
if (!range)
|
|
341
|
+
return;
|
|
342
|
+
const currentAlign = activeFormat.align || false;
|
|
343
|
+
const newValue = currentAlign === alignment ? false : alignment;
|
|
344
|
+
quill.format('align', newValue);
|
|
345
|
+
setActiveFormat((prev) => {
|
|
346
|
+
const next = Object.assign({}, prev);
|
|
347
|
+
if (newValue) {
|
|
348
|
+
next.align = newValue;
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
delete next.align;
|
|
352
|
+
}
|
|
353
|
+
return next;
|
|
354
|
+
});
|
|
355
|
+
}, [activeFormat.align, quillRef, setActiveFormat]);
|
|
356
|
+
const onAlignLeftClick = useCallback(() => onAlignClick(false), [onAlignClick]);
|
|
357
|
+
const onAlignCenterClick = useCallback(() => onAlignClick('center'), [onAlignClick]);
|
|
358
|
+
const onAlignRightClick = useCallback(() => onAlignClick('right'), [onAlignClick]);
|
|
359
|
+
const onAlignJustifyClick = useCallback(() => onAlignClick('justify'), [onAlignClick]);
|
|
360
|
+
const onRemoveFormatClick = useCallback(() => {
|
|
361
|
+
const quill = quillRef.current;
|
|
362
|
+
if (!quill)
|
|
363
|
+
return;
|
|
364
|
+
const range = quill.getSelection();
|
|
365
|
+
if (!range)
|
|
366
|
+
return;
|
|
367
|
+
if (range.length === 0) {
|
|
368
|
+
const [line, offset] = quill.getLine(range.index);
|
|
369
|
+
if (line) {
|
|
370
|
+
quill.removeFormat(range.index - offset, line.length());
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
quill.removeFormat(range.index, range.length);
|
|
375
|
+
}
|
|
376
|
+
setActiveFormat(quill.getFormat(range));
|
|
377
|
+
}, [quillRef, setActiveFormat]);
|
|
378
|
+
const onHeadingSelect = useCallback(() => setIsHeadingDropdownOpen(false), []);
|
|
379
|
+
const onHeadingToggleClick = useCallback(() => setIsHeadingDropdownOpen((prev) => !prev), []);
|
|
380
|
+
const onHeading1Click = useCallback(() => formatToggle('header', 1), [formatToggle]);
|
|
381
|
+
const onHeading2Click = useCallback(() => formatToggle('header', 2), [formatToggle]);
|
|
382
|
+
const onHeading3Click = useCallback(() => formatToggle('header', 3), [formatToggle]);
|
|
383
|
+
const onHeadingNormalClick = useCallback(() => formatToggle('header', false), [formatToggle]);
|
|
384
|
+
const onBulletListClick = useCallback(() => formatToggle('list', activeFormat.list === 'bullet' ? false : 'bullet'), [formatToggle, activeFormat.list]);
|
|
385
|
+
const onOrderedListClick = useCallback(() => formatToggle('list', activeFormat.list === 'ordered' ? false : 'ordered'), [formatToggle, activeFormat.list]);
|
|
386
|
+
const headingToggleRenderer = useCallback((toggleRef) => {
|
|
387
|
+
const headingLevel = activeFormat.header;
|
|
388
|
+
const label = headingLevel ? `Heading ${headingLevel}` : 'Normal';
|
|
389
|
+
return (React.createElement(MenuToggle, { ref: toggleRef, onClick: onHeadingToggleClick, isDisabled: disabled, variant: "plain", "aria-label": `Text style: ${label}`, className: `richtext-toolbar-btn ${headingLevel ? 'pf-m-active' : ''}` },
|
|
390
|
+
React.createElement("span", { className: "toggle-content" },
|
|
391
|
+
label,
|
|
392
|
+
" ",
|
|
393
|
+
React.createElement(CaretDownIcon, { className: "dropdown-caret" }))));
|
|
394
|
+
}, [activeFormat.header, disabled, onHeadingToggleClick]);
|
|
395
|
+
const editorStyle = {};
|
|
396
|
+
if (height) {
|
|
397
|
+
editorStyle.height = typeof height === 'number' ? `${height}px` : height;
|
|
398
|
+
}
|
|
399
|
+
const showCounter = charCount >= minCharsForCounter;
|
|
400
|
+
const isOverLimit = charCount > charLimit;
|
|
401
|
+
return (React.createElement("div", { id: id, className: `richtext-editor-container ${disabled ? 'richtext-editor-container--disabled' : ''} ${className}` },
|
|
402
|
+
!hideToolbar && (React.createElement(Toolbar, { className: "richtext-toolbar", role: "toolbar", "aria-label": "Formatting options", onMouseDown: onToolbarMouseDown },
|
|
403
|
+
React.createElement(ToolbarGroup, { className: "richtext-toolbar-left-section" },
|
|
404
|
+
React.createElement(Button, { variant: ButtonVariant.plain, isClicked: !!activeFormat.bold, isDisabled: disabled, onClick: onBoldClick, "aria-label": "Bold", "aria-pressed": !!activeFormat.bold, className: "richtext-toolbar-btn" },
|
|
405
|
+
React.createElement("strong", null, "B")),
|
|
406
|
+
React.createElement(Button, { variant: ButtonVariant.plain, isClicked: !!activeFormat.italic, isDisabled: disabled, onClick: onItalicClick, "aria-label": "Italic", "aria-pressed": !!activeFormat.italic, className: "richtext-toolbar-btn" },
|
|
407
|
+
React.createElement("em", null, "I")),
|
|
408
|
+
React.createElement(Button, { variant: ButtonVariant.plain, isClicked: !!activeFormat.underline, isDisabled: disabled, onClick: onUnderlineClick, "aria-label": "Underline", "aria-pressed": !!activeFormat.underline, className: "richtext-toolbar-btn" },
|
|
409
|
+
React.createElement("u", null, "U")),
|
|
410
|
+
React.createElement(Button, { icon: React.createElement(StrikethroughIcon, null), variant: ButtonVariant.plain, isClicked: !!activeFormat.strike, isDisabled: disabled, onClick: onStrikethroughClick, "aria-label": "Strikethrough", "aria-pressed": !!activeFormat.strike, className: "richtext-toolbar-btn" }),
|
|
411
|
+
React.createElement("div", { className: "btn-group-separator" }),
|
|
412
|
+
React.createElement(Dropdown, { isOpen: isHeadingDropdownOpen, onSelect: onHeadingSelect, onOpenChange: setIsHeadingDropdownOpen, toggle: headingToggleRenderer },
|
|
413
|
+
React.createElement(DropdownList, null,
|
|
414
|
+
React.createElement(DropdownItem, { key: "h1", onClick: onHeading1Click }, "Heading 1"),
|
|
415
|
+
React.createElement(DropdownItem, { key: "h2", onClick: onHeading2Click }, "Heading 2"),
|
|
416
|
+
React.createElement(DropdownItem, { key: "h3", onClick: onHeading3Click }, "Heading 3"),
|
|
417
|
+
React.createElement(DropdownItem, { key: "normal", onClick: onHeadingNormalClick }, "Normal"))),
|
|
418
|
+
React.createElement("div", { className: "btn-group-separator" }),
|
|
419
|
+
React.createElement(Button, { icon: React.createElement(ListUlIcon, null), variant: ButtonVariant.plain, isClicked: activeFormat.list === 'bullet', isDisabled: disabled, onClick: onBulletListClick, "aria-label": "Bullet List", "aria-pressed": activeFormat.list === 'bullet', className: "richtext-toolbar-btn" }),
|
|
420
|
+
React.createElement(Button, { icon: React.createElement(ListOlIcon, null), variant: ButtonVariant.plain, isClicked: activeFormat.list === 'ordered', isDisabled: disabled, onClick: onOrderedListClick, "aria-label": "Ordered List", "aria-pressed": activeFormat.list === 'ordered', className: "richtext-toolbar-btn" }),
|
|
421
|
+
React.createElement(Button, { icon: React.createElement(IndentIcon, null), variant: ButtonVariant.plain, isDisabled: disabled, onClick: onIndentClick, "aria-label": "Indent", className: "richtext-toolbar-btn" }),
|
|
422
|
+
React.createElement(Button, { icon: React.createElement(OutdentIcon, null), variant: ButtonVariant.plain, isDisabled: disabled, onClick: onOutdentClick, "aria-label": "Outdent", className: "richtext-toolbar-btn" }),
|
|
423
|
+
React.createElement("div", { className: "btn-group-separator" }),
|
|
424
|
+
React.createElement(Button, { icon: React.createElement(AlignLeftIcon, null), variant: ButtonVariant.plain, isClicked: !activeFormat.align, isDisabled: disabled, onClick: onAlignLeftClick, "aria-label": "Align Left", "aria-pressed": !activeFormat.align, className: "richtext-toolbar-btn" }),
|
|
425
|
+
React.createElement(Button, { icon: React.createElement(AlignCenterIcon, null), variant: ButtonVariant.plain, isClicked: activeFormat.align === 'center', isDisabled: disabled, onClick: onAlignCenterClick, "aria-label": "Align Center", "aria-pressed": activeFormat.align === 'center', className: "richtext-toolbar-btn" }),
|
|
426
|
+
React.createElement(Button, { icon: React.createElement(AlignRightIcon, null), variant: ButtonVariant.plain, isClicked: activeFormat.align === 'right', isDisabled: disabled, onClick: onAlignRightClick, "aria-label": "Align Right", "aria-pressed": activeFormat.align === 'right', className: "richtext-toolbar-btn" }),
|
|
427
|
+
React.createElement(Button, { icon: React.createElement(AlignJustifyIcon, null), variant: ButtonVariant.plain, isClicked: activeFormat.align === 'justify', isDisabled: disabled, onClick: onAlignJustifyClick, "aria-label": "Justify", "aria-pressed": activeFormat.align === 'justify', className: "richtext-toolbar-btn" }),
|
|
428
|
+
React.createElement("div", { className: "btn-group-separator" }),
|
|
429
|
+
React.createElement(Button, { icon: React.createElement(LinkIcon, null), variant: ButtonVariant.plain, isClicked: !!activeFormat.link, isDisabled: disabled, onClick: onLinkClick, "aria-label": "Link", "aria-pressed": !!activeFormat.link, className: "richtext-toolbar-btn" }),
|
|
430
|
+
React.createElement(Button, { icon: React.createElement(RemoveFormatIcon, null), variant: ButtonVariant.plain, isDisabled: disabled, onClick: onRemoveFormatClick, "aria-label": "Remove Format", className: "richtext-toolbar-btn" })))),
|
|
431
|
+
React.createElement("div", { ref: editorRef, role: "textbox", className: "richtext-editor-content", style: editorStyle, "aria-label": ariaLabel, tabIndex: -1, onClick: () => { var _a, _b; return (_b = (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.querySelector('[contenteditable="true"]')) === null || _b === void 0 ? void 0 : _b.focus(); }, onKeyDown: (e) => {
|
|
432
|
+
var _a, _b;
|
|
433
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
434
|
+
(_b = (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.querySelector('[contenteditable="true"]')) === null || _b === void 0 ? void 0 : _b.focus();
|
|
435
|
+
}
|
|
436
|
+
} }),
|
|
437
|
+
showCounter && (React.createElement("div", { className: `richtext-char-counter ${isOverLimit ? 'richtext-char-counter--exceeded' : ''}` },
|
|
438
|
+
charCount,
|
|
439
|
+
"/",
|
|
440
|
+
charLimit)),
|
|
441
|
+
React.createElement(Modal, { variant: ModalVariant.small, isOpen: isLinkModalOpen, onClose: onLinkCancel, "aria-labelledby": "insert-link-title" },
|
|
442
|
+
React.createElement(ModalHeader, { title: "Insert link", labelId: "insert-link-title" }),
|
|
443
|
+
React.createElement(ModalBody, null,
|
|
444
|
+
React.createElement(FormGroup, { label: "Display text", fieldId: "link-text-input" },
|
|
445
|
+
React.createElement(TextInput, { id: "link-text-input", value: linkText, onChange: onLinkTextChange, placeholder: "Link text (defaults to URL)" })),
|
|
446
|
+
React.createElement(FormGroup, { label: "URL", isRequired: true, fieldId: "link-url-input" },
|
|
447
|
+
React.createElement(TextInput, { id: "link-url-input", type: "url", value: linkUrl, onChange: onLinkUrlChange, placeholder: "https://", onKeyDown: onLinkUrlKeyDown }))),
|
|
448
|
+
React.createElement(ModalFooter, null,
|
|
449
|
+
React.createElement(Button, { variant: ButtonVariant.primary, onClick: onLinkInsert, isDisabled: !linkUrl.trim() }, "Insert"),
|
|
450
|
+
React.createElement(Button, { variant: ButtonVariant.link, onClick: onLinkCancel }, "Cancel"))),
|
|
451
|
+
React.createElement("input", { ref: fileInputRef, type: "file", accept: "image/jpeg,image/png,image/gif,image/svg+xml", style: { display: 'none' }, onChange: handleFileInputChange, tabIndex: -1 }),
|
|
452
|
+
!onImageUpload && (React.createElement(Modal, { variant: ModalVariant.small, isOpen: isImageModalOpen, onClose: onImageCancel, "aria-labelledby": "insert-image-title" },
|
|
453
|
+
React.createElement(ModalHeader, { title: "Insert image", labelId: "insert-image-title" }),
|
|
454
|
+
React.createElement(ModalBody, null,
|
|
455
|
+
React.createElement(FormGroup, { label: "Image URL", isRequired: true, fieldId: "image-url-input" },
|
|
456
|
+
React.createElement(TextInput, { id: "image-url-input", type: "url", value: imageUrl, validated: imageUrlError ? 'error' : 'default', onChange: onImageUrlChange, placeholder: "https://example.redhat.com/image.png", onKeyDown: onImageUrlKeyDown }),
|
|
457
|
+
imageUrlError && (React.createElement(FormGroup, null,
|
|
458
|
+
React.createElement("span", { className: "pf-v6-u-danger-color-100 pf-v6-u-font-size-sm" }, imageUrlError))))),
|
|
459
|
+
React.createElement(ModalFooter, null,
|
|
460
|
+
React.createElement(Button, { variant: ButtonVariant.primary, onClick: onImageInsert, isDisabled: !imageUrl.trim() || !!imageUrlError }, "Insert"),
|
|
461
|
+
React.createElement(Button, { variant: ButtonVariant.link, onClick: onImageCancel }, "Cancel"))))));
|
|
462
|
+
}
|
|
463
|
+
export default RichTextEditor;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/RichTextEditor/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC"}
|