@dejwcake/craftable 2.3.0 → 2.4.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.4.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>
@@ -32,15 +32,18 @@ export function useBaseListing(props) {
32
32
  const bulkItems = reactive({});
33
33
  const bulkCheckingAllLoader = ref(false);
34
34
 
35
+ const defaultConfirmTranslations = {
36
+ confirm_title: 'Warning!',
37
+ confirm_text: 'Are you sure?',
38
+ confirm_btn: 'Yes, confirm',
39
+ cancel_btn: 'Cancel',
40
+ };
41
+
35
42
  const confirmModal = reactive({
36
43
  show: false,
37
44
  onConfirm: null,
38
- translations: {
39
- confirm_title: 'Warning!',
40
- confirm_text: 'Are you sure?',
41
- confirm_btn: 'Yes, confirm',
42
- cancel_btn: 'Cancel',
43
- },
45
+ variant: 'danger',
46
+ translations: { ...defaultConfirmTranslations },
44
47
  });
45
48
 
46
49
  // Update "now" every second
@@ -152,13 +155,14 @@ export function useBaseListing(props) {
152
155
  }
153
156
  }
154
157
 
155
- function bulkDelete(url, trans = {}) {
156
- const itemsToDelete = Object.keys(bulkItems).filter((key) => bulkItems[key]);
158
+ function bulkAction(url, trans = {}, variant = 'danger') {
159
+ const selectedItems = Object.keys(bulkItems).filter((key) => bulkItems[key]);
157
160
 
161
+ confirmModal.variant = variant;
158
162
  confirmModal.translations = {
159
- ...confirmModal.translations,
163
+ ...defaultConfirmTranslations,
160
164
  ...trans,
161
- confirm_text: (trans.confirm_text || 'Do you really want to delete :number selected items?').replace(
165
+ confirm_text: (trans.confirm_text || defaultConfirmTranslations.confirm_text).replace(
162
166
  ':number',
163
167
  clickedBulkItemsCount.value,
164
168
  ),
@@ -167,7 +171,7 @@ export function useBaseListing(props) {
167
171
  confirmModal.show = false;
168
172
  axios
169
173
  .post(url, {
170
- ids: itemsToDelete,
174
+ ids: selectedItems,
171
175
  })
172
176
  .then((response) => {
173
177
  Object.keys(bulkItems).forEach((key) => delete bulkItems[key]);
@@ -181,6 +185,34 @@ export function useBaseListing(props) {
181
185
  confirmModal.show = true;
182
186
  }
183
187
 
188
+ function bulkDelete(url, trans = {}) {
189
+ bulkAction(url, {
190
+ ...trans,
191
+ confirm_text: trans.confirm_text || 'Do you really want to delete :number selected items?',
192
+ });
193
+ }
194
+
195
+ function bulkForceDelete(url, trans = {}) {
196
+ bulkAction(url, {
197
+ confirm_btn: 'Delete permanently',
198
+ ...trans,
199
+ confirm_text: trans.confirm_text || 'Delete :number selected items permanently? This cannot be undone.',
200
+ });
201
+ }
202
+
203
+ function bulkRestore(url, trans = {}) {
204
+ bulkAction(
205
+ url,
206
+ {
207
+ confirm_title: 'Restore',
208
+ confirm_btn: 'Restore',
209
+ ...trans,
210
+ confirm_text: trans.confirm_text || 'Do you really want to restore :number selected items?',
211
+ },
212
+ 'success',
213
+ );
214
+ }
215
+
184
216
  function loadData(resetCurrentPage) {
185
217
  const options = {
186
218
  params: {
@@ -298,7 +330,10 @@ export function useBaseListing(props) {
298
330
  onBulkItemsClickedAll,
299
331
  onBulkItemsClickedAllWithPagination,
300
332
  onBulkItemsClickedAllUncheck,
333
+ bulkAction,
301
334
  bulkDelete,
335
+ bulkForceDelete,
336
+ bulkRestore,
302
337
  loadData,
303
338
  filter,
304
339
  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';