interface Options { block: string blockSuffix?: string element?: string modifier?: string } function _bem(options: Options) { const { block, blockSuffix, element, modifier } = options let cls = block if (blockSuffix) cls += `-${blockSuffix}` if (element) cls += `__${element}` if (modifier) cls += `--${modifier}` return cls } function isTruthy(value: unknown): boolean { if (Array.isArray(value)) { return value.length > 0 } else if (typeof value === 'object' && value !== null) { return Object.keys(value).length > 0 } return !!value } export default (block: string) => { const b = (blockSuffix?: string) => _bem({ block, blockSuffix }) const e = (element: string) => _bem({ block, element }) const m = (modifier: string) => _bem({ block, modifier }) const be = (blockSuffix: string, element: string) => _bem({ block, blockSuffix, element }) const em = (element: string, modifier: string) => _bem({ block, element, modifier }) const bm = (blockSuffix: string, modifier: string) => _bem({ block, blockSuffix, modifier }) const bem = (blockSuffix: string, element: string, modifier: string) => _bem({ block, blockSuffix, element, modifier }) const is = (name: string, state: unknown) => isTruthy(state) ? `is-${name}` : '' const has = (name: string, state: unknown) => isTruthy(state) ? `has-${name}` : '' const exp = (condition: boolean, trueState: string, falseState: string = '') => condition ? trueState : falseState return { b, e, m, be, em, bm, bem, is, has, exp, } }