├── LICENSE
├── README.md
└── src
└── aura
├── lightningCore
├── lightningCore.cmp
├── lightningCore.cmp-meta.xml
├── lightningCoreController.js
└── lightningCoreHelper.js
└── lightningCoreModule
├── lightningCoreModule.intf
└── lightningCoreModule.intf-meta.xml
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 4an70m
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # lightning-core
2 |
3 | ## What is this?
4 | lightning-core is a small library to use features of lightning in es6 style. It allows to eliminate the most common boilerplate code such as:
5 | - Toasts
6 | - Server calling
7 | - Creating components, modals
8 | - Downloading files
9 | - Working with local/session storage
10 | - Working with libraries (overlay, navigation, notification)
11 | - Extension Modules
12 |
13 | ## Why do I need this?
14 | Lots of projects I've ran into has either custom library-like component or JS resource file or they are stuck with each component defining a "new solution" for each pain-point, e.g. method for creating a Toast in every single component.
15 | The purpose of this library component is to stop this madness.
16 |
17 | ## Lightning?
18 | Lightnig is still something viable for a new project or for refactoring an old one and, I guess, will be for a couple more years, considering how many beautiful and functional things are already created for it.
19 |
20 | ## How to use?
21 | The library is really simple and intuitive to use. You add it onse in the top level component, it immutably writes itself into the window object and after that you can use it anywhere. The variable in the window object is setted only once in a singleton manner, so don't worry if you reference the library somwhere else in your component hierarchy - the top-level lightning-core component will initiallize first.
22 |
23 | Start with:
24 | ```html
25 |
26 |
27 |
28 |
29 | ```
30 | And now you can use it anywhere in your project!
31 |
32 |
33 | ## Architecture
34 | Everything in this library is a class. Each use-case of lightning-core is represented by a single class. The classes are combined into a variable, that serves as an export list and then attached to a window object as a namespace.
35 | E.g.
36 | ```javascript
37 | new window.core.Toast().fire();
38 | ```
39 | Example shows how you can access one of the exported classes - `Toast` and fire it. But you may also skip window in your reference to the class:
40 | ```javascript
41 | new core.Toast().fire();
42 | ```
43 |
44 | ### Use-cases: Toast
45 | There are a number of classes, which are designed to ease the way of creating Toasts:
46 |
47 | ```javascript
48 | /*base toast*/
49 | new core.Toast({
50 | /*standard toast params*/
51 | }).fire();
52 |
53 | /*defaults mode to dismissable, time to 4s*/
54 | new core.ToastQuick(type, title, message).fire();
55 |
56 | /*defaults mode to dismissable, time to 8s*/
57 | new core.ToastLong(type, title, message).fire();
58 |
59 |
60 | /*defaults mode to dismissable, type to success, title to Success!, time to 4s*/
61 | new core.ToastQuickSuccess(type, title, message).fire();
62 |
63 | /*defaults mode to dismissable, type to error, title to Something went wrong!, time to 4s*/
64 | new core.ToastQuickError(type, title, message).fire();
65 |
66 | /*defaults mode to dismissable, type to success, title to Success!, time to 8s*/
67 | new core.ToastLongSuccess(type, title, message).fire();
68 |
69 | /*defaults mode to dismissable, type to error, title to Something went wrong!, time to 8s*/
70 | new core.ToastLongError(type, title, message).fire();
71 | ```
72 | The classes can be configured for each project independenlty and updated with default time, message, etc.
73 |
74 | ### Use-cases: Server calling
75 | Several classes are dedicated to perform server calls with or without Promises as well as providing a way to parse error messages and autohandle errors.
76 |
77 | ```javascript
78 | /*designed to perform promise-like async operations of calling the server, but that can be used with @AuraEnabled(cacheable=true). However, this cannot be chained like an actual promise*/
79 | new core.ServerAction(component, actionName, params).execute()
80 | .then(result => {
81 | //result handling
82 | })
83 | .catch(error => {
84 | //error handling
85 | })
86 | .finally(() => {
87 | //some operation regardles of result
88 | })
89 |
90 | /*default promise, that is designed to perform server calling, but that doesnt require to be wrapped with $A.getCallback(...)*/
91 | new core.ServerActionPromise(component, actionName, params).execute()
92 | .then(result => {
93 | //result handling
94 | })
95 | .catch(error => {
96 | //error handling
97 | })
98 | .finally(() => {
99 | //some operation regardles of result
100 | })
101 |
102 | /*...Handled classes are similar to the same classes without Handled, except for they automatically parse an error from response and show a toast with core.ToastLongError class*/
103 | new core.ServerActionHandled()/*...*/
104 | new core.ServerActionPromiseHandled()/*...*/
105 | ```
106 |
107 | ### Use-cases: Components
108 | With lightning-core dynamically creating new components is designed to be intuitive and easy - no need to check documentation every time you need a dynamically generated component.
109 |
110 | ```javascript
111 | /*each dynamic component is represented with a single instance of core.Component class and onyl requires a name and desired attributes. .create() method returns a Promsie*/
112 | new core.Component(name, params).create()
113 | .then((component) => {
114 | /*do smth with newly generated component*/
115 | })
116 | .catch((error) => {
117 | /*handle errors*/
118 | });
119 |
120 | /*there's also a way to create component in bulk*/
121 | new core.Components()
122 | .addComponent(new core.Component(name, params))
123 | .addComponent(new core.Component(name, params))
124 | .addComponent(new core.Component(name, params))
125 | .create()
126 | .then((components) => {
127 | /*do smth with newly generated components*/
128 | })
129 | .catch((error) => {
130 | /*handle errors*/
131 | });
132 | ```
133 |
134 | ### Use-cases: Modals
135 | Creating modals is usually also a drag. With lightning-core creating modals is as easy as creating component. But! Modals are created based on the `lightning:overlayLibrary`. To use this library in lightning-core component you need to include this library into your instance of the library - simply include a library instance into the markup between opening and closing tags of ``:
136 |
137 | ```html
138 |
139 |
140 |
141 |
142 |
143 |
144 | ```
145 | After that, you can use any supported functionality for the library, for example - Modal creation.
146 | ```javascript
147 | new core.Modal()
148 | .setBody(new Component(name, params))
149 | .setFooter(new Component(name, params))
150 | .show()
151 | .then((overlay) => {
152 | /*success callback*/
153 | })
154 | .catch((error) => {
155 | /*error callback*/
156 | });
157 | ```
158 |
159 | ### Use-cases: Files
160 | There's a small class for downloading files, instantiated from base64 or Blob.
161 | ```javascript
162 | /*new file*/
163 | new core.File(base64Data);
164 | new core.File(blobData);
165 |
166 | /*converting file*/
167 | new core.File(blobData).toBase64();
168 | new core.File(base64Data).toBlob();
169 |
170 | /*downlaoding*/
171 | new core.File(base64Data).download(filename);
172 | new core.File(blobData).download(filename);
173 | ```
174 |
175 | ### Use-cases: Local/Session Storage
176 | There's a small class for working with local or session storage.
177 | ```javascript
178 | const storage = new core.LocalStorage();
179 | //or new core.SessionStorage() - can be used instead, both classes has identical interface
180 | storage.set('key1', 'value');
181 | storage.setObject('key2', {'object-key': 'object-value'}); //performs JSON.stringify(...)
182 |
183 | storage.get('key1');
184 | storage.getObject('key2'); //performs JSON.parse(...)
185 |
186 | storage.clear();
187 | storage.print();
188 | ```
189 |
190 | ### Use-cases: Libraries
191 | As was mentioned before in the Modals section, to work with a library and it's functionality you need to include library markup into the body of lightning-core component:
192 | ```html
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 | ```
203 | Functionality which is supported through these libraries:
204 | - Page reference navigation (lightning:navigation)
205 | - Notices and Toasts (lightning:notificationsLibrary)
206 | - Modals and Popovers (lightning:overlayLibrary)
207 |
208 | Examples:
209 | ```javascript
210 | /*Navigation*/
211 | const navigation = new core.Navigation();
212 | navigation.navigate(new core.PageReferenceWebPage().setUrl(url));
213 |
214 | /*Notice*/
215 | new core.Notice()
216 | .setTitle(title)
217 | .setMessage(message)
218 | .show();
219 |
220 | /*Toast - toasts has the same classes, as regular toast, but it has X in it's name. E.g.*/
221 | new core.ToastXLongError(message).fire();
222 |
223 | /*Modal*/
224 | new core.Modal()
225 | .setBody(new Component(name, params))
226 | .setFooter(new Component(name, params))
227 | .show()
228 | .then((overlay) => {
229 | /*success callback*/
230 | })
231 | .catch((error) => {
232 | /*error callback*/
233 | });
234 |
235 | /*Popover*/
236 | new core.Popover()
237 | .setBody(body)
238 | .setReferenceSelector(referenceElementSelector)
239 | .show()
240 | .then((overlay) => {
241 | /*success callback*/
242 | })
243 | .catch((error) => {
244 | /*error callback*/
245 | });
246 | ```
247 | ### Use-case: Extension Modules
248 | If you would like to add new functions, custom libraries or even submodules to the lightning-core library, you can easily do this, by implementing lightningCoreModule interface in your custom component, writing logic for export() method and including your custom component into lightning-core body.
249 | First, create a module and implement interface and export method:
250 | ```html
251 |
252 |
253 |
254 | ```
255 |
256 | ```javascript
257 | ({
258 | //Controller
259 | //implementation of interface method
260 | export: function(cmp, evt, helper) {
261 | return helper.exportClasses();
262 | },
263 |
264 | //Helper
265 | exportClasses: function() {
266 | class AlertToast {
267 | constructor(message) {
268 | this.message = message;
269 | }
270 |
271 | fire() {
272 | alert(this.message);
273 | }
274 | }
275 |
276 | //returning value should always be an array in this format:
277 | //[module_name, {exported_classes}]
278 | return ['alerts', {
279 | "AlertToast": AlertToast
280 | }];
281 | },
282 | });
283 | ```
284 | Finally, after you include your module into lightning-core body, you may start using it everywhere lightning-core is available:
285 | ```html
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 | ```
294 | Usage:
295 | ```javascript
296 | new core.alerts.AlertToast('My message').fire();
297 | ```
298 | Lightning-core will perform a check if you've implemented export() method, if you've returned a propperly formatted value and if your submodule name overwrites any other functions/submodules. If any error occures, lightning-core will display an error message in error logs.
299 |
300 | ## ToDo
301 | - Complete readme
302 | - Complete JS Docs
303 | - Add more functions
304 |
--------------------------------------------------------------------------------
/src/aura/lightningCore/lightningCore.cmp:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/src/aura/lightningCore/lightningCore.cmp-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 46.0
4 | lightningCore
5 |
6 |
--------------------------------------------------------------------------------
/src/aura/lightningCore/lightningCoreController.js:
--------------------------------------------------------------------------------
1 | ({
2 | /**
3 | * Init function for component to load lightning-core library
4 | *
5 | * @version 1.0
6 | * @author github/4an70m
7 | * @param cmp
8 | * @param evt
9 | * @param helper
10 | */
11 | doInit: function(cmp, evt, helper) {
12 | helper.core(cmp).init();
13 | }
14 | });
--------------------------------------------------------------------------------
/src/aura/lightningCore/lightningCoreHelper.js:
--------------------------------------------------------------------------------
1 | ({
2 |
3 | /**
4 | * Function to import lightning-core into the window object.
5 | * Returns init() function, which should be explicitly called.
6 | *
7 | * @version 1.0
8 | * @author github/4an70m
9 | * @param cmp
10 | * @returns {{init}}
11 | */
12 | core: function (cmp) {
13 |
14 | const component = cmp;
15 |
16 | /**
17 | * Class for checking runtime environment.
18 | * Current implementations relays on if global toast event is supported.
19 | */
20 | class Environment {
21 |
22 | constructor() {
23 | //currently there's no better way to find out the runtime environment
24 | this.environment = $A.util.isUndefinedOrNull($A.get("e.force:showToast")) ? "App" : "Lightning";
25 | }
26 |
27 | /**
28 | * Checks if current environment is an Application
29 | * @returns {boolean}
30 | */
31 | isApp() {
32 | return this.environment === "App";
33 | }
34 |
35 | /**
36 | * Checks if current environment is a lightning org runtime
37 | * @returns {boolean}
38 | */
39 | isLightning() {
40 | return this.environment === "Lightning";
41 | }
42 | }
43 |
44 |
45 | /* classes for creating toasts */
46 | /**
47 | * Simple class for creation toasts.
48 | * Use this format:
49 | *
50 | * new core.Toast({...}).fire();
51 | *
52 | */
53 | class Toast {
54 |
55 | constructor(params = {}) {
56 | this.params = params;
57 | }
58 |
59 | /**
60 | * Sets the type of the toast. Accepted values are:
61 | *
62 | * - success
63 | * - warning
64 | * - error
65 | * - info
66 | *
67 | *
68 | * @param type
69 | * @returns {Toast}
70 | */
71 | setType(type) {
72 | this.params.type = type;
73 | return this;
74 | }
75 |
76 | /**
77 | * Sets the title of the toast.
78 | *
79 | * @param title
80 | * @returns {Toast}
81 | */
82 | setTitle(title) {
83 | this.params.title = title;
84 | return this;
85 | }
86 |
87 | /**
88 | * Sets the message of the toast.
89 | *
90 | * @param message
91 | * @returns {Toast}
92 | */
93 | setMessage(message) {
94 | this.params.message = message;
95 | return this;
96 | }
97 |
98 | /**
99 | * Sets the mode of the toast.
100 | * Supported values are:
101 | *
102 | * - dismissible
103 | * - pester
104 | * - sticky
105 | *
106 | *
107 | * @param mode
108 | * @returns {Toast}
109 | */
110 | setMode(mode) {
111 | this.params.mode = mode;
112 | return this;
113 | }
114 |
115 | /**
116 | * Sets the duration of the toast.
117 | *
118 | * @param duration
119 | * @returns {Toast}
120 | */
121 | setDuration(duration) {
122 | this.params.duration = duration;
123 | return this;
124 | }
125 |
126 | /**
127 | * Fires a toast
128 | * Displays an error in console, if toast is not supported in this environment
129 | */
130 | fire() {
131 | try {
132 | if (new Environment().isApp()) {
133 | throw new Error("$A.e.force:showToast is not supported in App");
134 | }
135 | const toastEvent = $A.get("e.force:showToast");
136 | toastEvent.setParams(this.params);
137 | toastEvent.fire();
138 | } catch (e) {
139 | console.error(`Core:\nRunning e.force:showToast raised an exception:\n${e}`)
140 | }
141 | }
142 | }
143 |
144 | /**
145 | * Toast with predefined mode = "dismissible" and duration = 4s
146 | * Use this format:
147 | *
148 | * new core.ToastQuick(type, title, message).fire();
149 | *
150 | */
151 | class ToastQuick extends Toast {
152 |
153 | constructor(type, title, message) {
154 | super({
155 | "type": type,
156 | "title": title,
157 | "message": message,
158 | "mode": "dismissible",
159 | "duration": 4000
160 | });
161 | }
162 | }
163 |
164 | /**
165 | * Toast with predefined mode = "dismissible" and duration = 8s
166 | * Use this format:
167 | *
168 | * new core.ToastLong(type, title, message).fire();
169 | *
170 | */
171 | class ToastLong extends Toast {
172 |
173 | constructor(type, title, message) {
174 | super({
175 | "type": type,
176 | "title": title,
177 | "message": message,
178 | "mode": "dismissible",
179 | "duration": 8000
180 | });
181 | }
182 | }
183 |
184 | /**
185 | * Toast with predefined
186 | *
mode = "dismissible"
187 | *
duration = 4s
188 | *
type = "success"
189 | *
title = "Success!"
190 | *
191 | * Use this format:
192 | *
193 | * new core.ToastQuickSuccess(message).fire();
194 | *
195 | */
196 | class ToastQuickSuccess extends ToastQuick {
197 |
198 | constructor(message) {
199 | super(
200 | "success",
201 | "Success!",
202 | message
203 | );
204 | }
205 | }
206 |
207 | /**
208 | * Toast with predefined
209 | *
mode = "dismissible"
210 | *
duration = 4s
211 | *
type = "error"
212 | *
title = "Something went wrong!"
213 | *
214 | * Use this format:
215 | *
216 | * new core.ToastQuickError(message).fire();
217 | *
218 | */
219 | class ToastQuickError extends ToastQuick {
220 |
221 | constructor(message) {
222 | super(
223 | "error",
224 | "Something went wrong!",
225 | message
226 | );
227 | }
228 | }
229 |
230 | /**
231 | * Toast with predefined
232 | *
mode = "dismissible"
233 | *
duration = 8s
234 | *
type = "success"
235 | *
title = "Success!"
236 | *
237 | * Use this format:
238 | *
239 | * new core.ToastLongSuccess(message).fire();
240 | *
241 | */
242 | class ToastLongSuccess extends ToastLong {
243 |
244 | constructor(message) {
245 | super(
246 | "success",
247 | "Success!",
248 | message
249 | );
250 | }
251 | }
252 |
253 | /**
254 | * Toast with predefined
255 | *
mode = "dismissible"
256 | *
duration = 8s
257 | *
type = "error"
258 | *
title = "Something went wrong!"
259 | *
260 | * Use this format:
261 | *
262 | * new core.ToastLongError(message).fire();
263 | *
264 | */
265 | class ToastLongError extends ToastLong {
266 |
267 | constructor(message) {
268 | super(
269 | "error",
270 | "Something went wrong!",
271 | message
272 | );
273 | }
274 | }
275 |
276 |
277 | /* classes for server interactions */
278 | /**
279 | * Promise-like class for server action calling.
280 | * Doesn't support chaining yet.
281 | * Use this format:
282 | *
283 | * new core.ServerAction(component, actionName, (opt) params).execute();
284 | *
285 | */
286 | class ServerAction {
287 |
288 | constructor(component, action, params) {
289 | this.action = ServerAction.getAction(component, action, params);
290 | }
291 |
292 | /**
293 | * Default message for parseResponseMessage() method, when the message cannot be parsed
294 | *
Replace default message this with appropriate label, if you support multiple languages
295 | *
296 | * @see ServerAction.parseResponseMessage
297 | * @returns {string}
298 | */
299 | static get messageUndefinedResponse() {
300 | return "Undefined response";
301 | }
302 |
303 | /**
304 | * Default message for parseResponseMessage() method, when the error is unknown
305 | *
Replace default message this with appropriate label, if you support multiple languages
306 | *
307 | * @see ServerAction.parseResponseMessage
308 | * @returns {string}
309 | */
310 | static get messageUnknownError() {
311 | return "Unknown error";
312 | }
313 |
314 | /**
315 | * Default message for parseResponseMessage() method, when action was interrupted
316 | *
Replace default message this with appropriate label, if you support multiple languages
317 | *
318 | * @see ServerAction.parseResponseMessage
319 | * @returns {string}
320 | */
321 | static get messageIncompleteAction() {
322 | return "No response from server or client is offline";
323 | }
324 |
325 | /**
326 | * Default message for parseResponseMessage() method, when the error was unexpected and no other error was applicable
327 | *
Replace default message this with appropriate label, if you support multiple languages
328 | *
329 | * @see ServerAction.parseResponseMessage
330 | * @returns {string}
331 | */
332 | static get messageUnexpectedError() {
333 | return "Unexpected error";
334 | }
335 |
336 | /**
337 | * Method for getting apex-based action.
338 | * Supports action name without "c." prefix.
339 | * params parameter is optional and can be omitted.
340 | *
341 | *
Displays an error in console if the action was not found.
342 | *
343 | * @param cmp
344 | * @param actionName
345 | * @param params
346 | * @returns {*}
347 | */
348 | static getAction(cmp, actionName, params) {
349 | if (actionName.indexOf("c.") <= -1) {
350 | actionName = "c." + actionName;
351 | }
352 | let action = null;
353 |
354 | try {
355 | action = cmp.get(actionName);
356 | } catch (error) {
357 | console.error(`\nCore:\n${actionName} is invalid action.\n + ${error}`);
358 | return action;
359 | }
360 |
361 | if (!$A.util.isUndefinedOrNull(params)) {
362 | action.setParams(params);
363 | }
364 | return action;
365 | }
366 |
367 | /**
368 | * Executes an action. Performs a server call.
369 | *
370 | * @returns {LightningAction}
371 | */
372 | execute() {
373 | return new LightningAction((context, success, error) => {
374 | this.action.setCallback(this, result => {
375 | let state = result.getState();
376 | if (state === "SUCCESS") {
377 | success(context, result.getReturnValue());
378 | } else {
379 | error(context, result);
380 | }
381 | });
382 | $A.enqueueAction(this.action);
383 | });
384 | }
385 |
386 | /**
387 | * Multipurpose method, which parses any response, that lightning actions might though
388 | *
389 | * @param response
390 | * @returns {*}
391 | */
392 | static parseResponseMessage(response) {
393 | if ($A.util.isUndefinedOrNull(response)) {
394 | return ServerAction.messageUndefinedResponse;
395 | }
396 |
397 | if (typeof response === "string") {
398 | return response;
399 | }
400 |
401 | if (response.message) {
402 | return response.message;
403 | }
404 |
405 | if (response.getState) {
406 | const state = response.getState();
407 | let message = ServerAction.messageUnknownError;
408 | if (state === "ERROR") {
409 | let errors = response.getError();
410 | if (errors && errors[0] && errors[0].message) {
411 | message = errors[0].message;
412 | }
413 | if (errors && errors[0] && errors[0].pageErrors && errors[0].pageErrors[0]) {
414 | message = errors[0].pageErrors[0].message;
415 | }
416 | } else if (state === "INCOMPLETE") {
417 | message = ServerAction.messageIncompleteAction;
418 | }
419 | return message;
420 | }
421 | return ServerAction.messageUnexpectedError;
422 | }
423 | }
424 |
425 | /**
426 | * Promise-like class for server action calling.
427 | * Automatically handles an error with core.ToastLongError(...)
428 | * Doesn't support chaining yet.
429 | * Use this format:
430 | *
431 | * new core.ServerActionHandled(component, actionName, (opt) params).execute();
432 | *
433 | */
434 | class ServerActionHandled extends ServerAction {
435 |
436 | constructor(component, action, params) {
437 | super(component, action, params);
438 | }
439 |
440 | /**
441 | * Executes an action. Performs a server call
442 | * If the action fails, a ToastLongError will be thrown.
443 | *
444 | * @see ToastLongError
445 | * @returns {LightningAction}
446 | */
447 | execute() {
448 | return new LightningAction((context, success, error) => {
449 | this.action.setCallback(this, result => {
450 | let state = result.getState();
451 | if (state === "SUCCESS") {
452 | success(context, result.getReturnValue());
453 | } else {
454 | new ToastLongError(ServerAction.parseResponseMessage(result));
455 | error(context, result);
456 | }
457 | });
458 | $A.enqueueAction(this.action);
459 | });
460 | }
461 | }
462 |
463 | /**
464 | * Promise class for server action calling.
465 | * Use this format:
466 | *
467 | * new core.ServerActionPromise(component, actionName, (opt) params).execute();
468 | *
469 | */
470 | class ServerActionPromise extends ServerAction {
471 |
472 | constructor(component, action, params) {
473 | super(component, action, params);
474 | }
475 |
476 | /**
477 | * Executes an action. Performs a Promise server call.
478 | *
479 | * @returns {LightningAction}
480 | */
481 | execute() {
482 | return new LightningPromise((resolve, reject) => {
483 | this.action.setCallback(this, result => {
484 | let state = result.getState();
485 | if (state === "SUCCESS") {
486 | resolve(result.getReturnValue());
487 | } else {
488 | reject(result);
489 | }
490 | });
491 | $A.enqueueAction(this.action);
492 | });
493 | }
494 | }
495 |
496 | /**
497 | * Promise class for server action calling.
498 | * Automatically handles an error with core.ToastLongError(...)
499 | * Use this format:
500 | *
501 | * new core.ServerActionPromiseHandled(component, actionName, (opt) params).execute();
502 | *
503 | */
504 | class ServerActionPromiseHandled extends ServerAction {
505 |
506 | constructor(component, action, params) {
507 | super(component, action, params);
508 | }
509 |
510 | /**
511 | * Executes an action. Performs a Promise server call
512 | * If the action fails, a ToastLongError will be thrown.
513 | *
514 | * @see ToastLongError
515 | * @returns {LightningAction}
516 | */
517 | execute() {
518 | return new LightningPromise((resolve, reject) => {
519 | this.action.setCallback(this, result => {
520 | let state = result.getState();
521 | if (state === "SUCCESS") {
522 | resolve(result.getReturnValue());
523 | } else {
524 | new ToastLongError(ServerAction.parseResponseMessage(result)).fire();
525 | reject(result);
526 | }
527 | });
528 | $A.enqueueAction(this.action);
529 | });
530 | }
531 | }
532 |
533 | /**
534 | * Promise substitute class.
535 | * Built with a single purpose - to execute server actions in a Promise-like manner for cacheable=true actions
536 | */
537 | class LightningAction {
538 |
539 | constructor(action) {
540 | this.action = action;
541 | this._resolve();
542 | }
543 |
544 | /**
545 | * Adds a handler function for success callback
546 | *
547 | * @param onSuccess
548 | * @returns {LightningAction}
549 | */
550 | then(onSuccess) {
551 | this.onSuccess = onSuccess;
552 | return this;
553 | }
554 |
555 | /**
556 | * Adds a handler function for error callback
557 | *
558 | * @param onError
559 | * @returns {LightningAction}
560 | */
561 | catch(onError) {
562 | this.onError = onError;
563 | return this;
564 | }
565 |
566 | /**
567 | * Adds a handler function for any outcome callback
568 | *
569 | * @param onFinally
570 | * @returns {LightningAction}
571 | */
572 | finally(onFinally) {
573 | this.onFinally = onFinally;
574 | return this;
575 | }
576 |
577 | _success(self, result) {
578 | try {
579 | if (self.onSuccess) {
580 | self.onSuccess(result);
581 | }
582 | if (self.onFinally) {
583 | self.onFinally();
584 | }
585 | } catch (e) {
586 | self._error(self, e);
587 | }
588 | }
589 |
590 | _error(self, error) {
591 | if (self.onError) {
592 | self.onError(error);
593 | } else {
594 | console.error(`Core:\nUnhandled error in Lightning Action: ${error}\n`);
595 | }
596 | if (self.onFinally) {
597 | self.onFinally();
598 | }
599 | }
600 |
601 | _resolve() {
602 | const self = this;
603 | window.setTimeout($A.getCallback(() => {
604 | this.action(self, this._success, this._error);
605 | }, 0));
606 | }
607 | }
608 |
609 | /**
610 | * Child class of Promise class.
611 | * Wraps most of the common functions with $A.getCallback(...) to eliminate the need of wrapping callback functions
612 | */
613 | class LightningPromise extends Promise {
614 |
615 | constructor(fn) {
616 | super($A.getCallback(fn));
617 | }
618 |
619 | /**
620 | * Adds a handler function on Success and Error outcome
621 | *
622 | * @param onSuccess
623 | * @param onError
624 | * @returns {Promise}
625 | */
626 | then(onSuccess, onError) {
627 | return super.then(
628 | (onSuccess ? $A.getCallback(onSuccess) : undefined),
629 | (onError ? $A.getCallback(onError) : undefined)
630 | );
631 | }
632 |
633 | /**
634 | * Adds a handle function on Error outcome
635 | *
636 | * @param onError
637 | * @returns {Promise}
638 | */
639 | catch(onError) {
640 | return super.catch(
641 | onError ? $A.getCallback(onError) : undefined
642 | );
643 | }
644 |
645 | /**
646 | * Adds a handler function on any outcome
647 | *
648 | * @param onFinally
649 | * @returns {Promise}
650 | */
651 | finally(onFinally) {
652 | return super.finally(
653 | onFinally ? $A.getCallback(onFinally) : undefined
654 | );
655 | }
656 | }
657 |
658 |
659 | /* classes for component creation*/
660 | /**
661 | * Class, which represents a container for a single component
662 | * Supports creation of the component with a Promise
663 | *
664 | * @see LightningPromise
665 | */
666 | class Component {
667 |
668 | constructor(name, params = {}) {
669 | this.name = name;
670 | this.params = params;
671 | }
672 |
673 | /**
674 | * Adds a parameter to component
675 | *
676 | * @param name
677 | * @param value
678 | * @returns {Component}
679 | */
680 | addParam(name, value) {
681 | this.params[name] = value;
682 | return this;
683 | }
684 |
685 | /**
686 | * Removes a parameter from component
687 | * @param name
688 | * @returns {Component}
689 | */
690 | removeParam(name) {
691 | delete this.params[name];
692 | return this;
693 | }
694 |
695 | /**
696 | * Converts component to the format, which is acceptable for $A.createComponents
697 | * @returns {*[]}
698 | */
699 | toParams() {
700 | return [
701 | this.name,
702 | this.params
703 | ];
704 | }
705 |
706 | /**
707 | * Creates a component using LightningPromise
708 | *
709 | * @see LightningPromise
710 | * @returns {LightningPromise}
711 | */
712 | create() {
713 | return new LightningPromise((resolve, reject) => {
714 | $A.createComponent(this.name, this.params, (components, status, errorMessage) => {
715 | if (status === "SUCCESS") {
716 | resolve(components);
717 | } else {
718 | reject(errorMessage, status);
719 | }
720 | });
721 | });
722 | }
723 | }
724 |
725 | /**
726 | * Container class for bulk-creation of components
727 | */
728 | class Components {
729 |
730 | constructor() {
731 | this.components = [];
732 | }
733 |
734 | /**
735 | * Adds a component to the container
736 | *
737 | * @param component
738 | * @returns {Components}
739 | */
740 | addComponent(component) {
741 | if (component instanceof Component) {
742 | this.components.push(component);
743 | }
744 | return this;
745 | }
746 |
747 | /**
748 | * Performs bulk creation of components
749 | * Returns null if there's no components to create
750 | * Returns LightningPromise if there are components to create
751 | *
752 | * @see LightningPromise
753 | * @returns {*}
754 | */
755 | create() {
756 | if (this.components.length === 0) {
757 | return null;
758 | }
759 | const params = this.components.map((component) => {
760 | return component.toParams();
761 | });
762 | return new LightningPromise((resolve, reject) => {
763 | $A.createComponents(params, (components, status, errorMessage) => {
764 | if (status === "SUCCESS") {
765 | resolve(components);
766 | } else {
767 | reject(errorMessage, status);
768 | }
769 | });
770 | });
771 | }
772 | }
773 |
774 |
775 | /* classes for working with files */
776 | /**
777 | * Files wrapper class, built for convenient conversion and downloading of a file
778 | */
779 | class File {
780 |
781 | /**
782 | * Accepts Base64 or Blob representation of a file
783 | *
784 | * @param fileData
785 | * @param fileName
786 | */
787 | constructor(fileData, fileName = "download") {
788 | this.fileData = fileData;
789 | this.fileName = fileName;
790 | }
791 |
792 | /**
793 | * Converts file to Base64 format
794 | *
795 | * @returns {LightningPromise}
796 | */
797 | toBase64() {
798 | return new LightningPromise((resolve, reject) => {
799 | const reader = new FileReader();
800 | reader.readAsDataURL(this.fileData);
801 | reader.onloadend = (evt) => {
802 | const error = evt.target.error;
803 | if (!$A.util.isUndefinedOrNull(error)) {
804 | reject(error);
805 | return;
806 | }
807 | resolve(evt.target.result);
808 | };
809 | });
810 | }
811 |
812 | /**
813 | * Converts file to Blob format
814 | *
815 | * @returns {Blob}
816 | */
817 | toBlob() {
818 | let sliceSize = 512;
819 | let byteCharacters = atob(this.fileData);
820 | let byteArrays = [];
821 | for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
822 | let slice = byteCharacters.slice(offset, offset + sliceSize);
823 | let byteNumbers = new Array(slice.length);
824 | for (var i = 0; i < slice.length; i++) {
825 | byteNumbers[i] = slice.charCodeAt(i);
826 | }
827 | let byteArray = new Uint8Array(byteNumbers);
828 | byteArrays.push(byteArray);
829 | }
830 | return new Blob(byteArrays, {type: "application/octet-stream"});
831 | }
832 |
833 | /**
834 | * Performs a client-side downloading of a file
835 | *
836 | * @param fileName
837 | */
838 | download(fileName) {
839 | const a = document.createElement("a");
840 | a.download = this.fileName || fileName;
841 | a.rel = "noopener";
842 | a.target = "_blank";
843 |
844 | if (this.fileData instanceof Blob) {
845 | a.href = window.URL.createObjectURL(this.fileData);
846 | } else {
847 | a.href = this.toBlob();
848 | }
849 | a.click();
850 |
851 | }
852 | }
853 |
854 |
855 | /* library class */
856 | /**
857 | * Class, which represents a dependency to a specific library
858 | * Retrieves it from the body and checks if there's a library
859 | * Throws specific errors, if there's no library attached
860 | */
861 | class Library {
862 |
863 | constructor(name) {
864 | const body = component.get("v.body") || [];
865 | this.library = body.find((component) => {
866 | return component.isInstanceOf(name);
867 | });
868 | if (!this.library) {
869 | const message = `Core:\nTo use ${this.constructor.name}, please include ${name} component inside c:lightningCore.\n`;
870 | console.error(message);
871 | throw new Error(message);
872 | }
873 | }
874 | }
875 |
876 |
877 | /* classes to work with lightning:navigation */
878 | /**
879 | * Navigation library, wraps lightning:navigation
880 | * Supports old-style navigation through "e.force:navigateTo..." as well as PageReference navigations
881 | */
882 | class Navigation extends Library {
883 |
884 | constructor() {
885 | super("lightning:navigation");
886 | }
887 |
888 | /**
889 | * Navigation with a PageReference classes
890 | * Supports a preNavigateCallback function, which is executed after url is generated, but before the navigation occurs.
891 | *
892 | * @see PageReference
893 | * @param pageReference
894 | * @param preNavigateCallback
895 | */
896 | navigate(pageReference) {
897 | if (!(pageReference instanceof PageReference)) {
898 | const message = `Core:\nnavigate() method should be called with PageReference parameter\n`;
899 | console.error(message);
900 | throw new Error(message);
901 | }
902 | this.library.navigate(pageReference.toPageReference())
903 | .catch($A.getCallback((error) => {
904 | console.error(`Core:\nUrl generation encountered an error: \n ${error}\n`);
905 | }));
906 | }
907 |
908 | /**
909 | * Base method for navigation
910 | *
911 | * @deprecated
912 | * @param type
913 | * @param params
914 | */
915 | oldNavigateTo(type, params) {
916 | console.warn(`Core:\nYou are using deprecated api.\nStarting with api v43 it is recommended to use`,
917 | "%clightning:navigation",
918 | "font-weight: bold",
919 | "for navigation."
920 | );
921 | const evt = $A.get(type);
922 | evt.setParams(params);
923 | evt.fire();
924 | }
925 |
926 | /**
927 | * This event enables you to navigate from one Lightning component to another.
928 | *
929 | * @deprecated
930 | * @param componentDef
931 | * @param componentAttributes
932 | */
933 | oldNavigateToComponent(componentDef, componentAttributes = {}) {
934 | this.oldNavigateTo("e.force:navigateToComponent", {
935 | "componentDef": componentDef,
936 | "componentAttributes": componentAttributes
937 | });
938 | }
939 |
940 |
941 | /**
942 | * This event enables you to navigate to the list view specified by listViewId.
943 | *
944 | * @deprecated
945 | * @param componentDef
946 | * @param listViewId
947 | * @param listViewName
948 | * @param scope
949 | */
950 | oldNavigateToList(componentDef, listViewId, listViewName = null, scope) {
951 | this.oldNavigateTo("e.force:navigateToList", {
952 | "listViewId": listViewId,
953 | "listViewName": null,
954 | "scope": scope
955 | });
956 | }
957 |
958 | /**
959 | * This event enables you to navigate to the object home specified by the scope attribute.
960 | *
961 | * @deprecated
962 | * @param scope
963 | */
964 | oldNavigateToObjectHome(scope) {
965 | this.oldNavigateTo("e.force:navigateToObjectHome", {
966 | "scope": scope
967 | })
968 | }
969 |
970 | /**
971 | * This event enables you to navigate to the related list specified by parentRecordId.
972 | *
973 | * @deprecated
974 | * @param relatedListId
975 | * @param parentRecordId
976 | */
977 | oldNavigateToRelatedList(relatedListId, parentRecordId) {
978 | this.oldNavigateTo("e.force:navigateToRelatedList", {
979 | "relatedListId": relatedListId,
980 | "parentRecordId": parentRecordId
981 | })
982 | }
983 |
984 |
985 | /**
986 | * This event enables you to navigate to an sObject record specified by recordId.
987 | *
988 | * @deprecated
989 | * @param recordId
990 | * @param slideDevName
991 | */
992 | oldNavigateToSObject(recordId, slideDevName) {
993 | this.oldNavigateTo("e.force:navigateToSObject", {
994 | "recordId": recordId,
995 | "slideDevName": slideDevName
996 | })
997 | }
998 |
999 | /**
1000 | * Relative and absolute URLs are supported. Relative URLs are relative to the Salesforce mobile web domain, and retain navigation history. External URLs open in a separate browser window.
1001 | *
1002 | * @param url
1003 | */
1004 | oldNavigateToUrl(url) {
1005 | this.oldNavigateTo("e.force:navigateToURL", {
1006 | "url": url,
1007 | })
1008 | }
1009 | }
1010 |
1011 | /**
1012 | * Base page reference without state attribute
1013 | * doc
1014 | */
1015 | class PageReference {
1016 |
1017 | constructor() {
1018 | this.attributes = {};
1019 | }
1020 |
1021 | /**
1022 | * Sets a type of a PageReference
1023 | *
1024 | * @param type
1025 | * @returns {PageReference}
1026 | */
1027 | setType(type) {
1028 | this.type = type;
1029 | return this;
1030 | }
1031 |
1032 | /**
1033 | * Sets attributes for a PageReference
1034 | *
1035 | * @param attributes
1036 | * @returns {PageReference}
1037 | */
1038 | setAttributes(attributes) {
1039 | this.attributes = attributes;
1040 | return this;
1041 | }
1042 |
1043 | /**
1044 | * Adds an attribute to PageReference
1045 | *
1046 | * @param name
1047 | * @param value
1048 | * @returns {PageReference}
1049 | */
1050 | addAttribute(name, value) {
1051 | this.attributes[name] = value;
1052 | return this;
1053 | }
1054 |
1055 | /**
1056 | * Removes an attribute from PageReference
1057 | *
1058 | * @param name
1059 | * @returns {PageReference}
1060 | */
1061 | removeAttribute(name) {
1062 | delete this.attributes[name];
1063 | return this;
1064 | }
1065 |
1066 | /**
1067 | * Converts object to PageReference
1068 | */
1069 | toPageReference() {
1070 | const preparedPageReference = {};
1071 | if (this.type) {
1072 | preparedPageReference.type = this.type;
1073 | }
1074 | preparedPageReference.attributes = this.attributes;
1075 | if (this.state) {
1076 | preparedPageReference.state = this.state;
1077 | }
1078 | return preparedPageReference;
1079 | }
1080 | }
1081 |
1082 | /**
1083 | * Base page reference with state attribute
1084 | * doc
1085 | */
1086 | class StatefulPageReference extends PageReference {
1087 |
1088 | constructor() {
1089 | super();
1090 | this.state = {};
1091 | }
1092 |
1093 | /**
1094 | * Sets a state of the StatefulPageReference
1095 | *
1096 | * @param state
1097 | * @returns {StatefulPageReference}
1098 | */
1099 | setState(state) {
1100 | this.state = state;
1101 | return this;
1102 | }
1103 |
1104 | /**
1105 | * Adds a state value to StatefulPageReference
1106 | *
1107 | * @param name
1108 | * @param value
1109 | * @returns {StatefulPageReference}
1110 | */
1111 | addState(name, value) {
1112 | this.state[name] = value;
1113 | return this;
1114 | }
1115 |
1116 | /**
1117 | * Removes a state value from StatefulPageReference
1118 | *
1119 | * @param name
1120 | * @returns {StatefulPageReference}
1121 | */
1122 | removeState(name) {
1123 | delete this.state[name];
1124 | return this;
1125 | }
1126 | }
1127 |
1128 | /**
1129 | * A Lightning component that implements the lightning:isUrlAddressable interface, which enables the component to be navigated directly via URL.
1130 | * doc
1131 | */
1132 | class PageReferenceStandardComponent extends StatefulPageReference {
1133 |
1134 | constructor(componentName) {
1135 | super();
1136 | this.setType("standard__component");
1137 | this.addAttribute("componentName", componentName);
1138 | }
1139 | }
1140 |
1141 | /**
1142 | * An authentication for a community.
1143 | *
1144 | */
1145 | class PageReferenceCommLoginPage extends PageReference {
1146 |
1147 | constructor(action = "login") {
1148 | super();
1149 | this.setType("comm__loginPage");
1150 | this.addAttribute("actionName", action);
1151 | }
1152 |
1153 | /**
1154 | * Sets the action to be "login"
1155 | *
1156 | * @returns {PageReference}
1157 | */
1158 | setActionLogin() {
1159 | return this.addAttribute("actionName", "login");
1160 | }
1161 |
1162 | /**
1163 | * Sets the action to be "logout"
1164 | *
1165 | * @returns {PageReference}
1166 | */
1167 | setActionLogout() {
1168 | return this.addAttribute("actionName", "logout");
1169 | }
1170 | }
1171 |
1172 | /**
1173 | * A page that interacts with a Knowledge Article record.
1174 | *
1175 | */
1176 | class PageReferenceKnowledgeArticlePage extends PageReference {
1177 |
1178 | constructor() {
1179 | super();
1180 | this.setType("standard__knowledgeArticlePage");
1181 | }
1182 |
1183 | /**
1184 | * Sets articleType attribute
1185 | *
1186 | * @param articleType
1187 | * @returns {PageReference}
1188 | */
1189 | setArticleType(articleType) {
1190 | return this.addAttribute("articleType", articleType);
1191 | }
1192 |
1193 |
1194 | /**
1195 | * Sets urlName attribute
1196 | *
1197 | * @param urlName
1198 | * @returns {PageReference}
1199 | */
1200 | setUrlName(urlName) {
1201 | return this.addAttribute("urlName", urlName);
1202 | }
1203 | }
1204 |
1205 | /**
1206 | * A standard page with a unique name. If an error occurs, the error view loads and the URL isn’t updated.
1207 | * Types: standard__namedPage, comm__namedPage
1208 | *
1209 | */
1210 | class PageReferenceNamedPage extends PageReference {
1211 |
1212 | constructor(pageType = "standard__namedPage") {
1213 | super();
1214 | this.setPageName(pageType);
1215 | }
1216 |
1217 | /**
1218 | * Sets pageName attribute
1219 | *
1220 | * @param pageName
1221 | * @returns {PageReference}
1222 | */
1223 | setPageName(pageName) {
1224 | return this.addAttribute("pageName", pageName);
1225 | }
1226 | }
1227 |
1228 | /**
1229 | * A page that displays the content mapped to a CustomTab. Visualforce tabs, web tabs, Lightning Pages, and Lightning Component tabs are supported.
1230 | *
1231 | */
1232 | class PageReferenceNavItemPage extends PageReference {
1233 |
1234 | constructor() {
1235 | super();
1236 | this.setType("standard__navItemPage");
1237 | }
1238 |
1239 | /**
1240 | * Sets apiName attribute
1241 | *
1242 | * @param apiName
1243 | * @returns {PageReference}
1244 | */
1245 | setApiName(apiName) {
1246 | return this.addAttribute("apiName", apiName);
1247 | }
1248 | }
1249 |
1250 | /**
1251 | * A page that interacts with a standard or custom object in the org and supports standard actions for that object.
1252 | *
1253 | */
1254 | class PageReferenceObjectPage extends StatefulPageReference {
1255 |
1256 | constructor() {
1257 | super();
1258 | this.setType("standard__objectPage");
1259 | }
1260 |
1261 | /**
1262 | * Sets objectApiName attribute
1263 | *
1264 | * @param objectApiName
1265 | * @returns {PageReference}
1266 | */
1267 | setObjectApiName(objectApiName) {
1268 | return this.addAttribute("objectApiName", objectApiName);
1269 | }
1270 |
1271 | /**
1272 | * Sets actionName attribute
1273 | *
1274 | * @param actionName
1275 | * @returns {PageReference}
1276 | */
1277 | setActionName(actionName) {
1278 | return this.addAttribute("actionName", actionName);
1279 | }
1280 |
1281 | /**
1282 | * Sets filterName attribute
1283 | *
1284 | * @param filterName
1285 | * @returns {StatefulPageReference}
1286 | */
1287 | setFilterName(filterName) {
1288 | return this.addState("filterName", filterName);
1289 | }
1290 | }
1291 |
1292 | /**
1293 | * A page that interacts with a record in the org and supports standard actions for that record.
1294 | *
1295 | */
1296 | class PageReferenceRecordPage extends PageReference {
1297 |
1298 | constructor() {
1299 | super();
1300 | this.setType("standard__recordPage");
1301 | }
1302 |
1303 | /**
1304 | * Sets actionName attribute
1305 | *
1306 | * @param actionName
1307 | * @returns {PageReference}
1308 | */
1309 | setActionName(actionName) {
1310 | return this.addAttribute("actionName", actionName);
1311 | }
1312 |
1313 | /**
1314 | * Sets objectApiName attribute
1315 | *
1316 | * @param objectApiName
1317 | * @returns {PageReference}
1318 | */
1319 | setObjectApiName(objectApiName) {
1320 | return this.addAttribute("objectApiName", objectApiName);
1321 | }
1322 |
1323 | /**
1324 | * Sets recordId attribute
1325 | *
1326 | * @param recordId
1327 | * @returns {PageReference}
1328 | */
1329 | setRecordId(recordId) {
1330 | return this.addAttribute("recordId", recordId);
1331 | }
1332 | }
1333 |
1334 | /**
1335 | * A page that interacts with a relationship on a particular record in the org. Only related lists are supported.
1336 | *
1337 | */
1338 | class PageReferenceRecordRelationshipPage extends PageReference {
1339 |
1340 | constructor() {
1341 | super();
1342 | this.setType("standard__recordRelationshipPage");
1343 | }
1344 |
1345 | /**
1346 | * Sets recordId attribute
1347 | *
1348 | * @param recordId
1349 | * @returns {PageReference}
1350 | */
1351 | setRecordId(recordId) {
1352 | return this.addAttribute("recordId", recordId);
1353 | }
1354 |
1355 | /**
1356 | * Sets objectApiName attribute
1357 | *
1358 | * @param objectApiName
1359 | * @returns {PageReference}
1360 | */
1361 | setObjectApiName(objectApiName) {
1362 | return this.addAttribute("objectApiName", objectApiName);
1363 | }
1364 |
1365 | /**
1366 | * Sets relationshipApiName attribute
1367 | *
1368 | * @param relationshipApiName
1369 | * @returns {PageReference}
1370 | */
1371 | setRelationshipApiName(relationshipApiName) {
1372 | return this.addAttribute("relationshipApiName", relationshipApiName);
1373 | }
1374 |
1375 | /**
1376 | * Sets actionName attribute
1377 | *
1378 | * @param actionName
1379 | * @returns {PageReference}
1380 | */
1381 | setActionName(actionName) {
1382 | return this.addAttribute("actionName", actionName);
1383 | }
1384 | }
1385 |
1386 | /**
1387 | * An external URL.
1388 | *
1389 | */
1390 | class PageReferenceWebPage extends PageReference {
1391 |
1392 | constructor(url) {
1393 | super();
1394 | this.setType("standard__webPage");
1395 | this.setUrl(url);
1396 | }
1397 |
1398 | /**
1399 | * Sets url attribute
1400 | *
1401 | * @param url
1402 | * @returns {PageReference}
1403 | */
1404 | setUrl(url) {
1405 | return this.addAttribute("url", url);
1406 | }
1407 | }
1408 |
1409 |
1410 | /* classes to work with lightning:notificationsLibrary */
1411 | /**
1412 | * Creates a notice object, based on lightning:notificationsLibrary
1413 | */
1414 | class Notice extends Library {
1415 |
1416 | /**
1417 | * Creates a new Notice with params
1418 | *
1419 | * @param params
1420 | */
1421 | constructor(params) {
1422 | super("lightning:notificationsLibrary");
1423 | this.setParams(params)
1424 | }
1425 |
1426 | /**
1427 | * Sets params for the Notice
1428 | *
1429 | * @param params
1430 | * @returns {Notice}
1431 | */
1432 | setParams(params = {}) {
1433 | this.params = params;
1434 | return this;
1435 | }
1436 |
1437 | /**
1438 | * Sets header for the Notice
1439 | *
1440 | * @param header
1441 | * @returns {Notice}
1442 | */
1443 | setHeader(header) {
1444 | this.params.header = header;
1445 | return this;
1446 | }
1447 |
1448 | /**
1449 | * Sets title for the Notice
1450 | *
1451 | * @param title
1452 | * @returns {Notice}
1453 | */
1454 | setTitle(title) {
1455 | this.params.title = title;
1456 | return this;
1457 | }
1458 |
1459 | /**
1460 | * Sets message for the Notice
1461 | *
1462 | * @param message
1463 | * @returns {Notice}
1464 | */
1465 | setMessage(message) {
1466 | this.params.message = message;
1467 | return this;
1468 | }
1469 |
1470 | /**
1471 | * Sets variant for the Notice
1472 | * Supported values:
1473 | *
1474 | * - info
1475 | * - warning
1476 | * - error
1477 | *
1478 | * @param variant
1479 | * @returns {Notice}
1480 | */
1481 | setVariant(variant) {
1482 | this.params.variant = variant;
1483 | return this;
1484 | }
1485 |
1486 | /**
1487 | * A callback function called, when Notice is closed
1488 | *
1489 | * @param closeCallback
1490 | * @returns {Notice}
1491 | */
1492 | setCloseCallback(closeCallback) {
1493 | this.params.closeCallback = closeCallback;
1494 | return this;
1495 | }
1496 |
1497 | /**
1498 | * Creates and shows a Notice component.
1499 | * Throws an error if Notice is not supported in this environment.
1500 | * Returns a Promise, if Notice is supported for this environment
1501 | *
1502 | * @returns {*}
1503 | */
1504 | show() {
1505 | try {
1506 | if (new Environment().isApp()) {
1507 | throw new Error("lightning:notificationsLibrary is not supported in App");
1508 | }
1509 | return this.library.showNotice(this.params);
1510 | } catch (e) {
1511 | console.error(`Core:\nRunning lightning:notificationsLibrary -> showNotice() raised an exception:\n${e}`)
1512 | }
1513 | }
1514 | }
1515 |
1516 | /**
1517 | * Simple class for creation toasts through lightning:notificationsLibrary.
1518 | * Use this format:
1519 | *
1520 | * new core.ToastX({...}).fire();
1521 | *
1522 | */
1523 | class ToastX extends Library {
1524 |
1525 | constructor(params) {
1526 | super("lightning:notificationsLibrary");
1527 | this.params = params;
1528 | }
1529 |
1530 | /**
1531 | * Sets the type of the toast. Accepted values are:
1532 | *
1533 | * - success
1534 | * - warning
1535 | * - error
1536 | * - info
1537 | *
1538 | *
1539 | * @param type
1540 | * @returns {Toast}
1541 | */
1542 | setType(type) {
1543 | this.params.type = type;
1544 | return this;
1545 | }
1546 |
1547 | /**
1548 | * Sets the title of the toast.
1549 | *
1550 | * @param title
1551 | * @returns {Toast}
1552 | */
1553 | setTitle(title) {
1554 | this.params.title = title;
1555 | return this;
1556 | }
1557 |
1558 | /**
1559 | * Sets the message of the toast.
1560 | *
1561 | * @param message
1562 | * @returns {Toast}
1563 | */
1564 | setMessage(message) {
1565 | this.params.message = message;
1566 | return this;
1567 | }
1568 |
1569 | /**
1570 | * Sets the mode of the toast.
1571 | * Supported values are:
1572 | *
1573 | * - dismissible
1574 | * - pester
1575 | * - sticky
1576 | *
1577 | *
1578 | * @param mode
1579 | * @returns {Toast}
1580 | */
1581 | setMode(mode) {
1582 | this.params.mode = mode;
1583 | return this;
1584 | }
1585 |
1586 | /**
1587 | * Sets the duration of the toast.
1588 | *
1589 | * @param duration
1590 | * @returns {Toast}
1591 | */
1592 | setDuration(duration) {
1593 | this.params.duration = duration;
1594 | return this;
1595 | }
1596 |
1597 | /**
1598 | * Fires a toast
1599 | * Displays an error in console, if toast is not supported in this environment
1600 | */
1601 | fire() {
1602 | try {
1603 | if (new Environment().isApp()) {
1604 | throw new Error("lightning:notificationsLibrary is not supported in App");
1605 | }
1606 | this.library.showToast(this.params);
1607 | } catch (e) {
1608 | console.error(`Core:\nRunning lightning:notificationsLibrary -> showToast() raised an exception:\n${e}`)
1609 | }
1610 | }
1611 | }
1612 |
1613 | /**
1614 | * Toast through lightning:notificationsLibrary with predefined mode = "dismissible" and duration = 4s
1615 | * Use this format:
1616 | *
1617 | * new core.ToastXQuick(type, title, message).fire();
1618 | *
1619 | */
1620 | class ToastXQuick extends ToastX {
1621 |
1622 | constructor(type, title, message) {
1623 | super({
1624 | "variant": type,
1625 | "title": title,
1626 | "message": message,
1627 | "mode": "dismissible",
1628 | "duration": 4000
1629 | });
1630 | }
1631 | }
1632 |
1633 | /**
1634 | * Toast through lightning:notificationsLibrary with predefined mode = "dismissible" and duration = 8s
1635 | * Use this format:
1636 | *
1637 | * new core.ToastXLong(type, title, message).fire();
1638 | *
1639 | */
1640 | class ToastXLong extends ToastX {
1641 |
1642 | constructor(type, title, message) {
1643 | super({
1644 | "type": type,
1645 | "title": title,
1646 | "message": message,
1647 | "mode": "dismissible",
1648 | "duration": 8000
1649 | });
1650 | }
1651 | }
1652 |
1653 | /**
1654 | * Toast through lightning:notificationsLibrary with predefined
1655 | *
mode = "dismissible"
1656 | *
duration = 4s
1657 | *
type = "success"
1658 | *
title = "Success!"
1659 | *
1660 | * Use this format:
1661 | *
1662 | * new core.ToastXQuickSuccess(message).fire();
1663 | *
1664 | */
1665 | class ToastXQuickSuccess extends ToastXQuick {
1666 |
1667 | constructor(message) {
1668 | super(
1669 | "success",
1670 | "Success!",
1671 | message
1672 | );
1673 | }
1674 | }
1675 |
1676 | /**
1677 | * Toast through lightning:notificationsLibrary with predefined
1678 | *
mode = "dismissible"
1679 | *
duration = 4s
1680 | *
type = "error"
1681 | *
title = "Something went wrong!"
1682 | *
1683 | * Use this format:
1684 | *
1685 | * new core.ToastXQuickError(message).fire();
1686 | *
1687 | */
1688 | class ToastXQuickError extends ToastXQuick {
1689 |
1690 | constructor(message) {
1691 | super(
1692 | "error",
1693 | "Something went wrong!",
1694 | message
1695 | );
1696 | }
1697 | }
1698 |
1699 | /**
1700 | * Toast through lightning:notificationsLibrary with predefined
1701 | *
mode = "dismissible"
1702 | *
duration = 8s
1703 | *
type = "success"
1704 | *
title = "Success!"
1705 | *
1706 | * Use this format:
1707 | *
1708 | * new core.ToastXLongSuccess(message).fire();
1709 | *
1710 | */
1711 | class ToastXLongSuccess extends ToastXLong {
1712 |
1713 | constructor(message) {
1714 | super(
1715 | "success",
1716 | "Success!",
1717 | message
1718 | );
1719 | }
1720 | }
1721 |
1722 | /**
1723 | * Toast through lightning:notificationsLibrary with predefined
1724 | *
mode = "dismissible"
1725 | *
duration = 8s
1726 | *
type = "error"
1727 | *
title = "Something went wrong!"
1728 | *
1729 | * Use this format:
1730 | *
1731 | * new core.ToastXLongError(message).fire();
1732 | *
1733 | */
1734 | class ToastXLongError extends ToastXLong {
1735 |
1736 | constructor(message) {
1737 | super(
1738 | "error",
1739 | "Something went wrong!",
1740 | message
1741 | );
1742 | }
1743 | }
1744 |
1745 |
1746 | /* classes to work with lightning:overlayLibrary */
1747 | /**
1748 | * Creates a Modal with lightning:overlayLibrary
1749 | */
1750 | class Modal extends Library {
1751 |
1752 | /**
1753 | * Creates a new Modal with body and Footer
1754 | *
1755 | * @param body
1756 | * @param footer
1757 | */
1758 | constructor(body = null, footer = null) {
1759 | super("lightning:overlayLibrary");
1760 | this.setBody(body);
1761 | this.setFooter(footer);
1762 | }
1763 |
1764 | /**
1765 | * Sets the body for the Modal
1766 | *
1767 | * @param body
1768 | * @returns {Modal}
1769 | */
1770 | setBody(body = null) {
1771 | this._checkAttribute(body);
1772 | this.body = body;
1773 | return this;
1774 | }
1775 |
1776 | /**
1777 | * Sets footer for the Modal
1778 | *
1779 | * @param footer
1780 | * @returns {Modal}
1781 | */
1782 | setFooter(footer = null) {
1783 | this._checkAttribute(footer);
1784 | this.footer = footer;
1785 | return this;
1786 | }
1787 |
1788 | /**
1789 | * Sets header for the Modal
1790 | *
1791 | * @param header
1792 | * @returns {Modal}
1793 | */
1794 | setHeader(header = "") {
1795 | this.header = header;
1796 | return this;
1797 | }
1798 |
1799 | /**
1800 | * Sets show close button for the Modal
1801 | *
1802 | * @param showCloseButton
1803 | * @returns {Modal}
1804 | */
1805 | setShowCloseButton(showCloseButton = true) {
1806 | this.showCloseButton = showCloseButton;
1807 | return this;
1808 | }
1809 |
1810 | /**
1811 | * Sets css class for the Modal
1812 | *
1813 | * @param cssClass
1814 | * @returns {Modal}
1815 | */
1816 | setCssClass(cssClass = "") {
1817 | this.cssClass = cssClass;
1818 | return this;
1819 | }
1820 |
1821 | /**
1822 | * Sets close callback for the Modal
1823 | *
1824 | * @param closeCallback
1825 | * @returns {Modal}
1826 | */
1827 | setCloseCallback(closeCallback) {
1828 | this.closeCallback = closeCallback;
1829 | return this;
1830 | }
1831 |
1832 | /**
1833 | * Builds component for Body and Footer and builds a Modal from them
1834 | *
1835 | * @returns {LightningPromise}
1836 | */
1837 | show() {
1838 | try {
1839 | if (new Environment().isApp()) {
1840 | throw new Error(`Core:\nlightning:overlayLibrary is not supported in App`);
1841 | }
1842 | const components = new Components();
1843 | if (this.body !== null) {
1844 | components.addComponent(this.body);
1845 | }
1846 | if (this.footer !== null) {
1847 | components.addComponent(this.footer);
1848 | }
1849 |
1850 | return new LightningPromise((resolve, reject) => {
1851 | components.create()
1852 | .then((components) => {
1853 | let body = null;
1854 | let footer = null;
1855 | if (components.length >= 1) {
1856 | body = components[0];
1857 | }
1858 | if (components.length === 2) {
1859 | footer = components[1];
1860 | }
1861 | try {
1862 | resolve(this.library.showCustomModal(this.toParams(body, footer)));
1863 | } catch (e) {
1864 | console.error(`Core:\nRunning lightning:overlayLibrary -> showCustomModal() raised an exception:\n${e}`)
1865 | reject(e)
1866 | }
1867 | })
1868 | .catch((error) => {
1869 | console.error(`Core:\nCreating component(s) for modal raised an exception:\n${error}`);
1870 | reject(error)
1871 | });
1872 | });
1873 | } catch (e) {
1874 | console.error(`Core:\nRunning lightning:overlayLibrary -> Modal.show() raised an exception:\n${e}`)
1875 | }
1876 | }
1877 |
1878 | /**
1879 | * Converts the Modal class to params, which are applicable to create a new Modal with lightning:overlayLibrary
1880 | *
1881 | * @param body
1882 | * @param footer
1883 | * @returns {{header: string, body: *, footer: *, showCloseButton: boolean, cssClass: string, closeCallback: *}}
1884 | */
1885 | toParams(body, footer) {
1886 | return {
1887 | header: this.header,
1888 | body: body,
1889 | footer: footer,
1890 | showCloseButton: this.showCloseButton,
1891 | cssClass: this.cssClass,
1892 | closeCallback: this.closeCallback
1893 | }
1894 | }
1895 |
1896 | _checkAttribute(component) {
1897 | if (component && !(component instanceof Component)) {
1898 | const message = `Core:\nArgument must be of type core.Component.\n`;
1899 | console.error(message);
1900 | throw new Error(message);
1901 | }
1902 | }
1903 | }
1904 |
1905 | /**
1906 | * Creates a Popover with lightning:overlayLibrary
1907 | */
1908 | class Popover extends Library {
1909 |
1910 | constructor(body) {
1911 | super("lightning:overlayLibrary");
1912 | this.setBody(body);
1913 | }
1914 |
1915 | /**
1916 | * Sets the body for the Popover
1917 | *
1918 | * @param body
1919 | */
1920 | setBody(body) {
1921 | this.body = body;
1922 | return this;
1923 | }
1924 |
1925 | /**
1926 | * Sets reference selector for the Popover
1927 | *
1928 | * @param referenceElementSelector
1929 | * @returns {Popover}
1930 | */
1931 | setReferenceSelector(referenceElementSelector) {
1932 | this.referenceElementSelector = referenceElementSelector;
1933 | return this;
1934 | }
1935 |
1936 | /**
1937 | * Sets reference selector for the Popover
1938 | *
1939 | * @param cssClassList
1940 | * @returns {Popover}
1941 | */
1942 | setCssClass(cssClassList) {
1943 | this.cssClassList = cssClassList;
1944 | return this;
1945 | }
1946 |
1947 | /**
1948 | * Creates a Popover and shows it in UI
1949 | *
1950 | * @returns {LightningPromise}
1951 | */
1952 | show() {
1953 | try {
1954 | if (new Environment().isApp()) {
1955 | throw new Error(`Core:\nlightning:overlayLibrary is not supported in App`);
1956 | }
1957 |
1958 | return this.library.showCustomPopover(this.toParams());
1959 | } catch (e) {
1960 | console.error(`Core:\nRunning lightning:overlayLibrary -> Popover.show() raised an exception:\n${e}`)
1961 | }
1962 | }
1963 |
1964 | /**
1965 | * Converts the Popover class to params, which are applicable to create a new Popover with lightning:overlayLibrary
1966 | *
1967 | * @returns {{body: (*|string), referenceSelector: *, cssClass: *}}
1968 | */
1969 | toParams() {
1970 | return {
1971 | body: this.body || "",
1972 | referenceSelector: this.referenceElementSelector,
1973 | cssClass: this.cssClassList
1974 | };
1975 | }
1976 | }
1977 |
1978 |
1979 | /* classes to work with browser storage */
1980 | /**
1981 | * Base class for the Local- and Session Storage
1982 | * @see LocalStorage
1983 | * @see SessionStorage
1984 | */
1985 | class Storage {
1986 |
1987 | constructor(sourceName) {
1988 | this.domain = new URL(window.location).origin;
1989 | this.source = window[sourceName];
1990 | }
1991 |
1992 | /**
1993 | * Returns domain name for current Storage
1994 | *
1995 | * @returns {string | *}
1996 | */
1997 | getDomain() {
1998 | return this.domain;
1999 | }
2000 |
2001 | /**
2002 | * Returns a named items from the Storage
2003 | *
2004 | * @param name
2005 | * @returns {Object|SVGPoint|SVGTransform|SVGNumber|string|T|SVGLength|SVGPathSeg}
2006 | */
2007 | get(name) {
2008 | return this.source.getItem(name);
2009 | }
2010 |
2011 | /**
2012 | * REturns a named object from the Storage
2013 | *
2014 | * @param name
2015 | * @returns {any}
2016 | */
2017 | getObject(name) {
2018 | return JSON.parse(this.get(name));
2019 | }
2020 |
2021 | /**
2022 | * Returns all the items from the Storage
2023 | *
2024 | * @returns {*}
2025 | */
2026 | getAll() {
2027 | return this.source;
2028 | }
2029 |
2030 | /**
2031 | * Sets a named item into the Storage
2032 | *
2033 | * @param name
2034 | * @param value
2035 | */
2036 | set(name, value) {
2037 | this.source.setItem(name, value);
2038 | }
2039 |
2040 | /**
2041 | * Sets a named object into the Storage
2042 | *
2043 | * @param name
2044 | * @param value
2045 | */
2046 | setObject(name, value) {
2047 | this.source.setItem(name, JSON.stringify(value));
2048 | }
2049 |
2050 | /**
2051 | * Checks if there's a named item in the Storage
2052 | *
2053 | * @param name
2054 | * @returns {boolean}
2055 | */
2056 | has(name) {
2057 | return !$A.util.isUndefinedOrNull(this.get(name));
2058 | }
2059 |
2060 | /**
2061 | * Removes a named items from the Storage
2062 | *
2063 | * @param name
2064 | */
2065 | remove(name) {
2066 | delete this.source[name];
2067 | }
2068 |
2069 | /**
2070 | * Removes all named items from the Storage
2071 | */
2072 | clear() {
2073 | this.source.clear();
2074 | }
2075 |
2076 | /**
2077 | * Prints everything from the Storage
2078 | */
2079 | print() {
2080 | console.log(`Storage for ${this.getDomain()}`);
2081 | const keys = Object.keys(localStorage);
2082 | for (let key of keys) {
2083 | console.log(`${key}: ${this.get(key)}`);
2084 | }
2085 | }
2086 | }
2087 |
2088 | /**
2089 | * Storage class, which represents a Local Storage
2090 | */
2091 | class LocalStorage extends Storage {
2092 |
2093 | constructor() {
2094 | super("localStorage");
2095 | }
2096 | }
2097 |
2098 | /**
2099 | * Storage class, which represents a Session Storage
2100 | */
2101 | class SessionStorage extends Storage {
2102 |
2103 | constructor() {
2104 | super("sessionStorage")
2105 | }
2106 | }
2107 |
2108 | const core = {
2109 | "File": File,
2110 |
2111 | "Toast": Toast,
2112 | "ToastQuick": ToastQuick,
2113 | "ToastLong": ToastLong,
2114 | "ToastQuickSuccess": ToastQuickSuccess,
2115 | "ToastQuickError": ToastQuickError,
2116 | "ToastLongSuccess": ToastLongSuccess,
2117 | "ToastLongError": ToastLongError,
2118 |
2119 | "Notice": Notice,
2120 | "ToastX": ToastX,
2121 | "ToastXQuick": ToastXQuick,
2122 | "ToastXLong": ToastXLong,
2123 | "ToastXQuickSuccess": ToastXQuickSuccess,
2124 | "ToastXQuickError": ToastXQuickError,
2125 | "ToastXLongSuccess": ToastXLongSuccess,
2126 | "ToastXLongError": ToastXLongError,
2127 |
2128 |
2129 | "ServerAction": ServerAction,
2130 | "ServerActionHandled": ServerActionHandled,
2131 | "ServerActionPromise": ServerActionPromise,
2132 | "ServerActionPromiseHandled": ServerActionPromiseHandled,
2133 | "LightningPromise": LightningPromise,
2134 |
2135 |
2136 | "Component": Component,
2137 | "Components": Components,
2138 |
2139 |
2140 | "Navigation": Navigation,
2141 | "PageReferenceStandardComponent": PageReferenceStandardComponent,
2142 | "PageReferenceCommLoginPage": PageReferenceCommLoginPage,
2143 | "PageReferenceKnowledgeArticlePage": PageReferenceKnowledgeArticlePage,
2144 | "PageReferenceNamedPage": PageReferenceNamedPage,
2145 | "PageReferenceNavItemPage": PageReferenceNavItemPage,
2146 | "PageReferenceObjectPage": PageReferenceObjectPage,
2147 | "PageReferenceRecordPage": PageReferenceRecordPage,
2148 | "PageReferenceRecordRelationshipPage": PageReferenceRecordRelationshipPage,
2149 | "PageReferenceWebPage": PageReferenceWebPage,
2150 |
2151 |
2152 | "Modal": Modal,
2153 | "Popover": Popover,
2154 |
2155 |
2156 | "LocalStorage": LocalStorage,
2157 | "SessionStorage": SessionStorage
2158 | };
2159 |
2160 | return ((component) => {
2161 |
2162 | const loadModules = (component, core) => {
2163 | const body = component.get("v.body");
2164 | body.filter(component => {
2165 | return component.isInstanceOf("c:lightningCoreModule");
2166 | })
2167 | .forEach(module => {
2168 | let exported = null;
2169 | try {
2170 | exported = module.export()
2171 | } catch (e) {
2172 | console.error(`Core:\nModule ${module.getName()} does not implement export method\n${e}`);
2173 | return;
2174 | }
2175 | if (!exported || !Array.isArray(exported) || exported.length !== 2 || typeof exported[0] !== 'string') {
2176 | console.error(`Core:\nModule ${module.getName()} has wrong return format.\nReturn format should be [module_name, {exported_classes}]`);
2177 | return;
2178 | }
2179 | if (core[exported[0]]) {
2180 | console.error(`Core:\n${exported[0]} namespace from module ${module.getName()} is already defined in lightning-core. Please, consider other module name`);
2181 | return;
2182 | }
2183 | core[exported[0]] = exported[1];
2184 | });
2185 | };
2186 |
2187 | return {
2188 | init: () => {
2189 | if ($A.util.isUndefinedOrNull(window.core)) {
2190 | Object.defineProperty(window, "core", {
2191 | writable: false,
2192 | configurable: false,
2193 | enumerable: false,
2194 | value: core
2195 | });
2196 | loadModules(component, core);
2197 | }
2198 | }
2199 | };
2200 | })(component);
2201 |
2202 | }
2203 | })
2204 |
--------------------------------------------------------------------------------
/src/aura/lightningCoreModule/lightningCoreModule.intf:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/aura/lightningCoreModule/lightningCoreModule.intf-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 46.0
4 | lightningCoreModule
5 |
6 |
--------------------------------------------------------------------------------