@doyosi/laraisy 1.0.1 → 1.0.3
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/LICENSE +1 -1
- package/package.json +1 -1
- package/src/CodeInput.js +48 -48
- package/src/DSAlert.js +352 -352
- package/src/DSAvatar.js +207 -207
- package/src/DSDelete.js +274 -274
- package/src/DSForm.js +568 -568
- package/src/DSGridOrTable.js +453 -453
- package/src/DSLocaleSwitcher.js +239 -239
- package/src/DSLogout.js +293 -293
- package/src/DSNotifications.js +365 -365
- package/src/DSRestore.js +181 -181
- package/src/DSSelect.js +1071 -1071
- package/src/DSSelectBox.js +563 -563
- package/src/DSSimpleSlider.js +517 -517
- package/src/DSSvgFetch.js +69 -69
- package/src/DSTable/DSTableExport.js +68 -68
- package/src/DSTable/DSTableFilter.js +224 -224
- package/src/DSTable/DSTablePagination.js +136 -136
- package/src/DSTable/DSTableSearch.js +40 -40
- package/src/DSTable/DSTableSelection.js +192 -192
- package/src/DSTable/DSTableSort.js +58 -58
- package/src/DSTable.js +353 -353
- package/src/DSTabs.js +488 -488
- package/src/DSUpload.js +887 -887
- package/dist/CodeInput.d.ts +0 -10
- package/dist/DSAlert.d.ts +0 -112
- package/dist/DSAvatar.d.ts +0 -45
- package/dist/DSDelete.d.ts +0 -61
- package/dist/DSForm.d.ts +0 -151
- package/dist/DSGridOrTable/DSGOTRenderer.d.ts +0 -60
- package/dist/DSGridOrTable/DSGOTViewToggle.d.ts +0 -26
- package/dist/DSGridOrTable.d.ts +0 -296
- package/dist/DSLocaleSwitcher.d.ts +0 -71
- package/dist/DSLogout.d.ts +0 -76
- package/dist/DSNotifications.d.ts +0 -54
- package/dist/DSRestore.d.ts +0 -56
- package/dist/DSSelect.d.ts +0 -221
- package/dist/DSSelectBox.d.ts +0 -123
- package/dist/DSSimpleSlider.d.ts +0 -136
- package/dist/DSSvgFetch.d.ts +0 -17
- package/dist/DSTable/DSTableExport.d.ts +0 -11
- package/dist/DSTable/DSTableFilter.d.ts +0 -40
- package/dist/DSTable/DSTablePagination.d.ts +0 -12
- package/dist/DSTable/DSTableSearch.d.ts +0 -8
- package/dist/DSTable/DSTableSelection.d.ts +0 -46
- package/dist/DSTable/DSTableSort.d.ts +0 -8
- package/dist/DSTable.d.ts +0 -116
- package/dist/DSTabs.d.ts +0 -156
- package/dist/DSUpload.d.ts +0 -220
- package/dist/index.d.ts +0 -17
package/src/DSAvatar.js
CHANGED
|
@@ -1,208 +1,208 @@
|
|
|
1
|
-
import { DSAlert } from './DSAlert.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* DSAvatar
|
|
5
|
-
*
|
|
6
|
-
* Handles avatar file uploads and resets via AJAX.
|
|
7
|
-
* - Integrates with DSAlert for notifications.
|
|
8
|
-
* - Manages loading states and UI updates.
|
|
9
|
-
* - Uses Laravel-style CSRF protection.
|
|
10
|
-
*/
|
|
11
|
-
export class DSAvatar {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @param {string|HTMLElement} selector - The .avatar-uploader wrapper
|
|
16
|
-
* @param {Object} config - Configuration overrides
|
|
17
|
-
*/
|
|
18
|
-
constructor(selector, config = {}) {
|
|
19
|
-
this.wrapper = typeof selector === 'string' ? document.querySelector(selector) : selector;
|
|
20
|
-
if (!this.wrapper) throw new Error('DSAvatar: Wrapper not found.');
|
|
21
|
-
|
|
22
|
-
this.cfg = {
|
|
23
|
-
headers: {},
|
|
24
|
-
...config
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
// Cache elements
|
|
28
|
-
this.img = this.wrapper.querySelector('[data-ds-avatar-img]');
|
|
29
|
-
this.input = this.wrapper.querySelector('[data-ds-avatar-input]');
|
|
30
|
-
this.loadingOverlay = this.wrapper.querySelector('[data-ds-avatar-loading]');
|
|
31
|
-
this.triggerBtn = this.wrapper.querySelector('[data-ds-avatar-action="trigger-upload"]');
|
|
32
|
-
this.removeBtn = this.wrapper.querySelector('[data-ds-avatar-action="trigger-remove"]');
|
|
33
|
-
|
|
34
|
-
// Data attributes
|
|
35
|
-
this.uploadUrl = this.wrapper.dataset.uploadUrl;
|
|
36
|
-
this.removeUrl = this.wrapper.dataset.removeUrl;
|
|
37
|
-
this.inputName = this.wrapper.dataset.name;
|
|
38
|
-
|
|
39
|
-
// State backup for error reversion
|
|
40
|
-
this._backupSrc = this.img ? this.img.src : '';
|
|
41
|
-
|
|
42
|
-
this.bind();
|
|
43
|
-
this.initTooltips();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
initTooltips() {
|
|
47
|
-
if (window.tippy) {
|
|
48
|
-
if (this.triggerBtn) window.tippy(this.triggerBtn);
|
|
49
|
-
if (this.removeBtn) window.tippy(this.removeBtn);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
bind() {
|
|
54
|
-
// Trigger file input click
|
|
55
|
-
if (this.triggerBtn) {
|
|
56
|
-
this.triggerBtn.addEventListener('click', (e) => {
|
|
57
|
-
e.preventDefault();
|
|
58
|
-
this.input.click();
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Handle file selection
|
|
63
|
-
if (this.input) {
|
|
64
|
-
this.input.addEventListener('change', (e) => this._handleFileSelect(e));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Handle remove/reset
|
|
68
|
-
if (this.removeBtn) {
|
|
69
|
-
this.removeBtn.addEventListener('click', (e) => {
|
|
70
|
-
e.preventDefault();
|
|
71
|
-
this._handleRemove();
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Upload Process
|
|
78
|
-
*/
|
|
79
|
-
async _handleFileSelect(e) {
|
|
80
|
-
const file = e.target.files[0];
|
|
81
|
-
if (!file) return;
|
|
82
|
-
|
|
83
|
-
if (!this.uploadUrl) {
|
|
84
|
-
console.warn('DSAvatar: No upload URL provided.');
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// 1. Show loading
|
|
89
|
-
this._toggleLoading(true);
|
|
90
|
-
|
|
91
|
-
// 2. Prepare Data
|
|
92
|
-
const formData = new FormData();
|
|
93
|
-
formData.append(this.inputName, file);
|
|
94
|
-
// Append _method if needed, usually POST for upload
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
const { response, data } = await this._sendRequest(this.uploadUrl, 'POST', formData);
|
|
98
|
-
|
|
99
|
-
if (response.ok) {
|
|
100
|
-
// Success: Update Image & Toast
|
|
101
|
-
if (data.url) this.img.src = data.url;
|
|
102
|
-
this._backupSrc = data.url; // Update backup on success
|
|
103
|
-
this.input.value = ''; // Clear input to allow re-uploading same file
|
|
104
|
-
this._toast(data.message || 'Avatar updated successfully', 'success');
|
|
105
|
-
} else {
|
|
106
|
-
// Server Error
|
|
107
|
-
throw new Error(data.message || 'Upload failed');
|
|
108
|
-
}
|
|
109
|
-
} catch (err) {
|
|
110
|
-
// Error: Revert & Toast
|
|
111
|
-
console.error(err);
|
|
112
|
-
this.img.src = this._backupSrc;
|
|
113
|
-
this.input.value = '';
|
|
114
|
-
this._toast(err.message || 'Network error occurred', 'error');
|
|
115
|
-
} finally {
|
|
116
|
-
this._toggleLoading(false);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Remove/Reset Process
|
|
122
|
-
*/
|
|
123
|
-
async _handleRemove() {
|
|
124
|
-
if (!this.removeUrl) return;
|
|
125
|
-
|
|
126
|
-
// Confirm dialog (Optional, but good UX)
|
|
127
|
-
/*
|
|
128
|
-
const confirmed = confirm('Reset avatar to default?');
|
|
129
|
-
if (!confirmed) return;
|
|
130
|
-
*/
|
|
131
|
-
|
|
132
|
-
this._toggleLoading(true);
|
|
133
|
-
|
|
134
|
-
try {
|
|
135
|
-
// Usually DELETE or POST with _method=DELETE
|
|
136
|
-
const formData = new FormData();
|
|
137
|
-
formData.append('_method', 'DELETE');
|
|
138
|
-
|
|
139
|
-
const { response, data } = await this._sendRequest(this.removeUrl, 'POST', formData);
|
|
140
|
-
|
|
141
|
-
if (response.ok) {
|
|
142
|
-
if (data.url) this.img.src = data.url; // Server usually returns the default placeholder URL
|
|
143
|
-
this._backupSrc = this.img.src;
|
|
144
|
-
this._toast(data.message || 'Avatar reset successfully', 'success');
|
|
145
|
-
} else {
|
|
146
|
-
throw new Error(data.message || 'Reset failed');
|
|
147
|
-
}
|
|
148
|
-
} catch (err) {
|
|
149
|
-
console.error(err);
|
|
150
|
-
this._toast(err.message || 'Could not reset avatar', 'error');
|
|
151
|
-
} finally {
|
|
152
|
-
this._toggleLoading(false);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Shared Request Logic (mirrors DSForm)
|
|
158
|
-
*/
|
|
159
|
-
async _sendRequest(url, method, body) {
|
|
160
|
-
const headers = {
|
|
161
|
-
'Accept': 'application/json',
|
|
162
|
-
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '',
|
|
163
|
-
...this.cfg.headers
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
const resp = await fetch(url, {
|
|
167
|
-
method: method,
|
|
168
|
-
headers: headers,
|
|
169
|
-
body: body
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
let data = {};
|
|
173
|
-
try {
|
|
174
|
-
data = await resp.json();
|
|
175
|
-
} catch (e) { /* ignore non-json */ }
|
|
176
|
-
|
|
177
|
-
return { response: resp, data };
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
_toggleLoading(show) {
|
|
181
|
-
if (show) {
|
|
182
|
-
this.loadingOverlay.classList.remove('hidden');
|
|
183
|
-
if (this.triggerBtn) this.triggerBtn.disabled = true;
|
|
184
|
-
if (this.removeBtn) this.removeBtn.disabled = true;
|
|
185
|
-
} else {
|
|
186
|
-
this.loadingOverlay.classList.add('hidden');
|
|
187
|
-
if (this.triggerBtn) this.triggerBtn.disabled = false;
|
|
188
|
-
if (this.removeBtn) this.removeBtn.disabled = false;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
_toast(message, type = 'info') {
|
|
193
|
-
if (typeof DSAlert !== 'undefined') {
|
|
194
|
-
DSAlert.fire({
|
|
195
|
-
toast: true,
|
|
196
|
-
icon: type,
|
|
197
|
-
title: message,
|
|
198
|
-
position: 'top-end',
|
|
199
|
-
showConfirmButton: false,
|
|
200
|
-
timer: 3000,
|
|
201
|
-
timerProgressBar: true,
|
|
202
|
-
});
|
|
203
|
-
} else {
|
|
204
|
-
// Fallback if DSAlert is missing
|
|
205
|
-
console.log(`[${type.toUpperCase()}] ${message}`);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
1
|
+
import { DSAlert } from './DSAlert.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* DSAvatar
|
|
5
|
+
*
|
|
6
|
+
* Handles avatar file uploads and resets via AJAX.
|
|
7
|
+
* - Integrates with DSAlert for notifications.
|
|
8
|
+
* - Manages loading states and UI updates.
|
|
9
|
+
* - Uses Laravel-style CSRF protection.
|
|
10
|
+
*/
|
|
11
|
+
export class DSAvatar {
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string|HTMLElement} selector - The .avatar-uploader wrapper
|
|
16
|
+
* @param {Object} config - Configuration overrides
|
|
17
|
+
*/
|
|
18
|
+
constructor(selector, config = {}) {
|
|
19
|
+
this.wrapper = typeof selector === 'string' ? document.querySelector(selector) : selector;
|
|
20
|
+
if (!this.wrapper) throw new Error('DSAvatar: Wrapper not found.');
|
|
21
|
+
|
|
22
|
+
this.cfg = {
|
|
23
|
+
headers: {},
|
|
24
|
+
...config
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Cache elements
|
|
28
|
+
this.img = this.wrapper.querySelector('[data-ds-avatar-img]');
|
|
29
|
+
this.input = this.wrapper.querySelector('[data-ds-avatar-input]');
|
|
30
|
+
this.loadingOverlay = this.wrapper.querySelector('[data-ds-avatar-loading]');
|
|
31
|
+
this.triggerBtn = this.wrapper.querySelector('[data-ds-avatar-action="trigger-upload"]');
|
|
32
|
+
this.removeBtn = this.wrapper.querySelector('[data-ds-avatar-action="trigger-remove"]');
|
|
33
|
+
|
|
34
|
+
// Data attributes
|
|
35
|
+
this.uploadUrl = this.wrapper.dataset.uploadUrl;
|
|
36
|
+
this.removeUrl = this.wrapper.dataset.removeUrl;
|
|
37
|
+
this.inputName = this.wrapper.dataset.name;
|
|
38
|
+
|
|
39
|
+
// State backup for error reversion
|
|
40
|
+
this._backupSrc = this.img ? this.img.src : '';
|
|
41
|
+
|
|
42
|
+
this.bind();
|
|
43
|
+
this.initTooltips();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
initTooltips() {
|
|
47
|
+
if (window.tippy) {
|
|
48
|
+
if (this.triggerBtn) window.tippy(this.triggerBtn);
|
|
49
|
+
if (this.removeBtn) window.tippy(this.removeBtn);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
bind() {
|
|
54
|
+
// Trigger file input click
|
|
55
|
+
if (this.triggerBtn) {
|
|
56
|
+
this.triggerBtn.addEventListener('click', (e) => {
|
|
57
|
+
e.preventDefault();
|
|
58
|
+
this.input.click();
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Handle file selection
|
|
63
|
+
if (this.input) {
|
|
64
|
+
this.input.addEventListener('change', (e) => this._handleFileSelect(e));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Handle remove/reset
|
|
68
|
+
if (this.removeBtn) {
|
|
69
|
+
this.removeBtn.addEventListener('click', (e) => {
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
this._handleRemove();
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Upload Process
|
|
78
|
+
*/
|
|
79
|
+
async _handleFileSelect(e) {
|
|
80
|
+
const file = e.target.files[0];
|
|
81
|
+
if (!file) return;
|
|
82
|
+
|
|
83
|
+
if (!this.uploadUrl) {
|
|
84
|
+
console.warn('DSAvatar: No upload URL provided.');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 1. Show loading
|
|
89
|
+
this._toggleLoading(true);
|
|
90
|
+
|
|
91
|
+
// 2. Prepare Data
|
|
92
|
+
const formData = new FormData();
|
|
93
|
+
formData.append(this.inputName, file);
|
|
94
|
+
// Append _method if needed, usually POST for upload
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const { response, data } = await this._sendRequest(this.uploadUrl, 'POST', formData);
|
|
98
|
+
|
|
99
|
+
if (response.ok) {
|
|
100
|
+
// Success: Update Image & Toast
|
|
101
|
+
if (data.url) this.img.src = data.url;
|
|
102
|
+
this._backupSrc = data.url; // Update backup on success
|
|
103
|
+
this.input.value = ''; // Clear input to allow re-uploading same file
|
|
104
|
+
this._toast(data.message || 'Avatar updated successfully', 'success');
|
|
105
|
+
} else {
|
|
106
|
+
// Server Error
|
|
107
|
+
throw new Error(data.message || 'Upload failed');
|
|
108
|
+
}
|
|
109
|
+
} catch (err) {
|
|
110
|
+
// Error: Revert & Toast
|
|
111
|
+
console.error(err);
|
|
112
|
+
this.img.src = this._backupSrc;
|
|
113
|
+
this.input.value = '';
|
|
114
|
+
this._toast(err.message || 'Network error occurred', 'error');
|
|
115
|
+
} finally {
|
|
116
|
+
this._toggleLoading(false);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Remove/Reset Process
|
|
122
|
+
*/
|
|
123
|
+
async _handleRemove() {
|
|
124
|
+
if (!this.removeUrl) return;
|
|
125
|
+
|
|
126
|
+
// Confirm dialog (Optional, but good UX)
|
|
127
|
+
/*
|
|
128
|
+
const confirmed = confirm('Reset avatar to default?');
|
|
129
|
+
if (!confirmed) return;
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
this._toggleLoading(true);
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
// Usually DELETE or POST with _method=DELETE
|
|
136
|
+
const formData = new FormData();
|
|
137
|
+
formData.append('_method', 'DELETE');
|
|
138
|
+
|
|
139
|
+
const { response, data } = await this._sendRequest(this.removeUrl, 'POST', formData);
|
|
140
|
+
|
|
141
|
+
if (response.ok) {
|
|
142
|
+
if (data.url) this.img.src = data.url; // Server usually returns the default placeholder URL
|
|
143
|
+
this._backupSrc = this.img.src;
|
|
144
|
+
this._toast(data.message || 'Avatar reset successfully', 'success');
|
|
145
|
+
} else {
|
|
146
|
+
throw new Error(data.message || 'Reset failed');
|
|
147
|
+
}
|
|
148
|
+
} catch (err) {
|
|
149
|
+
console.error(err);
|
|
150
|
+
this._toast(err.message || 'Could not reset avatar', 'error');
|
|
151
|
+
} finally {
|
|
152
|
+
this._toggleLoading(false);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Shared Request Logic (mirrors DSForm)
|
|
158
|
+
*/
|
|
159
|
+
async _sendRequest(url, method, body) {
|
|
160
|
+
const headers = {
|
|
161
|
+
'Accept': 'application/json',
|
|
162
|
+
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '',
|
|
163
|
+
...this.cfg.headers
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const resp = await fetch(url, {
|
|
167
|
+
method: method,
|
|
168
|
+
headers: headers,
|
|
169
|
+
body: body
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
let data = {};
|
|
173
|
+
try {
|
|
174
|
+
data = await resp.json();
|
|
175
|
+
} catch (e) { /* ignore non-json */ }
|
|
176
|
+
|
|
177
|
+
return { response: resp, data };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
_toggleLoading(show) {
|
|
181
|
+
if (show) {
|
|
182
|
+
this.loadingOverlay.classList.remove('hidden');
|
|
183
|
+
if (this.triggerBtn) this.triggerBtn.disabled = true;
|
|
184
|
+
if (this.removeBtn) this.removeBtn.disabled = true;
|
|
185
|
+
} else {
|
|
186
|
+
this.loadingOverlay.classList.add('hidden');
|
|
187
|
+
if (this.triggerBtn) this.triggerBtn.disabled = false;
|
|
188
|
+
if (this.removeBtn) this.removeBtn.disabled = false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
_toast(message, type = 'info') {
|
|
193
|
+
if (typeof DSAlert !== 'undefined') {
|
|
194
|
+
DSAlert.fire({
|
|
195
|
+
toast: true,
|
|
196
|
+
icon: type,
|
|
197
|
+
title: message,
|
|
198
|
+
position: 'top-end',
|
|
199
|
+
showConfirmButton: false,
|
|
200
|
+
timer: 3000,
|
|
201
|
+
timerProgressBar: true,
|
|
202
|
+
});
|
|
203
|
+
} else {
|
|
204
|
+
// Fallback if DSAlert is missing
|
|
205
|
+
console.log(`[${type.toUpperCase()}] ${message}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
208
|
}
|