2
0
mirror of https://github.com/iconify/iconify.git synced 2025-02-12 16:48:28 +00:00

Merge branch 'next' into userquin/feat-split-modern-loader-next

# Conflicts:
#	packages/utils/package-lock.json
#	packages/utils/src/loader/loader.ts
#	packages/utils/src/loader/types.ts
#	packages/utils/tests/iconify-icon-test.ts
This commit is contained in:
Joaquín Sánchez Jiménez 2022-03-14 14:23:57 +01:00
commit 1b3680647c
28 changed files with 565 additions and 3510 deletions

View File

@ -1,12 +1,12 @@
{
"name": "@iconify/react",
"version": "3.1.3",
"version": "3.1.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@iconify/react",
"version": "3.1.3",
"version": "3.1.4",
"license": "MIT",
"devDependencies": {
"@babel/preset-env": "^7.16.11",

View File

@ -2,7 +2,7 @@
"name": "@iconify/react",
"description": "Iconify icon component for React.",
"author": "Vjacheslav Trushkin",
"version": "3.1.3",
"version": "3.1.4",
"license": "MIT",
"bugs": "https://github.com/iconify/iconify/issues",
"homepage": "https://iconify.design/",
@ -37,6 +37,11 @@
"import": "./dist/offline.mjs",
"types": "./dist/offline.d.ts",
"default": "./dist/offline.js"
},
"./dist/offline": {
"import": "./dist/offline.mjs",
"types": "./dist/offline.d.ts",
"default": "./dist/offline.js"
}
},
"devDependencies": {

View File

@ -1,6 +1,7 @@
/**
* @jest-environment jsdom
*/
import { tick } from 'svelte';
import { render } from '@testing-library/svelte';
import Icon, { loadIcons, iconExists } from '../../';
import { mockAPIData } from '@iconify/core/lib/api/modules/mock';
@ -61,7 +62,7 @@ describe('Rendering icon', () => {
const node = component.container.querySelector('svg')!;
const html = (node.parentNode as HTMLDivElement).innerHTML;
// Check HTML
// Check HTML immediately
expect(html).toBe(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24" class="' +
className +
@ -104,28 +105,6 @@ describe('Rendering icon', () => {
// Test it again
expect(iconExists(iconName)).toBe(true);
// Check if state was changed
// Wrapped in double setTimeout() because re-render takes 2 ticks
setTimeout(() => {
setTimeout(() => {
const node = component.container.querySelector('svg')!;
const html = (node.parentNode as HTMLDivElement)
.innerHTML;
// Check HTML
expect(html).toBe(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="test ' +
className +
'" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
// onLoad should have been called
expect(onLoadCalled).toBe(true);
done();
}, 0);
}, 0);
},
});
@ -141,6 +120,24 @@ describe('Rendering icon', () => {
expect(name).toBe(iconName);
expect(onLoadCalled).toBe(false);
onLoadCalled = true;
// Check component on next tick
tick()
.then(() => {
const node = component.container.querySelector('svg')!;
const html = (node.parentNode as HTMLDivElement)
.innerHTML;
// Check HTML
expect(html).toBe(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="test ' +
className +
'" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
done();
})
.catch(done);
},
});
@ -172,16 +169,24 @@ describe('Rendering icon', () => {
// Test it again
expect(iconExists(iconName)).toBe(false);
// Check if state was changed
// Wrapped in double setTimeout() because re-render takes 2 ticks
setTimeout(() => {
setTimeout(() => {
// Check if state was changed on next few ticks
tick()
.then(() => {
const html = component.container.innerHTML;
expect(html).toBe('<div></div>');
return tick();
})
.then(() => {
const html = component.container.innerHTML;
expect(html).toBe('<div></div>');
return tick();
})
.then(() => {
const html = component.container.innerHTML;
expect(html).toBe('<div></div>');
done();
}, 0);
}, 0);
})
.catch(done);
},
});

View File

@ -1,6 +1,7 @@
/**
* @jest-environment jsdom
*/
import { tick } from 'svelte';
import { render } from '@testing-library/svelte';
import { iconExists } from '../../';
import { mockAPIData } from '@iconify/core/lib/api/modules/mock';
@ -58,29 +59,6 @@ describe('Rendering icon', () => {
// Test it again
expect(iconExists(iconName)).toBe(true);
// Check if state was changed
// Wrapped in double setTimeout() because re-render takes 2 ticks
setTimeout(() => {
setTimeout(() => {
const node = component.container.querySelector('svg')!;
const html = (node.parentNode as HTMLDivElement)
.innerHTML;
// Check HTML
expect(html).toBe(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24" class="' +
className +
'"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
// onLoad should have been called
expect(onLoadCalled).toBe(iconName);
// Change property
triggerSwap!();
}, 0);
}, 0);
},
});
@ -106,28 +84,6 @@ describe('Rendering icon', () => {
// Test it again
expect(iconExists(iconName2)).toBe(true);
// Check if state was changed
// Wrapped in double setTimeout() because re-render takes 2 ticks
setTimeout(() => {
setTimeout(() => {
const node = component.container.querySelector('svg')!;
const html = (node.parentNode as HTMLDivElement)
.innerHTML;
// Check HTML
expect(html).toBe(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32" class="' +
className +
'"><path d="M19.031 4.281l-11 11l-.687.719l.687.719l11 11l1.438-1.438L10.187 16L20.47 5.719z" fill="currentColor"></path></svg>'
);
// onLoad should have been called for second icon
expect(onLoadCalled).toBe(iconName2);
done();
}, 0);
}, 0);
},
});
@ -147,11 +103,50 @@ describe('Rendering icon', () => {
// First onLoad call
case iconName:
expect(onLoadCalled).toBe('');
// Wait 1 tick, then test rendered icon
tick()
.then(() => {
const node =
component.container.querySelector('svg')!;
const html = (node.parentNode as HTMLDivElement)
.innerHTML;
// Check HTML
expect(html).toBe(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24" class="' +
className +
'"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
// Change property
triggerSwap!();
})
.catch(done);
break;
// Second onLoad call
case iconName2:
expect(onLoadCalled).toBe(iconName);
// Wait 1 tick, then test rendered icon
tick()
.then(() => {
const node =
component.container.querySelector('svg')!;
const html = (node.parentNode as HTMLDivElement)
.innerHTML;
// Check HTML
expect(html).toBe(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32" class="' +
className +
'"><path d="M19.031 4.281l-11 11l-.687.719l.687.719l11 11l1.438-1.438L10.187 16L20.47 5.719z" fill="currentColor"></path></svg>'
);
done();
})
.catch(done);
break;
default:
@ -224,28 +219,6 @@ describe('Rendering icon', () => {
// Test it again
expect(iconExists(iconName2)).toBe(true);
// Check if state was changed
// Wrapped in double setTimeout() because re-render takes 2 ticks
setTimeout(() => {
setTimeout(() => {
const node = component.container.querySelector('svg')!;
const html = (node.parentNode as HTMLDivElement)
.innerHTML;
// Check HTML
expect(html).toBe(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32" class="' +
className +
'"><path d="M19.031 4.281l-11 11l-.687.719l.687.719l11 11l1.438-1.438L10.187 16L20.47 5.719z" fill="currentColor"></path></svg>'
);
// onLoad should have been called for second icon
expect(onLoadCalled).toBe(iconName2);
done();
}, 0);
}, 0);
},
});
@ -263,6 +236,24 @@ describe('Rendering icon', () => {
// onLoad should be called only for second icon
expect(name).toBe(iconName2);
onLoadCalled = name;
// Wait 1 tick, then test rendered icon
tick()
.then(() => {
const node = component.container.querySelector('svg')!;
const html = (node.parentNode as HTMLDivElement)
.innerHTML;
// Check HTML
expect(html).toBe(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32" class="' +
className +
'"><path d="M19.031 4.281l-11 11l-.687.719l.687.719l11 11l1.438-1.438L10.187 16L20.47 5.719z" fill="currentColor"></path></svg>'
);
done();
})
.catch(done);
},
});
@ -313,48 +304,6 @@ describe('Rendering icon', () => {
// Test it again
expect(iconExists(iconName)).toBe(true);
// Check if state was changed
// Wrapped in double setTimeout() because re-render takes 2 ticks
setTimeout(() => {
setTimeout(() => {
const node = component.container.querySelector('svg')!;
const html = (node.parentNode as HTMLDivElement)
.innerHTML;
// Check HTML
expect(html).toBe(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24" class="' +
className +
'"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
// onLoad should have been called
expect(onLoadCalled).toBe(true);
// Add horizontal flip and style
triggerSwap!();
// Wait for component to re-render
setTimeout(() => {
setTimeout(() => {
// Check HTML again
const node =
component.container.querySelector('svg')!;
const html = (node.parentNode as HTMLDivElement)
.innerHTML;
expect(html).toBe(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24" class="' +
className +
'"><g transform="translate(24 0) scale(-1 1)"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></g></svg>'
);
done();
}, 0);
}, 0);
}, 0);
}, 0);
},
});
@ -369,9 +318,46 @@ describe('Rendering icon', () => {
},
onLoad: (name: string) => {
expect(name).toBe(iconName);
// Should be called only once
expect(onLoadCalled).toBe(false);
onLoadCalled = true;
// Check if state was changed on next tick
tick()
.then(() => {
const node = component.container.querySelector('svg')!;
const html = (node.parentNode as HTMLDivElement)
.innerHTML;
// Check HTML
expect(html).toBe(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24" class="' +
className +
'"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
// Add horizontal flip and style
triggerSwap!();
// Wait for component to re-render
return tick();
})
.then(() => {
// Check HTML again
const node = component.container.querySelector('svg')!;
const html = (node.parentNode as HTMLDivElement)
.innerHTML;
expect(html).toBe(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24" class="' +
className +
'"><g transform="translate(24 0) scale(-1 1)"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></g></svg>'
);
done();
})
.catch(done);
},
});

View File

@ -5,12 +5,21 @@ module.exports = {
node: true,
jasmine: true,
},
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:prettier/recommended',
],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
},
parser: '@typescript-eslint/parser',
parserOptions: {
tsconfigRootDir: __dirname,
project: ['tsconfig.json', 'tests/tsconfig.json'],
},
plugins: ['@typescript-eslint', 'jasmine'],
rules: {
'no-mixed-spaces-and-tabs': ['off'],

File diff suppressed because it is too large Load Diff

View File

@ -213,10 +213,12 @@
"@iconify/library-builder": "^1.0.5",
"@types/debug": "^4.1.7",
"@types/jest": "^27.0.1",
"@typescript-eslint/eslint-plugin": "^4.31.1",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"cross-env": "^7.0.3",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-jasmine": "^4.1.2",
"eslint-plugin-prettier": "^4.0.0",
"jasmine": "^3.9.0",
"jest": "^27.2.1",
"rimraf": "^3.0.2",

View File

@ -60,8 +60,8 @@ function fromFunction(value: string): Color | null {
}
// Get function and values
const func = (parts[0] as string).trim();
const content = (parts[1] as string).trim();
const func = parts[0].trim();
const content = parts[1].trim();
// Get alpha and split content
let values: string[];
@ -133,7 +133,7 @@ function fromFunction(value: string): Color | null {
const isPercentage: boolean[] = [];
const numbers: number[] = [];
for (let i = 0; i < 3; i++) {
const colorStr = values[i] as string;
const colorStr = values[i];
const index = colorStr.indexOf('%');
const hasPercentage = index !== -1;
if (hasPercentage && index !== colorStr.length - 1) {
@ -435,7 +435,11 @@ export function colorToString(color: Color): string {
}
case 'hsl': {
const list = [color.h, color.s + '%', color.l + '%'];
const list = [
color.h,
color.s.toString() + '%',
color.l.toString() + '%',
];
if (color.alpha !== 1) {
list.push(color.alpha);
}
@ -445,17 +449,17 @@ export function colorToString(color: Color): string {
}
case 'lab': {
const list = [color.l + '%', color.a, color.b];
const list = [color.l.toString() + '%', color.a, color.b];
if (color.alpha !== 1) {
list.push('/ ' + color.alpha);
list.push('/ ' + color.alpha.toString());
}
return 'lab(' + list.join(' ') + ')';
}
case 'lch': {
const list = [color.l + '%', color.c, color.h];
const list = [color.l.toString() + '%', color.c, color.h];
if (color.alpha !== 1) {
list.push('/ ' + color.alpha);
list.push('/ ' + color.alpha.toString());
}
return 'lch(' + list.join(' ') + ')';
}

View File

@ -88,9 +88,9 @@ export function convertIconSetInfo(
// Get name
let name: string;
if (typeof source.name === 'string') {
name = source.name as string;
name = source.name;
} else if (typeof source.title === 'string') {
name = source.title as string;
name = source.title;
} else {
return null;
}

View File

@ -19,7 +19,7 @@ export function getIcons(
): IconifyJSON | null {
const result: IconifyJSON = {
prefix: data.prefix,
icons: Object.create(null),
icons: Object.create(null) as never,
};
const tested: Set<string> = new Set();
let empty = true;
@ -43,9 +43,9 @@ export function getIcons(
const copied = copy(data.aliases[name].parent, iteration + 1);
if (copied) {
if (result.aliases === void 0) {
result.aliases = Object.create(null);
result.aliases = Object.create(null) as never;
}
result.aliases![name] = { ...data.aliases[name] };
result.aliases[name] = { ...data.aliases[name] };
}
return copied;
}
@ -56,9 +56,9 @@ export function getIcons(
const copied = copy(parent, iteration + 1);
if (copied) {
if (result.aliases === void 0) {
result.aliases = Object.create(null);
result.aliases = Object.create(null) as never;
}
result.aliases![name] = {
result.aliases[name] = {
parent,
};
}

View File

@ -304,7 +304,7 @@ export function validateIconSet(
if (typeof data.chars === 'object') {
const chars = data.chars;
Object.keys(chars).forEach((char) => {
if (!char.match(matchChar) || typeof chars[char] !== 'string') {
if (!matchChar.exec(char) || typeof chars[char] !== 'string') {
if (fix) {
delete chars[char];
return;

View File

@ -1,5 +1,9 @@
import createDebugger from 'debug';
import type { CustomIconLoader, IconifyLoaderOptions, InlineCollection } from './types';
import type {
CustomIconLoader,
IconifyLoaderOptions,
InlineCollection,
} from './types';
import { mergeIconProps } from './utils';
const debug = createDebugger('@iconify-loader:custom');
@ -11,7 +15,7 @@ export async function getCustomIcon(
custom: CustomIconLoader | InlineCollection,
collection: string,
icon: string,
options?: IconifyLoaderOptions,
options?: IconifyLoaderOptions
): Promise<string | undefined> {
let result: string | undefined | null;
@ -37,7 +41,7 @@ export async function getCustomIcon(
collection,
icon,
options,
undefined,
undefined
);
}
}

View File

@ -1,13 +1,16 @@
import { promises as fs, Stats } from 'fs';
import { isPackageExists, resolveModule } from 'local-pkg'
import type { IconifyJSON } from '@iconify/types'
import { isPackageExists, resolveModule } from 'local-pkg';
import type { IconifyJSON } from '@iconify/types';
import { tryInstallPkg } from './install-pkg';
const _collections: Record<string, Promise<IconifyJSON | undefined>> = {};
const isLegacyExists = isPackageExists('@iconify/json');
export async function loadCollectionFromFS(name: string, autoInstall = false): Promise<IconifyJSON | undefined> {
if (!_collections[name]) {
export async function loadCollectionFromFS(
name: string,
autoInstall = false
): Promise<IconifyJSON | undefined> {
if (!(await _collections[name])) {
_collections[name] = task();
}
return _collections[name];
@ -30,9 +33,10 @@ export async function loadCollectionFromFS(name: string, autoInstall = false): P
return undefined;
}
if (stat && stat.isFile()) {
return JSON.parse(await fs.readFile(jsonPath as string, 'utf8')) as IconifyJSON;
}
else {
return JSON.parse(
await fs.readFile(jsonPath as string, 'utf8')
) as IconifyJSON;
} else {
return undefined;
}
}

View File

@ -13,7 +13,7 @@ export async function searchForIcon(
iconSet: IconifyJSON,
collection: string,
ids: string[],
options?: IconifyLoaderOptions,
options?: IconifyLoaderOptions
): Promise<string | undefined> {
let iconData: FullIconifyIcon | null;
const { customize } = options?.customizations ?? {};
@ -34,7 +34,7 @@ export async function searchForIcon(
collection,
id,
options,
() => attributes,
() => attributes
);
}
}

View File

@ -6,16 +6,21 @@ export async function mergeIconProps(
collection: string,
icon: string,
options?: IconifyLoaderOptions,
propsProvider?: () => Awaitable<Record<string, string>>,
propsProvider?: () => Awaitable<Record<string, string>>
): Promise<string> {
const { scale, addXmlNs = false } = options ?? {}
const {
additionalProps = {},
iconCustomizer,
} = options?.customizations ?? {};
const { scale, addXmlNs = false } = options ?? {};
const { additionalProps = {}, iconCustomizer } =
options?.customizations ?? {};
const props: Record<string, string> = (await propsProvider?.()) ?? {};
if (!svg.includes(" width=") && !svg.includes(" height=") && typeof scale === 'number') {
if ((typeof props.width === 'undefined' || props.width === null) && (typeof props.height === 'undefined' || props.height === null)) {
if (
!svg.includes(' width=') &&
!svg.includes(' height=') &&
typeof scale === 'number'
) {
if (
(typeof props.width === 'undefined' || props.width === null) &&
(typeof props.height === 'undefined' || props.height === null)
) {
props.width = `${scale}em`;
props.height = `${scale}em`;
}
@ -33,18 +38,24 @@ export async function mergeIconProps(
props['xmlns'] = 'http://www.w3.org/2000/svg';
}
// add xmlns:xlink if xlink present and the xmlns missing
if (!svg.includes(' xmlns:xlink=') && svg.includes('xlink:') && !props['xmlns:xlink']) {
if (
!svg.includes(' xmlns:xlink=') &&
svg.includes('xlink:') &&
!props['xmlns:xlink']
) {
props['xmlns:xlink'] = 'http://www.w3.org/1999/xlink';
}
}
svg = svg.replace(
'<svg ',
`<svg ${Object.keys(props).map((p) => `${p}="${props[p]}"`).join(' ')}`
`<svg ${Object.keys(props)
.map((p) => `${p}="${props[p]}"`)
.join(' ')}`
);
if (svg && options) {
const { defaultStyle, defaultClass } = options
const { defaultStyle, defaultClass } = options;
// additional props and iconCustomizer takes precedence
if (defaultClass && !svg.includes(' class=')) {
svg = svg.replace('<svg ', `<svg class="${defaultClass}" `);

View File

@ -102,9 +102,9 @@ export function iconToSVG(
// Horizontal flip
transformations.push(
'translate(' +
(box.width + box.left) +
(box.width + box.left).toString() +
' ' +
(0 - box.top) +
(0 - box.top).toString() +
')'
);
transformations.push('scale(-1 1)');
@ -114,9 +114,9 @@ export function iconToSVG(
// Vertical flip
transformations.push(
'translate(' +
(0 - box.left) +
(0 - box.left).toString() +
' ' +
(box.height + box.top) +
(box.height + box.top).toString() +
')'
);
transformations.push('scale(1 -1)');
@ -133,7 +133,11 @@ export function iconToSVG(
// 90deg
tempValue = box.height / 2 + box.top;
transformations.unshift(
'rotate(90 ' + tempValue + ' ' + tempValue + ')'
'rotate(90 ' +
tempValue.toString() +
' ' +
tempValue.toString() +
')'
);
break;
@ -141,9 +145,9 @@ export function iconToSVG(
// 180deg
transformations.unshift(
'rotate(180 ' +
(box.width / 2 + box.left) +
(box.width / 2 + box.left).toString() +
' ' +
(box.height / 2 + box.top) +
(box.height / 2 + box.top).toString() +
')'
);
break;
@ -152,7 +156,11 @@ export function iconToSVG(
// 270deg
tempValue = box.width / 2 + box.left;
transformations.unshift(
'rotate(-90 ' + tempValue + ' ' + tempValue + ')'
'rotate(-90 ' +
tempValue.toString() +
' ' +
tempValue.toString() +
')'
);
break;
}
@ -214,8 +222,8 @@ export function iconToSVG(
}
// Convert to string
width = typeof width === 'string' ? width : width + '';
height = typeof height === 'string' ? height : height + '';
width = typeof width === 'string' ? width : width.toString() + '';
height = typeof height === 'string' ? height : height.toString() + '';
// Result
const result: IconifyIconBuildResult = {
@ -224,7 +232,13 @@ export function iconToSVG(
height,
preserveAspectRatio: preserveAspectRatio(customisations),
viewBox:
box.left + ' ' + box.top + ' ' + box.width + ' ' + box.height,
box.left.toString() +
' ' +
box.top.toString() +
' ' +
box.width.toString() +
' ' +
box.height.toString(),
},
body,
};

View File

@ -2,12 +2,19 @@
export function encodeSvgForCss(svg: string): string {
let useSvg = svg.startsWith('<svg>') ? svg.replace('<svg>', '<svg >') : svg;
if (!useSvg.includes(' xmlns:xlink=') && useSvg.includes(' xlink:')) {
useSvg = useSvg.replace('<svg ', '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ');
useSvg = useSvg.replace(
'<svg ',
'<svg xmlns:xlink="http://www.w3.org/1999/xlink" '
);
}
if (!useSvg.includes(' xmlns=')) {
useSvg = useSvg.replace('<svg ', '<svg xmlns="http://www.w3.org/2000/svg" ');
useSvg = useSvg.replace(
'<svg ',
'<svg xmlns="http://www.w3.org/2000/svg" '
);
}
return useSvg.replace(/"/g, '\'')
return useSvg
.replace(/"/g, "'")
.replace(/%/g, '%25')
.replace(/#/g, '%23')
.replace(/{/g, '%7B')

View File

@ -52,7 +52,9 @@ export function replaceIDs(
// Replace with unique ids
ids.forEach((id) => {
const newID =
typeof prefix === 'function' ? prefix(id) : prefix + counter++;
typeof prefix === 'function'
? prefix(id)
: prefix + (counter++).toString();
const escapedID = id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

View File

@ -33,7 +33,7 @@ export function calculateSize(
const newParts = [];
let code = oldParts.shift() as string;
let isNumber = unitsTest.test(code as string);
let isNumber = unitsTest.test(code);
// eslint-disable-next-line no-constant-condition
while (true) {

View File

@ -15,9 +15,12 @@ describe('Testing getCustomIcon', () => {
const result = await getCustomIcon(() => svg, 'a', 'b', {
customizations: {
transform(icon) {
return icon.replace('<svg ', '<svg width="1em" height="1em" ');
return icon.replace(
'<svg ',
'<svg width="1em" height="1em" '
);
},
}
},
});
expect(result && result.indexOf('width="1em"') > -1).toBeTruthy();
expect(result && result.indexOf('height="1em"') > -1).toBeTruthy();

View File

@ -1,7 +1,6 @@
import { loadNodeIcon } from '../lib/loader/node-loader';
describe('Testing loadIcon with @iconify-json/flat-color-icons>', () => {
test('loadIcon works', async () => {
const result = await loadNodeIcon('flat-color-icons', 'up-right');
expect(result).toBeTruthy();
@ -10,7 +9,7 @@ describe('Testing loadIcon with @iconify-json/flat-color-icons>', () => {
test('loadIcon adds xmlns:xlink', async () => {
const result = await loadNodeIcon('flat-color-icons', 'up-right', { addXmlNs: true });
expect(result).toBeTruthy();
expect(result && result.indexOf('xmlns:xlink=') > - 1).toBeTruthy();
expect(result && result.indexOf('xmlns:xlink=') > -1).toBeTruthy();
});
test('loadIcon with customize with default style and class', async () => {
@ -23,13 +22,13 @@ describe('Testing loadIcon with @iconify-json/flat-color-icons>', () => {
props.height = '2em';
return props;
},
}
},
});
expect(result).toBeTruthy();
expect(result && result.indexOf('margin-top: 1rem;') > - 1).toBeTruthy();
expect(result && result.indexOf('class="clazz"') > - 1).toBeTruthy();
expect(result && result.indexOf('width="2em"') > - 1).toBeTruthy();
expect(result && result.indexOf('height="2em"') > - 1).toBeTruthy();
expect(result && result.indexOf('margin-top: 1rem;') > -1).toBeTruthy();
expect(result && result.indexOf('class="clazz"') > -1).toBeTruthy();
expect(result && result.indexOf('width="2em"') > -1).toBeTruthy();
expect(result && result.indexOf('height="2em"') > -1).toBeTruthy();
});
test('loadIcon preserves customizations order', async () => {
@ -39,16 +38,19 @@ describe('Testing loadIcon with @iconify-json/flat-color-icons>', () => {
defaultClass: 'clazz1',
customizations: {
additionalProps: {
'width': '2em',
'height': '2em',
'style': 'color: blue;',
'class': 'clazz2',
width: '2em',
height: '2em',
style: 'color: blue;',
class: 'clazz2',
},
// it will never be called, it is not a custom icon
transform(icon) {
return icon.replace('<svg ', '<svg width="4em" height="4em" ');
return icon.replace(
'<svg ',
'<svg width="4em" height="4em" '
);
},
}
},
});
expect(result).toBeTruthy();
expect(result && result.includes('style="color: blue;"')).toBeTruthy();

View File

@ -4,9 +4,9 @@ import { loadIcon } from '../lib';
const fixturesDir = './tests/fixtures';
const loader: CustomIconLoader = async(name) => {
const loader: CustomIconLoader = async (name) => {
return await fs.readFile(`${fixturesDir}/${name}.svg`, 'utf8');
}
};
describe('Testing loadIcon', () => {
test('CustomCollection', async () => {
@ -14,8 +14,8 @@ describe('Testing loadIcon', () => {
expect(svg).toBeTruthy();
const result = await loadIcon('a', 'circle', {
customCollections: {
'a': {
'circle': svg as string,
a: {
circle: svg as string,
},
},
});
@ -39,13 +39,16 @@ describe('Testing loadIcon', () => {
expect(svg).toBeTruthy();
const result = await loadIcon('a', 'circle', {
customCollections: {
'a': {
'circle': svg as string,
a: {
circle: svg as string,
},
},
customizations: {
transform(icon) {
return icon.replace('<svg ', '<svg width="1em" height="1em" ');
return icon.replace(
'<svg ',
'<svg width="1em" height="1em" '
);
},
},
});
@ -67,7 +70,7 @@ describe('Testing loadIcon', () => {
const result = await loadIcon('a', '1f3eb', {
customCollections: {
'a': {
a: {
'1f3eb': svg as string,
},
},

View File

@ -9,7 +9,9 @@ describe('Testing replaceIDs', () => {
// Using callback
let counter = 0;
expect(replaceIDs(body, () => 'callback' + counter++)).toBe(expected);
expect(
replaceIDs(body, () => 'callback' + (counter++).toString())
).toBe(expected);
});
test('Many IDs', () => {
@ -23,7 +25,9 @@ describe('Testing replaceIDs', () => {
// Using callback
let counter = 0;
expect(replaceIDs(body, () => 'callbackID' + counter++)).toBe(
expect(
replaceIDs(body, () => 'callbackID' + (counter++).toString())
).toBe(
body
.replace(/ssvg-id-1st-place-medala/g, 'callbackID0')
.replace(/ssvg-id-1st-place-medalb/g, 'callbackID7')
@ -70,7 +74,9 @@ describe('Testing replaceIDs', () => {
// To avoid messing up counter, using custom callback
let counter = 0;
expect(replaceIDs(body, () => 'callbackID' + counter++)).toBe(
expect(
replaceIDs(body, () => 'callbackID' + (counter++).toString())
).toBe(
body
.replace(/ssvg-id-compassa/g, 'callbackID2')
.replace(/ssvg-id-compassb/g, 'callbackID0')

View File

@ -2,7 +2,14 @@ import { validateIconSet } from '../lib/icon-set/validate';
describe('Testing validating icon', () => {
// Add various types for testing
const validationValues = new Map();
const validationValues = new Map<
| boolean
| Record<string | number | symbol, never>
| []
| number
| string,
{ text: string; type: string }
>();
beforeAll(() => {
validationValues.set(true, {

View File

@ -1,12 +1,12 @@
{
"name": "@iconify/vue",
"version": "3.1.3",
"version": "3.1.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@iconify/vue",
"version": "3.1.3",
"version": "3.1.4",
"license": "MIT",
"devDependencies": {
"@babel/preset-env": "^7.16.11",

View File

@ -2,7 +2,7 @@
"name": "@iconify/vue",
"description": "Iconify icon component for Vue 3.",
"author": "Vjacheslav Trushkin",
"version": "3.1.3",
"version": "3.1.4",
"license": "MIT",
"bugs": "https://github.com/iconify/iconify/issues",
"homepage": "https://iconify.design/",
@ -34,6 +34,11 @@
"import": "./dist/offline.mjs",
"types": "./dist/offline.d.ts",
"default": "./dist/offline.js"
},
"./dist/offline": {
"import": "./dist/offline.mjs",
"types": "./dist/offline.d.ts",
"default": "./dist/offline.js"
}
},
"devDependencies": {

View File

@ -1,12 +1,12 @@
{
"name": "@iconify/vue2",
"version": "1.1.3",
"version": "1.1.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@iconify/vue2",
"version": "1.1.3",
"version": "1.1.4",
"license": "MIT",
"devDependencies": {
"@babel/preset-env": "^7.16.11",

View File

@ -2,7 +2,7 @@
"name": "@iconify/vue2",
"description": "Iconify icon component for Vue 2.",
"author": "Vjacheslav Trushkin",
"version": "1.1.3",
"version": "1.1.4",
"license": "MIT",
"bugs": "https://github.com/iconify/iconify/issues",
"homepage": "https://iconify.design/",
@ -34,6 +34,11 @@
"import": "./dist/offline.mjs",
"types": "./dist/offline.d.ts",
"default": "./dist/offline.js"
},
"./dist/offline": {
"import": "./dist/offline.mjs",
"types": "./dist/offline.d.ts",
"default": "./dist/offline.js"
}
},
"devDependencies": {