@rh-support/components 2.5.0 → 2.5.1
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/MarkdownEditor/MarkdownEditor.d.ts +4 -0
- package/lib/esm/MarkdownEditor/MarkdownEditor.d.ts.map +1 -1
- package/lib/esm/MarkdownEditor/MarkdownEditor.js +104 -6
- package/lib/esm/TagsSelector/TagsSelector.d.ts.map +1 -1
- package/lib/esm/TagsSelector/TagsSelector.js +3 -2
- package/lib/esm/TagsSelector/tagSelector.css +4 -0
- package/package.json +2 -2
|
@@ -37,6 +37,10 @@ export interface IMDEditorProps extends Omit<HTMLProps<HTMLTextAreaElement>, 'on
|
|
|
37
37
|
onCommentExceedCharsLimit: (val: boolean) => void;
|
|
38
38
|
fileSelectorProps?: Partial<IFileSelectorProps>;
|
|
39
39
|
}
|
|
40
|
+
export declare const validateFile: (file: Partial<IAttachment>) => {
|
|
41
|
+
isValid: boolean;
|
|
42
|
+
errorMsg: string;
|
|
43
|
+
};
|
|
40
44
|
declare function MarkdownEditor(props: IMDEditorProps): React.JSX.Element;
|
|
41
45
|
declare namespace MarkdownEditor {
|
|
42
46
|
var defaultProps: Partial<IMDEditorProps>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarkdownEditor.d.ts","sourceRoot":"","sources":["../../../src/MarkdownEditor/MarkdownEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B,OAAO,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAwB,MAAM,qBAAqB,CAAC;AAgC7E,OAAO,KAAK,EAAE,EAAE,SAAS,EAA+B,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"MarkdownEditor.d.ts","sourceRoot":"","sources":["../../../src/MarkdownEditor/MarkdownEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B,OAAO,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAwB,MAAM,qBAAqB,CAAC;AAgC7E,OAAO,KAAK,EAAE,EAAE,SAAS,EAA+B,MAAM,OAAO,CAAC;AAMtE,oBAAY,UAAU;IAClB,KAAK,cAAc;IACnB,QAAQ,aAAa;CACxB;AAED,UAAU,kBAAkB;IACxB,yBAAyB,EAAE,OAAO,CAAC;IACnC,SAAS,EAAE,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;IAClC,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IACnD,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IACnD,gBAAgB,EAAE,CAAC,IAAI,KAAA,KAAK,IAAI,CAAC;IACjC,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAChD,aAAa,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,eAAe,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,KAAK,CAAC;IACtG,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAClD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yBAAyB,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,iBAAiB,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACnD;AA0BD,eAAO,MAAM,YAAY,SAAU,OAAO,CAAC,WAAW,CAAC,KAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAc7F,CAAC;AAEF,iBAAS,cAAc,CAAC,KAAK,EAAE,cAAc,qBAi8B5C;kBAj8BQ,cAAc;;;AAq8BvB,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -27,6 +27,7 @@ import isEmpty from 'lodash/isEmpty';
|
|
|
27
27
|
import React, { useEffect, useRef, useState } from 'react';
|
|
28
28
|
import { Trans, useTranslation } from 'react-i18next';
|
|
29
29
|
import { useDebounce, usePrevious, useUndo } from '../hooks';
|
|
30
|
+
import { ToastNotification } from '../index';
|
|
30
31
|
export var EditorMode;
|
|
31
32
|
(function (EditorMode) {
|
|
32
33
|
EditorMode["PLAIN"] = "plaintext";
|
|
@@ -52,6 +53,21 @@ const defaultProps = {
|
|
|
52
53
|
showFileSelectorInToolbar: false,
|
|
53
54
|
},
|
|
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 { isValid: false, errorMsg: 'Invalid file type, Only JPEG, PNG, GIF files are allowed.' };
|
|
63
|
+
}
|
|
64
|
+
// Check file size
|
|
65
|
+
if (file.sizeKB > MAX_FILE_SIZE) {
|
|
66
|
+
// ToastNotification.addDangerMessage(t('File too large, File size must be less than 2MB'));
|
|
67
|
+
return { isValid: false, errorMsg: 'File too large, File size must be less than 2MB' };
|
|
68
|
+
}
|
|
69
|
+
return { isValid: true, errorMsg: '' };
|
|
70
|
+
};
|
|
55
71
|
function MarkdownEditor(props) {
|
|
56
72
|
const { t } = useTranslation();
|
|
57
73
|
const { hidePreviewText, showPreviewText, mdPlaceholder, plainTextPlaceholder, textAreaClassName, bindTextArea, markedownOptions, allowEmoji, hideHeadingOptions, rows, showMarkdownPlainTextToggle, editorMode, className, charLimit, minCharsForCounter, id, value, onCommentExceedCharsLimit, fileSelectorProps: { onClipboardPaste, onFileDelete, 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", "onCommentExceedCharsLimit", "fileSelectorProps"]);
|
|
@@ -77,6 +93,12 @@ function MarkdownEditor(props) {
|
|
|
77
93
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
78
94
|
}, [filesList]);
|
|
79
95
|
const onFileInsert = (file) => {
|
|
96
|
+
const { isValid, errorMsg } = validateFile(file);
|
|
97
|
+
if (!isValid) {
|
|
98
|
+
ToastNotification.addDangerMessage(t(errorMsg));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Create markdown image syntax
|
|
80
102
|
const template = isImageFile(file.fileType)
|
|
81
103
|
? ``
|
|
82
104
|
: `[${file.fileName}](${file.link})`;
|
|
@@ -220,17 +242,93 @@ function MarkdownEditor(props) {
|
|
|
220
242
|
const whitespaceEdges = selectedText.match(/^\s*|\s*$/g) || [];
|
|
221
243
|
const leadingWhitespace = whitespaceEdges[0] || '';
|
|
222
244
|
const trailingWhitespace = whitespaceEdges[1] || '';
|
|
245
|
+
const trimmedText = selectedText.trim();
|
|
246
|
+
let coreText = trimmedText;
|
|
247
|
+
// Check existing formatting
|
|
248
|
+
const existingFormats = {
|
|
249
|
+
h1: coreText.startsWith('# '),
|
|
250
|
+
h2: coreText.startsWith('## '),
|
|
251
|
+
h3: coreText.startsWith('### '),
|
|
252
|
+
bold: coreText.startsWith('**') && coreText.endsWith('**'),
|
|
253
|
+
italic: coreText.startsWith('_') && coreText.endsWith('_'),
|
|
254
|
+
quote: coreText.startsWith('> '),
|
|
255
|
+
link: coreText.match(/^\[.*\]\(.*\)$/),
|
|
256
|
+
};
|
|
257
|
+
// Remove existing formatting to get core text
|
|
258
|
+
if (existingFormats.h1)
|
|
259
|
+
coreText = coreText.substring(2);
|
|
260
|
+
else if (existingFormats.h2)
|
|
261
|
+
coreText = coreText.substring(3);
|
|
262
|
+
else if (existingFormats.h3)
|
|
263
|
+
coreText = coreText.substring(4);
|
|
264
|
+
if (existingFormats.bold)
|
|
265
|
+
coreText = coreText.substring(2, coreText.length - 2);
|
|
266
|
+
if (existingFormats.italic)
|
|
267
|
+
coreText = coreText.substring(1, coreText.length - 1);
|
|
268
|
+
if (existingFormats.quote)
|
|
269
|
+
coreText = coreText.substring(2);
|
|
270
|
+
if (existingFormats.link) {
|
|
271
|
+
const match = coreText.match(/^\[(.*)\]\((.*)\)$/);
|
|
272
|
+
if (match)
|
|
273
|
+
coreText = match[1];
|
|
274
|
+
}
|
|
275
|
+
// Determine what formatting we're adding
|
|
276
|
+
const newFormat = {
|
|
277
|
+
h1: templateStart === '# ' && templateEnd === '',
|
|
278
|
+
h2: templateStart === '## ' && templateEnd === '',
|
|
279
|
+
h3: templateStart === '### ' && templateEnd === '',
|
|
280
|
+
bold: templateStart === '**' && templateEnd === '**',
|
|
281
|
+
italic: templateStart === '_' && templateEnd === '_',
|
|
282
|
+
quote: templateStart === '> ' && (templateEnd === '' || templateEnd === '\n'),
|
|
283
|
+
link: templateEnd.includes(']('),
|
|
284
|
+
};
|
|
285
|
+
// Apply formatting
|
|
286
|
+
let formattedText = coreText;
|
|
287
|
+
// First apply inline formatting (bold, italic)
|
|
288
|
+
if (newFormat.italic && !existingFormats.italic) {
|
|
289
|
+
formattedText = `_${formattedText}_`;
|
|
290
|
+
}
|
|
291
|
+
if (existingFormats.italic && !newFormat.italic) {
|
|
292
|
+
formattedText = `_${formattedText}_`;
|
|
293
|
+
}
|
|
294
|
+
if (newFormat.bold && !existingFormats.bold) {
|
|
295
|
+
formattedText = `**${formattedText}**`;
|
|
296
|
+
}
|
|
297
|
+
if (existingFormats.bold && !newFormat.bold) {
|
|
298
|
+
formattedText = `**${formattedText}**`;
|
|
299
|
+
}
|
|
300
|
+
// Then apply block-level formatting (headings, quotes)
|
|
301
|
+
if (newFormat.h1 || existingFormats.h1) {
|
|
302
|
+
formattedText = `# ${formattedText}`;
|
|
303
|
+
}
|
|
304
|
+
else if (newFormat.h2 || existingFormats.h2) {
|
|
305
|
+
formattedText = `## ${formattedText}`;
|
|
306
|
+
}
|
|
307
|
+
else if (newFormat.h3 || existingFormats.h3) {
|
|
308
|
+
formattedText = `### ${formattedText}`;
|
|
309
|
+
}
|
|
310
|
+
// Handle quotes - keep them at the start of the line
|
|
311
|
+
if (newFormat.quote || existingFormats.quote) {
|
|
312
|
+
formattedText = `> ${formattedText}`;
|
|
313
|
+
}
|
|
314
|
+
// Special handling for links
|
|
315
|
+
if (newFormat.link) {
|
|
316
|
+
formattedText = `[${formattedText}](URL "TITLE")`;
|
|
317
|
+
}
|
|
318
|
+
if (existingFormats.link && !newFormat.link) {
|
|
319
|
+
const match = trimmedText.match(/^\[(.*)\]\((.*)\)$/);
|
|
320
|
+
if (match)
|
|
321
|
+
formattedText = `[${formattedText}](${match[2]})`;
|
|
322
|
+
}
|
|
223
323
|
const newText = currentText.substring(0, posStart) +
|
|
224
324
|
leadingWhitespace +
|
|
225
|
-
|
|
226
|
-
selectedText.trim() +
|
|
227
|
-
templateEnd +
|
|
325
|
+
formattedText +
|
|
228
326
|
trailingWhitespace +
|
|
229
327
|
currentText.substring(posEnd);
|
|
230
328
|
// Set the value in the textarea
|
|
231
329
|
textareaRef.current.value = newText;
|
|
232
|
-
textareaRef.current.selectionStart =
|
|
233
|
-
textareaRef.current.selectionEnd =
|
|
330
|
+
textareaRef.current.selectionStart = posStart + leadingWhitespace.length;
|
|
331
|
+
textareaRef.current.selectionEnd = posStart + leadingWhitespace.length + formattedText.length;
|
|
234
332
|
textareaRef.current.focus();
|
|
235
333
|
// Call onValueChangeLocal only once
|
|
236
334
|
onValueChangeLocal(newText);
|
|
@@ -315,7 +413,7 @@ function MarkdownEditor(props) {
|
|
|
315
413
|
};
|
|
316
414
|
const quote = () => {
|
|
317
415
|
if (hasSelection()) {
|
|
318
|
-
wrapSelection('> ', '
|
|
416
|
+
wrapSelection('> ', '');
|
|
319
417
|
}
|
|
320
418
|
else {
|
|
321
419
|
insert('> QUOTE', 2, 7);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TagsSelector.d.ts","sourceRoot":"","sources":["../../../src/TagsSelector/TagsSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,CAAC;AAmB3B,OAAO,KAAuC,MAAM,OAAO,CAAC;AAK5D,UAAU,MAAM;IACZ,UAAU,EAAE,GAAG,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;IACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,YAAY,CAAC,EACzB,UAAU,EACV,QAAQ,EACR,WAAW,EACX,EAAE,EACF,SAAS,EACT,YAAiB,EACjB,QAAgB,GACnB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"TagsSelector.d.ts","sourceRoot":"","sources":["../../../src/TagsSelector/TagsSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,CAAC;AAmB3B,OAAO,KAAuC,MAAM,OAAO,CAAC;AAK5D,UAAU,MAAM;IACZ,UAAU,EAAE,GAAG,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;IACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,YAAY,CAAC,EACzB,UAAU,EACV,QAAQ,EACR,WAAW,EACX,EAAE,EACF,SAAS,EACT,YAAiB,EACjB,QAAgB,GACnB,EAAE,MAAM,qBA2JR"}
|
|
@@ -22,7 +22,8 @@ export function TagsSelector({ tagOptions, onChange, placeholder, id, typeahead,
|
|
|
22
22
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
23
23
|
}, [query, tagOptions]);
|
|
24
24
|
const { t } = useTranslation();
|
|
25
|
-
const onToggleClick = () => {
|
|
25
|
+
const onToggleClick = (e) => {
|
|
26
|
+
e.stopPropagation();
|
|
26
27
|
setIsOpen(!isOpen);
|
|
27
28
|
};
|
|
28
29
|
const onSelect = (selection) => {
|
|
@@ -58,7 +59,7 @@ export function TagsSelector({ tagOptions, onChange, placeholder, id, typeahead,
|
|
|
58
59
|
e.stopPropagation();
|
|
59
60
|
onSelect(label);
|
|
60
61
|
} }, label.tagName)))));
|
|
61
|
-
const toggle = (toggleRef) => (React.createElement(MenuToggle, Object.assign({ ref: toggleRef, onClick: onToggleClick, className:
|
|
62
|
+
const toggle = (toggleRef) => (React.createElement(MenuToggle, Object.assign({ ref: toggleRef, onClick: onToggleClick, className: `pf-v5-u-flex-grow-1 tags-selector-menu-toggle ${isEmpty(tagOptions) || disabled ? 'menu-is-disabld' : ''}`, isExpanded: isOpen, onKeyDown: onInputKeyDown, isDisabled: isEmpty(tagOptions) || disabled }, (typeahead && { variant: 'typeahead' }), { id: id, isFullWidth: true }), typeahead ? (React.createElement(TextInputGroup, { isPlain: true },
|
|
62
63
|
React.createElement(TextInputGroupMain, { id: "tags-selector-search-input", value: query, onClick: onToggleClick, onChange: (e, v) => setQuery(v), onKeyDown: onInputKeyDown, innerRef: textInputRef, isExpanded: isOpen, placeholder: placeholder || t('Search for a tag') }, selectedChipGroup),
|
|
63
64
|
React.createElement(TextInputGroupUtilities, null, selected.length > 0 && (React.createElement(Button, { variant: "plain", onClick: handleClear, "aria-label": "clear-all-tags" },
|
|
64
65
|
React.createElement(TimesIcon, { "aria-hidden": true })))))) : (React.createElement(Flex, { justifyContent: { default: 'justifyContentSpaceBetween' }, alignItems: { default: 'alignItemsCenter' } },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rh-support/components",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "Contains all reusabel components for support app",
|
|
5
5
|
"author": "Vikas Rathee <vrathee@redhat.com>",
|
|
6
6
|
"license": "ISC",
|
|
@@ -111,5 +111,5 @@
|
|
|
111
111
|
"defaults and supports es6-module",
|
|
112
112
|
"maintained node versions"
|
|
113
113
|
],
|
|
114
|
-
"gitHead": "
|
|
114
|
+
"gitHead": "78f51b2cfd60107a49503d13542ba46d29df4d04"
|
|
115
115
|
}
|