@veritree/ui 0.23.0 → 0.24.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.
Files changed (73) hide show
  1. package/index.js +64 -72
  2. package/mixins/floating-ui-content.js +1 -1
  3. package/mixins/floating-ui-item.js +11 -7
  4. package/mixins/floating-ui.js +8 -1
  5. package/mixins/form-control-icon.js +3 -3
  6. package/mixins/form-control.js +9 -14
  7. package/nuxt.js +30 -24
  8. package/package.json +14 -6
  9. package/src/components/Alert/VTAlert.vue +55 -14
  10. package/src/components/Button/VTButton.vue +19 -11
  11. package/src/components/Checkbox/VTCheckbox.vue +134 -0
  12. package/src/components/Checkbox/VTCheckboxLabel.vue +3 -0
  13. package/src/components/Checkbox/VTCheckboxText.vue +20 -0
  14. package/src/components/Dialog/VTDialog.vue +22 -23
  15. package/src/components/Dialog/VTDialogClose.vue +1 -1
  16. package/src/components/Dialog/VTDialogContent.vue +13 -5
  17. package/src/components/Dialog/VTDialogFooter.vue +8 -2
  18. package/src/components/Dialog/VTDialogHeader.vue +2 -1
  19. package/src/components/Dialog/VTDialogMain.vue +1 -1
  20. package/src/components/Dialog/VTDialogOverlay.vue +5 -1
  21. package/src/components/Disclosure/VTDisclosureContent.vue +1 -1
  22. package/src/components/Disclosure/VTDisclosureDetails.vue +1 -1
  23. package/src/components/Disclosure/VTDisclosureHeader.vue +2 -2
  24. package/src/components/Disclosure/VTDisclosureIcon.vue +1 -1
  25. package/src/components/Drawer/VTDrawer.vue +6 -15
  26. package/src/components/Drawer/VTDrawerClose.vue +5 -5
  27. package/src/components/Drawer/VTDrawerContent.vue +10 -10
  28. package/src/components/Drawer/VTDrawerFooter.vue +4 -4
  29. package/src/components/Drawer/VTDrawerHeader.vue +4 -4
  30. package/src/components/Drawer/VTDrawerMain.vue +5 -5
  31. package/src/components/Drawer/VTDrawerOverlay.vue +6 -6
  32. package/src/components/Drawer/VTDrawerTitle.vue +5 -5
  33. package/src/components/DropdownMenu/VTDropdownMenu.vue +0 -6
  34. package/src/components/DropdownMenu/VTDropdownMenuContent.vue +10 -1
  35. package/src/components/DropdownMenu/VTDropdownMenuDivider.vue +7 -16
  36. package/src/components/DropdownMenu/VTDropdownMenuItem.vue +5 -1
  37. package/src/components/DropdownMenu/VTDropdownMenuLabel.vue +1 -10
  38. package/src/components/DropdownMenu/VTDropdownMenuTrigger.vue +2 -4
  39. package/src/components/Form/VTFormFeedback.vue +7 -1
  40. package/src/components/Form/VTFormGroup.vue +5 -7
  41. package/src/components/Form/VTFormLabel.vue +22 -0
  42. package/src/components/Form/VTFormRow.vue +5 -0
  43. package/src/components/Form/VTInput.vue +2 -5
  44. package/src/components/Form/VTInputIcon.vue +1 -2
  45. package/src/components/Form/VTInputPassword.vue +14 -5
  46. package/src/components/Form/VTTextarea.vue +3 -6
  47. package/src/components/Image/VTImage.vue +39 -8
  48. package/src/components/Listbox/VTListbox.vue +72 -5
  49. package/src/components/Listbox/VTListboxContent.vue +0 -1
  50. package/src/components/Listbox/VTListboxItem.vue +11 -1
  51. package/src/components/Listbox/VTListboxLabel.vue +3 -4
  52. package/src/components/Listbox/VTListboxList.vue +3 -1
  53. package/src/components/Listbox/VTListboxSearch.vue +10 -7
  54. package/src/components/Listbox/VTListboxTrigger.vue +2 -0
  55. package/src/components/Popover/VTPopoverContent.vue +3 -3
  56. package/src/components/Popover/VTPopoverItem.vue +6 -2
  57. package/src/components/ProgressBar/VTProgressBar.vue +21 -3
  58. package/src/components/Skeleton/VTSkeleton.vue +11 -0
  59. package/src/components/Skeleton/VTSkeletonItem.vue +9 -0
  60. package/src/components/Tabs/VTTab.vue +6 -5
  61. package/src/components/Tabs/VTTabGroup.vue +9 -7
  62. package/src/components/Tabs/VTTabPanel.vue +4 -5
  63. package/src/components/Tooltip/VTTooltipTrigger.vue +3 -5
  64. package/src/components/Transitions/FadeInOut.vue +2 -2
  65. package/src/components/Utils/FloatingUi.vue +31 -13
  66. package/src/utils/components.js +18 -0
  67. package/src/utils/images.js +18 -25
  68. package/src/components/Input/VTInput.vue +0 -82
  69. package/src/components/Input/VTInputDate.vue +0 -36
  70. package/src/components/Input/VTInputFile.vue +0 -60
  71. package/src/components/Input/VTInputUpload.vue +0 -54
  72. package/src/components/Modal/VTModal.vue +0 -69
  73. package/src/utils/genId.js +0 -13
@@ -1,10 +1,9 @@
1
1
  <template>
2
2
  <component
3
3
  :is="as"
4
- :class="{
5
- ListboxLabel: headless,
6
- 'mb-2 block text-xs font-normal uppercase': !headless,
7
- }"
4
+ :class="[
5
+ headless ? 'listbox-label' : 'mb-2 block text-xs font-normal uppercase',
6
+ ]"
8
7
  >
9
8
  <slot></slot>
10
9
  </component>
@@ -2,7 +2,9 @@
2
2
  <ul
3
3
  :id="id"
4
4
  :class="[
5
- headless ? 'listbox-list' : '-mx-3 max-h-[160px] w-auto overflow-y-auto',
5
+ headless
6
+ ? 'listbox-list'
7
+ : '-mx-3 max-h-[160px] w-auto overflow-y-auto scroll-auto',
6
8
  ]"
7
9
  >
8
10
  <slot></slot>
@@ -2,11 +2,11 @@
2
2
  <div class="-mx-3 -mt-2">
3
3
  <input
4
4
  ref="search"
5
- v-model="search"
5
+ :value="modelValue"
6
6
  v-bind="$attrs"
7
7
  type="text"
8
8
  class="leading-0 font-inherit w-full max-w-full appearance-none border-b border-solid border-gray-300 py-2 px-3 text-base text-inherit"
9
- @input="onChange"
9
+ @input="onInput"
10
10
  @click.stop
11
11
  @keydown.down.prevent="focusNextItem"
12
12
  @keydown.up.prevent="focusPreviousItem"
@@ -62,9 +62,9 @@ export default {
62
62
  this.$nextTick(() => setTimeout(() => this.$refs.search.focus(), 150));
63
63
  },
64
64
 
65
- beforeDestroy() {
65
+ beforeUnmount() {
66
66
  this.search = '';
67
- this.$emit('change', this.search.trim());
67
+ this.$emit('update:modelValue', '');
68
68
  },
69
69
 
70
70
  methods: {
@@ -110,7 +110,10 @@ export default {
110
110
  }
111
111
 
112
112
  // select new item
113
- this.items[newSelectedIndex].select();
113
+ if (this.items[newSelectedIndex]) {
114
+ this.index = newSelectedIndex;
115
+ this.items[newSelectedIndex].select();
116
+ }
114
117
  },
115
118
 
116
119
  unselectItem() {
@@ -121,11 +124,11 @@ export default {
121
124
  return this.items.findIndex((item) => item.isSelected());
122
125
  },
123
126
 
124
- onChange() {
127
+ onInput(event) {
125
128
  this.index = 0;
126
129
  if (this.item) this.item.select();
127
130
 
128
- this.$emit('change', this.search.trim());
131
+ this.$emit('update:modelValue', event.target.value);
129
132
  },
130
133
 
131
134
  onKeyEnter() {
@@ -41,6 +41,7 @@ export default {
41
41
 
42
42
  data() {
43
43
  return {
44
+ name: 'listbox-trigger',
44
45
  expanded: false,
45
46
  hasPopup: false,
46
47
  };
@@ -123,6 +124,7 @@ export default {
123
124
 
124
125
  onClick(e) {
125
126
  this.init(e);
127
+ this.$emit('click');
126
128
  },
127
129
 
128
130
  onKeyDownOrUp(e) {
@@ -1,10 +1,10 @@
1
1
  <template>
2
2
  <FloatingUi
3
- :id="id"
4
3
  :visible="visible"
4
+ :id="id"
5
5
  :headless="headless"
6
- :portal-class="$vnode.data.staticClass"
7
- component="popover"
6
+ :class="{ 'popover-content': headless }"
7
+ :floating-ui-class="floatingUiClass"
8
8
  >
9
9
  <slot></slot>
10
10
  </FloatingUi>
@@ -5,7 +5,7 @@
5
5
  // default styles
6
6
  headless
7
7
  ? 'popover-item'
8
- : 'relative z-10 -mx-3 flex items-center gap-2 px-3 py-2 text-inherit no-underline hover:bg-secondary-200/10',
8
+ : 'hover:bg-secondary-200/10 relative z-10 -mx-3 flex items-center gap-2 px-3 py-2 text-inherit no-underline',
9
9
  ]"
10
10
  @click="onClick"
11
11
  >
@@ -44,7 +44,11 @@ export default {
44
44
  },
45
45
 
46
46
  as() {
47
- return this.href ? 'a' : this.to ? 'NuxtLink' : 'button';
47
+ return this.href
48
+ ? 'a'
49
+ : this.to
50
+ ? resolveComponent('NuxtLink')
51
+ : 'button';
48
52
  },
49
53
  },
50
54
 
@@ -7,7 +7,7 @@
7
7
  ]"
8
8
  role="progressbar"
9
9
  aria-valuemin="0"
10
- aria-valuemax="100"
10
+ :aria-valuemax="max"
11
11
  :aria-valuenow="value"
12
12
  :aria-label="label"
13
13
  >
@@ -15,9 +15,9 @@
15
15
  :class="[
16
16
  headless
17
17
  ? 'progress-bar__indicator'
18
- : 'absolute left-0 h-full bg-secondary-300 transition-all',
18
+ : 'bg-secondary-200 absolute left-0 h-full transition-all',
19
19
  ]"
20
- :style="{ width: `${value}%` }"
20
+ :style="{ width: `${percentageComputed}%` }"
21
21
  ></div>
22
22
  </div>
23
23
  </template>
@@ -37,6 +37,24 @@ export default {
37
37
  type: [String, Number],
38
38
  default: 0,
39
39
  },
40
+ max: {
41
+ type: [String, Number],
42
+ default: 100,
43
+ },
44
+ percentage: {
45
+ type: [String, Number],
46
+ default: null,
47
+ },
48
+ },
49
+
50
+ computed: {
51
+ percentageComputed() {
52
+ if (typeof this.percentage !== 'undefined' && this.percentage !== null) {
53
+ return this.percentage;
54
+ }
55
+
56
+ return (this.value / this.max) * 100;
57
+ },
40
58
  },
41
59
  };
42
60
  </script>
@@ -0,0 +1,11 @@
1
+ <template>
2
+ <div class="animate-pulse">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ export default {
9
+ name: 'VTSkeleton',
10
+ };
11
+ </script>
@@ -0,0 +1,9 @@
1
+ <template>
2
+ <div class="bg-gray-300" />
3
+ </template>
4
+
5
+ <script>
6
+ export default {
7
+ name: 'VTSkeletonItem',
8
+ };
9
+ </script>
@@ -18,11 +18,11 @@
18
18
  ]"
19
19
  role="tab"
20
20
  type="button"
21
- @click.stop="onClick"
21
+ @click.prevent.stop="onClick"
22
22
  @keydown="onKeyDown"
23
23
  @blur="onBlur"
24
24
  >
25
- <slot></slot>
25
+ <slot :selected="selected"></slot>
26
26
  </button>
27
27
  </template>
28
28
 
@@ -32,7 +32,7 @@ import { keys } from '../../utils/keyboard';
32
32
  export default {
33
33
  name: 'VTTabItem',
34
34
 
35
- inject: ['api'],
35
+ inject: ['apiTabs'],
36
36
 
37
37
  props: {
38
38
  headless: {
@@ -43,7 +43,7 @@ export default {
43
43
 
44
44
  data() {
45
45
  return {
46
- api: this.api(),
46
+ api: this.apiTabs(),
47
47
  index: null,
48
48
  indexFocus: null,
49
49
  selected: false,
@@ -60,7 +60,7 @@ export default {
60
60
  this.api.registerTab(this);
61
61
  },
62
62
 
63
- beforeDestroy() {
63
+ beforeUnmount() {
64
64
  this.api.unregisterTab(this.index);
65
65
  },
66
66
 
@@ -106,6 +106,7 @@ export default {
106
106
  onClick() {
107
107
  this.api.selectTab(this.index);
108
108
  this.$emit('change');
109
+ this.$emit('click');
109
110
  },
110
111
 
111
112
  onKeyDown(event) {
@@ -5,10 +5,10 @@
5
5
  </template>
6
6
 
7
7
  <style scoped>
8
- .TabGroup.vertical{
8
+ .TabGroup.vertical {
9
9
  display: flex;
10
10
  }
11
- .TabGroup.vertical .TabList{
11
+ .TabGroup.vertical .TabList {
12
12
  display: flex;
13
13
  flex-direction: column;
14
14
  }
@@ -17,15 +17,17 @@
17
17
  <script>
18
18
  export default {
19
19
  name: 'VTTabGroup',
20
+
20
21
  props: {
21
- vertical: {
22
- type: Boolean,
23
- default: false
24
- }
22
+ vertical: {
23
+ type: Boolean,
24
+ default: false,
25
+ },
25
26
  },
27
+
26
28
  provide() {
27
29
  return {
28
- api: () => {
30
+ apiTabs: () => {
29
31
  const registerTab = (tab) => {
30
32
  _register(this.tabs, tab);
31
33
  if (tab.index === 0) tab.select();
@@ -14,11 +14,10 @@
14
14
  export default {
15
15
  name: 'VTTabPanel',
16
16
 
17
- inject: ['api'],
17
+ inject: ['apiTabs'],
18
18
 
19
19
  data() {
20
20
  return {
21
- api: this.api(),
22
21
  index: null,
23
22
  visible: false,
24
23
  };
@@ -31,11 +30,11 @@ export default {
31
30
  },
32
31
 
33
32
  mounted() {
34
- this.api.registerTabPanel(this);
33
+ this.apiTabs().registerTabPanel(this);
35
34
  },
36
35
 
37
- beforeDestroy() {
38
- this.api.unregisterTabPanel(this.id);
36
+ beforeUnmount() {
37
+ this.apiTabs().unregisterTabPanel(this.id);
39
38
  },
40
39
 
41
40
  methods: {
@@ -2,9 +2,8 @@
2
2
  <div
3
3
  :id="id"
4
4
  :aria-describedby="ariaDescribedBy"
5
- class="inline-flex"
6
5
  @mouseenter="onMouseenter"
7
- @mouseleave="onMouseleave"
6
+ @mouseleave="onmouseout"
8
7
  >
9
8
  <slot />
10
9
  </div>
@@ -55,7 +54,7 @@ export default {
55
54
  }, this.apiTooltip().delayDuration);
56
55
  },
57
56
 
58
- onMouseleave() {
57
+ onmouseout() {
59
58
  clearTimeout(tooltipTriggerTimeout);
60
59
  this.cancel();
61
60
  },
@@ -75,8 +74,7 @@ export default {
75
74
  // delay stop propagation to close other visible
76
75
  // dropdowns and delay click event to control
77
76
  // this dropdown visibility
78
- setTimeout(() => e.stopImmediatePropagation(), 50);
79
- setTimeout(() => this.showComponentContent(), 100);
77
+ this.showComponentContent();
80
78
  },
81
79
 
82
80
  cancel() {
@@ -1,10 +1,10 @@
1
1
  <template>
2
2
  <transition
3
3
  enter-active-class="duration-300 ease-out"
4
- enter-class="transform opacity-0"
4
+ enter-from-class="transform opacity-0"
5
5
  enter-to-class="opacity-100"
6
6
  leave-active-class="duration-300 ease-in"
7
- leave-class="opacity-100"
7
+ leave-from-class="opacity-100"
8
8
  leave-to-class="transform opacity-0"
9
9
  @after-enter="afterEnter"
10
10
  @after-leave="afterLeave"
@@ -1,12 +1,12 @@
1
1
  <template>
2
- <Portal>
2
+ <Teleport to="body">
3
3
  <transition
4
4
  enter-active-class="duration-200 ease-out"
5
- enter-class="translate-y-[15px] opacity-0"
6
- enter-to-class="translate-y-0 opacity-100"
5
+ :enter-from-class="transitionEnterClass"
6
+ :enter-to-class="transitionEnterToClass"
7
7
  leave-active-class="duration-200 ease-in"
8
- leave-class="translate-y-0 opacity-100"
9
- leave-to-class="translate-y-[15px] opacity-0"
8
+ :leave-from-class="transitionLeaveClass"
9
+ :leave-to-class="transitionLeaveToClass"
10
10
  @after-leave="hidden"
11
11
  @after-enter="shown"
12
12
  >
@@ -15,23 +15,19 @@
15
15
  :class="[
16
16
  headless
17
17
  ? `${this.component}-content`
18
- : `shadow-300 absolute z-50 grid overflow-x-hidden rounded-md py-2 px-3 ${this.classes} ${this.portalClass}`,
18
+ : `absolute z-50 grid overflow-hidden rounded-md shadow-md ${this.classes} ${this.portalClass}`,
19
19
  ]"
20
20
  v-bind="$attrs"
21
21
  >
22
22
  <slot />
23
23
  </div>
24
24
  </transition>
25
- </Portal>
25
+ </Teleport>
26
26
  </template>
27
27
 
28
28
  <script>
29
- import { Portal } from '@linusborg/vue-simple-portal';
30
-
31
29
  export default {
32
- components: {
33
- Portal,
34
- },
30
+ inheritAttrs: false,
35
31
 
36
32
  props: {
37
33
  component: {
@@ -46,6 +42,10 @@ export default {
46
42
  type: Boolean,
47
43
  default: false,
48
44
  },
45
+ ariaActivedescendant: {
46
+ type: String,
47
+ default: null,
48
+ },
49
49
  portalClass: {
50
50
  type: [String, Function],
51
51
  default: null,
@@ -58,7 +58,25 @@ export default {
58
58
  },
59
59
 
60
60
  classes() {
61
- return this.isTooltip ? 'bg-gray-800 text-sm text-white' : 'bg-white';
61
+ return this.isTooltip
62
+ ? 'bg-gray-800 text-sm text-white py-1 px-2'
63
+ : 'bg-white py-2 px-3';
64
+ },
65
+
66
+ transitionEnterClass() {
67
+ return this.isTooltip ? 'opacity-0' : 'translate-y-[15px] opacity-0';
68
+ },
69
+
70
+ transitionEnterToClass() {
71
+ return this.isTooltip ? 'opacity-100' : 'translate-y-0 opacity-100';
72
+ },
73
+
74
+ transitionLeaveClass() {
75
+ return this.transitionEnterToClass;
76
+ },
77
+
78
+ transitionLeaveToClass() {
79
+ return this.transitionEnterClass;
62
80
  },
63
81
  },
64
82
 
@@ -0,0 +1,18 @@
1
+ /**
2
+ *
3
+ * @param {HTMLElement} el
4
+ * @param {HTMLElement} parent
5
+ */
6
+ export const scrollElementIntoView = (el, parent) => {
7
+ // this works better than scrollIntoView
8
+ if (parent.scrollHeight <= parent.clientHeight) return;
9
+
10
+ const scrollBottom = parent.clientHeight + parent.scrollTop;
11
+ const elBottom = el.offsetTop + el.offsetHeight;
12
+
13
+ if (elBottom > scrollBottom) {
14
+ parent.scrollTop = elBottom - parent.clientHeight;
15
+ } else if (el.offsetTop < parent.scrollTop) {
16
+ parent.scrollTop = el.offsetTop;
17
+ }
18
+ };
@@ -3,42 +3,35 @@ export const handleImageResizing = (url, width) => {
3
3
  if (!url) {
4
4
  return '';
5
5
  }
6
-
6
+
7
7
  let cdn;
8
-
8
+
9
9
  try {
10
- if (typeof url === 'object') {
11
- cdn = url.cdn_url;
12
- }
10
+ // new code
11
+ if (typeof url === 'string' && url.includes('cloudfront.net/')) {
12
+ cdn = url;
13
+ } else {
14
+ // old code
15
+ if (typeof url === 'object') {
16
+ cdn = url.cdn_url;
17
+ }
13
18
 
14
- if (typeof url === 'string') {
15
- cdn = JSON.parse(url).cdn_url;
19
+ if (typeof url === 'string') {
20
+ cdn = JSON.parse(url).cdn_url;
21
+ }
16
22
  }
17
23
 
18
24
  const base64String = cdn.split('net/');
19
25
  const firstPart = base64String[0];
26
+ const decodedString = JSON.parse(atob(base64String[1]));
20
27
 
21
- // if atob is crossed and your editor says is not supported,
22
- // don't worry, it seems to be about NodeJS and not about
23
- // browsers.
24
- //
25
- // Details: https://developer.mozilla.org/en-US/docs/Web/API/atob
26
- const decodedString = JSON.parse(atob(base64String[1]));
27
-
28
- width
29
- ? (decodedString.edits.resize.width = width)
30
- : (decodedString.edits.resize.width = 450);
28
+ decodedString.edits.resize.width = width ?? 450;
31
29
 
32
- // if btoa is crossed and your editor says is not supported,
33
- // don't worry, it seems to be about NodeJS and not about
34
- // browsers.
35
- //
36
- // Details: https://developer.mozilla.org/en-US/docs/Web/API/atob
37
30
  const encodedString = btoa(JSON.stringify(decodedString));
38
31
  const encodedUrl = `${firstPart}net/${encodedString}`;
39
32
 
40
- return encodedUrl
41
- } catch(error) {
33
+ return encodedUrl;
34
+ } catch (error) {
42
35
  console.error(`${error} in ${cdn}`);
43
36
  }
44
- };
37
+ };
@@ -1,82 +0,0 @@
1
- <template>
2
- <input
3
- :class="classes"
4
- class="form-control"
5
- :data-theme="theme"
6
- :type="type"
7
- :value="value"
8
- v-on="listeners"
9
- />
10
- </template>
11
-
12
- <script>
13
- export default {
14
- name: 'VTInput',
15
-
16
- props: {
17
- lazy: {
18
- type: Boolean,
19
- default: false,
20
- },
21
- type: {
22
- type: String,
23
- default: 'text',
24
- },
25
- theme: {
26
- type: String,
27
- default: null,
28
- validator(value) {
29
- return ['dark'].includes(value);
30
- },
31
- },
32
- variant: {
33
- type: [String, Object],
34
- default: '',
35
- validator(value) {
36
- if (value === '' || typeof value === 'object') {
37
- return true;
38
- }
39
-
40
- return ['success', 'warning', 'error'].includes(value);
41
- },
42
- },
43
- value: {
44
- type: [String, Number, Object, Array],
45
- default: null,
46
- },
47
- },
48
-
49
- computed: {
50
- classes() {
51
- const classes = {};
52
-
53
- if (this.variant) {
54
- classes[`form-control--${this.variant}`] = true;
55
- }
56
-
57
- return classes;
58
- },
59
-
60
- listeners() {
61
- // `Object.assign` merges objects together to form a new object
62
- return Object.assign(
63
- {},
64
- // We add all the listeners from the parent
65
- this.$listeners,
66
- // Then we can add custom listeners or override the
67
- // behavior of some listeners.
68
- {
69
- // This ensures that the component works with v-model
70
- input: (event) => {
71
- if (this.lazy) return;
72
- this.$emit('input', event.target.value);
73
- },
74
- blur: (event) => {
75
- this.$emit('blur', event);
76
- },
77
- }
78
- );
79
- },
80
- },
81
- };
82
- </script>
@@ -1,36 +0,0 @@
1
- <template>
2
- <VTInput v-model="date" type="date" />
3
- </template>
4
-
5
- <script>
6
- import VTInput from './VTInput.vue';
7
-
8
- export default {
9
- name: 'VTInputDate',
10
-
11
- components: { VTInput },
12
-
13
- model: {
14
- prop: 'value',
15
- event: 'input',
16
- },
17
-
18
- props: {
19
- value: {
20
- type: String,
21
- default: '',
22
- },
23
- },
24
-
25
- computed: {
26
- date: {
27
- get() {
28
- return this.$date.format(this.value, 'YYYY-MM-DD');
29
- },
30
- set(newDate) {
31
- this.$emit('input', newDate);
32
- },
33
- },
34
- },
35
- };
36
- </script>