2
0
mirror of https://github.com/iconify/iconify.git synced 2024-11-09 14:50:56 +00:00

feat(utils): support removing width and height attributes in generated icon

This commit is contained in:
Vjacheslav Trushkin 2023-01-27 10:13:39 +02:00
parent a3732f627a
commit d3f52d44fa
5 changed files with 176 additions and 55 deletions

View File

@ -1,5 +1,5 @@
import type { IconifyJSON, IconifyIcon } from '@iconify/types';
import { iconToSVG } from '../svg/build';
import { iconToSVG, isUnsetKeyword } from '../svg/build';
import { getIconData } from '../icon-set/get-icon';
import { mergeIconProps } from './utils';
import createDebugger from 'debug';
@ -39,26 +39,40 @@ export async function searchForIcon(
return { ...restAttributes };
},
(props) => {
if (
typeof props.width === 'undefined' ||
props.width === null
) {
if (typeof scale === 'number') {
props.width = `${scale}em`;
} else {
props.width = width;
// Check if value has 'unset' keyword
const check = (
prop: 'width' | 'height',
defaultValue: string | undefined
) => {
const propValue = props[prop];
let value: string | undefined;
if (!isUnsetKeyword(propValue)) {
if (propValue) {
// Do not change it
return;
}
if (typeof scale === 'number') {
// Scale icon, unless scale is 0
if (scale) {
value = `${scale}em`;
}
} else {
// Use result from iconToSVG()
value = defaultValue;
}
}
}
if (
typeof props.height === 'undefined' ||
props.height === null
) {
if (typeof scale === 'number') {
props.height = `${scale}em`;
// Change / unset
if (!value) {
delete props[prop];
} else {
props.height = height;
props[prop] = value;
}
}
};
check('width', width);
check('height', height);
}
);
}

View File

@ -1,4 +1,5 @@
import type { Awaitable } from '@antfu/utils';
import { isUnsetKeyword } from '../svg/build';
import type { IconifyLoaderOptions } from './types';
const svgWidthRegex = /width\s*=\s*["'](\w+)["']/;
@ -12,27 +13,40 @@ function configureSvgSize(
): [boolean, boolean] {
const svgNode = svg.slice(0, svg.indexOf('>'));
let result = svgWidthRegex.exec(svgNode);
const w = result != null;
if (typeof props.width === 'undefined' || props.width === null) {
if (typeof scale === 'number') {
props.width = `${scale}em`;
} else if (result) {
props.width = result[1];
}
}
const check = (prop: 'width' | 'height', regex: RegExp): boolean => {
const result = regex.exec(svgNode);
const w = result != null;
result = svgHeightRegex.exec(svgNode);
const h = result != null;
if (typeof props.height === 'undefined' || props.height === null) {
if (typeof scale === 'number') {
props.height = `${scale}em`;
} else if (result) {
props.height = result[1];
}
}
const propValue = props[prop];
let value: string | undefined;
return [w, h];
if (!isUnsetKeyword(propValue)) {
if (propValue) {
// Do not change it
return w;
}
if (typeof scale === 'number') {
// Scale icon, unless scale is 0
if (scale) {
value = `${scale}em`;
}
} else if (result) {
// Use result from iconToSVG()
value = result[1];
}
}
// Change / unset
if (!value) {
delete props[prop];
return false;
}
props[prop] = value;
return true;
};
return [check('width', svgWidthRegex), check('height', svgHeightRegex)];
}
export async function mergeIconProps(

View File

@ -11,8 +11,8 @@ import { calculateSize } from './size';
export interface IconifyIconBuildResult {
attributes: {
// Attributes for <svg>
width: string;
height: string;
width?: string;
height?: string;
viewBox: string;
};
@ -30,6 +30,12 @@ interface ViewBox {
height: number;
}
/**
* Check if value should be unset. Allows multiple keywords
*/
export const isUnsetKeyword = (value: unknown) =>
value === 'unset' || value === 'undefined' || value === 'none';
/**
* Get SVG attributes and content from icon + customisations
*
@ -194,22 +200,28 @@ export function iconToSVG(
: customisationsHeight;
}
// Result
const result: IconifyIconBuildResult = {
attributes: {
width: width.toString(),
height: height.toString(),
viewBox:
box.left.toString() +
' ' +
box.top.toString() +
' ' +
boxWidth.toString() +
' ' +
boxHeight.toString(),
},
// Attributes for result
const attributes = {} as IconifyIconBuildResult['attributes'];
const setAttr = (prop: 'width' | 'height', value: string | number) => {
if (!isUnsetKeyword(value)) {
attributes[prop] = value.toString();
}
};
setAttr('width', width);
setAttr('height', height);
attributes.viewBox =
box.left.toString() +
' ' +
box.top.toString() +
' ' +
boxWidth.toString() +
' ' +
boxHeight.toString();
return {
attributes,
body,
};
return result;
}

View File

@ -86,4 +86,40 @@ describe('Testing loadIcon with @iconify-json/flat-color-icons>', () => {
expect(result && result.includes('width="1.2em"')).toBeTruthy();
expect(result && result.includes('height="1.2em"')).toBeTruthy();
});
test('loadIcon with unset width', async () => {
const result = await loadNodeIcon('flat-color-icons', 'up-right', {
customizations: {
additionalProps: {
width: 'unset',
},
},
});
expect(result).toBeTruthy();
expect(result && result.includes('width="')).toBeFalsy();
expect(result && result.includes('height="1em"')).toBeTruthy();
});
test('loadIcon with 0 scale', async () => {
const result = await loadNodeIcon('flat-color-icons', 'up-right', {
scale: 0,
});
expect(result).toBeTruthy();
expect(result && result.includes('width="')).toBeFalsy();
expect(result && result.includes('height="')).toBeFalsy();
});
test('loadIcon with 0 scale and custom height', async () => {
const result = await loadNodeIcon('flat-color-icons', 'up-right', {
scale: 0,
customizations: {
additionalProps: {
height: '1em',
},
},
});
expect(result).toBeTruthy();
expect(result && result.includes('width="')).toBeFalsy();
expect(result && result.includes('height="1em"')).toBeTruthy();
});
});

View File

@ -99,6 +99,51 @@ describe('Testing iconToSVG', () => {
expect(result).toEqual(expected);
});
test('Unset height', () => {
const custom: IconifyIconCustomisations = {
// Testing 'unset' keyword
height: 'unset',
width: 'auto',
};
const icon: IconifyIcon = {
width: 20,
height: 16,
body: '<path d="..." />',
};
const expected: IconifyIconBuildResult = {
attributes: {
width: '20',
viewBox: '0 0 20 16',
},
body: '<path d="..." />',
};
const result = iconToSVG(icon, custom);
expect(result).toEqual(expected);
});
test('Unset size', () => {
const custom: IconifyIconCustomisations = {
// Testing 'undefined' and 'none' keywords
width: 'undefined',
height: 'none',
};
const icon: IconifyIcon = {
width: 20,
height: 16,
body: '<path d="..." />',
};
const expected: IconifyIconBuildResult = {
attributes: {
viewBox: '0 0 20 16',
},
body: '<path d="..." />',
};
const result = iconToSVG(icon, custom);
expect(result).toEqual(expected);
});
test('Rotation', () => {
const custom: IconifyIconCustomisations = {
height: '40px',