@iit/precision-ui 0.8.34 → 0.8.36
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,7 +1,8 @@
|
|
|
1
|
-
declare const SafeHtmlRenderer: ({ html, className, withoutContentClass, }: {
|
|
1
|
+
declare const SafeHtmlRenderer: ({ html, className, withoutContentClass, truncateLength, }: {
|
|
2
2
|
html: string;
|
|
3
3
|
className?: string;
|
|
4
4
|
withoutContentClass?: boolean;
|
|
5
|
+
truncateLength?: number;
|
|
5
6
|
}) => import("react/jsx-dev-runtime").JSX.Element;
|
|
6
7
|
export default SafeHtmlRenderer;
|
|
7
8
|
//# sourceMappingURL=SafeHtml.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SafeHtml.d.ts","sourceRoot":"","sources":["../../src/components/SafeHtml.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SafeHtml.d.ts","sourceRoot":"","sources":["../../src/components/SafeHtml.tsx"],"names":[],"mappings":"AA6KA,QAAA,MAAM,gBAAgB,8DAKnB;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,gDAuBA,CAAA;AAED,eAAe,gBAAgB,CAAA"}
|
package/dist/index.es19.js
CHANGED
|
@@ -1,77 +1,87 @@
|
|
|
1
|
-
import
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
let
|
|
5
|
-
for (; (
|
|
6
|
-
const s =
|
|
1
|
+
import d from "react";
|
|
2
|
+
const m = (t) => {
|
|
3
|
+
const e = /* @__PURE__ */ new Set(["br", "img", "input", "hr", "meta", "link"]), c = /<\/?([a-z]+)(\s[^>]*)?>/gi, a = [];
|
|
4
|
+
let r;
|
|
5
|
+
for (; (r = c.exec(t)) !== null; ) {
|
|
6
|
+
const s = r[0], n = r[1].toLowerCase();
|
|
7
7
|
if (s === "</br>")
|
|
8
8
|
return !1;
|
|
9
9
|
if (s.startsWith("</")) {
|
|
10
|
-
if (
|
|
10
|
+
if (e.has(n) || a.length === 0 || a.pop() !== n)
|
|
11
11
|
return !1;
|
|
12
12
|
} else
|
|
13
|
-
!
|
|
13
|
+
!e.has(n) && !s.endsWith("/>") && a.push(n);
|
|
14
14
|
}
|
|
15
|
-
return
|
|
16
|
-
},
|
|
17
|
-
const
|
|
18
|
-
for (const [s,
|
|
19
|
-
const
|
|
20
|
-
for (const
|
|
21
|
-
if (
|
|
22
|
-
const [
|
|
15
|
+
return a.length === 0;
|
|
16
|
+
}, h = (t) => {
|
|
17
|
+
const e = /<[a-z]+\s+([^>]+)>/gi, c = t.matchAll(e);
|
|
18
|
+
for (const [s, n] of c) {
|
|
19
|
+
const l = n.match(/\w+\s*=\s*(['"])(.*?)\1|\w+(?!=)/g) || [];
|
|
20
|
+
for (const o of l)
|
|
21
|
+
if (o.includes("=")) {
|
|
22
|
+
const [f, ...u] = o.split("="), i = u.join("=");
|
|
23
23
|
if (!(i.startsWith('"') && i.endsWith('"') || i.startsWith("'") && i.endsWith("'")))
|
|
24
24
|
return !1;
|
|
25
|
-
const
|
|
26
|
-
if (i.slice(1, -1).includes(
|
|
25
|
+
const g = i[0];
|
|
26
|
+
if (i.slice(1, -1).includes(g))
|
|
27
27
|
return !1;
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
const
|
|
31
|
-
for (const s of
|
|
32
|
-
let
|
|
33
|
-
for (let
|
|
34
|
-
(s[
|
|
35
|
-
if (
|
|
30
|
+
const a = /<[^>]*?['"][^'"]*>/g, r = t.match(a) || [];
|
|
31
|
+
for (const s of r) {
|
|
32
|
+
let n = !1, l = "";
|
|
33
|
+
for (let o = 0; o < s.length; o++)
|
|
34
|
+
(s[o] === "'" || s[o] === '"') && (n ? s[o] === l && (n = !1) : (n = !0, l = s[o]));
|
|
35
|
+
if (n)
|
|
36
36
|
return !1;
|
|
37
37
|
}
|
|
38
38
|
return !0;
|
|
39
|
-
},
|
|
40
|
-
const
|
|
41
|
-
for (const [c,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
}, T = (t) => {
|
|
40
|
+
const e = [...t.matchAll(/<([a-z]+)(\s[^>]*)\/>/gi)];
|
|
41
|
+
for (const [c, a, r] of e)
|
|
42
|
+
if (r) {
|
|
43
|
+
const s = /(\w+)=["']([^"']*)["']/g, n = [...r.matchAll(s)];
|
|
44
|
+
for (const [f, u, i] of n)
|
|
45
|
+
if (!u || !i)
|
|
46
|
+
return !1;
|
|
47
|
+
const l = /(\w+)=["'][^"']*$/g;
|
|
48
|
+
if ([...r.matchAll(l)].length > 0)
|
|
45
49
|
return !1;
|
|
46
|
-
|
|
47
|
-
if ([...l.matchAll(r)].length > 0)
|
|
48
|
-
return !1;
|
|
49
|
-
}
|
|
50
|
+
}
|
|
50
51
|
return !0;
|
|
51
52
|
}, p = (t) => {
|
|
52
|
-
const
|
|
53
|
-
for (const [
|
|
54
|
-
const s =
|
|
55
|
-
for (const
|
|
56
|
-
if (
|
|
57
|
-
const [
|
|
58
|
-
if (!
|
|
53
|
+
const e = /style=["']([^"']*)["']/gi, c = [...t.matchAll(e)];
|
|
54
|
+
for (const [a, r] of c) {
|
|
55
|
+
const s = r.split(";");
|
|
56
|
+
for (const n of s)
|
|
57
|
+
if (n.trim()) {
|
|
58
|
+
const [l, o] = n.split(":").map((f) => f.trim());
|
|
59
|
+
if (!l || !o)
|
|
59
60
|
return !1;
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
return !0;
|
|
63
|
-
},
|
|
64
|
+
}, v = (t) => m(t) && h(t) && T(t) && p(t), C = (t) => {
|
|
65
|
+
const e = document.createElement("div");
|
|
66
|
+
return e.innerHTML = t, e.textContent || e.innerText || "";
|
|
67
|
+
}, y = (t, e) => t.length <= e ? t : t.slice(0, e).trim() + "...", A = ({
|
|
64
68
|
html: t,
|
|
65
|
-
className:
|
|
66
|
-
withoutContentClass: c
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
className: e,
|
|
70
|
+
withoutContentClass: c,
|
|
71
|
+
truncateLength: a
|
|
72
|
+
}) => {
|
|
73
|
+
if (!v(t))
|
|
74
|
+
return console.error("Invalid HTML content:", t), /* @__PURE__ */ d.createElement("div", { style: { color: "red" } }, "Некорректное HTML содержимое");
|
|
75
|
+
const s = a ? y(C(t), a) : t;
|
|
76
|
+
return /* @__PURE__ */ d.createElement(
|
|
77
|
+
"div",
|
|
78
|
+
{
|
|
79
|
+
className: [!c && "content", e].filter(Boolean).join(" "),
|
|
80
|
+
dangerouslySetInnerHTML: { __html: s }
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
}, _ = A;
|
|
74
84
|
export {
|
|
75
|
-
|
|
85
|
+
_ as default
|
|
76
86
|
};
|
|
77
87
|
//# sourceMappingURL=index.es19.js.map
|
package/dist/index.es19.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.es19.js","sources":["../src/components/SafeHtml.tsx"],"sourcesContent":["import React from 'react'\r\n\r\n// Check if tags are correctly opened and closed\r\nconst validateTags = (htmlString: string) => {\r\n // List of known self-closing tags\r\n const selfClosingTags = new Set(['br', 'img', 'input', 'hr', 'meta', 'link'])\r\n\r\n const tagPattern = /<\\/?([a-z]+)(\\s[^>]*)?>/gi\r\n const tagStack: string[] = []\r\n let match\r\n\r\n while ((match = tagPattern.exec(htmlString)) !== null) {\r\n const fullTag = match[0]\r\n const tagName = match[1].toLowerCase()\r\n\r\n // Check for incorrect </br> usage\r\n if (fullTag === '</br>') {\r\n return false // </br> is invalid HTML\r\n }\r\n\r\n if (fullTag.startsWith('</')) {\r\n // Closing tag\r\n if (selfClosingTags.has(tagName)) {\r\n return false // Self-closing tags shouldn't have closing tags\r\n }\r\n if (tagStack.length === 0) return false\r\n const lastTag = tagStack.pop()\r\n if (lastTag !== tagName) return false\r\n } else {\r\n // Opening tag\r\n if (!selfClosingTags.has(tagName) && !fullTag.endsWith('/>')) {\r\n tagStack.push(tagName)\r\n }\r\n }\r\n }\r\n\r\n return tagStack.length === 0\r\n}\r\n\r\nconst validateAttributes = (htmlString: string) => {\r\n const attributeRegex = /<[a-z]+\\s+([^>]+)>/gi\r\n const matches = htmlString.matchAll(attributeRegex)\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n for (const [fullMatch, attributes] of matches) {\r\n // Split attributes by space, but keep quoted values together\r\n const attrs = attributes.match(/\\w+\\s*=\\s*(['\"])(.*?)\\1|\\w+(?!=)/g) || []\r\n\r\n for (const attr of attrs) {\r\n // Check if attribute has quotes\r\n if (attr.includes('=')) {\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n const [name, ...rest] = attr.split('=')\r\n const value = rest.join('=') // Rejoin in case value contains =\r\n\r\n // Check for proper quote matching\r\n if (\r\n !(\r\n (value.startsWith('\"') && value.endsWith('\"')) ||\r\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\r\n )\r\n ) {\r\n return false\r\n }\r\n\r\n // Extract the quote character used\r\n const quoteChar = value[0]\r\n // Check if there are any unescaped quotes of the same type inside the value\r\n const valueContent = value.slice(1, -1)\r\n if (valueContent.includes(quoteChar)) {\r\n return false\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Additional check for unclosed quotes before >\r\n const unclosedQuoteCheck = /<[^>]*?['\"][^'\"]*>/g\r\n const potentiallyUnclosedTags = htmlString.match(unclosedQuoteCheck) || []\r\n for (const tag of potentiallyUnclosedTags) {\r\n let inQuote = false\r\n let quoteChar = ''\r\n for (let i = 0; i < tag.length; i++) {\r\n if (tag[i] === \"'\" || tag[i] === '\"') {\r\n if (!inQuote) {\r\n inQuote = true\r\n quoteChar = tag[i]\r\n } else if (tag[i] === quoteChar) {\r\n inQuote = false\r\n }\r\n }\r\n }\r\n if (inQuote) {\r\n return false\r\n }\r\n }\r\n\r\n return true\r\n}\r\n\r\n// Check if self-closing tags are valid\r\nconst validateSelfClosingTags = (htmlString: string) => {\r\n const selfClosingTags = [...htmlString.matchAll(/<([a-z]+)(\\s[^>]*)\\/>/gi)]\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n for (const [_, tag, attributes] of selfClosingTags) {\r\n const attrRegex = /(\\w+)=[\"']([^\"']*)[\"']/g\r\n const attrs = [...attributes.matchAll(attrRegex)]\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n for (const [_, attrName, attrValue] of attrs) {\r\n if (!attrName || !attrValue) {\r\n return false // Invalid attribute format\r\n }\r\n }\r\n\r\n // Check for unclosed attributes\r\n const unclosedAttrRegex = /(\\w+)=[\"'][^\"']*$/g\r\n const unclosedAttrs = [...attributes.matchAll(unclosedAttrRegex)]\r\n\r\n if (unclosedAttrs.length > 0) {\r\n return false // Unclosed attribute in self-closing tag\r\n }\r\n }\r\n\r\n return true // Self-closing tags are valid\r\n}\r\n\r\n// Check for inline styles and ensure they follow a valid format\r\nconst validateInlineStyles = (htmlString: string) => {\r\n const styleRegex = /style=[\"']([^\"']*)[\"']/gi\r\n const styles = [...htmlString.matchAll(styleRegex)]\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n for (const [_, styleContent] of styles) {\r\n // A basic check for properly formatted style declarations (e.g., \"property: value;\")\r\n const styleRules = styleContent.split(';')\r\n for (const rule of styleRules) {\r\n if (rule.trim()) {\r\n const [property, value] = rule.split(':')\r\n if (!property || !value) {\r\n return false // Invalid style declaration\r\n }\r\n }\r\n }\r\n }\r\n\r\n return true // Inline styles are valid\r\n}\r\n\r\n// Main function that calls the smaller subfunctions\r\nconst validateHtmlBasic = (htmlString: string) => {\r\n return (\r\n validateTags(htmlString) &&\r\n validateAttributes(htmlString) &&\r\n validateSelfClosingTags(htmlString) &&\r\n validateInlineStyles(htmlString)\r\n )\r\n}\r\n\r\n////\r\n\r\nconst SafeHtmlRenderer = ({\r\n html,\r\n className,\r\n withoutContentClass,\r\n}: {\r\n html: string\r\n className?: string\r\n withoutContentClass?: boolean\r\n}) => {\r\n const isValid = validateHtmlBasic(html)\r\n // console.log(\"isValidHTML\", isValid);\r\n // TODO: add send error to TG\r\n\r\n if (!isValid) {\r\n console.error('Invalid HTML content:', html)\r\n return <div style={{ color: 'red' }}>Invalid HTML content</div>\r\n }\r\n\r\n return (\r\n <div\r\n className={[!withoutContentClass && 'content', className].filter(Boolean).join(' ')}\r\n dangerouslySetInnerHTML={{ __html: html }}\r\n />\r\n )\r\n}\r\n\r\nexport default SafeHtmlRenderer\r\n"],"names":["validateTags","htmlString","selfClosingTags","tagPattern","tagStack","match","fullTag","tagName","validateAttributes","attributeRegex","matches","fullMatch","attributes","attrs","attr","name","rest","value","quoteChar","unclosedQuoteCheck","potentiallyUnclosedTags","tag","inQuote","i","validateSelfClosingTags","_","attrRegex","attrName","attrValue","unclosedAttrRegex","validateInlineStyles","styleRegex","styles","styleContent","styleRules","rule","property","validateHtmlBasic","SafeHtmlRenderer","html","className","withoutContentClass","React","SafeHtmlRenderer$1"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.es19.js","sources":["../src/components/SafeHtml.tsx"],"sourcesContent":["import React from 'react'\r\n\r\n// Check if tags are correctly opened and closed\r\nconst validateTags = (htmlString: string) => {\r\n // List of known self-closing tags\r\n const selfClosingTags = new Set(['br', 'img', 'input', 'hr', 'meta', 'link'])\r\n\r\n const tagPattern = /<\\/?([a-z]+)(\\s[^>]*)?>/gi\r\n const tagStack: string[] = []\r\n let match\r\n\r\n while ((match = tagPattern.exec(htmlString)) !== null) {\r\n const fullTag = match[0]\r\n const tagName = match[1].toLowerCase()\r\n\r\n // Check for incorrect </br> usage\r\n if (fullTag === '</br>') {\r\n return false // </br> is invalid HTML\r\n }\r\n\r\n if (fullTag.startsWith('</')) {\r\n // Closing tag\r\n if (selfClosingTags.has(tagName)) {\r\n return false // Self-closing tags shouldn't have closing tags\r\n }\r\n if (tagStack.length === 0) return false\r\n const lastTag = tagStack.pop()\r\n if (lastTag !== tagName) return false\r\n } else {\r\n // Opening tag\r\n if (!selfClosingTags.has(tagName) && !fullTag.endsWith('/>')) {\r\n tagStack.push(tagName)\r\n }\r\n }\r\n }\r\n\r\n return tagStack.length === 0\r\n}\r\n\r\nconst validateAttributes = (htmlString: string) => {\r\n const attributeRegex = /<[a-z]+\\s+([^>]+)>/gi\r\n const matches = htmlString.matchAll(attributeRegex)\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n for (const [fullMatch, attributes] of matches) {\r\n // Split attributes by space, but keep quoted values together\r\n const attrs = attributes.match(/\\w+\\s*=\\s*(['\"])(.*?)\\1|\\w+(?!=)/g) || []\r\n\r\n for (const attr of attrs) {\r\n // Check if attribute has quotes\r\n if (attr.includes('=')) {\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n const [name, ...rest] = attr.split('=')\r\n const value = rest.join('=') // Rejoin in case value contains =\r\n\r\n // Check for proper quote matching\r\n if (\r\n !(\r\n (value.startsWith('\"') && value.endsWith('\"')) ||\r\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\r\n )\r\n ) {\r\n return false\r\n }\r\n\r\n // Extract the quote character used\r\n const quoteChar = value[0]\r\n // Check if there are any unescaped quotes of the same type inside the value\r\n const valueContent = value.slice(1, -1)\r\n if (valueContent.includes(quoteChar)) {\r\n return false\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Additional check for unclosed quotes before >\r\n const unclosedQuoteCheck = /<[^>]*?['\"][^'\"]*>/g\r\n const potentiallyUnclosedTags = htmlString.match(unclosedQuoteCheck) || []\r\n for (const tag of potentiallyUnclosedTags) {\r\n let inQuote = false\r\n let quoteChar = ''\r\n for (let i = 0; i < tag.length; i++) {\r\n if (tag[i] === \"'\" || tag[i] === '\"') {\r\n if (!inQuote) {\r\n inQuote = true\r\n quoteChar = tag[i]\r\n } else if (tag[i] === quoteChar) {\r\n inQuote = false\r\n }\r\n }\r\n }\r\n if (inQuote) {\r\n return false\r\n }\r\n }\r\n\r\n return true\r\n}\r\n\r\n// Check if self-closing tags are valid\r\nconst validateSelfClosingTags = (htmlString: string) => {\r\n const selfClosingTags = [...htmlString.matchAll(/<([a-z]+)(\\s[^>]*)\\/>/gi)]\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n for (const [_, tag, attributes] of selfClosingTags) {\r\n if (attributes) {\r\n const attrRegex = /(\\w+)=[\"']([^\"']*)[\"']/g\r\n const attrs = [...attributes.matchAll(attrRegex)]\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n for (const [__, attrName, attrValue] of attrs) {\r\n if (!attrName || !attrValue) {\r\n return false // Invalid attribute format\r\n }\r\n }\r\n // Check for unclosed attributes\r\n const unclosedAttrRegex = /(\\w+)=[\"'][^\"']*$/g\r\n const unclosedAttrs = [...attributes.matchAll(unclosedAttrRegex)]\r\n if (unclosedAttrs.length > 0) {\r\n return false // Unclosed attribute in self-closing tag\r\n }\r\n }\r\n }\r\n\r\n return true // Self-closing tags are valid\r\n}\r\n\r\n// Check for inline styles and ensure they follow a valid format\r\nconst validateInlineStyles = (htmlString: string) => {\r\n const styleRegex = /style=[\"']([^\"']*)[\"']/gi\r\n const styles = [...htmlString.matchAll(styleRegex)]\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n for (const [_, styleContent] of styles) {\r\n // A basic check for properly formatted style declarations (e.g., \"property: value;\")\r\n const styleRules = styleContent.split(';')\r\n for (const rule of styleRules) {\r\n if (rule.trim()) {\r\n const [property, value] = rule.split(':').map((s) => s.trim())\r\n if (!property || !value) {\r\n return false // Invalid style declaration\r\n }\r\n }\r\n }\r\n }\r\n\r\n return true // Inline styles are valid\r\n}\r\n\r\n// Main function that calls the smaller subfunctions\r\nconst validateHtmlBasic = (htmlString: string) => {\r\n return (\r\n validateTags(htmlString) &&\r\n validateAttributes(htmlString) &&\r\n validateSelfClosingTags(htmlString) &&\r\n validateInlineStyles(htmlString)\r\n )\r\n}\r\n\r\n// Utility to extract plain text from HTML string\r\nconst htmlToText = (html: string): string => {\r\n const tempDiv = document.createElement('div')\r\n tempDiv.innerHTML = html\r\n return tempDiv.textContent || tempDiv.innerText || ''\r\n}\r\n\r\n// Utility to truncate text safely\r\nconst truncateText = (text: string, maxLength: number): string => {\r\n if (text.length <= maxLength) return text\r\n return text.slice(0, maxLength).trim() + '...'\r\n}\r\n\r\nconst SafeHtmlRenderer = ({\r\n html,\r\n className,\r\n withoutContentClass,\r\n truncateLength,\r\n}: {\r\n html: string\r\n className?: string\r\n withoutContentClass?: boolean\r\n truncateLength?: number\r\n}) => {\r\n const isValid = validateHtmlBasic(html)\r\n // console.log(\"isValidHTML\", isValid);\r\n // TODO: add send error to TG\r\n\r\n if (!isValid) {\r\n console.error('Invalid HTML content:', html)\r\n return <div style={{ color: 'red' }}>Некорректное HTML содержимое</div>\r\n }\r\n\r\n // If truncateLength is provided, extract text, truncate, and render as plain HTML-safe text\r\n const processedHtml = truncateLength\r\n ? truncateText(htmlToText(html), truncateLength)\r\n : html\r\n\r\n return (\r\n <div\r\n className={[!withoutContentClass && 'content', className]\r\n .filter(Boolean)\r\n .join(' ')}\r\n dangerouslySetInnerHTML={{ __html: processedHtml }}\r\n />\r\n )\r\n}\r\n\r\nexport default SafeHtmlRenderer\r\n"],"names":["validateTags","htmlString","selfClosingTags","tagPattern","tagStack","match","fullTag","tagName","validateAttributes","attributeRegex","matches","fullMatch","attributes","attrs","attr","name","rest","value","quoteChar","unclosedQuoteCheck","potentiallyUnclosedTags","tag","inQuote","i","validateSelfClosingTags","_","attrRegex","__","attrName","attrValue","unclosedAttrRegex","validateInlineStyles","styleRegex","styles","styleContent","styleRules","rule","property","s","validateHtmlBasic","htmlToText","html","tempDiv","truncateText","text","maxLength","SafeHtmlRenderer","className","withoutContentClass","truncateLength","processedHtml","React","SafeHtmlRenderer$1"],"mappings":"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"}
|
package/dist/index.es41.js
CHANGED
|
@@ -39,7 +39,7 @@ const g = [
|
|
|
39
39
|
withTopMargin: l,
|
|
40
40
|
withBottomMargin: o
|
|
41
41
|
},
|
|
42
|
-
/* @__PURE__ */ e.createElement("div", { className: "grid grid-cols-1 sm:auto-rows-[222px] sm:grid-cols-3 lg:grid-cols-6 gap-3 mt-4" }, /* @__PURE__ */ e.createElement("div", { className: "p-6 bg-whitish rounded-lg col-span-1 sm:col-span-3 row-start-1 row-end-3 flex flex-col justify-between" }, /* @__PURE__ */ e.createElement(p, { level: 2 }, "Более 50 000", " ", /* @__PURE__ */ e.createElement("span", { className: "text-navy-opacity-40" }, "компаний работают с нами")), /* @__PURE__ */ e.createElement(r, { variant: "body", className: "w-[87.5%]" }, "Среди наших клиентов — крупнейшие холдинги, государственные учреждения, финансовые и производственные компании, а также более
|
|
42
|
+
/* @__PURE__ */ e.createElement("div", { className: "grid grid-cols-1 sm:auto-rows-[222px] sm:grid-cols-3 lg:grid-cols-6 gap-3 mt-4" }, /* @__PURE__ */ e.createElement("div", { className: "p-6 bg-whitish rounded-lg col-span-1 sm:col-span-3 row-start-1 row-end-3 flex flex-col justify-between" }, /* @__PURE__ */ e.createElement(p, { level: 2 }, "Более 50 000", " ", /* @__PURE__ */ e.createElement("span", { className: "text-navy-opacity-40" }, "компаний работают с нами")), /* @__PURE__ */ e.createElement(r, { variant: "body", className: "w-[87.5%]" }, "Среди наших клиентов — крупнейшие холдинги, государственные учреждения, финансовые и производственные компании, а также более 50 000 представителей малого и среднего бизнеса.")), g.map((t, c) => /* @__PURE__ */ e.createElement(
|
|
43
43
|
"div",
|
|
44
44
|
{
|
|
45
45
|
key: c,
|
package/dist/index.es41.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.es41.js","sources":["../src/sections/Partners.tsx"],"sourcesContent":["import Section, { SectionProps } from './Section'\r\n\r\nimport Heading from '@/components/core/typography/Heading'\r\nimport React from 'react'\r\nimport Text from '@/components/core/typography/Text'\r\nimport { getAdapter } from '@/Adapters'\r\n\r\nconst partners = [\r\n '/partners/rostelekom.svg',\r\n '/partners/mts.svg',\r\n '/partners/uralsib.svg',\r\n '/partners/sberbank.svg',\r\n '/partners/rosbank.svg',\r\n '/partners/pochtabank.svg',\r\n '/partners/vtb.svg',\r\n '/partners/alfa.svg',\r\n '/partners/otkrytie.svg',\r\n // \"МТС\",\r\n // \"Уралсиб\",\r\n // \"Сбербанк\",\r\n // \"Росбанк\",\r\n // \"Почта Банк\",\r\n // \"ВТБ\",\r\n // \"РосБанк\",\r\n // \"Открытие\",\r\n]\r\n\r\nexport interface PartnersProps {\r\n variant?: SectionProps['variant']\r\n withoutTopPadding?: boolean\r\n withoutBottomPadding?: boolean\r\n withTopMargin?: boolean\r\n withBottomMargin?: boolean\r\n}\r\n\r\nconst Partners: React.FC<PartnersProps> = ({\r\n variant,\r\n withoutTopPadding,\r\n withoutBottomPadding,\r\n withTopMargin,\r\n withBottomMargin,\r\n}) => {\r\n const ImageWrapper = getAdapter('ImageWrapper')\r\n\r\n return (\r\n <Section\r\n title=\"\"\r\n variant={variant}\r\n withoutTopPadding={withoutTopPadding}\r\n withoutBottomPadding={withoutBottomPadding}\r\n withTopMargin={withTopMargin}\r\n withBottomMargin={withBottomMargin}\r\n >\r\n <div className=\"grid grid-cols-1 sm:auto-rows-[222px] sm:grid-cols-3 lg:grid-cols-6 gap-3 mt-4\">\r\n <div className=\"p-6 bg-whitish rounded-lg col-span-1 sm:col-span-3 row-start-1 row-end-3 flex flex-col justify-between\">\r\n <Heading level={2}>\r\n Более 50 000{' '}\r\n <span className=\"text-navy-opacity-40\">\r\n компаний работают с нами\r\n </span>\r\n </Heading>\r\n\r\n <Text variant=\"body\" className=\"w-[87.5%]\">\r\n Среди наших клиентов — крупнейшие холдинги, государственные\r\n учреждения, финансовые и производственные компании, а
|
|
1
|
+
{"version":3,"file":"index.es41.js","sources":["../src/sections/Partners.tsx"],"sourcesContent":["import Section, { SectionProps } from './Section'\r\n\r\nimport Heading from '@/components/core/typography/Heading'\r\nimport React from 'react'\r\nimport Text from '@/components/core/typography/Text'\r\nimport { getAdapter } from '@/Adapters'\r\n\r\nconst partners = [\r\n '/partners/rostelekom.svg',\r\n '/partners/mts.svg',\r\n '/partners/uralsib.svg',\r\n '/partners/sberbank.svg',\r\n '/partners/rosbank.svg',\r\n '/partners/pochtabank.svg',\r\n '/partners/vtb.svg',\r\n '/partners/alfa.svg',\r\n '/partners/otkrytie.svg',\r\n // \"МТС\",\r\n // \"Уралсиб\",\r\n // \"Сбербанк\",\r\n // \"Росбанк\",\r\n // \"Почта Банк\",\r\n // \"ВТБ\",\r\n // \"РосБанк\",\r\n // \"Открытие\",\r\n]\r\n\r\nexport interface PartnersProps {\r\n variant?: SectionProps['variant']\r\n withoutTopPadding?: boolean\r\n withoutBottomPadding?: boolean\r\n withTopMargin?: boolean\r\n withBottomMargin?: boolean\r\n}\r\n\r\nconst Partners: React.FC<PartnersProps> = ({\r\n variant,\r\n withoutTopPadding,\r\n withoutBottomPadding,\r\n withTopMargin,\r\n withBottomMargin,\r\n}) => {\r\n const ImageWrapper = getAdapter('ImageWrapper')\r\n\r\n return (\r\n <Section\r\n title=\"\"\r\n variant={variant}\r\n withoutTopPadding={withoutTopPadding}\r\n withoutBottomPadding={withoutBottomPadding}\r\n withTopMargin={withTopMargin}\r\n withBottomMargin={withBottomMargin}\r\n >\r\n <div className=\"grid grid-cols-1 sm:auto-rows-[222px] sm:grid-cols-3 lg:grid-cols-6 gap-3 mt-4\">\r\n <div className=\"p-6 bg-whitish rounded-lg col-span-1 sm:col-span-3 row-start-1 row-end-3 flex flex-col justify-between\">\r\n <Heading level={2}>\r\n Более 50 000{' '}\r\n <span className=\"text-navy-opacity-40\">\r\n компаний работают с нами\r\n </span>\r\n </Heading>\r\n\r\n <Text variant=\"body\" className=\"w-[87.5%]\">\r\n Среди наших клиентов — крупнейшие холдинги, государственные\r\n учреждения, финансовые и производственные компании, а также\r\n более 50 000 представителей малого и среднего бизнеса.\r\n </Text>\r\n </div>\r\n {partners.map((partner, index) => (\r\n <div\r\n key={index}\r\n className=\"rounded-lg border border-solid border-navy-opacity-16 flex align-middle justify-center items-center\"\r\n >\r\n <ImageWrapper src={partner} width={160} height={64} alt={partner} />\r\n </div>\r\n ))}\r\n <div className=\"bg-whitish rounded-lg flex items-center justify-center\">\r\n <Text variant=\"body\" className=\"text-navy-opacity-40 py-6 sm:py-0\">\r\n 50 000+\r\n </Text>\r\n </div>\r\n </div>\r\n </Section>\r\n )\r\n}\r\n\r\nexport default Partners\r\n"],"names":["partners","Partners","variant","withoutTopPadding","withoutBottomPadding","withTopMargin","withBottomMargin","ImageWrapper","getAdapter","React","Section","Heading","Text","partner","index","Partners$1"],"mappings":";;;;;AAOA,MAAMA,IAAW;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASF,GAUMC,IAAoC,CAAC;AAAA,EACzC,SAAAC;AAAA,EACA,mBAAAC;AAAA,EACA,sBAAAC;AAAA,EACA,eAAAC;AAAA,EACA,kBAAAC;AACF,MAAM;AACE,QAAAC,IAAeC,EAAW,cAAc;AAG5C,SAAAC,gBAAAA,EAAA;AAAA,IAACC;AAAA,IAAA;AAAA,MACC,OAAM;AAAA,MACN,SAAAR;AAAA,MACA,mBAAAC;AAAA,MACA,sBAAAC;AAAA,MACA,eAAAC;AAAA,MACA,kBAAAC;AAAA,IAAA;AAAA,IAECG,gBAAAA,EAAA,cAAA,OAAA,EAAI,WAAU,iFAAA,mCACZ,OAAI,EAAA,WAAU,4GACbA,gBAAAA,EAAA,cAACE,KAAQ,OAAO,KAAG,gBACJ,qCACZ,QAAK,EAAA,WAAU,uBAAuB,GAAA,0BAEvC,CACF,GAECF,gBAAAA,EAAA,cAAAG,GAAA,EAAK,SAAQ,QAAO,WAAU,YAAY,GAAA,gLAI3C,CACF,GACCZ,EAAS,IAAI,CAACa,GAASC,MACtBL,gBAAAA,EAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAKK;AAAA,QACL,WAAU;AAAA,MAAA;AAAA,MAEVL,gBAAAA,EAAA,cAACF,KAAa,KAAKM,GAAS,OAAO,KAAK,QAAQ,IAAI,KAAKA,EAAS,CAAA;AAAA,IAAA,CAErE,GACAJ,gBAAAA,EAAA,cAAA,OAAA,EAAI,WAAU,yDACb,GAAAA,gBAAAA,EAAA,cAACG,GAAK,EAAA,SAAQ,QAAO,WAAU,oCAAoC,GAAA,SAEnE,CACF,CACF;AAAA,EAAA;AAGN,GAEAG,IAAed;"}
|
package/dist/styles.css
CHANGED