@veritree/ui 0.58.2 → 0.60.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/index.js CHANGED
@@ -76,6 +76,10 @@ import VTDisclosureIcon from './src/components/Disclosure/VTDisclosureIcon.vue';
76
76
  import VTDisclosureContent from './src/components/Disclosure/VTDisclosureContent.vue';
77
77
  import VTSkeleton from './src/components/Skeleton/VTSkeleton.vue';
78
78
  import VTSkeletonItem from './src/components/Skeleton/VTSkeletonItem.vue';
79
+ import VTCarousel from './src/components/Carousel/VTCarousel.vue';
80
+ import VTCarouselBackward from './src/components/Carousel/VTCarouselBackward.vue';
81
+ import VTCarouselForward from './src/components/Carousel/VTCarouselForward.vue';
82
+ import VTCarouselTracker from './src/components/Carousel/VTCarouselTracker.vue';
79
83
 
80
84
  export {
81
85
  VTAvatar,
@@ -156,4 +160,8 @@ export {
156
160
  VTSkeleton,
157
161
  VTSkeletonItem,
158
162
  VTChip,
163
+ VTCarousel,
164
+ VTCarouselBackward,
165
+ VTCarouselForward,
166
+ VTCarouselTracker
159
167
  };
package/nuxt.js CHANGED
@@ -4,6 +4,7 @@ const components = [
4
4
  'src/components/Alert',
5
5
  'src/components/Avatar',
6
6
  'src/components/Button',
7
+ 'src/components/Carousel',
7
8
  'src/components/Chip',
8
9
  'src/components/Drawer',
9
10
  'src/components/Dialog',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veritree/ui",
3
- "version": "0.58.2",
3
+ "version": "0.60.0",
4
4
  "description": "veritree ui library",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -0,0 +1,100 @@
1
+ <template>
2
+ <div class="relative overflow-hidden">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ let windowResizeTimeout = null;
9
+
10
+ export default {
11
+ name: 'VTCarousel',
12
+
13
+ provide() {
14
+ return {
15
+ apiCarousel: () => {
16
+ const registerElTracker = (elTracker) => {
17
+ if (!elTracker) return;
18
+ this.elTracker = elTracker;
19
+ };
20
+
21
+ return {
22
+ elTracker: this.elTracker,
23
+ hasContentOverflow: this.hasContentOverflow,
24
+ hasScrolledToLeftEnd: this.hasScrolledToLeftEnd,
25
+ hasScrolledToLeftStart: this.hasScrolledToLeftStart,
26
+ isLeftEndReached: this.isLeftEndReached,
27
+ isLeftStartReached: this.isLeftStartReached,
28
+ onMutateTracker: this.onMutateTracker,
29
+ registerElTracker,
30
+ };
31
+ },
32
+ };
33
+ },
34
+
35
+ data() {
36
+ return {
37
+ elTracker: null,
38
+ hasContentOverflow: false,
39
+ hasScrolledToLeftEnd: false,
40
+ hasScrolledToLeftStart: true,
41
+ };
42
+ },
43
+
44
+ mounted() {
45
+ this.addEvents();
46
+ this.hasContentOverflow = this.isContentOverflowing();
47
+ },
48
+
49
+ methods: {
50
+ addEvents() {
51
+ window.addEventListener('resize', this.onResizeWindow);
52
+ },
53
+
54
+ onResizeWindow() {
55
+ clearTimeout(windowResizeTimeout);
56
+
57
+ windowResizeTimeout = setTimeout(() => {
58
+ this.hasContentOverflow = this.isContentOverflowing();
59
+ }, 250);
60
+ },
61
+
62
+ onMutateTracker() {
63
+ this.hasContentOverflow = this.isContentOverflowing();
64
+ },
65
+
66
+ /**
67
+ * Checks if the scroll position is close to the left end of an element within a small margin.
68
+ *
69
+ * @returns {boolean} True if the scroll position is near the left end, otherwise false.
70
+ */
71
+ isLeftEndReached() {
72
+ const { offsetWidth, scrollLeft, scrollWidth } = this.elTracker;
73
+ const distanceToLeftEnd = Math.abs(
74
+ scrollWidth - offsetWidth - scrollLeft
75
+ );
76
+
77
+ // Check if the calculated distance is less than or equal to 10 pixels
78
+ this.hasScrolledToLeftEnd = distanceToLeftEnd <= 10;
79
+ },
80
+
81
+ /**
82
+ * Checks if the scroll position is at the leftmost starting point of an element.
83
+ *
84
+ * @returns {boolean} True if the scroll position is at the leftmost starting point, otherwise false.
85
+ */
86
+ isLeftStartReached() {
87
+ this.hasScrolledToLeftStart = this.elTracker.scrollLeft === 0;
88
+ },
89
+
90
+ /**
91
+ * Checks if the content inside the carousel overflows horizontally.
92
+ *
93
+ * @returns {boolean} True if the content overflows, false otherwise.
94
+ */
95
+ isContentOverflowing() {
96
+ return this.elTracker.offsetWidth !== this.elTracker.scrollWidth;
97
+ },
98
+ },
99
+ };
100
+ </script>
@@ -0,0 +1,36 @@
1
+ <template>
2
+ <div
3
+ :class="[visible ? 'pointer-events-none opacity-0' : 'opacity-100']"
4
+ class="transition-opacity duration-100"
5
+ >
6
+ <slot :scroll-backward="scrollBackward" />
7
+ </div>
8
+ </template>
9
+
10
+ <script>
11
+ export default {
12
+ name: 'VTCarouselPrev',
13
+
14
+ inject: ['apiCarousel'],
15
+
16
+ computed: {
17
+ carousel() {
18
+ return this.apiCarousel();
19
+ },
20
+
21
+ elTracker() {
22
+ return this.carousel.elTracker;
23
+ },
24
+
25
+ visible() {
26
+ return this.carousel.hasScrolledToLeftStart;
27
+ },
28
+ },
29
+
30
+ methods: {
31
+ scrollBackward() {
32
+ this.elTracker.scrollLeft -= 200;
33
+ },
34
+ },
35
+ };
36
+ </script>
@@ -0,0 +1,38 @@
1
+ <template>
2
+ <div
3
+ :class="[visible ? 'opacity-100' : 'pointer-events-none opacity-0']"
4
+ class="transition-opacity duration-100"
5
+ >
6
+ <slot :scroll-forward="scrollForward" />
7
+ </div>
8
+ </template>
9
+
10
+ <script>
11
+ export default {
12
+ name: 'VTCarouselNext',
13
+
14
+ inject: ['apiCarousel'],
15
+
16
+ computed: {
17
+ carousel() {
18
+ return this.apiCarousel();
19
+ },
20
+
21
+ elTracker() {
22
+ return this.carousel.elTracker;
23
+ },
24
+
25
+ visible() {
26
+ return (
27
+ this.carousel.hasContentOverflow && !this.carousel.hasScrolledToLeftEnd
28
+ );
29
+ },
30
+ },
31
+
32
+ methods: {
33
+ scrollForward() {
34
+ this.elTracker.scrollLeft += 200;
35
+ },
36
+ },
37
+ };
38
+ </script>
@@ -0,0 +1,81 @@
1
+ <template>
2
+ <div class="relative flex snap-start overflow-x-scroll">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ export default {
9
+ name: 'VTCarouselTracker',
10
+
11
+ inject: ['apiCarousel'],
12
+
13
+ data() {
14
+ return {
15
+ mutationObserver: null,
16
+ };
17
+ },
18
+
19
+ beforeDestroy() {
20
+ if (!this.$el) {
21
+ return;
22
+ }
23
+
24
+ this.$el.removeEventListener('scrollend', this.onScrollEnd);
25
+ this.mutationObserver.disconnect(this.$el);
26
+ },
27
+
28
+ mounted() {
29
+ this.registerElTracker();
30
+ this.registerMutationObserver();
31
+ this.addEventListeners();
32
+ },
33
+
34
+ methods: {
35
+ /**
36
+ * Registers the current element as a tracker with the parent carousel API.
37
+ */
38
+ registerElTracker() {
39
+ this.apiCarousel().registerElTracker(this.$el);
40
+ },
41
+
42
+ /**
43
+ * Sets up a MutationObserver to monitor changes within the current element
44
+ * and triggers the appropriate action in the parent carousel API when mutations occur.
45
+ */
46
+ registerMutationObserver() {
47
+ this.mutationObserver = new MutationObserver(() => {
48
+ this.apiCarousel().onMutateTracker();
49
+ });
50
+
51
+ this.mutationObserver.observe(this.$el, {
52
+ childList: true,
53
+ });
54
+ },
55
+
56
+ /**
57
+ * Adds an event listener to the current element to listen for the 'scrollend' event
58
+ * and invokes the 'onScrollEnd' method when the event is triggered.
59
+ */
60
+ addEventListeners() {
61
+ this.$el.addEventListener('scrollend', this.onScrollEnd);
62
+ },
63
+
64
+ onScrollEnd() {
65
+ this.apiCarousel().isLeftEndReached();
66
+ this.apiCarousel().isLeftStartReached();
67
+ },
68
+ },
69
+ };
70
+ </script>
71
+
72
+ <style scoped type="postcss">
73
+ div {
74
+ scrollbar-width: 0;
75
+
76
+ &::-webkit-scrollbar {
77
+ width: 0;
78
+ height: 0;
79
+ }
80
+ }
81
+ </style>
@@ -4,12 +4,19 @@
4
4
  :class="[
5
5
  headless
6
6
  ? 'details-content'
7
- : 'overflow-hidden transition-[height] duration-300 ease-linear',
8
- invisible ? 'absolute opacity-0' : 'h-[var(--height)]',
7
+ : 'grid transition-[grid-template-rows] duration-300 ease-linear',
8
+ headless
9
+ ? expanded
10
+ ? 'details-content--expanded'
11
+ : null
12
+ : expanded
13
+ ? 'grid-rows-[1fr]'
14
+ : 'grid-rows-[0fr]',
9
15
  ]"
10
- :style="`--height: ${height};`"
11
16
  >
12
- <slot></slot>
17
+ <div :class="[headless ? 'detail-content__overflow' : 'overflow-hidden']">
18
+ <slot></slot>
19
+ </div>
13
20
  </div>
14
21
  </template>
15
22
 
@@ -39,15 +46,9 @@ export default {
39
46
  id() {
40
47
  return `disclosure-content-${this.idGroup}`;
41
48
  },
42
-
43
- height() {
44
- return this.expanded ? this.expandedHeight : 0;
45
- },
46
49
  },
47
50
 
48
51
  mounted() {
49
- this.handleVisibleHeight();
50
-
51
52
  const content = {
52
53
  id: this.id,
53
54
  idGroup: this.idGroup,
@@ -64,21 +65,6 @@ export default {
64
65
  },
65
66
 
66
67
  methods: {
67
- handleVisibleHeight() {
68
- // make panel position absolute and opacity 0,
69
- // to have it fully opened but not visible
70
- this.invisible = true;
71
-
72
- setTimeout(() => {
73
- // get computed height now since its fully opened
74
- // this height will be used by a css custom property to enable the transition
75
- this.expandedHeight = window.getComputedStyle(this.$el).height;
76
-
77
- // make invisible false once the full height is set
78
- this.invisible = false;
79
- }, 500);
80
- },
81
-
82
68
  expand() {
83
69
  this.expanded = true;
84
70
  },
@@ -86,7 +72,6 @@ export default {
86
72
  collapse() {
87
73
  this.expanded = false;
88
74
  },
89
-
90
75
  isVisible() {
91
76
  return this.expanded;
92
77
  },