import { Arr, Fun, Obj } from '@ephox/katamari';
import { Attribute, Css } from '@ephox/sugar';
import * as AlloyParts from '../../parts/AlloyParts';
import * as PartType from '../../parts/PartType';
import * as SketchBehaviours from '../component/SketchBehaviours';
import * as AlloyTriggers from '../events/AlloyTriggers';
import * as SystemEvents from '../events/SystemEvents';
import * as GuiTypes from './GuiTypes';
import * as UiSketcher from './UiSketcher';
const owner = 'container';
const schema = [
    SketchBehaviours.field('slotBehaviours', [])
];
const getPartName = (name) => '<alloy.field.' + name + '>';
const sketch = (sSpec) => {
    // As parts.slot is called, record all of the parts that are registered
    // as part of this SlotContainer.
    const parts = (() => {
        const record = [];
        const slot = (name, config) => {
            record.push(name);
            return AlloyParts.generateOne(owner, getPartName(name), config);
        };
        return {
            slot,
            record: Fun.constant(record)
        };
    })();
    const spec = sSpec(parts);
    const partNames = parts.record();
    // Like a Form, a SlotContainer does not know its parts in advance. So the
    // record lists the names of the parts to put in the schema.
    // TODO: Find a nice way to remove dupe with Form
    const fieldParts = Arr.map(partNames, (n) => PartType.required({ name: n, pname: getPartName(n) }));
    return UiSketcher.composite(owner, schema, fieldParts, make, spec);
};
const make = (detail, components) => {
    const getSlotNames = (_) => AlloyParts.getAllPartNames(detail);
    const getSlot = (container, key) => AlloyParts.getPart(container, detail, key);
    const onSlot = (f, def) => (container, key) => AlloyParts.getPart(container, detail, key).map((slot) => f(slot, key)).getOr(def);
    const onSlots = (f) => (container, keys) => {
        Arr.each(keys, (key) => f(container, key));
    };
    const doShowing = (comp, _key) => Attribute.get(comp.element, 'aria-hidden') !== 'true';
    const doShow = (comp, key) => {
        // NOTE: May need to restore old values.
        if (!doShowing(comp, key)) {
            const element = comp.element;
            Css.remove(element, 'display');
            Attribute.remove(element, 'aria-hidden');
            AlloyTriggers.emitWith(comp, SystemEvents.slotVisibility(), { name: key, visible: true });
        }
    };
    const doHide = (comp, key) => {
        // NOTE: May need to save old values.
        if (doShowing(comp, key)) {
            const element = comp.element;
            Css.set(element, 'display', 'none');
            Attribute.set(element, 'aria-hidden', 'true');
            AlloyTriggers.emitWith(comp, SystemEvents.slotVisibility(), { name: key, visible: false });
        }
    };
    const isShowing = onSlot(doShowing, false);
    const hideSlot = onSlot(doHide);
    const hideSlots = onSlots(hideSlot);
    const hideAllSlots = (container) => hideSlots(container, getSlotNames(container));
    const showSlot = onSlot(doShow);
    const apis = {
        getSlotNames,
        getSlot,
        isShowing,
        hideSlot,
        hideAllSlots,
        showSlot
    };
    return {
        uid: detail.uid,
        dom: detail.dom,
        components,
        behaviours: SketchBehaviours.get(detail.slotBehaviours),
        apis
    };
};
// No type safety doing it this way. But removes dupe.
// We could probably use spread operator to help here.
const slotApis = Obj.map({
    getSlotNames: (apis, c) => apis.getSlotNames(c),
    getSlot: (apis, c, key) => apis.getSlot(c, key),
    isShowing: (apis, c, key) => apis.isShowing(c, key),
    hideSlot: (apis, c, key) => apis.hideSlot(c, key),
    hideAllSlots: (apis, c) => apis.hideAllSlots(c),
    showSlot: (apis, c, key) => apis.showSlot(c, key)
}, (value) => GuiTypes.makeApi(value));
const SlotContainer = {
    ...slotApis,
    ...{ sketch }
};
export { SlotContainer };
//# sourceMappingURL=SlotContainer.js.map