@reusable-ui/icon
Version:
An icon set component for React app.
85 lines (84 loc) • 3.35 kB
JavaScript
// react:
import {
// hooks:
useMemo, } from 'react';
// cssfn:
import {
// writes css in javascript:
style, vars, cssVars, } from '@cssfn/core'; // writes css in javascript
// internals:
import { iconConfig, } from '../config.js';
import { concatUrl, } from '../utilities.js';
const [iconVars] = cssVars({ prefix: 'icon', minify: false }); // shared variables: ensures the server-side & client-side have the same generated css variable names
/**
* Uses icon image and icon color.
* @param config A configuration of `iconRule`.
* @returns A `IconStuff` represents the icon rules.
*/
export const usesIcon = (config) => {
return {
iconRule: () => style({
...vars({
// appearances:
[iconVars.image]: config?.image,
// sizes:
[iconVars.size]: config?.size,
// backgrounds:
[iconVars.color]: config?.color,
}),
}),
iconVars,
};
};
const getFileNameWithoutExtension = (file) => {
if (!file)
return null;
const fileName = (typeof (file) === 'string') ? file : file.name;
const lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex < 0)
return fileName; // extension is not found => it's a pure fileName without extension
return fileName.slice(0, lastDotIndex);
};
export const useIcon = ({ icon }) => {
return useMemo(() => {
// dependencies:
// features:
const { iconVars } = usesIcon();
const [iconImage, iconRatio] = (() => {
const file = iconConfig.image.files.find((file) => getFileNameWithoutExtension(file) === icon);
if (!file)
return [null, null];
return [
concatUrl(iconConfig.image.path, (typeof (file) === 'string') ? file : file.name),
(typeof (file) === 'string') ? null : (file.ratio ?? null)
];
})();
const isIconFont = !iconImage; // && iconConfig.font.items.includes(icon); // assumes the user use TypeScript for validating the font name
// memorized a whole object:
return {
class: (() => {
if (iconImage)
return 'image'; // icon name is found in iconImage
if (isIconFont)
return 'font'; // icon name is found in iconFont
return null; // icon name is not found in both iconImage & iconFont
})(),
style: {
// appearances:
[iconVars.image
.slice(4, -1) // fix: var(--customProp) => --customProp
]: (() => {
if (iconImage)
return `url("${iconImage}")`; // the url of the icon's image
if (isIconFont)
return `"${icon}"`; // the icon's name
return undefined; // icon name is not found in both iconImage & iconFont
})(),
[iconVars.ratio
.slice(4, -1) // fix: var(--customProp) => --customProp
]: iconRatio ?? '1/1', // defaults to '1/1' if the ratio is not defined
},
};
}, [icon]);
};
//#endregion icon