UNPKG

@bem/sdk.cell

Version:

Representation of identifier of a part of BEM entity.

335 lines (302 loc) 11.1 kB
'use strict'; const assert = require('assert'); const util = require('util'); const deprecate = require('depd')(require('./package.json').name); const BemEntityName = require('@bem/sdk.entity-name'); /** * Bem mod representation * * @typedef {Object} BemMod - the modifier of entity. * @property {string} name - the modifier name of entity. * @property {string} [val] - the modifier value of entity. */ /** * Bem cell * * @type {module.BemCell} */ module.exports = class BemCell { /** * @param {Object} obj — representation of cell. * @param {BemEntityName} obj.entity — representation of entity name. * @param {String} [obj.tech] - tech of cell. * @param {String} [obj.layer] - layer of cell. */ constructor(obj) { assert(obj && obj.entity, 'Required `entity` field'); assert(BemEntityName.isBemEntityName(obj.entity), 'The `entity` field should be an instance of BemEntityName'); this._entity = obj.entity; this._layer = obj.layer; this._tech = obj.tech; this.__isBemCell__ = true; } /** * Returns the name of entity. * * @example * const BemCell = require('@bem/sdk.cell'); * const BemEntityName = require('@bem/sdk.entity-name'); * * const cell = new BemCell({ * entity: new BemEntityName({ block: 'button', elem: 'text' }) * }); * * cell.entity; // ➜ BemEntityName { block: 'button', elem: 'text' } * * @returns {BemEntityName} name of entity. */ get entity() { return this._entity; } /** * Returns the tech of cell. * * @example * const BemCell = require('@bem/sdk.cell'); * const BemEntityName = require('@bem/sdk.entity-name'); * * const cell = new BemCell({ * entity: new BemEntityName({ block: 'button', elem: 'text' }), * tech: 'css' * }); * * cell.tech; // ➜ css * * @returns {String} tech of cell. */ get tech() { return this._tech; } /** * Returns the layer of this cell. * * @example * const BemCell = require('@bem/sdk.cell'); * const BemEntityName = require('@bem/sdk.entity-name'); * * const cell = new BemCell({ * entity: new BemEntityName({ block: 'button', elem: 'text' }), * layer: 'desktop' * }); * * cell.layer; // ➜ desktop * * @returns {String} layer of cell. */ get layer() { return this._layer; } /** * Proxies `block` field from entity. * * @returns {String} */ get block() { return this._entity.block; } /** * Proxies `elem` field from entity. * * @returns {String|undefined} */ get elem() { return this._entity.elem; } /** * Proxies `mod` field from entity. * * @returns {Object|undefined} - field with `name` and `val` */ get mod() { return this._entity.mod; } /** * Proxies `modVal` field from entity. * * @deprecated - just for compatibility. Use {@link BemCell#mod.name} * @returns {String|undefined} - modifier name */ get modName() { deprecate('modName: just for compatibility and can be dropped in future. Instead use \'mod.name\''); return this._entity.mod && this._entity.mod.name; } /** * Proxies `modVal` field from entity. * * @deprecated - just for compatibility. Use {@link BemCell#mod.val} * @returns {String|true|undefined} - modifier value */ get modVal() { deprecate('modVal: just for compatibility and can be dropped in future. Instead use \'mod.val\''); return this._entity.mod && this._entity.mod.val; } /** * Returns the identifier of this cell. * * Important: should only be used to determine uniqueness of cell. * * @example * const BemCell = require('@bem/sdk.cell'); * const BemEntityName = require('@bem/sdk.entity-name'); * * const cell = new BemCell({ * entity: new BemEntityName({ block: 'button', elem: 'text' }), * tech: 'css', * layer: 'desktop' * }); * * cell.id; // ➜ "button__text@desktop.css" * * @returns {String} identifier of cell. */ get id() { if (this._id) { return this._id; } const layer = this._layer ? `@${this._layer}` : ''; const tech = this._tech ? `.${this._tech}` : ''; this._id = `${this._entity}${layer}${tech}`; return this._id; } /** * Returns string representing the bem cell. * * Important: If you want to get string representation in accordance with the provisions naming convention * you should use `@bem/sdk.naming` package. * * @example * const BemCell = require('@bem/sdk.cell'); * const BemEntityName§ = require('@bem/sdk.entity-name'); * const cell = new BemCell({ entity: new BemEntityName({ block: 'button', mod: 'focused' }), * tech: 'css', layer: 'desktop' }); * * cell.toString(); // button_focused@desktop.css * * @returns {String} */ toString() { return this.id; } /** * Returns object representing the bem cell. Is needed for debug in Node.js. * * In some browsers `console.log()` calls `valueOf()` on each argument. * This method will be called to get custom string representation of the object. * * The representation object contains only `entity`, `tech` and `layer` * without private and deprecated fields (`modName` and `modVal`). * * @example * const BemCell = require('@bem/sdk.cell'); * const BemEntityName = require('@bem/sdk.entity-name'); * const cell = new BemCell({ entity: new BemEntityName({ block: 'button', mod: 'focused' }), * tech: 'css', layer: 'desktop' }); * * cell.valueOf(); * * // ➜ { entity: { block: 'button', mod: { name: 'focused', value: true } }, * // tech: 'css', * // layer: 'desktop' } * * @returns {{ entity: {block: String, elem: ?String, mod: ?{name: String, val: *}}, tech: *, layer: *}} */ valueOf() { const res = { entity: this._entity.valueOf() }; this._tech && (res.tech = this._tech); this._layer && (res.layer = this._layer); return res; } /** * Returns object representing the bem cell. Is needed for debug in Node.js. * * In Node.js, `console.log()` calls `util.inspect()` on each argument without a formatting placeholder. * This method will be called to get custom string representation of the object. * * The representation object contains only `entity`, `tech` and `layer` fields * without private fields. * * @example * const BemCell = require('@bem/sdk.cell'); * const BemEntityName = require('@bem/sdk.entity-name'); * const cell = new BemCell({ entity: new BemEntityName({ block: 'button' }), tech: 'css', layer: 'desktop' }); * * console.log(cell); // BemCell { entity: { block: 'button' }, tech: 'css', layer: 'desktop' } * * @param {Number} depth — tells inspect how many times to recurse while formatting the object. * @param {Object} [options] — An optional `options` object may be passed * that alters certain aspects of the formatted string. * @returns {String} */ inspect(depth, options) { const stringRepresentation = util.inspect(this.valueOf(), options); return `BemCell ${stringRepresentation}`; } /** * Return raw data for `JSON.stringify()`. * * @returns {{ entity: {block: String, elem: ?String, mod: ?{name: String, val: *}}, tech: *, layer: *}} */ toJSON() { return this.valueOf(); } /** * Determines whether specified cell is deep equal to cell or not * * @example * const BemCell = require('@bem/sdk.cell'); * const buttonCell1 = BemCell.create({ block: 'button', tech: 'css', layer: 'desktop' }); * const buttonCell2 = BemCell.create({ block: 'button', tech: 'css', layer: 'desktop' }); * const inputCell = BemCell.create({ block: 'input', tech: 'css', layer: 'common' }); * * buttonCell1.isEqual(buttonCell2); // true * buttonCell1.isEqual(inputCell); // false * * @param {BemCell} cell - the cell to compare * @returns {Boolean} */ isEqual(cell) { return (cell.tech === this.tech) && (cell.layer === this.layer) && cell.entity.isEqual(this.entity); } /** * Determines whether specified cell is instance of BemCell. * * @example * const BemCell = require('@bem/sdk.cell'); * const BemEntityName = require('@bem/sdk.entity-name'); * * const cell = new BemCell({ * entity: new BemEntityName({ block: 'button', elem: 'text' }) * }); * * BemCell.isBemCell(cell); // true * BemCell.isBemCell({}); // false * * @param {(BemCell|*)} cell - the cell to check. * @returns {boolean} A Boolean indicating whether or not specified entity is instance of BemCell. */ static isBemCell(cell) { const C = cell && cell.constructor; return C === this || Boolean(C && cell.__isBemCell__ && C !== Object); } /** * Creates BemCell instance by any object representation. * * @example * const BemCell = require('@bem/sdk.cell'); * * BemCell.create({ block: 'my-button', mod: 'theme', val: 'red', tech: 'css' }); * BemCell.create({ block: 'my-button', modName: 'theme', modVal: 'red', tech: 'css' }); * BemCell.create({ entity: { block: 'my-button', modName: 'theme', modVal: 'red' }, tech: 'css' }); * // BemCell { block: 'my-button', mod: { name: 'theme', val: 'red' }, tech: 'css' } * * @param {Object} obj — representation of cell. * @param {string} obj.block — the block name of entity. * @param {string} [obj.elem] — the element name of entity. * @param {BemMod|string} [obj.mod] — the modifier of entity. * @param {string} [obj.val] — The modifier value of entity. Used if `mod` is a string. * @param {string} [obj.modName] — the modifier name of entity. Used if `mod.name` wasn't specified. * @param {string} [obj.modVal] — the modifier value of entity. Used if neither `mod.val` nor `val` were not specified. * @param {string} [obj.tech] — technology of cell. * @param {string} [obj.layer] — layer of cell. * @returns {BemCell} An object representing cell. */ static create(obj) { if (BemEntityName.isBemEntityName(obj)) { return new BemCell({ entity: obj }); } if (BemCell.isBemCell(obj)) { return obj; } const data = {}; data.entity = BemEntityName.create(obj.entity || obj); obj.tech && (data.tech = obj.tech); obj.layer && (data.layer = obj.layer); return new BemCell(data); } };