primevue 4.3.9 → 4.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.
Files changed (88) hide show
  1. package/autocomplete/AutoComplete.vue +18 -3
  2. package/autocomplete/BaseAutoComplete.vue +4 -0
  3. package/autocomplete/index.d.ts +20 -0
  4. package/autocomplete/index.mjs +34 -7
  5. package/autocomplete/index.mjs.map +1 -1
  6. package/autocomplete/style/index.d.ts +4 -0
  7. package/autocomplete/style/index.mjs +3 -1
  8. package/autocomplete/style/index.mjs.map +1 -1
  9. package/checkbox/Checkbox.vue +15 -2
  10. package/checkbox/index.mjs +14 -2
  11. package/checkbox/index.mjs.map +1 -1
  12. package/colorpicker/ColorPicker.vue +3 -4
  13. package/colorpicker/index.mjs +3 -3
  14. package/colorpicker/index.mjs.map +1 -1
  15. package/confirmdialog/ConfirmDialog.vue +1 -1
  16. package/confirmdialog/index.d.ts +5 -0
  17. package/confirmdialog/index.mjs +3 -2
  18. package/confirmdialog/index.mjs.map +1 -1
  19. package/datatable/DataTable.vue +2 -2
  20. package/datatable/HeaderCheckbox.vue +3 -3
  21. package/datatable/index.mjs +12 -3
  22. package/datatable/index.mjs.map +1 -1
  23. package/datepicker/BaseDatePicker.vue +8 -0
  24. package/datepicker/DatePicker.vue +199 -81
  25. package/datepicker/index.d.ts +25 -0
  26. package/datepicker/index.mjs +258 -150
  27. package/datepicker/index.mjs.map +1 -1
  28. package/datepicker/style/index.d.ts +4 -0
  29. package/datepicker/style/index.mjs +5 -2
  30. package/datepicker/style/index.mjs.map +1 -1
  31. package/drawer/BaseDrawer.vue +4 -0
  32. package/drawer/Drawer.vue +4 -4
  33. package/drawer/index.d.ts +5 -0
  34. package/drawer/index.mjs +8 -4
  35. package/drawer/index.mjs.map +1 -1
  36. package/editor/Editor.vue +5 -1
  37. package/editor/index.mjs +5 -0
  38. package/editor/index.mjs.map +1 -1
  39. package/image/index.d.ts +5 -11
  40. package/inputmask/InputMask.vue +1 -2
  41. package/inputmask/index.mjs +1 -1
  42. package/inputmask/index.mjs.map +1 -1
  43. package/inputnumber/BaseInputNumber.vue +4 -0
  44. package/inputnumber/InputNumber.vue +30 -5
  45. package/inputnumber/index.d.ts +20 -0
  46. package/inputnumber/index.mjs +45 -7
  47. package/inputnumber/index.mjs.map +1 -1
  48. package/inputnumber/style/index.mjs +1 -0
  49. package/inputnumber/style/index.mjs.map +1 -1
  50. package/inputotp/InputOtp.vue +7 -3
  51. package/inputotp/index.mjs +6 -3
  52. package/inputotp/index.mjs.map +1 -1
  53. package/listbox/Listbox.vue +1 -1
  54. package/listbox/index.mjs +1 -1
  55. package/listbox/index.mjs.map +1 -1
  56. package/multiselect/MultiSelect.vue +9 -5
  57. package/multiselect/index.mjs +14 -10
  58. package/multiselect/index.mjs.map +1 -1
  59. package/package.json +5 -5
  60. package/panel/Panel.vue +1 -1
  61. package/panel/index.d.ts +4 -0
  62. package/panel/index.mjs +2 -1
  63. package/panel/index.mjs.map +1 -1
  64. package/password/BasePassword.vue +4 -0
  65. package/password/Password.vue +12 -1
  66. package/password/index.d.ts +20 -0
  67. package/password/index.mjs +23 -1
  68. package/password/index.mjs.map +1 -1
  69. package/password/style/index.d.ts +4 -0
  70. package/password/style/index.mjs +1 -0
  71. package/password/style/index.mjs.map +1 -1
  72. package/select/Select.vue +1 -0
  73. package/select/index.mjs +1 -0
  74. package/select/index.mjs.map +1 -1
  75. package/tree/BaseTree.vue +16 -0
  76. package/tree/Tree.vue +179 -4
  77. package/tree/TreeNode.vue +225 -3
  78. package/tree/index.d.ts +65 -0
  79. package/tree/index.mjs +499 -22
  80. package/tree/index.mjs.map +1 -1
  81. package/tree/style/index.mjs +9 -4
  82. package/tree/style/index.mjs.map +1 -1
  83. package/treenode/index.d.ts +12 -0
  84. package/treeselect/TreeSelect.vue +1 -1
  85. package/treeselect/index.mjs +1 -1
  86. package/treeselect/index.mjs.map +1 -1
  87. package/umd/primevue.min.js +1 -1
  88. package/web-types.json +1 -1
package/tree/BaseTree.vue CHANGED
@@ -70,6 +70,22 @@ export default {
70
70
  type: Number,
71
71
  default: 0
72
72
  },
73
+ dragdrop: {
74
+ type: Boolean,
75
+ default: null
76
+ },
77
+ draggableScope: {
78
+ type: [String, Array],
79
+ default: null
80
+ },
81
+ droppableScope: {
82
+ type: [String, Array],
83
+ default: null
84
+ },
85
+ validateDrop: {
86
+ type: Boolean,
87
+ default: false
88
+ },
73
89
  ariaLabelledby: {
74
90
  type: String,
75
91
  default: null
package/tree/Tree.vue CHANGED
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :class="cx('root')" :data-p="containerDataP" v-bind="ptmi('root')">
2
+ <div :class="cx('root')" @dragover="onDragOver" @dragenter="onDragEnter" @dragleave="onDragLeave" @drop="onDrop" :data-p="containerDataP" v-bind="ptmi('root')">
3
3
  <template v-if="loading && loadingMode === 'mask'">
4
4
  <div :class="cx('mask')" v-bind="ptm('mask')">
5
5
  <slot name="loadingicon" :class="cx('loadingIcon')">
@@ -19,11 +19,12 @@
19
19
  </IconField>
20
20
  <div :class="cx('wrapper')" :style="{ maxHeight: scrollHeight }" :data-p="wrapperDataP" v-bind="ptm('wrapper')">
21
21
  <slot name="header" :value="value" :expandedKeys="expandedKeys" :selectionKeys="selectionKeys" />
22
- <ul :class="cx('rootChildren')" role="tree" :aria-labelledby="ariaLabelledby" :aria-label="ariaLabel" v-bind="ptm('rootChildren')">
22
+ <ul v-if="!empty" :class="cx('rootChildren')" role="tree" :aria-labelledby="ariaLabelledby" :aria-label="ariaLabel" v-bind="ptm('rootChildren')">
23
23
  <TreeNode
24
24
  v-for="(node, index) of valueToRender"
25
25
  :key="node.key"
26
26
  :node="node"
27
+ :rootNodes="valueToRender"
27
28
  :templates="$slots"
28
29
  :level="level + 1"
29
30
  :index="index"
@@ -34,10 +35,19 @@
34
35
  :selectionKeys="selectionKeys"
35
36
  @checkbox-change="onCheckboxChange"
36
37
  :loadingMode="loadingMode"
38
+ :draggableScope="draggableScope"
39
+ :dragdrop="dragdrop"
40
+ :validateDrop="validateDrop"
41
+ @node-drop="onNodeDrop"
37
42
  :unstyled="unstyled"
38
43
  :pt="pt"
39
44
  ></TreeNode>
40
45
  </ul>
46
+ <div v-else :class="cx('emptyMessage')" v-bind="ptm('emptyMessage')">
47
+ <slot name="empty">
48
+ {{ emptyMessageText }}
49
+ </slot>
50
+ </div>
41
51
  <slot name="footer" :value="value" :expandedKeys="expandedKeys" :selectionKeys="selectionKeys" />
42
52
  </div>
43
53
  </div>
@@ -52,24 +62,62 @@ import IconField from 'primevue/iconfield';
52
62
  import InputIcon from 'primevue/inputicon';
53
63
  import InputText from 'primevue/inputtext';
54
64
  import BaseTree from './BaseTree.vue';
65
+ import { useTreeDragDropService } from './TreeDragDropService';
55
66
  import TreeNode from './TreeNode.vue';
56
67
 
57
68
  export default {
58
69
  name: 'Tree',
59
70
  extends: BaseTree,
60
71
  inheritAttrs: false,
61
- emits: ['node-expand', 'node-collapse', 'update:expandedKeys', 'update:selectionKeys', 'node-select', 'node-unselect', 'filter'],
72
+ emits: ['node-expand', 'node-collapse', 'update:expandedKeys', 'update:selectionKeys', 'node-select', 'node-unselect', 'filter', 'node-drop', 'update:value'],
62
73
  data() {
63
74
  return {
64
75
  d_expandedKeys: this.expandedKeys || {},
65
- filterValue: null
76
+ filterValue: null,
77
+ dragNode: null,
78
+ dragNodeSubNodes: null,
79
+ dragNodeIndex: null,
80
+ dragNodeScope: null,
81
+ dragHover: null
66
82
  };
67
83
  },
84
+ dragDropService: null,
85
+ dragStartCleanup: null,
86
+ dragStopCleanup: null,
68
87
  watch: {
69
88
  expandedKeys(newValue) {
70
89
  this.d_expandedKeys = newValue;
71
90
  }
72
91
  },
92
+ mounted() {
93
+ if (this.dragdrop) {
94
+ this.dragDropService = useTreeDragDropService();
95
+
96
+ this.dragStartCleanup = this.dragDropService.onDragStart((event) => {
97
+ this.dragNode = event.node;
98
+ this.dragNodeSubNodes = event.subNodes;
99
+ this.dragNodeIndex = event.index;
100
+ this.dragNodeScope = event.scope;
101
+ });
102
+
103
+ this.dragStopCleanup = this.dragDropService.onDragStop(() => {
104
+ this.dragNode = null;
105
+ this.dragNodeSubNodes = null;
106
+ this.dragNodeIndex = null;
107
+ this.dragNodeScope = null;
108
+ this.dragHover = false;
109
+ });
110
+ }
111
+ },
112
+ beforeUnmount() {
113
+ if (this.dragStartCleanup) {
114
+ this.dragStartCleanup();
115
+ }
116
+
117
+ if (this.dragStopCleanup) {
118
+ this.dragStopCleanup();
119
+ }
120
+ },
73
121
  methods: {
74
122
  onNodeToggle(node) {
75
123
  const key = node.key;
@@ -220,6 +268,127 @@ export default {
220
268
  }
221
269
 
222
270
  return matched;
271
+ },
272
+ onNodeDrop(event) {
273
+ this.$emit('node-drop', event);
274
+ },
275
+ allowDrop(dragNode, dropNode, dragNodeScope) {
276
+ if (!dragNode) {
277
+ //prevent random html elements to be dragged
278
+ return false;
279
+ } else if (this.isValidDragScope(dragNodeScope)) {
280
+ let allow = true;
281
+
282
+ if (dropNode) {
283
+ if (dragNode === dropNode) {
284
+ allow = false;
285
+ } else {
286
+ let parent = dropNode.parent;
287
+
288
+ while (parent != null) {
289
+ if (parent === dragNode) {
290
+ allow = false;
291
+
292
+ break;
293
+ }
294
+
295
+ parent = parent.parent;
296
+ }
297
+ }
298
+ }
299
+
300
+ return allow;
301
+ } else {
302
+ return false;
303
+ }
304
+ },
305
+ allowNodeDrop(dropNode) {
306
+ return this.allowDrop(this.dragNode, dropNode, this.dragNodeScope);
307
+ },
308
+ isValidDragScope(dragScope) {
309
+ let dropScope = this.droppableScope;
310
+
311
+ if (dropScope !== null) {
312
+ if (typeof dropScope === 'string') {
313
+ if (typeof dragScope === 'string') return dropScope === dragScope;
314
+ else if (Array.isArray(dragScope)) return dragScope.indexOf(dropScope) != -1;
315
+ } else if (Array.isArray(dropScope)) {
316
+ if (typeof dragScope === 'string') {
317
+ return dropScope.indexOf(dragScope) != -1;
318
+ } else if (Array.isArray(dragScope)) {
319
+ for (let s of dropScope) {
320
+ for (let ds of dragScope) {
321
+ if (s === ds) {
322
+ return true;
323
+ }
324
+ }
325
+ }
326
+ }
327
+ }
328
+ return false;
329
+ } else {
330
+ return true;
331
+ }
332
+ },
333
+ onDragOver(event) {
334
+ if (this.dragdrop && (!this.value || this.value.length === 0)) {
335
+ event.dataTransfer.dropEffect = 'move';
336
+ event.preventDefault();
337
+ }
338
+ },
339
+ onDragEnter() {
340
+ if (this.dragdrop && this.allowDrop(this.dragNode, null, this.dragNodeScope)) {
341
+ this.dragHover = true;
342
+ }
343
+ },
344
+ onDragLeave(event) {
345
+ if (this.dragdrop) {
346
+ let rect = event.currentTarget.getBoundingClientRect();
347
+
348
+ if (event.x > rect.left + rect.width || event.x < rect.left || event.y > rect.top + rect.height || event.y < rect.top) {
349
+ this.dragHover = false;
350
+ }
351
+ }
352
+ },
353
+ processTreeDrop(dragNode, dragNodeIndex) {
354
+ this.dragNodeSubNodes.splice(dragNodeIndex, 1);
355
+ const newValue = [...(this.value || []), dragNode];
356
+ this.$emit('update:value', newValue);
357
+
358
+ this.dragDropService.stopDrag({
359
+ node: dragNode
360
+ });
361
+ },
362
+ onDrop(event) {
363
+ if (this.dragdrop && (!this.value || this.value.length === 0)) {
364
+ event.preventDefault();
365
+ let dragNode = this.dragNode;
366
+
367
+ if (this.allowDrop(dragNode, null, this.dragNodeScope)) {
368
+ let dragNodeIndex = this.dragNodeIndex;
369
+
370
+ if (this.validateDrop) {
371
+ this.$emit('node-drop', {
372
+ originalEvent: event,
373
+ dragNode: dragNode,
374
+ dropNode: null,
375
+ index: dragNodeIndex,
376
+ accept: () => {
377
+ this.processTreeDrop(dragNode, dragNodeIndex);
378
+ }
379
+ });
380
+ } else {
381
+ this.$emit('node-drop', {
382
+ originalEvent: event,
383
+ dragNode: dragNode,
384
+ dropNode: null,
385
+ index: dragNodeIndex
386
+ });
387
+
388
+ this.processTreeDrop(dragNode, dragNodeIndex);
389
+ }
390
+ }
391
+ }
223
392
  }
224
393
  },
225
394
  computed: {
@@ -247,6 +416,12 @@ export default {
247
416
  if (this.filterValue && this.filterValue.trim().length > 0) return this.filteredValue;
248
417
  else return this.value;
249
418
  },
419
+ empty() {
420
+ return !this.valueToRender || this.valueToRender.length === 0;
421
+ },
422
+ emptyMessageText() {
423
+ return this.$primevue.config?.locale?.emptyMessage || '';
424
+ },
250
425
  containerDataP() {
251
426
  return cn({
252
427
  loading: this.loading,
package/tree/TreeNode.vue CHANGED
@@ -14,7 +14,22 @@
14
14
  @keydown="onKeyDown"
15
15
  v-bind="getPTOptions('node')"
16
16
  >
17
- <div :class="cx('nodeContent')" @click="onClick" @touchend="onTouchEnd" :style="node.style" v-bind="getPTOptions('nodeContent')" :data-p-selected="checkboxMode ? checked : selected" :data-p-selectable="selectable">
17
+ <div v-if="isPrevDropPointActive" :class="cx('dropPoint')" aria-hidden="true" />
18
+ <div
19
+ :class="cx('nodeContent')"
20
+ :style="node.style"
21
+ :draggable="isDraggable"
22
+ @click="onClick"
23
+ @touchend="onTouchEnd"
24
+ @dragstart="onNodeDragStart"
25
+ @dragover="onNodeDragOver"
26
+ @dragleave="onNodeDragLeave"
27
+ @dragend="onNodeDragEnd"
28
+ @drop="onNodeDrop"
29
+ v-bind="getPTOptions('nodeContent')"
30
+ :data-p-selected="checkboxMode ? checked : selected"
31
+ :data-p-selectable="selectable"
32
+ >
18
33
  <button v-ripple type="button" :class="cx('nodeToggleButton')" @click="toggle" tabindex="-1" :data-p-leaf="leaf" v-bind="getPTOptions('nodeToggleButton')">
19
34
  <template v-if="node.loading && loadingMode === 'icon'">
20
35
  <!-- TODO: nodetogglericon deprecated since v4.0-->
@@ -50,11 +65,14 @@
50
65
  <template v-else>{{ label(node) }}</template>
51
66
  </span>
52
67
  </div>
68
+ <div v-if="isNextDropPointActive" :class="cx('dropPoint')" aria-hidden="true" />
53
69
  <ul v-if="hasChildren && expanded" :class="cx('nodeChildren')" role="group" v-bind="ptm('nodeChildren')">
54
70
  <TreeNode
55
71
  v-for="(childNode, index) of node.children"
56
72
  :key="childNode.key"
57
73
  :node="childNode"
74
+ :parentNode="node"
75
+ :rootNodes="rootNodes"
58
76
  :templates="templates"
59
77
  :level="level + 1"
60
78
  :index="index"
@@ -65,6 +83,10 @@
65
83
  :selectionMode="selectionMode"
66
84
  :selectionKeys="selectionKeys"
67
85
  @checkbox-change="propagateUp"
86
+ :draggableScope="draggableScope"
87
+ :dragdrop="dragdrop"
88
+ :validateDrop="validateDrop"
89
+ @node-drop="onChildNodeDrop"
68
90
  :unstyled="unstyled"
69
91
  :pt="pt"
70
92
  />
@@ -73,7 +95,7 @@
73
95
  </template>
74
96
 
75
97
  <script>
76
- import { find, findSingle, getAttribute } from '@primeuix/utils/dom';
98
+ import { find, findSingle, getAttribute, getOuterHeight, getOuterWidth } from '@primeuix/utils';
77
99
  import BaseComponent from '@primevue/core/basecomponent';
78
100
  import CheckIcon from '@primevue/icons/check';
79
101
  import ChevronDownIcon from '@primevue/icons/chevrondown';
@@ -87,12 +109,20 @@ export default {
87
109
  name: 'TreeNode',
88
110
  hostName: 'Tree',
89
111
  extends: BaseComponent,
90
- emits: ['node-toggle', 'node-click', 'checkbox-change'],
112
+ emits: ['node-toggle', 'node-click', 'checkbox-change', 'node-drop'],
91
113
  props: {
92
114
  node: {
93
115
  type: null,
94
116
  default: null
95
117
  },
118
+ parentNode: {
119
+ type: null,
120
+ default: null
121
+ },
122
+ rootNodes: {
123
+ type: Array,
124
+ default: null
125
+ },
96
126
  expandedKeys: {
97
127
  type: null,
98
128
  default: null
@@ -117,10 +147,34 @@ export default {
117
147
  type: Number,
118
148
  default: null
119
149
  },
150
+ draggableScope: {
151
+ type: [String, Array],
152
+ default: null
153
+ },
154
+ dragdrop: {
155
+ type: Boolean,
156
+ default: null
157
+ },
158
+ validateDrop: {
159
+ type: Boolean,
160
+ default: false
161
+ },
120
162
  index: null
121
163
  },
122
164
  nodeTouched: false,
123
165
  toggleClicked: false,
166
+ inject: {
167
+ $pcTree: {
168
+ default: undefined
169
+ }
170
+ },
171
+ data() {
172
+ return {
173
+ isPrevDropPointHovered: false,
174
+ isNextDropPointHovered: false,
175
+ isNodeDropHovered: false
176
+ };
177
+ },
124
178
  mounted() {
125
179
  this.setAllNodesTabIndexes();
126
180
  },
@@ -288,6 +342,147 @@ export default {
288
342
  onTabKey() {
289
343
  this.setAllNodesTabIndexes();
290
344
  },
345
+ onChildNodeDrop(event) {
346
+ this.$emit('node-click', event);
347
+ },
348
+ insertNodeOnDrop() {
349
+ const { dragNode, dragNodeIndex, dragNodeSubNodes, dragDropService } = this.$pcTree;
350
+
351
+ if (!this.node || dragNodeIndex == null || !dragNode || !dragNodeSubNodes) {
352
+ return;
353
+ }
354
+
355
+ const position = this.dropPosition;
356
+ const subNodes = this.subNodes || [];
357
+ const index = this.index || 0;
358
+ const dropIndex = dragNodeSubNodes === subNodes ? (dragNodeIndex > index ? index : index - 1) : index;
359
+
360
+ dragNodeSubNodes.splice(dragNodeIndex, 1);
361
+
362
+ if (position < 0) {
363
+ // insert before a Node
364
+ subNodes.splice(dropIndex, 0, dragNode);
365
+ } else if (position > 0) {
366
+ // insert after a Node
367
+ subNodes.splice(dropIndex + 1, 0, dragNode);
368
+ } else {
369
+ // insert as child of a Node
370
+ this.node.children = this.node.children || [];
371
+ this.node.children.push(dragNode);
372
+ }
373
+
374
+ dragDropService.stopDrag({
375
+ node: dragNode,
376
+ subNodes,
377
+ index: dragNodeIndex
378
+ });
379
+ },
380
+ onNodeDrop(event) {
381
+ event.preventDefault();
382
+ event.stopPropagation();
383
+
384
+ if (this.isDroppable) {
385
+ const { dragNode } = this.$pcTree;
386
+ const position = this.dropPosition;
387
+ const isValidDrop = position !== 0 || (position === 0 && this.isNodeDroppable);
388
+
389
+ if (isValidDrop) {
390
+ if (this.validateDrop) {
391
+ this.$emit('node-drop', {
392
+ originalEvent: event,
393
+ dragNode: dragNode,
394
+ dropNode: this.node,
395
+ index: this.index,
396
+ accept: () => {
397
+ this.insertNodeOnDrop();
398
+ }
399
+ });
400
+ } else {
401
+ this.insertNodeOnDrop();
402
+ this.$emit('node-drop', {
403
+ originalEvent: event,
404
+ dragNode: dragNode,
405
+ dropNode: this.node,
406
+ index: this.index
407
+ });
408
+ }
409
+ }
410
+ }
411
+
412
+ this.isPrevDropPointHovered = false;
413
+ this.isNextDropPointHovered = false;
414
+ this.isNodeDropHovered = false;
415
+ },
416
+ onNodeDragStart(event) {
417
+ if (this.isNodeDraggable) {
418
+ event.dataTransfer?.setData('text', 'data');
419
+
420
+ const target = event.currentTarget;
421
+ const dragEl = target.cloneNode(true);
422
+ const toggler = dragEl.querySelector('[data-pc-section="nodetogglebutton"]');
423
+ const checkbox = dragEl.querySelector('[data-pc-name="pcnodecheckbox"]');
424
+
425
+ target.setAttribute('data-p-dragging', 'true');
426
+ dragEl.style.width = getOuterWidth(target) + 'px';
427
+ dragEl.style.height = getOuterHeight(target) + 'px';
428
+ dragEl.setAttribute('data-pc-section', 'drag-image');
429
+ toggler.style.visibility = 'hidden';
430
+ checkbox?.remove();
431
+ document.body.appendChild(dragEl);
432
+ event.dataTransfer?.setDragImage(dragEl, 0, 0);
433
+
434
+ setTimeout(() => document.body.removeChild(dragEl), 0);
435
+
436
+ this.$pcTree.dragDropService.startDrag({
437
+ node: this.node,
438
+ subNodes: this.subNodes,
439
+ index: this.index,
440
+ scope: this.draggableScope
441
+ });
442
+ } else {
443
+ event.preventDefault();
444
+ }
445
+ },
446
+ onNodeDragOver(event) {
447
+ event.dataTransfer.dropEffect = 'move';
448
+
449
+ if (this.isDroppable) {
450
+ const nodeElement = event.currentTarget;
451
+ const rect = nodeElement.getBoundingClientRect();
452
+ const y = event.clientY - rect.top;
453
+
454
+ this.isPrevDropPointHovered = false;
455
+ this.isNextDropPointHovered = false;
456
+ this.isNodeDropHovered = false;
457
+
458
+ if (y < rect.height * 0.25) {
459
+ this.isPrevDropPointHovered = true;
460
+ } else if (y > rect.height * 0.75) {
461
+ this.isNextDropPointHovered = true;
462
+ } else if (this.isNodeDroppable) {
463
+ this.isNodeDropHovered = true;
464
+ }
465
+ }
466
+
467
+ if (this.dragdrop) {
468
+ event.preventDefault();
469
+ event.stopPropagation();
470
+ }
471
+ },
472
+ onNodeDragLeave() {
473
+ this.isPrevDropPointHovered = false;
474
+ this.isNextDropPointHovered = false;
475
+ this.isNodeDropHovered = false;
476
+ },
477
+ onNodeDragEnd(event) {
478
+ event.currentTarget?.removeAttribute('data-p-dragging');
479
+
480
+ this.$pcTree.dragDropService.stopDrag({
481
+ node: this.node,
482
+ subNodes: this.subNodes,
483
+ index: this.index
484
+ });
485
+ },
291
486
  setAllNodesTabIndexes() {
292
487
  const nodes = find(this.$refs.currentNode.closest('[data-pc-section="rootchildren"]'), '[role="treeitem"]');
293
488
 
@@ -458,6 +653,33 @@ export default {
458
653
  },
459
654
  ariaSelected() {
460
655
  return this.checkboxMode ? this.checked : undefined;
656
+ },
657
+ isPrevDropPointActive() {
658
+ return this.isPrevDropPointHovered && this.isDroppable;
659
+ },
660
+ isNextDropPointActive() {
661
+ return this.isNextDropPointHovered && this.isDroppable;
662
+ },
663
+ dropPosition() {
664
+ return this.isPrevDropPointActive ? -1 : this.isNextDropPointActive ? 1 : 0;
665
+ },
666
+ subNodes() {
667
+ return this.parentNode ? this.parentNode.children : this.rootNodes;
668
+ },
669
+ isDraggable() {
670
+ return this.dragdrop;
671
+ },
672
+ isDroppable() {
673
+ return this.dragdrop && this.$pcTree.allowNodeDrop(this.node);
674
+ },
675
+ isNodeDraggable() {
676
+ return this.node?.draggable !== false && this.isDraggable;
677
+ },
678
+ isNodeDroppable() {
679
+ return this.node?.droppable !== false && this.isDroppable;
680
+ },
681
+ isNodeDropActive() {
682
+ return this.isNodeDropHovered && this.isNodeDroppable;
461
683
  }
462
684
  },
463
685
  components: {
package/tree/index.d.ts CHANGED
@@ -97,6 +97,29 @@ export interface TreeFilterEvent {
97
97
  value: string;
98
98
  }
99
99
 
100
+ /**
101
+ * Custom node drop event.
102
+ * @see {@link TreeEmitsOptions.node-drop}
103
+ */
104
+ export interface TreeNodeDropEvent {
105
+ /**
106
+ * Original event
107
+ */
108
+ originalEvent: Event;
109
+ /**
110
+ * Dragged node
111
+ */
112
+ dragNode: TreeNode;
113
+ /**
114
+ * Dropped node
115
+ */
116
+ dropNode: TreeNode;
117
+ /**
118
+ * Index of the dropped node
119
+ */
120
+ index: number;
121
+ }
122
+
100
123
  /**
101
124
  * Custom passthrough(pt) options.
102
125
  * @see {@link TreeProps.pt}
@@ -173,6 +196,14 @@ export interface TreePassThroughOptions<T = any> {
173
196
  * Used to pass attributes to the loading icon's DOM element.
174
197
  */
175
198
  loadingIcon?: TreePassThroughOptionType<T>;
199
+ /**
200
+ * Used to pass attributes to the empty message's DOM element.
201
+ */
202
+ emptyMessage?: TreePassThroughOptionType<T>;
203
+ /**
204
+ * Used to pass attributes to the drop point's DOM element.
205
+ */
206
+ dropPoint?: TreePassThroughOptionType<T>;
176
207
  /**
177
208
  * Used to manage all lifecycle hooks.
178
209
  * @see {@link BaseComponent.ComponentHooks}
@@ -312,6 +343,26 @@ export interface TreeProps {
312
343
  * Height of the scroll viewport in fixed units or the 'flex' keyword for a dynamic size.
313
344
  */
314
345
  scrollHeight?: HintedString<'flex'> | undefined;
346
+ /**
347
+ * Whether the nodes are draggable and droppable.
348
+ * @defaultValue null
349
+ */
350
+ dragdrop?: boolean | undefined;
351
+ /**
352
+ * Scope of the draggable nodes to match a droppableScope.
353
+ * @defaultValue null
354
+ */
355
+ draggableScope?: string | string[] | undefined;
356
+ /**
357
+ * Scope of the droppable nodes to match a draggableScope.
358
+ * @defaultValue null
359
+ */
360
+ droppableScope?: string | string[] | undefined;
361
+ /**
362
+ * When enabled, drop can be accepted or rejected based on condition defined at node-drop.
363
+ * @defaultValue false
364
+ */
365
+ validateDrop?: boolean | undefined;
315
366
  /**
316
367
  * Defines a string value that labels an interactive element.
317
368
  */
@@ -501,6 +552,10 @@ export interface TreeSlots {
501
552
  */
502
553
  selectionKeys: TreeSelectionKeys;
503
554
  }): VNode[];
555
+ /**
556
+ * Custom empty template.
557
+ */
558
+ empty(): VNode[];
504
559
  /**
505
560
  * Optional slots.
506
561
  * @todo
@@ -512,6 +567,11 @@ export interface TreeSlots {
512
567
  * Defines valid emits in Tree component.
513
568
  */
514
569
  export interface TreeEmitsOptions {
570
+ /**
571
+ * Emitted when the value change.
572
+ * @param {TreeNode} value - New value.
573
+ */
574
+ 'update:value'(value: TreeNode[]): void;
515
575
  /**
516
576
  * Emitted when the expanded keys change.
517
577
  * @param {TreeNode} value - New expanded keys.
@@ -542,6 +602,11 @@ export interface TreeEmitsOptions {
542
602
  * @param {TreeNode} node - Node instance.
543
603
  */
544
604
  'node-collapse'(node: TreeNode): void;
605
+ /**
606
+ * Callback to invoke when a node is collapsed.
607
+ * @param {TreeNode} node - Node instance.
608
+ */
609
+ 'node-drop'(node: TreeNodeDropEvent): void;
545
610
  /**
546
611
  * Callback to invoke on filter input.
547
612
  * @param {TreeFilterEvent} event - Custom filter event.