lightning-base-components 1.28.14-alpha → 1.28.16-alpha
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/metadata/raptor.json +33 -11
- package/package.json +29 -1
- package/scopedImports/@salesforce-label-LightningControl.indeterminate.js +6 -0
- package/src/lightning/baseCombobox/baseCombobox.d.ts.map +1 -1
- package/src/lightning/baseCombobox/baseCombobox.js +3 -1
- package/src/lightning/combobox/combobox.d.ts.map +1 -1
- package/src/lightning/combobox/combobox.js +1 -1
- package/src/lightning/datatable/keyboard.d.ts.map +1 -1
- package/src/lightning/datatable/keyboard.js +4 -4
- package/src/lightning/dualListbox/dualListbox.d.ts +3 -0
- package/src/lightning/dualListbox/dualListbox.d.ts.map +1 -1
- package/src/lightning/dualListbox/dualListbox.html +5 -5
- package/src/lightning/dualListbox/dualListbox.js +15 -0
- package/src/lightning/dualListbox/dualListbox.lbc.empty.css +8 -0
- package/src/lightning/dualListbox/dualListbox.lbc.resizereflow.css +35 -0
- package/src/lightning/features/gates/imports.d.ts +5 -0
- package/src/lightning/features/gates/imports.d.ts.map +1 -1
- package/src/lightning/features/gates/imports.js +5 -0
- package/src/lightning/features/types.d.ts +5 -0
- package/src/lightning/features/types.d.ts.map +1 -1
- package/src/lightning/formattedRichText/colorShiftUtil.d.ts +17 -0
- package/src/lightning/formattedRichText/colorShiftUtil.d.ts.map +1 -0
- package/src/lightning/formattedRichText/colorShiftUtil.js +118 -0
- package/src/lightning/formattedRichText/formattedRichText.csr.html +3 -0
- package/src/lightning/formattedRichText/formattedRichText.d.ts +54 -2
- package/src/lightning/formattedRichText/formattedRichText.d.ts.map +1 -1
- package/src/lightning/formattedRichText/formattedRichText.js +253 -1
- package/src/lightning/formattedRichText/formattedRichText.ssr.html +8 -0
- package/src/lightning/formattedRichText/linkTextNodesSSR.d.ts +2 -0
- package/src/lightning/formattedRichText/linkTextNodesSSR.d.ts.map +1 -0
- package/src/lightning/formattedRichText/linkTextNodesSSR.js +37 -0
- package/src/lightning/formattedRichText/richTextConfig.d.ts +9 -0
- package/src/lightning/formattedRichText/richTextConfig.d.ts.map +1 -0
- package/src/lightning/formattedRichText/richTextConfig.js +176 -0
- package/src/lightning/formattedRichTextRenderer/formattedRichTextRenderer.d.ts.map +1 -1
- package/src/lightning/formattedRichTextRenderer/formattedRichTextRenderer.js +16 -12
- package/src/lightning/groupedCombobox/groupedCombobox.d.ts +4 -0
- package/src/lightning/groupedCombobox/groupedCombobox.d.ts.map +1 -1
- package/src/lightning/groupedCombobox/groupedCombobox.html +1 -0
- package/src/lightning/groupedCombobox/groupedCombobox.js +13 -1
- package/src/lightning/input/input.d.ts +4 -0
- package/src/lightning/input/input.d.ts.map +1 -1
- package/src/lightning/input/input.html +41 -18
- package/src/lightning/input/input.js +30 -0
- package/src/lightning/modalBase/modalBase.d.ts +1 -0
- package/src/lightning/modalBase/modalBase.d.ts.map +1 -1
- package/src/lightning/modalBase/modalBase.html +8 -0
- package/src/lightning/modalBase/modalBase.js +3 -0
- package/src/lightning/multiColumnSortingModal/multiColumnSortingModal.d.ts +1 -0
- package/src/lightning/multiColumnSortingModal/multiColumnSortingModal.d.ts.map +1 -1
- package/src/lightning/multiColumnSortingModal/multiColumnSortingModal.html +1 -1
- package/src/lightning/multiColumnSortingModal/multiColumnSortingModal.js +14 -0
- package/src/lightning/overlayBase/overlayBase.css +21 -0
- package/src/lightning/overlayBase/overlayBase.d.ts +28 -0
- package/src/lightning/overlayBase/overlayBase.d.ts.map +1 -0
- package/src/lightning/overlayBase/overlayBase.html +24 -0
- package/src/lightning/overlayBase/overlayBase.js +125 -0
- package/src/lightning/overlayBase/overlayBase.js-meta.xml +8 -0
- package/src/lightning/overlayPositionUtils/anchorPositioningAdapter.d.ts +24 -0
- package/src/lightning/overlayPositionUtils/anchorPositioningAdapter.d.ts.map +1 -0
- package/src/lightning/overlayPositionUtils/anchorPositioningAdapter.js +49 -0
- package/src/lightning/overlayPositionUtils/cssAnchorPositioning.d.ts +14 -0
- package/src/lightning/overlayPositionUtils/cssAnchorPositioning.d.ts.map +1 -0
- package/src/lightning/overlayPositionUtils/cssAnchorPositioning.js +94 -0
- package/src/lightning/overlayPositionUtils/overlayPositionUtils.d.ts +55 -0
- package/src/lightning/overlayPositionUtils/overlayPositionUtils.d.ts.map +1 -0
- package/src/lightning/overlayPositionUtils/overlayPositionUtils.js +107 -0
- package/src/lightning/overlayPositionUtils/overlayPositionUtils.js-meta.xml +6 -0
- package/src/lightning/overlayPositionUtils/positionLibraryFallback.d.ts +30 -0
- package/src/lightning/overlayPositionUtils/positionLibraryFallback.d.ts.map +1 -0
- package/src/lightning/overlayPositionUtils/positionLibraryFallback.js +435 -0
- package/src/lightning/primitiveCellFactory/primitiveCellFactory.d.ts +1 -1
- package/src/lightning/primitiveCellFactory/primitiveCellFactory.d.ts.map +1 -1
- package/src/lightning/primitiveCellFactory/primitiveCellFactory.js +4 -4
- package/src/lightning/primitiveHeaderFactory/selectableHeader.html +1 -0
- package/src/lightning/primitiveInputCheckbox/primitiveInputCheckbox.d.ts +5 -1
- package/src/lightning/primitiveInputCheckbox/primitiveInputCheckbox.d.ts.map +1 -1
- package/src/lightning/primitiveInputCheckbox/primitiveInputCheckbox.js +35 -1
- package/src/lightning/primitiveInputCheckboxButton/primitiveInputCheckboxButton.js +1 -1
- package/src/lightning/primitiveInputSimple/primitiveInputSimple.d.ts.map +1 -1
- package/src/lightning/primitiveInputSimple/primitiveInputSimple.js +2 -1
- package/src/lightning/primitiveOverlay/__examples__/alert/alert.d.ts +13 -0
- package/src/lightning/primitiveOverlay/__examples__/alert/alert.d.ts.map +1 -0
- package/src/lightning/primitiveOverlay/__examples__/alert/alert.html +27 -0
- package/src/lightning/primitiveOverlay/__examples__/alert/alert.js +34 -0
- package/src/lightning/primitiveOverlay/__examples__/basic/basic.css +7 -0
- package/src/lightning/primitiveOverlay/__examples__/basic/basic.d.ts +12 -0
- package/src/lightning/primitiveOverlay/__examples__/basic/basic.d.ts.map +1 -0
- package/src/lightning/primitiveOverlay/__examples__/basic/basic.html +18 -0
- package/src/lightning/primitiveOverlay/__examples__/basic/basic.js +58 -0
- package/src/lightning/primitiveOverlay/__examples__/demo/demo.d.ts +11 -0
- package/src/lightning/primitiveOverlay/__examples__/demo/demo.d.ts.map +1 -0
- package/src/lightning/primitiveOverlay/__examples__/demo/demo.html +29 -0
- package/src/lightning/primitiveOverlay/__examples__/demo/demo.js +41 -0
- package/src/lightning/primitiveOverlay/__examples__/panel/panel.d.ts +9 -0
- package/src/lightning/primitiveOverlay/__examples__/panel/panel.d.ts.map +1 -0
- package/src/lightning/primitiveOverlay/__examples__/panel/panel.html +17 -0
- package/src/lightning/primitiveOverlay/__examples__/panel/panel.js +24 -0
- package/src/lightning/primitiveOverlay/primitiveOverlay.d.ts +8 -0
- package/src/lightning/primitiveOverlay/primitiveOverlay.d.ts.map +1 -0
- package/src/lightning/primitiveOverlay/primitiveOverlay.html +4 -0
- package/src/lightning/primitiveOverlay/primitiveOverlay.js +37 -0
- package/src/lightning/primitiveOverlay/primitiveOverlay.js-meta.xml +7 -0
- package/src/lightning/tabBar/tabBar.d.ts.map +1 -1
- package/src/lightning/tabBar/tabBar.js +4 -1
- package/src/lightning/tooltipLibrary/tooltipLibrary.d.ts +5 -0
- package/src/lightning/tooltipLibrary/tooltipLibrary.d.ts.map +1 -1
- package/src/lightning/tooltipLibrary/tooltipLibrary.js +119 -5
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
export function srgbToLinear(v) {
|
|
7
|
+
v /= 255;
|
|
8
|
+
return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
|
9
|
+
}
|
|
10
|
+
export function getLuminance({ r, g, b }) {
|
|
11
|
+
const linearR = srgbToLinear(r);
|
|
12
|
+
const linearG = srgbToLinear(g);
|
|
13
|
+
const linearB = srgbToLinear(b);
|
|
14
|
+
return (0.2126 * linearR) + (0.7152 * linearG) + (0.0722 * linearB);
|
|
15
|
+
}
|
|
16
|
+
export function getContrastRatio(color1, color2) {
|
|
17
|
+
const lum1 = getLuminance(color1);
|
|
18
|
+
const lum2 = getLuminance(color2);
|
|
19
|
+
const lighter = Math.max(lum1, lum2);
|
|
20
|
+
const darker = Math.min(lum1, lum2);
|
|
21
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
22
|
+
}
|
|
23
|
+
export function hslToRgb(h, s, l) {
|
|
24
|
+
s /= 100;
|
|
25
|
+
l /= 100;
|
|
26
|
+
const c = (1 - Math.abs((2 * l) - 1)) * s, x = c * (1 - Math.abs(((h / 60) % 2) - 1)), m = l - (c / 2);
|
|
27
|
+
let r = 0, g = 0, b = 0;
|
|
28
|
+
if (h >= 0 && h < 60) {
|
|
29
|
+
r = c;
|
|
30
|
+
g = x;
|
|
31
|
+
b = 0;
|
|
32
|
+
}
|
|
33
|
+
else if (h >= 60 && h < 120) {
|
|
34
|
+
r = x;
|
|
35
|
+
g = c;
|
|
36
|
+
b = 0;
|
|
37
|
+
}
|
|
38
|
+
else if (h >= 120 && h < 180) {
|
|
39
|
+
r = 0;
|
|
40
|
+
g = c;
|
|
41
|
+
b = x;
|
|
42
|
+
}
|
|
43
|
+
else if (h >= 180 && h < 240) {
|
|
44
|
+
r = 0;
|
|
45
|
+
g = x;
|
|
46
|
+
b = c;
|
|
47
|
+
}
|
|
48
|
+
else if (h >= 240 && h < 300) {
|
|
49
|
+
r = x;
|
|
50
|
+
g = 0;
|
|
51
|
+
b = c;
|
|
52
|
+
}
|
|
53
|
+
else if (h >= 300 && h < 360) {
|
|
54
|
+
r = c;
|
|
55
|
+
g = 0;
|
|
56
|
+
b = x;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
r: Math.round((r + m) * 255),
|
|
60
|
+
g: Math.round((g + m) * 255),
|
|
61
|
+
b: Math.round((b + m) * 255)
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export function rgbToHsl({ r, g, b }) {
|
|
65
|
+
r /= 255;
|
|
66
|
+
g /= 255;
|
|
67
|
+
b /= 255;
|
|
68
|
+
const max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
69
|
+
let h, s;
|
|
70
|
+
const l = (max + min) / 2;
|
|
71
|
+
if (max === min) {
|
|
72
|
+
h = s = 0;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
const d = max - min;
|
|
76
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
77
|
+
switch (max) {
|
|
78
|
+
case r:
|
|
79
|
+
h = ((g - b) / d) + (g < b ? 6 : 0);
|
|
80
|
+
break;
|
|
81
|
+
case g:
|
|
82
|
+
h = ((b - r) / d) + 2;
|
|
83
|
+
break;
|
|
84
|
+
case b:
|
|
85
|
+
h = ((r - g) / d) + 4;
|
|
86
|
+
break;
|
|
87
|
+
default:
|
|
88
|
+
}
|
|
89
|
+
h /= 6;
|
|
90
|
+
}
|
|
91
|
+
return { h: h * 360, s: s * 100, l: l * 100 };
|
|
92
|
+
}
|
|
93
|
+
export function ensureWcagCompliance(bgColor, fgColor) {
|
|
94
|
+
const MIN_CONTRAST = 4.5;
|
|
95
|
+
let currentRatio = getContrastRatio(bgColor, fgColor);
|
|
96
|
+
if (currentRatio >= MIN_CONTRAST) {
|
|
97
|
+
return fgColor;
|
|
98
|
+
}
|
|
99
|
+
const hslColor = rgbToHsl(fgColor);
|
|
100
|
+
let currentLightness = hslColor.l;
|
|
101
|
+
const bgLuminance = getLuminance(bgColor);
|
|
102
|
+
const fgLuminance = getLuminance(fgColor);
|
|
103
|
+
const adjustDirection = fgLuminance < bgLuminance ? -1 : 1;
|
|
104
|
+
while (currentRatio < MIN_CONTRAST && currentLightness >= 0 && currentLightness <= 100) {
|
|
105
|
+
currentLightness += adjustDirection * 1;
|
|
106
|
+
if (currentLightness < 0 || currentLightness > 100) {
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
const newFgColor = hslToRgb(hslColor.h, hslColor.s, currentLightness);
|
|
110
|
+
currentRatio = getContrastRatio(bgColor, newFgColor);
|
|
111
|
+
if (currentRatio >= MIN_CONTRAST) {
|
|
112
|
+
return newFgColor;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const white = { r: 255, g: 255, b: 255 };
|
|
116
|
+
const black = { r: 0, g: 0, b: 0 };
|
|
117
|
+
return getContrastRatio(bgColor, white) >= getContrastRatio(bgColor, black) ? white : black;
|
|
118
|
+
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import LightningFormattedRichTextRenderer from 'lightning/formattedRichTextRenderer';
|
|
2
|
+
import type { Template } from 'lwc';
|
|
3
|
+
import LightningShadowBaseClass from 'lightning/shadowBaseClassPrivate';
|
|
4
|
+
import { Stylesheets } from '@lwc/engine-core';
|
|
2
5
|
interface ILightningFormattedRichText {
|
|
3
6
|
csrOnly: boolean;
|
|
4
7
|
connectedCallback(): void;
|
|
@@ -6,7 +9,7 @@ interface ILightningFormattedRichText {
|
|
|
6
9
|
renderRichText(): void;
|
|
7
10
|
disconnectedCallback(): void;
|
|
8
11
|
}
|
|
9
|
-
|
|
12
|
+
declare class LightningFormattedRichText extends LightningFormattedRichTextRenderer implements ILightningFormattedRichText {
|
|
10
13
|
csrOnly: boolean;
|
|
11
14
|
get enableFormattedRichTextParts(): boolean;
|
|
12
15
|
constructor();
|
|
@@ -15,5 +18,54 @@ export default class LightningFormattedRichText extends LightningFormattedRichTe
|
|
|
15
18
|
renderRichText(): void;
|
|
16
19
|
disconnectedCallback(): void;
|
|
17
20
|
}
|
|
18
|
-
|
|
21
|
+
interface ILightningFormattedRichTextConsolidated {
|
|
22
|
+
connectedCallback(): void;
|
|
23
|
+
renderedCallback(): void;
|
|
24
|
+
renderRichText(): void;
|
|
25
|
+
disconnectedCallback(): void;
|
|
26
|
+
render(): Template;
|
|
27
|
+
handleClick(event: MouseEvent): void;
|
|
28
|
+
linkRichText(): void;
|
|
29
|
+
removeLinkClickListeners(): void;
|
|
30
|
+
readonly links: HTMLAnchorElement[];
|
|
31
|
+
readonly container: HTMLSpanElement | null;
|
|
32
|
+
}
|
|
33
|
+
declare class LightningFormattedRichTextConsolidated extends LightningShadowBaseClass implements ILightningFormattedRichTextConsolidated {
|
|
34
|
+
static validationOptOut: string[];
|
|
35
|
+
static stylesheets: Stylesheets[];
|
|
36
|
+
rendered: boolean;
|
|
37
|
+
_value: string;
|
|
38
|
+
_disableLinkify: boolean;
|
|
39
|
+
connected: boolean;
|
|
40
|
+
richText: string | null;
|
|
41
|
+
rawTextIfSanitizerThrewError: string | null;
|
|
42
|
+
serverRenderedContent: string | null;
|
|
43
|
+
linkingRequired: boolean;
|
|
44
|
+
_themeKey: string;
|
|
45
|
+
_backgroundColor: any;
|
|
46
|
+
get disableLinkify(): boolean;
|
|
47
|
+
set disableLinkify(val: boolean);
|
|
48
|
+
get value(): string;
|
|
49
|
+
set value(val: string);
|
|
50
|
+
render(): Template;
|
|
51
|
+
constructor();
|
|
52
|
+
connectedCallback(): void;
|
|
53
|
+
renderedCallback(): void;
|
|
54
|
+
renderRichText(): void;
|
|
55
|
+
disconnectedCallback(): void;
|
|
56
|
+
handleClick(event: MouseEvent): void;
|
|
57
|
+
linkRichText(): void;
|
|
58
|
+
removeLinkClickListeners(): void;
|
|
59
|
+
get links(): HTMLAnchorElement[];
|
|
60
|
+
get container(): HTMLSpanElement | null;
|
|
61
|
+
get backgroundColor(): any;
|
|
62
|
+
get backgroundColorRgb(): {
|
|
63
|
+
r: number;
|
|
64
|
+
g: number;
|
|
65
|
+
b: number;
|
|
66
|
+
};
|
|
67
|
+
adjustColors(): void;
|
|
68
|
+
}
|
|
69
|
+
declare let toExport: typeof LightningFormattedRichText | typeof LightningFormattedRichTextConsolidated;
|
|
70
|
+
export default toExport;
|
|
19
71
|
//# sourceMappingURL=formattedRichText.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formattedRichText.d.ts","sourceRoot":"","sources":["../../../../../modules/lightning/formattedRichText/formattedRichText.ts"],"names":[],"mappings":"AAAA,OAAO,kCAAkC,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"formattedRichText.d.ts","sourceRoot":"","sources":["../../../../../modules/lightning/formattedRichText/formattedRichText.ts"],"names":[],"mappings":"AAAA,OAAO,kCAAkC,MAAM,qCAAqC,CAAC;AAGrF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAIpC,OAAO,wBAAwB,MAAM,kCAAkC,CAAC;AAexE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAgC/C,UAAU,2BAA2B;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,IAAI,IAAI,CAAC;IAC1B,gBAAgB,IAAI,IAAI,CAAC;IACzB,cAAc,IAAI,IAAI,CAAC;IACvB,oBAAoB,IAAI,IAAI,CAAC;CAChC;AAED,cAAM,0BACF,SAAQ,kCACR,YAAW,2BAA2B;IACtC,OAAO,EAAE,OAAO,CAAS;IAEzB,IAAI,4BAA4B,IAAI,OAAO,CAE1C;;IASD,iBAAiB,IAAI,IAAI;IASzB,gBAAgB,IAAI,IAAI;IAMxB,cAAc,IAAI,IAAI;IAgCtB,oBAAoB,IAAI,IAAI;CAK/B;AAED,UAAU,uCAAuC;IAC7C,iBAAiB,IAAI,IAAI,CAAC;IAC1B,gBAAgB,IAAI,IAAI,CAAC;IACzB,cAAc,IAAI,IAAI,CAAC;IACvB,oBAAoB,IAAI,IAAI,CAAC;IAC7B,MAAM,IAAI,QAAQ,CAAC;IACnB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACrC,YAAY,IAAI,IAAI,CAAC;IACrB,wBAAwB,IAAI,IAAI,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,EAAE,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;CAC9C;AAED,cAAM,sCACF,SAAQ,wBACR,YAAW,uCAAuC;IAClD,MAAM,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAa;IAC9C,MAAM,CAAC,WAAW,EAAE,WAAW,EAAE,CAAkB;IAEnD,QAAQ,UAAS;IACjB,MAAM,SAAM;IACZ,eAAe,UAAS;IACxB,SAAS,EAAE,OAAO,CAAS;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC/B,4BAA4B,EAAE,MAAM,GAAG,IAAI,CAAQ;IACnD,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC5C,eAAe,EAAE,OAAO,CAAS;IACjC,SAAS,SAAW;IACpB,gBAAgB,MAAQ;IAOxB,IACI,cAAc,IAAI,OAAO,CAE5B;IAED,IAAI,cAAc,CAAC,GAAG,EAAE,OAAO,EAG9B;IAOD,IACI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,KAAK,CAAC,GAAG,EAAE,MAAM,EAGpB;IAED,MAAM,IAAI,QAAQ;;IAelB,iBAAiB,IAAI,IAAI;IAqBzB,gBAAgB,IAAI,IAAI;IAgBxB,cAAc,IAAI,IAAI;IAuFtB,oBAAoB,IAAI,IAAI;IAK5B,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAgBpC,YAAY,IAAI,IAAI;IAUpB,wBAAwB,IAAI,IAAI;IAMhC,IAAI,KAAK,IAAI,iBAAiB,EAAE,CAG/B;IAED,IAAI,SAAS,IAAI,eAAe,GAAG,IAAI,CAEtC;IAED,IAAI,eAAe,QAiBlB;IAED,IAAI,kBAAkB;;;;MAcrB;IAED,YAAY;CA0Bf;AAED,QAAA,IAAI,QAAQ,EAAE,OAAO,0BAA0B,GAAG,OAAO,sCAAsC,CAAC;AAOhG,eAAe,QAAQ,CAAC"}
|
|
@@ -6,8 +6,38 @@
|
|
|
6
6
|
import LightningFormattedRichTextRenderer from 'lightning/formattedRichTextRenderer';
|
|
7
7
|
import { privateContext } from 'lightning/utilsInternal';
|
|
8
8
|
import { features } from 'lightning/features';
|
|
9
|
+
import ssrTemplate from './formattedRichText.ssr.html';
|
|
10
|
+
import csrTemplate from './formattedRichText.csr.html';
|
|
11
|
+
import LightningShadowBaseClass from 'lightning/shadowBaseClassPrivate';
|
|
12
|
+
import { api } from 'lwc';
|
|
13
|
+
import nativeStyles from './formattedRichText.css';
|
|
14
|
+
import { setHooks } from 'lwc';
|
|
15
|
+
import sanitizeHTML from 'lightning/purifyLib';
|
|
16
|
+
import { richTextConfig } from './richTextConfig';
|
|
17
|
+
import { updateRawLinkInfo } from 'lightning/routingService';
|
|
18
|
+
import { normalizeBoolean, hasOnlyAllowedVideoIframes, } from 'lightning/utilsPrivate';
|
|
19
|
+
import { linkTextNodes } from 'lightning/utils';
|
|
20
|
+
import { linkTextNodesSSR } from './linkTextNodesSSR';
|
|
21
|
+
import { ensureWcagCompliance } from './colorShiftUtil';
|
|
9
22
|
const { setContext, assertContext } = privateContext;
|
|
10
|
-
|
|
23
|
+
try {
|
|
24
|
+
if (features.enableFormattedRichTextNoNestedShadow && import.meta.env.SSR) {
|
|
25
|
+
setHooks({
|
|
26
|
+
sanitizeHtmlContent(rawHTMLString) {
|
|
27
|
+
return rawHTMLString;
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
}
|
|
34
|
+
function sldsIsV2Enabled() {
|
|
35
|
+
if (!import.meta.env.SSR) {
|
|
36
|
+
return getComputedStyle(document.head).getPropertyValue('--_slds-is-v2-enabled');
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
class LightningFormattedRichText extends LightningFormattedRichTextRenderer {
|
|
11
41
|
csrOnly = false;
|
|
12
42
|
get enableFormattedRichTextParts() {
|
|
13
43
|
return features.enableFormattedRichTextParts;
|
|
@@ -57,3 +87,225 @@ export default class LightningFormattedRichText extends LightningFormattedRichTe
|
|
|
57
87
|
}
|
|
58
88
|
}
|
|
59
89
|
}
|
|
90
|
+
class LightningFormattedRichTextConsolidated extends LightningShadowBaseClass {
|
|
91
|
+
static validationOptOut = ['class'];
|
|
92
|
+
static stylesheets = [nativeStyles];
|
|
93
|
+
rendered = false;
|
|
94
|
+
_value = '';
|
|
95
|
+
_disableLinkify = false;
|
|
96
|
+
connected = false;
|
|
97
|
+
richText = null;
|
|
98
|
+
rawTextIfSanitizerThrewError = null;
|
|
99
|
+
serverRenderedContent = null;
|
|
100
|
+
linkingRequired = false;
|
|
101
|
+
_themeKey = 'light';
|
|
102
|
+
_backgroundColor = null;
|
|
103
|
+
@api
|
|
104
|
+
get disableLinkify() {
|
|
105
|
+
return this._disableLinkify;
|
|
106
|
+
}
|
|
107
|
+
set disableLinkify(val) {
|
|
108
|
+
this._disableLinkify = normalizeBoolean(val);
|
|
109
|
+
this.renderRichText();
|
|
110
|
+
}
|
|
111
|
+
@api
|
|
112
|
+
get value() {
|
|
113
|
+
return this._value;
|
|
114
|
+
}
|
|
115
|
+
set value(val) {
|
|
116
|
+
this._value = val === undefined || val === null ? '' : String(val);
|
|
117
|
+
this.renderRichText();
|
|
118
|
+
}
|
|
119
|
+
render() {
|
|
120
|
+
return import.meta.env.SSR ? ssrTemplate : csrTemplate;
|
|
121
|
+
}
|
|
122
|
+
constructor() {
|
|
123
|
+
super();
|
|
124
|
+
if (features.enableAssertContext260) {
|
|
125
|
+
setContext(this);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
connectedCallback() {
|
|
129
|
+
super.connectedCallback();
|
|
130
|
+
this.classList.add('slds-rich-text-editor__output');
|
|
131
|
+
this.connected = true;
|
|
132
|
+
if (!import.meta.env.SSR) {
|
|
133
|
+
this.serverRenderedContent = this.container?.innerHTML;
|
|
134
|
+
if (this.serverRenderedContent) {
|
|
135
|
+
this.richText = this.serverRenderedContent;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
renderedCallback() {
|
|
140
|
+
if (this.linkingRequired) {
|
|
141
|
+
this.linkRichText();
|
|
142
|
+
}
|
|
143
|
+
if (!this.rendered) {
|
|
144
|
+
this.rendered = true;
|
|
145
|
+
if ((features.enableWcagRichTextLight || features.enableWcagRichTextDark) && sldsIsV2Enabled() && !import.meta.env.SSR) {
|
|
146
|
+
const isDark = window.getComputedStyle(this.template.host)?.colorScheme.includes('dark');
|
|
147
|
+
if (isDark) {
|
|
148
|
+
this._themeKey = 'dark';
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
this.renderRichText();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
renderRichText() {
|
|
155
|
+
if (features.enableAssertContext260) {
|
|
156
|
+
assertContext(this);
|
|
157
|
+
}
|
|
158
|
+
if (import.meta.env.SSR) {
|
|
159
|
+
this.richText = this.disableLinkify
|
|
160
|
+
? this.value
|
|
161
|
+
: linkTextNodesSSR(this.value);
|
|
162
|
+
}
|
|
163
|
+
if (this.rendered) {
|
|
164
|
+
try {
|
|
165
|
+
this.rawTextIfSanitizerThrewError = null;
|
|
166
|
+
if (this.value) {
|
|
167
|
+
let computedRichTextConfig = richTextConfig;
|
|
168
|
+
if (hasOnlyAllowedVideoIframes(this.value)) {
|
|
169
|
+
computedRichTextConfig = {
|
|
170
|
+
...richTextConfig,
|
|
171
|
+
ALLOWED_TAGS: richTextConfig.ALLOWED_TAGS.concat([
|
|
172
|
+
'iframe',
|
|
173
|
+
]),
|
|
174
|
+
ALLOWED_ATTR: richTextConfig.ALLOWED_ATTR.concat([
|
|
175
|
+
'allowfullscreen',
|
|
176
|
+
]),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
this.richText = sanitizeHTML(this.value, computedRichTextConfig);
|
|
180
|
+
this.linkingRequired = true;
|
|
181
|
+
if (sldsIsV2Enabled()) {
|
|
182
|
+
this.adjustColors();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
this.richText = this.value;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch (e) {
|
|
190
|
+
console.warn(e.message);
|
|
191
|
+
this.richText = null;
|
|
192
|
+
this.rawTextIfSanitizerThrewError = this.value;
|
|
193
|
+
}
|
|
194
|
+
if (!import.meta.env.SSR) {
|
|
195
|
+
const container = this.container;
|
|
196
|
+
if (this.rawTextIfSanitizerThrewError) {
|
|
197
|
+
const textNode = document.createTextNode(this.rawTextIfSanitizerThrewError);
|
|
198
|
+
while (container.hasChildNodes()) {
|
|
199
|
+
container.removeChild(container.lastChild);
|
|
200
|
+
}
|
|
201
|
+
container.appendChild(textNode);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
container.innerHTML = this.richText;
|
|
205
|
+
if (this.linkingRequired) {
|
|
206
|
+
this.linkRichText();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
disconnectedCallback() {
|
|
213
|
+
this.removeLinkClickListeners();
|
|
214
|
+
this.connected = false;
|
|
215
|
+
}
|
|
216
|
+
handleClick(event) {
|
|
217
|
+
const anchor = event.currentTarget;
|
|
218
|
+
if (anchor === null) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const target = anchor.target;
|
|
222
|
+
const url = anchor.href;
|
|
223
|
+
updateRawLinkInfo(this, { url, target }).then((linkInfo) => {
|
|
224
|
+
anchor.href = linkInfo.url;
|
|
225
|
+
linkInfo.dispatcher(event);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
linkRichText() {
|
|
229
|
+
if (!this.disableLinkify) {
|
|
230
|
+
linkTextNodes(this.container);
|
|
231
|
+
}
|
|
232
|
+
this.links.forEach((link) => {
|
|
233
|
+
link.addEventListener('click', this.handleClick.bind(this));
|
|
234
|
+
});
|
|
235
|
+
this.linkingRequired = false;
|
|
236
|
+
}
|
|
237
|
+
removeLinkClickListeners() {
|
|
238
|
+
this.links.forEach((link) => {
|
|
239
|
+
link.removeEventListener('click', this.handleClick.bind(this));
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
get links() {
|
|
243
|
+
return this.container ? [...this.container.querySelectorAll('a')] : [];
|
|
244
|
+
}
|
|
245
|
+
get container() {
|
|
246
|
+
return this.template.querySelector('span');
|
|
247
|
+
}
|
|
248
|
+
get backgroundColor() {
|
|
249
|
+
if (import.meta.env.SSR) {
|
|
250
|
+
this._backgroundColor = 'rgb(255, 255, 255)';
|
|
251
|
+
return this._backgroundColor;
|
|
252
|
+
}
|
|
253
|
+
if (!import.meta.env.SSR) {
|
|
254
|
+
if (this._backgroundColor === null) {
|
|
255
|
+
this._backgroundColor = window.getComputedStyle(this.template.host).backgroundColor;
|
|
256
|
+
if (this._backgroundColor === 'rgba(0, 0, 0, 0)') {
|
|
257
|
+
this.template.host.style.backgroundColor = 'inherit';
|
|
258
|
+
this._backgroundColor = window.getComputedStyle(this.template.host).backgroundColor;
|
|
259
|
+
this.template.host.style.removeProperty('background-color');
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return this._backgroundColor;
|
|
264
|
+
}
|
|
265
|
+
get backgroundColorRgb() {
|
|
266
|
+
const match = this.backgroundColor.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
|
|
267
|
+
if (!match) {
|
|
268
|
+
if (this._themeKey === 'light') {
|
|
269
|
+
return { r: 255, g: 255, b: 255 };
|
|
270
|
+
}
|
|
271
|
+
return { r: 0, g: 0, b: 0 };
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
r: parseInt(match[1], 10),
|
|
275
|
+
g: parseInt(match[2], 10),
|
|
276
|
+
b: parseInt(match[3], 10),
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
adjustColors() {
|
|
280
|
+
if (this._themeKey === 'light' && !features.enableWcagRichTextLight) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (this._themeKey === 'dark' && !features.enableWcagRichTextDark) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
const colorRgbRegex = new RegExp('(?<=<[^>]*style="[^"]*)color:\\s*rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)', 'g');
|
|
288
|
+
this.richText = this.richText.replace(colorRgbRegex, (_, rs, gs, bs) => {
|
|
289
|
+
const r = parseInt(rs, 10), g = parseInt(gs, 10), b = parseInt(bs, 10);
|
|
290
|
+
const shifted = ensureWcagCompliance(this.backgroundColorRgb, { r, g, b });
|
|
291
|
+
if ((shifted.r === 255 && shifted.g === 255 && shifted.b === 255)) {
|
|
292
|
+
return '';
|
|
293
|
+
}
|
|
294
|
+
if ((shifted.r === 0 && shifted.g === 0 && shifted.b === 0)) {
|
|
295
|
+
return '';
|
|
296
|
+
}
|
|
297
|
+
return `color:rgb(${shifted.r},${shifted.g},${shifted.b})`;
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
catch (e) {
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
let toExport;
|
|
305
|
+
if (features.enableFormattedRichTextNoNestedShadow) {
|
|
306
|
+
toExport = LightningFormattedRichTextConsolidated;
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
toExport = LightningFormattedRichText;
|
|
310
|
+
}
|
|
311
|
+
export default toExport;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<template lwc:if={rawTextIfSanitizerThrewError}>
|
|
3
|
+
<span part="formatted-rich-text">{rawTextIfSanitizerThrewError}</span>
|
|
4
|
+
</template>
|
|
5
|
+
<template lwc:else>
|
|
6
|
+
<span lwc:inner-html={richText} part="formatted-rich-text"></span>
|
|
7
|
+
</template>
|
|
8
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linkTextNodesSSR.d.ts","sourceRoot":"","sources":["../../../../../modules/lightning/formattedRichText/linkTextNodesSSR.ts"],"names":[],"mappings":"AAyBA,eAAO,MAAM,gBAAgB,SAAmB,OAAO,KAAG,MA4BzD,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import { urlRegexString, emailRegexString, tagRegexString, createHttpHref, createEmailHref, } from 'lightning/utils';
|
|
7
|
+
const linkRegex = new RegExp(`${tagRegexString}|${emailRegexString}|${urlRegexString}`, 'gi');
|
|
8
|
+
const createHttpLink = function (match) {
|
|
9
|
+
const href = createHttpHref(match);
|
|
10
|
+
return `<a href="${href}" target="_blank" rel="noopener">${match}</a>`;
|
|
11
|
+
};
|
|
12
|
+
const createEmailLink = function (match) {
|
|
13
|
+
const href = createEmailHref(match);
|
|
14
|
+
return `<a href="${href}">${match}</a>`;
|
|
15
|
+
};
|
|
16
|
+
export const linkTextNodesSSR = function (text) {
|
|
17
|
+
if (typeof text !== 'string') {
|
|
18
|
+
return '';
|
|
19
|
+
}
|
|
20
|
+
return text.replace(linkRegex, (match, tagMatch, emailMatch, hrefMatch) => {
|
|
21
|
+
if (tagMatch) {
|
|
22
|
+
return tagMatch;
|
|
23
|
+
}
|
|
24
|
+
else if (hrefMatch) {
|
|
25
|
+
const endsWithQuote = hrefMatch.endsWith('"');
|
|
26
|
+
let href = hrefMatch;
|
|
27
|
+
if (endsWithQuote) {
|
|
28
|
+
href = hrefMatch.slice(0, hrefMatch.lastIndexOf('"'));
|
|
29
|
+
}
|
|
30
|
+
return createHttpLink(href) + (endsWithQuote ? '"' : '');
|
|
31
|
+
}
|
|
32
|
+
else if (emailMatch) {
|
|
33
|
+
return createEmailLink(emailMatch);
|
|
34
|
+
}
|
|
35
|
+
return match;
|
|
36
|
+
});
|
|
37
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface RichTextConfig {
|
|
2
|
+
ALLOWED_TAGS: readonly string[];
|
|
3
|
+
ALLOWED_ATTR: readonly string[];
|
|
4
|
+
ALLOWED_URI_REGEXP: RegExp;
|
|
5
|
+
ALLOW_UNKNOWN_PROTOCOLS: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare const richTextConfig: Readonly<RichTextConfig>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=richTextConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"richTextConfig.d.ts","sourceRoot":"","sources":["../../../../../modules/lightning/formattedRichText/richTextConfig.ts"],"names":[],"mappings":"AACA,UAAU,cAAc;IACpB,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,uBAAuB,EAAE,OAAO,CAAC;CACpC;AA4KD,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,cAAc,CAKlD,CAAC"}
|