lkt-table 1.4.3 → 2.0.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.
@@ -1,11 +1,7 @@
1
- import { ModalConfig } from "lkt-vue-kernel";
1
+ import { ButtonConfig } from "lkt-vue-kernel";
2
2
  type __VLS_Props = {
3
+ config?: ButtonConfig;
3
4
  disabled?: boolean;
4
- text?: string;
5
- icon?: string;
6
- to?: string;
7
- modal?: string;
8
- modalData?: Partial<ModalConfig>;
9
5
  };
10
6
  declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
7
  click: (...args: any[]) => void;
@@ -14,10 +10,7 @@ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {
14
10
  onClick?: ((...args: any[]) => any) | undefined;
15
11
  onAppend?: ((...args: any[]) => any) | undefined;
16
12
  }>, {
17
- to: string;
18
13
  disabled: boolean;
19
- modal: string;
20
- text: string;
21
- icon: string;
14
+ config: ButtonConfig;
22
15
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
23
16
  export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lkt-table",
3
- "version": "1.4.3",
3
+ "version": "2.0.0",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "lkt",
@@ -1,30 +1,23 @@
1
1
  <script setup lang="ts">
2
2
  import {computed} from "vue";
3
3
  import {Settings} from "../settings/Settings";
4
- import {LktObject, ModalConfig} from "lkt-vue-kernel";
4
+ import {ButtonConfig, LktObject} from "lkt-vue-kernel";
5
5
 
6
6
  const emit = defineEmits(['click', 'append']);
7
7
 
8
8
  const props = withDefaults(defineProps<{
9
+ config?: ButtonConfig
9
10
  disabled?: boolean
10
- text?: string
11
- icon?: string
12
- to?: string
13
- modal?: string
14
- modalData?: Partial<ModalConfig>
15
11
  }>(), {
12
+ config: undefined,
16
13
  disabled: false,
17
- text: '',
18
- icon: '',
19
- to: '',
20
- modal: '',
21
14
  });
22
15
 
23
16
  const hasCreateButtonSlot = computed(() => Settings.createButtonSlot !== ''),
24
17
  createButtonSlot = computed(() => Settings.createButtonSlot);
25
18
 
26
19
  const calculatedModalData = {
27
- ...props.modalData,
20
+ ...props.config?.modalData,
28
21
  beforeClose: (data: LktObject) => {
29
22
  // Checks lkt-item-crud as modal flow
30
23
  if ('itemCreated' in data && data.itemCreated === true) {
@@ -33,8 +26,13 @@ const calculatedModalData = {
33
26
  }
34
27
  };
35
28
 
29
+ const calculatedConfig = {
30
+ ...props.config,
31
+ }
32
+ calculatedConfig.modalData = calculatedModalData;
33
+
36
34
  const onClick = () => {
37
- if (!props.modal) {
35
+ if (!props.config?.modal) {
38
36
  emit('click');
39
37
  return;
40
38
  }
@@ -43,13 +41,8 @@ const onClick = () => {
43
41
 
44
42
  <template>
45
43
  <lkt-button
46
- palette="table-create"
44
+ v-bind="calculatedConfig"
47
45
  :disabled="disabled"
48
- :icon="hasCreateButtonSlot ? '' : icon"
49
- :text="hasCreateButtonSlot ? '' : text"
50
- :modal="modal"
51
- :modal-data="calculatedModalData"
52
- :on-click-to="to"
53
46
  @click="onClick">
54
47
  <template v-if="hasCreateButtonSlot">
55
48
  <component
@@ -3,7 +3,9 @@ import {defaultTableSorter, getColumnByKey, getDefaultSortColumn} from "../funct
3
3
  import LktTableRow from "../components/LktTableRow.vue";
4
4
  import {computed, nextTick, onMounted, ref, useSlots, watch} from "vue";
5
5
  import {
6
+ ButtonType,
6
7
  Column,
8
+ extractI18nValue,
7
9
  getDefaultValues,
8
10
  LktObject,
9
11
  SortDirection,
@@ -19,7 +21,6 @@ import {HTTPResponse} from "lkt-http-client";
19
21
  import CreateButton from "../components/CreateButton.vue";
20
22
  import Sortable from 'sortablejs';
21
23
  import TableHeader from "../components/TableHeader.vue";
22
- import {__} from "lkt-i18n";
23
24
  import {time} from "lkt-date-tools";
24
25
  import {Settings} from "../settings/Settings";
25
26
 
@@ -47,23 +48,23 @@ const hiddenColumnsStack: LktObject = {};
47
48
 
48
49
  const Sorter = ref(typeof props.sorter === 'function' ? props.sorter : defaultTableSorter),
49
50
  SortBy = ref(getDefaultSortColumn(props.columns)),
50
- SortingDirection = ref(<SortDirection>SortDirection.Asc),
51
+ SortingDirection = ref(SortDirection.Asc),
51
52
  Items = ref(props.modelValue),
52
53
  Hidden = ref(hiddenColumnsStack),
53
- tableBody = ref(<HTMLElement|null>null),
54
+ tableBody = ref(<HTMLElement | null>null),
54
55
  Columns = ref(props.columns);
55
56
 
56
- const Page = ref(props.page),
57
+ const Page = ref(props.paginator?.modelValue),
57
58
  isLoading = ref(props.loading),
58
59
  firstLoadReady = ref(false),
59
60
  permissions = ref(props.perms),
60
- paginator = ref(null),
61
+ paginatorRef = ref(null),
61
62
  element = ref(null),
62
63
  sortableObject = ref({}),
63
- dataState = ref(new DataState({items: Items.value}, props.dataStateConfig)),
64
+ dataState = ref(<DataState>new DataState({items: Items.value}, props.dataStateConfig)),
64
65
  editModeEnabled = ref(props.editMode),
65
66
  updateTimeStamp = ref(0),
66
- sortableContainer = ref(<HTMLElement|null>null)
67
+ sortableContainer = ref(<HTMLElement | null>null)
67
68
  ;
68
69
 
69
70
  const dataStateChanged = ref(false);
@@ -83,13 +84,14 @@ const onPerms = (r: string[]) => {
83
84
  dataState.value.store({items: Items.value}).turnStoredIntoOriginal();
84
85
  dataStateChanged.value = false;
85
86
  nextTick(() => {
87
+ saveIsDisabled.value; // Force calc call
86
88
  emit('read-response', r);
87
89
  })
88
90
  },
89
91
  onLoading = () => nextTick(() => isLoading.value = true),
90
92
  doRefresh = () => {
91
93
  //@ts-ignore
92
- paginator.value.doRefresh();
94
+ paginatorRef.value.doRefresh();
93
95
  };
94
96
 
95
97
 
@@ -142,7 +144,7 @@ const emptyColumns = computed(() => {
142
144
  showSaveButton = computed(() => {
143
145
  if (props.hiddenSave) return false;
144
146
  if (isLoading.value) return false;
145
- if (!props.saveResource) return false;
147
+ if (!(props.saveButton?.resource || props.saveButton.type)) return false;
146
148
  if (editModeEnabled.value && dataStateChanged.value) return true;
147
149
 
148
150
  return editModeEnabled.value;
@@ -152,10 +154,14 @@ const emptyColumns = computed(() => {
152
154
  if (props.switchEditionEnabled) return true;
153
155
  return showSaveButton.value || (editModeEnabled.value && hasCreatePerm.value);
154
156
  }),
155
- ableToSave = computed(() => {
156
- if (props.saveDisabled) return false;
157
- if (typeof props.saveValidator === 'function' && !props.saveValidator(Items.value)) return false;
158
- return dataStateChanged.value;
157
+ saveIsDisabled = computed(() => {
158
+ updateTimeStamp.value;
159
+ if (typeof props.saveButton?.disabled === 'function') return props.saveButton.disabled({
160
+ value: Items.value,
161
+ dataState: <DataState>dataState.value,
162
+ });
163
+ if (typeof props.saveButton?.disabled === 'boolean') return props.saveButton.disabled;
164
+ return !dataStateChanged.value;
159
165
  }),
160
166
  amountOfItems = computed(() => {
161
167
  return Items.value.length;
@@ -163,7 +169,7 @@ const emptyColumns = computed(() => {
163
169
  computedSaveResourceData = computed(() => {
164
170
  return {
165
171
  items: Items.value,
166
- ...props.saveResourceData
172
+ ...props.saveButton?.resourceData
167
173
  }
168
174
  }),
169
175
  computedTitleTag = computed(() => {
@@ -175,22 +181,13 @@ const emptyColumns = computed(() => {
175
181
  return props.wrapContentTag;
176
182
  }),
177
183
  computedTitle = computed(() => {
178
- if (props.title.startsWith('__:')) {
179
- return __(props.title.substring(3));
180
- }
181
- return props.title;
182
- }),
183
- computedSaveText = computed(() => {
184
- if (props.saveText.startsWith('__:')) {
185
- return __(props.saveText.substring(3));
186
- }
187
- return props.saveText;
184
+ return extractI18nValue(props.title);
188
185
  }),
189
186
  computedEditModeText = computed(() => {
190
- if (props.editModeText.startsWith('__:')) {
191
- return __(props.editModeText.substring(3));
192
- }
193
- return props.editModeText;
187
+ return extractI18nValue(props.editModeText);
188
+ }),
189
+ computedDragModeEnabled = computed(() => {
190
+ return props.drag?.enabled;
194
191
  }),
195
192
  hasCreatePerm = computed(() => permissions.value.includes(TablePermission.Create)),
196
193
  hasReadPerm = computed(() => permissions.value.includes('read')),
@@ -248,13 +245,12 @@ const getItemByEvent = (e: any) => {
248
245
  },
249
246
  validDragChecker = (evt: any) => {
250
247
  let targetIndex = parseInt(evt?.originalEvent?.toElement?.closest('tr')?.dataset?.i);
251
- if (typeof props.disabledDrag === 'function' && props.disabledDrag(Items.value[targetIndex])) return false;
252
- if (typeof props.disabledDrag === 'boolean' && props.disabledDrag) return false;
253
- if (typeof props.checkValidDrag === 'function') return props.checkValidDrag(evt);
248
+ if (typeof props.drag?.isValid === 'function' && !props.drag?.isValid(Items.value[targetIndex])) return false;
249
+ if (typeof props.drag?.isValid === 'boolean' && !props.drag?.isValid) return false;
254
250
  return true;
255
251
  },
256
252
  isDraggable = (element: any) => {
257
- if (typeof props.draggableChecker === 'function') return props.draggableChecker(element);
253
+ if (typeof props.drag?.isDraggable === 'function') return props.drag?.isDraggable(element);
258
254
  return true;
259
255
  },
260
256
  onClickAddItem = () => {
@@ -280,15 +276,21 @@ const getItemByEvent = (e: any) => {
280
276
  onAppend = (data: LktObject) => {
281
277
  Items.value.push(data);
282
278
  },
283
- onButtonLoading = () => {
284
- isLoading.value = true;
285
- },
286
- onButtonLoaded = () => {
287
- isLoading.value = false;
288
- },
279
+ onButtonLoading = () => isLoading.value = true,
280
+ onButtonLoaded = () => isLoading.value = false,
289
281
  onSave = ($event: PointerEvent, r: HTTPResponse) => {
282
+ if (props.saveButton?.type) {
283
+ if ([
284
+ ButtonType.Split,
285
+ ButtonType.SplitEver,
286
+ ButtonType.SplitLazy,
287
+ ].includes(props.saveButton?.type)) {
288
+ return;
289
+ }
290
+ }
291
+
290
292
  emit('before-save');
291
- if (props.saveResource) {
293
+ if (props.saveButton?.resource) {
292
294
  isLoading.value = false;
293
295
  if (!r.success) {
294
296
  emit('error', r.httpStatus);
@@ -394,14 +396,14 @@ onMounted(() => {
394
396
  }
395
397
  dataState.value.store({items: Items.value}).turnStoredIntoOriginal();
396
398
  dataStateChanged.value = false;
397
- if (props.sortable) {
399
+ if (props.drag?.enabled) {
398
400
  nextTick(() => {
399
401
  initSortable();
400
402
  })
401
403
  }
402
404
  })
403
405
 
404
- watch(() => props.sortable, (v) => {
406
+ watch(() => props.drag?.enabled, (v) => {
405
407
  if (v) {
406
408
  initSortable();
407
409
  } else {
@@ -426,6 +428,12 @@ defineExpose({
426
428
  getRowByIndex,
427
429
  doRefresh,
428
430
  getHtml: () => element.value,
431
+ turnStoredIntoOriginal: () => {
432
+ dataState.value.turnStoredIntoOriginal();
433
+ nextTick(()=> {
434
+ updateTimeStamp.value = time();
435
+ })
436
+ },
429
437
  });
430
438
 
431
439
  const hasEmptySlot = computed(() => {
@@ -461,14 +469,9 @@ const hasEmptySlot = computed(() => {
461
469
  class="lkt-table--save-button"
462
470
  ref="saveButton"
463
471
  v-show="showSaveButton"
464
- :icon="Settings.defaultSaveIcon"
465
- :disabled="!ableToSave"
466
- :confirm-modal="saveConfirm"
467
- :confirm-data="confirmData"
468
- :resource="saveResource"
469
- :resource-data="computedSaveResourceData"
470
- :split="splitSave"
471
- :tooltip-engine="saveTooltipEngine"
472
+ v-bind="saveButton"
473
+ :disabled="saveIsDisabled"
474
+ :modal-data="computedSaveResourceData"
472
475
  v-on:loading="onButtonLoading"
473
476
  v-on:loaded="onButtonLoaded"
474
477
  v-on:click="onSave">
@@ -476,8 +479,7 @@ const hasEmptySlot = computed(() => {
476
479
  name="button-save"
477
480
  :items="Items"
478
481
  :edit-mode="editMode"
479
- :can-update="!saveDisabled"></slot>
480
- <span v-else>{{ computedSaveText }}</span>
482
+ :can-update="!saveIsDisabled"/>
481
483
 
482
484
  <template v-slot:split="{doClose, doRootClick}">
483
485
  <slot name="button-save-split"
@@ -485,18 +487,14 @@ const hasEmptySlot = computed(() => {
485
487
  :do-root-click="doRootClick"
486
488
  :data-state="dataState"
487
489
  :on-button-loading="onButtonLoading"
488
- :on-button-loaded="onButtonLoaded" />
490
+ :on-button-loaded="onButtonLoaded"/>
489
491
  </template>
490
492
  </lkt-button>
491
493
 
492
494
  <create-button
493
495
  v-if="computedDisplayCreateButton && Items.length >= requiredItemsForTopCreate"
496
+ :config="createButton"
494
497
  :disabled="!createEnabled || createDisabled"
495
- :text="createText"
496
- :icon="createIcon"
497
- :to="createRoute"
498
- :modal="modal"
499
- :modal-data="modalData"
500
498
  @click="onClickAddItem"
501
499
  @append="onAppend"
502
500
  />
@@ -522,11 +520,11 @@ const hasEmptySlot = computed(() => {
522
520
 
523
521
  <lkt-loader v-if="isLoading"/>
524
522
 
525
- <div v-show="!isLoading && Items.length > 0" class="lkt-table" :data-sortable="sortable">
523
+ <div v-show="!isLoading && Items.length > 0" class="lkt-table">
526
524
  <table v-if="type === TableType.Table">
527
525
  <thead>
528
526
  <tr>
529
- <th v-if="sortable && editModeEnabled" data-role="drag-indicator"/>
527
+ <th v-if="computedDragModeEnabled && editModeEnabled" data-role="drag-indicator"/>
530
528
  <th v-if="addNavigation && editModeEnabled"/>
531
529
  <th v-if="displayHiddenColumnsIndicator"/>
532
530
  <template v-for="column in visibleColumns">
@@ -554,94 +552,94 @@ const hasEmptySlot = computed(() => {
554
552
  ref="tableBody"
555
553
  :id="'lkt-table-body-' + uniqueId"
556
554
  >
557
- <lkt-table-row
558
- v-for="(item, i) in Items"
559
- v-model="Items[i]"
560
- v-show="canDisplayItem(Items[i], i)"
561
- :key="getRowKey(item, i)"
562
- :i="i"
563
- :display-hidden-columns-indicator="displayHiddenColumnsIndicator"
564
- :is-draggable="isDraggable(item)"
565
- :sortable="sortable"
566
- :visible-columns="visibleColumns"
567
- :empty-columns="emptyColumns"
568
- :add-navigation="addNavigation"
569
- :hidden-is-visible="isVisible(i)"
570
- :latest-row="i+1 === amountOfItems"
571
- :can-drop="hasDropPerm && editModeEnabled"
572
- :drop-confirm="dropConfirm"
573
- :drop-resource="dropResource"
574
- :drop-text="dropText"
575
- :drop-icon="dropIcon"
576
- :can-edit="hasEditPerm && hasUpdatePerm && editModeEnabled"
577
- :edit-text="editText"
578
- :edit-icon="editIcon"
579
- :edit-link="editLink"
580
- :edit-mode-enabled="editModeEnabled"
581
- :has-inline-edit-perm="hasInlineEditPerm"
582
- :row-display-type="rowDisplayType"
583
- :render-drag="renderDrag"
584
- :disabled-drag="disabledDrag"
585
- v-on:click="onClick"
586
- v-on:show="show"
587
- v-on:item-up="onItemUp"
588
- v-on:item-down="onItemDown"
589
- v-on:item-drop="onItemDrop"
590
- >
591
- <template v-if="slots[`item-${i}`]" v-slot:[`item-${i}`]="row">
592
- <slot
593
- :name="`item-${i}`"
594
- :[slotItemVar]="row.item"
595
- v-bind:index="i"
596
- />
597
- </template>
598
- <template v-else-if="slots.item" #item="row">
599
- <slot
600
- name="item"
601
- :[slotItemVar]="row.item"
602
- v-bind:index="i"
603
- />
604
- </template>
605
- <template
606
- v-for="column in colSlots"
607
- v-slot:[column]="row">
608
- <slot
609
- :name="column"
610
- :[slotItemVar]="row.item"
611
- :value="row.value"
612
- :column="row.column"
613
- />
614
- </template>
615
- </lkt-table-row>
616
- <lkt-hidden-row
617
- v-if="hiddenColumns.length > 0"
618
- v-model="Items[i]"
619
- v-for="(item, i) in Items"
620
- :key="getRowKey(item, i, true)"
621
- :i="i"
622
- :hidden-columns="hiddenColumns"
623
- :hidden-columns-col-span="hiddenColumnsColSpan"
624
- :is-draggable="isDraggable(item)"
625
- :sortable="sortable"
626
- :visible-columns="visibleColumns"
627
- :empty-columns="emptyColumns"
628
- :hidden-is-visible="isVisible(i)"
629
- :edit-mode-enabled="editModeEnabled"
630
- :has-inline-edit-perm="hasInlineEditPerm"
631
- v-on:click="onClick"
632
- v-on:show="show"
633
- >
634
- <template
635
- v-for="column in colSlots"
636
- v-slot:[column]="row">
637
- <slot
638
- :name="column"
639
- :[slotItemVar]="row.item"
640
- :value="row.value"
641
- :column="row.column"
642
- />
643
- </template>
644
- </lkt-hidden-row>
555
+ <lkt-table-row
556
+ v-for="(item, i) in Items"
557
+ v-model="Items[i]"
558
+ v-show="canDisplayItem(Items[i], i)"
559
+ :key="getRowKey(item, i)"
560
+ :i="i"
561
+ :display-hidden-columns-indicator="displayHiddenColumnsIndicator"
562
+ :is-draggable="isDraggable(item)"
563
+ :sortable="computedDragModeEnabled"
564
+ :visible-columns="visibleColumns"
565
+ :empty-columns="emptyColumns"
566
+ :add-navigation="addNavigation"
567
+ :hidden-is-visible="isVisible(i)"
568
+ :latest-row="i+1 === amountOfItems"
569
+ :can-drop="hasDropPerm && editModeEnabled"
570
+ :drop-confirm="dropConfirm"
571
+ :drop-resource="dropResource"
572
+ :drop-text="dropText"
573
+ :drop-icon="dropIcon"
574
+ :can-edit="hasEditPerm && hasUpdatePerm && editModeEnabled"
575
+ :edit-text="editText"
576
+ :edit-icon="editIcon"
577
+ :edit-link="editLink"
578
+ :edit-mode-enabled="editModeEnabled"
579
+ :has-inline-edit-perm="hasInlineEditPerm"
580
+ :row-display-type="rowDisplayType"
581
+ :render-drag="drag?.canRender"
582
+ :disabled-drag="drag?.isDisabled"
583
+ v-on:click="onClick"
584
+ v-on:show="show"
585
+ v-on:item-up="onItemUp"
586
+ v-on:item-down="onItemDown"
587
+ v-on:item-drop="onItemDrop"
588
+ >
589
+ <template v-if="slots[`item-${i}`]" v-slot:[`item-${i}`]="row">
590
+ <slot
591
+ :name="`item-${i}`"
592
+ :[slotItemVar]="row.item"
593
+ v-bind:index="i"
594
+ />
595
+ </template>
596
+ <template v-else-if="slots.item" #item="row">
597
+ <slot
598
+ name="item"
599
+ :[slotItemVar]="row.item"
600
+ v-bind:index="i"
601
+ />
602
+ </template>
603
+ <template
604
+ v-for="column in colSlots"
605
+ v-slot:[column]="row">
606
+ <slot
607
+ :name="column"
608
+ :[slotItemVar]="row.item"
609
+ :value="row.value"
610
+ :column="row.column"
611
+ />
612
+ </template>
613
+ </lkt-table-row>
614
+ <lkt-hidden-row
615
+ v-if="hiddenColumns.length > 0"
616
+ v-model="Items[i]"
617
+ v-for="(item, i) in Items"
618
+ :key="getRowKey(item, i, true)"
619
+ :i="i"
620
+ :hidden-columns="hiddenColumns"
621
+ :hidden-columns-col-span="hiddenColumnsColSpan"
622
+ :is-draggable="isDraggable(item)"
623
+ :sortable="computedDragModeEnabled"
624
+ :visible-columns="visibleColumns"
625
+ :empty-columns="emptyColumns"
626
+ :hidden-is-visible="isVisible(i)"
627
+ :edit-mode-enabled="editModeEnabled"
628
+ :has-inline-edit-perm="hasInlineEditPerm"
629
+ v-on:click="onClick"
630
+ v-on:show="show"
631
+ >
632
+ <template
633
+ v-for="column in colSlots"
634
+ v-slot:[column]="row">
635
+ <slot
636
+ :name="column"
637
+ :[slotItemVar]="row.item"
638
+ :value="row.value"
639
+ :column="row.column"
640
+ />
641
+ </template>
642
+ </lkt-hidden-row>
645
643
  </tbody>
646
644
  </table>
647
645
 
@@ -672,7 +670,8 @@ const hasEmptySlot = computed(() => {
672
670
  </template>
673
671
  </div>
674
672
 
675
- <component :is="type" v-else-if="computedIsList" class="lkt-table-items-container" :class="itemsContainerClass">
673
+ <component :is="type" v-else-if="computedIsList" class="lkt-table-items-container"
674
+ :class="itemsContainerClass">
676
675
  <template
677
676
  v-for="(item, i) in Items">
678
677
  <li class="lkt-table-item" v-if="canDisplayItem(item, i)" :data-i="i">
@@ -708,12 +707,8 @@ const hasEmptySlot = computed(() => {
708
707
  class="lkt-table-page-buttons lkt-table-page-buttons-bottom">
709
708
  <create-button
710
709
  v-if="computedDisplayCreateButton && Items.length >= requiredItemsForBottomCreate"
710
+ :config="createButton"
711
711
  :disabled="!createEnabled || createDisabled"
712
- :text="createText"
713
- :icon="createIcon"
714
- :to="createRoute"
715
- :modal="modal"
716
- :modal-data="modalData"
717
712
  @click="onClickAddItem"
718
713
  @append="onAppend"
719
714
  />
@@ -721,7 +716,7 @@ const hasEmptySlot = computed(() => {
721
716
  </div>
722
717
 
723
718
  <lkt-paginator
724
- ref="paginator"
719
+ ref="paginatorRef"
725
720
  v-if="resource.length > 0"
726
721
  v-model="Page"
727
722
  :resource="resource"