lightning-base-components 1.16.3-alpha → 1.16.5-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/README.md +7 -0
- package/metadata/raptor.json +110 -0
- package/package.json +59 -2
- package/scopedImports/@salesforce-label-LightningForm.cancel.js +1 -0
- package/scopedImports/@salesforce-label-LightningForm.closeError.js +1 -0
- package/scopedImports/@salesforce-label-LightningForm.edit.js +1 -0
- package/scopedImports/@salesforce-label-LightningForm.editErrorHelp.js +1 -0
- package/scopedImports/@salesforce-label-LightningForm.error.js +1 -0
- package/scopedImports/@salesforce-label-LightningForm.errorPopoverHeading.js +1 -0
- package/scopedImports/@salesforce-label-LightningForm.preview.js +1 -0
- package/scopedImports/@salesforce-label-LightningForm.previewHeader.js +1 -0
- package/scopedImports/@salesforce-label-LightningForm.reload.js +1 -0
- package/scopedImports/@salesforce-label-LightningForm.save.js +1 -0
- package/scopedImports/@salesforce-label-LightningForm.saveFieldErrorSummary.js +1 -0
- package/scopedImports/@salesforce-label-LightningForm.undo.js +1 -0
- package/scopedImports/@salesforce-label-LightningLookup.messageWhenMissingInformation.js +1 -0
- package/src/lightning/button/__docs__/button.md +13 -0
- package/src/lightning/button/button.slds.css +155 -11
- package/src/lightning/buttonGroup/button-group.slds.css +35 -59
- package/src/lightning/buttonIcon/button-icon.slds.css +287 -122
- package/src/lightning/buttonIconStateful/button-icon-stateful.slds.css +224 -39
- package/src/lightning/buttonStateful/button-stateful.slds.css +3269 -0
- package/src/lightning/card/card.slds.css +50 -0
- package/src/lightning/colorPickerCustom/color-picker-custom.slds.css +180 -364
- package/src/lightning/colorPickerPanel/color-picker-panel.slds.css +46 -413
- package/src/lightning/datatable/datatable.js +2 -2
- package/src/lightning/datatable/rowSelection.js +21 -4
- package/src/lightning/datetimepicker/datetimepicker.html +1 -3
- package/src/lightning/datetimepicker/datetimepicker.js +5 -0
- package/src/lightning/fileDownload/__docs__/fileDownload.md +41 -0
- package/src/lightning/helptext/help-text.slds.css +184 -39
- package/src/lightning/icon/icon.slds.css +823 -3
- package/src/lightning/input/input-checkbox.slds.css +291 -32
- package/src/lightning/input/input-text.slds.css +70 -7
- package/src/lightning/inputAddress/__docs__/inputAddress.md +1 -1
- package/src/lightning/inputAddress/inputAddress.js +2 -1
- package/src/lightning/internationalizationLibrary/datetime/intlFormat.js +20 -2
- package/src/lightning/iso8601Utils/iso8601Utils.js +2 -3
- package/src/lightning/mediaUtils/__docs__/mediaUtils.md +87 -0
- package/src/lightning/mediaUtils/mediaUtils.js +321 -0
- package/src/lightning/mediaUtils/mediaUtils.js-meta.xml +6 -0
- package/src/lightning/modal/__docs__/migration.md +158 -0
- package/src/lightning/modal/__docs__/modal.md +414 -0
- package/src/lightning/modal/__examples__disabled/all/all.css +7 -0
- package/src/lightning/modal/__examples__disabled/all/all.html +9 -0
- package/src/lightning/modal/__examples__disabled/all/all.js +25 -0
- package/src/lightning/modal/__examples__disabled/allform/allform.css +7 -0
- package/src/lightning/modal/__examples__disabled/allform/allform.html +9 -0
- package/src/lightning/modal/__examples__disabled/allform/allform.js +49 -0
- package/src/lightning/modal/__examples__disabled/allmulti/allmulti.html +24 -0
- package/src/lightning/modal/__examples__disabled/allmulti/allmulti.js +12 -0
- package/src/lightning/modal/__examples__disabled/basic/basic.css +7 -0
- package/src/lightning/modal/__examples__disabled/basic/basic.html +9 -0
- package/src/lightning/modal/__examples__disabled/basic/basic.js +27 -0
- package/src/lightning/modal/__examples__disabled/demo/demo.html +15 -0
- package/src/lightning/modal/__examples__disabled/demo/demo.js +13 -0
- package/src/lightning/modal/__examples__disabled/demoall/demoall.html +26 -0
- package/src/lightning/modal/__examples__disabled/demoall/demoall.js +13 -0
- package/src/lightning/modal/__examples__disabled/demoallform/demoallform.css +3 -0
- package/src/lightning/modal/__examples__disabled/demoallform/demoallform.html +146 -0
- package/src/lightning/modal/__examples__disabled/demoallform/demoallform.js +240 -0
- package/src/lightning/modal/__examples__disabled/demofootless/demofootless.html +17 -0
- package/src/lightning/modal/__examples__disabled/demofootless/demofootless.js +11 -0
- package/src/lightning/modal/__examples__disabled/demoheadless/demoheadless.html +20 -0
- package/src/lightning/modal/__examples__disabled/demoheadless/demoheadless.js +12 -0
- package/src/lightning/modal/__examples__disabled/footless/footless.css +7 -0
- package/src/lightning/modal/__examples__disabled/footless/footless.html +9 -0
- package/src/lightning/modal/__examples__disabled/footless/footless.js +19 -0
- package/src/lightning/modal/__examples__disabled/headless/headless.css +7 -0
- package/src/lightning/modal/__examples__disabled/headless/headless.html +9 -0
- package/src/lightning/modal/__examples__disabled/headless/headless.js +27 -0
- package/src/lightning/modal/modal.html +3 -0
- package/src/lightning/modal/modal.js +93 -0
- package/src/lightning/modal/modal.js-meta.xml +6 -0
- package/src/lightning/modalBody/__docs__/modalBody.md +61 -0
- package/src/lightning/modalBody/modalBody.html +13 -0
- package/src/lightning/modalBody/modalBody.js +203 -0
- package/src/lightning/modalBody/modalBody.js-meta.xml +6 -0
- package/src/lightning/modalFooter/__docs__/modalFooter.md +72 -0
- package/src/lightning/modalFooter/modalFooter.html +8 -0
- package/src/lightning/modalFooter/modalFooter.js +161 -0
- package/src/lightning/modalFooter/modalFooter.js-meta.xml +6 -0
- package/src/lightning/modalHeader/__docs__/modalHeader.md +64 -0
- package/src/lightning/modalHeader/modalHeader.html +16 -0
- package/src/lightning/modalHeader/modalHeader.js +204 -0
- package/src/lightning/modalHeader/modalHeader.js-meta.xml +6 -0
- package/src/lightning/primitiveBubble/tooltip.slds.css +45 -1
- package/src/lightning/primitiveCellFactory/primitiveCellFactory.js +4 -12
- package/src/lightning/primitiveColorpickerButton/color-picker-button.slds.css +2994 -319
- package/src/lightning/primitiveDatatableIeditPanel/primitiveDatatableIeditPanel.html +14 -11
- package/src/lightning/primitiveDatatableIeditPanel/primitiveDatatableIeditPanel.js +1 -0
- package/src/lightning/primitiveIcon/icon.slds.css +823 -3
- package/src/lightning/progressStep/base.html +1 -0
- package/src/lightning/progressStep/path.html +1 -1
- package/src/lightning/radioGroup/input-radio-group.slds.css +168 -379
- package/src/lightning/spinner/spinner.slds.css +8 -2
- package/src/lightning/timepicker/timepicker.html +1 -4
- package/src/lightning/timepicker/timepicker.js +9 -5
- package/src/lightning/treeGrid/treeGrid.js +66 -1
- package/src/lightning/utilsPrivate/linkUtils.js +1 -1
- package/src/lightning/formattedAddress/__component__/formattedAddress.spec.js +0 -61
- package/src/lightning/formattedAddress/__component__/formattedAddressDisabled.spec.js +0 -20
- package/src/lightning/formattedAddress/__component__/x/basic/basic.html +0 -10
- package/src/lightning/formattedAddress/__component__/x/basic/basic.js +0 -17
- package/src/lightning/input/__component__/inputCheckbox.spec.js +0 -60
- package/src/lightning/input/__component__/inputDateTimePicker.spec.js +0 -60
- package/src/lightning/input/__component__/inputNumber.spec.js +0 -75
- package/src/lightning/input/__component__/inputSelection.spec.js +0 -83
- package/src/lightning/input/__component__/x/tall/tall.css +0 -5
- package/src/lightning/input/__component__/x/tall/tall.html +0 -5
- package/src/lightning/input/__component__/x/tall/tall.js +0 -7
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
The `mediaUtils` library contains utility functions that can be used by an LWC developer to process media files. The following functions are contained in the `mediaUtils` library:
|
|
2
|
+
|
|
3
|
+
## processImage
|
|
4
|
+
|
|
5
|
+
You can use `processImage` function to resize and compress image files. To use this function, simply import it in your LWC first:
|
|
6
|
+
```
|
|
7
|
+
import { processImage } from 'lightning/mediaUtils';
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
You can then call this function by passing in an input image and a set of options to be used to process the input image, as further described below. It will return a promise that will resolve to a `Blob` object containing the output image data.
|
|
11
|
+
|
|
12
|
+
#### Parameters
|
|
13
|
+
|
|
14
|
+
* `input`: Defines the input image, which can either be a `File` or `Blob` object
|
|
15
|
+
* `options`: An object that defines the options to be used when processing the input image. It is an optional parameter containing a number of flags. If this parameter or any of its flags are omitted, default values will be used as further described below.
|
|
16
|
+
* `resizeMode`: A string that determines how the image will be resized. It can contain one of the below values
|
|
17
|
+
* `fill`: This is default. The image will be resized to fill the target dimension. If necessary, the image will be stretched or squished to fit.
|
|
18
|
+
* `contain`: The image keeps its aspect ratio but will be resized to fit within the target dimension.
|
|
19
|
+
* `none`: The image will not be resized and will retain its original dimension.
|
|
20
|
+
* `resizeStrategy`: A string that determines how to resize the image. If `resizeMode` is set to `none` this flag will be ignored.
|
|
21
|
+
* `reduce`: Only resize if the image is larger than the target size (smaller images won't be resized).
|
|
22
|
+
* `enlarge`: Only resize if the image is smaller than the target size (larger images won't be resized).
|
|
23
|
+
* `always`: This is default. Always resize the image to the target size regardless of the original image dimensions.
|
|
24
|
+
* `targetWidth`: The target width when resizing an image. If omitted, defaults to the original image width. If `resizeMode` is set to `none` this flag will be ignored.
|
|
25
|
+
* `targetHeight`: The target height when resizing an image. If omitted, defaults to the original image height. If `resizeMode` is set to `none` this flag will be ignored.
|
|
26
|
+
* `compressionQuality`: A number between 0-1 that determines the compression quality. If omitted then the browser/webview picks a default value as it sees fit. Note that this parameter will be considered as a suggested compression quality, however the browser/webview may choose to override this value if it deems it necessary. For example if the value is larger than 1 or if it is considered to be too small by the browser/webview, then it will override the value to something that it deems more appropriate.
|
|
27
|
+
* `imageSmoothingEnabled`: A boolean that determines whether scaled images are smoothed or not. Defaults to `true`.
|
|
28
|
+
* `preserveTransparency`: A boolean that determines whether the transparency info of the input image (if any) should be preserved or not. Defaults to `true`. If the input image is a GIF/PNG and this flag is set to `true` the output image will be a PNG. For all other cases the output will be a JPEG.
|
|
29
|
+
* `backgroundColor`: A string that defines a CSS color as described [here](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). Defaults to `white`. When `preserveTransparency` is set to `false`, the output image will have its background set to this color before the input image is resized and drawn on top.
|
|
30
|
+
|
|
31
|
+
#### Example
|
|
32
|
+
|
|
33
|
+
As an example, consider the scenario where you would like to upload files to a Salesforce org using a [`lightning-input`](bundle/lightning-input/documentation) component. In your HTML code, you may have:
|
|
34
|
+
```
|
|
35
|
+
.
|
|
36
|
+
.
|
|
37
|
+
.
|
|
38
|
+
<lightning-input
|
|
39
|
+
type="file"
|
|
40
|
+
label="Select Files to Upload"
|
|
41
|
+
accept="image/*"
|
|
42
|
+
multiple
|
|
43
|
+
onchange={handleFilesSelected}>
|
|
44
|
+
</lightning-input>
|
|
45
|
+
.
|
|
46
|
+
.
|
|
47
|
+
.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
In your Javascript file, you can now use `processImage` from `mediaUtils`, for example to reduce image sizes and hence reduce the bandwidth used to upload the images, as illustrated below:
|
|
51
|
+
```
|
|
52
|
+
import { processImage } from 'lightning/mediaUtils';
|
|
53
|
+
.
|
|
54
|
+
.
|
|
55
|
+
.
|
|
56
|
+
async handleFilesSelected(event) {
|
|
57
|
+
try {
|
|
58
|
+
// Using the below options we resize images to a maximum of 2048x2048 pixels
|
|
59
|
+
// while containing their aspect ratio. By setting 'resizeStrategy' to 'reduce'
|
|
60
|
+
// we ensure that only images that have either width or height larger than
|
|
61
|
+
// 2048 pixels will be resized. Moreover, we've chosen not to preserve transparency
|
|
62
|
+
// in the input images and instead convert transparent pixels to white. Lastly,
|
|
63
|
+
// the images will be compressed with a 75% compression quality to reduce their byte size.
|
|
64
|
+
let options = {
|
|
65
|
+
resizeMode: 'contain',
|
|
66
|
+
resizeStrategy: 'reduce',
|
|
67
|
+
targetWidth: 2048,
|
|
68
|
+
targetHeight: 2048,
|
|
69
|
+
compressionQuality: 0.75,
|
|
70
|
+
imageSmoothingEnabled: true,
|
|
71
|
+
preserveTransparency: false,
|
|
72
|
+
backgroundColor: 'white'
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
for (const file of event.target.files) {
|
|
76
|
+
let blob = await processImage(file, options);
|
|
77
|
+
// here we can upload the data contained in the blob that is returned by processImage
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error("ERROR: ", error)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
.
|
|
85
|
+
.
|
|
86
|
+
.
|
|
87
|
+
```
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Given an input image (as a file handle or a blob) this method will resize
|
|
3
|
+
* and compress the image according to the specified options, and return a Blob
|
|
4
|
+
* object containing the resulting image data.
|
|
5
|
+
*
|
|
6
|
+
* @param {(File|Blob)} input the input image
|
|
7
|
+
* @param {Object} [options] the options to be used when processing the image.
|
|
8
|
+
* It is an optional parameter containing a number of flags. If this
|
|
9
|
+
* parameter or any of its flags are omitted, default values will be
|
|
10
|
+
* used as described below.
|
|
11
|
+
*
|
|
12
|
+
* resizeMode: A string that determines how the image will be resized.
|
|
13
|
+
* fill: This is default. The image will be resized to fill the target dimension.
|
|
14
|
+
* If necessary, the image will be stretched or squished to fit.
|
|
15
|
+
* contain: The image keeps its aspect ratio but will be resized to fit within the target dimension.
|
|
16
|
+
* none: The image will not be resized and will retain its original dimension.
|
|
17
|
+
*
|
|
18
|
+
* resizeStrategy: A string that determines how to resize the image. If resizeMode is set to 'none' this flag will be ignored.
|
|
19
|
+
* reduce: Only resize if the image is larger than the target size (smaller images won't be resized)
|
|
20
|
+
* enlarge: Only resize if the image is smaller than the target size (larger images won't be resized)
|
|
21
|
+
* always: This is default. Always resize the image to the target size regardless of the original image dimensions.
|
|
22
|
+
*
|
|
23
|
+
* targetWidth: The target width when resizing an image. If omitted, defaults to the original image width.
|
|
24
|
+
* If resizeMode is set to 'none' this flag will be ignored.
|
|
25
|
+
*
|
|
26
|
+
* targetHeight: The target height when resizing an image. If omitted, defaults to the original image height.
|
|
27
|
+
* If resizeMode is set to 'none' this flag will be ignored.
|
|
28
|
+
*
|
|
29
|
+
* compressionQuality: A number between 0-1 that determines the compression quality. If omitted then the browser/webview picks
|
|
30
|
+
* a default value as it sees fit. Note that this parameter will be considered as a suggested/desired
|
|
31
|
+
* compression quality however the browser/webview may choose to override this value if it deems it necessary.
|
|
32
|
+
* For example if the value is larger than 1 or if it is considered to be too small by the browser/webview,
|
|
33
|
+
* then it will override the value to something that it deems more appropriate.
|
|
34
|
+
*
|
|
35
|
+
* imageSmoothingEnabled: A boolean that determines whether scaled images are smoothed or not. Defaults to true.
|
|
36
|
+
*
|
|
37
|
+
* preserveTransparency: A boolean that determines whether the transparency info of the input image (if any) should
|
|
38
|
+
* be preserved or not. Defaults to true. If the input image is a GIF/PNG and this flag is set to true
|
|
39
|
+
* the output image will be a PNG. For all other cases the output will be a JPEG.
|
|
40
|
+
*
|
|
41
|
+
* backgroundColor: A string that defines a CSS color as described here: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
|
|
42
|
+
* Defaults to white. When preserveTransparency is set to false, the output image will have its background set to
|
|
43
|
+
* this color before the input image is resized and drawn on top.
|
|
44
|
+
*
|
|
45
|
+
* @returns {Promise} a promise that resolves to a Blob object containing the output image data.
|
|
46
|
+
*/
|
|
47
|
+
export function processImage(input, options = null) {
|
|
48
|
+
return readInputData(input)
|
|
49
|
+
.then((dataURL) => loadInputDataIntoImage(dataURL))
|
|
50
|
+
.then((image) => resizeAndCompressImage(image, input.type, options));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function readInputData(input) {
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
const reader = new FileReader();
|
|
56
|
+
reader.onloadend = (event) => {
|
|
57
|
+
resolve(event.target.result);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
reader.onerror = () => {
|
|
61
|
+
reject(new Error('Unable to read the input data.'));
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
reader.readAsDataURL(input);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
reject(new Error('Unable to read the input data.'));
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function loadInputDataIntoImage(dataUrl) {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
let image = new Image();
|
|
75
|
+
|
|
76
|
+
image.onload = function () {
|
|
77
|
+
resolve(image);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
image.onerror = function () {
|
|
81
|
+
reject(new Error('Unable to load the input as an image.'));
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
image.src = dataUrl;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function getResizeMode(defaultValue, options) {
|
|
89
|
+
let resizeMode = defaultValue;
|
|
90
|
+
if (options && options.resizeMode) {
|
|
91
|
+
resizeMode = `${options.resizeMode}`.toLowerCase().trim();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (
|
|
95
|
+
resizeMode === 'fill' ||
|
|
96
|
+
resizeMode === 'contain' ||
|
|
97
|
+
resizeMode === 'none'
|
|
98
|
+
) {
|
|
99
|
+
return resizeMode;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
throw new Error('Invalid parameter value for resizeMode.');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function getResizeStrategy(defaultValue, options) {
|
|
106
|
+
let resizeStrategy = defaultValue;
|
|
107
|
+
if (options && options.resizeStrategy) {
|
|
108
|
+
resizeStrategy = `${options.resizeStrategy}`.toLowerCase().trim();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (
|
|
112
|
+
resizeStrategy === 'reduce' ||
|
|
113
|
+
resizeStrategy === 'enlarge' ||
|
|
114
|
+
resizeStrategy === 'always'
|
|
115
|
+
) {
|
|
116
|
+
return resizeStrategy;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
throw new Error('Invalid parameter value for resizeStrategy.');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function getTargetWidth(defaultValue, options) {
|
|
123
|
+
let targetWidth = defaultValue;
|
|
124
|
+
|
|
125
|
+
if (options && options.targetWidth) {
|
|
126
|
+
targetWidth = parseFloat(options.targetWidth);
|
|
127
|
+
if (isNaN(targetWidth)) {
|
|
128
|
+
throw new Error('Invalid parameter value for targetWidth.');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return Math.round(targetWidth);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getTargetHeight(defaultValue, options) {
|
|
136
|
+
let targetHeight = defaultValue;
|
|
137
|
+
|
|
138
|
+
if (options && options.targetHeight) {
|
|
139
|
+
targetHeight = parseFloat(options.targetHeight);
|
|
140
|
+
if (isNaN(targetHeight)) {
|
|
141
|
+
throw new Error('Invalid parameter value for targetHeight.');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return Math.round(targetHeight);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function getCompressionQuality(defaultValue, options) {
|
|
149
|
+
let compressionQuality = defaultValue;
|
|
150
|
+
|
|
151
|
+
if (options && options.compressionQuality) {
|
|
152
|
+
compressionQuality = parseFloat(options.compressionQuality);
|
|
153
|
+
if (isNaN(compressionQuality)) {
|
|
154
|
+
throw new Error('Invalid parameter value for compressionQuality.');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return compressionQuality;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function getPreserveTransparency(defaultValue, options) {
|
|
162
|
+
if (
|
|
163
|
+
options &&
|
|
164
|
+
options.preserveTransparency !== undefined &&
|
|
165
|
+
options.preserveTransparency !== null
|
|
166
|
+
) {
|
|
167
|
+
let strVal = `${options.preserveTransparency}`.toLowerCase().trim();
|
|
168
|
+
|
|
169
|
+
if (strVal === `true`) {
|
|
170
|
+
return true;
|
|
171
|
+
} else if (strVal === `false`) {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
throw new Error('Invalid parameter value for preserveTransparency.');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return defaultValue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function getImageSmoothingEnabled(defaultValue, options) {
|
|
182
|
+
if (
|
|
183
|
+
options &&
|
|
184
|
+
options.imageSmoothingEnabled !== undefined &&
|
|
185
|
+
options.imageSmoothingEnabled !== null
|
|
186
|
+
) {
|
|
187
|
+
let strVal = `${options.imageSmoothingEnabled}`.toLowerCase().trim();
|
|
188
|
+
|
|
189
|
+
if (strVal === `true`) {
|
|
190
|
+
return true;
|
|
191
|
+
} else if (strVal === `false`) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
throw new Error('Invalid parameter value for imageSmoothingEnabled.');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return defaultValue;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function getBackgroundColor(defaultValue, options) {
|
|
202
|
+
if (options && options.backgroundColor) {
|
|
203
|
+
let strVal = `${options.backgroundColor}`;
|
|
204
|
+
if (CSS.supports('color', strVal)) {
|
|
205
|
+
return strVal;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
throw new Error('Invalid parameter value for backgroundColor.');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return defaultValue;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function computeFinalDimensions(
|
|
215
|
+
originalWidth,
|
|
216
|
+
originalHeight,
|
|
217
|
+
targetWidth,
|
|
218
|
+
targetHeight,
|
|
219
|
+
resizeMode,
|
|
220
|
+
resizeStrategy
|
|
221
|
+
) {
|
|
222
|
+
let finalWidth = originalWidth;
|
|
223
|
+
let finalHeight = originalHeight;
|
|
224
|
+
|
|
225
|
+
// first check to see if we even need to resize at all
|
|
226
|
+
if (
|
|
227
|
+
resizeMode !== 'none' &&
|
|
228
|
+
(resizeStrategy === 'always' ||
|
|
229
|
+
(resizeStrategy === 'reduce' &&
|
|
230
|
+
(originalWidth > targetWidth ||
|
|
231
|
+
originalHeight > targetHeight)) ||
|
|
232
|
+
(resizeStrategy === 'enlarge' &&
|
|
233
|
+
(originalWidth < targetWidth || originalHeight < targetHeight)))
|
|
234
|
+
) {
|
|
235
|
+
// if resizing is needed then compute the final size
|
|
236
|
+
if (resizeMode === 'fill') {
|
|
237
|
+
finalWidth = targetWidth;
|
|
238
|
+
finalHeight = targetHeight;
|
|
239
|
+
} else if (resizeMode === 'contain') {
|
|
240
|
+
let ratio = Math.min(
|
|
241
|
+
targetWidth / originalWidth,
|
|
242
|
+
targetHeight / originalHeight
|
|
243
|
+
);
|
|
244
|
+
finalWidth = Math.round(originalWidth * ratio);
|
|
245
|
+
finalHeight = Math.round(originalHeight * ratio);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return { finalWidth, finalHeight };
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function resizeAndCompressImage(image, mimeType, options) {
|
|
253
|
+
return new Promise((resolve, reject) => {
|
|
254
|
+
let resizeMode;
|
|
255
|
+
let resizeStrategy;
|
|
256
|
+
let targetWidth;
|
|
257
|
+
let targetHeight;
|
|
258
|
+
let compressionQuality;
|
|
259
|
+
let imageSmoothingEnabled;
|
|
260
|
+
let preserveTransparency;
|
|
261
|
+
let backgroundColor;
|
|
262
|
+
|
|
263
|
+
// parse the options
|
|
264
|
+
try {
|
|
265
|
+
resizeMode = getResizeMode('fill', options);
|
|
266
|
+
resizeStrategy = getResizeStrategy('always', options);
|
|
267
|
+
targetWidth = getTargetWidth(image.width, options);
|
|
268
|
+
targetHeight = getTargetHeight(image.height, options);
|
|
269
|
+
compressionQuality = getCompressionQuality(null, options);
|
|
270
|
+
imageSmoothingEnabled = getImageSmoothingEnabled(true, options);
|
|
271
|
+
preserveTransparency = getPreserveTransparency(true, options);
|
|
272
|
+
backgroundColor = getBackgroundColor('white', options);
|
|
273
|
+
} catch (error) {
|
|
274
|
+
reject(error);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
let { finalWidth, finalHeight } = computeFinalDimensions(
|
|
279
|
+
image.width,
|
|
280
|
+
image.height,
|
|
281
|
+
targetWidth,
|
|
282
|
+
targetHeight,
|
|
283
|
+
resizeMode,
|
|
284
|
+
resizeStrategy
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
// Render the image into a canvas at the calculated final size
|
|
288
|
+
let canvas = document.createElement('canvas');
|
|
289
|
+
canvas.width = finalWidth;
|
|
290
|
+
canvas.height = finalHeight;
|
|
291
|
+
canvas.imageSmoothingEnabled = imageSmoothingEnabled;
|
|
292
|
+
|
|
293
|
+
let ctx = canvas.getContext('2d');
|
|
294
|
+
|
|
295
|
+
if (!preserveTransparency) {
|
|
296
|
+
ctx.fillStyle = backgroundColor;
|
|
297
|
+
ctx.fillRect(0, 0, finalWidth, finalHeight);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
ctx.drawImage(image, 0, 0, finalWidth, finalHeight);
|
|
301
|
+
|
|
302
|
+
let targetType = 'image/jpeg';
|
|
303
|
+
if (
|
|
304
|
+
preserveTransparency &&
|
|
305
|
+
(mimeType === 'image/gif' || mimeType === 'image/png')
|
|
306
|
+
) {
|
|
307
|
+
targetType = 'image/png';
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
let resultFunc = function (blob) {
|
|
311
|
+
resolve(blob);
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
// Compress at the specified compression quality and return the final result
|
|
315
|
+
if (compressionQuality) {
|
|
316
|
+
canvas.toBlob(resultFunc, targetType, compressionQuality);
|
|
317
|
+
} else {
|
|
318
|
+
canvas.toBlob(resultFunc, targetType);
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Migrating from Other Modal Solutions to LightningModal
|
|
2
|
+
|
|
3
|
+
This document is about migrating existing modal implementations.
|
|
4
|
+
|
|
5
|
+
## **Creating a new LWC-based modal implementation?**
|
|
6
|
+
* Starting in release 236, when utilizing LWC or Aura code, your team should use `LightningModal`
|
|
7
|
+
* Dive into the details here by [Creating a Modal Component](modal.md#creating-a-modal-component)
|
|
8
|
+
* Or, take a look at some of our [Modal Code Examples](modal.md#modal-code-examples).
|
|
9
|
+
|
|
10
|
+
## Why `LightningModal`?
|
|
11
|
+
|
|
12
|
+
`LightningModal` provides a full-featured, modern, accessible modal solution intended for on and off the Salesforce platform (see [intended timeline](#intended-timeline-for-modal-solutions)) _that will **replace all** previous Lightning modal solutions_. Whether you are starting a completely new modal implementation, or have an existing modal implementation using a prior Aura or LWC modal solution, `LightningModal` is your Salesforce modal migration path *forward*.
|
|
13
|
+
|
|
14
|
+
**Why migrate to `LightningModal`?** The `LightningModal` component has fewer limitations, is actively supported for use both on the Salesforce platform (starting in 236) and off Salesforce platform (likely, starting in 238), has SLDS blueprints and accessibility best practices built in, is performant, provides a scroll bar when your modal content is taller than the screen height, and is well-tested. Using `LightningModal` gives your team more time to implement your app's unique features instead of recreating modal functionality.
|
|
15
|
+
|
|
16
|
+
### Intended Timeline for Modal Solutions:
|
|
17
|
+
1. `LightningModal`:
|
|
18
|
+
* Availability:
|
|
19
|
+
* for new Salesforce internal projects in release 236 via `lwc-components-lightning` package
|
|
20
|
+
* for new Salesforce internal _and_ planned for external projects in release 238 via `lightning-base-components` (npmjs.com) package
|
|
21
|
+
* Ongoing Support:
|
|
22
|
+
* starting in release 236 for Salesforce internal teams
|
|
23
|
+
1. `lightning-dialog`:
|
|
24
|
+
* Availability:
|
|
25
|
+
* from 230, for all new modal projects, _within documented limited use cases_ (**not recommended in one.app**)
|
|
26
|
+
* **recommend** transitioning new Salesforce internal modal work to `LightningModal` starting in release 236
|
|
27
|
+
* Ongoing Support:
|
|
28
|
+
* for LWC modal projects, through end of 236, _within documented limited use cases_
|
|
29
|
+
* planned review for deprecation starting in 238
|
|
30
|
+
1. `ui:createPanel`:
|
|
31
|
+
* Availability:
|
|
32
|
+
* for Aura modal projects through end of 236
|
|
33
|
+
* **recommend** transitioning new Salesforce internal modal work to `LightningModal` starting in release 236
|
|
34
|
+
* Ongoing Support:
|
|
35
|
+
* through the end of release 236
|
|
36
|
+
* planned review for deprecation starting in 238
|
|
37
|
+
|
|
38
|
+
## Migrating Existing Modal Implementations
|
|
39
|
+
|
|
40
|
+
In this section, we’ll discuss migrating your existing modal implementation to `LightningModal` by showing the implementation differences in markup and APIs.
|
|
41
|
+
|
|
42
|
+
* **Migrating from custom modal implementations** using [SLDS modal blueprints](https://www.lightningdesignsystem.com/components/modals/)
|
|
43
|
+
* **Migrating from Aura** **`ui:createPanel`**
|
|
44
|
+
* **Migrating from** [**`lightning-dialog`**](https://git.soma.salesforce.com/aura/lightning-global/tree/master/ui-lightning-components/src/main/modules/lightning/dialog) (Salesforce internal only)
|
|
45
|
+
|
|
46
|
+
## Key Points For Modal Code Migration
|
|
47
|
+
|
|
48
|
+
* **There isn't a `<lightning-modal>` tag!** Instead, you extend `LightningModal`, and call `.open()` and `.close()` on your modal implementation. You'll never use the `<lightning-modal>` tag within your code.
|
|
49
|
+
* **In most implementations, you’ll have, at minimum, two components**:
|
|
50
|
+
1. your `CustomModal` modal component which defines behavior and is wrapped in the modal window
|
|
51
|
+
1. your application component that imports your `CustomModal` component and calls `.open({})` to open your modal
|
|
52
|
+
* **You don’t need to set the base SLDS modal CSS classes, or manage the accessibility of the modal itself.** Each of the modal helper components have the SLDS styling classes and accessibility built in. However, you're responsible for making sure your modal’s content has accessibility covered!
|
|
53
|
+
* **Currently, auto-focus on the first interactive element is performed automatically** once at the beginning of modal creation.
|
|
54
|
+
* **You only work directly with helper components named `lightning-modal-*`**. `LightningModal` is actually a collection of supporting components that create the underlying LWC-based modal features and functionality. When you extend `LightningModal`, you can use the modal content helper components like `lightning-modal-header` (optional), `lightning-modal-body`, and `lightning-modal-footer` (optional) to set up the content of the modal, and call `.open()` in the `onclick` event handler of your app page's button or link.
|
|
55
|
+
* **We’ve provided a few `LightningModal` code examples for use in our LWR-based `playground.html` file, or our deprecated `demo/app` code playground to get you started.** Each of these examples is based on an SLDS blueprint pattern, and can be found in the '`modal/__examples__`' folder. Review the [Modal Code Examples](modal.md#modal-code-examples) section.
|
|
56
|
+
|
|
57
|
+
## Migrating From a Custom Built Modal
|
|
58
|
+
|
|
59
|
+
If you have an existing LWC custom modal that implements the HTML and CSS from the [SLDS modal blueprint](https://www.lightningdesignsystem.com/components/modals/) and custom JavaScript behavior, you likely have your own custom api attributes and methods. We recommend the following:
|
|
60
|
+
|
|
61
|
+
* Start by looking at our [Modal Code Examples](modal.md#modal-code-examples) for comparison to your own implementation
|
|
62
|
+
* Each of these code examples can be viewed within the `modal/__examples__` folder
|
|
63
|
+
* Each can be previewed within our repo by editing the `playground.html` file (by running `yarn start`) or the deprecated `demo/app` and (running `yarn app:start`) utilizing the corresponding tag:
|
|
64
|
+
* `<modal-all>`
|
|
65
|
+
* `<modal-headless>`
|
|
66
|
+
* `<modal-footless>`
|
|
67
|
+
* Wrap the desired example tag within a `<template></template>` tag
|
|
68
|
+
* For modal blueprints and variants, review the [Modal and supported variants](modal.md#modal-and-supported-variants) section.
|
|
69
|
+
* `LightningModal` provides three helper components for header, body content, and footer sections. The `lightning-modal-body` is the only required component.
|
|
70
|
+
* You don’t need to worry about setting any of the base SLDS modal CSS classes. These are set for you. If you’d like to further style your modal, review the modal [Style Hooks](modal.md#style-hooks) section
|
|
71
|
+
* Review our `LightningModal` documentation:
|
|
72
|
+
* [Creating a Modal Component](modal.md#creating-a-modal-component)
|
|
73
|
+
* [Opening a Modal Instance](modal.md#opening-a-modal-instance)
|
|
74
|
+
* [About Modal Instances](modal.md#about-modal-instances)
|
|
75
|
+
* [Using the open() method](modal.md#using-the-open-method)
|
|
76
|
+
* [Using the close() method](modal.md#using-the-close-method)
|
|
77
|
+
* [Modal Code Examples](modal.md#modal-code-examples)
|
|
78
|
+
|
|
79
|
+
## Migrating From Aura Modals
|
|
80
|
+
|
|
81
|
+
If you are ready to move an existing modal implementation from Aura-based `ui:createPanel`, `ui:createModal`, or `ui:modal`, here are some key differences:
|
|
82
|
+
|
|
83
|
+
### Implementation differences
|
|
84
|
+
|
|
85
|
+
This section covers implementation differences between Aura modal solutions and `LightningModal`.
|
|
86
|
+
|
|
87
|
+
* When setting up the config before opening the modal, either pass your content via custom written `@api` (see [Using the open() method](modal.md#using-the-open-method)), or set it statically within your template (see the [Base Modal](modal.md#base-modal) HTML template example)
|
|
88
|
+
* If you don't want a header and title section, simply remove `lightning-modal-header`. You must then pass the required `label` value (for the accessible modal title) when opening the modal, for example `Modal.open({label: ‘Descriptive Modal Header’})`. See our [Headless Variant](modal.md#headless-variant) documentation, and [Modal Code Examples](modal.md#modal-code-examples). Same goes for the footer section, if you don’t want a footer, don't use `lightning-modal-footer`. Only `lightning-modal-body` is required.
|
|
89
|
+
* **For specific events and event listeners availability** within `createPanel` or `createModal`, for example, `onBeforeShow`, `onAfterShow`, `onCreate` or `onDestroy`, see our section on [About Modal Events](modal.md#about-modal-events).
|
|
90
|
+
* **Recommend:** create these as [custom events](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/events_create_dispatch)
|
|
91
|
+
* **If you want to get element reference,** for example, `linkElement.querySelector(‘[data-my-link]’)` within the content you’ve set within your modal, utilize data attributes. For this example, `<a href=“#” data-my-link>`, rather than relying on ID references, since these dynamically change in LWC).
|
|
92
|
+
* **Recommend:** See the note within the [ARIA attributes](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.create_components_accessibility_attributes) section.
|
|
93
|
+
* If you need to style aspects of your modal, you can apply CSS styles directly to your markup within the helper components.
|
|
94
|
+
* **Recommend:** See the example under [Directional Variant](modal.md#directional-variant) section.
|
|
95
|
+
* If you need to support a modal with a [directional variant](https://www.lightningdesignsystem.com/components/modals/#Directional), please review our [Directional Variant](modal.md#directional-variant) documentation.
|
|
96
|
+
|
|
97
|
+
### API differences
|
|
98
|
+
|
|
99
|
+
This section covers `@api` or attribute differences between Aura modal solutions and `LightningModal`.
|
|
100
|
+
|
|
101
|
+
#### Supported APIs
|
|
102
|
+
See [ui:modal](https://git.soma.salesforce.com/aura/aura/tree/master/aura-components/src/main/components/ui/modal)
|
|
103
|
+
|
|
104
|
+
* `title` attribute has been changed to the `label` attribute
|
|
105
|
+
* You set the `label` attribute on the `lightning-modal-header` helper component or in the case of a headless modal, when you open the modal, you would set the `label` attribute when opening the modal. For example: `Modal.open({ label: ‘Modal Descriptive Title’ })`
|
|
106
|
+
* `**LightningModal**` currently has only four official attributes A
|
|
107
|
+
* `size` - to set the width of the modal
|
|
108
|
+
* `label` - to set the modal heading
|
|
109
|
+
* `description` - to set the modal's `aria-description` or `aria-describedby` property
|
|
110
|
+
* `disableClose` - a boolean value to toggle usability of the Close button
|
|
111
|
+
* **Modal enables you to pass in your own `@api`** to interact and wire-up your own desired functionality within the modal (interactive elements like buttons, inputs, etc) by setting key value pairs on `.open({ options: [], buttons: [] })`, for example
|
|
112
|
+
* **Modal enables setting event listeners** that may be listened to by the base LWC component (that opens the modal) by passing custom events into your LightningModal implementation by setting keys startin with `on` `.open({ onselect: () => { /* do stuff */ } })`, setting the corresponding listener on the outer templates markup, and firing the custom event `select` from within the modal. See [modal.md](modal.md#about-modal-events) for examples
|
|
113
|
+
|
|
114
|
+
#### Unsupported API
|
|
115
|
+
See [ui:modal](https://git.soma.salesforce.com/aura/aura/tree/master/aura-components/src/main/components/ui/modal)
|
|
116
|
+
|
|
117
|
+
* **Animation related:** `animation`, `closeAnimation`, `useTransition`
|
|
118
|
+
* **recommend:** remove for now, future support may be available
|
|
119
|
+
* **Close button related:** `closeAction`**,** `showCloseButton`, `closeButton`, `closeDialogLabel`
|
|
120
|
+
* **notes:** no recommendations, these properties cannot be altered currently
|
|
121
|
+
* **Setting CSS Classes related:** `class,` `modalClass`, `headerClass`, `bodyClass`, `footerClass`
|
|
122
|
+
* **recommend:** apply your required styles within the modal component. See the [Directional Variant](modal.md#directional-variant) section for an example.
|
|
123
|
+
* **Accessibility related:** trapFocus, ariaLabelleBy, ariaDescribedBy
|
|
124
|
+
* **notes:** the first two properties are managed for you, ariaDescribedBy feature will be added later
|
|
125
|
+
* **autoFocus** - **note:** this is currently happens automatically at open time
|
|
126
|
+
* **icon** - **note:** no current ability set an icon
|
|
127
|
+
* **titleDisplay** - note: not supported
|
|
128
|
+
* **Recommend:** If you don’t want a header section or title in the modal, set a descriptive label attribute value for accessibility, and don’t add the `lightning-modal-header` component.
|
|
129
|
+
|
|
130
|
+
## Migrating From `lightning-dialog`
|
|
131
|
+
|
|
132
|
+
If you need to move an existing `lightning-dialog` implementation, you’ll need to consider these changes:
|
|
133
|
+
|
|
134
|
+
### Implementation Differences
|
|
135
|
+
|
|
136
|
+
This section covers implementation differences between `lightning-dialog` and `LightningModal`.
|
|
137
|
+
|
|
138
|
+
* `lightning-dialog` and `LightningModal` are fairly similar in terms of their template code implementation, with the exception that the different sections within the `LightningModal` have separate helper components.
|
|
139
|
+
* `lightning-dialog` is inline with your application limiting the z-index to the context where it is placed. `LightningModal` is created outside and above all elements with a managed z-index to not conflict with other overlays.
|
|
140
|
+
* You implement `lightning-dialog` via its tag in your markup. There is no equivalent `lightning-modal`, instead you extend `LightningModal`. For example:
|
|
141
|
+
* import **LightningModal** from 'lightning/modal';
|
|
142
|
+
* export default class **MyModal _extends_ LightningModal** { /* your code */ }
|
|
143
|
+
* `LightningModal` makes use of extending `LightningOverlay` which provides `.open()` and `.close()` methods, whereas `lightning-dialog` worked from LWC declarative implementation without extends. See our sample [Modal Code Examples](modal.md#modal-code-examples) documentation.
|
|
144
|
+
* Custom events - `LightningModal` only provides a `privateclose` event. `<lightning-dialog>` provided events `cancel` or `close` events.
|
|
145
|
+
|
|
146
|
+
### API and Method Differences
|
|
147
|
+
|
|
148
|
+
This section covers api or attribute and method differences between `lightning-dialog` and `LightningModal`.
|
|
149
|
+
|
|
150
|
+
* The API to indicate the modal's title or heading is renamed from `header` to `label`
|
|
151
|
+
* From: `<lightning-dialog header=“Descriptive Modal Heading”>`
|
|
152
|
+
* To:
|
|
153
|
+
1. `<lightning-modal-header label=“Descriptive Modal Heading”>` for modal with header section
|
|
154
|
+
1. Or, `MyModal.open({ label: ‘Descriptive Modal Heading’ })` for headless modal
|
|
155
|
+
* The method to show the modal is renamed
|
|
156
|
+
* From: `.showModal()`
|
|
157
|
+
* To: `.open()`
|
|
158
|
+
* `.open()` is inherited when you extend LightningModal.
|