import { Arr, Optional } from '@ephox/katamari'; import { Compare, Insert, type SugarElement } from '@ephox/sugar'; import type { AlloyComponent } from '../../api/component/ComponentApi'; import type { AlloySpec } from '../../api/component/SpecTypes'; import * as Attachment from '../../api/system/Attachment'; import * as Patching from '../../dom/Patching'; import * as InternalAttachment from '../../system/InternalAttachment'; import type { Stateless } from '../common/BehaviourState'; import { withoutReuse, withReuse } from './ReplacingAll'; import type { ReplacingConfig } from './ReplacingTypes'; const virtualReplace = (component: AlloyComponent, replacee: AlloyComponent, replaceeIndex: number, childSpec: AlloySpec) => { InternalAttachment.virtualDetach(replacee); const child = Patching.patchSpecChild(component.element, replaceeIndex, childSpec, component.getSystem().buildOrPatch); InternalAttachment.virtualAttach(component, child); component.syncComponents(); }; const insert = (component: AlloyComponent, insertion: (p: SugarElement, c: SugarElement) => void, childSpec: AlloySpec): void => { const child = component.getSystem().build(childSpec); Attachment.attachWith(component, child, insertion); }; const replace = (component: AlloyComponent, replacee: AlloyComponent, replaceeIndex: number, childSpec: AlloySpec) => { Attachment.detach(replacee); insert(component, (p, c) => Insert.appendAt(p, c, replaceeIndex), childSpec); }; const set = (component: AlloyComponent, replaceConfig: ReplacingConfig, replaceState: Stateless, data: AlloySpec[]): void => { const replacer = replaceConfig.reuseDom ? withReuse : withoutReuse; return replacer(component, data); }; const append = (component: AlloyComponent, replaceConfig: ReplacingConfig, replaceState: Stateless, appendee: AlloySpec): void => { insert(component, Insert.append, appendee); }; const prepend = (component: AlloyComponent, replaceConfig: ReplacingConfig, replaceState: Stateless, prependee: AlloySpec): void => { insert(component, Insert.prepend, prependee); }; // NOTE: Removee is going to be a component, not a spec. const remove = (component: AlloyComponent, replaceConfig: ReplacingConfig, replaceState: Stateless, removee: AlloyComponent): void => { const children = contents(component, replaceConfig); const foundChild = Arr.find(children, (child) => Compare.eq(removee.element, child.element)); foundChild.each(Attachment.detach); }; // TODO: Rename const contents = (component: AlloyComponent, _replaceConfig: ReplacingConfig): AlloyComponent[] => component.components(); const replaceAt = (component: AlloyComponent, replaceConfig: ReplacingConfig, replaceState: Stateless, replaceeIndex: number, replacer: Optional): Optional => { const children = contents(component, replaceConfig); return Optional.from(children[replaceeIndex]).map((replacee) => { replacer.fold( () => Attachment.detach(replacee), (r) => { const replacer = replaceConfig.reuseDom ? virtualReplace : replace; replacer(component, replacee, replaceeIndex, r); } ); return replacee; }); }; const replaceBy = (component: AlloyComponent, replaceConfig: ReplacingConfig, replaceState: Stateless, replaceePred: (comp: AlloyComponent) => boolean, replacer: Optional): Optional => { const children = contents(component, replaceConfig); return Arr.findIndex(children, replaceePred).bind((replaceeIndex) => replaceAt(component, replaceConfig, replaceState, replaceeIndex, replacer)); }; export { append, prepend, remove, replaceAt, replaceBy, set, contents };