0
# Click and Interaction System
1
2
Types for managing slide interactions, animations, and click-based progression through presentation content.
3
4
## Capabilities
5
6
### Click Value Types
7
8
Types for defining click values and ranges for slide interactions.
9
10
```typescript { .api }
11
/**
12
* Raw single value types for 'at' attribute
13
*/
14
type RawSingleAtValue = null | undefined | boolean | string | number;
15
16
/**
17
* Raw range value types for 'at' attribute
18
*/
19
type RawRangeAtValue = null | undefined | false | [string | number, string | number];
20
21
/**
22
* Combined raw 'at' value type
23
*/
24
type RawAtValue = RawSingleAtValue | RawRangeAtValue;
25
26
/**
27
* Normalized single click value types
28
*/
29
type NormalizedSingleClickValue =
30
| number // since absolute click
31
| string // since relative click
32
| null; // disabled
33
34
/**
35
* Normalized range click value types
36
*/
37
type NormalizedRangeClickValue =
38
| [number, number] // [absolute start, absolute end]
39
| [number, string] // [absolute start, absolute end based on start]
40
| [string, number] // [relative start, absolute end]
41
| [string, string] // [relative start, relative end]
42
| [string | number, string | number] // make TypeScript happy
43
| null; // disabled
44
45
/**
46
* Combined normalized value type
47
*/
48
type NormalizedAtValue =
49
| NormalizedSingleClickValue // since
50
| NormalizedRangeClickValue; // range
51
52
/**
53
* Click element type
54
*/
55
type ClicksElement = Element | string;
56
```
57
58
### Clicks Information Interface
59
60
Information structure for click states and calculations.
61
62
```typescript { .api }
63
/**
64
* Click information interface providing state and calculations
65
*/
66
interface ClicksInfo {
67
/** The absolute start click num */
68
start: number;
69
/** The absolute end click num */
70
end: number;
71
/** The required total click num */
72
max: number;
73
/** The delta for relative clicks */
74
delta: number;
75
/** currentClicks - start */
76
currentOffset: ComputedRef<number>;
77
/** currentOffset === 0 */
78
isCurrent: ComputedRef<boolean>;
79
/** Computed ref of whether the click is active */
80
isActive: ComputedRef<boolean>;
81
}
82
```
83
84
### Clicks Context Interface
85
86
Main interface for managing clicks within a slide.
87
88
```typescript { .api }
89
/**
90
* Clicks context interface for managing slide interactions
91
*/
92
interface ClicksContext {
93
/** Current click position */
94
current: number;
95
/** Starting click position for the slide */
96
readonly clicksStart: number;
97
/** Map of elements to their relative sizes */
98
readonly relativeSizeMap: Map<ClicksElement, number>;
99
/** Map of elements to their maximum click values */
100
readonly maxMap: Map<ClicksElement, number>;
101
/** Calculate click info for since-style values */
102
calculateSince: (at: RawSingleAtValue, size?: number) => ClicksInfo | null;
103
/** Calculate click info for range-style values */
104
calculateRange: (at: RawRangeAtValue) => ClicksInfo | null;
105
/** Calculate click info for any at value */
106
calculate: (at: RawAtValue) => ClicksInfo | null;
107
/** Register an element with click information */
108
register: (el: ClicksElement, info: Pick<ClicksInfo, 'delta' | 'max'> | null) => void;
109
/** Unregister an element */
110
unregister: (el: ClicksElement) => void;
111
/** Whether the context is mounted */
112
readonly isMounted: boolean;
113
/** Setup the clicks context */
114
setup: () => void;
115
/** Current offset from start */
116
readonly currentOffset: number;
117
/** Total clicks for the slide */
118
readonly total: number;
119
}
120
```
121
122
## Usage Examples
123
124
**Basic Click Usage:**
125
126
```typescript
127
import type { ClicksContext, ClicksInfo } from "@slidev/types";
128
129
// Using clicks context in a component
130
function useSlideClicks(clicksContext: ClicksContext) {
131
// Register an element for clicks
132
const element = document.querySelector('.animated-element');
133
134
clicksContext.register(element, {
135
delta: 1, // This element needs 1 click
136
max: 3 // Maximum 3 clicks total for this element
137
});
138
139
// Calculate click info for specific values
140
const sinceInfo = clicksContext.calculateSince(2); // Show since click 2
141
const rangeInfo = clicksContext.calculateRange([1, 3]); // Show from click 1 to 3
142
143
return {
144
isVisible: sinceInfo?.isActive,
145
currentClick: clicksContext.current
146
};
147
}
148
```
149
150
**Click Info Calculations:**
151
152
```typescript
153
// Example of working with click information
154
function handleClickInfo(clicksInfo: ClicksInfo | null) {
155
if (!clicksInfo) return;
156
157
console.log(`Click range: ${clicksInfo.start} to ${clicksInfo.end}`);
158
console.log(`Total clicks needed: ${clicksInfo.max}`);
159
console.log(`Current offset: ${clicksInfo.currentOffset.value}`);
160
161
// React to click state changes
162
watch(clicksInfo.isActive, (active) => {
163
if (active) {
164
console.log('Element is now active');
165
} else {
166
console.log('Element is now inactive');
167
}
168
});
169
170
watch(clicksInfo.isCurrent, (current) => {
171
if (current) {
172
console.log('Element is at current click position');
173
}
174
});
175
}
176
```
177
178
**Dynamic Click Registration:**
179
180
```typescript
181
function dynamicClickRegistration(clicksContext: ClicksContext) {
182
const elements = document.querySelectorAll('.click-element');
183
184
elements.forEach((element, index) => {
185
// Register each element with different click requirements
186
clicksContext.register(element, {
187
delta: 1,
188
max: index + 1
189
});
190
});
191
192
// Later, unregister elements if needed
193
const cleanup = () => {
194
elements.forEach(element => {
195
clicksContext.unregister(element);
196
});
197
};
198
199
return cleanup;
200
}
201
```
202
203
**Advanced Click Calculations:**
204
205
```typescript
206
function advancedClickUsage(clicksContext: ClicksContext) {
207
// Calculate different types of click ranges
208
const examples = [
209
clicksContext.calculate(1), // Show since click 1
210
clicksContext.calculate([2, 4]), // Show from click 2 to 4
211
clicksContext.calculate(["+1", 3]), // Relative start, absolute end
212
clicksContext.calculate(["1", "+2"]), // Relative start and end
213
clicksContext.calculate(null), // Disabled
214
clicksContext.calculate(false) // Disabled range
215
];
216
217
examples.forEach((info, index) => {
218
if (info) {
219
console.log(`Example ${index}:`, {
220
start: info.start,
221
end: info.end,
222
max: info.max,
223
delta: info.delta
224
});
225
} else {
226
console.log(`Example ${index}: Disabled`);
227
}
228
});
229
}
230
```
231
232
**Reactive Click State:**
233
234
```typescript
235
import { computed, watch } from 'vue';
236
237
function reactiveClickState(clicksContext: ClicksContext) {
238
// Create reactive computations based on click state
239
const progress = computed(() => {
240
if (clicksContext.total === 0) return 0;
241
return (clicksContext.current - clicksContext.clicksStart) / clicksContext.total;
242
});
243
244
const isComplete = computed(() => {
245
return clicksContext.current >= clicksContext.clicksStart + clicksContext.total;
246
});
247
248
const remainingClicks = computed(() => {
249
return Math.max(0, clicksContext.clicksStart + clicksContext.total - clicksContext.current);
250
});
251
252
// Watch for click changes
253
watch(() => clicksContext.current, (newClick, oldClick) => {
254
console.log(`Click changed from ${oldClick} to ${newClick}`);
255
console.log(`Progress: ${Math.round(progress.value * 100)}%`);
256
257
if (isComplete.value) {
258
console.log('All clicks completed for this slide');
259
} else {
260
console.log(`${remainingClicks.value} clicks remaining`);
261
}
262
});
263
264
return {
265
progress,
266
isComplete,
267
remainingClicks
268
};
269
}
270
```