@dejwcake/craftable 2.3.0 → 2.5.0
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/dist/index.cjs +1 -1
- package/dist/index.mjs +690 -524
- package/package.json +1 -1
- package/src/components/listing/BulkOperationsBar.vue +19 -4
- package/src/components/listing/ForceDeleteButton.vue +57 -0
- package/src/components/listing/RestoreButton.vue +62 -0
- package/src/components/listing/ShowDeleted.vue +28 -0
- package/src/composables/useBaseListing.js +64 -13
- package/src/index.js +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dejwcake/craftable",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "This is a frontend dependency for Craftable. Craftable is a Laravel-based open-source toolkit for building administration interfaces.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -13,9 +13,20 @@
|
|
|
13
13
|
|
|
14
14
|
<span class="float-end pe-2">
|
|
15
15
|
<slot>
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
<span class="d-inline-flex gap-2">
|
|
17
|
+
<button v-if="onBulkDelete" class="btn btn-sm btn-danger px-4" @click="onBulkDelete()">
|
|
18
|
+
<i class="fa fa-trash me-1"></i>
|
|
19
|
+
{{ translations.delete_btn }}
|
|
20
|
+
</button>
|
|
21
|
+
<button v-if="onBulkForceDelete" class="btn btn-sm btn-dark px-4" @click="onBulkForceDelete()">
|
|
22
|
+
<i class="fa fa-eraser me-1"></i>
|
|
23
|
+
{{ translations.force_delete_btn }}
|
|
24
|
+
</button>
|
|
25
|
+
<button v-if="onBulkRestore" class="btn btn-sm btn-success text-white px-4" @click="onBulkRestore()">
|
|
26
|
+
<i class="fa fa-trash-arrow-up me-1"></i>
|
|
27
|
+
{{ translations.restore_btn }}
|
|
28
|
+
</button>
|
|
29
|
+
</span>
|
|
19
30
|
</slot>
|
|
20
31
|
</span>
|
|
21
32
|
</template>
|
|
@@ -27,7 +38,9 @@ defineProps({
|
|
|
27
38
|
loading: { type: Boolean, default: false },
|
|
28
39
|
onCheckAll: { type: Function, required: true },
|
|
29
40
|
onUncheckAll: { type: Function, required: true },
|
|
30
|
-
onBulkDelete: { type: Function,
|
|
41
|
+
onBulkDelete: { type: Function, default: null },
|
|
42
|
+
onBulkForceDelete: { type: Function, default: null },
|
|
43
|
+
onBulkRestore: { type: Function, default: null },
|
|
31
44
|
translations: {
|
|
32
45
|
type: Object,
|
|
33
46
|
default: () => ({
|
|
@@ -35,6 +48,8 @@ defineProps({
|
|
|
35
48
|
check_all_items: 'Check all items',
|
|
36
49
|
uncheck_all_items: 'Uncheck all items',
|
|
37
50
|
delete_btn: 'Delete',
|
|
51
|
+
force_delete_btn: 'Delete permanently',
|
|
52
|
+
restore_btn: 'Restore',
|
|
38
53
|
}),
|
|
39
54
|
},
|
|
40
55
|
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button
|
|
3
|
+
type="button"
|
|
4
|
+
class="btn btn-sm btn-dark"
|
|
5
|
+
:title="translations.force_delete_btn"
|
|
6
|
+
@click="showConfirm = true"
|
|
7
|
+
>
|
|
8
|
+
<i class="fa fa-eraser"></i>
|
|
9
|
+
</button>
|
|
10
|
+
|
|
11
|
+
<ConfirmModal
|
|
12
|
+
:show="showConfirm"
|
|
13
|
+
variant="danger"
|
|
14
|
+
:translations="translations"
|
|
15
|
+
@confirm="onConfirm"
|
|
16
|
+
@cancel="showConfirm = false"
|
|
17
|
+
/>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script setup>
|
|
21
|
+
import { ref } from 'vue';
|
|
22
|
+
const axios = window.axios;
|
|
23
|
+
import { notifySuccess, notifyError } from '../../utils/notify.js';
|
|
24
|
+
import ConfirmModal from '../ConfirmModal.vue';
|
|
25
|
+
|
|
26
|
+
const props = defineProps({
|
|
27
|
+
url: { type: String, required: true },
|
|
28
|
+
translations: {
|
|
29
|
+
type: Object,
|
|
30
|
+
default: () => ({
|
|
31
|
+
force_delete_btn: 'Delete permanently',
|
|
32
|
+
confirm_title: 'Deletion',
|
|
33
|
+
confirm_text: 'Delete this item permanently? This cannot be undone.',
|
|
34
|
+
confirm_btn: 'Delete permanently',
|
|
35
|
+
cancel_btn: 'Cancel',
|
|
36
|
+
}),
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const emit = defineEmits(['deleted']);
|
|
41
|
+
|
|
42
|
+
const showConfirm = ref(false);
|
|
43
|
+
|
|
44
|
+
function onConfirm() {
|
|
45
|
+
showConfirm.value = false;
|
|
46
|
+
|
|
47
|
+
axios.delete(props.url).then(
|
|
48
|
+
(response) => {
|
|
49
|
+
notifySuccess(response.data.message);
|
|
50
|
+
emit('deleted');
|
|
51
|
+
},
|
|
52
|
+
(error) => {
|
|
53
|
+
notifyError(error.response?.data?.message);
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
</script>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<span :title="disabled ? translations.not_restorable : translations.restore_btn">
|
|
3
|
+
<button
|
|
4
|
+
type="button"
|
|
5
|
+
class="btn btn-sm btn-success text-white"
|
|
6
|
+
:aria-label="disabled ? translations.not_restorable : translations.restore_btn"
|
|
7
|
+
:disabled="disabled"
|
|
8
|
+
@click="showConfirm = true"
|
|
9
|
+
>
|
|
10
|
+
<i class="fa fa-trash-arrow-up"></i>
|
|
11
|
+
</button>
|
|
12
|
+
</span>
|
|
13
|
+
|
|
14
|
+
<ConfirmModal
|
|
15
|
+
:show="showConfirm"
|
|
16
|
+
variant="success"
|
|
17
|
+
:translations="translations"
|
|
18
|
+
@confirm="onConfirm"
|
|
19
|
+
@cancel="showConfirm = false"
|
|
20
|
+
/>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<script setup>
|
|
24
|
+
import { ref } from 'vue';
|
|
25
|
+
const axios = window.axios;
|
|
26
|
+
import { notifySuccess, notifyError } from '../../utils/notify.js';
|
|
27
|
+
import ConfirmModal from '../ConfirmModal.vue';
|
|
28
|
+
|
|
29
|
+
const props = defineProps({
|
|
30
|
+
url: { type: String, required: true },
|
|
31
|
+
disabled: { type: Boolean, default: false },
|
|
32
|
+
translations: {
|
|
33
|
+
type: Object,
|
|
34
|
+
default: () => ({
|
|
35
|
+
restore_btn: 'Restore',
|
|
36
|
+
not_restorable: 'This item cannot be restored, an item it belongs to is deleted.',
|
|
37
|
+
confirm_title: 'Restore',
|
|
38
|
+
confirm_text: 'Do you really want to restore this item?',
|
|
39
|
+
confirm_btn: 'Restore',
|
|
40
|
+
cancel_btn: 'Cancel',
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const emit = defineEmits(['restored']);
|
|
46
|
+
|
|
47
|
+
const showConfirm = ref(false);
|
|
48
|
+
|
|
49
|
+
function onConfirm() {
|
|
50
|
+
showConfirm.value = false;
|
|
51
|
+
|
|
52
|
+
axios.post(props.url).then(
|
|
53
|
+
(response) => {
|
|
54
|
+
notifySuccess(response.data.message);
|
|
55
|
+
emit('restored');
|
|
56
|
+
},
|
|
57
|
+
(error) => {
|
|
58
|
+
notifyError(error.response?.data?.message);
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
</script>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<label class="d-inline-flex align-items-center gap-2 mb-0 text-nowrap text-reset">
|
|
3
|
+
<span class="form-check form-switch form-switch-inline mb-0">
|
|
4
|
+
<input
|
|
5
|
+
class="form-check-input"
|
|
6
|
+
type="checkbox"
|
|
7
|
+
role="switch"
|
|
8
|
+
:checked="modelValue"
|
|
9
|
+
@change="$emit('update:modelValue', $event.target.checked)"
|
|
10
|
+
/>
|
|
11
|
+
</span>
|
|
12
|
+
{{ translations.show_deleted }}
|
|
13
|
+
</label>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup>
|
|
17
|
+
defineProps({
|
|
18
|
+
modelValue: { type: Boolean, default: false },
|
|
19
|
+
translations: {
|
|
20
|
+
type: Object,
|
|
21
|
+
default: () => ({
|
|
22
|
+
show_deleted: 'Show deleted',
|
|
23
|
+
}),
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
defineEmits(['update:modelValue']);
|
|
28
|
+
</script>
|
|
@@ -20,10 +20,12 @@ export function useBaseListing(props) {
|
|
|
20
20
|
},
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
const
|
|
23
|
+
const defaultOrderBy = {
|
|
24
24
|
column: 'id',
|
|
25
25
|
direction: 'asc',
|
|
26
|
-
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const orderBy = reactive({ ...defaultOrderBy });
|
|
27
29
|
|
|
28
30
|
const filters = reactive({});
|
|
29
31
|
const search = ref('');
|
|
@@ -32,15 +34,18 @@ export function useBaseListing(props) {
|
|
|
32
34
|
const bulkItems = reactive({});
|
|
33
35
|
const bulkCheckingAllLoader = ref(false);
|
|
34
36
|
|
|
37
|
+
const defaultConfirmTranslations = {
|
|
38
|
+
confirm_title: 'Warning!',
|
|
39
|
+
confirm_text: 'Are you sure?',
|
|
40
|
+
confirm_btn: 'Yes, confirm',
|
|
41
|
+
cancel_btn: 'Cancel',
|
|
42
|
+
};
|
|
43
|
+
|
|
35
44
|
const confirmModal = reactive({
|
|
36
45
|
show: false,
|
|
37
46
|
onConfirm: null,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
confirm_text: 'Are you sure?',
|
|
41
|
-
confirm_btn: 'Yes, confirm',
|
|
42
|
-
cancel_btn: 'Cancel',
|
|
43
|
-
},
|
|
47
|
+
variant: 'danger',
|
|
48
|
+
translations: { ...defaultConfirmTranslations },
|
|
44
49
|
});
|
|
45
50
|
|
|
46
51
|
// Update "now" every second
|
|
@@ -57,6 +62,19 @@ export function useBaseListing(props) {
|
|
|
57
62
|
return Object.values(bulkItems).filter((item) => item === true).length;
|
|
58
63
|
});
|
|
59
64
|
|
|
65
|
+
const showDeleted = computed({
|
|
66
|
+
get() {
|
|
67
|
+
return Boolean(filters.trashed);
|
|
68
|
+
},
|
|
69
|
+
set(value) {
|
|
70
|
+
if (!value && orderBy.column === 'deleted_at') {
|
|
71
|
+
Object.assign(orderBy, defaultOrderBy);
|
|
72
|
+
}
|
|
73
|
+
onBulkItemsClickedAllUncheck();
|
|
74
|
+
filter('trashed', value ? 1 : '');
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
60
78
|
const isClickedAll = computed({
|
|
61
79
|
get() {
|
|
62
80
|
return (
|
|
@@ -152,13 +170,14 @@ export function useBaseListing(props) {
|
|
|
152
170
|
}
|
|
153
171
|
}
|
|
154
172
|
|
|
155
|
-
function
|
|
156
|
-
const
|
|
173
|
+
function bulkAction(url, trans = {}, variant = 'danger') {
|
|
174
|
+
const selectedItems = Object.keys(bulkItems).filter((key) => bulkItems[key]);
|
|
157
175
|
|
|
176
|
+
confirmModal.variant = variant;
|
|
158
177
|
confirmModal.translations = {
|
|
159
|
-
...
|
|
178
|
+
...defaultConfirmTranslations,
|
|
160
179
|
...trans,
|
|
161
|
-
confirm_text: (trans.confirm_text ||
|
|
180
|
+
confirm_text: (trans.confirm_text || defaultConfirmTranslations.confirm_text).replace(
|
|
162
181
|
':number',
|
|
163
182
|
clickedBulkItemsCount.value,
|
|
164
183
|
),
|
|
@@ -167,7 +186,7 @@ export function useBaseListing(props) {
|
|
|
167
186
|
confirmModal.show = false;
|
|
168
187
|
axios
|
|
169
188
|
.post(url, {
|
|
170
|
-
ids:
|
|
189
|
+
ids: selectedItems,
|
|
171
190
|
})
|
|
172
191
|
.then((response) => {
|
|
173
192
|
Object.keys(bulkItems).forEach((key) => delete bulkItems[key]);
|
|
@@ -181,6 +200,34 @@ export function useBaseListing(props) {
|
|
|
181
200
|
confirmModal.show = true;
|
|
182
201
|
}
|
|
183
202
|
|
|
203
|
+
function bulkDelete(url, trans = {}) {
|
|
204
|
+
bulkAction(url, {
|
|
205
|
+
...trans,
|
|
206
|
+
confirm_text: trans.confirm_text || 'Do you really want to delete :number selected items?',
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function bulkForceDelete(url, trans = {}) {
|
|
211
|
+
bulkAction(url, {
|
|
212
|
+
confirm_btn: 'Delete permanently',
|
|
213
|
+
...trans,
|
|
214
|
+
confirm_text: trans.confirm_text || 'Delete :number selected items permanently? This cannot be undone.',
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function bulkRestore(url, trans = {}) {
|
|
219
|
+
bulkAction(
|
|
220
|
+
url,
|
|
221
|
+
{
|
|
222
|
+
confirm_title: 'Restore',
|
|
223
|
+
confirm_btn: 'Restore',
|
|
224
|
+
...trans,
|
|
225
|
+
confirm_text: trans.confirm_text || 'Do you really want to restore :number selected items?',
|
|
226
|
+
},
|
|
227
|
+
'success',
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
184
231
|
function loadData(resetCurrentPage) {
|
|
185
232
|
const options = {
|
|
186
233
|
params: {
|
|
@@ -292,13 +339,17 @@ export function useBaseListing(props) {
|
|
|
292
339
|
// Computed
|
|
293
340
|
isClickedAll,
|
|
294
341
|
clickedBulkItemsCount,
|
|
342
|
+
showDeleted,
|
|
295
343
|
|
|
296
344
|
// Methods
|
|
297
345
|
onBulkItemClicked,
|
|
298
346
|
onBulkItemsClickedAll,
|
|
299
347
|
onBulkItemsClickedAllWithPagination,
|
|
300
348
|
onBulkItemsClickedAllUncheck,
|
|
349
|
+
bulkAction,
|
|
301
350
|
bulkDelete,
|
|
351
|
+
bulkForceDelete,
|
|
352
|
+
bulkRestore,
|
|
302
353
|
loadData,
|
|
303
354
|
filter,
|
|
304
355
|
resolveUrl,
|
package/src/index.js
CHANGED
|
@@ -25,6 +25,9 @@ export { default as ListingHeader } from './components/listing/ListingHeader.vue
|
|
|
25
25
|
export { default as ShowButton } from './components/listing/ShowButton.vue';
|
|
26
26
|
export { default as EditButton } from './components/listing/EditButton.vue';
|
|
27
27
|
export { default as DeleteButton } from './components/listing/DeleteButton.vue';
|
|
28
|
+
export { default as ForceDeleteButton } from './components/listing/ForceDeleteButton.vue';
|
|
29
|
+
export { default as RestoreButton } from './components/listing/RestoreButton.vue';
|
|
30
|
+
export { default as ShowDeleted } from './components/listing/ShowDeleted.vue';
|
|
28
31
|
export { default as BackButton } from './components/BackButton.vue';
|
|
29
32
|
export { default as ConfirmModal } from './components/ConfirmModal.vue';
|
|
30
33
|
export { default as ToggleSwitch } from './components/listing/ToggleSwitch.vue';
|