@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dejwcake/craftable",
3
- "version": "2.3.0",
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
- <button class="btn btn-sm btn-danger px-4" @click="onBulkDelete()">
17
- {{ translations.delete_btn }}
18
- </button>
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, required: true },
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 orderBy = reactive({
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
- translations: {
39
- confirm_title: 'Warning!',
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 bulkDelete(url, trans = {}) {
156
- const itemsToDelete = Object.keys(bulkItems).filter((key) => bulkItems[key]);
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
- ...confirmModal.translations,
178
+ ...defaultConfirmTranslations,
160
179
  ...trans,
161
- confirm_text: (trans.confirm_text || 'Do you really want to delete :number selected items?').replace(
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: itemsToDelete,
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';