UNPKG

@skele/core

Version:

Core package of the Skele framework. Element definitions, registrations and traversal.

200 lines (168 loc) 4.78 kB
'use strict' import * as R from 'ramda' import invariant from 'invariant' import { List, Seq, is, Iterable } from 'immutable' import memoize from '../registry/memoize' import deprecated from '../log/deprecated' /** * Checks if a given object is of the provided kind. * * @param kind the kind we are checking * @param element the element * @returns {*} */ export const isOfKind = R.curry(function isOfKind(kind, element) { if (element == null) { return false } const normalized = canonical(kind) const elementKindNormalized = canonical(element.get('kind')) invariant(normalized != null, 'You must provide a valid element kind') invariant( elementKindNormalized, 'You must provide an element that has a valid kind' ) return is(elementKindNormalized.take(normalized.count()), normalized) }) export function isElementRef(obj) { const isString = o => typeof o === 'string' if (isString(obj)) return true if (Array.isArray(obj) && (R.all(isString)(obj) || obj.length === 0)) return true if (obj instanceof List && (obj.every(isString) || obj.isEmpty())) return true return false } /** * Like isOfKind but checks for exactly the provided type. * * @param kind the kind * @param element tne element * @returns {*} */ export const isExactlyOfKind = R.curry(function isExactlyOfKind(kind, element) { if (element == null) { return false } const normalized = normalize(kind) const elementKindNormalized = normalize(element.get('kind')) return is(elementKindNormalized, normalized) }) /** * Returns the kind of an element. * * @param element any object, potentially an element * @returns the kind of that element or null (which means the provided object is not an element */ export function kindOf(element) { const kind = element.get('kind') if (kind != null) { return canonical(kind) } return null } /** * Returns true if object provided is an element */ export function isElement(obj) { return Iterable.isIterable(obj) && kindOf(obj) != null } /** * Given an element kind, returns all ancestor kinds. * * Eg.e for ['nav'. 'stack', 'modern'] will return a list of * * [['nav'. 'stack', 'modern'], * ['nav'. 'stack'], * ['nav']] * * @param ref the kind * @returns {*} */ export function ancestorKinds(ref) { const cRef = canonical(ref) invariant(cRef != null, 'you must provide a valid element reference') function* subKinds() { let current = List(cRef) while (current != null && !current.isEmpty()) { yield current current = current.butLast() } } if (Array.isArray(ref) && ref.length === 0) { return List() } return Seq(subKinds()) } function _normalize(kind) { if (typeof kind === 'string') { return List.of(kind) } if (Array.isArray(kind)) { return List(kind) } if (List.isList(kind)) { return kind } if (Seq.isSeq(kind)) { return kind.toList() } return null } const normalize = memoize(_normalize) /** * @returns the canonical version for the reference kind */ export const canonical = normalize export function pathsToChildElements(element) { return childPositions(element).flatMap(childrenPath => { const children = element.get(childrenPath) if (Iterable.isIndexed(children)) { return children.map((_, i) => List.of(childrenPath, i)) } else if (Iterable.isAssociative(children)) { return List.of(List.of(childrenPath)) } else { return List() } }) } /** * Property name of the location where positions of the elements' children * can be found */ export const childrenProperty = '@@skele/children' const deprecatedChildrenProperty = '@@girders-elements/children' /** * Returns the value as a list. * * @param v The value; can be * - a List; the same will be returned * - an array, it will be converted in a list * - null or undefined => an empty list will be returned * - any other value will be wrapped as a single-element list */ // eslint-disable-next-line no-nested-ternary export const asList = v => Iterable.isIndexed(v) ? v : Array.isArray(v) ? List(v) : v != null ? List.of(v) : List() const deprecatedChildrenGetter = deprecated( 'The use of `@@girders-elements/children` to demarcate child positions in an element ' + 'is deprecated. Please use `@@skele/children` or `propNames.children` instead', el => el.get(deprecatedChildrenProperty) ) /** * Returns a list of property names where the element's children may be found * * @parma the element, the default value */ export const childPositions = element => asList( element.get(childrenProperty) || (element.has(deprecatedChildrenProperty) ? deprecatedChildrenGetter(element) : null) )