@limetech/lime-elements 37.14.0 → 37.15.0
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 +16 -0
- package/dist/cjs/limel-markdown.cjs.entry.js +1302 -2907
- package/dist/cjs/limel-markdown.cjs.entry.js.map +1 -1
- package/dist/cjs/limel-portal_3.cjs.entry.js +6 -4
- package/dist/cjs/limel-portal_3.cjs.entry.js.map +1 -1
- package/dist/collection/components/markdown/allowed-css-properties.js +13 -0
- package/dist/collection/components/markdown/allowed-css-properties.js.map +1 -0
- package/dist/collection/components/markdown/markdown-parser.js +15 -1
- package/dist/collection/components/markdown/markdown-parser.js.map +1 -1
- package/dist/collection/components/markdown/sanitize-style.js +65 -0
- package/dist/collection/components/markdown/sanitize-style.js.map +1 -0
- package/dist/collection/components/tooltip/tooltip.js +6 -4
- package/dist/collection/components/tooltip/tooltip.js.map +1 -1
- package/dist/esm/limel-markdown.entry.js +1302 -2907
- package/dist/esm/limel-markdown.entry.js.map +1 -1
- package/dist/esm/limel-portal_3.entry.js +6 -4
- package/dist/esm/limel-portal_3.entry.js.map +1 -1
- package/dist/lime-elements/lime-elements.esm.js +1 -1
- package/dist/lime-elements/{p-4b29d287.entry.js → p-66c9403f.entry.js} +2 -2
- package/dist/lime-elements/p-66c9403f.entry.js.map +1 -0
- package/dist/lime-elements/p-bc582bc6.entry.js +8 -0
- package/dist/lime-elements/p-bc582bc6.entry.js.map +1 -0
- package/dist/types/components/markdown/allowed-css-properties.d.ts +2 -0
- package/dist/types/components/markdown/sanitize-style.d.ts +36 -0
- package/package.json +6 -3
- package/dist/lime-elements/p-264bf26c.entry.js +0 -2
- package/dist/lime-elements/p-264bf26c.entry.js.map +0 -1
- package/dist/lime-elements/p-4b29d287.entry.js.map +0 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const allowedCssProperties = [
|
|
2
|
+
'background-color',
|
|
3
|
+
'color',
|
|
4
|
+
'font-style',
|
|
5
|
+
'font-weight',
|
|
6
|
+
'text-decoration-color',
|
|
7
|
+
'text-decoration-line',
|
|
8
|
+
'text-decoration-skip-ink',
|
|
9
|
+
'text-decoration-style',
|
|
10
|
+
'text-decoration-thickness',
|
|
11
|
+
'text-decoration',
|
|
12
|
+
];
|
|
13
|
+
//# sourceMappingURL=allowed-css-properties.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allowed-css-properties.js","sourceRoot":"","sources":["../../../src/components/markdown/allowed-css-properties.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG;EAChC,kBAAkB;EAClB,OAAO;EACP,YAAY;EACZ,aAAa;EACb,uBAAuB;EACvB,sBAAsB;EACtB,0BAA0B;EAC1B,uBAAuB;EACvB,2BAA2B;EAC3B,iBAAiB;CACpB,CAAC","sourcesContent":["export const allowedCssProperties = [\n 'background-color',\n 'color',\n 'font-style',\n 'font-weight',\n 'text-decoration-color',\n 'text-decoration-line',\n 'text-decoration-skip-ink',\n 'text-decoration-style',\n 'text-decoration-thickness',\n 'text-decoration',\n];\n"]}
|
|
@@ -6,6 +6,8 @@ import rehypeExternalLinks from 'rehype-external-links';
|
|
|
6
6
|
import rehypeSanitize from 'rehype-sanitize';
|
|
7
7
|
import rehypeStringify from 'rehype-stringify';
|
|
8
8
|
import rehypeRaw from 'rehype-raw';
|
|
9
|
+
import { visit } from 'unist-util-visit';
|
|
10
|
+
import { sanitizeStyle } from './sanitize-style';
|
|
9
11
|
/**
|
|
10
12
|
* Takes a string as input and returns a new string
|
|
11
13
|
* where the text has been converted to HTML.
|
|
@@ -30,7 +32,19 @@ export async function markdownToHTML(text, options) {
|
|
|
30
32
|
.use(remarkRehype, { allowDangerousHtml: true })
|
|
31
33
|
.use(rehypeExternalLinks, { target: '_blank' })
|
|
32
34
|
.use(rehypeRaw)
|
|
33
|
-
.use(rehypeSanitize
|
|
35
|
+
.use(rehypeSanitize, {
|
|
36
|
+
// Allow the `style` attribute on all elements
|
|
37
|
+
attributes: {
|
|
38
|
+
'*': ['style'],
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
.use(() => {
|
|
42
|
+
return (tree) => {
|
|
43
|
+
// Run the sanitizeStyle function on all elements, to sanitize
|
|
44
|
+
// the value of the `style` attribute, if there is one.
|
|
45
|
+
visit(tree, 'element', sanitizeStyle);
|
|
46
|
+
};
|
|
47
|
+
})
|
|
34
48
|
.use(rehypeStringify)
|
|
35
49
|
.process(text);
|
|
36
50
|
return file.toString();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-parser.js","sourceRoot":"","sources":["../../../src/components/markdown/markdown-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,WAAW,MAAM,cAAc,CAAC;AACvC,OAAO,YAAY,MAAM,eAAe,CAAC;AACzC,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AACxD,OAAO,cAAc,MAAM,iBAAiB,CAAC;AAC7C,OAAO,eAAe,MAAM,kBAAkB,CAAC;AAC/C,OAAO,SAAS,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"markdown-parser.js","sourceRoot":"","sources":["../../../src/components/markdown/markdown-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,WAAW,MAAM,cAAc,CAAC;AACvC,OAAO,YAAY,MAAM,eAAe,CAAC;AACzC,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AACxD,OAAO,cAAc,MAAM,iBAAiB,CAAC;AAC7C,OAAO,eAAe,MAAM,kBAAkB,CAAC;AAC/C,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAChC,IAAY,EACZ,OAA+B;EAE/B,IAAI,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,mBAAmB,EAAE;IAC9B,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;GAC5C;EAED,MAAM,IAAI,GAAG,MAAM,OAAO,EAAE;KACvB,GAAG,CAAC,WAAW,CAAC;KAChB,GAAG,CAAC,SAAS,CAAC;KACd,GAAG,CAAC,YAAY,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC;KAC/C,GAAG,CAAC,mBAAmB,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;KAC9C,GAAG,CAAC,SAAS,CAAC;KACd,GAAG,CAAC,cAAc,EAAE;IACjB,8CAA8C;IAC9C,UAAU,EAAE;MACR,GAAG,EAAE,CAAC,OAAO,CAAC;KACjB;GACJ,CAAC;KACD,GAAG,CAAC,GAAG,EAAE;IACN,OAAO,CAAC,IAAS,EAAE,EAAE;MACjB,8DAA8D;MAC9D,uDAAuD;MACvD,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;IAC1C,CAAC,CAAC;EACN,CAAC,CAAC;KACD,GAAG,CAAC,eAAe,CAAC;KACpB,OAAO,CAAC,IAAI,CAAC,CAAC;EAEnB,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;AAC3B,CAAC","sourcesContent":["import { unified } from 'unified';\nimport remarkParse from 'remark-parse';\nimport remarkRehype from 'remark-rehype';\nimport remarkGfm from 'remark-gfm';\nimport rehypeExternalLinks from 'rehype-external-links';\nimport rehypeSanitize from 'rehype-sanitize';\nimport rehypeStringify from 'rehype-stringify';\nimport rehypeRaw from 'rehype-raw';\nimport { visit } from 'unist-util-visit';\nimport { sanitizeStyle } from './sanitize-style';\n\n/**\n * Takes a string as input and returns a new string\n * where the text has been converted to HTML.\n *\n * If the text is formatted with .md markdown, it will\n * be transformed to HTML.\n *\n * If the text already is in HTML it will be sanitized and\n * \"dangerous\" tags such as <script> will be removed.\n *\n * @param text - The string to convert.\n * @param options - Options for the conversions.\n * @returns The resulting HTML.\n */\nexport async function markdownToHTML(\n text: string,\n options?: markdownToHTMLOptions,\n): Promise<string> {\n if (options?.forceHardLineBreaks) {\n text = text.replace(/([\\n\\r])/g, ' $1');\n }\n\n const file = await unified()\n .use(remarkParse)\n .use(remarkGfm)\n .use(remarkRehype, { allowDangerousHtml: true })\n .use(rehypeExternalLinks, { target: '_blank' })\n .use(rehypeRaw)\n .use(rehypeSanitize, {\n // Allow the `style` attribute on all elements\n attributes: {\n '*': ['style'],\n },\n })\n .use(() => {\n return (tree: any) => {\n // Run the sanitizeStyle function on all elements, to sanitize\n // the value of the `style` attribute, if there is one.\n visit(tree, 'element', sanitizeStyle);\n };\n })\n .use(rehypeStringify)\n .process(text);\n\n return file.toString();\n}\n\n/**\n * Options for markdownToHTML.\n */\nexport interface markdownToHTMLOptions {\n /**\n * Set to `true` to convert all soft line breaks to hard line breaks.\n */\n forceHardLineBreaks?: boolean;\n}\n"]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import parse from 'style-to-object';
|
|
2
|
+
import parseCSSColor from 'parse-css-color';
|
|
3
|
+
import { allowedCssProperties } from './allowed-css-properties';
|
|
4
|
+
/**
|
|
5
|
+
* Checks a node for a `style` attribute and, if found, sanitizes it.
|
|
6
|
+
*
|
|
7
|
+
* @param node - node to check
|
|
8
|
+
*/
|
|
9
|
+
export function sanitizeStyle(node) {
|
|
10
|
+
if (node.tagName && node.properties && node.properties.style) {
|
|
11
|
+
// Sanitize the 'style' attribute of the node.
|
|
12
|
+
node.properties.style = sanitizeStyleValue(node.properties.style);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Applies a whitelist to the CSS properties in the input string.
|
|
17
|
+
* Any CSS properties not in the whitelist will be removed.
|
|
18
|
+
*
|
|
19
|
+
* @param styleValue - a string with CSS properties and values
|
|
20
|
+
* @returns a sanitized version of the input string
|
|
21
|
+
*/
|
|
22
|
+
export function sanitizeStyleValue(styleValue) {
|
|
23
|
+
try {
|
|
24
|
+
const css = parse(styleValue);
|
|
25
|
+
const normalizedCss = normalizeBackgroundColor(css);
|
|
26
|
+
return Object.entries(normalizedCss)
|
|
27
|
+
.filter(([key]) => allowedCssProperties.includes(key))
|
|
28
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
29
|
+
.join('; ');
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
// eslint-disable-next-line no-console
|
|
33
|
+
console.error('Failed to parse style value', styleValue, error);
|
|
34
|
+
return '';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Returns a copy of the input object with the `background` property removed.
|
|
39
|
+
* If the `background` property's value was a valid CSS color value, the
|
|
40
|
+
* returned object will have a `background-color` property with the same value.
|
|
41
|
+
*
|
|
42
|
+
* @param css - an object with CSS properties as keys and CSS values as values
|
|
43
|
+
* @returns a modified copy of the input object
|
|
44
|
+
*/
|
|
45
|
+
export function normalizeBackgroundColor(css) {
|
|
46
|
+
const result = Object.assign({}, css);
|
|
47
|
+
delete result.background;
|
|
48
|
+
if ('background' in css && isValidCssColorValue(css.background)) {
|
|
49
|
+
result['background-color'] = css.background;
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Check if a value is a valid CSS color value.
|
|
55
|
+
* Note that this function is not 100% comprehensive. It does not support
|
|
56
|
+
* `currentColor` or `inherit`. It also does not support `var(--variable)` or
|
|
57
|
+
* `rgb(var(--variable))`, for example.
|
|
58
|
+
*
|
|
59
|
+
* @param value - a string to check
|
|
60
|
+
* @returns `true` if the value is a valid CSS color value, `false` otherwise
|
|
61
|
+
*/
|
|
62
|
+
export function isValidCssColorValue(value) {
|
|
63
|
+
return parseCSSColor(value) !== null;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=sanitize-style.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize-style.js","sourceRoot":"","sources":["../../../src/components/markdown/sanitize-style.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,iBAAiB,CAAC;AACpC,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAEhE;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAS;EACnC,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE;IAC1D,8CAA8C;IAC9C,IAAI,CAAC,UAAU,CAAC,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;GACrE;AACL,CAAC;AAED;;;;;;GAMG;AAEH,MAAM,UAAU,kBAAkB,CAAC,UAAkB;EACjD,IAAI;IACA,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;IAC9B,MAAM,aAAa,GAAG,wBAAwB,CAAC,GAAG,CAAC,CAAC;IAEpD,OAAO,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC;OAC/B,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,oBAAoB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;OACrD,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,KAAK,EAAE,CAAC;OACzC,IAAI,CAAC,IAAI,CAAC,CAAC;GACnB;EAAC,OAAO,KAAK,EAAE;IACZ,sCAAsC;IACtC,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;IAEhE,OAAO,EAAE,CAAC;GACb;AACL,CAAC;AAED;;;;;;;GAOG;AAEH,MAAM,UAAU,wBAAwB,CAAC,GAA2B;EAChE,MAAM,MAAM,qBAAQ,GAAG,CAAE,CAAC;EAC1B,OAAO,MAAM,CAAC,UAAU,CAAC;EAEzB,IAAI,YAAY,IAAI,GAAG,IAAI,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE;IAC7D,MAAM,CAAC,kBAAkB,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC;GAC/C;EAED,OAAO,MAAM,CAAC;AAClB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAa;EAC9C,OAAO,aAAa,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;AACzC,CAAC","sourcesContent":["import parse from 'style-to-object';\nimport parseCSSColor from 'parse-css-color';\nimport { allowedCssProperties } from './allowed-css-properties';\n\n/**\n * Checks a node for a `style` attribute and, if found, sanitizes it.\n *\n * @param node - node to check\n */\nexport function sanitizeStyle(node: any) {\n if (node.tagName && node.properties && node.properties.style) {\n // Sanitize the 'style' attribute of the node.\n node.properties.style = sanitizeStyleValue(node.properties.style);\n }\n}\n\n/**\n * Applies a whitelist to the CSS properties in the input string.\n * Any CSS properties not in the whitelist will be removed.\n *\n * @param styleValue - a string with CSS properties and values\n * @returns a sanitized version of the input string\n */\n\nexport function sanitizeStyleValue(styleValue: string): string {\n try {\n const css = parse(styleValue);\n const normalizedCss = normalizeBackgroundColor(css);\n\n return Object.entries(normalizedCss)\n .filter(([key]) => allowedCssProperties.includes(key))\n .map(([key, value]) => `${key}: ${value}`)\n .join('; ');\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('Failed to parse style value', styleValue, error);\n\n return '';\n }\n}\n\n/**\n * Returns a copy of the input object with the `background` property removed.\n * If the `background` property's value was a valid CSS color value, the\n * returned object will have a `background-color` property with the same value.\n *\n * @param css - an object with CSS properties as keys and CSS values as values\n * @returns a modified copy of the input object\n */\n\nexport function normalizeBackgroundColor(css: Record<string, string>) {\n const result = { ...css };\n delete result.background;\n\n if ('background' in css && isValidCssColorValue(css.background)) {\n result['background-color'] = css.background;\n }\n\n return result;\n}\n\n/**\n * Check if a value is a valid CSS color value.\n * Note that this function is not 100% comprehensive. It does not support\n * `currentColor` or `inherit`. It also does not support `var(--variable)` or\n * `rgb(var(--variable))`, for example.\n *\n * @param value - a string to check\n * @returns `true` if the value is a valid CSS color value, `false` otherwise\n */\nexport function isValidCssColorValue(value: string): boolean {\n return parseCSSColor(value) !== null;\n}\n"]}
|
|
@@ -86,16 +86,18 @@ export class Tooltip {
|
|
|
86
86
|
(_a = this.ownerElement) === null || _a === void 0 ? void 0 : _a.setAttribute('aria-describedby', this.tooltipId);
|
|
87
87
|
}
|
|
88
88
|
addListeners() {
|
|
89
|
-
var _a, _b, _c;
|
|
89
|
+
var _a, _b, _c, _d;
|
|
90
90
|
(_a = this.ownerElement) === null || _a === void 0 ? void 0 : _a.addEventListener('mouseover', this.showTooltip);
|
|
91
91
|
(_b = this.ownerElement) === null || _b === void 0 ? void 0 : _b.addEventListener('mouseout', this.hideTooltip);
|
|
92
|
-
(_c = this.ownerElement) === null || _c === void 0 ? void 0 : _c.addEventListener('
|
|
92
|
+
(_c = this.ownerElement) === null || _c === void 0 ? void 0 : _c.addEventListener('focus', this.showTooltip);
|
|
93
|
+
(_d = this.ownerElement) === null || _d === void 0 ? void 0 : _d.addEventListener('blur', this.hideTooltip);
|
|
93
94
|
}
|
|
94
95
|
removeListeners() {
|
|
95
|
-
var _a, _b, _c;
|
|
96
|
+
var _a, _b, _c, _d;
|
|
96
97
|
(_a = this.ownerElement) === null || _a === void 0 ? void 0 : _a.removeEventListener('mouseover', this.showTooltip);
|
|
97
98
|
(_b = this.ownerElement) === null || _b === void 0 ? void 0 : _b.removeEventListener('mouseout', this.hideTooltip);
|
|
98
|
-
(_c = this.ownerElement) === null || _c === void 0 ? void 0 : _c.removeEventListener('
|
|
99
|
+
(_c = this.ownerElement) === null || _c === void 0 ? void 0 : _c.removeEventListener('focus', this.showTooltip);
|
|
100
|
+
(_d = this.ownerElement) === null || _d === void 0 ? void 0 : _d.removeEventListener('blur', this.hideTooltip);
|
|
99
101
|
}
|
|
100
102
|
getOwnerElement() {
|
|
101
103
|
let element = this.host;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tooltip.js","sourceRoot":"","sources":["../../../src/components/tooltip/tooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAEnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAG9D,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAMH,MAAM,OAAO,OAAO;EA+ChB;
|
|
1
|
+
{"version":3,"file":"tooltip.js","sourceRoot":"","sources":["../../../src/components/tooltip/tooltip.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Component, h, Prop, Element, State } from '@stencil/core';\nimport { JSX } from 'react';\nimport { createRandomString } from '../../util/random-string';\nimport { OpenDirection } from '../menu/menu.types';\n\nconst DEFAULT_MAX_LENGTH = 50;\n\n/**\n * A tooltip can be used to display a descriptive text for any element.\n * The displayed content must be a brief and supplemental string of text,\n * identifying the element or describing its function for the user,\n * helping them better understand unfamiliar objects that aren't described\n * directly in the UI.\n *\n * ## Interaction\n * The tooltip appears after a slight delay, when the element is hovered;\n * and disappears as soon as the cursor leaves the element.\n * Therefore, users cannot interact with the tip, but if the trigger element\n * itself is interactive, it will remain interactible even with a tooltip bound\n * to it.\n *\n * :::note\n * In order to display the tooltip, the tooltip element and its trigger element\n * must be within the same document or document fragment (the same shadowRoot).\n * Often, it's easiest to just place them next to each other like in the example\n * below, but if you need to, you can place them differently.\n *\n * ```html\n * <limel-button icon=\"search\" id=\"tooltip-example\" />\n * <limel-tooltip label=\"Search\" elementId=\"tooltip-example\" />\n * ```\n * :::\n *\n * ## Usage\n * - Keep in mind that tooltips can be distracting, and can be perceived as an interruption.\n * Use them only when they add significant value.\n * - A good tip is concise, helpful, and informative.\n * Don't explain the obvious or simply repeat what is already on the screen.\n * When used correctly, supplemental info of a tooltip helps to [declutter the UI](/#/DesignGuidelines/decluttering.md/).\n * - If the tip is essential to the primary tasks that the user is performing,\n * such as warnings or important notes, include the information directly in the\n * interface instead.\n * - When a component offers a helper text (e.g. [Input field](/#/component/limel-input-field/)),\n * use that, not a tooltip.\n * - Make sure to use the tooltip on an element that users naturally and\n * effortlessly recognize can be hovered.\n *\n * @exampleComponent limel-example-tooltip-basic\n * @exampleComponent limel-example-tooltip-max-character\n * @exampleComponent limel-example-tooltip-composite\n */\n@Component({\n tag: 'limel-tooltip',\n shadow: true,\n styleUrl: 'tooltip.scss',\n})\nexport class Tooltip {\n /**\n * ID of the owner element that the tooltip should describe.\n * Must be a child within the same document fragment as the tooltip element\n * itself.\n */\n @Prop({ reflect: true })\n public elementId!: string;\n\n /**\n * Short descriptive text of the owner element.\n */\n @Prop({ reflect: true })\n public label!: string;\n\n /**\n * Additional helper text for the element.\n * Example usage can be a keyboard shortcut to activate the function of the\n * owner element.\n */\n @Prop({ reflect: true })\n public helperLabel?: string;\n\n /**\n * The maximum amount of characters before rendering 'label' and\n * 'helperLabel' in two rows.\n */\n @Prop({ reflect: true })\n public maxlength?: number = DEFAULT_MAX_LENGTH;\n\n /**\n * Decides the tooltip's location in relation to its trigger.\n */\n @Prop({ reflect: true })\n public openDirection: OpenDirection = 'top';\n\n @Element()\n private host: HTMLLimelTooltipElement;\n\n @State()\n private open: boolean;\n\n private portalId: string;\n private tooltipId: string;\n private showTooltipTimeoutHandle: number;\n private ownerElement: HTMLElement;\n\n public constructor() {\n this.portalId = createRandomString();\n this.tooltipId = createRandomString();\n }\n\n public connectedCallback() {\n this.ownerElement = this.getOwnerElement();\n this.setOwnerAriaLabel();\n this.addListeners();\n }\n\n public disconnectedCallback() {\n this.removeListeners();\n }\n\n public render(): JSX.Element {\n const tooltipZIndex = getComputedStyle(this.host).getPropertyValue(\n '--tooltip-z-index',\n );\n\n return (\n <div class=\"trigger-anchor\">\n <limel-portal\n openDirection={this.openDirection}\n visible={this.open}\n containerId={this.portalId}\n containerStyle={{\n 'z-index': tooltipZIndex,\n 'pointer-events': 'none',\n }}\n anchor={this.ownerElement}\n >\n <limel-tooltip-content\n label={this.label}\n helperLabel={this.helperLabel}\n maxlength={this.maxlength}\n role=\"tooltip\"\n aria-hidden={!this.open}\n id={this.tooltipId}\n />\n </limel-portal>\n </div>\n );\n }\n\n private setOwnerAriaLabel() {\n this.ownerElement?.setAttribute('aria-describedby', this.tooltipId);\n }\n\n private addListeners() {\n this.ownerElement?.addEventListener('mouseover', this.showTooltip);\n this.ownerElement?.addEventListener('mouseout', this.hideTooltip);\n this.ownerElement?.addEventListener('focus', this.showTooltip);\n this.ownerElement?.addEventListener('blur', this.hideTooltip);\n }\n\n private removeListeners() {\n this.ownerElement?.removeEventListener('mouseover', this.showTooltip);\n this.ownerElement?.removeEventListener('mouseout', this.hideTooltip);\n this.ownerElement?.removeEventListener('focus', this.showTooltip);\n this.ownerElement?.removeEventListener('blur', this.hideTooltip);\n }\n\n private showTooltip = () => {\n const tooltipDelay = 500;\n this.showTooltipTimeoutHandle = window.setTimeout(() => {\n this.open = true;\n }, tooltipDelay);\n };\n\n private hideTooltip = () => {\n clearTimeout(this.showTooltipTimeoutHandle);\n this.open = false;\n };\n\n private getOwnerElement(): HTMLElement | undefined {\n let element: Node = this.host;\n\n do {\n element = element.parentNode;\n } while (\n element &&\n element.nodeType !== Node.DOCUMENT_FRAGMENT_NODE &&\n element.nodeType !== Node.DOCUMENT_NODE\n );\n\n return (element as ShadowRoot)?.getElementById(this.elementId);\n }\n}\n"]}
|