@adpt/core
Version:
AdaptJS core library
150 lines • 4.78 kB
JavaScript
;
/*
* 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