2
0
mirror of https://github.com/iconify/iconify.git synced 2025-01-08 15:54:09 +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 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 { getIconData } from '../icon-set/get-icon';
import { mergeIconProps } from './utils'; import { mergeIconProps } from './utils';
import createDebugger from 'debug'; import createDebugger from 'debug';
@ -39,26 +39,40 @@ export async function searchForIcon(
return { ...restAttributes }; return { ...restAttributes };
}, },
(props) => { (props) => {
if ( // Check if value has 'unset' keyword
typeof props.width === 'undefined' || const check = (
props.width === null 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') { if (typeof scale === 'number') {
props.width = `${scale}em`; // Scale icon, unless scale is 0
if (scale) {
value = `${scale}em`;
}
} else { } else {
props.width = width; // Use result from iconToSVG()
value = defaultValue;
} }
} }
if (
typeof props.height === 'undefined' || // Change / unset
props.height === null if (!value) {
) { delete props[prop];
if (typeof scale === 'number') {
props.height = `${scale}em`;
} else { } 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 type { Awaitable } from '@antfu/utils';
import { isUnsetKeyword } from '../svg/build';
import type { IconifyLoaderOptions } from './types'; import type { IconifyLoaderOptions } from './types';
const svgWidthRegex = /width\s*=\s*["'](\w+)["']/; const svgWidthRegex = /width\s*=\s*["'](\w+)["']/;
@ -12,27 +13,40 @@ function configureSvgSize(
): [boolean, boolean] { ): [boolean, boolean] {
const svgNode = svg.slice(0, svg.indexOf('>')); const svgNode = svg.slice(0, svg.indexOf('>'));
let result = svgWidthRegex.exec(svgNode); const check = (prop: 'width' | 'height', regex: RegExp): boolean => {
const result = regex.exec(svgNode);
const w = result != null; const w = result != null;
if (typeof props.width === 'undefined' || props.width === null) {
const propValue = props[prop];
let value: string | undefined;
if (!isUnsetKeyword(propValue)) {
if (propValue) {
// Do not change it
return w;
}
if (typeof scale === 'number') { if (typeof scale === 'number') {
props.width = `${scale}em`; // Scale icon, unless scale is 0
if (scale) {
value = `${scale}em`;
}
} else if (result) { } else if (result) {
props.width = result[1]; // Use result from iconToSVG()
value = result[1];
} }
} }
result = svgHeightRegex.exec(svgNode); // Change / unset
const h = result != null; if (!value) {
if (typeof props.height === 'undefined' || props.height === null) { delete props[prop];
if (typeof scale === 'number') { return false;
props.height = `${scale}em`;
} else if (result) {
props.height = result[1];
}
} }
props[prop] = value;
return true;
};
return [w, h]; return [check('width', svgWidthRegex), check('height', svgHeightRegex)];
} }
export async function mergeIconProps( export async function mergeIconProps(

View File

@ -11,8 +11,8 @@ import { calculateSize } from './size';
export interface IconifyIconBuildResult { export interface IconifyIconBuildResult {
attributes: { attributes: {
// Attributes for <svg> // Attributes for <svg>
width: string; width?: string;
height: string; height?: string;
viewBox: string; viewBox: string;
}; };
@ -30,6 +30,12 @@ interface ViewBox {
height: number; 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 * Get SVG attributes and content from icon + customisations
* *
@ -194,22 +200,28 @@ export function iconToSVG(
: customisationsHeight; : customisationsHeight;
} }
// Result // Attributes for result
const result: IconifyIconBuildResult = { const attributes = {} as IconifyIconBuildResult['attributes'];
attributes: {
width: width.toString(), const setAttr = (prop: 'width' | 'height', value: string | number) => {
height: height.toString(), if (!isUnsetKeyword(value)) {
viewBox: attributes[prop] = value.toString();
}
};
setAttr('width', width);
setAttr('height', height);
attributes.viewBox =
box.left.toString() + box.left.toString() +
' ' + ' ' +
box.top.toString() + box.top.toString() +
' ' + ' ' +
boxWidth.toString() + boxWidth.toString() +
' ' + ' ' +
boxHeight.toString(), boxHeight.toString();
},
return {
attributes,
body, 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('width="1.2em"')).toBeTruthy();
expect(result && result.includes('height="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); 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', () => { test('Rotation', () => {
const custom: IconifyIconCustomisations = { const custom: IconifyIconCustomisations = {
height: '40px', height: '40px',