@exadel/esl
Version:
Exadel Smart Library (ESL) is the lightweight custom elements library that provide a set of super-flexible components
134 lines (133 loc) • 5.75 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var ESLMediaRuleList_1;
import { ExportNs } from '../../esl-utils/environment/export-ns';
import { parseObjectSafe } from '../../esl-utils/misc/format';
import { deepMerge, isEqual } from '../../esl-utils/misc/object';
import { SyntheticEventTarget } from '../../esl-utils/dom/events';
import { ESLMediaRule } from './esl-media-rule';
// keep a named guard for strict narrowing
const isDefinedRule = (rule) => !!rule;
/** A custom event dispatched by {@link ESLMediaRuleList} instances */
export class ESLMediaRuleListEvent extends Event {
constructor(current, previous) {
super(ESLMediaRuleListEvent.TYPE);
Object.assign(this, { current, previous });
}
}
ESLMediaRuleListEvent.TYPE = 'change';
// Singleton empty rule list placeholder
let empty;
/**
* ESLMediaRuleList - {@link ESLMediaRule} observable collection
* @author Yuliya Adamskaya, Alexey Stsefanovich (ala'n)
*
* Represents observable object that wraps environment to value mapping
*/
let ESLMediaRuleList = ESLMediaRuleList_1 = class ESLMediaRuleList extends SyntheticEventTarget {
/** @returns empty {@link ESLMediaRuleList} instance */
static empty() {
if (!empty)
empty = new ESLMediaRuleList_1([]);
return empty;
}
static parse(query, ...common) {
const parser = typeof common[common.length - 1] === 'function' ? common.pop() : String;
const mask = common.pop();
if (typeof mask !== 'string' || query.includes('=>') || !query.includes('|')) {
return ESLMediaRuleList_1.parseQuery(query, parser);
}
return ESLMediaRuleList_1.parseTuple(mask, query, parser);
}
static parseQuery(query, parser = String) {
const rules = query.split('|')
.map((lex) => ESLMediaRule.parse(lex, parser))
.filter(isDefinedRule);
return new ESLMediaRuleList_1(rules);
}
static parseTuple(mask, values, parser = String) {
const queries = mask.split('|');
const valueList = values.split('|');
while (valueList.length < queries.length && valueList.length !== 0)
valueList.push(valueList[valueList.length - 1]);
if (valueList.length !== queries.length)
throw new TypeError(`tuple "${values}" doesn't correspond to mask "${mask}"`);
const rules = queries.map((query, i) => ESLMediaRule.create(valueList[i], query, parser));
const validRules = rules.filter(isDefinedRule);
return new ESLMediaRuleList_1(validRules);
}
constructor(rules) {
super();
this._rules = rules;
this._onMatchChanged = this._onMatchChanged.bind(this);
}
addEventListener(type, callback = type) {
super.addEventListener(type, callback);
if (this.getEventListeners('change').length > 1)
return;
this._value = this.computedValue;
this.rules.forEach((rule) => rule.addEventListener(this._onMatchChanged));
}
removeEventListener(type, callback = type) {
super.removeEventListener(type, callback);
if (this.hasEventListener())
return;
delete this._value;
this.rules.forEach((rule) => rule.removeEventListener(this._onMatchChanged));
}
/** Array of {@link ESLMediaRule}s that forms the current {@link ESLMediaRuleList} */
get rules() {
return this._rules;
}
/** All active {@link ESLMediaRule}s */
get active() {
return this.rules.filter((rule) => rule.matches);
}
/** Value of the last of active rules */
get activeValue() {
var _a;
return (_a = this.active.pop()) === null || _a === void 0 ? void 0 : _a.payload;
}
/** All active rule values */
get activeValues() {
return this.active.map((rule) => rule.payload);
}
/**
* Current value of {@link ESLMediaRuleList} object
* Uses cache if current object is under observation
*/
get value() {
if (!this.hasEventListener())
return this.computedValue;
return Object.hasOwnProperty.call(this, '_value') ? this._value : this.computedValue;
}
/** Always computed value of the current {@link ESLMediaRuleList} object */
get computedValue() {
return deepMerge(undefined, ...this.activeValues);
}
/** Handles inner rules state change */
_onMatchChanged() {
const curValue = this.value;
const newValue = this.computedValue;
if (isEqual(curValue, newValue))
return;
this._value = newValue;
this.dispatchEvent(new ESLMediaRuleListEvent(newValue, curValue));
}
/** @returns serialized {@link ESLMediaRuleList} object representation*/
toString() {
return this.rules.join(' | ');
}
};
/** String value parser (default) */
ESLMediaRuleList.STRING_PARSER = String;
/** Object value parser. Uses {@link parseObjectSafe} to parse value */
ESLMediaRuleList.OBJECT_PARSER = (val) => parseObjectSafe(val, () => console.warn(`[ESL]: Cannot parse object value "${val}"`));
ESLMediaRuleList = ESLMediaRuleList_1 = __decorate([
ExportNs('MediaRuleList')
], ESLMediaRuleList);
export { ESLMediaRuleList };