{"version":3,"file":"nanopop.mjs","sources":["../src/index.ts"],"sourcesContent":["type Direction = 'top' | 'left' | 'bottom' | 'right';\ntype Alignment = 'start' | 'middle' | 'end';\n\nexport type VariantFlipOrder = {\n    start: string;\n    middle: string;\n    end: string;\n};\n\nexport type PositionFlipOrder = {\n    top: string;\n    right: string;\n    bottom: string;\n    left: string;\n};\n\nexport type NanoPopPosition = `${Direction}-${Alignment}` | Direction;\n\nexport type NanoPopOptions = {\n    container: DOMRect;\n    position: NanoPopPosition;\n    variantFlipOrder: VariantFlipOrder;\n    positionFlipOrder: PositionFlipOrder;\n    margin: number;\n    reference?: HTMLElement;\n    popper?: HTMLElement;\n    arrow?: HTMLElement;\n    padding?: number;\n};\n\ntype AvailablePositions = {\n    t: number;\n    b: number;\n    l: number;\n    r: number;\n};\n\ntype AvailableVariants = {\n    vs: number;\n    vm: number;\n    ve: number;\n    hs: number;\n    hm: number;\n    he: number;\n};\n\ntype PositionPairs = [Direction, Direction];\n\nexport type PositionMatch = 'ts' | 'tm' | 'te' | 'bs' | 'bm' | 'be' | 'ls' | 'lm' | 'le' | 'rs' | 'rm' | 're';\n\nexport interface NanoPop {\n    update(updatedOptions?: Partial<NanoPopOptions>): PositionMatch | null;\n}\n\nexport interface NanoPopConstructor {\n\n    /**\n     * @param reference Reference element\n     * @param popper Actual popper element\n     * @param options Optional options\n     */\n    (reference: HTMLElement, popper: HTMLElement, options?: Partial<NanoPopOptions>): NanoPop;\n\n    /**\n     * @param options Partial options which get merged with the current one\n     */\n    (options?: Partial<NanoPopOptions>): NanoPop;\n}\n\n// Export current version\nexport const version = VERSION;\n\n// Export default\nexport const defaults = {\n    variantFlipOrder: {start: 'sme', middle: 'mse', end: 'ems'},\n    positionFlipOrder: {top: 'tbrl', right: 'rltb', bottom: 'btrl', left: 'lrbt'},\n    position: 'bottom',\n    margin: 8,\n    padding: 0\n};\n\n/**\n * Repositions an element once using the provided options and elements.\n * @param reference Reference element\n * @param popper Popper element\n * @param opt Optional, additional options\n */\nexport const reposition = (\n    reference: HTMLElement,\n    popper: HTMLElement,\n    opt?: Partial<NanoPopOptions>\n): PositionMatch | null => {\n    const {\n        container,\n        arrow,\n        margin,\n        padding,\n        position,\n        variantFlipOrder,\n        positionFlipOrder\n    } = {\n        container: document.documentElement.getBoundingClientRect(),\n        ...defaults,\n        ...opt\n    };\n\n    /**\n     * Reset position to resolve viewport\n     * See https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed\n     */\n    const {left: originalLeft, top: originalTop} = popper.style;\n    popper.style.left = '0';\n    popper.style.top = '0';\n\n    const refBox = reference.getBoundingClientRect();\n    const popBox = popper.getBoundingClientRect();\n\n    /**\n     * Holds coordinates of top, left, bottom and right alignment\n     */\n    const positionStore: AvailablePositions = {\n        t: refBox.top - popBox.height - margin,\n        b: refBox.bottom + margin,\n        r: refBox.right + margin,\n        l: refBox.left - popBox.width - margin\n    };\n\n    /**\n     * Holds corresponding variants (start, middle, end).\n     * The values depend on horizontal / vertical orientation\n     */\n    const variantStore: AvailableVariants = {\n        vs: refBox.left,\n        vm: refBox.left + refBox.width / 2 - popBox.width / 2,\n        ve: refBox.left + refBox.width - popBox.width,\n        hs: refBox.top,\n        hm: refBox.bottom - refBox.height / 2 - popBox.height / 2,\n        he: refBox.bottom - popBox.height\n    };\n\n    // Extract position and variant\n    // Top-start -> top is \"position\" and \"start\" is the variant\n    const [posKey, varKey = 'middle'] = position.split('-');\n    const positions = positionFlipOrder[posKey as keyof PositionFlipOrder];\n    const variants = variantFlipOrder[varKey as keyof VariantFlipOrder];\n\n    // Try out all possible combinations, starting with the preferred one.\n    const {top, left, bottom, right} = container;\n\n    for (const p of positions) {\n        const vertical = (p === 't' || p === 'b');\n\n        // The position-value\n        let positionVal = positionStore[p as keyof AvailablePositions];\n\n        // Which property has to be changes.\n        const [positionKey, variantKey] = (vertical ? ['top', 'left'] : ['left', 'top']) as PositionPairs;\n\n        /**\n         * box refers to the size of the popper element. Depending on the orientation this is width or height.\n         * The limit is the corresponding, maximum value for this position.\n         */\n        const [positionSize, variantSize] = vertical ? [popBox.height, popBox.width] : [popBox.width, popBox.height];\n\n        const [positionMaximum, variantMaximum] = vertical ? [bottom, right] : [right, bottom];\n        const [positionMinimum, variantMinimum] = vertical ? [top, left] : [left, top];\n\n        // Skip pre-clipped values\n        if (positionVal < positionMinimum || (positionVal + positionSize + padding) > positionMaximum) {\n            continue;\n        }\n\n        for (const v of variants) {\n\n            // The position-value, the related size value of the popper and the limit\n            let variantVal = variantStore[((vertical ? 'v' : 'h') + v) as keyof AvailableVariants];\n\n            if (variantVal < variantMinimum || (variantVal + variantSize + padding) > variantMaximum) {\n                continue;\n            }\n\n            // Subtract popBox's initial position\n            variantVal -= popBox[variantKey];\n            positionVal -= popBox[positionKey];\n\n            // Apply styles and normalize viewport\n            popper.style[variantKey] = `${variantVal}px`;\n            popper.style[positionKey] = `${positionVal}px`;\n\n            if (arrow) {\n                // Calculate refBox's center offset from its variant position for arrow positioning\n                const refBoxCenterOffset = vertical ? refBox.width / 2 : refBox.height / 2;\n\n                // When refBox is larger than popBox, have the arrow's variant position be the center of popBox instead.\n                const arrowVariantVal = refBoxCenterOffset * 2 < variantSize ?\n                    refBox[variantKey] + refBoxCenterOffset : variantVal + variantSize / 2;\n\n                // Arrow position is either on one side of the popBox or the other.\n                if (positionVal < refBox[positionKey]) {\n                    positionVal += positionSize;\n                }\n\n                // Apply styles to arrow\n                arrow.style[variantKey] = `${arrowVariantVal}px`;\n                arrow.style[positionKey] = `${positionVal}px`;\n            }\n\n            return (p + v) as PositionMatch;\n        }\n    }\n\n    // Revert style values (won't work with styled-elements or similar systems)\n    // \"Fix\" for https://github.com/Simonwep/nanopop/issues/7\n    popper.style.left = originalLeft;\n    popper.style.top = originalTop;\n\n    return null;\n};\n\n/**\n * Creates a stateful popper.\n * You can either...\n * ... pass an options object: createPopper(<options>)\n * ... pass both the reference and popper: create(<ref>, <el>, <?options>)\n * ... pass nothing, in this case you'll have to set at least both a reference and a popper in update.\n *\n * @param reference | options Reference element or options\n * @param popper Popper element\n * @param options Optional additional options\n */\nexport const createPopper: NanoPopConstructor = (\n    reference?: HTMLElement | Partial<NanoPopOptions>,\n    popper?: HTMLElement,\n    options?: Partial<NanoPopOptions>\n): NanoPop => {\n\n    // Resolve options\n    const baseOptions: Partial<NanoPopOptions> = typeof reference === 'object' && !(reference instanceof HTMLElement) ?\n        reference : {reference, popper, ...options};\n\n    return {\n\n        /**\n         * Repositions the current popper.\n         * @param options Optional options which get merged with the current ones.\n         */\n        update(options: Partial<NanoPopOptions> = baseOptions): PositionMatch | null {\n            const {reference, popper} = Object.assign(baseOptions, options);\n\n            if (!popper || !reference) {\n                throw new Error('Popper- or reference-element missing.');\n            }\n\n            return reposition(reference, popper, baseOptions);\n        }\n    };\n};\n"],"names":["version","defaults","reposition","reference","popper","opt","container","arrow","margin","padding","position","variantFlipOrder","positionFlipOrder","originalLeft","originalTop","refBox","popBox","positionStore","variantStore","posKey","varKey","positions","variants","top","left","bottom","right","vertical","positionVal","positionKey","variantKey","positionSize","variantSize","positionMaximum","variantMaximum","positionMinimum","variantMinimum","v","variantVal","refBoxCenterOffset","arrowVariantVal","createPopper","options","baseOptions"],"mappings":"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"}