mirror of
https://github.com/iconify/iconify.git
synced 2025-01-26 08:38:30 +00:00
Support storage for simple names without provider and prefix
This commit is contained in:
parent
257d3d60f0
commit
a846dbcc8c
@ -22,7 +22,8 @@ const match = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|||||||
*/
|
*/
|
||||||
export const stringToIcon = (
|
export const stringToIcon = (
|
||||||
value: string,
|
value: string,
|
||||||
validate?: boolean
|
validate?: boolean,
|
||||||
|
allowSimpleName?: boolean
|
||||||
): IconifyIconName | null => {
|
): IconifyIconName | null => {
|
||||||
let provider = '';
|
let provider = '';
|
||||||
const colonSeparated = value.split(':');
|
const colonSeparated = value.split(':');
|
||||||
@ -55,7 +56,8 @@ export const stringToIcon = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to split by dash: "prefix-name"
|
// Attempt to split by dash: "prefix-name"
|
||||||
const dashSeparated = colonSeparated[0].split('-');
|
const name = colonSeparated[0];
|
||||||
|
const dashSeparated = name.split('-');
|
||||||
if (dashSeparated.length > 1) {
|
if (dashSeparated.length > 1) {
|
||||||
const result: IconifyIconName = {
|
const result: IconifyIconName = {
|
||||||
provider: provider,
|
provider: provider,
|
||||||
@ -65,6 +67,18 @@ export const stringToIcon = (
|
|||||||
return validate && !validateIcon(result) ? null : result;
|
return validate && !validateIcon(result) ? null : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If allowEmpty is set, allow empty provider and prefix, allowing names like "home"
|
||||||
|
if (allowSimpleName && provider === '') {
|
||||||
|
const result: IconifyIconName = {
|
||||||
|
provider: provider,
|
||||||
|
prefix: '',
|
||||||
|
name,
|
||||||
|
};
|
||||||
|
return validate && !validateIcon(result, allowSimpleName)
|
||||||
|
? null
|
||||||
|
: result;
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -73,14 +87,17 @@ export const stringToIcon = (
|
|||||||
*
|
*
|
||||||
* This function is not part of stringToIcon because validation is not needed for most code.
|
* This function is not part of stringToIcon because validation is not needed for most code.
|
||||||
*/
|
*/
|
||||||
export const validateIcon = (icon: IconifyIconName | null): boolean => {
|
export const validateIcon = (
|
||||||
|
icon: IconifyIconName | null,
|
||||||
|
allowSimpleName?: boolean
|
||||||
|
): boolean => {
|
||||||
if (!icon) {
|
if (!icon) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !!(
|
return !!(
|
||||||
(icon.provider === '' || icon.provider.match(match)) &&
|
(icon.provider === '' || icon.provider.match(match)) &&
|
||||||
icon.prefix.match(match) &&
|
((allowSimpleName && icon.prefix === '') || icon.prefix.match(match)) &&
|
||||||
icon.name.match(match)
|
icon.name.match(match)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
import type { IconifyJSON } from '@iconify/types';
|
import type { IconifyJSON } from '@iconify/types';
|
||||||
import type { FullIconifyIcon, IconifyIcon } from '../icon';
|
import type { FullIconifyIcon, IconifyIcon } from '../icon';
|
||||||
|
import { parseIconSet } from '../icon/icon-set';
|
||||||
import type { IconifyIconName } from '../icon/name';
|
import type { IconifyIconName } from '../icon/name';
|
||||||
import { stringToIcon, validateIcon } from '../icon/name';
|
import { stringToIcon, validateIcon } from '../icon/name';
|
||||||
import { merge } from '../misc/merge';
|
import { merge } from '../misc/merge';
|
||||||
import { getStorage, getIcon, listIcons, addIcon, addIconSet } from './storage';
|
import {
|
||||||
|
getStorage,
|
||||||
|
getIcon,
|
||||||
|
listIcons,
|
||||||
|
addIcon as storeIcon,
|
||||||
|
addIconSet,
|
||||||
|
} from './storage';
|
||||||
|
// import { parseIconSet } from '../icon';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for exported storage functions
|
* Interface for exported storage functions
|
||||||
@ -36,31 +44,75 @@ export interface IconifyStorageFunctions {
|
|||||||
addCollection: (data: IconifyJSON, provider?: string) => boolean;
|
addCollection: (data: IconifyJSON, provider?: string) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow storing icons without provider or prefix, making it possible to store icons like "home"
|
||||||
|
*/
|
||||||
|
let simpleNames = false;
|
||||||
|
|
||||||
|
export function allowSimpleNames(allow?: boolean): boolean {
|
||||||
|
if (typeof allow === 'boolean') {
|
||||||
|
simpleNames = allow;
|
||||||
|
}
|
||||||
|
return simpleNames;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get icon data
|
* Get icon data
|
||||||
*/
|
*/
|
||||||
export function getIconData(
|
export function getIconData(
|
||||||
name: string | IconifyIconName
|
name: string | IconifyIconName
|
||||||
): FullIconifyIcon | null {
|
): FullIconifyIcon | null {
|
||||||
const icon = typeof name === 'string' ? stringToIcon(name, true) : name;
|
const icon =
|
||||||
|
typeof name === 'string' ? stringToIcon(name, true, simpleNames) : name;
|
||||||
return icon
|
return icon
|
||||||
? getIcon(getStorage(icon.provider, icon.prefix), icon.name)
|
? getIcon(getStorage(icon.provider, icon.prefix), icon.name)
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add one icon
|
||||||
|
*/
|
||||||
|
export function addIcon(name: string, data: IconifyIcon): boolean {
|
||||||
|
const icon = stringToIcon(name, true, simpleNames);
|
||||||
|
if (!icon) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const storage = getStorage(icon.provider, icon.prefix);
|
||||||
|
return storeIcon(storage, icon.name, data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add icon set
|
* Add icon set
|
||||||
*/
|
*/
|
||||||
export function addCollection(data: IconifyJSON, provider?: string): boolean {
|
export function addCollection(data: IconifyJSON, provider?: string): boolean {
|
||||||
|
if (typeof data !== 'object') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get provider
|
||||||
if (typeof provider !== 'string') {
|
if (typeof provider !== 'string') {
|
||||||
provider = typeof data.provider === 'string' ? data.provider : '';
|
provider = typeof data.provider === 'string' ? data.provider : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for simple names: requires empty provider and prefix
|
||||||
|
if (
|
||||||
|
simpleNames &&
|
||||||
|
provider === '' &&
|
||||||
|
(typeof data.prefix !== 'string' || data.prefix === '')
|
||||||
|
) {
|
||||||
|
// Simple names: add icons one by one
|
||||||
|
let added = false;
|
||||||
|
parseIconSet(data, (name, icon) => {
|
||||||
|
if (icon !== null && addIcon(name, icon)) {
|
||||||
|
added = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return added;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate provider and prefix
|
||||||
if (
|
if (
|
||||||
typeof data !== 'object' ||
|
|
||||||
// Prefix must be present
|
|
||||||
typeof data.prefix !== 'string' ||
|
typeof data.prefix !== 'string' ||
|
||||||
// Validate provider and prefix
|
|
||||||
!validateIcon({
|
!validateIcon({
|
||||||
provider,
|
provider,
|
||||||
prefix: data.prefix,
|
prefix: data.prefix,
|
||||||
@ -91,14 +143,7 @@ export const storageFunctions: IconifyStorageFunctions = {
|
|||||||
listIcons,
|
listIcons,
|
||||||
|
|
||||||
// Add icon
|
// Add icon
|
||||||
addIcon: (name, data) => {
|
addIcon,
|
||||||
const icon = stringToIcon(name, true);
|
|
||||||
if (!icon) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const storage = getStorage(icon.provider, icon.prefix);
|
|
||||||
return addIcon(storage, icon.name, data);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Add icon set
|
// Add icon set
|
||||||
addCollection,
|
addCollection,
|
||||||
|
@ -34,16 +34,47 @@ describe('Testing icon name', () => {
|
|||||||
});
|
});
|
||||||
expect(validateIcon(icon)).to.be.equal(true);
|
expect(validateIcon(icon)).to.be.equal(true);
|
||||||
|
|
||||||
|
// Simple word without prefix
|
||||||
|
icon = stringToIcon('home');
|
||||||
|
expect(icon).to.be.eql(null);
|
||||||
|
expect(validateIcon(icon)).to.be.equal(false);
|
||||||
|
|
||||||
|
// Same as above, but with empty names enabled
|
||||||
|
icon = stringToIcon('home', false, true);
|
||||||
|
expect(icon).to.be.eql({
|
||||||
|
provider: '',
|
||||||
|
prefix: '',
|
||||||
|
name: 'home',
|
||||||
|
});
|
||||||
|
expect(validateIcon(icon)).to.be.equal(false);
|
||||||
|
expect(validateIcon(icon, true)).to.be.equal(true);
|
||||||
|
|
||||||
// Missing icon name
|
// Missing icon name
|
||||||
icon = stringToIcon('@iconify-home-icon');
|
icon = stringToIcon('@iconify-home-icon');
|
||||||
expect(icon).to.be.eql(null);
|
expect(icon).to.be.eql(null);
|
||||||
expect(validateIcon(icon)).to.be.equal(false);
|
expect(validateIcon(icon)).to.be.equal(false);
|
||||||
|
|
||||||
|
// Same as above, but with empty names enabled
|
||||||
|
icon = stringToIcon('@iconify-home-icon', false, true);
|
||||||
|
expect(icon).to.be.eql(null);
|
||||||
|
expect(validateIcon(icon)).to.be.equal(false);
|
||||||
|
expect(validateIcon(icon, true)).to.be.equal(false);
|
||||||
|
|
||||||
// Underscore is not an acceptable separator
|
// Underscore is not an acceptable separator
|
||||||
icon = stringToIcon('fa_home');
|
icon = stringToIcon('fa_home');
|
||||||
expect(icon).to.be.eql(null);
|
expect(icon).to.be.eql(null);
|
||||||
expect(validateIcon(icon)).to.be.equal(false);
|
expect(validateIcon(icon)).to.be.equal(false);
|
||||||
|
|
||||||
|
// Same as above, but with empty names enabled
|
||||||
|
icon = stringToIcon('fa_home', false, true);
|
||||||
|
expect(icon).to.be.eql({
|
||||||
|
provider: '',
|
||||||
|
prefix: '',
|
||||||
|
name: 'fa_home',
|
||||||
|
});
|
||||||
|
expect(validateIcon(icon)).to.be.equal(false);
|
||||||
|
expect(validateIcon(icon, true)).to.be.equal(false);
|
||||||
|
|
||||||
// Invalid character '_': fail validateIcon
|
// Invalid character '_': fail validateIcon
|
||||||
icon = stringToIcon('fa:home_outline') as IconifyIconName;
|
icon = stringToIcon('fa:home_outline') as IconifyIconName;
|
||||||
expect(icon).to.be.eql({
|
expect(icon).to.be.eql({
|
||||||
@ -134,6 +165,10 @@ describe('Testing icon name', () => {
|
|||||||
expect(icon).to.be.eql(null);
|
expect(icon).to.be.eql(null);
|
||||||
expect(validateIcon(icon)).to.be.equal(false);
|
expect(validateIcon(icon)).to.be.equal(false);
|
||||||
|
|
||||||
|
// Same as above, empty names allowed
|
||||||
|
icon = stringToIcon('@mdi:light:home:outline', false, true);
|
||||||
|
expect(icon).to.be.eql(null);
|
||||||
|
|
||||||
// Upper case: fail validateIcon
|
// Upper case: fail validateIcon
|
||||||
icon = stringToIcon('@MD:home-outline') as IconifyIconName;
|
icon = stringToIcon('@MD:home-outline') as IconifyIconName;
|
||||||
expect(icon).to.be.eql({
|
expect(icon).to.be.eql({
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import 'mocha';
|
import 'mocha';
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { storageFunctions } from '../../lib/storage/functions';
|
import {
|
||||||
|
storageFunctions,
|
||||||
|
allowSimpleNames,
|
||||||
|
} from '../../lib/storage/functions';
|
||||||
|
import { fullIcon } from '../../lib/icon';
|
||||||
|
|
||||||
describe('Testing IconifyStorageFunctions', () => {
|
describe('Testing IconifyStorageFunctions', () => {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
@ -27,4 +31,120 @@ describe('Testing IconifyStorageFunctions', () => {
|
|||||||
expect(storageFunctions.iconExists(testName)).to.be.equal(true);
|
expect(storageFunctions.iconExists(testName)).to.be.equal(true);
|
||||||
expect(storageFunctions.listIcons(provider)).to.be.eql([testName]);
|
expect(storageFunctions.listIcons(provider)).to.be.eql([testName]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Invalid icon name', () => {
|
||||||
|
const testName = 'storage' + count++;
|
||||||
|
|
||||||
|
// Reset module
|
||||||
|
allowSimpleNames(false);
|
||||||
|
|
||||||
|
// Empty
|
||||||
|
expect(storageFunctions.iconExists(testName)).to.be.equal(false);
|
||||||
|
expect(storageFunctions.getIcon(testName)).to.be.equal(null);
|
||||||
|
|
||||||
|
// Add and test one icon (icon should not be added)
|
||||||
|
expect(
|
||||||
|
storageFunctions.addIcon(testName, {
|
||||||
|
body: '<g />',
|
||||||
|
})
|
||||||
|
).to.be.equal(false);
|
||||||
|
expect(storageFunctions.iconExists(testName)).to.be.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid icon set', () => {
|
||||||
|
// Reset module
|
||||||
|
allowSimpleNames(false);
|
||||||
|
|
||||||
|
// Icon set without prefix (should work only when simple names are allowed, tested later in this file)
|
||||||
|
expect(
|
||||||
|
storageFunctions.addCollection({
|
||||||
|
prefix: '',
|
||||||
|
icons: {
|
||||||
|
foo: {
|
||||||
|
body: '<g />',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).to.be.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Simple icon name', () => {
|
||||||
|
const testName = 'storage' + count++;
|
||||||
|
|
||||||
|
// Enable empty storage
|
||||||
|
allowSimpleNames(true);
|
||||||
|
|
||||||
|
// Empty
|
||||||
|
expect(storageFunctions.iconExists(testName)).to.be.equal(false);
|
||||||
|
expect(storageFunctions.getIcon(testName)).to.be.equal(null);
|
||||||
|
|
||||||
|
// Add and test one icon
|
||||||
|
expect(
|
||||||
|
storageFunctions.addIcon(testName, {
|
||||||
|
body: '<g />',
|
||||||
|
})
|
||||||
|
).to.be.equal(true);
|
||||||
|
expect(storageFunctions.iconExists(testName)).to.be.equal(true);
|
||||||
|
|
||||||
|
// Reset config after test
|
||||||
|
allowSimpleNames(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Collection with simple icon name', () => {
|
||||||
|
const n = count++;
|
||||||
|
const n2 = count++;
|
||||||
|
let name: string;
|
||||||
|
|
||||||
|
// Enable empty storage
|
||||||
|
allowSimpleNames(true);
|
||||||
|
|
||||||
|
// Add icon set
|
||||||
|
const name1 = 'test' + n;
|
||||||
|
const prefix2 = `prefixed${n}`;
|
||||||
|
const name2 = `icon${n2}`;
|
||||||
|
expect(
|
||||||
|
storageFunctions.addCollection({
|
||||||
|
prefix: '',
|
||||||
|
icons: {
|
||||||
|
[name1]: {
|
||||||
|
body: '<g data-icon="basic-icon" />',
|
||||||
|
},
|
||||||
|
[`${prefix2}-${name2}`]: {
|
||||||
|
body: '<g data-icon="prefixed-icon" />',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).to.be.equal(true);
|
||||||
|
|
||||||
|
// Test 'test'
|
||||||
|
name = name1;
|
||||||
|
expect(storageFunctions.iconExists(name)).to.be.equal(true);
|
||||||
|
expect(storageFunctions.getIcon(name)).to.be.eql(
|
||||||
|
fullIcon({
|
||||||
|
body: '<g data-icon="basic-icon" />',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test prefixed icon, using ':' separator
|
||||||
|
name = `${prefix2}:${name2}`;
|
||||||
|
expect(storageFunctions.listIcons('', prefix2)).to.be.eql([name]);
|
||||||
|
expect(storageFunctions.iconExists(name)).to.be.equal(true);
|
||||||
|
expect(storageFunctions.getIcon(name)).to.be.eql(
|
||||||
|
fullIcon({
|
||||||
|
body: '<g data-icon="prefixed-icon" />',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test prefixed icon, using '-' separator
|
||||||
|
name = `${prefix2}-${name2}`;
|
||||||
|
expect(storageFunctions.iconExists(name)).to.be.equal(true);
|
||||||
|
expect(storageFunctions.getIcon(name)).to.be.eql(
|
||||||
|
fullIcon({
|
||||||
|
body: '<g data-icon="prefixed-icon" />',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reset config after test
|
||||||
|
allowSimpleNames(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user