|
/** |
|
* Create and maintain classes with [BEM](http://getbem.com/). |
|
* @since Feature |
|
*/ |
|
export default class BEM { |
|
private _block: string; |
|
private elementArray: string[]; |
|
private modifierArray: string[]; |
|
private _record: { [element: string]: string[] }; |
|
|
|
/** |
|
* Input the block name. |
|
* @param {string} block - The block name. |
|
*/ |
|
constructor(block: string) { |
|
this._block = BEM.convertStringIntoKebabCase(block); |
|
this.elementArray = []; |
|
this.modifierArray = []; |
|
this._record = {}; |
|
} |
|
|
|
get block() { |
|
return this._block; |
|
} |
|
|
|
get element() { |
|
return BEM.excludeDuplicatedFromArray(this.elementArray); |
|
} |
|
|
|
get modifier() { |
|
return BEM.excludeDuplicatedFromArray(this.modifierArray); |
|
} |
|
|
|
get infoElement() { |
|
return BEM.countTimesForEachArrayMember(this.elementArray); |
|
} |
|
|
|
get infoModifier() { |
|
return BEM.countTimesForEachArrayMember(this.modifierArray); |
|
} |
|
|
|
get record() { |
|
const handledRecord: { [key: string]: string[] } = {}; |
|
Object.entries(this._record).forEach(([element, modifierArray]) => { |
|
handledRecord[element] = BEM.excludeDuplicatedFromArray(modifierArray); |
|
}); |
|
|
|
return handledRecord; |
|
} |
|
|
|
/** |
|
* Convert a string into `kebab-case`. |
|
* @param {string} input - The string. |
|
* @return {string} The input in `kebab-case` format. |
|
* @see See {@link https://stackoverflow.com/a/63116134/13564651 StackOverFlow} for the origin of this method. |
|
*/ |
|
static convertStringIntoKebabCase(input: string): string { |
|
return input |
|
.split('') |
|
.map((letter, index) => { |
|
return letter.toUpperCase() === letter ? `${index !== 0 ? '-' : ''}${letter.toLowerCase()}` : letter; |
|
}) |
|
.join(''); |
|
} |
|
|
|
/** |
|
* Exclude duplicated value from a string array. |
|
* @param {string[]} inputArray - A string array. |
|
* @return {string[]} A string array without duplicated value. |
|
*/ |
|
static excludeDuplicatedFromArray(inputArray: string[]): string[] { |
|
return inputArray.filter((element, index) => inputArray.indexOf(element) === index); |
|
} |
|
|
|
/** |
|
* Count each shown times for the array members. |
|
* @param {string[]} inputArray - A string array. |
|
* @return {Record<string, number>} An object with key from the array members and value for their shown times. |
|
*/ |
|
static countTimesForEachArrayMember(inputArray: string[]): Record<string, number> { |
|
return inputArray.reduce((accumulator, currentValue) => { |
|
accumulator[currentValue] = (accumulator[currentValue] ?? 0) + 1; |
|
return accumulator; |
|
}, {} as Record<string, number>); |
|
} |
|
|
|
/** |
|
* Create a valid `className` with BEM standard. |
|
* @param {string[]} element - Element name list. |
|
* @param {string} modifier - Modifier name. Optional. |
|
* @return {string} A valid `className` with BEM standard.. |
|
*/ |
|
create(elementList: string[], modifier?: string): string { |
|
elementList.forEach((element) => { |
|
this.elementArray.push(element); |
|
|
|
if (!this._record[element]) { |
|
this._record[element] = []; |
|
} |
|
if (modifier) { |
|
elementList.forEach((element) => { |
|
this._record[element].push(modifier); |
|
}); |
|
} |
|
}); |
|
|
|
if (modifier) { |
|
this.modifierArray.push(modifier); |
|
} |
|
|
|
let elementFull = ''; |
|
|
|
return `${this._block}${elementList |
|
.map((element) => { |
|
elementFull = elementFull + `__${element}`; |
|
return `__${element}`; |
|
}) |
|
.join('')}${modifier ? ` ${this._block}${elementFull}--${modifier}` : ''}`; |
|
} |
|
} |