/*!
 * (C) Ionic http://ionicframework.com - MIT License
 */
import { forceUpdate, h, Host } from "@stencil/core";
import { createColorClasses, hostContext } from "../../utils/theme";
import { getIonTheme } from "../../global/ionic-global";
/**
 * @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component.
 * @virtualProp {"ios" | "md" | "ionic"} theme - The theme determines the visual appearance of the component.
 *
 * @slot - Content is placed between the named slots if provided without a slot.
 * @slot start - Content is placed to the left of the toolbar text in LTR, and to the right in RTL.
 * @slot secondary - Content is placed to the left of the toolbar text in the `"ios"` theme, and directly to the right in the `"md"` theme.
 * @slot primary - Content is placed to the right of the toolbar text in the `"ios"` theme, and to the far right in the `"md"` theme.
 * @slot end - Content is placed to the right of the toolbar text in LTR, and to the left in RTL.
 *
 * @part background - The background of the toolbar, covering the entire area behind the toolbar content.
 * @part container - The container that wraps all toolbar content, including the default slot and named slot content.
 * @part content - The container for the default slot, wrapping content provided without a named slot.
 */
export class Toolbar {
    constructor() {
        this.childrenStyles = new Map();
    }
    componentWillLoad() {
        const buttons = Array.from(this.el.querySelectorAll('ion-buttons'));
        const firstButtons = buttons.find((button) => {
            return button.slot === 'start';
        });
        if (firstButtons) {
            firstButtons.classList.add('buttons-first-slot');
        }
        const buttonsReversed = buttons.reverse();
        const lastButtons = buttonsReversed.find((button) => button.slot === 'end') ||
            buttonsReversed.find((button) => button.slot === 'primary') ||
            buttonsReversed.find((button) => button.slot === 'secondary');
        if (lastButtons) {
            lastButtons.classList.add('buttons-last-slot');
        }
        this.updateSlotClasses();
    }
    componentDidLoad() {
        this.updateSlotClasses();
        this.updateSlotWidths();
    }
    /**
     * Updates the CSS custom properties for slot widths
     * This ensures that slots shown by their met conditions
     * have a minimum width matching their required slot
     */
    updateSlotWidths(tries = 0) {
        // Set timeout to try to execute after everything is rendered
        setTimeout(() => {
            // Attempt to measure and update
            const success = this.measureAndUpdateSlots();
            // If not all measurements were successful, try again in 100 ms
            // cap recursion at 5 tries for safety
            if (!success && tries < 5) {
                setTimeout(() => {
                    this.updateSlotWidths(tries + 1);
                }, 100);
            }
        });
    }
    /**
     * Measure the widths of the slots and update the CSS custom properties
     * for the minimum width of each pair of slots based on the largest width in each pair.
     * Returns whether we successfully measured all of the slots we expect to have content.
     * If not, the content probably hasn't rendered yet and we need to try again.
     */
    measureAndUpdateSlots() {
        // Define the relationship between slots based on the conditions array
        // Group slots that should have the same width
        const slotPairs = [
            { name: 'start-end', slots: ['start', 'end'] },
            { name: 'primary-secondary', slots: ['primary', 'secondary'] },
        ];
        // First, measure all slot widths
        const slotWidths = new Map();
        let allMeasurementsSuccessful = true;
        // Measure all slots with content
        const slots = ['start', 'end', 'primary', 'secondary'];
        slots.forEach((slot) => {
            var _a;
            if (this.el.classList.contains(`has-${slot}-content`)) {
                const slotElement = (_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(`slot[name="${slot}"]`);
                if (slotElement) {
                    // Check if the slot contains an img or ion-img
                    const assignedElements = slotElement.assignedElements({ flatten: true });
                    const hasImg = assignedElements.some((el) => {
                        if (el.tagName === 'IMG' || el.tagName === 'ION-IMG') {
                            return true;
                        }
                        // Check for nested images
                        return el.querySelector('img, ion-img');
                    });
                    // Temporarily allow slot to size to content by setting flex-basis
                    // to 'auto'. This ensures that slotted images can render at their
                    // intrinsic width for measurement.
                    if (hasImg) {
                        const { name } = slotPairs.find((pair) => pair.slots.includes(slot));
                        this.el.style.setProperty(`--${name}-size`, 'auto');
                    }
                    const width = slotElement.offsetWidth;
                    if (width > 0) {
                        slotWidths.set(slot, width);
                    }
                    else {
                        allMeasurementsSuccessful = false;
                    }
                }
            }
        });
        // Then set the CSS custom properties based on the largest width in each pair
        slotPairs.forEach(({ name, slots }) => {
            // Find the maximum width among the slots in this pair
            let maxWidth = 0;
            let hasAnyContent = false;
            slots.forEach((slot) => {
                var _a;
                if (slotWidths.has(slot)) {
                    hasAnyContent = true;
                    maxWidth = Math.max(maxWidth, (_a = slotWidths.get(slot)) !== null && _a !== void 0 ? _a : 0);
                }
            });
            // If at least one slot in the pair has content, set the min-width for the pair
            if (hasAnyContent && maxWidth > 0) {
                // Set a single CSS variable for the pair
                this.el.style.setProperty(`--${name}-size`, `${maxWidth}px`);
            }
        });
        return allMeasurementsSuccessful;
    }
    updateSlotClasses() {
        // Check if slots have content
        const slots = ['start', 'end', 'primary', 'secondary'];
        const classesToAdd = [];
        const classesToRemove = [];
        slots.forEach((slot) => {
            const slotHasContent = this.hasSlotContent(slot);
            const slotClass = `has-${slot}-content`;
            if (slotHasContent) {
                classesToAdd.push(slotClass);
            }
            else {
                classesToRemove.push(slotClass);
            }
        });
        // Force visibilities in certain conditions. This works by adding a class to the toolbar
        // named `show-{slot}`. This class will be added if the toolbar has the required slots
        // and does not have any of the excluded slots, otherwise it will be removed.
        // This is useful to enforce centering of the toolbar content when there are different amounts
        // of slots on either side of the toolbar.
        const conditions = [
            { name: 'end', requiredSlots: ['start'], excludeSlots: ['end', 'primary'] },
            { name: 'start', requiredSlots: ['end'], excludeSlots: ['start', 'secondary'] },
            { name: 'secondary', requiredSlots: ['primary'], excludeSlots: ['secondary', 'start'] },
            { name: 'primary', requiredSlots: ['secondary'], excludeSlots: ['primary', 'end'] },
        ];
        conditions.forEach((condition) => {
            const hasRequiredSlots = condition.requiredSlots.every((slot) => classesToAdd.includes(`has-${slot}-content`));
            const hasExcludedSlots = condition.excludeSlots.some((slot) => classesToAdd.includes(`has-${slot}-content`));
            const className = `show-${condition.name}`;
            if (hasRequiredSlots && !hasExcludedSlots) {
                classesToAdd.push(className);
            }
            else {
                classesToRemove.push(className);
            }
        });
        // Add classes to the toolbar element
        this.el.classList.add(...classesToAdd);
        this.el.classList.remove(...classesToRemove);
        // Update slot widths after classes have been updated
        this.updateSlotWidths();
    }
    hasSlotContent(slotName) {
        var _a;
        const slotNode = (_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(`slot[name="${slotName}"]`);
        return !!slotNode && slotNode.assignedNodes().length > 0;
    }
    childrenStyle(ev) {
        ev.stopPropagation();
        const tagName = ev.target.tagName;
        const updatedStyles = ev.detail;
        const newStyles = {};
        const childStyles = this.childrenStyles.get(tagName) || {};
        let hasStyleChange = false;
        Object.keys(updatedStyles).forEach((key) => {
            const childKey = `toolbar-${key}`;
            const newValue = updatedStyles[key];
            if (newValue !== childStyles[childKey]) {
                hasStyleChange = true;
            }
            if (newValue) {
                newStyles[childKey] = true;
            }
        });
        if (hasStyleChange) {
            this.childrenStyles.set(tagName, newStyles);
            forceUpdate(this);
        }
    }
    render() {
        const theme = getIonTheme(this);
        const childStyles = {};
        this.childrenStyles.forEach((style) => {
            Object.assign(childStyles, style);
        });
        return (h(Host, { key: '2c88dcfd26c7d24aaf479d1d993b5562d3619f6b', class: Object.assign(Object.assign({}, createColorClasses(this.color, {
                [theme]: true,
                'in-toolbar': hostContext('ion-toolbar', this.el),
            })), childStyles) }, h("div", { key: 'b0ee3cb68417c5013d7dd61b5d57bce29f575bad', class: "toolbar-background", part: "background" }), h("div", { key: '0626d61b71f5d0ba1ee37508cffbd63034f7b292', class: "toolbar-container", part: "container" }, h("slot", { key: 'e7a540e310d55ec6a1b61bee70bb285a75a3f019', name: "start", onSlotchange: () => this.updateSlotClasses }), h("slot", { key: '0f9e4d832691123209abca302532825efe5f7c98', name: "secondary", onSlotchange: () => this.updateSlotClasses }), h("div", { key: 'ff8fa2ab1ed25ed95dc2a889e07d9f2e85b7245d', class: "toolbar-content", part: "content" }, h("slot", { key: '78cc3ba30a35a71f464f7256343e2a523740a8e9' })), h("slot", { key: 'db88e674acc2befc1d4e5f2c2b03b48dde54d12e', name: "primary", onSlotchange: () => this.updateSlotClasses }), h("slot", { key: 'd8fd9de870c5d77a139442d38565686121c68887', name: "end", onSlotchange: () => this.updateSlotClasses }))));
    }
    static get is() { return "ion-toolbar"; }
    static get encapsulation() { return "shadow"; }
    static get originalStyleUrls() {
        return {
            "ios": ["toolbar.ios.scss"],
            "md": ["toolbar.md.scss"],
            "ionic": ["toolbar.ionic.scss"]
        };
    }
    static get styleUrls() {
        return {
            "ios": ["toolbar.ios.css"],
            "md": ["toolbar.md.css"],
            "ionic": ["toolbar.ionic.css"]
        };
    }
    static get properties() {
        return {
            "color": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "Color",
                    "resolved": "\"danger\" | \"dark\" | \"light\" | \"medium\" | \"primary\" | \"secondary\" | \"success\" | \"tertiary\" | \"warning\" | string & Record<never, never> | undefined",
                    "references": {
                        "Color": {
                            "location": "import",
                            "path": "../../interface",
                            "id": "src/interface.d.ts::Color"
                        }
                    }
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "The color to use from your application's color palette.\nDefault options are: `\"primary\"`, `\"secondary\"`, `\"tertiary\"`, `\"success\"`, `\"warning\"`, `\"danger\"`, `\"light\"`, `\"medium\"`, and `\"dark\"`.\nFor more information on colors, see [theming](/docs/theming/basics)."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "color"
            }
        };
    }
    static get elementRef() { return "el"; }
    static get listeners() {
        return [{
                "name": "ionStyle",
                "method": "childrenStyle",
                "target": undefined,
                "capture": false,
                "passive": false
            }];
    }
}
