import { Optional } from '@ephox/katamari'; import { Attribute, Classes, Css, Html, InsertAll, SugarElement, SugarNode, Value } from '@ephox/sugar'; import { isPremade } from '../api/ui/GuiTypes'; import * as Tagger from '../registry/Tagger'; import type * as DomDefinition from './DomDefinition'; import { reconcileToDom } from './Reconcile'; const introduceToDom = (definition: DomDefinition.GeneralDefinitionDetail>): SugarElement => { const subject = SugarElement.fromTag(definition.tag); Attribute.setAll(subject, definition.attributes); Classes.add(subject, definition.classes); Css.setAll(subject, definition.styles); // Remember: Order of innerHtml vs children is important. definition.innerHtml.each((html) => Html.set(subject, html)); // Children are already elements. const children = definition.domChildren; InsertAll.append(subject, children); definition.value.each((value) => { Value.set(subject as SugarElement, value); }); return subject; }; const attemptPatch = (definition: DomDefinition.GeneralDefinitionDetail>, obsoleted: SugarElement): Optional> => { try { const e = reconcileToDom(definition, obsoleted); return Optional.some(e); } catch { return Optional.none(); } }; // If a component has both innerHtml and children then we can't patch it const hasMixedChildren = (definition: DomDefinition.GeneralDefinitionDetail>) => definition.innerHtml.isSome() && definition.domChildren.length > 0; const renderToDom = (definition: DomDefinition.GeneralDefinitionDetail>, optObsoleted: Optional>): SugarElement => { // If the current tag doesn't match, let's not try to add anything further down the tree. // If it does match though and we don't have mixed children then attempt to patch attributes etc... const canBePatched = (candidate: SugarElement): candidate is SugarElement => SugarNode.name(candidate) === definition.tag && !hasMixedChildren(definition) && !isPremade(candidate); const elem = optObsoleted .filter(canBePatched) .bind((obsoleted) => attemptPatch(definition, obsoleted)) .getOrThunk(() => introduceToDom(definition)); Tagger.writeOnly(elem, definition.uid); return elem; }; export { renderToDom };