140 lines
4.7 KiB
TypeScript
Raw Normal View History

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {RElement} from '../interfaces/renderer';
import {StylingMapArray} from '../interfaces/styling';
import {TEMPLATE_DIRECTIVE_INDEX} from '../util/styling_utils';
/**
* --------
*
* This file contains all state-based logic for styling in Angular.
*
* Styling in Angular is evaluated with a series of styling-specific
* template instructions which are called one after another each time
* change detection occurs in Angular.
*
* Styling makes use of various temporary, state-based variables between
* instructions so that it can better cache and optimize its values.
* These values are usually populated and cleared when an element is
* exited in change detection (once all the instructions are run for
* that element).
*
* To learn more about the algorithm see `TStylingContext`.
*
* --------
*/
/**
* Used as a state reference for update values between style/class binding instructions.
*
* In addition to storing the element and bit-mask related values, the state also
* stores the `sourceIndex` value. The `sourceIndex` value is an incremented value
* that identifies what "source" (i.e. the template, a specific directive by index or
* component) is currently applying its styling bindings to the element.
*/
export interface StylingState {
/** The element that is currently being processed */
element: RElement|null;
/** The directive index that is currently active (`0` === template) */
directiveIndex: number;
/** The source (column) index that is currently active (`0` === template) */
sourceIndex: number;
/** The classes update bit mask value that is processed during each class binding */
classesBitMask: number;
/** The classes update bit index value that is processed during each class binding */
classesIndex: number;
/** The styles update bit mask value that is processed during each style binding */
stylesBitMask: number;
/** The styles update bit index value that is processed during each style binding */
stylesIndex: number;
/**
* The last class map that was applied (i.e. `[class]="x"`).
*
* Note that this property is only populated when direct class values are applied
* (i.e. context resolution is not used).
*
* See `allowDirectStyling` for more info.
*/
lastDirectClassMap: StylingMapArray|null;
/**
* The last style map that was applied (i.e. `[style]="x"`)
*
* Note that this property is only populated when direct style values are applied
* (i.e. context resolution is not used).
*
* See `allowDirectStyling` for more info.
*/
lastDirectStyleMap: StylingMapArray|null;
}
// these values will get filled in the very first time this is accessed...
const _state: StylingState = {
element: null,
directiveIndex: -1,
sourceIndex: -1,
classesBitMask: -1,
classesIndex: -1,
stylesBitMask: -1,
stylesIndex: -1,
lastDirectClassMap: null,
lastDirectStyleMap: null,
};
const BIT_MASK_START_VALUE = 0;
// the `0` start value is reserved for [map]-based entries
const INDEX_START_VALUE = 1;
/**
* Returns (or instantiates) the styling state for the given element.
*
* Styling state is accessed and processed each time a style or class binding
* is evaluated.
*
* If and when the provided `element` doesn't match the current element in the
* state then this means that styling was recently cleared or the element has
* changed in change detection. In both cases the styling state is fully reset.
*
* If and when the provided `directiveIndex` doesn't match the current directive
* index in the state then this means that a new source has introduced itself into
* the styling code (or, in other words, another directive or component has started
* to apply its styling host bindings to the element).
*/
export function getStylingState(element: RElement, directiveIndex: number): StylingState {
if (_state.element !== element) {
_state.element = element;
_state.directiveIndex = directiveIndex;
_state.sourceIndex = directiveIndex === TEMPLATE_DIRECTIVE_INDEX ? 0 : 1;
_state.classesBitMask = BIT_MASK_START_VALUE;
_state.classesIndex = INDEX_START_VALUE;
_state.stylesBitMask = BIT_MASK_START_VALUE;
_state.stylesIndex = INDEX_START_VALUE;
_state.lastDirectClassMap = null;
_state.lastDirectStyleMap = null;
} else if (_state.directiveIndex !== directiveIndex) {
_state.directiveIndex = directiveIndex;
_state.sourceIndex++;
}
return _state;
}
/**
* Clears the styling state so that it can be used by another element's styling code.
*/
export function resetStylingState() {
_state.element = null;
}