@rh-support/components 2.5.203 → 2.5.205

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.
@@ -1,602 +0,0 @@
1
- var __rest = (this && this.__rest) || function (s, e) {
2
- var t = {};
3
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
- t[p] = s[p];
5
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
- t[p[i]] = s[p[i]];
9
- }
10
- return t;
11
- };
12
- import './markdownEditor.css';
13
- import { parseCommentMarkdown } from '@cee-eng/ui-toolkit';
14
- import { Button, ButtonVariant, Content, ContentVariants, Divider, DrilldownMenu, Dropdown, DropdownItem, DropdownList, Menu, MenuContainer, MenuContent, MenuItem, MenuList, MenuToggle, Spinner, Toolbar, ToolbarGroup, ToolbarItem, } from '@patternfly/react-core';
15
- import CaretDownIcon from '@patternfly/react-icons/dist/js/icons/caret-down-icon';
16
- import CodeIcon from '@patternfly/react-icons/dist/js/icons/code-icon';
17
- import EllipsisVIcon from '@patternfly/react-icons/dist/js/icons/ellipsis-v-icon';
18
- import ImageIcon from '@patternfly/react-icons/dist/js/icons/image-icon';
19
- import LinkIcon from '@patternfly/react-icons/dist/js/icons/link-icon';
20
- import ListUlIcon from '@patternfly/react-icons/dist/js/icons/list-ul-icon';
21
- import QuoteRightIcon from '@patternfly/react-icons/dist/js/icons/quote-right-icon';
22
- import { emojiPattern, haltEvent, isEmojiPattern, isImageFile } from '@rh-support/utils';
23
- import cx from 'classnames';
24
- import DOMPurify from 'dompurify';
25
- import isEmpty from 'lodash/isEmpty';
26
- import React, { useEffect, useRef, useState } from 'react';
27
- import { Trans, useTranslation } from 'react-i18next';
28
- import { useDebounce, usePrevious, useUndo } from '../hooks';
29
- import { ToastNotification } from '../index';
30
- export var EditorMode;
31
- (function (EditorMode) {
32
- EditorMode["PLAIN"] = "plaintext";
33
- EditorMode["MARKDOWN"] = "markdown";
34
- })(EditorMode || (EditorMode = {}));
35
- const defaultProps = {
36
- value: '',
37
- onChange: () => { },
38
- id: '',
39
- className: '',
40
- hidePreviewText: 'Hide preview',
41
- showPreviewText: 'Show preview',
42
- textAreaClassName: '',
43
- mdPlaceholder: 'Add a comment using markdown',
44
- plainTextPlaceholder: 'Add a comment to this case',
45
- allowEmoji: true,
46
- hideHeadingOptions: false,
47
- showMarkdownPlainTextToggle: false,
48
- editorMode: EditorMode.PLAIN,
49
- hideToolbars: false,
50
- charLimit: 32000,
51
- minCharsForCounter: 31800,
52
- fileSelectorProps: {
53
- showFileSelectorInToolbar: false,
54
- },
55
- };
56
- const MAX_FILE_SIZE = 2 * 1024; // 2MB in KB
57
- const ALLOWED_FILE_TYPES = ['image/jpeg', 'image/png', 'image/gif'];
58
- export const validateFile = (file) => {
59
- // Check file type
60
- if (!ALLOWED_FILE_TYPES.includes(file.fileType)) {
61
- // ToastNotification.addDangerMessage(t('Invalid file type, Only JPEG, PNG, GIF files are allowed.'));
62
- return {
63
- isValid: false,
64
- errorMsg: 'Invalid file type, Only JPEG, PNG, GIF files are allowed inside Markdown Editor',
65
- };
66
- }
67
- // Check file size
68
- if (file.sizeKB > MAX_FILE_SIZE) {
69
- // ToastNotification.addDangerMessage(t('File too large, File size must be less than 2MB'));
70
- return { isValid: false, errorMsg: 'File too large, File size must be less than 2MB inside Markdown Editor' };
71
- }
72
- return { isValid: true, errorMsg: '' };
73
- };
74
- function MarkdownEditor(props) {
75
- const { t } = useTranslation();
76
- const { hidePreviewText, showPreviewText, mdPlaceholder, plainTextPlaceholder, textAreaClassName, bindTextArea, markedownOptions, allowEmoji, hideHeadingOptions, rows, showMarkdownPlainTextToggle, editorMode, className, charLimit, minCharsForCounter, id, value, hideToolbars, onCommentExceedCharsLimit, fileSelectorProps: { onClipboardPaste, onFileSelect, isSupportedFile, showFileSelectorInToolbar = false, isUploadingFile = false, filesList = [], attachFileBtn, } } = props, textAreaProps = __rest(props, ["hidePreviewText", "showPreviewText", "mdPlaceholder", "plainTextPlaceholder", "textAreaClassName", "bindTextArea", "markedownOptions", "allowEmoji", "hideHeadingOptions", "rows", "showMarkdownPlainTextToggle", "editorMode", "className", "charLimit", "minCharsForCounter", "id", "value", "hideToolbars", "onCommentExceedCharsLimit", "fileSelectorProps"]);
77
- const [isPreview, setIsPreview] = useState(false);
78
- const [mdValue, setMdValue] = useState(props.value || '');
79
- const previousFileList = usePrevious(filesList);
80
- const [isPlainModeEnabled, setIsPlainModeEnabled] = useState(showMarkdownPlainTextToggle && props.editorMode === EditorMode.PLAIN ? true : false);
81
- const textareaRef = useRef(null);
82
- const { state, setURValue, undo, redo, resetURValue } = useUndo(props.value);
83
- // for access inside event handlers
84
- const isPlainModeEnabledRef = useRef(isPlainModeEnabled);
85
- const [isFileSelectorOpen, setIsFileSelectorOpen] = useState(false);
86
- const [isFileFromOnPaste, setIsFileFromOnPaste] = useState(false);
87
- const onFileSelectLocal = () => {
88
- setIsFileSelectorOpen((open) => !open);
89
- };
90
- const onFileSelectToggle = (isOpen) => {
91
- setIsFileSelectorOpen(isOpen);
92
- };
93
- useEffect(() => {
94
- if (isFileFromOnPaste) {
95
- onFileInsert(filesList[0]);
96
- }
97
- if ((previousFileList || []).length < (filesList || []).length && isUploadingFile) {
98
- onFileInsert(filesList[0]);
99
- }
100
- return () => {
101
- setIsFileFromOnPaste(false);
102
- };
103
- // eslint-disable-next-line react-hooks/exhaustive-deps
104
- }, [filesList.length]);
105
- const onFileInsert = (file) => {
106
- const { isValid, errorMsg } = validateFile(file);
107
- if (!isValid) {
108
- ToastNotification.addDangerMessage(t(errorMsg));
109
- return;
110
- }
111
- // Create markdown image syntax
112
- const template = isImageFile(file.fileType)
113
- ? `![${file.fileName}](${file.link})`
114
- : `[${file.fileName}](${file.link})`;
115
- insert(template, 0, 0);
116
- const currentStart = textareaRef.current.selectionStart;
117
- textareaRef.current.setSelectionRange(currentStart + template.length, currentStart + template.length);
118
- onFileSelect && onFileSelect(file);
119
- };
120
- function pastedFile(items) {
121
- for (const item of items) {
122
- if (isSupportedFile && isSupportedFile(item.type)) {
123
- return item.getAsFile();
124
- }
125
- }
126
- return null;
127
- }
128
- const onPaste = (event) => {
129
- if (!event.clipboardData || isPlainModeEnabledRef.current)
130
- return;
131
- if (!event.clipboardData.items)
132
- return;
133
- const file = pastedFile(event.clipboardData.items);
134
- if (!file)
135
- return;
136
- const files = [file];
137
- event.preventDefault();
138
- onClipboardPaste(files);
139
- setIsFileFromOnPaste(true);
140
- };
141
- // To handle dropped images
142
- const onDrop = (event) => {
143
- if (!event.dataTransfer.files)
144
- return;
145
- const file = event.dataTransfer.files[0];
146
- if (!file)
147
- return;
148
- const files = [file];
149
- event.preventDefault();
150
- onClipboardPaste(files);
151
- setIsFileFromOnPaste(true);
152
- };
153
- const getFileSelectorList = (isMobile = false) => {
154
- const filesAsDropdownItems = filesList
155
- .filter((file) => validateFile(file).isValid)
156
- .map((file) => (React.createElement(DropdownItem, { key: file.id, value: file, onClick: () => {
157
- onFileInsert(file);
158
- } },
159
- React.createElement(Content, { component: ContentVariants.small }, file.fileName))));
160
- if (isMobile) {
161
- return [
162
- React.createElement(DropdownItem, { key: "separated link", className: "pf-v6-u-p-0 pf-v6-u-m-0 pf-v6-u-w-100" }, attachFileBtn),
163
- ...(filesAsDropdownItems.length > 0 ? [React.createElement(Divider, { key: "separator" })] : []),
164
- ...filesAsDropdownItems,
165
- ];
166
- }
167
- else {
168
- return [
169
- ...filesAsDropdownItems,
170
- ...(filesAsDropdownItems.length > 0 ? [React.createElement(Divider, { key: "separator" })] : []),
171
- React.createElement(DropdownItem, { key: "separated link", className: "pf-v6-u-p-0 pf-v6-u-m-0 pf-v6-u-w-100" }, attachFileBtn),
172
- ];
173
- }
174
- };
175
- const bindTextAreaRef = (ref) => {
176
- textareaRef.current = ref;
177
- if (props.bindTextArea)
178
- props.bindTextArea(ref);
179
- };
180
- const onModeChange = (isPlainMode) => {
181
- setIsPlainModeEnabled(isPlainMode);
182
- isPlainModeEnabledRef.current = isPlainMode;
183
- isPlainMode && isPreview && setIsPreview(false);
184
- const mode = isPlainMode ? EditorMode.PLAIN : EditorMode.MARKDOWN;
185
- props.onChange(mdValue, mode);
186
- };
187
- // Add this useEffect to properly initialize the mdValue when component mounts
188
- useEffect(() => {
189
- setMdValue(props.value || '');
190
- }, [props.value]);
191
- // Update the existing useEffect to handle both value and editorMode changes
192
- useEffect(() => {
193
- if (value !== mdValue) {
194
- setMdValue(value);
195
- }
196
- // eslint-disable-next-line react-hooks/exhaustive-deps
197
- }, [value]);
198
- useEffect(() => {
199
- const isPlainMode = props.editorMode === EditorMode.PLAIN;
200
- if (isPlainMode !== isPlainModeEnabled) {
201
- setIsPlainModeEnabled(isPlainMode);
202
- isPlainModeEnabledRef.current = isPlainMode;
203
- if (isPlainMode && isPreview) {
204
- setIsPreview(false);
205
- }
206
- }
207
- // eslint-disable-next-line react-hooks/exhaustive-deps
208
- }, [props.editorMode]);
209
- useEffect(() => {
210
- // update state on undo redo
211
- if (mdValue !== state.present) {
212
- onValueChangeLocal(state.present);
213
- }
214
- // eslint-disable-next-line react-hooks/exhaustive-deps
215
- }, [state.present]);
216
- useDebounce(() => {
217
- if (isEmpty(mdValue))
218
- return;
219
- !isPlainModeEnabled && setURValue(mdValue);
220
- }, [mdValue], 500);
221
- const onValueChangeLocal = (text) => {
222
- const mode = showMarkdownPlainTextToggle && isPlainModeEnabled ? EditorMode.PLAIN : EditorMode.MARKDOWN;
223
- setMdValue(text);
224
- props.onChange(text, mode);
225
- };
226
- const textChanged = (event) => {
227
- let value = event.target.value;
228
- if (!props.allowEmoji && isEmojiPattern(value)) {
229
- value = value.replace(emojiPattern, '').trim();
230
- }
231
- onValueChangeLocal(value);
232
- // Add a null check before calling onCommentExceedCharsLimit
233
- if (props.onCommentExceedCharsLimit) {
234
- charLimit - value.length < 0
235
- ? props.onCommentExceedCharsLimit(true)
236
- : props.onCommentExceedCharsLimit(false);
237
- }
238
- };
239
- const hasSelection = () => {
240
- return textareaRef.current.selectionStart !== textareaRef.current.selectionEnd;
241
- };
242
- const wrapSelection = (templateStart, templateEnd) => {
243
- const currentText = textareaRef.current.value;
244
- const posStart = textareaRef.current.selectionStart;
245
- const posEnd = textareaRef.current.selectionEnd;
246
- const selectedText = currentText.substring(posStart, posEnd);
247
- const whitespaceEdges = selectedText.match(/^\s*|\s*$/g) || [];
248
- const leadingWhitespace = whitespaceEdges[0] || '';
249
- const trailingWhitespace = whitespaceEdges[1] || '';
250
- const trimmedText = selectedText.trim();
251
- let coreText = trimmedText;
252
- // Check existing formatting
253
- const existingFormats = {
254
- h1: coreText.startsWith('# '),
255
- h2: coreText.startsWith('## '),
256
- h3: coreText.startsWith('### '),
257
- bold: coreText.startsWith('**') && coreText.endsWith('**'),
258
- italic: coreText.startsWith('_') && coreText.endsWith('_'),
259
- quote: coreText.startsWith('> '),
260
- link: coreText.match(/^\[.*\]\(.*\)$/),
261
- };
262
- // Remove existing formatting to get core text
263
- if (existingFormats.h1)
264
- coreText = coreText.substring(2);
265
- else if (existingFormats.h2)
266
- coreText = coreText.substring(3);
267
- else if (existingFormats.h3)
268
- coreText = coreText.substring(4);
269
- if (existingFormats.bold)
270
- coreText = coreText.substring(2, coreText.length - 2);
271
- if (existingFormats.italic)
272
- coreText = coreText.substring(1, coreText.length - 1);
273
- if (existingFormats.quote)
274
- coreText = coreText.substring(2);
275
- if (existingFormats.link) {
276
- const match = coreText.match(/^\[(.*)\]\((.*)\)$/);
277
- if (match)
278
- coreText = match[1];
279
- }
280
- // Determine what formatting we're adding
281
- const newFormat = {
282
- h1: templateStart === '# ' && templateEnd === '',
283
- h2: templateStart === '## ' && templateEnd === '',
284
- h3: templateStart === '### ' && templateEnd === '',
285
- bold: templateStart === '**' && templateEnd === '**',
286
- italic: templateStart === '_' && templateEnd === '_',
287
- quote: templateStart === '> ' && (templateEnd === '' || templateEnd === '\n'),
288
- link: templateEnd.includes(']('),
289
- };
290
- // Apply formatting
291
- let formattedText = coreText;
292
- // First apply inline formatting (bold, italic)
293
- if (newFormat.italic && !existingFormats.italic) {
294
- formattedText = `_${formattedText}_`;
295
- }
296
- if (existingFormats.italic && !newFormat.italic) {
297
- formattedText = `_${formattedText}_`;
298
- }
299
- if (newFormat.bold && !existingFormats.bold) {
300
- formattedText = `**${formattedText}**`;
301
- }
302
- if (existingFormats.bold && !newFormat.bold) {
303
- formattedText = `**${formattedText}**`;
304
- }
305
- // Then apply block-level formatting (headings, quotes)
306
- if (newFormat.h1 || existingFormats.h1) {
307
- formattedText = `# ${formattedText}`;
308
- }
309
- else if (newFormat.h2 || existingFormats.h2) {
310
- formattedText = `## ${formattedText}`;
311
- }
312
- else if (newFormat.h3 || existingFormats.h3) {
313
- formattedText = `### ${formattedText}`;
314
- }
315
- // Handle quotes - keep them at the start of the line
316
- if (newFormat.quote || existingFormats.quote) {
317
- formattedText = `> ${formattedText}`;
318
- }
319
- // Special handling for links
320
- if (newFormat.link) {
321
- formattedText = `[${formattedText}](URL "TITLE")`;
322
- }
323
- if (existingFormats.link && !newFormat.link) {
324
- const match = trimmedText.match(/^\[(.*)\]\((.*)\)$/);
325
- if (match)
326
- formattedText = `[${formattedText}](${match[2]})`;
327
- }
328
- const newText = currentText.substring(0, posStart) +
329
- leadingWhitespace +
330
- formattedText +
331
- trailingWhitespace +
332
- currentText.substring(posEnd);
333
- // Set the value in the textarea
334
- textareaRef.current.value = newText;
335
- textareaRef.current.selectionStart = posStart + leadingWhitespace.length;
336
- textareaRef.current.selectionEnd = posStart + leadingWhitespace.length + formattedText.length;
337
- textareaRef.current.focus();
338
- // Call onValueChangeLocal only once
339
- onValueChangeLocal(newText);
340
- };
341
- const wrapBasedOnNewLines = () => {
342
- const currentText = textareaRef.current.value;
343
- const posStart = textareaRef.current.selectionStart;
344
- const posEnd = textareaRef.current.selectionEnd;
345
- const selectedText = currentText.substring(posStart, posEnd);
346
- const splitText = selectedText.split('\n');
347
- const newText = currentText
348
- .substring(0, posStart)
349
- .concat(splitText.map((v) => `- ${v}`).join('\n'), '\n', currentText.substring(posEnd));
350
- onValueChangeLocal(newText);
351
- };
352
- const insert = (template, selectionStart, selectionEnd) => {
353
- const currentText = textareaRef.current.value;
354
- const pos = textareaRef.current.selectionStart;
355
- const newText = currentText.substring(0, pos) + template + currentText.substring(pos);
356
- onValueChangeLocal(newText);
357
- textareaRef.current.value = newText;
358
- textareaRef.current.selectionStart = pos + selectionStart;
359
- textareaRef.current.selectionEnd = pos + (selectionEnd || selectionStart);
360
- textareaRef.current.focus();
361
- onValueChangeLocal(newText);
362
- };
363
- const bold = () => {
364
- if (hasSelection()) {
365
- wrapSelection('**', '**');
366
- }
367
- else {
368
- insert('**TEXT**', 2, 6);
369
- }
370
- };
371
- const italic = () => {
372
- if (hasSelection()) {
373
- wrapSelection('_', '_');
374
- }
375
- else {
376
- insert('_TEXT_', 1, 5);
377
- }
378
- };
379
- const h1 = () => {
380
- if (hasSelection()) {
381
- wrapSelection('# ', '');
382
- }
383
- else {
384
- insert('# HEADING', 2, 9);
385
- }
386
- };
387
- const h2 = () => {
388
- if (hasSelection()) {
389
- wrapSelection('## ', '');
390
- }
391
- else {
392
- insert('## HEADING', 3, 10);
393
- }
394
- };
395
- const h3 = () => {
396
- if (hasSelection()) {
397
- wrapSelection('### ', '');
398
- }
399
- else {
400
- insert('### HEADING', 4, 11);
401
- }
402
- };
403
- const list = () => {
404
- if (hasSelection()) {
405
- wrapBasedOnNewLines();
406
- }
407
- else {
408
- insert('- ITEM', 2, 6);
409
- }
410
- };
411
- const link = () => {
412
- if (hasSelection()) {
413
- wrapSelection('[', '](URL "TITLE")');
414
- }
415
- else {
416
- insert('[LINK TEXT](URL "TITLE")', 1, 10);
417
- }
418
- };
419
- const quote = () => {
420
- if (hasSelection()) {
421
- wrapSelection('> ', '');
422
- }
423
- else {
424
- insert('> QUOTE', 2, 7);
425
- }
426
- };
427
- const code = () => {
428
- if (hasSelection()) {
429
- wrapSelection('~~~\n', '\n~~~\n');
430
- }
431
- else {
432
- insert('~~~\nCODE\n~~~\n', 4, 8);
433
- }
434
- };
435
- const preview = () => {
436
- setIsPreview((previewFlag) => !previewFlag);
437
- };
438
- const previewMDText = (mdText) => {
439
- // Preprocess to insert a blank line between list and non-list lines
440
- const fixedMdText = mdText.replace(/(^|\n)(-\s.+)(\n(?![-\s]))/g, '$1$2\n\n');
441
- const markeddownOptions = Object.assign(Object.assign({}, props.markedownOptions), { openLinksInNewTab: true, gfm: true, breaks: true });
442
- return parseCommentMarkdown(fixedMdText, { openImagesInNewTab: false, lazyLoadImages: false }, markeddownOptions);
443
- };
444
- const keydown = (event) => {
445
- if (event.ctrlKey) {
446
- switch (event.key) {
447
- case 'b':
448
- bold();
449
- haltEvent(event);
450
- break;
451
- case 'i':
452
- italic();
453
- haltEvent(event);
454
- break;
455
- case '1':
456
- h1();
457
- haltEvent(event);
458
- break;
459
- case '2':
460
- h2();
461
- haltEvent(event);
462
- break;
463
- case '3':
464
- h3();
465
- haltEvent(event);
466
- break;
467
- case 'z':
468
- undo();
469
- haltEvent(event);
470
- break;
471
- case 'y':
472
- redo();
473
- haltEvent(event);
474
- break;
475
- }
476
- }
477
- };
478
- const getCharCountText = (comment) => {
479
- const charCountLeft = charLimit - comment.length;
480
- return (React.createElement("span", { className: `character-count-wrapper ${charCountLeft <= 0 ? 'red' : ''}` },
481
- "(",
482
- t(`{{charCountLeft}} characters left`, { charCountLeft }),
483
- ")"));
484
- };
485
- const contentRows = (props.value && props.value.split('\n').length + 1) || 4;
486
- // reset undo redo value after post comment
487
- props.disabled && !isEmpty(state.present) && resetURValue('');
488
- // editor is disabled and preview is true -> set preview to false
489
- props.disabled && isPreview && setIsPreview(false);
490
- const disablePreviewButton = mdValue === '';
491
- const [isHeadingDropdownOpen, setIsHeadingDropdownOpen] = React.useState(false);
492
- const onToggle = (isOpen) => {
493
- setIsHeadingDropdownOpen(isOpen);
494
- };
495
- const onHeadingSelect = () => {
496
- setIsHeadingDropdownOpen(false);
497
- };
498
- const [isDrillMenuOpen, setIsDrillMenuOpen] = React.useState(false);
499
- const [activeMenu, setActiveMenu] = useState('toolsKebabMenu');
500
- const [menuDrilledIn, setMenuDrilledIn] = useState([]);
501
- const [drilldownPath, setDrilldownPath] = useState([]);
502
- const [menuHeights, setMenuHeights] = useState({});
503
- const toggleRef = useRef(null);
504
- const drillMenuRef = useRef(null);
505
- const onToggleClick = (e) => {
506
- e.stopPropagation();
507
- setIsDrillMenuOpen(!isDrillMenuOpen);
508
- setMenuDrilledIn([]);
509
- setDrilldownPath([]);
510
- setActiveMenu('toolsKebabMenu');
511
- };
512
- const drillIn = (_event, fromMenuId, toMenuId, pathId) => {
513
- setMenuDrilledIn([...menuDrilledIn, fromMenuId]);
514
- setDrilldownPath([...drilldownPath, pathId]);
515
- setActiveMenu(toMenuId);
516
- };
517
- const drillOut = (_event, toMenuId) => {
518
- setMenuDrilledIn(menuDrilledIn.slice(0, menuDrilledIn.length - 1));
519
- setDrilldownPath(drilldownPath.slice(0, drilldownPath.length - 1));
520
- setActiveMenu(toMenuId);
521
- };
522
- const setHeight = (menuId, height) => {
523
- if (!menuHeights[menuId] || (menuId !== 'toolsKebabMenu' && menuHeights[menuId] !== height)) {
524
- setMenuHeights(Object.assign(Object.assign({}, menuHeights), { [menuId]: height }));
525
- }
526
- };
527
- const markdownGuidanceBtn = (React.createElement("a", { className: "pf-v6-c-button pf-m-link pf-m-inline markdown-guidance", rel: "noopener noreferrer", target: "_blank", href: "/articles/4729621", "data-tracking-id": "md-guidance" },
528
- React.createElement(Trans, null, "Markdown guidance")));
529
- return (React.createElement("div", { id: "markdown-editor" },
530
- !isPlainModeEnabled && React.createElement("div", { className: "markdown-guidance-container" }, markdownGuidanceBtn),
531
- React.createElement("div", { className: `markdown-editor-container ${props.className}`, id: props.id, "data-tracking-id": "md-editor-container", onDrop: onDrop, onPaste: onPaste },
532
- React.createElement("div", { className: "toolbar-container border-bottom", style: hideToolbars ? { display: 'none' } : undefined }, !isPlainModeEnabled && (React.createElement(React.Fragment, null,
533
- React.createElement(Toolbar, { className: "desktop-markdown-toolbar pf-v6-l-flex" },
534
- React.createElement(ToolbarGroup, { "data-tracking-id": "md-editor-toolbar", className: "markdown-toolbar-left-section pf-m-wrap" },
535
- React.createElement(Button, { icon: "B", variant: ButtonVariant.plain, tabIndex: -1, isDisabled: props.disabled || isPreview, onClick: bold, "data-tracking-id": "md-editor-toolbar-b", title: "Bold" }),
536
- React.createElement(Button, { icon: "I", variant: ButtonVariant.plain, tabIndex: -1, isDisabled: props.disabled || isPreview, onClick: italic, "data-tracking-id": "md-editor-toolbar-i", title: "Italic" }),
537
- React.createElement("div", { className: "btn-group-separator" }),
538
- !props.hideHeadingOptions && (React.createElement(React.Fragment, null,
539
- React.createElement(Button, { icon: "H1", tabIndex: -1, variant: ButtonVariant.plain, isDisabled: props.disabled || isPreview, onClick: h1, title: "Heading 1", "data-tracking-id": "md-editor-toolbar-h1" }),
540
- React.createElement(Button, { icon: "H2", tabIndex: -1, variant: ButtonVariant.plain, isDisabled: props.disabled || isPreview, onClick: h2, title: "Heading 2", "data-tracking-id": "md-editor-toolbar-h2" }),
541
- React.createElement(Button, { icon: "H3", tabIndex: -1, variant: ButtonVariant.plain, isDisabled: props.disabled || isPreview, onClick: h3, title: "Heading 3", "data-tracking-id": "md-editor-toolbar-h3" }),
542
- React.createElement("div", { className: "btn-group-separator" }))),
543
- React.createElement(Button, { icon: React.createElement(ListUlIcon, null), tabIndex: -1, isDisabled: props.disabled || isPreview, variant: ButtonVariant.plain, onClick: list, title: "Bullet List", "data-tracking-id": "md-editor-toolbar-bul-list" }),
544
- React.createElement("div", { className: "btn-group-separator" }),
545
- React.createElement(Button, { icon: React.createElement(LinkIcon, null), tabIndex: -1, variant: ButtonVariant.plain, isDisabled: props.disabled || isPreview, onClick: link, title: "Link", "data-tracking-id": "md-editor-toolbar-link" }),
546
- React.createElement(Button, { icon: React.createElement(QuoteRightIcon, null), tabIndex: -1, variant: ButtonVariant.plain, isDisabled: props.disabled || isPreview, onClick: quote, title: "Quote", "data-tracking-id": "md-editor-toolbar-quote" }),
547
- React.createElement(Button, { icon: React.createElement(CodeIcon, null), tabIndex: -1, variant: ButtonVariant.plain, isDisabled: props.disabled || isPreview, onClick: code, title: "Code", "data-tracking-id": "md-editor-toolbar-code" }),
548
- showFileSelectorInToolbar && (React.createElement(Dropdown, { isOpen: isFileSelectorOpen, onSelect: onFileSelectLocal, onOpenChange: (isOpen) => onFileSelectToggle(isOpen), toggle: (toggleRef) => (React.createElement(MenuToggle, { ref: toggleRef, onClick: () => onFileSelectToggle(!isFileSelectorOpen), isExpanded: isFileSelectorOpen, isDisabled: props.disabled || isPreview, "aria-label": "file selector", variant: "plain" },
549
- React.createElement("span", { className: "toggle-content" },
550
- React.createElement(ImageIcon, null),
551
- React.createElement(CaretDownIcon, { className: "dropdown-caret" })))), id: "md-editor-toolbar-file-selector", "data-tracking-id": "md-editor-toolbar-file-selector", shouldFocusFirstItemOnOpen: false },
552
- React.createElement(DropdownList, null, getFileSelectorList())))),
553
- React.createElement(ToolbarItem, null, markdownGuidanceBtn)),
554
- React.createElement(Toolbar, { className: "mobile-markdown-toolbar" },
555
- React.createElement("div", { className: "mobile-markdown-toolbar" },
556
- React.createElement(ToolbarGroup, { "data-tracking-id": "mobile-md-editor-toolbar", className: "markdown-toolbar-left-section" },
557
- !props.hideHeadingOptions && (React.createElement(Dropdown, { isOpen: isHeadingDropdownOpen, onSelect: onHeadingSelect, onOpenChange: (isOpen) => onToggle(isOpen), toggle: (toggleRef) => (React.createElement(MenuToggle, { ref: toggleRef, onClick: () => onToggle(!isHeadingDropdownOpen), isExpanded: isHeadingDropdownOpen, isDisabled: props.disabled || isPreview, "aria-label": "heading selector", variant: "plain", id: "heading-selector" },
558
- React.createElement("span", { className: "toggle-content" },
559
- "Heading",
560
- React.createElement(CaretDownIcon, { className: "dropdown-caret" })))), id: "md-editor-toolbar-heading-selector", "data-tracking-id": "md-editor-toolbar-heading-selector", shouldFocusFirstItemOnOpen: false },
561
- React.createElement(DropdownList, null,
562
- React.createElement(DropdownItem, { key: "heading-1", isDisabled: props.disabled || isPreview, onClick: h1, "data-tracking-id": "mobile-md-editor-toolbar-h1" }, "Heading 1"),
563
- React.createElement(DropdownItem, { key: "heading-2", isDisabled: props.disabled || isPreview, onClick: h2, "data-tracking-id": "mobile-md-editor-toolbar-h2" }, "Heading 2"),
564
- React.createElement(DropdownItem, { key: "heading-3", isDisabled: props.disabled || isPreview, onClick: h3, "data-tracking-id": "mobile-md-editor-toolbar-h3" }, "Heading 3")))),
565
- React.createElement(Button, { icon: "B", variant: ButtonVariant.plain, tabIndex: -1, isDisabled: props.disabled || isPreview, onClick: bold, "data-tracking-id": "mobile-md-editor-toolbar-b", title: "Bold", className: "mobile-markdown-tool-item" }),
566
- React.createElement(Button, { icon: "I", variant: ButtonVariant.plain, tabIndex: -1, isDisabled: props.disabled || isPreview, onClick: italic, "data-tracking-id": "mobile-md-editor-toolbar-i", title: "Italic", className: "mobile-markdown-tool-item" }),
567
- React.createElement(Button, { icon: React.createElement(ListUlIcon, null), tabIndex: -1, isDisabled: props.disabled || isPreview, variant: ButtonVariant.plain, onClick: list, title: "Bullet List", "data-tracking-id": "mobile-md-editor-toolbar-bul-list", className: "mobile-markdown-tool-item" }),
568
- React.createElement(Button, { icon: React.createElement(CodeIcon, null), tabIndex: -1, variant: ButtonVariant.plain, isDisabled: props.disabled || isPreview, onClick: code, title: "Code", "data-tracking-id": "mobile-md-editor-toolbar-code", className: "mobile-markdown-tool-item" })),
569
- React.createElement(ToolbarItem, { id: "tools-kebab-menu-container", className: "mobile-markdown-tool-item", gap: { default: 'gapSm' } },
570
- React.createElement(MenuContainer, { isOpen: isDrillMenuOpen, onOpenChange: (isOpen) => setIsDrillMenuOpen(isOpen), popperProps: { position: 'left' }, menu: React.createElement(Menu, { id: "toolsKebabMenu", containsDrilldown: true, drilldownItemPath: drilldownPath, drilledInMenus: menuDrilledIn, activeMenu: activeMenu, onDrillIn: drillIn, onDrillOut: drillOut, onGetMenuHeight: setHeight, ref: drillMenuRef },
571
- React.createElement(MenuContent, { menuHeight: `${menuHeights[activeMenu]}px` },
572
- React.createElement(MenuList, null,
573
- React.createElement(MenuItem, { itemId: "link-btn-menu-item", isDisabled: props.disabled || isPreview, onClick: link, "data-tracking-id": "mobile-md-editor-toolbar-link", icon: React.createElement(LinkIcon, { "aria-hidden": true }) }, "Link"),
574
- React.createElement(MenuItem, { itemId: "code-btn-menu-item", isDisabled: props.disabled || isPreview, onClick: quote, "data-tracking-id": "mobile-md-editor-toolbar-quote", icon: React.createElement(QuoteRightIcon, { "aria-hidden": true }) }, "Quote"),
575
- React.createElement(MenuItem, { itemId: "group:manage_attachments", direction: "down", icon: React.createElement(ImageIcon, { "aria-hidden": true }), drilldownMenu: React.createElement(DrilldownMenu, { id: "drilldownMenuStart" },
576
- React.createElement(MenuItem, { itemId: "group:manage_attachments_breadcrumb", direction: "up" }, "Manage attachments"),
577
- React.createElement("div", { className: "attachments-items" }, getFileSelectorList(true))) }, "Manage Attachments")))), menuRef: drillMenuRef, toggle: React.createElement(MenuToggle, { variant: "plain", ref: toggleRef, onClick: onToggleClick, isExpanded: isDrillMenuOpen },
578
- React.createElement(EllipsisVIcon, null)), toggleRef: toggleRef }))))))),
579
- React.createElement("div", { className: "markdown-content" }, !isPreview ? (React.createElement("textarea", Object.assign({}, textAreaProps, { onKeyDown: keydown, placeholder: isPlainModeEnabled ? t(props.plainTextPlaceholder) : t(props.mdPlaceholder), ref: bindTextAreaRef, className: cx('form-control', 'markdown-text-area', props.textAreaClassName, 'textArea'), value: mdValue, onChange: textChanged, rows: props.rows || contentRows, style: { height: '313px' } }))) : (React.createElement("div", { className: "markdown-preview-placeholder", style: { height: '313px' } },
580
- React.createElement("div", { className: "markdown-preview", style: { maxHeight: '313px' } },
581
- React.createElement("div", { dangerouslySetInnerHTML: {
582
- __html: DOMPurify.sanitize(previewMDText(mdValue)),
583
- } }))))),
584
- React.createElement("div", { style: hideToolbars ? { display: 'none' } : undefined }, props.showMarkdownPlainTextToggle && (React.createElement("div", { className: `markdown-toggle-toolbar pf-v6-l-flex` },
585
- React.createElement("span", { className: "markdown-toolbar-left-section pf-m-wrap" },
586
- !isPlainModeEnabled && (React.createElement(Button, { className: "markdown-preview-btn", tabIndex: -1, variant: ButtonVariant.plain, onClick: preview, title: "Show preview", "data-tracking-id": `md-preview-${isPreview ? 'hide' : 'show'}`, isDisabled: disablePreviewButton, icon: React.createElement(Trans, null, isPreview ? (React.createElement("span", null,
587
- " ",
588
- t(hidePreviewText))) : (React.createElement("span", null,
589
- " ",
590
- t(showPreviewText)))) })),
591
- isUploadingFile && (React.createElement(React.Fragment, null,
592
- React.createElement(Trans, null,
593
- React.createElement("small", { className: "pf-v6-u-pl-md uploading-spinner-text" },
594
- React.createElement(Trans, null, "Uploading File"))),
595
- React.createElement(Spinner, { className: "pf-v6-u-ml-sm", size: "sm" }))),
596
- mdValue.length > props.minCharsForCounter && getCharCountText(mdValue)),
597
- React.createElement("span", null,
598
- React.createElement(Button, { icon: React.createElement(Trans, null, "Plain text"), variant: "plain", className: `${isPlainModeEnabled ? 'markdown-active-mode' : ''} plain-mode-btn`, onClick: () => onModeChange(true), isDisabled: isPlainModeEnabled || isUploadingFile, "data-tracking-id": "md-editor-plaintext" }),
599
- React.createElement(Button, { icon: React.createElement(Trans, null, "Markdown"), variant: "plain", className: `${!isPlainModeEnabled ? 'markdown-active-mode' : ''} markdown-mode-btn`, onClick: () => onModeChange(false), isDisabled: !isPlainModeEnabled, "data-tracking-id": "md-editor-md" }))))))));
600
- }
601
- MarkdownEditor.defaultProps = defaultProps;
602
- export { MarkdownEditor };
@@ -1,2 +0,0 @@
1
- export * from './MarkdownEditor';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/MarkdownEditor/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
@@ -1 +0,0 @@
1
- export * from './MarkdownEditor';