refactor(ivy): move styling/interfaces.ts to interfaces/styling.ts (#32731)

PR Close #32731
This commit is contained in:
Matias Niemelä
2019-09-17 14:51:28 -07:00
committed by Andrew Kushnir
parent ec0aa88f4e
commit 5d12cb9fdf
13 changed files with 21 additions and 21 deletions

View File

@ -8,9 +8,9 @@
import {SafeValue, unwrapSafeValue} from '../../sanitization/bypass';
import {StyleSanitizeFn, StyleSanitizeMode} from '../../sanitization/style_sanitizer';
import {ProceduralRenderer3, RElement, Renderer3, RendererStyleFlags3, isProceduralRenderer} from '../interfaces/renderer';
import {ApplyStylingFn, LStylingData, StylingMapArray, StylingMapArrayIndex, StylingMapsSyncMode, SyncStylingMapsFn, TStylingConfig, TStylingContext, TStylingContextIndex, TStylingContextPropConfigFlags} from '../interfaces/styling';
import {NO_CHANGE} from '../tokens';
import {ApplyStylingFn, LStylingData, StylingMapArray, StylingMapArrayIndex, StylingMapsSyncMode, SyncStylingMapsFn, TStylingConfig, TStylingContext, TStylingContextIndex, TStylingContextPropConfigFlags} from './interfaces';
import {getStylingState, resetStylingState} from './state';
import {DEFAULT_BINDING_INDEX, DEFAULT_BINDING_VALUE, DEFAULT_GUARD_MASK_VALUE, MAP_BASED_ENTRY_PROP_NAME, getBindingValue, getConfig, getDefaultValue, getGuardMask, getMapProp, getMapValue, getProp, getPropValuesStartPosition, getStylingMapArray, getTotalSources, getValue, getValuesCount, hasConfig, hasValueChanged, isContextLocked, isHostStylingActive, isSanitizationRequired, isStylingValueDefined, lockContext, patchConfig, setDefaultValue, setGuardMask, setMapAsDirty, setValue} from './util';

View File

@ -1,580 +0,0 @@
/**
* @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 {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
import {ProceduralRenderer3, RElement, Renderer3} from '../interfaces/renderer';
import {LView} from '../interfaces/view';
/**
* --------
*
* This file contains the core interfaces for styling in Angular.
*
* To learn more about the algorithm see `TStylingContext`.
*
* --------
*/
/**
* A static-level representation of all style or class bindings/values
* associated with a `TNode`.
*
* The `TStylingContext` unites all template styling bindings (i.e.
* `[class]` and `[style]` bindings) as well as all host-level
* styling bindings (for components and directives) together into
* a single manifest
*
* The styling context is stored on a `TNode` on and there are
* two instances of it: one for classes and another for styles.
*
* ```typescript
* tNode.styles = [ ... a context only for styles ... ];
* tNode.classes = [ ... a context only for classes ... ];
* ```
*
* The styling context is created each time there are one or more
* styling bindings (style or class bindings) present for an element,
* but is only created once per `TNode`.
*
* `tNode.styles` and `tNode.classes` can be an instance of the following:
*
* ```typescript
* tNode.styles = null; // no static styling or styling bindings active
* tNode.styles = StylingMapArray; // only static values present (e.g. `<div style="width:200">`)
* tNode.styles = TStylingContext; // one or more styling bindings present (e.g. `<div
* [style.width]>`)
* ```
*
* Both `tNode.styles` and `tNode.classes` are instantiated when anything
* styling-related is active on an element. They are first created from
* from the any of the element-level instructions (e.g. `element`,
* `elementStart`, `elementHostAttrs`). When any static style/class
* values are encountered they are registered on the `tNode.styles`
* and `tNode.classes` data-structures. By default (when any static
* values are encountered) the `tNode.styles` or `tNode.classes` values
* are instances of a `StylingMapArray`. Only when style/class bindings
* are detected then that styling map is converted into an instance of
* `TStylingContext`.
*
* Due to the fact the the `TStylingContext` is stored on a `TNode`
* this means that all data within the context is static. Instead of
* storing actual styling binding values, the lView binding index values
* are stored within the context. (static nature means it is more compact.)
*
* The code below shows a breakdown of two instances of `TStylingContext`
* (one for `tNode.styles` and another for `tNode.classes`):
*
* ```typescript
* // <div [class.active]="c" // lView binding index = 20
* // [style.width]="x" // lView binding index = 21
* // [style.height]="y"> // lView binding index = 22
* // ...
* // </div>
* tNode.styles = [
* 0, // the context config value (see `TStylingContextConfig`)
* 1, // the total amount of sources present (only `1` b/c there are only template
* bindings)
* [null], // initial values array (an instance of `StylingMapArray`)
*
* 0, // config entry for the property (see `TStylingContextPropConfigFlags`)
* 0b010, // template guard mask for height
* 0, // host bindings guard mask for height
* 'height', // the property name
* 22, // the binding location for the "y" binding in the lView
* null, // the default value for height
*
* 0, // config entry for the property (see `TStylingContextPropConfigFlags`)
* 0b001, // template guard mask for width
* 0, // host bindings guard mask for width
* 'width', // the property name
* 21, // the binding location for the "x" binding in the lView
* null, // the default value for width
* ];
*
* tNode.classes = [
* 0, // the context config value (see `TStylingContextConfig`)
* 1, // the total amount of sources present (only `1` b/c there are only template
* bindings)
* [null], // initial values array (an instance of `StylingMapArray`)
*
* 0, // config entry for the property (see `TStylingContextPropConfigFlags`)
* 0b001, // template guard mask for width
* 0, // host bindings guard mask for width
* 'active', // the property name
* 20, // the binding location for the "c" binding in the lView
* null, // the default value for the `active` class
* ];
* ```
*
* Entry value present in an entry (called a tuple) within the
* styling context is as follows:
*
* ```typescript
* context = [
* //...
* configValue,
* templateGuardMask,
* hostBindingsGuardMask,
* propName,
* ...bindingIndices...,
* defaultValue
* //...
* ];
* ```
*
* Below is a breakdown of each value:
*
* - **configValue**:
* Property-specific configuration values. The only config setting
* that is implemented right now is whether or not to sanitize the
* value.
*
* - **templateGuardMask**:
* A numeric value where each bit represents a binding index
* location. Each binding index location is assigned based on
* a local counter value that increments each time an instruction
* is called:
*
* ```
* <div [style.width]="x" // binding index = 21 (counter index = 0)
* [style.height]="y"> // binding index = 22 (counter index = 1)
* ```
*
* In the example code above, if the `width` value where to change
* then the first bit in the local bit mask value would be flipped
* (and the second bit for when `height`).
*
* If and when there are more than 32 binding sources in the context
* (more than 32 `[style/class]` bindings) then the bit masking will
* overflow and we are left with a situation where a `-1` value will
* represent the bit mask. Due to the way that JavaScript handles
* negative values, when the bit mask is `-1` then all bits within
* that value will be automatically flipped (this is a quick and
* efficient way to flip all bits on the mask when a special kind
* of caching scenario occurs or when there are more than 32 bindings).
*
* - **hostBindingsGuardMask**:
* Another instance of a guard mask that is specific to host bindings.
* This behaves exactly the same way as does the `templateGuardMask`,
* but will not contain any binding information processed in the template.
* The reason why there are two instances of guard masks (one for the
* template and another for host bindings) is because the template bindings
* are processed before host bindings and the state information is not
* carried over into the host bindings code. As soon as host bindings are
* processed for an element the counter and state-based bit mask values are
* set to `0`.
*
* ```
* <div [style.width]="x" // binding index = 21 (counter index = 0)
* [style.height]="y" // binding index = 22 (counter index = 1)
* dir-that-sets-width // binding index = 30 (counter index = 0)
* dir-that-sets-width> // binding index = 31 (counter index = 1)
* ```
*
* - **propName**:
* The CSS property name or class name (e.g `width` or `active`).
*
* - **bindingIndices...**:
* A series of numeric binding values that reflect where in the
* lView to find the style/class values associated with the property.
* Each value is in order in terms of priority (templates are first,
* then directives and then components). When the context is flushed
* and the style/class values are applied to the element (this happens
* inside of the `stylingApply` instruction) then the flushing code
* will keep checking each binding index against the associated lView
* to find the first style/class value that is non-null.
*
* - **defaultValue**:
* This is the default that will always be applied to the element if
* and when all other binding sources return a result that is null.
* Usually this value is `null` but it can also be a static value that
* is intercepted when the tNode is first constructured (e.g.
* `<div style="width:200px">` has a default value of `200px` for
* the `width` property).
*
* Each time a new binding is encountered it is registered into the
* context. The context then is continually updated until the first
* styling apply call has been called (which is automatically scheduled
* to be called once an element exits during change detection). Note that
* each entry in the context is stored in alphabetical order.
*
* Once styling has been flushed for the first time for an element the
* context will set as locked (this prevents bindings from being added
* to the context later on).
*
* # How Styles/Classes are Rendered
* Each time a styling instruction (e.g. `[class.name]`, `[style.prop]`,
* etc...) is executed, the associated `lView` for the view is updated
* at the current binding location. Also, when this happens, a local
* counter value is incremented. If the binding value has changed then
* a local `bitMask` variable is updated with the specific bit based
* on the counter value.
*
* Below is a lightweight example of what happens when a single style
* property is updated (i.e. `<div [style.prop]="val">`):
*
* ```typescript
* function updateStyleProp(prop: string, value: string) {
* const lView = getLView();
* const bindingIndex = BINDING_INDEX++;
*
* // update the local counter value
* const indexForStyle = stylingState.stylesCount++;
* if (lView[bindingIndex] !== value) {
* lView[bindingIndex] = value;
*
* // tell the local state that we have updated a style value
* // by updating the bit mask
* stylingState.bitMaskForStyles |= 1 << indexForStyle;
* }
* }
* ```
*
* Once all the bindings have updated a `bitMask` value will be populated.
* This `bitMask` value is used in the apply algorithm (which is called
* context resolution).
*
* ## The Apply Algorithm (Context Resolution)
* As explained above, each time a binding updates its value, the resulting
* value is stored in the `lView` array. These styling values have yet to
* be flushed to the element.
*
* Once all the styling instructions have been evaluated, then the styling
* context(s) are flushed to the element. When this happens, the context will
* be iterated over (property by property) and each binding source will be
* examined and the first non-null value will be applied to the element.
*
* Let's say that we the following template code:
*
* ```html
* <div [style.width]="w1" dir-that-set-width="w2"></div>
* ```
*
* There are two styling bindings in the code above and they both write
* to the `width` property. When styling is flushed on the element, the
* algorithm will try and figure out which one of these values to write
* to the element.
*
* In order to figure out which value to apply, the following
* binding prioritization is adhered to:
*
* 1. First template-level styling bindings are applied (if present).
* This includes things like `[style.width]` and `[class.active]`.
*
* 2. Second are styling-level host bindings present in directives.
* (if there are sub/super directives present then the sub directives
* are applied first).
*
* 3. Third are styling-level host bindings present in components.
* (if there are sub/super components present then the sub directives
* are applied first).
*
* This means that in the code above the styling binding present in the
* template is applied first and, only if its falsy, then the directive
* styling binding for width will be applied.
*
* ### What about map-based styling bindings?
* Map-based styling bindings are activated when there are one or more
* `[style]` and/or `[class]` bindings present on an element. When this
* code is activated, the apply algorithm will iterate over each map
* entry and apply each styling value to the element with the same
* prioritization rules as above.
*
* For the algorithm to apply styling values efficiently, the
* styling map entries must be applied in sync (property by property)
* with prop-based bindings. (The map-based algorithm is described
* more inside of the `render3/styling/map_based_bindings.ts` file.)
*
* ## Sanitization
* Sanitization is used to prevent invalid style values from being applied to
* the element.
*
* It is enabled in two cases:
*
* 1. The `styleSanitizer(sanitizerFn)` instruction was called (just before any other
* styling instructions are run).
*
* 2. The component/directive `LView` instance has a sanitizer object attached to it
* (this happens when `renderComponent` is executed with a `sanitizer` value or
* if the ngModule contains a sanitizer provider attached to it).
*
* If and when sanitization is active then all property/value entries will be evaluated
* through the active sanitizer before they are applied to the element (or the styling
* debug handler).
*
* If a `Sanitizer` object is used (via the `LView[SANITIZER]` value) then that object
* will be used for every property.
*
* If a `StyleSanitizerFn` function is used (via the `styleSanitizer`) then it will be
* called in two ways:
*
* 1. property validation mode: this will be called early to mark whether a property
* should be sanitized or not at during the flushing stage.
*
* 2. value sanitization mode: this will be called during the flushing stage and will
* run the sanitizer function against the value before applying it to the element.
*
* If sanitization returns an empty value then that empty value will be applied
* to the element.
*/
export interface TStylingContext extends
Array<number|string|number|boolean|null|StylingMapArray|{}> {
/** Configuration data for the context */
[TStylingContextIndex.ConfigPosition]: TStylingConfig;
/** The total amount of sources present in the context */
[TStylingContextIndex.TotalSourcesPosition]: number;
/** Initial value position for static styles */
[TStylingContextIndex.InitialStylingValuePosition]: StylingMapArray;
}
/**
* A series of flags used to configure the config value present within an instance of
* `TStylingContext`.
*/
export const enum TStylingConfig {
/**
* The initial state of the styling context config.
*/
Initial = 0b0000000,
/**
* Whether or not there are prop-based bindings present.
*
* Examples include:
* 1. `<div [style.prop]="x">`
* 2. `<div [class.prop]="x">`
* 3. `@HostBinding('style.prop') x`
* 4. `@HostBinding('class.prop') x`
*/
HasPropBindings = 0b0000001,
/**
* Whether or not there are map-based bindings present.
*
* Examples include:
* 1. `<div [style]="x">`
* 2. `<div [class]="x">`
* 3. `@HostBinding('style') x`
* 4. `@HostBinding('class') x`
*/
HasMapBindings = 0b0000010,
/**
* Whether or not there are map-based and prop-based bindings present.
*
* Examples include:
* 1. `<div [style]="x" [style.prop]="y">`
* 2. `<div [class]="x" [style.prop]="y">`
* 3. `<div [style]="x" dir-that-sets-some-prop>`
* 4. `<div [class]="x" dir-that-sets-some-class>`
*/
HasPropAndMapBindings = 0b0000011,
/**
* Whether or not there are two or more sources for a single property in the context.
*
* Examples include:
* 1. prop + prop: `<div [style.width]="x" dir-that-sets-width>`
* 2. map + prop: `<div [style]="x" [style.prop]>`
* 3. map + map: `<div [style]="x" dir-that-sets-style>`
*/
HasCollisions = 0b0000100,
/**
* Whether or not the context contains initial styling values.
*
* Examples include:
* 1. `<div style="width:200px">`
* 2. `<div class="one two three">`
* 3. `@Directive({ host: { 'style': 'width:200px' } })`
* 4. `@Directive({ host: { 'class': 'one two three' } })`
*/
HasInitialStyling = 0b00001000,
/**
* Whether or not the context contains one or more template bindings.
*
* Examples include:
* 1. `<div [style]="x">`
* 2. `<div [style.width]="x">`
* 3. `<div [class]="x">`
* 4. `<div [class.name]="x">`
*/
HasTemplateBindings = 0b00010000,
/**
* Whether or not the context contains one or more host bindings.
*
* Examples include:
* 1. `@HostBinding('style') x`
* 2. `@HostBinding('style.width') x`
* 3. `@HostBinding('class') x`
* 4. `@HostBinding('class.name') x`
*/
HasHostBindings = 0b00100000,
/**
* Whether or not the template bindings are allowed to be registered in the context.
*
* This flag is after one or more template-based style/class bindings were
* set and processed for an element. Once the bindings are processed then a call
* to stylingApply is issued and the lock will be put into place.
*
* Note that this is only set once.
*/
TemplateBindingsLocked = 0b01000000,
/**
* Whether or not the host bindings are allowed to be registered in the context.
*
* This flag is after one or more host-based style/class bindings were
* set and processed for an element. Once the bindings are processed then a call
* to stylingApply is issued and the lock will be put into place.
*
* Note that this is only set once.
*/
HostBindingsLocked = 0b10000000,
/** A Mask of all the configurations */
Mask = 0b11111111,
/** Total amount of configuration bits used */
TotalBits = 8,
}
/**
* An index of position and offset values used to navigate the `TStylingContext`.
*/
export const enum TStylingContextIndex {
ConfigPosition = 0,
TotalSourcesPosition = 1,
InitialStylingValuePosition = 2,
ValuesStartPosition = 3,
// each tuple entry in the context
// (config, templateBitGuard, hostBindingBitGuard, prop, ...bindings||default-value)
ConfigOffset = 0,
TemplateBitGuardOffset = 1,
HostBindingsBitGuardOffset = 2,
PropOffset = 3,
BindingsStartOffset = 4
}
/**
* A series of flags used for each property entry within the `TStylingContext`.
*/
export const enum TStylingContextPropConfigFlags {
Default = 0b0,
SanitizationRequired = 0b1,
TotalBits = 1,
Mask = 0b1,
}
/**
* A function used to apply or remove styling from an element for a given property.
*/
export interface ApplyStylingFn {
(renderer: Renderer3|ProceduralRenderer3|null, element: RElement, prop: string, value: any,
bindingIndex?: number|null): void;
}
/**
* Runtime data type that is used to store binding data referenced from the `TStylingContext`.
*
* Because `LView` is just an array with data, there is no reason to
* special case `LView` everywhere in the styling algorithm. By allowing
* this data type to be an array that contains various scalar data types,
* an instance of `LView` doesn't need to be constructed for tests.
*/
export type LStylingData = LView | (string | number | boolean | null)[];
/**
* Array-based representation of a key/value array.
*
* The format of the array is "property", "value", "property2",
* "value2", etc...
*
* The first value in the array is reserved to store the instance
* of the key/value array that was used to populate the property/
* value entries that take place in the remainder of the array.
*/
export interface StylingMapArray extends Array<{}|string|number|null> {
[StylingMapArrayIndex.RawValuePosition]: {}|string|null;
}
/**
* An index of position and offset points for any data stored within a `StylingMapArray` instance.
*/
export const enum StylingMapArrayIndex {
/** Where the values start in the array */
ValuesStartPosition = 1,
/** The location of the raw key/value map instance used last to populate the array entries */
RawValuePosition = 0,
/** The size of each property/value entry */
TupleSize = 2,
/** The offset for the property entry in the tuple */
PropOffset = 0,
/** The offset for the value entry in the tuple */
ValueOffset = 1,
}
/**
* Used to apply/traverse across all map-based styling entries up to the provided `targetProp`
* value.
*
* When called, each of the map-based `StylingMapArray` entries (which are stored in
* the provided `LStylingData` array) will be iterated over. Depending on the provided
* `mode` value, each prop/value entry may be applied or skipped over.
*
* If `targetProp` value is provided the iteration code will stop once it reaches
* the property (if found). Otherwise if the target property is not encountered then
* it will stop once it reaches the next value that appears alphabetically after it.
*
* If a `defaultValue` is provided then it will be applied to the element only if the
* `targetProp` property value is encountered and the value associated with the target
* property is `null`. The reason why the `defaultValue` is needed is to avoid having the
* algorithm apply a `null` value and then apply a default value afterwards (this would
* end up being two style property writes).
*
* @returns whether or not the target property was reached and its value was
* applied to the element.
*/
export interface SyncStylingMapsFn {
(context: TStylingContext, renderer: Renderer3|ProceduralRenderer3|null, element: RElement,
data: LStylingData, sourceIndex: number, applyStylingFn: ApplyStylingFn,
sanitizer: StyleSanitizeFn|null, mode: StylingMapsSyncMode, targetProp?: string|null,
defaultValue?: boolean|string|null): boolean;
}
/**
* Used to direct how map-based values are applied/traversed when styling is flushed.
*/
export const enum StylingMapsSyncMode {
/** Only traverse values (no prop/value styling entries get applied) */
TraverseValues = 0b000,
/** Apply every prop/value styling entry to the element */
ApplyAllValues = 0b001,
/** Only apply the target prop/value entry */
ApplyTargetProp = 0b010,
/** Skip applying the target prop/value entry */
SkipTargetProp = 0b100,
/** Iterate over inner maps map values in the context */
RecurseInnerMaps = 0b1000,
/** Only check to see if a value was set somewhere in each map */
CheckValuesOnly = 0b10000,
}

View File

@ -8,9 +8,9 @@
import {unwrapSafeValue} from '../../sanitization/bypass';
import {StyleSanitizeFn, StyleSanitizeMode} from '../../sanitization/style_sanitizer';
import {ProceduralRenderer3, RElement, Renderer3} from '../interfaces/renderer';
import {ApplyStylingFn, LStylingData, StylingMapArray, StylingMapArrayIndex, StylingMapsSyncMode, SyncStylingMapsFn, TStylingContext, TStylingContextIndex} from '../interfaces/styling';
import {setStylingMapsSyncFn} from './bindings';
import {ApplyStylingFn, LStylingData, StylingMapArray, StylingMapArrayIndex, StylingMapsSyncMode, SyncStylingMapsFn, TStylingContext, TStylingContextIndex} from './interfaces';
import {getBindingValue, getMapProp, getMapValue, getValue, getValuesCount, isStylingValueDefined} from './util';

View File

@ -7,11 +7,11 @@
*/
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
import {RElement} from '../interfaces/renderer';
import {ApplyStylingFn, LStylingData, TStylingConfig, TStylingContext, TStylingContextIndex} from '../interfaces/styling';
import {getCurrentStyleSanitizer} from '../state';
import {attachDebugObject} from '../util/debug_utils';
import {applyStylingViaContext} from './bindings';
import {ApplyStylingFn, LStylingData, TStylingConfig, TStylingContext, TStylingContextIndex} from './interfaces';
import {activateStylingMapFeature} from './map_based_bindings';
import {allowDirectStyling as _allowDirectStyling, getDefaultValue, getGuardMask, getProp, getPropValuesStartPosition, getValuesCount, hasConfig, isContextLocked, isMapBased, isSanitizationRequired} from './util';

View File

@ -5,12 +5,10 @@
* 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 {unwrapSafeValue} from '../../sanitization/bypass';
import {TNode, TNodeFlags} from '../interfaces/node';
import {LStylingData, StylingMapArray, StylingMapArrayIndex, TStylingConfig, TStylingContext, TStylingContextIndex, TStylingContextPropConfigFlags} from '../interfaces/styling';
import {NO_CHANGE} from '../tokens';
import {LStylingData, StylingMapArray, StylingMapArrayIndex, TStylingConfig, TStylingContext, TStylingContextIndex, TStylingContextPropConfigFlags} from './interfaces';
export const MAP_BASED_ENTRY_PROP_NAME = '[MAP]';
export const TEMPLATE_DIRECTIVE_INDEX = 0;