tide-design-system 2.2.7 → 2.2.8
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
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
import { PRIORITY } from '@/types/Priority';
|
|
7
7
|
import { CSS } from '@/types/Styles';
|
|
8
8
|
|
|
9
|
+
import type { Ref } from 'vue';
|
|
10
|
+
|
|
9
11
|
type Props = {
|
|
10
12
|
bleed?: number;
|
|
11
13
|
isFloating?: boolean;
|
|
@@ -26,10 +28,16 @@
|
|
|
26
28
|
title: undefined,
|
|
27
29
|
});
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
type Emits = {
|
|
32
|
+
(event: 'slideChange', slideIndex: number): void;
|
|
33
|
+
(event: 'slidesAddedToView', slidesInView: number[]): void;
|
|
34
|
+
};
|
|
30
35
|
|
|
31
|
-
const
|
|
36
|
+
const emit = defineEmits<Emits>();
|
|
32
37
|
|
|
38
|
+
const containerRef: Ref<HTMLDivElement | null> = ref(null);
|
|
39
|
+
const slideObserver = ref<IntersectionObserver | null>(null);
|
|
40
|
+
const slotObserver = ref<MutationObserver | null>(null);
|
|
33
41
|
const contentRightEdge = ref();
|
|
34
42
|
const contentWidth = ref();
|
|
35
43
|
const currentSlide = ref(0);
|
|
@@ -42,6 +50,7 @@
|
|
|
42
50
|
|
|
43
51
|
const updateContainerBleed = () => {
|
|
44
52
|
if (props.bleed == undefined) return;
|
|
53
|
+
if (containerRef.value === null) return;
|
|
45
54
|
|
|
46
55
|
if (isLastSlide.value && showButtons.value) {
|
|
47
56
|
// Prevent gradient from bleeding off left edge in last position.
|
|
@@ -61,17 +70,21 @@
|
|
|
61
70
|
* the frame.
|
|
62
71
|
*/
|
|
63
72
|
const measureCurrentSlide = () => {
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
const container = containerRef.value;
|
|
74
|
+
if (!container) return;
|
|
75
|
+
|
|
76
|
+
// Cast once, so TS knows these are HTMLElements
|
|
77
|
+
const slideOffsets = Array.from(container.children as HTMLCollectionOf<HTMLElement>).map(
|
|
78
|
+
(slide) => slide.offsetLeft
|
|
79
|
+
);
|
|
66
80
|
|
|
67
81
|
if (slideOffsets.length === 0) return;
|
|
68
82
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
});
|
|
83
|
+
const scrollLeft = container.scrollLeft;
|
|
84
|
+
|
|
85
|
+
const closestSlideOffset = slideOffsets.reduce((prev, curr) =>
|
|
86
|
+
Math.abs(curr - scrollLeft) < Math.abs(prev - scrollLeft) ? curr : prev
|
|
87
|
+
);
|
|
75
88
|
|
|
76
89
|
currentSlide.value = slideOffsets.indexOf(closestSlideOffset);
|
|
77
90
|
|
|
@@ -79,6 +92,7 @@
|
|
|
79
92
|
};
|
|
80
93
|
|
|
81
94
|
const measureDom = () => {
|
|
95
|
+
if (containerRef.value === null) return;
|
|
82
96
|
contentRightEdge.value = containerRef.value.scrollLeft + containerRef.value.clientWidth;
|
|
83
97
|
contentWidth.value = containerRef.value.scrollWidth;
|
|
84
98
|
frameWidth.value = containerRef.value.clientWidth;
|
|
@@ -97,17 +111,19 @@
|
|
|
97
111
|
};
|
|
98
112
|
|
|
99
113
|
const scrollToSlide = (slideIndex: number) => {
|
|
114
|
+
if (containerRef.value === null) return;
|
|
100
115
|
containerRef.value.scrollTo({
|
|
101
116
|
behavior: 'smooth',
|
|
102
|
-
left: containerRef.value.children[slideIndex].offsetLeft,
|
|
117
|
+
left: (containerRef.value.children[slideIndex] as HTMLElement).offsetLeft,
|
|
103
118
|
});
|
|
104
119
|
};
|
|
105
120
|
|
|
106
121
|
const showNextSlide = () => {
|
|
122
|
+
if (containerRef.value === null) return;
|
|
107
123
|
if (contentRightEdge.value > contentWidth.value) {
|
|
108
124
|
containerRef.value.scrollTo({
|
|
109
125
|
behavior: 'smooth',
|
|
110
|
-
left: contentWidth,
|
|
126
|
+
left: contentWidth.value,
|
|
111
127
|
});
|
|
112
128
|
} else if (!isLastSlide.value) {
|
|
113
129
|
scrollToSlide(currentSlide.value + 1);
|
|
@@ -120,13 +136,52 @@
|
|
|
120
136
|
}
|
|
121
137
|
};
|
|
122
138
|
|
|
139
|
+
const observeSlides = () => {
|
|
140
|
+
const options = {
|
|
141
|
+
root: containerRef?.value,
|
|
142
|
+
rootMargin: '0px',
|
|
143
|
+
threshold: 0.5,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
slideObserver.value = new IntersectionObserver((entries) => {
|
|
147
|
+
let slides: number[] = [];
|
|
148
|
+
|
|
149
|
+
entries.forEach((entry) => {
|
|
150
|
+
const slideIndex = Array.from(containerRef.value?.children ?? []).indexOf(entry.target);
|
|
151
|
+
if (entry.isIntersecting) {
|
|
152
|
+
slides.push(slideIndex);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
if (slides.length > 0) emit('slidesAddedToView', slides);
|
|
157
|
+
}, options);
|
|
158
|
+
Array.from(containerRef.value?.children ?? []).forEach((child) => {
|
|
159
|
+
if (slideObserver.value) slideObserver.value.observe(child);
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const observeSlot = () => {
|
|
164
|
+
const observerCallback = () => {
|
|
165
|
+
observeSlides();
|
|
166
|
+
};
|
|
167
|
+
slotObserver.value = new MutationObserver(observerCallback);
|
|
168
|
+
if (containerRef.value) slotObserver.value.observe(containerRef.value, { childList: true });
|
|
169
|
+
};
|
|
170
|
+
|
|
123
171
|
onMounted(() => {
|
|
172
|
+
if (window) {
|
|
173
|
+
window.addEventListener('resize', measureDom);
|
|
174
|
+
}
|
|
175
|
+
|
|
124
176
|
measureDom();
|
|
125
|
-
|
|
177
|
+
observeSlides();
|
|
178
|
+
observeSlot();
|
|
126
179
|
});
|
|
127
180
|
|
|
128
181
|
onUnmounted(() => {
|
|
129
182
|
window.removeEventListener('resize', measureDom);
|
|
183
|
+
slideObserver.value?.disconnect();
|
|
184
|
+
slotObserver.value?.disconnect();
|
|
130
185
|
});
|
|
131
186
|
|
|
132
187
|
onUpdated(() => {
|
|
@@ -134,7 +189,7 @@
|
|
|
134
189
|
});
|
|
135
190
|
|
|
136
191
|
watch(currentSlide, () => {
|
|
137
|
-
emit('
|
|
192
|
+
emit('slideChange', currentSlide.value);
|
|
138
193
|
});
|
|
139
194
|
</script>
|
|
140
195
|
|
|
@@ -2,7 +2,7 @@ import { action } from '@storybook/addon-actions';
|
|
|
2
2
|
|
|
3
3
|
import TideCard from '@/components/TideCard.vue';
|
|
4
4
|
import TideCarousel from '@/components/TideCarousel.vue';
|
|
5
|
-
import { argTypeBooleanUnrequired,
|
|
5
|
+
import { argTypeBooleanUnrequired, disabledArgType, doSomething, lineBreak, tab } from '@/utilities/storybook';
|
|
6
6
|
|
|
7
7
|
import type { StoryContext } from '@storybook/vue3';
|
|
8
8
|
|
|
@@ -17,7 +17,8 @@ const formatSnippet = (code: string, context: StoryContext) => {
|
|
|
17
17
|
if (args.isTouchscreen !== undefined) argsWithValues.push(`:is-touchscreen="${args.isTouchscreen}"`);
|
|
18
18
|
if (args.subtitle !== '') argsWithValues.push(`subtitle="${args.subtitle}"`);
|
|
19
19
|
if (args.title !== '') argsWithValues.push(`title="${args.title}"`);
|
|
20
|
-
if (args.
|
|
20
|
+
if (args.handleSlideChange) argsWithValues.push(`@slide-change="${args.handleSlideChange}"`);
|
|
21
|
+
if (args.handleSlidesAddedToView) argsWithValues.push(`@slides-added-to-view="${args.handleSlidesAddedToView}"`);
|
|
21
22
|
|
|
22
23
|
const slotContentMisc = args.misc === '' ? '' : `${tab}<template #misc>${args.misc}</template>${lineBreak}`;
|
|
23
24
|
|
|
@@ -46,17 +47,30 @@ const render = (args: any) => ({
|
|
|
46
47
|
components: { TideCard, TideCarousel },
|
|
47
48
|
methods: {
|
|
48
49
|
doSomething,
|
|
49
|
-
|
|
50
|
-
action(`Current slide
|
|
50
|
+
handleSlideChange: (index: number) => {
|
|
51
|
+
action(`Current slide`)(index);
|
|
51
52
|
|
|
52
53
|
try {
|
|
53
|
-
const callback = eval(args.
|
|
54
|
+
const callback = eval(args.handleSlideChange);
|
|
54
55
|
|
|
55
56
|
if (callback) {
|
|
56
57
|
callback();
|
|
57
58
|
}
|
|
58
59
|
} catch {
|
|
59
|
-
alert('Please specify a valid handler in the "
|
|
60
|
+
alert('Please specify a valid handler in the "slideChange" control.');
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
handleSlidesAddedToView: (slides: number[]) => {
|
|
64
|
+
action(`Slides added to view`)(slides);
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const callback = eval(args.handleSlidesAddedToView);
|
|
68
|
+
|
|
69
|
+
if (callback) {
|
|
70
|
+
callback();
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
alert('Please specify a valid handler in the "slidesAddedToView" control.');
|
|
60
74
|
}
|
|
61
75
|
},
|
|
62
76
|
},
|
|
@@ -64,7 +78,8 @@ const render = (args: any) => ({
|
|
|
64
78
|
// prettier-ignore
|
|
65
79
|
template:
|
|
66
80
|
`<TideCarousel
|
|
67
|
-
@change="
|
|
81
|
+
@slide-change="handleSlideChange"
|
|
82
|
+
@slides-added-to-view="handleSlidesAddedToView"
|
|
68
83
|
class="tide-margin-x-1"
|
|
69
84
|
v-bind="args"
|
|
70
85
|
>
|
|
@@ -74,7 +89,7 @@ const render = (args: any) => ({
|
|
|
74
89
|
class="tide-shrink-none tide-border-1 tide-border tide-padding-1"
|
|
75
90
|
v-for="(_child, index) in new Array(12)"
|
|
76
91
|
>
|
|
77
|
-
{{ args.default }}
|
|
92
|
+
{{ args.default.replace('#', index) }}
|
|
78
93
|
</li>
|
|
79
94
|
</TideCarousel>`,
|
|
80
95
|
updated() {
|
|
@@ -96,7 +111,6 @@ export default {
|
|
|
96
111
|
type: { summary: 'number' },
|
|
97
112
|
},
|
|
98
113
|
},
|
|
99
|
-
change: disabledArgType,
|
|
100
114
|
default: {
|
|
101
115
|
control: 'text',
|
|
102
116
|
description: 'Dynamic card content',
|
|
@@ -105,14 +119,28 @@ export default {
|
|
|
105
119
|
type: { summary: 'HTML' },
|
|
106
120
|
},
|
|
107
121
|
},
|
|
108
|
-
|
|
109
|
-
|
|
122
|
+
handleSlideChange: {
|
|
123
|
+
control: 'text',
|
|
124
|
+
description: 'JS code or function to execute on slideChange event',
|
|
125
|
+
isEmit: true,
|
|
126
|
+
name: 'slideChange',
|
|
110
127
|
table: {
|
|
111
128
|
category: 'Events',
|
|
112
129
|
defaultValue: { summary: 'None' },
|
|
113
130
|
type: { summary: '(currentSlide: number) => void' },
|
|
114
131
|
},
|
|
115
132
|
},
|
|
133
|
+
handleSlidesAddedToView: {
|
|
134
|
+
control: 'text',
|
|
135
|
+
description: 'JS code or function to execute on slidesAddedToView event',
|
|
136
|
+
isEmit: true,
|
|
137
|
+
name: 'slidesAddedToView',
|
|
138
|
+
table: {
|
|
139
|
+
category: 'Events',
|
|
140
|
+
defaultValue: { summary: 'None' },
|
|
141
|
+
type: { summary: '(slides: number[]) => void' },
|
|
142
|
+
},
|
|
143
|
+
},
|
|
116
144
|
isFloating: {
|
|
117
145
|
...argTypeBooleanUnrequired,
|
|
118
146
|
description: 'Determines whether to display on-page or floating carousel',
|
|
@@ -150,6 +178,8 @@ export default {
|
|
|
150
178
|
type: { summary: 'HTML' },
|
|
151
179
|
},
|
|
152
180
|
},
|
|
181
|
+
slideChange: disabledArgType,
|
|
182
|
+
slidesAddedToView: disabledArgType,
|
|
153
183
|
subtitle: {
|
|
154
184
|
control: 'text',
|
|
155
185
|
description: 'Determines the text displayed to the right of the title',
|
|
@@ -169,8 +199,9 @@ export default {
|
|
|
169
199
|
},
|
|
170
200
|
args: {
|
|
171
201
|
bleed: '',
|
|
172
|
-
default: 'Card',
|
|
173
|
-
|
|
202
|
+
default: 'Card #',
|
|
203
|
+
handleSlideChange: 'doSomething',
|
|
204
|
+
handleSlidesAddedToView: 'doSomething',
|
|
174
205
|
isFloating: undefined,
|
|
175
206
|
isHideawayButtons: undefined,
|
|
176
207
|
isTouchscreen: undefined,
|