123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- /**
- * @license
- * Copyright 2017 Google LLC
- * SPDX-License-Identifier: BSD-3-Clause
- */
- import { isSingleExpression } from './directive-helpers.js';
- import { Directive, PartType } from './directive.js';
- export { directive } from './directive.js';
- const DEV_MODE = true;
- /**
- * Recursively walks down the tree of Parts/TemplateInstances/Directives to set
- * the connected state of directives and run `disconnected`/ `reconnected`
- * callbacks.
- *
- * @return True if there were children to disconnect; false otherwise
- */
- const notifyChildrenConnectedChanged = (parent, isConnected) => {
- var _a, _b;
- const children = parent._$disconnectableChildren;
- if (children === undefined) {
- return false;
- }
- for (const obj of children) {
- // The existence of `_$notifyDirectiveConnectionChanged` is used as a "brand" to
- // disambiguate AsyncDirectives from other DisconnectableChildren
- // (as opposed to using an instanceof check to know when to call it); the
- // redundancy of "Directive" in the API name is to avoid conflicting with
- // `_$notifyConnectionChanged`, which exists `ChildParts` which are also in
- // this list
- // Disconnect Directive (and any nested directives contained within)
- // This property needs to remain unminified.
- (_b = (_a = obj)['_$notifyDirectiveConnectionChanged']) === null || _b === void 0 ? void 0 : _b.call(_a, isConnected, false);
- // Disconnect Part/TemplateInstance
- notifyChildrenConnectedChanged(obj, isConnected);
- }
- return true;
- };
- /**
- * Removes the given child from its parent list of disconnectable children, and
- * if the parent list becomes empty as a result, removes the parent from its
- * parent, and so forth up the tree when that causes subsequent parent lists to
- * become empty.
- */
- const removeDisconnectableFromParent = (obj) => {
- let parent, children;
- do {
- if ((parent = obj._$parent) === undefined) {
- break;
- }
- children = parent._$disconnectableChildren;
- children.delete(obj);
- obj = parent;
- } while ((children === null || children === void 0 ? void 0 : children.size) === 0);
- };
- const addDisconnectableToParent = (obj) => {
- // Climb the parent tree, creating a sparse tree of children needing
- // disconnection
- for (let parent; (parent = obj._$parent); obj = parent) {
- let children = parent._$disconnectableChildren;
- if (children === undefined) {
- parent._$disconnectableChildren = children = new Set();
- }
- else if (children.has(obj)) {
- // Once we've reached a parent that already contains this child, we
- // can short-circuit
- break;
- }
- children.add(obj);
- installDisconnectAPI(parent);
- }
- };
- /**
- * Changes the parent reference of the ChildPart, and updates the sparse tree of
- * Disconnectable children accordingly.
- *
- * Note, this method will be patched onto ChildPart instances and called from
- * the core code when parts are moved between different parents.
- */
- function reparentDisconnectables(newParent) {
- if (this._$disconnectableChildren !== undefined) {
- removeDisconnectableFromParent(this);
- this._$parent = newParent;
- addDisconnectableToParent(this);
- }
- else {
- this._$parent = newParent;
- }
- }
- /**
- * Sets the connected state on any directives contained within the committed
- * value of this part (i.e. within a TemplateInstance or iterable of
- * ChildParts) and runs their `disconnected`/`reconnected`s, as well as within
- * any directives stored on the ChildPart (when `valueOnly` is false).
- *
- * `isClearingValue` should be passed as `true` on a top-level part that is
- * clearing itself, and not as a result of recursively disconnecting directives
- * as part of a `clear` operation higher up the tree. This both ensures that any
- * directive on this ChildPart that produced a value that caused the clear
- * operation is not disconnected, and also serves as a performance optimization
- * to avoid needless bookkeeping when a subtree is going away; when clearing a
- * subtree, only the top-most part need to remove itself from the parent.
- *
- * `fromPartIndex` is passed only in the case of a partial `_clear` running as a
- * result of truncating an iterable.
- *
- * Note, this method will be patched onto ChildPart instances and called from the
- * core code when parts are cleared or the connection state is changed by the
- * user.
- */
- function notifyChildPartConnectedChanged(isConnected, isClearingValue = false, fromPartIndex = 0) {
- const value = this._$committedValue;
- const children = this._$disconnectableChildren;
- if (children === undefined || children.size === 0) {
- return;
- }
- if (isClearingValue) {
- if (Array.isArray(value)) {
- // Iterable case: Any ChildParts created by the iterable should be
- // disconnected and removed from this ChildPart's disconnectable
- // children (starting at `fromPartIndex` in the case of truncation)
- for (let i = fromPartIndex; i < value.length; i++) {
- notifyChildrenConnectedChanged(value[i], false);
- removeDisconnectableFromParent(value[i]);
- }
- }
- else if (value != null) {
- // TemplateInstance case: If the value has disconnectable children (will
- // only be in the case that it is a TemplateInstance), we disconnect it
- // and remove it from this ChildPart's disconnectable children
- notifyChildrenConnectedChanged(value, false);
- removeDisconnectableFromParent(value);
- }
- }
- else {
- notifyChildrenConnectedChanged(this, isConnected);
- }
- }
- /**
- * Patches disconnection API onto ChildParts.
- */
- const installDisconnectAPI = (obj) => {
- var _a, _b;
- var _c, _d;
- if (obj.type == PartType.CHILD) {
- (_a = (_c = obj)._$notifyConnectionChanged) !== null && _a !== void 0 ? _a : (_c._$notifyConnectionChanged = notifyChildPartConnectedChanged);
- (_b = (_d = obj)._$reparentDisconnectables) !== null && _b !== void 0 ? _b : (_d._$reparentDisconnectables = reparentDisconnectables);
- }
- };
- /**
- * An abstract `Directive` base class whose `disconnected` method will be
- * called when the part containing the directive is cleared as a result of
- * re-rendering, or when the user calls `part.setConnected(false)` on
- * a part that was previously rendered containing the directive (as happens
- * when e.g. a LitElement disconnects from the DOM).
- *
- * If `part.setConnected(true)` is subsequently called on a
- * containing part, the directive's `reconnected` method will be called prior
- * to its next `update`/`render` callbacks. When implementing `disconnected`,
- * `reconnected` should also be implemented to be compatible with reconnection.
- *
- * Note that updates may occur while the directive is disconnected. As such,
- * directives should generally check the `this.isConnected` flag during
- * render/update to determine whether it is safe to subscribe to resources
- * that may prevent garbage collection.
- */
- export class AsyncDirective extends Directive {
- constructor() {
- super(...arguments);
- // @internal
- this._$disconnectableChildren = undefined;
- }
- /**
- * Initialize the part with internal fields
- * @param part
- * @param parent
- * @param attributeIndex
- */
- _$initialize(part, parent, attributeIndex) {
- super._$initialize(part, parent, attributeIndex);
- addDisconnectableToParent(this);
- this.isConnected = part._$isConnected;
- }
- // This property needs to remain unminified.
- /**
- * Called from the core code when a directive is going away from a part (in
- * which case `shouldRemoveFromParent` should be true), and from the
- * `setChildrenConnected` helper function when recursively changing the
- * connection state of a tree (in which case `shouldRemoveFromParent` should
- * be false).
- *
- * @param isConnected
- * @param isClearingDirective - True when the directive itself is being
- * removed; false when the tree is being disconnected
- * @internal
- */
- ['_$notifyDirectiveConnectionChanged'](isConnected, isClearingDirective = true) {
- var _a, _b;
- if (isConnected !== this.isConnected) {
- this.isConnected = isConnected;
- if (isConnected) {
- (_a = this.reconnected) === null || _a === void 0 ? void 0 : _a.call(this);
- }
- else {
- (_b = this.disconnected) === null || _b === void 0 ? void 0 : _b.call(this);
- }
- }
- if (isClearingDirective) {
- notifyChildrenConnectedChanged(this, isConnected);
- removeDisconnectableFromParent(this);
- }
- }
- /**
- * Sets the value of the directive's Part outside the normal `update`/`render`
- * lifecycle of a directive.
- *
- * This method should not be called synchronously from a directive's `update`
- * or `render`.
- *
- * @param directive The directive to update
- * @param value The value to set
- */
- setValue(value) {
- if (isSingleExpression(this.__part)) {
- this.__part._$setValue(value, this);
- }
- else {
- // this.__attributeIndex will be defined in this case, but
- // assert it in dev mode
- if (DEV_MODE && this.__attributeIndex === undefined) {
- throw new Error(`Expected this.__attributeIndex to be a number`);
- }
- const newValues = [...this.__part._$committedValue];
- newValues[this.__attributeIndex] = value;
- this.__part._$setValue(newValues, this, 0);
- }
- }
- /**
- * User callbacks for implementing logic to release any resources/subscriptions
- * that may have been retained by this directive. Since directives may also be
- * re-connected, `reconnected` should also be implemented to restore the
- * working state of the directive prior to the next render.
- */
- disconnected() { }
- reconnected() { }
- }
- //# sourceMappingURL=async-directive.js.map
|