UNPKG

@amaui/style

Version:
395 lines (394 loc) 17 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const copy_1 = __importDefault(require("@amaui/utils/copy")); const hash_1 = __importDefault(require("@amaui/utils/hash")); const isEnvironment_1 = __importDefault(require("@amaui/utils/isEnvironment")); const getEnvironment_1 = __importDefault(require("@amaui/utils/getEnvironment")); const merge_1 = __importDefault(require("@amaui/utils/merge")); const AmauiStyleRule_1 = __importDefault(require("./AmauiStyleRule")); const utils_1 = require("./utils"); const env = (0, getEnvironment_1.default)(); const optionsDefault = { style: { attributes: {}, }, rule: { sort: true, prefix: true, rtl: true, } }; class AmauiStyleSheet { constructor(value, options = (0, copy_1.default)(optionsDefault)) { this.value = value; this.options = options; this.version = 'static'; this.mode = 'regular'; this.pure = false; this.priority = 'upper'; this.status = 'idle'; this.props_ = {}; this.values = { css: '', }; this.rules = []; this.names = { classNames: {}, classes: {}, keyframes: {}, styles: (...args) => { const value = []; args.forEach(arg => { if (this.names.classes[arg]) value.push(this.names.classes[arg]); }); return value.join(' '); }, }; this.options = (0, merge_1.default)(options, optionsDefault, { copy: true }); this.init(); } get props() { return this.props_; } set props(props) { if (this.propsAreNew(props)) { this.props_ = (0, copy_1.default)(props); // Update if new props are set this.updateProps(); } } get response() { // Response this.values.css = ``; this.rules.filter(rule => !rule.value.ref).forEach(rule => { const css = rule.value.css; if (css) { this.values.css += `\n${css}\n`; } }); return this.values; } get css() { return this.response.css; } get sort() { // Sort // Native sort based on levels first // for making, updating & refs this.rules.sort((a, b) => { if (a.value.level === b.value.level) return 0; return a.value.level < b.value.level ? -1 : 1; }); // and then based on pure // pure rules have lower priority, are on top // more specific rules are on bottom have higher specificity this.rules.sort((a, b) => { if (a.value.pure && !b.value.pure) return -1; if (b.value.pure && !a.value.pure) return 1; return 0; }); return this.rules; } init() { var _a; // Options this.version = this.options.version || this.version; this.mode = this.options.mode || this.mode; this.pure = this.options.pure || this.pure; this.priority = this.options.priority || this.priority; this.amauiTheme = this.options.amauiTheme; this.amauiStyleSheetManager = this.options.amauiStyleSheetManager; this.amauiStyle = this.options.amauiStyle; this.props = this.options.props; this.id = (0, utils_1.getID)(); // Inherits first from amauiStyle this.mode = this.amauiStyle.mode || this.mode; // Reset rules this.rules = []; // If value is an object if ((0, utils_1.is)('object', this.value)) { // Sort all properties so at-rules are at the start (ie. @keyframes for animation makeSelector value) // but @media rules go at the bottom const props = Object.keys(this.value).sort((a, b) => { if (a.indexOf('@keyframes') > -1) return -1; if (b.indexOf('@') > -1) return 1; return 0; }); const ignore = ['@pure', '@p']; // Make an AmauiStyleRule for all lvl 0 props const propsAll = props .filter(prop => ignore.indexOf(prop) === -1) .flatMap(prop => (0, utils_1.is)('array', this.value[prop]) ? this.value[prop].map((item) => ({ property: prop, value: item })) : { property: prop, value: this.value[prop] }); propsAll.forEach((item, index) => this.makeRule(item.property, item.value, { index })); // Pure const pure = Object.assign(Object.assign({}, (this.value['@p'] || {})), (this.value['@pure'] || {})); const pureAll = Object.keys(pure).flatMap(prop => ({ property: prop, value: pure[prop] })); pureAll.forEach((item, index) => this.makeRule(item.property, item.value, { index: props.length + index, pure: true })); // Sort this.sort; // Make selectors // on init so they are // available on init in node for // critical css extraction this.rules.forEach(rule => { // Update values rule.value.updateValues(false); // Update owned rules css // to use for allCss and hash value rule.value.rules_owned.filter(rule_ => rule_ instanceof AmauiStyleRule_1.default).forEach(rule_ => rule_.updateValues()); // Make selectors rule.value.makeSelector(); }); } // Add to amauiStyle and amauiStyleSheetManager if (this.amauiStyleSheetManager) (_a = this.amauiStyleSheetManager.sheets[this.version]) === null || _a === void 0 ? void 0 : _a.push(this); if (this.amauiStyleSheetManager && this.amauiStyleSheetManager.options.amaui_style_cache) this.amauiStyle.sheets.push(this); // Update inited status this.status = 'inited'; } addRule(value, property_, add = true) { const isDynamic = (0, utils_1.dynamic)(value); if (value !== undefined && ((this.version === 'static' && !isDynamic) || (this.version === 'dynamic' && isDynamic))) { let property = property_ !== undefined ? property_ : env.amaui_methods.makeName.next().value; const props = ((0, utils_1.is)('object', this.value) && Object.keys(this.value)) || []; if (!!props.length) while (props.indexOf(property) > -1) property = env.amaui_methods.makeName.next().value; const isPure = ['@pure', '@p']; if ((isPure.indexOf(property) > -1 && (0, utils_1.is)('object', value))) { Object.keys(value).forEach(item => this.makeRule(item, value[item])); } else { const rule = this.makeRule(property, value); // Add if (add) rule.add(); if (rule.status === 'active') { const response = { className: rule.className, classNames: rule.classNames, keyframeName: rule.keyframesName, }; return response; } } } } add(props) { var _a; // Update props if (props !== undefined) this.props = props; if (this.status !== 'active' && !!this.rules.length && this.rules.some(rule => rule.value && !!rule.value.rules.length)) { // If in browser only if ((0, isEnvironment_1.default)('browser')) { // Make a style tag const attributes = { element: { type: 'text/css', id: 'a' + this.id, }, data: Object.assign(Object.assign({}, (((_a = this.options.style) === null || _a === void 0 ? void 0 : _a.attributes) || {})), { amaui: true, mode: this.mode, pure: this.pure, version: this.version, name: this.options.name }), }; this.element = this.amauiStyle.renderer.make(attributes); // Add to the DOM this.amauiStyle.renderer.add(this.element, this.priority, attributes); // Add this.rules.filter(item => !item.value.ref).forEach(item => { item.value.add(); }); // Make css // Only if it's not a ref rule // this.rules.filter(item => !item.value.ref).forEach(item => item.value.addRuleToCss()); this.element.innerHTML = this.response.css; // Make a sheet ref this.sheet = this.element.sheet; // Move through rules this.rules.forEach(rule => rule.value.addRuleRef()); // Update active status this.status = 'active'; this.amauiStyle.subscriptions.sheet.add.emit(this); } // Node only make names else { this.rules.filter(item => !item.value.ref).forEach(item => { // Add item.value.add(); }); } // Dom this.domElementForTesting = (0, isEnvironment_1.default)('browser') && window.document.createElement('div'); } } update(value) { if ((0, utils_1.is)('object', value)) { // Update active status if (this.status === 'remove') this.status = 'active'; // Update all the items to pure if (this.pure) Object.keys(value).forEach(item => { if ((0, utils_1.is)('object', value[item])) value[item]['@p'] = true; }); const properties = { add: [], update: [], remove: [], }; const items = { previous: this.rules, new: [], }; const pure = Object.assign(Object.assign({}, (value['@pure'] || {})), (value['@p'] || {})); // Props Object.keys(value) .filter(item => ['@pure', '@p'].indexOf(item) === -1) .forEach(prop => { if ((0, utils_1.is)('array', value[prop])) value[prop].forEach(item => items.new.push({ property: prop, value: item, parents: prop })); else items.new.push({ property: prop, value: value[prop], parents: prop }); }); // Pure Object.keys(pure).forEach(prop => { items.new.push({ property: prop, value: pure[prop], parents: prop }); }); // Extract any & ref rules from new and add 'em to new const add = []; const refValues = (item, parents_) => { if ((0, utils_1.is)('object', item)) Object.keys(item).forEach(key => { if (key === null || key === void 0 ? void 0 : key.includes('&')) add.push({ property: key, value: item[key], parents: parents_ }); refValues(item[key], parents_ + ' ' + key); }); }; items.new.forEach(item => refValues(item.value, item.property)); items.new.push(...add); const parents = item => { const parents_ = item.value.parents.filter(item_ => !(item_ instanceof AmauiStyleSheet)); return parents_.map(item_ => item_.property).join(' ') || item.value.property; }; // To update, add items.new.forEach(itemNew => { const previouses = items.previous.filter(itemPrevious => ((itemPrevious.value.pure === !!(itemNew.value['@pure']) || itemNew.value['@p'])) && itemPrevious.property === itemNew.property && parents(itemPrevious) === itemNew.parents); // Add or update if (!previouses.length) properties.add.push(itemNew); else if (previouses.some(item => (parents(item) === itemNew.parents && (0, hash_1.default)(item.value.values.value) !== (0, hash_1.default)(itemNew.value)))) properties.update.push(itemNew); }); // To remove items.previous.forEach(itemPrevious => { const newItem = items.new.find(itemNew => ((itemPrevious.value.pure === !!(itemNew.value['@pure'] || itemNew.value['@p'])) && itemPrevious.property === itemNew.property && parents(itemPrevious) === itemNew.parents)); // Remove if (!newItem) properties.remove.push(itemPrevious); }); // Activity Object.keys(properties).forEach(activity => { // Activity items properties[activity].forEach(item => { const rule = this.rules.find(rule_ => ((rule_ === item) || ((rule_.value.pure === !!(item.value['@pure'] || item.value['@p'])) && (rule_.value.property === item.property) && item.parents === parents(rule_)))); switch (activity) { case 'add': this.addRule(item.value, item.property); break; case 'remove': if (rule) rule.value.remove(); break; case 'update': if (rule) rule.value.update(item.value); break; default: break; } }); }); this.amauiStyle.subscriptions.sheet.update.emit(this); } } remove() { var _a; // Remove all the rules const rules = this.rules.map(item => item.value); rules.forEach(rule => rule.remove()); // Remove the style tag, only if all the rules are removed if (!this.rules.length) { if ((0, utils_1.is)('function', (_a = this.element) === null || _a === void 0 ? void 0 : _a.remove)) this.amauiStyle.renderer.remove(this.element); // Remove from amauistyle let index = this.amauiStyle.sheets.findIndex(sheet => sheet.id === this.id); if (index > -1) this.amauiStyle.sheets.splice(index, 1); // Remove from amauiStyleSheetManager index = this.amauiStyleSheetManager.sheets[this.version].findIndex(sheet => sheet.id === this.id); if (index > -1) this.amauiStyleSheetManager.sheets[this.version].splice(index, 1); // Update idle status this.status = 'idle'; this.element = undefined; this.sheet = undefined; this.amauiStyle.subscriptions.sheet.remove.emit(this); } else { // Update remove status this.status = 'remove'; } } updateProps() { this.rules.forEach(rule => rule.value.updateProps()); this.amauiStyle.subscriptions.sheet.update_props.emit(this); } propsAreNew(props) { return ((props && Object.keys(props).reduce((result, item) => result += item + String(props[item]), '')) !== (this.props && Object.keys(this.props).reduce((result, item) => result += item + String(this.props[item]), ''))); } makeRule(property, value, options = { index: this.rules.length, pure: false }) { // Pre this.amauiStyle.subscriptions.rule.pre.emit(); const rule = AmauiStyleRule_1.default.make(value, property, { mode: 'regular', version: 'property', pure: options.pure !== undefined ? options.pure : this.pure, index: options.index, owner: this, parents: [this], amauiStyleSheet: this, amauiStyle: this.amauiStyle }); // Post this.amauiStyle.subscriptions.rule.post.emit(rule); return rule; } } exports.default = AmauiStyleSheet;