Skip to main content

Hooks

These hooks offer different way to interact with the Vev Editor.

useIntersection

Tracks the portion of an element that intersects with the visible area of the screen (the viewport).

const el_intersection: false | IntersectionObserverEntry = useIntersection(ref: React.RefObject, options?: IObserverOptions);

Interface

interface IntersectionObserverEntry {
/**
* Properties of the tracked reference element
*/
boundingClientRect: DOMRectReadOnly;
/**
* Visible amount of the tracked reference element
*/
intersectionRatio: number;
/**
* Properties of only the visible portion of the tracked reference element (a subset of `boundingClientRect`)
*/
intersectionRect: DOMRectReadOnly;
isIntersecting: boolean;
rootBounds: DOMRectReadOnly | null;
target: Element;
time: number;
}

interface IObserverOptions {
/**
* Number of times the hook updates as the tracked element enters the visible screen. Defaults to 1.
*/
steps?: number;
/**
* Decimal values indicating the intersection percentage at which the hook updates.
*/
threshold?: number[];
}

It is not possible to define both a step and a threshold. Define one at most.

Example

Display the percentage of a widget that intersects with the viewport. Update this value 10 times over the course of the widget’s entrance into the viewport.

import { useRef } from 'react';
import { useIntersection } from '@vev/react';

export default function() {
const widgetReference = useRef(null);
const intersection = useIntersection(widgetReference, { steps: 10 });

return (
<div className='fill' ref={widgetReference}>
<h1>{(intersection.intersectionRatio) * 100}</h1>
</div>
);
}

useDevice

Vev projects can be custom-designed for various presentation modes such as desktop, tablet, or mobile. useDevice() returns the currently active mode ('desktop' | 'tablet' | 'mobile').

Usage

const device: string = useDevice();

Example

import { useDevice } from '@vev/react';

export default function() {
const device: 'desktop' | 'tablet' | 'mobile' = useDevice();

return <h1>Device: {device}</h1>;
}

useEditorState

For better widget usability, consider changing the state of your widget depending on which mode the user is in. For example, play a video when in preview mode, but stop it when in editor mode. Or, if the user is changing the background color of a hidden dropdown in your widget, reveal that dropdown when its background colour property is being changed.

useEditorState() returns the following:

  • disabled: boolean - true when in editor mode, false when in preview mode.
  • rule: string - tells which CSS rule the user is editing. Sets to ‘host’ when no rule is being edited .
  • selected: boolean - true when the widget is selected in editor mode.

Example

import { useEditorState } from '@vev/react';
import { useState, useEffect, useRef } from 'react';

export default function({ url }: Props) {
const { disabled } = useEditorState();
const videoReference = useRef<HTMLVideoElement>(null);

// this function runs on every value change of 'disabled'
useEffect(() => {
// If disabled, pause video
if (disabled) {
videoReference.current.pause();
} else {
videoReference.current.play();
}
}, [disabled]);

return (
<video
className="video fill"
src={url}
ref={videoReference}
/>
)
}

useEditorState example hook

useFrame

Triggers a callback function every animation frame. Optionally pass a time parameter in your callback function to track callback start time. useFrame() calls are paused when running in background tabs.

In the past, setTimeout and setInterval were commonly used to iterate a block of code for animations. Using these causes the browser to paint frames quicker than most screen refresh rates of 60 FPS. This results in skipped frames and unnecessary computation, making snappy animations. useFrame() executes the passed call back function more efficiently by eliminating unnecessary repaints and bundling multiple animations into a single repaint cycle.

Usage

useFrame(callback: (time?: number), deps?: readonly any[]): () => void;

Example

Sliding an element to the right by 500px.

import { useFrame } from '@vev/react';

export default function() {
var startTime = null;

useFrame((time) => {
if (!startTime) startTime = time;
var progress = time - startTime;
const boxElement = document.getElementById('SomeElementYouWantToAnimate');
if (progress < 5000) {
boxElement.style.left = (progress / 10) + 'px';
}
});

return (
<div className="fill">
<div id="SomeElementYouWantToAnimate" style={{ left: 0 }} />
</div>
);
}

Showing a moving element using useFrame

useIcon

Watches and returns information about an icon given its icon key. The icon key can be created/found in the widget’s manifest file.

For SVG icons the following information is returned:

  • width - the width of the SVG viewbox
  • height - the height of the SVG viewbox
  • path - the SVG path to render the icon

Usage

const iconInfo: [ number, number, string | string[] ] = useIcon( iconKey: string );

Example

Render an icon

import { useIcon } from '@vev/react';

export default function() {
const [width, height, ...path] = useIcon('smiley-icon');

return (
<svg viewBox={`0 0 ${width} ${height}`}>
{path.map((d, index) => <path key={index} d={d} />)}
</svg>
);
}

useImage

Watches and returns information about an image.

Usage

const imageData: ImageModel = useImage(imageKey: string);

Interfaces

interface ImageModel {
meta?: {
description?: string;
alt?: string;
photographer: string;
width?: number;
height?: number;
};
src: string;
srcset: [string, number][];
key: string;
}

useInterval

Run code at specified time intervals. Code will continue to run after every interval lapse until interval is cleared or window is closed.

Example

import { useInterval } from '@vev/react';
import { useState } from 'react';

export default function() {
const [count, setCount] = useState<number>(0);
// Increase counter every second
useInterval(() => setCount(count + 1), 1000);

return <h1>{count}</h1>;
}

Example of a widget using the useInterval hook to count upwards

useMenu

In the editor, the designer can build custom menus. To use these menus in a widget, use the useMenu hook.

Usage

const menuChildren: IMenu = useMenu(menuKey?: string);
//if no menu key is defined, the primary menu will be used.

Interfaces

interface IMenu {
title: string;
children: IMenuItem[];
}

interface IMenuItem {
key: string;
title: string;
link: IPageLink | IPhoneLink | IEmailLink | IExternalLink | IWidgetLink;
children?: IMenuItem[];
}

Example

Create a simple menu.

import { Link, registerVevComponent, useMenu } from "@vev/react";

type Props = {
menuselect: string;
};

const MenuExample = ({ menuselect }: Props) => {
const menu = useMenu(menuselect);

if(!menu) {
return <p>No menu items</p>
}

return (
<div>
<h1>{menu.title}</h1>
{
menu.children.map((child) => {
return <div>
<Link to={child.link}>{child.title}</Link>
</div>
})
}
</div>
);
};

registerVevComponent(MenuExample, {
name: "MenuExample",
props: [
{
name: "menuselect",
type: "select",
options: {
display: "autocomplete",
items: (context: any) => {
if(context.menus) {
return Object.keys(context.menus).map((menuKey) => {
return { label: context.menus[menuKey].title, value: context.menus[menuKey].key }
});
} else {
return []
}
},
},
},
],
});

export default MenuExample;

Example of a simple menu element using the useMenu hook

useModel

Watches and returns the content model using the widget key. Content model refers to the intrinsic properties of the widget like element ID, classname, icon’s, and form-field values.

Usage

const widgetModel: IContent = useModel(key? string);
// empty key parameter returns the model of the widget you are currently coding.

IContent type interface

interface IContent {
/**
* Widget's element ID
*/
key: string;
/**
* Widget's classname
*/
type?: string;
style?: {}; //doesn't even work
/**
* Contents of the widget's formfield
*/
content?: {};
/**
* Classname
*/
cl?: string;
pin?: boolean; //doesn't even work
html?: boolean; //DONT DOCUMENT
/**
* Wcons set on the widget
*/
icons?: { [key: string]: IShape };
actions?: string[];
children?: string[];
}

Example

Observe another widget’s form-field value.

import { useModel } from '@vev/react';

export default function({ trackedWidget }: Props) {
const usemodel = useModel(trackedWidget.key);

return <h1>{usemodel.content.text}</h1>;
}

Example of a tracked model reflecting changes from one widget inside another

useRoute

Watches and returns information about the current page route.

Usage

const { pageKey, path } = useRoute();

useScrollTop

Track how much the page has scrolled down relative to the top of the view port.

Usage

const scrollTop : number = useScrollTop(asPercentage?:boolean);

Example

import { useScrollTop } from '@vev/react';

export default function() {
return (
<div>
<h1>Scroll Top (pixels): {useScrollTop()}</h1>
<h1>Scroll Top (percentage): {useScrollTop(true)}</h1>
</div>
);
}

useSize

Detect when an element resizes. Returns width and height of the element passed into the hook parameter.

useSize Usage

const useSizeHook: { width: number, height: number } = useSize(React.RefObject);

useSize Example

import { useSize } from '@vev/react';
import { useRef } from 'react';

export default function() {
const elementReference = useRef<Element>(null);
const { width, height } = useSize(elementReference);

return (
<div ref={elementReference}>
{width} x {height}
</div>
);
}

Showing how the useSize hook can reflect a containers width and height

usePages

Returns all the projects pages and the root directory of published project.

Usage

const [pages, rootDir] = usePages();

Example

import { usePages } from '@vev/react';

export default function() {
const [pages, rootDir] = usePages();

return (
<div>
<p>Root dir: {rootDir}</p>

<p>Page titles: {pages.map(page => page.title).join(',')}</p>
< /div>
);
};

useViewport

Watches and returns the viewport width, height, and scrollHeight. The viewport is the user's visible area of a webpage and can vary based on device sizes.

Usage

const { width, height, scrollHeight } = useViewport();

Example

import { useViewport } from '@vev/react';

export default function({ text }: Props) {
const [count, setCount] = useState<number>(0);
const viewport = useViewport();
const { disabled } = useEditorState();

function test() {
console.log('viewport:', viewport);
}

return (
<div className='fill wrapper' onClick={test}>
<h1>Width: {viewport.width}</h1>
<h1>Height: {viewport.width}</h1>
<h1>ScrollHeight: {viewport.scrollHeight}</h1>
</div>
);
}

Example of useViewport hook in use

useVisible

Detect if an element is visible in the viewport.

Usage

const isVisible: boolean = useVisible(ref: React.RefObject, object?: IVisibleOptions);

IVisibleOptions Interface

interface IVisibleOptions {
offSetTop?: number | string;
offSetBottom?: number | string;
}

Example

import { useVisible } from '@vev/react';

export function VisibleTest() {
const elementReference = useRef<HTMLDivElement>(null);
const visible = useVisible(elementReference);

return <div ref={elementReference} className='fill' />
}

useVevEvent

Use Vev events dispatched from Interactions. To use this hook, remember to also add interactions to registerVevComponent.

Usage

useVevEvent(string, () => {});

Example

import { useVevEvent } from '@vev/react';

const YouTube = () => {
const playerRef = React.useRef();

useVevEvent('PLAY_VIDEO', () => playerRef.current?.playVideo());

// ...,

return <iframe ref={ref} />;
};

registerVevComponent(Youtube, {
name: 'YouTube',

// ...,

interactions: [
{
type: 'PLAY_VIDEO',
description: 'Start playing video',
},
]
});

useDispatchVevEvent

Emits Vev events from your component which can be used to trigger interactions.

Usage

const dispatch = useDispatchVevEvent();
dispatch('EVENT')

Example

import { useDispatchVevEvent } from '@vev/react';

const YouTube = () => {
const playerRef = React.useRef();
const dispatch = useDispatchVevEvent();

React.useEffect(() => {
// ...

function onPlayerStateChange(event) {
switch (event.data) {
case YT.PlayerState.PLAYING:
dispatch('VIDEO_PLAYING');
}
}
}, []);

// ...

return (
<iframe ref={ref} />
);
};

registerVevComponent(Youtube, {
name: 'YouTube',

// ...

events: [
{
type: 'VIDEO_PLAYING',
description: 'Video starts playing',
},
],
});