vfunc.js

Extensions, plugins, IE

Extension rules

Extend vfunc through its public API, never by editing its source. Then your extensions keep working when vfunc is updated.

LevelHow
Settingsvf.config({ strict: true }), messages, tokens
Stylesoverride --vf-* after the token file
Compositionfunctions that create components, childs, your own components around the official components (below)
Third-partyonMount + data-vf-keep + onDestroy; levels L0, L1, L2 in Third-party integration
Pluginsvf.use(plugin) → vf.ext.<name>

Your own components

Components you build from the official ones read like the rest of your code when they keep the layer 2 rules. Sample: layer2/examples/custom-component.

// shop-ui.js — your module, not the vf root
export function vsPriceTag({ amount, was, currency = 'USD' }) {
  const off = was > amount ? Math.round((1 - amount / was) * 100) : 0;
  return vf.html`<span class="price-tag">${vf.fmt.currency(amount, currency)}${
    off ? vf.vsBadge({ label: '-' + off + '%', variant: 'danger' }) : ''}</span>`;
}

Plugins — vf.use

vf.use({
  name: 'company', version: '1.0.0', requires: '^1.0.0',
  install(vf, options) { return { toast: (message) => { /* … */ } }; }
}, { duration: 3000 });
vf.ext.company.toast('saved');

Official plugin — vf.ext.update

Brings new releases to places without a reload button (web views, kiosks, installed PWAs, in-app browsers).

import vfUpdate from 'vfunc/plugins/update';     // <script>: dist/plugins/update.min.js → global vfUpdate
vf.use(vfUpdate, { url: './version.json', current: APP_VERSION, policy: 'next-navigation' });
PolicyBehaviour
next-navigation (default)replace on the next screen change; nothing in progress is lost
promptonAvailable(info, apply) lets the app show its own notice; replace when the user agrees
immediatereplace at once (security fixes)

See Deployment and cache for the rest.

Official plugin — vf.ext.shortcut

Keyboard shortcuts for the whole page. Keys typed into inputs are ignored unless you allow them, and so are keys that belong to an IME composition, so typing Korean, Japanese or Chinese never fires a shortcut.

import vfShortcut from 'vfunc/plugins/shortcut';   // <script>: dist/plugins/shortcut.min.js → global vfShortcut
const keys = vf.use(vfShortcut);
const off = keys.add('mod+k', () => search.focus(), { label: 'Search' });   // mod = ⌘ on Apple devices, Ctrl elsewhere
keys.add('escape', closeDialog, { allowInInput: true });
keys.list();   // [{ combo: 'ctrl+k', label: 'Search' }, …] for your own help screen
off();         // remove one shortcut; keys.destroy() removes all

The newest shortcut of a combo wins, so a dialog can take escape while it is open and give it back with off(). The plugin draws no UI.

Official plugin — vf.ext.list

A keyed list. A component's render redraws its whole inside, which is slow for long lists where a few rows change often (update, swap, remove). This plugin keeps one element per item, draws only the rows that changed and only moves rows when the order changes. The numbers are in the FAQ.

import vfList from 'vfunc/plugins/list';   // <script>: dist/plugins/list.min.js → global vfList
const list = vf.use(vfList);
const rows = list.create('#tbody', {       // the direct parent of the rows (tbody, ul, div …); nothing else in it
  key: (item) => item.id,
  render: (item) => vf.html`<tr><td>${item.name}</td><td><button type="button" data-action="remove">Remove</button></td></tr>`
});
rows.set(items);                            // same key and same object: kept; a new object: only that row is drawn
rows.set(rows.items().filter((x) => x.id !== id));   // remove: only that row goes

IE11 / Edge IE mode

One engine, several files. vfunc.legacy.min.js is transpiled to ES5 with a single Promise polyfill (about 10 KB gzip).