Comprehensive TypeScript type definitions and interfaces for the Slidev presentation framework ecosystem
—
Types for managing slide interactions, animations, and click-based progression through presentation content.
Types for defining click values and ranges for slide interactions.
/**
* Raw single value types for 'at' attribute
*/
type RawSingleAtValue = null | undefined | boolean | string | number;
/**
* Raw range value types for 'at' attribute
*/
type RawRangeAtValue = null | undefined | false | [string | number, string | number];
/**
* Combined raw 'at' value type
*/
type RawAtValue = RawSingleAtValue | RawRangeAtValue;
/**
* Normalized single click value types
*/
type NormalizedSingleClickValue =
| number // since absolute click
| string // since relative click
| null; // disabled
/**
* Normalized range click value types
*/
type NormalizedRangeClickValue =
| [number, number] // [absolute start, absolute end]
| [number, string] // [absolute start, absolute end based on start]
| [string, number] // [relative start, absolute end]
| [string, string] // [relative start, relative end]
| [string | number, string | number] // make TypeScript happy
| null; // disabled
/**
* Combined normalized value type
*/
type NormalizedAtValue =
| NormalizedSingleClickValue // since
| NormalizedRangeClickValue; // range
/**
* Click element type
*/
type ClicksElement = Element | string;Information structure for click states and calculations.
/**
* Click information interface providing state and calculations
*/
interface ClicksInfo {
/** The absolute start click num */
start: number;
/** The absolute end click num */
end: number;
/** The required total click num */
max: number;
/** The delta for relative clicks */
delta: number;
/** currentClicks - start */
currentOffset: ComputedRef<number>;
/** currentOffset === 0 */
isCurrent: ComputedRef<boolean>;
/** Computed ref of whether the click is active */
isActive: ComputedRef<boolean>;
}Main interface for managing clicks within a slide.
/**
* Clicks context interface for managing slide interactions
*/
interface ClicksContext {
/** Current click position */
current: number;
/** Starting click position for the slide */
readonly clicksStart: number;
/** Map of elements to their relative sizes */
readonly relativeSizeMap: Map<ClicksElement, number>;
/** Map of elements to their maximum click values */
readonly maxMap: Map<ClicksElement, number>;
/** Calculate click info for since-style values */
calculateSince: (at: RawSingleAtValue, size?: number) => ClicksInfo | null;
/** Calculate click info for range-style values */
calculateRange: (at: RawRangeAtValue) => ClicksInfo | null;
/** Calculate click info for any at value */
calculate: (at: RawAtValue) => ClicksInfo | null;
/** Register an element with click information */
register: (el: ClicksElement, info: Pick<ClicksInfo, 'delta' | 'max'> | null) => void;
/** Unregister an element */
unregister: (el: ClicksElement) => void;
/** Whether the context is mounted */
readonly isMounted: boolean;
/** Setup the clicks context */
setup: () => void;
/** Current offset from start */
readonly currentOffset: number;
/** Total clicks for the slide */
readonly total: number;
}Basic Click Usage:
import type { ClicksContext, ClicksInfo } from "@slidev/types";
// Using clicks context in a component
function useSlideClicks(clicksContext: ClicksContext) {
// Register an element for clicks
const element = document.querySelector('.animated-element');
clicksContext.register(element, {
delta: 1, // This element needs 1 click
max: 3 // Maximum 3 clicks total for this element
});
// Calculate click info for specific values
const sinceInfo = clicksContext.calculateSince(2); // Show since click 2
const rangeInfo = clicksContext.calculateRange([1, 3]); // Show from click 1 to 3
return {
isVisible: sinceInfo?.isActive,
currentClick: clicksContext.current
};
}Click Info Calculations:
// Example of working with click information
function handleClickInfo(clicksInfo: ClicksInfo | null) {
if (!clicksInfo) return;
console.log(`Click range: ${clicksInfo.start} to ${clicksInfo.end}`);
console.log(`Total clicks needed: ${clicksInfo.max}`);
console.log(`Current offset: ${clicksInfo.currentOffset.value}`);
// React to click state changes
watch(clicksInfo.isActive, (active) => {
if (active) {
console.log('Element is now active');
} else {
console.log('Element is now inactive');
}
});
watch(clicksInfo.isCurrent, (current) => {
if (current) {
console.log('Element is at current click position');
}
});
}Dynamic Click Registration:
function dynamicClickRegistration(clicksContext: ClicksContext) {
const elements = document.querySelectorAll('.click-element');
elements.forEach((element, index) => {
// Register each element with different click requirements
clicksContext.register(element, {
delta: 1,
max: index + 1
});
});
// Later, unregister elements if needed
const cleanup = () => {
elements.forEach(element => {
clicksContext.unregister(element);
});
};
return cleanup;
}Advanced Click Calculations:
function advancedClickUsage(clicksContext: ClicksContext) {
// Calculate different types of click ranges
const examples = [
clicksContext.calculate(1), // Show since click 1
clicksContext.calculate([2, 4]), // Show from click 2 to 4
clicksContext.calculate(["+1", 3]), // Relative start, absolute end
clicksContext.calculate(["1", "+2"]), // Relative start and end
clicksContext.calculate(null), // Disabled
clicksContext.calculate(false) // Disabled range
];
examples.forEach((info, index) => {
if (info) {
console.log(`Example ${index}:`, {
start: info.start,
end: info.end,
max: info.max,
delta: info.delta
});
} else {
console.log(`Example ${index}: Disabled`);
}
});
}Reactive Click State:
import { computed, watch } from 'vue';
function reactiveClickState(clicksContext: ClicksContext) {
// Create reactive computations based on click state
const progress = computed(() => {
if (clicksContext.total === 0) return 0;
return (clicksContext.current - clicksContext.clicksStart) / clicksContext.total;
});
const isComplete = computed(() => {
return clicksContext.current >= clicksContext.clicksStart + clicksContext.total;
});
const remainingClicks = computed(() => {
return Math.max(0, clicksContext.clicksStart + clicksContext.total - clicksContext.current);
});
// Watch for click changes
watch(() => clicksContext.current, (newClick, oldClick) => {
console.log(`Click changed from ${oldClick} to ${newClick}`);
console.log(`Progress: ${Math.round(progress.value * 100)}%`);
if (isComplete.value) {
console.log('All clicks completed for this slide');
} else {
console.log(`${remainingClicks.value} clicks remaining`);
}
});
return {
progress,
isComplete,
remainingClicks
};
}Install with Tessl CLI
npx tessl i tessl/npm-slidev--types