@reusable-ui/icon
Version:
An icon set component for React app.
195 lines (194 loc) • 6.57 kB
JavaScript
// cssfn:
import {
// writes css in javascript:
rule, fallback, fontFace, children, style,
// strongly typed of css variables:
switchOf,
// reads/writes css variables configuration:
usesCssProps,
// writes complex stylesheets in simpler way:
watchChanges, memoizeStyle, } from '@cssfn/core'; // writes css in javascript
// reusable-ui core:
import {
// common layouts:
fillTextLineHeightLayout,
// size options of UI:
usesResizable,
// color options of UI:
usesThemeable,
// colorize the UI based on its theme or the background theme:
usesColorable, } from '@reusable-ui/core'; // a set of reusable-ui packages which are responsible for building any component
// internals:
import {
// configs:
icons, cssIconConfig, } from './config.js';
import {
// configs:
iconConfig, } from '../config.js';
import { usesIcon, } from '../features/icon.js';
import { sizeOptions, } from '../variants/resizable.js';
import {
// utilities:
concatUrl, formatOf, } from '../utilities.js';
// styles:
export const onIconStylesChange = watchChanges(cssIconConfig.onChange);
export const usesIconLayout = memoizeStyle(() => {
// dependencies:
// variants:
const { colorableVars } = usesColorable();
// features:
const { iconRule, iconVars } = usesIcon({
size: icons.size,
color: colorableVars.color,
});
return style({
// layouts:
...style({
// layouts:
display: 'inline-flex',
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
flexWrap: 'nowrap',
// positions:
verticalAlign: 'baseline',
// sizes:
blockSize: iconVars.size,
aspectRatio: switchOf(// set background_image's aspect_ratio
iconVars.ratio, 'initial'),
// children:
...children('::before', {
// layouts:
...fillTextLineHeightLayout(),
}),
// customize:
...usesCssProps(icons),
color: null, // delete color prop from config, we use color prop in special way
}),
// features:
...iconRule(), // must be placed at the last
});
}, onIconStylesChange);
export const usesIconFontLayout = (options) => {
// dependencies:
// features:
const { iconVars } = usesIcon();
return style({
// load a custom_font:
...fontFace({
// fonts:
...iconConfig.font.style,
...style({
src: [
...iconConfig.font.files
.map((file) => [
`url("${concatUrl(iconConfig.font.path, file)}")`,
formatOf(file) ?? '',
]),
],
}),
}),
// children:
...children(options?.iconFontElement ?? '&', {
// fonts:
// use the loaded custom_font:
...iconConfig.font.style,
// layouts:
...style({
// layouts:
...fallback({
content: iconVars.image,
display: 'inline', // use inline, so it takes the width & height automatically
}),
// sizes:
fontSize: iconVars.size,
// overflowY : 'hidden', // a hack: hides the pseudo-inherited underline
overflow: 'hidden',
...fallback({
blockSize: 'inherit',
aspectRatio: 'inherit', // follows <parent>'s aspect_ratio
}),
// accessibilities:
userSelect: 'none',
// backgrounds:
backg: 'transparent',
// foregrounds:
foreg: iconVars.color,
// animations:
...fallback({
transition: 'inherit', // inherit transition for smooth sizing changes
}),
// typos:
lineHeight: 1,
textTransform: 'none',
letterSpacing: 'normal',
overflowWrap: 'normal',
whiteSpace: 'nowrap',
direction: 'ltr',
//#region turn on available browser features
WebkitFontSmoothing: 'antialiased',
textRendering: 'optimizeLegibility',
MozOsxFontSmoothing: 'grayscale',
fontFeatureSettings: 'liga', // support for IE
//#endregion turn on available browser features
}),
}),
});
};
export const usesIconImageLayout = memoizeStyle(() => {
// dependencies:
// features:
const { iconVars } = usesIcon();
// layouts:
return style({
// appearances:
maskSize: 'contain',
WebkitMaskSize: 'contain',
maskRepeat: 'no-repeat',
WebkitMaskRepeat: 'no-repeat',
maskPosition: 'center',
WebkitMaskPosition: 'center',
maskImage: iconVars.image,
WebkitMaskImage: iconVars.image,
// backgrounds:
backg: iconVars.color, // set icon's color
});
});
export const usesIconVariants = memoizeStyle(() => {
// dependencies:
// variants:
const { resizableRule } = usesResizable(icons, sizeOptions());
const { themeableRule } = usesThemeable();
const { colorableRule } = usesColorable(icons, /* outlinedDefinition = not_supported */ null, /* mildDefinition = is_supported */ undefined);
return style({
// variants:
...resizableRule(),
...themeableRule(),
...colorableRule(),
});
}, onIconStylesChange);
export const usesIconImage = (config) => {
// dependencies:
// features:
const { iconRule } = usesIcon(config);
return style({
// layouts:
...usesIconImageLayout(),
// features:
...iconRule(), // must be placed at the last
});
};
export default memoizeStyle(() => style({
// layouts:
...usesIconLayout(),
...rule('.font', {
// layouts:
...usesIconFontLayout({ iconFontElement: '::after' }),
}),
...rule('.image', {
// layouts:
...usesIconImageLayout(),
}),
// variants:
...usesIconVariants(),
}), onIconStylesChange);