saasufy-components 3.14.0 → 3.15.1
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 +27 -1
- package/confirm-modal.js +5 -5
- package/download-link.js +94 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,6 +77,7 @@ To use components, you just need to include them into your `.html` file inside y
|
|
|
77
77
|
<script src="https://saasufy.com/node_modules/saasufy-components/collection-adder-group.js" type="module" defer></script>
|
|
78
78
|
<script src="https://saasufy.com/node_modules/saasufy-components/confirm-modal.js" type="module" defer></script>
|
|
79
79
|
<script src="https://saasufy.com/node_modules/saasufy-components/overlay-modal.js" type="module" defer></script>
|
|
80
|
+
<script src="https://saasufy.com/node_modules/saasufy-components/download-link.js" type="module" defer></script>
|
|
80
81
|
```
|
|
81
82
|
|
|
82
83
|
You should only add the script tags for the components which your page uses to avoid wasting bandwidth and unnecessarily delaying page load for your users.
|
|
@@ -100,7 +101,7 @@ This project does not impose any specific approach to styling components, howeve
|
|
|
100
101
|
The following utility functions can be used anywhere inside template `{{expression}}` placeholders:
|
|
101
102
|
|
|
102
103
|
- Generate a unique random ID in UUID format: `{{uuid.v4()}}`
|
|
103
|
-
- Generate a deterministic ID in UUID-compatible format: `{{computeId('value1', 'value2')}}`
|
|
104
|
+
- Generate a deterministic ID in UUID-compatible format derived from one or more values: `{{computeId('value1', 'value2')}}`
|
|
104
105
|
- Convert text to a format suitable for use inside a URL: `{{url('This is a test')}}`
|
|
105
106
|
- Convert text to lower case: `{{lowerCase('TEST')}}`
|
|
106
107
|
- Convert text to upper case: `{{upperCase('test')}}`
|
|
@@ -895,3 +896,28 @@ A modal component to prompt the user for confirmation before performing sensitiv
|
|
|
895
896
|
### overlay-modal
|
|
896
897
|
|
|
897
898
|
A general purpose modal component.
|
|
899
|
+
|
|
900
|
+
### download-link
|
|
901
|
+
|
|
902
|
+
A component to download a file from a model. The file must be stored on the model instance in base64 format.
|
|
903
|
+
|
|
904
|
+
**Import**
|
|
905
|
+
|
|
906
|
+
```html
|
|
907
|
+
<script src="https://saasufy.com/node_modules/saasufy-components/download-link.js" type="module" defer></script>
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
**Example usage**
|
|
911
|
+
|
|
912
|
+
```html
|
|
913
|
+
<download-link model-type="Image" model-id="dce962f8-34f1-4a82-95b9-377e42b57e2d" model-field="src" file-name="my-image.png">
|
|
914
|
+
<a href="javascript: void(0)">Download image</a>
|
|
915
|
+
</download-link>
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
**Attributes**
|
|
919
|
+
|
|
920
|
+
- `model-type` (required): Specifies the type of model to download the file from. This should match a `Model` available in your Saasufy service.
|
|
921
|
+
- `model-id` (required): The id of the model instance/record to download the file from.
|
|
922
|
+
- `model-field` (required): The field of the model instance/record where the file is stored in base64 format (with content type).
|
|
923
|
+
- `file-name` (required): The default name of the file to save as. The file extension is optional.
|
package/confirm-modal.js
CHANGED
|
@@ -24,7 +24,7 @@ class ConfirmModal extends HTMLElement {
|
|
|
24
24
|
show(message, confirmCallback) {
|
|
25
25
|
let overlayModal = this.querySelector('overlay-modal');
|
|
26
26
|
if (overlayModal) {
|
|
27
|
-
overlayModal.
|
|
27
|
+
overlayModal.removeAttribute('style');
|
|
28
28
|
}
|
|
29
29
|
this.confirmCallback = confirmCallback;
|
|
30
30
|
this.hidden = false;
|
|
@@ -38,7 +38,7 @@ class ConfirmModal extends HTMLElement {
|
|
|
38
38
|
let cancelButtonLabel = this.getAttribute('cancel-button-label') || 'Cancel';
|
|
39
39
|
|
|
40
40
|
this.innerHTML = `
|
|
41
|
-
<overlay-modal
|
|
41
|
+
<overlay-modal${this.hidden ? ' style="display: none;"' : ''}>
|
|
42
42
|
<div slot="title">${title}</div>
|
|
43
43
|
<div class="confirm-modal-content" slot="content">
|
|
44
44
|
<div>${message}</div>
|
|
@@ -52,16 +52,16 @@ class ConfirmModal extends HTMLElement {
|
|
|
52
52
|
|
|
53
53
|
let overlayModal = this.querySelector('overlay-modal');
|
|
54
54
|
overlayModal.addEventListener('close', () => {
|
|
55
|
-
overlayModal.
|
|
55
|
+
overlayModal.style.display = 'none';
|
|
56
56
|
});
|
|
57
57
|
let cancelButton = this.querySelector('.modal-cancel-button');
|
|
58
58
|
cancelButton.addEventListener('click', () => {
|
|
59
|
-
overlayModal.
|
|
59
|
+
overlayModal.style.display = 'none';
|
|
60
60
|
this.dispatchEvent(new CustomEvent('cancel'));
|
|
61
61
|
});
|
|
62
62
|
let confirmButton = this.querySelector('.modal-confirm-button');
|
|
63
63
|
confirmButton.addEventListener('click', () => {
|
|
64
|
-
overlayModal.
|
|
64
|
+
overlayModal.style.display = 'none';
|
|
65
65
|
this.dispatchEvent(new CustomEvent('confirm'));
|
|
66
66
|
this.confirmCallback && this.confirmCallback();
|
|
67
67
|
});
|
package/download-link.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { SocketConsumer } from './socket.js';
|
|
2
|
+
|
|
3
|
+
class DownloadLink extends SocketConsumer {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
let dataTypeRegExp = /data:([^;]+);base64,(.*)/;
|
|
7
|
+
|
|
8
|
+
this.handleLinkClickEvent = async (event) => {
|
|
9
|
+
let socket = this.getSocket();
|
|
10
|
+
|
|
11
|
+
let modelType = this.getAttribute('model-type');
|
|
12
|
+
let modelId = this.getAttribute('model-id');
|
|
13
|
+
let modelField = this.getAttribute('model-field');
|
|
14
|
+
|
|
15
|
+
this.setAttribute('is-loading', '');
|
|
16
|
+
this.removeAttribute('failed');
|
|
17
|
+
|
|
18
|
+
let data;
|
|
19
|
+
try {
|
|
20
|
+
data = await socket.invoke('crud', {
|
|
21
|
+
action: 'read',
|
|
22
|
+
type: modelType,
|
|
23
|
+
id: modelId,
|
|
24
|
+
field: modelField
|
|
25
|
+
});
|
|
26
|
+
} catch (error) {
|
|
27
|
+
this.setAttribute('failed', '');
|
|
28
|
+
if (!this.hasAttribute('hide-error-logs')) {
|
|
29
|
+
console.error(
|
|
30
|
+
`Failed to download file because of error: ${error.message}`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
this.removeAttribute('is-loading');
|
|
36
|
+
|
|
37
|
+
if (typeof data !== 'string') {
|
|
38
|
+
this.setAttribute('failed', '');
|
|
39
|
+
if (!this.hasAttribute('hide-error-logs')) {
|
|
40
|
+
console.error(
|
|
41
|
+
'Failed to download file because it did not exist'
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
let dataParts = data.match(dataTypeRegExp);
|
|
48
|
+
if (!dataParts) {
|
|
49
|
+
this.setAttribute('failed', '');
|
|
50
|
+
if (!this.hasAttribute('hide-error-logs')) {
|
|
51
|
+
console.error(
|
|
52
|
+
'Failed to process downloaded file because format was invalid'
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let contentType = dataParts[1];
|
|
59
|
+
let content = dataParts[2];
|
|
60
|
+
|
|
61
|
+
let binaryString = atob(content);
|
|
62
|
+
let length = binaryString.length;
|
|
63
|
+
let bytes = new Uint8Array(length);
|
|
64
|
+
for (let i = 0; i < length; i++) {
|
|
65
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
66
|
+
}
|
|
67
|
+
let blob = new Blob([ bytes ], { type: contentType });
|
|
68
|
+
|
|
69
|
+
let fileName = this.getAttribute('file-name');
|
|
70
|
+
let fileDataURL = URL.createObjectURL(blob);
|
|
71
|
+
|
|
72
|
+
let downloadElement = document.createElement('a');
|
|
73
|
+
downloadElement.style.display = 'none';
|
|
74
|
+
downloadElement.setAttribute('href', fileDataURL);
|
|
75
|
+
downloadElement.setAttribute('download', fileName);
|
|
76
|
+
document.body.appendChild(downloadElement);
|
|
77
|
+
downloadElement.click();
|
|
78
|
+
downloadElement.remove();
|
|
79
|
+
URL.revokeObjectURL(fileDataURL);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
connectedCallback() {
|
|
84
|
+
this.removeAttribute('failed');
|
|
85
|
+
this.removeAttribute('is-loading');
|
|
86
|
+
this.addEventListener('click', this.handleLinkClickEvent);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
disconnectedCallback() {
|
|
90
|
+
this.removeEventListener('click', this.handleLinkClickEvent);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
window.customElements.define('download-link', DownloadLink);
|