window.EasySvgPanel.registerCollections()
Lets an add-on list its own icon collections inside the Icon library tab, next to the site's icons, and optionally move, rename, add or export them.
JavaScript · Easy SVG · Status: Stable
registerCollections() lets your add-on show its own icons in Easy SVG's Icon library tab. You hand over a provider: an object with an id and a load function that returns your collections. The library lists them in its sidebar, puts their icons into the same grid and search as the site's own icons, and offers editing actions only for what your provider implements.
Usage
window.EasySvgPanel.registerCollections( {
id: 'myaddon', // required
load: ( { signal } ) => Promise.resolve( collections ), // required
// All optional:
move, create, rename, trash,
relabel, remove, add, recolour, replaceDrawing,
importFile, addMedia,
history, restoreVersion,
exportIcons, exportMax,
hint,
} );
When to use it
- Your add-on has icons of its own (a theme folder, a bundled icon set, sets stored in a custom post type) and people should find them where they already look for icons.
- You want the library's search, grid and copy buttons for those icons without building a tab.
- Your add-on works on sites with the free plugin alone. Unlike
registerTab(), collections are not tied to an Easy SVG Pro licence.
When not to use it:
- To render icons on the front end. That is
easy_svg_icon()and theeasy_svg_icon_markupfilter in PHP. The panel only displays what you hand it. - To change the site's own library icons. Those belong to Easy SVG's REST routes.
- When the panel is older than contract level 3: the method does not exist there.
Parameters
A unique id for your provider. A second call with the same id replaces the provider. Prefix it with your add-on's slug.
Called as load( { signal } ) and must return an array of collections, or a Promise of one. signal is an AbortSignal; pass it to fetch(), because the library aborts a running load when it starts a new one or when the tab closes. Each collection is an object with:
id(string, required): unique across all providers. Do not useallorlibrary, which are the library's own views.label(string): the name in the sidebar. Falls back to the id.readonly(boolean): when true, no action is offered on this collection or its icons.icons(array, required): the icons, each withkey(unique tile key, falls back toref),slug,ref(the address a template uses, in the formcollection/slug),labelandcontent(the SVG markup, already sanitized and hardened by you).
Any other fields you add to a collection, such as a post id, come back to you unchanged in the collection argument of the optional methods.
move( { refs, to } ), called when tiles are dropped on one of your non read-only collections or sent there through the tile menu. refs is an array of up to 25 refs (larger selections arrive in batches), to the target collection id. Resolve to { message, undo }; undo is optional and returns a Promise of { message }.
Managing collections from the sidebar. create( { label } ) resolves to { id, message } and the library switches to the new id. rename( { id, label, collection } ) resolves to { message }. trash( { id, collection } ) resolves to { message, undo }. Only the first provider that has create gets the "new collection" control.
Working on the icons inside one collection. relabel( { ref, label, collection } ), remove( { refs, collection } ) (said with Undo, not asked twice), add( { icons, to, collection } ) where icons is a list of name plus markup (files dropped, picked or pasted while your collection is open go here), recolour( { refs, collection } ) and replaceDrawing( { ref, markup, collection } ). Each resolves to { message }, optionally with undo; add may also return rows as a per-file list of file, ok and message.
importFile( { file, collection } ) receives a ZIP or an SVG font (a file with glyph elements) dropped on an open collection. addMedia( { ids, collection } ) receives media library attachment ids. Both resolve like add.
Earlier drawings of an icon. history( { ref, collection } ) resolves to { versions, note }, each version with id, date, since, markup and current. restoreVersion( { ref, version, collection } ) resolves to { message }. Both must exist for the action to appear. For icons of the site's own library collection is null, and the first provider with both methods is asked.
exportIcons( { refs } ) exports the picked tiles from any collections and delivers the file itself; resolve to { message }. With a finite exportMax, Export is not offered when more tiles are picked. Only the first provider with exportIcons is used.
A note in the sidebar, for example when a feature your collections need is switched off: { text, action: { label, run } }, where run returns a Promise. The action is optional.
Return value
Nothing. A provider without id or without a load function is ignored silently.
Default
No providers are registered by the free plugin. Without readonly, a collection counts as writable, but the library still offers only the actions whose methods exist.
Examples
A read-only collection from inline data
The minimum: one collection, two icons, nothing editable. The markup here is static and already clean.
( function () {
const panel = window.EasySvgPanel;
if ( ! panel || 'function' !== typeof panel.registerCollections ) {
return;
}
const icons = [
{
key: 'myaddon-brand/logo',
slug: 'logo',
ref: 'myaddon-brand/logo',
label: 'Logo',
content: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/></svg>',
},
{
key: 'myaddon-brand/mark',
slug: 'mark',
ref: 'myaddon-brand/mark',
label: 'Mark',
content: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 20 12 4l8 16z"/></svg>',
},
];
panel.registerCollections( {
id: 'myaddon',
load: () => [ { id: 'myaddon-brand', label: 'Brand', readonly: true, icons } ],
} );
} )();
Load collections from your REST route
A real provider asks the server, passes the abort signal on and caches the answer, because load() runs every time the library opens and every time anything is registered.
( function () {
const panel = window.EasySvgPanel;
if ( ! panel || 'function' !== typeof panel.registerCollections ) {
return;
}
const config = window.EasySvgPanelData || {};
let cache = null;
function request( path, options = {} ) {
return fetch( config.restRoot + 'myaddon/v1' + path, {
credentials: 'same-origin',
...options,
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': config.nonce,
},
} ).then( ( response ) =>
response.json().then( ( data ) =>
response.ok ? data : Promise.reject( new Error( data.message || response.statusText ) )
)
);
}
panel.registerCollections( {
id: 'myaddon',
load: ( { signal } ) => {
if ( cache ) {
return Promise.resolve( cache );
}
return request( '/collections', { signal } ).then( ( list ) => {
cache = list;
return list;
} );
},
relabel: ( { ref, label } ) => {
cache = null;
return request( '/relabel', {
method: 'POST',
body: JSON.stringify( { ref, label } ),
} );
},
} );
} )();
window.EasySvgPanelData is the panel's own localized data; reading restRoot and nonce from it saves you localizing them again. Your routes must answer { message } for actions and must check capabilities themselves.
Accept moves, with undo
move() receives refs of tiles from anywhere in the library. Return an undo when you can reverse the move.
window.EasySvgPanel.registerCollections( {
id: 'myaddon',
load: myaddonLoad,
move: async ( { refs, to } ) => {
const done = await myaddonPost( '/move', { refs, to } );
return {
message: done.message,
undo: () => myaddonPost( '/move-back', { token: done.token } ),
};
},
} );
Defensive: only on contract level 3
( function () {
const panel = window.EasySvgPanel;
const level = panel ? Number( panel.api || 1 ) : 0;
if ( 3 > level || 'function' !== typeof panel.registerCollections ) {
return; // Older panel: no collections in the library.
}
panel.registerCollections( { id: 'myaddon', load: () => [] } );
} )();
How it works
The Icon library tab (src/panel/tabs/Library.jsx) does all the work:
- When it loads. On mount, and again on every registry notification (any
registerTab()orregisterCollections()call), the tab aborts the previous load and callsload( { signal } )on every provider, wrapped inPromise.resolve(). - What it keeps. A result that is not an array counts as no collections, and a rejected Promise is swallowed the same way. Collections without an
idor without aniconsarray are dropped. Each kept collection remembers its provider. - The sidebar. It appears once there is a collection, a provider with
createor ahint. It lists All icons, Icon library, then every collection with its icon count. Ahintfrom any provider is shown there. - The grid and the search. In All icons the library's icons and every collection's icons share one grid; selecting a collection shows only its icons. The search filters your icons in the browser by
label,slugandref. Tiles are keyed bykey, elseref. - Drawing. Your
contentis drawn as markup after a second pass through DOMPurify with its SVG profile, as defence in depth. - Actions. An action appears only when your provider has the method and the collection is not
readonly. The library waits for your Promise, shows itsmessage(or "Done."), offers Undo when you returnedundo, showserror.messagewhen you rejected, and reloads all collections afterwards.
The library tab is only drawn for users who hold its capability (manage_options by default), and its content appears only while the icon library feature is switched on. When it is off, the tab shows a notice instead. Your load() is still called when the tab opens, but nothing of its answer is shown.
Pitfalls
load()runs often. Every time the library opens and every time any add-on registers anything. Cache, and passsignalto your requests.- Errors in
load()vanish. A rejection simply shows no collections. Log the error yourself while developing. - Collection ids must be unique across all providers and must not be
allorlibrary. - Keep refs in the form
collection/slug. When tiles are moved, refs that already start with the target id and a slash are skipped as "already there". - Sanitize on the server. The panel's DOMPurify pass is a safety net, not your sanitizer. Clean markup with
easy_svg_sanitizer()before you send it. readonlyturns off everything for that collection, including moves into it.- One exporter, one creator. If several providers implement
exportIconsorcreate, only the first one registered is used. - Off means off. With the icon library switched off in Settings, your collections are not shown at all, and your
hintis not shown either, althoughload()still runs.
Markup you return as content, markup or in history versions is inserted into an admin page. Hand over only SVG that went through the site's sanitizer, and let every endpoint behind move, add, remove and the other methods check current_user_can() and the wp_rest nonce. The library offers an action based on your JavaScript; it does not check permissions for you.