@happeouikit/content-renderer 3.0.4 → 3.0.8

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.0.8
4
+
5
+ - Publish
6
+
7
+ ## 3.0.7
8
+
9
+ - Publish
10
+
11
+ ## 3.0.6
12
+
13
+ - [Added] Support for user defined font families and weights
14
+
15
+ ## 3.0.5
16
+
17
+ - Publish
18
+
3
19
  ## 3.0.4
4
20
 
5
21
  - [Fixed] ContentRenderer should now render content without applying links to domain-like text.
@@ -0,0 +1,241 @@
1
+ import _taggedTemplateLiteral from "@babel/runtime/helpers/taggedTemplateLiteral";
2
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
4
+
5
+ var _templateObject, _templateObject2, _templateObject3, _templateObject4, _templateObject5;
6
+
7
+ /* eslint-disable no-param-reassign */
8
+
9
+ /**
10
+ *
11
+ * ContentRenderer
12
+ *
13
+ */
14
+ import React, { useRef, useEffect, useState, useCallback, useMemo } from "react";
15
+ import ReactDOM from "react-dom";
16
+ import PropTypes from "prop-types";
17
+ import styled, { css } from "styled-components";
18
+ import loadjs from "loadjs";
19
+ import { CopyToClipboardButton } from "@happeouikit/copy-to-clipboard-button";
20
+ import { BodyUI, sansFamily } from "@happeouikit/typography";
21
+ import { margin200, media, padding200 } from "@happeouikit/layout";
22
+ import { active, gray04, gray07, gray09, lighten, warn } from "@happeouikit/colors";
23
+ import { toHtml, loadCodeFont, decodeHtmlEntities, removeSpecialCharacters, toSafeText } from "./utils";
24
+ import { ELEMENT_TYPE_PRE, ELEMENT_TYPE_CODE, HIGHLIGHJS_CSS, HIGHLIGHJS_JS, CODE_BLOCK_AVAILABLE_LANGUAGES } from "./constants";
25
+ import { jsx as _jsx } from "react/jsx-runtime";
26
+ import { Fragment as _Fragment } from "react/jsx-runtime";
27
+ import { jsxs as _jsxs } from "react/jsx-runtime";
28
+
29
+ var ContentRenderer = function ContentRenderer(_ref) {
30
+ var content = _ref.content,
31
+ type = _ref.type,
32
+ _ref$widgetType = _ref.widgetType,
33
+ widgetType = _ref$widgetType === void 0 ? "post" : _ref$widgetType,
34
+ headerFont = _ref.headerFont,
35
+ bodyFont = _ref.bodyFont,
36
+ createAnchors = _ref.createAnchors,
37
+ useHashAutoscroll = _ref.useHashAutoscroll,
38
+ _ref$hashAutoscrollTo = _ref.hashAutoscrollTopMargin,
39
+ hashAutoscrollTopMargin = _ref$hashAutoscrollTo === void 0 ? 0 : _ref$hashAutoscrollTo,
40
+ decodeHtmlEntitiesOnRender = _ref.decodeHtmlEntitiesOnRender,
41
+ forceDefaultLinkTarget = _ref.forceDefaultLinkTarget;
42
+ var element = useRef({});
43
+
44
+ var _useState = useState([]),
45
+ _useState2 = _slicedToArray(_useState, 2),
46
+ copyBtns = _useState2[0],
47
+ setCopyBtns = _useState2[1]; // Autoscroll to hashes
48
+
49
+
50
+ var scrollAndHighlightHash = useCallback(function () {
51
+ try {
52
+ var hash = window.location.hash;
53
+
54
+ if (hash) {
55
+ var hashEl = element.current.querySelector(hash);
56
+
57
+ if (hashEl) {
58
+ setTimeout(function () {
59
+ window.scrollTo({
60
+ top: hashEl.getBoundingClientRect().top - hashAutoscrollTopMargin,
61
+ behavior: "smooth"
62
+ });
63
+ hashEl.classList.add("highlight");
64
+ setTimeout(function () {
65
+ hashEl.classList.remove("highlight");
66
+ }, 5000);
67
+ }, 500);
68
+ }
69
+ }
70
+ } catch (error) {
71
+ /* In some cases the hash may be cause this function to fail. In this case, handle and do nothing */
72
+ }
73
+ }, [hashAutoscrollTopMargin]);
74
+ useEffect(function () {
75
+ if (!element.current) return;
76
+ var headerElems = createAnchors ? element.current.querySelectorAll("h1, h2, h3, h4, h5, h6") : [];
77
+
78
+ if (headerElems.length > 0) {
79
+ headerElems.forEach(function (header) {
80
+ var _header$textContent = header.textContent,
81
+ textContent = _header$textContent === void 0 ? "" : _header$textContent;
82
+
83
+ if (textContent.length > 0) {
84
+ header.id = header.id || removeSpecialCharacters(textContent.toLowerCase().replace(/\s/g, "-"));
85
+ }
86
+ });
87
+ }
88
+
89
+ var linkElems = forceDefaultLinkTarget ? element.current.querySelectorAll("a") : [];
90
+
91
+ if (linkElems.length > 0) {
92
+ var currentOrigin = window.origin;
93
+ linkElems.forEach(function (link) {
94
+ var href = link.href;
95
+
96
+ if (href.startsWith(currentOrigin)) {
97
+ delete link.target;
98
+ } else {
99
+ link.target = "_blank";
100
+ }
101
+ });
102
+ }
103
+
104
+ var blockElems = element.current.querySelectorAll(ELEMENT_TYPE_PRE);
105
+
106
+ if (blockElems.length > 0) {
107
+ if (!loadjs.isDefined("highlightjs")) {
108
+ loadCodeFont();
109
+ loadjs([].concat(_toConsumableArray(HIGHLIGHJS_JS), _toConsumableArray(HIGHLIGHJS_CSS)), "highlightjs");
110
+ }
111
+
112
+ loadjs.ready("highlightjs", function () {
113
+ // highlight.js register only languages we want to support?
114
+ window.hljs.configure({
115
+ languages: CODE_BLOCK_AVAILABLE_LANGUAGES,
116
+ useBr: true
117
+ });
118
+ blockElems.forEach(function (elem) {
119
+ var plainCode = elem.innerText; // Take the inner content and put inside single code block
120
+ // This will ensure nice readability.
121
+ // Needs to be done since editor will generate multiple lines for code for ease of editing
122
+
123
+ var codeBlock = document.createElement(ELEMENT_TYPE_CODE);
124
+ var decodedText = decodeHtmlEntities(plainCode);
125
+ codeBlock.textContent = decodedText; // Done like this to prevent xss
126
+
127
+ elem.innerHTML = ""; // Clear content so we can set just the single code block there
128
+
129
+ elem.appendChild(codeBlock);
130
+ window.hljs.highlightBlock(elem);
131
+ });
132
+ setCopyBtns(function (prevState) {
133
+ return [].concat(_toConsumableArray(prevState), _toConsumableArray(blockElems));
134
+ });
135
+ });
136
+ }
137
+
138
+ setCopyBtns(_toConsumableArray(headerElems));
139
+
140
+ if (useHashAutoscroll) {
141
+ scrollAndHighlightHash();
142
+ }
143
+ }, [element, createAnchors, forceDefaultLinkTarget, useHashAutoscroll, scrollAndHighlightHash]);
144
+ var safeText = useMemo(function () {
145
+ var lowercaseType = type && typeof type === "string" ? type.toLowerCase() : "html";
146
+ var transformedContent = lowercaseType === "text" ? toSafeText("<p>".concat(content, "</p>")) : toHtml(content, lowercaseType);
147
+
148
+ if (decodeHtmlEntitiesOnRender) {
149
+ transformedContent = decodeHtmlEntities(transformedContent);
150
+ }
151
+
152
+ return transformedContent;
153
+ }, [content, type, decodeHtmlEntitiesOnRender]); // Use dangerouslySetInnerHTML as there might be nested <p>'s header etc
154
+ // This requires that all content is run through the toSafeText filter
155
+ // before displaying. This will run it through xss filter.
156
+
157
+ return /*#__PURE__*/_jsxs(_Fragment, {
158
+ children: [/*#__PURE__*/_jsx(Wrapper, {
159
+ className: "content-renderer ".concat(widgetType === "page" ? "fr-view pages-text" : ""),
160
+ ref: element,
161
+ widgetType: widgetType,
162
+ headerFont: headerFont,
163
+ bodyFont: bodyFont,
164
+ createAnchors: createAnchors,
165
+ dangerouslySetInnerHTML: {
166
+ __html: safeText
167
+ }
168
+ }), copyBtns.map(function (el) {
169
+ var clipboardContent = el.textContent || "";
170
+
171
+ if (el.tagName !== ELEMENT_TYPE_PRE) {
172
+ clipboardContent = "".concat(window.location.href.split("#")[0], "#").concat(el.id);
173
+ } // eslint-disable-next-line no-control-regex
174
+
175
+
176
+ clipboardContent = removeSpecialCharacters(clipboardContent);
177
+ return /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/_jsx(BtnContainer, {
178
+ className: "content-copy-btn",
179
+ children: /*#__PURE__*/_jsx(CopyToClipboardButton, {
180
+ clipboardContent: clipboardContent
181
+ })
182
+ }), el);
183
+ })]
184
+ });
185
+ };
186
+
187
+ var BtnContainer = styled.div.withConfig({
188
+ displayName: "ContentRenderer__BtnContainer",
189
+ componentId: "sc-1mleu9k-0"
190
+ })(["display:inline-flex;margin-left:", ";button{opacity:0;transition-property:opacity;transition-duration:150ms;transition-timing-function:ease-in;}button:hover,button:active,button:focus{opacity:1 !important;}div p{font-size:12px;line-height:16px;}"], margin200);
191
+ var Wrapper = styled(BodyUI).attrs({
192
+ as: "div"
193
+ }).withConfig({
194
+ displayName: "ContentRenderer__Wrapper",
195
+ componentId: "sc-1mleu9k-1"
196
+ })(["", " h1.highlight,h2.highlight,h3.highlight,h4.highlight,h5.highlight,h6.highlight{border-radius:6px;position:relative;:after{content:\" \";position:absolute;top:-8px;bottom:-8px;left:-8px;right:-8px;border-radius:6px;border:5px solid ", ";}}pre{border-radius:4px;background:", ";padding:8px;margin-bottom:10px;overflow:auto;white-space:pre;position:relative;overflow:visible;*:not(p){font-weight:inherit;font-family:\"IBM Plex Mono\",monospace !important;}.content-copy-btn{position:absolute;top:0;right:0;padding:", " 0;}:hover button{opacity:1;}}.mention{border-radius:0.2em;background-color:#fff5cc;border:1px solid #fff5cc;color:#0f1621;font-weight:400;&:hover{text-decoration:none;border:1px solid #fabd24;}}.extraMention{cursor:inherit;}", " ", ""], function (_ref2) {
197
+ var createAnchors = _ref2.createAnchors;
198
+ return createAnchors ? "\n h1, h2, h3, h4, h5, h6 {\n display: flex;\n align-items: center;\n :hover button {\n opacity: 0.4;\n }\n }\n " : "";
199
+ }, lighten(warn, 0.8), gray09, padding200, function (_ref3) {
200
+ var widgetType = _ref3.widgetType;
201
+ return widgetType === "page" ? "" : "\n * {\n font-family: ".concat(sansFamily, ";\n }\n\n h1 {\n font-family: var(--channels-h1-font-family, ").concat(sansFamily, ");\n font-weight: var(--channels-h1-font-weight, 500);\n font-size: 24px;\n letter-spacing: -0.5px;\n line-height: 32px;\n + h1,\n + h2,\n + h3,\n + h4,\n + p {\n margin-top: 16px;\n }\n }\n h2 {\n font-family: var(--channels-h2-font-family, ").concat(sansFamily, ");\n font-weight: var(--channels-h2-font-weight, 500);\n font-size: 20px;\n line-height: 28px;\n + h1,\n + h2,\n + h3,\n + h4,\n + p {\n margin-top: 14px;\n }\n }\n h3 {\n font-family: var(--channels-h3-font-family, ").concat(sansFamily, ");\n font-weight: var(--channels-h3-font-weight, 500);\n font-size: 16px;\n line-height: 24px;\n + h1,\n + h2,\n + h3,\n + h4,\n + p {\n margin-top: 12px;\n }\n }\n h4 {\n font-size: 16px;\n line-height: 24px;\n + h1,\n + h2,\n + h3,\n + h4,\n + p {\n margin-top: 12px;\n }\n }\n p {\n font-size: 16px;\n line-height: 24px;\n letter-spacing: 0px;\n + h1,\n + h2,\n + h3,\n + h4,\n + p {\n margin-top: 12px;\n }\n }\n a {\n color: ").concat(active, ";\n text-decoration: none;\n :hover {\n text-decoration: underline;\n }\n }\n ul {\n list-style-type: disc;\n }\n ol {\n list-style-type: decimal;\n counter-reset: item;\n li {\n padding-left: 12px;\n text-indent: -33px;\n list-style-type: none;\n counter-increment: item;\n &:before {\n display: inline-block;\n width: 13px;\n padding-right: 15px;\n padding-left: 5px;\n font-weight: bold;\n text-align: right;\n content: counter(item) \".\";\n }\n }\n }\n ol,\n ul {\n font-size: 16px;\n line-height: 24px;\n margin: 18px 16px;\n display: block;\n -webkit-margin-before: 0.5em;\n -webkit-margin-after: 0.5em;\n -webkit-padding-start: 0.5em;\n li,\n li {\n margin-bottom: 5px;\n padding-left: 10px;\n > ul,\n > ol {\n -webkit-margin-before: 8px !important;\n -webkit-margin-after: 8px !important;\n }\n }\n\n &:first-child,\n &:first-child {\n -webkit-margin-before: 0;\n }\n &:last-child,\n &:last-child {\n -webkit-margin-after: 0;\n }\n }\n hr {\n -webkit-margin-before: 16px;\n -webkit-margin-after: 13px;\n border-color: @border-color;\n border-style: solid;\n height: 0px;\n border-width: 1px 0 0 0;\n }\n table {\n width: 100%;\n margin: 15px 0;\n text-align: left;\n thead th {\n background: @white;\n }\n th,\n td {\n border-width: 1px;\n border-style: solid;\n border-color: @border-color;\n padding: 0.5em;\n }\n tr:nth-child(even) {\n background: @almost-white;\n }\n tr:nth-child(odd) {\n background: @white;\n }\n }\n table.no-borders {\n th,\n td {\n border: none;\n }\n }\n table.padded-2 {\n th,\n td {\n padding: 2em;\n }\n }\n table.padded-1 {\n th,\n td {\n padding: 1em;\n }\n }\n img {\n max-width: 100%;\n margin: 15px 0;\n & + img {\n margin-top: 0;\n }\n }\n .xl-emoji * {\n font-size: 40px;\n line-height: 1.2;\n }\n \n blockquote {\n border-top: 2px solid ").concat(gray07, ";\n border-bottom: 2px solid ").concat(gray07, ";\n font-size: 18px;\n padding: 24px 48px;\n position: relative;\n text-align: center;\n margin: 48px 0;\n }\n ");
202
+ }, function (_ref4) {
203
+ var widgetType = _ref4.widgetType,
204
+ bodyFont = _ref4.bodyFont,
205
+ headerFont = _ref4.headerFont;
206
+ return widgetType === "article" && css(["h1{font-family:", ";& *{font-family:", ";}}h2{font-family:", ";& *{font-family:", ";}}h3{font-family:", ";& *{font-family:", ";}}h4,h5,h6,p,ul,ol,li,table{font-family:", ";& *{font-family:", ";}}h1{font-size:32px;line-height:1.13;margin:0 0 20px;letter-spacing:-0.75px;}h2{font-size:24px;line-height:1.17;margin:0 0 16px;letter-spacing:-0.5px;}h3{font-size:20px;line-height:1.2;margin:0 0 14px;}h4,h5,h6,p,ul,ol,li{font-size:18px;line-height:1.56;margin-bottom:13px;margin-top:0;}hr{border-color:", ";}table{width:100% !important;margin:15px 0;font-size:18px !important;text-align:left;border-collapse:collapse;border-spacing:0;thead th{background:@white;}th,td{border-width:1px;border-style:solid;border-color:", ";padding:0.5em;word-wrap:break-word;}tr:nth-child(odd){background:", ";}tr:nth-child(even){background:@white;}}table.no-borders{th,td{border:none;}tr:nth-child(odd){background:", ";}tr:nth-child(even){background:@white;}}pre{position:relative;background-color:", ";padding:1em;margin:1em 0;border-radius:4px;white-space:pre;}img,span.fr-img-caption,span.fr-video{display:block;margin:30px 0;", ";&.article-align-left{margin:30px auto;display:block;max-width:100%;", "}&.article-align-center{margin:30px auto;display:block;}&.article-align-right{margin:30px auto;display:block;max-width:100%;", "}&.article-align-overflow{margin:30px auto;display:block;max-width:100%;", " img{width:100%;}}}img{&.image-border-radius{border-radius:6px;}&.image-box-shadow{box-shadow:0 4px 16px 0 #bacad5;}}span.fr-img-caption{&.image-border-radius img{border-radius:6px;}&.image-box-shadow img{box-shadow:0 4px 16px 0 #bacad5;}}p{img:first-child,span.fr-img-caption:first-child{&.article-align-left,&.article-align-right{", "}}}span.fr-img-caption{text-align:center;img{margin:0;}.fr-inner{font-weight:400;font-size:16px;margin:8px auto 0 auto;padding:0 8px;color:", ";display:block;max-width:671px;}}span.fr-video{position:relative;padding-bottom:56.25%;padding-top:30px;height:0;overflow:hidden;border-radius:6px;&.article-align-overflow{padding-top:170px;}iframe{position:absolute;top:0;left:0;width:100% !important;height:100% !important;}}blockquote{border-left:0 !important;padding-left:0 !important;margin:0 !important;color:inherit !important;p{line-height:30px;font-size:26px;text-align:center;margin:0 auto;}}.content-attachments{width:100%;}"], headerFont ? "".concat(headerFont, " !important;") : "var(--channels-h1-font-family, ".concat(sansFamily, ");"), headerFont ? "".concat(headerFont, " !important;") : "var(--channels-h1-font-family, ".concat(sansFamily, ");"), headerFont ? "".concat(headerFont, " !important;") : "var(--channels-h2-font-family, ".concat(sansFamily, ");"), headerFont ? "".concat(headerFont, " !important;") : "var(--channels-h2-font-family, ".concat(sansFamily, ");"), headerFont ? "".concat(headerFont, " !important;") : "var(--channels-h3-font-family, ".concat(sansFamily, ");"), headerFont ? "".concat(headerFont, " !important;") : "var(--channels-h3-font-family, ".concat(sansFamily, ");"), bodyFont ? "".concat(bodyFont, " !important;") : "var(--channels-body-font-family, ".concat(sansFamily, ");"), bodyFont ? "".concat(bodyFont, " !important;") : "var(--channels-body-font-family, ".concat(sansFamily, ");"), gray07, gray07, gray09, gray09, gray09, media.only.xs(_templateObject || (_templateObject = _taggedTemplateLiteral([" \n max-width: 100%;\n "]))), media.min.md(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n margin: 30px 30px 10px -150px;\n float: left;\n max-width: 75%;\n "]))), media.min.md(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\n margin: 30px -150px 10px 30px;\n float: right;\n max-width: 75%;\n "]))), media.min.md(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["\n margin: 30px -150px;\n width: calc(100% + 300px) !important;\n max-width: none;\n "]))), media.min.md(_templateObject5 || (_templateObject5 = _taggedTemplateLiteral(["\n margin-top: 8px;\n "]))), gray04);
207
+ });
208
+ ContentRenderer.propTypes = {
209
+ content: PropTypes.string,
210
+ type: function type(props, propName) {
211
+ if (propName === "type" && props[propName] !== props[propName].toUpperCase()) {
212
+ // eslint-disable-next-line no-console
213
+ console.warn("Please use uppercase type 'HTML', 'MARKDOWN' or 'TEXT'");
214
+ }
215
+
216
+ if (propName === "type" && !["HTML", "MARKDOWN", "TEXT"].includes(props[propName].toUpperCase())) {
217
+ throw new Error("Invalid type, use 'HTML', 'MARKDOWN' or 'TEXT'");
218
+ }
219
+ },
220
+ widgetType: PropTypes.oneOf(["post", "article", "page"]),
221
+ bodyFont: PropTypes.string,
222
+ headerFont: PropTypes.string,
223
+ decodeHtmlEntitiesOnRender: PropTypes.bool,
224
+ createAnchors: PropTypes.bool,
225
+ forceDefaultLinkTarget: PropTypes.bool,
226
+ useHashAutoscroll: PropTypes.bool,
227
+ hashAutoscrollTopMargin: PropTypes.number
228
+ };
229
+ ContentRenderer.defaultProps = {
230
+ content: "",
231
+ type: "html",
232
+ widgetType: "post",
233
+ bodyFont: "",
234
+ headerFont: "",
235
+ decodeHtmlEntitiesOnRender: false,
236
+ createAnchors: false,
237
+ useHashAutoscroll: true,
238
+ forceDefaultLinkTarget: false,
239
+ hashAutoscrollTopMargin: 0
240
+ };
241
+ export default ContentRenderer;
@@ -0,0 +1,73 @@
1
+ export var safeTagsAndAttributes = {
2
+ a: ["class", "target", "href", "title", "data-collection-id", "data-page-id", "rel", "data-user-id", "data-user-id-old"],
3
+ abbr: ["title"],
4
+ address: [],
5
+ area: ["shape", "coords", "href", "alt"],
6
+ article: [],
7
+ aside: [],
8
+ audio: ["autoplay", "controls", "loop", "preload", "src"],
9
+ b: [],
10
+ bdi: ["dir"],
11
+ bdo: ["dir"],
12
+ big: [],
13
+ blockquote: ["cite"],
14
+ br: [],
15
+ caption: [],
16
+ center: [],
17
+ cite: [],
18
+ code: [],
19
+ col: ["align", "valign", "span", "width"],
20
+ colgroup: ["align", "valign", "span", "width"],
21
+ dd: [],
22
+ del: ["datetime"],
23
+ details: ["open"],
24
+ div: ["style"],
25
+ dl: [],
26
+ dt: [],
27
+ em: [],
28
+ font: ["color", "size", "face", "style"],
29
+ footer: [],
30
+ h1: ["style"],
31
+ h2: ["style"],
32
+ h3: ["style"],
33
+ h4: ["style"],
34
+ h5: ["style"],
35
+ h6: ["style"],
36
+ header: ["style"],
37
+ hr: ["style", "class"],
38
+ i: [],
39
+ img: ["src", "alt", "title", "width", "height", "style", "class"],
40
+ ins: ["datetime"],
41
+ li: ["style"],
42
+ mark: [],
43
+ nav: [],
44
+ ol: ["style"],
45
+ p: ["style"],
46
+ pre: ["style"],
47
+ s: [],
48
+ section: [],
49
+ small: [],
50
+ span: ["style", "class"],
51
+ sub: [],
52
+ sup: [],
53
+ strong: [],
54
+ table: ["width", "border", "align", "valign", "style", "class"],
55
+ tbody: ["align", "valign", "style", "class"],
56
+ td: ["width", "rowspan", "colspan", "align", "valign", "style", "class"],
57
+ tfoot: ["align", "valign", "style", "class"],
58
+ th: ["width", "rowspan", "colspan", "align", "valign", "style", "class"],
59
+ thead: ["align", "valign", "style", "class"],
60
+ tr: ["rowspan", "align", "valign", "style", "class"],
61
+ tt: [],
62
+ u: [],
63
+ ul: ["style", "class"],
64
+ video: ["style", "autoplay", "controls", "loop", "preload", "src", "height", "width"],
65
+ iframe: ["src", "frameborder", "allow", "sandbox", "referrerpolicy", "allowfullscreen", "style", "class"]
66
+ };
67
+ var HIGHLIGHT_JS_THEME = "a11y-light";
68
+ export var MONOSPACE_FONT_URL = "https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&display=swap";
69
+ export var HIGHLIGHJS_JS = ["https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.6.0/highlight.min.js"];
70
+ export var HIGHLIGHJS_CSS = ["https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.6.0/styles/default.min.css", "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.6.0/styles/".concat(HIGHLIGHT_JS_THEME, ".min.css")];
71
+ export var ELEMENT_TYPE_PRE = "PRE";
72
+ export var ELEMENT_TYPE_CODE = "CODE";
73
+ export var CODE_BLOCK_AVAILABLE_LANGUAGES = ["javascript", "html", "css", "xml", "json", "java", "php", "typescript"];
@@ -0,0 +1,61 @@
1
+ import showdown from "showdown";
2
+ import { extraMentioning, mentioningRegex } from "@universe/frontend-utils";
3
+ var mentioningRegexCaseInsensitive = new RegExp(mentioningRegex, "i");
4
+ var mentioningRegexGroupCaseInsensitive = new RegExp(mentioningRegex, "gi");
5
+ var converter;
6
+ var customRules = {
7
+ color: {
8
+ type: "lang",
9
+ regex: /\((.*?)\)\[(rgb\((?:([0-9]{1,2}|1[0-9]{1,2}|2[0-4][0-9]|25[0-5]), ?)(?:([0-9]{1,2}|1[0-9]{1,2}|2[0-4][0-9]|25[0-5]), ?)(?:([0-9]{1,2}|1[0-9]{1,2}|2[0-4][0-9]|25[0-5]))\))\]/gi,
10
+ replace: function replace(s, val) {
11
+ var color = s.match(/\[(rgb\((?:([0-9]{1,2}|1[0-9]{1,2}|2[0-4][0-9]|25[0-5]), ?)(?:([0-9]{1,2}|1[0-9]{1,2}|2[0-4][0-9]|25[0-5]), ?)(?:([0-9]{1,2}|1[0-9]{1,2}|2[0-4][0-9]|25[0-5]))\))\]/gi)[0].replace("[", "").replace("]", "");
12
+ return "<span style=\"color:".concat(color, "\">").concat(val, "</span>");
13
+ }
14
+ },
15
+ mention: {
16
+ type: "lang",
17
+ regex: mentioningRegexGroupCaseInsensitive,
18
+ replace: function replace(s) {
19
+ var r = s.match(mentioningRegexCaseInsensitive);
20
+ var id = r && r.length > 1 && r[1];
21
+ var name = r && r.length > 2 && r[2];
22
+
23
+ if (extraMentioning.find(function (mentioning) {
24
+ return mentioning.toLowerCase() === String(id).toLowerCase();
25
+ })) {
26
+ return "<a class=\"mention extraMention\" data-user-id=\"".concat(id, "\">@").concat(name, "</a>");
27
+ }
28
+
29
+ return "<a class=\"mention\" data-user-id=\"".concat(id, "\">@").concat(name, "</a>");
30
+ }
31
+ },
32
+ userMentioTag: {
33
+ type: "lang",
34
+ regex: /<user-mentio[^>]*>(.+?)<\/user-mentio>/gi,
35
+ replace: function replace(s) {
36
+ var r = s.match(/<user-mentio[^>]*>(.+?)<\/user-mentio>/);
37
+ var attr = s.match(/data-user-id=["']([^"]+)["']/);
38
+ var name = r && r.length > 1 && r[1];
39
+ var id = attr && attr.length > 1 && attr[1];
40
+ return "<a class=\"mention\" data-user-id=\"".concat(id, "\">").concat(name, "</a>");
41
+ }
42
+ }
43
+ };
44
+
45
+ function getConverter() {
46
+ if (converter) return converter;
47
+ showdown.extension("color", customRules.color);
48
+ showdown.extension("mention", customRules.mention);
49
+ showdown.extension("usermentiotag", customRules.userMentioTag);
50
+ converter = new showdown.Converter({
51
+ extensions: ["color", "mention", "usermentiotag"]
52
+ });
53
+ converter.setOption("strikethrough", true); // <del>
54
+
55
+ converter.setOption("tables", true); // <table>
56
+
57
+ converter.setOption("openLinksInNewWindow", true);
58
+ return converter;
59
+ }
60
+
61
+ export default getConverter();
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { default as ContentRenderer } from "./ContentRenderer";
package/dist/utils.js ADDED
@@ -0,0 +1,116 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+
3
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
4
+
5
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
6
+
7
+ import linkifyHtml from "linkifyjs/html";
8
+ import xss from "xss";
9
+ import { decode as decodeHtmlEntities } from "html-entities";
10
+ import { extraMentioning, mentioningRegex } from "@universe/frontend-utils";
11
+ import { fixEmojioneBug, shortToUnicode } from "@happeouikit/emojis";
12
+ import { MONOSPACE_FONT_URL, safeTagsAndAttributes } from "./constants";
13
+ import htmlConverter from "./htmlConverter";
14
+ var mentioningRegexGroupCaseInsensitive = new RegExp(mentioningRegex, "gi");
15
+ var mentioningHtmlRegexGroupCaseInsensitive = new RegExp(/<user-mentio[^>]*data-user-id=["']([^"]+)["'][^>]*>(?:@|(?:&#64;))(.+?)<\/user-mentio>/, "gi");
16
+
17
+ var processMatch = function processMatch(match, id, name) {
18
+ if (extraMentioning.find(function (mentioning) {
19
+ return mentioning.toLowerCase() === String(id).toLowerCase();
20
+ })) {
21
+ return "<a class=\"mention extraMention\" data-user-id=\"".concat(id, "\">@").concat(name, "</a>");
22
+ }
23
+
24
+ return "<a class=\"mention\" data-user-id=\"".concat(id, "\">@").concat(name, "</a>");
25
+ };
26
+
27
+ var processMentions = function processMentions() {
28
+ var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
29
+ return str.replace(mentioningRegexGroupCaseInsensitive, processMatch).replace(mentioningHtmlRegexGroupCaseInsensitive, processMatch);
30
+ };
31
+ /**
32
+ * @name toSafeText
33
+ * @description Returns filtered text with filterXSS library
34
+ * @author Antero Hanhirova
35
+ * @param {String} - Text to whitelist
36
+ * @returns {String}
37
+ */
38
+
39
+
40
+ var toSafeText = function toSafeText(text) {
41
+ return xss(text, {
42
+ whiteList: safeTagsAndAttributes,
43
+ css: {
44
+ whiteList: _objectSpread(_objectSpread({}, xss.getDefaultCSSWhiteList()), {}, {
45
+ "vertical-align": true
46
+ })
47
+ },
48
+ stripIgnoreTag: true
49
+ }).replace(/&nbsp;/g, " ").replace(/\u2060/g, ""); // Replaces a hidden character that resulted in a "?"-character in backend
50
+ };
51
+ /**
52
+ * @name toHtml
53
+ * @description Converts markdown to html, adds space to end of String
54
+ * The non-breaking space helps users to add text if last word is mention item
55
+ * @author Antero Hanhirova
56
+ * @param {String} - Markdown
57
+ * @returns {String} - Html
58
+ */
59
+
60
+
61
+ var toHtml = function toHtml() {
62
+ var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
63
+ var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "html";
64
+ var newString = shortToUnicode(str);
65
+ newString = fixEmojioneBug(newString);
66
+
67
+ if (type === "markdown") {
68
+ newString = htmlConverter.makeHtml(newString);
69
+ } else {
70
+ newString = processMentions(newString);
71
+ }
72
+
73
+ newString = toSafeText(newString);
74
+ return linkifyHtml(newString, {
75
+ target: "_blank",
76
+ ignoreTags: ["pre", "code", "script", "style"],
77
+ validate: {
78
+ url: function url(value) {
79
+ return /^(http|ftp)s?:\/\//.test(value);
80
+ }
81
+ }
82
+ });
83
+ };
84
+
85
+ var loadCodeFont = function loadCodeFont() {
86
+ var id = "ibmMonoOnDemand";
87
+
88
+ if (!document.getElementById(id)) {
89
+ var style = document.createElement("style");
90
+ style.id = id;
91
+ style.appendChild(document.createTextNode("@import url(".concat(MONOSPACE_FONT_URL, ");")));
92
+ document.head.appendChild(style);
93
+ }
94
+ };
95
+
96
+ var getNearestRelativeParent = function getNearestRelativeParent(el) {
97
+ if (!el) return null;
98
+ var elStyle = el.currentStyle || window.getComputedStyle(el, "");
99
+ var positionStyle = elStyle.position;
100
+
101
+ if (["relative", "absolute"].includes(positionStyle)) {
102
+ return el;
103
+ }
104
+
105
+ return getNearestRelativeParent(el.parentNode);
106
+ }; // Remove special and hidden characters from string
107
+
108
+
109
+ var removeSpecialCharacters = function removeSpecialCharacters() {
110
+ var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
111
+ return (// eslint-disable-next-line no-control-regex
112
+ str.replace(/[^\u0000-\u007E]/g, "")
113
+ );
114
+ };
115
+
116
+ export { toHtml, toSafeText, loadCodeFont, decodeHtmlEntities, getNearestRelativeParent, removeSpecialCharacters };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happeouikit/content-renderer",
3
- "version": "3.0.4",
3
+ "version": "3.0.8",
4
4
  "description": "Rendering html and markdown content",
5
5
  "main": "dist/index.js",
6
6
  "module": "src/index.js",
@@ -34,6 +34,5 @@
34
34
  },
35
35
  "publishConfig": {
36
36
  "access": "public"
37
- },
38
- "gitHead": "592299ab27c858a3540e4614ce4c09dec84a6fcc"
37
+ }
39
38
  }