UNPKG

@adpt/core

Version:
150 lines 4.78 kB
"use strict"; /* * Copyright 2018-2019 Unbounded Systems, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const util = tslib_1.__importStar(require("util")); const ld = tslib_1.__importStar(require("lodash")); const jsx_1 = require("./jsx"); const defaultKeySym = Symbol("defaultKey"); function isDefaultKey(props) { return props[defaultKeySym] === true; } exports.isDefaultKey = isDefaultKey; function setKey(elem, key) { // Ensure key doesn't have extra properties for later use of assign key = ld.pick(key, "key", defaultKeySym); if (Object.isFrozen(elem.props)) { const newProps = Object.assign(ld.clone(elem.props), key); Object.freeze(newProps); elem.props = newProps; } else { Object.assign(elem.props, key); } } exports.setKey = setKey; function computeMountKey(elem, parentStateNamespace) { let newKey = elem.props.key; if (newKey == null) { const lastKey = ld.last(parentStateNamespace); const name = elem.componentName; newKey = (lastKey == null) ? name : `${lastKey}-${name}`; } return { key: newKey, [defaultKeySym]: isDefaultKey(elem.props) || elem.props.key == null, }; } exports.computeMountKey = computeMountKey; function assignKeysAtPlacement(siblingsIn) { const existingKeys = new KeyNames(); const needsKeys = []; const duplicateKeys = []; if (siblingsIn == null) return; const siblings = ld.isArray(siblingsIn) ? siblingsIn : [siblingsIn]; for (const node of siblings) { if (jsx_1.isElement(node)) { if (("key" in node.props) && (node.props.key != null)) { if (ld.isString(node.props.key)) { if (existingKeys.has(node.props.key)) { duplicateKeys.push(node); } else { existingKeys.add(node.props.key); } } else { throw new Error(`children have non-string keys: ${node.componentName}: ${util.inspect(node.props.key)}`); } } else { needsKeys.push(node); } } } if (duplicateKeys.length !== 0) { throw new Error(`children have duplicate keys: ${util.inspect(duplicateKeys)}`); } for (const elem of needsKeys) { const key = existingKeys.getUnique(elem.componentName); setKey(elem, { key, [defaultKeySym]: true }); } } exports.assignKeysAtPlacement = assignKeysAtPlacement; /** * Stores the set of keys for a given group of sibling elements to keep * track of uniqueness and optionally generate a new unique key, given * a base name. */ class KeyNames { constructor() { this.names = new Map(); } getUnique(baseName) { let unique = baseName; let next = this.names.get(baseName); if (next === undefined) { next = 1; } else { unique += next.toString(); next++; } this.names.set(baseName, next); return unique; } has(key) { return this.names.has(key); } add(key) { if (this.names.has(key)) { throw new Error(`Cannot add duplicate key '${key}`); } } } class KeyTracker { constructor() { this.path = []; this.names = []; this.depth = 0; this.names.push(new KeyNames()); } lastKeyPath() { return this.path.join("."); } addKey(component) { // TODO: Allow components to make names for themselves const compName = component.constructor.name; const uniqueName = this.names[this.depth].getUnique(compName); this.path[this.depth] = uniqueName; } pathPush() { this.names.push(new KeyNames()); this.depth++; } pathPop() { if (this.depth <= 0) { throw new Error(`Attempt to pop KeyTracker past 0`); } this.depth--; this.path.pop(); this.names.pop(); } } exports.KeyTracker = KeyTracker; //# sourceMappingURL=keys.js.map