/**
 * Note! This file is forwarded via `src/style/exports.scss`
 * and exposed to consumers through the root `index.scss`.
 *
 * Consumers import it using:
 * `@use '@limetech/lime-elements' as lime-elements;`
 *
 * Legacy import paths (`dist/scss/mixins`) are maintained
 * for backward compatibility via copy rules in the Stencil config.
 */

@mixin visualize-keyboard-focus {
    &:focus {
        outline: none;
    }

    &:focus-visible {
        outline: none;
        box-shadow: var(--shadow-depth-8-focused);
    }
}

/**
* This can be used on a trigger element that opens a dropdown menu or a popover.
*/

@mixin visualize-aria-expanded($trigger-element) {
    :host([aria-expanded='true']),
    :host([aria-expanded]:not([aria-expanded='false'])) {
        #{$trigger-element} {
            box-shadow: var(--button-shadow-inset-pressed) !important;
        }
    }
}

@mixin in($media) {
    // ⛔️ As long as we don't have a script that generates a
    // `.css` files automatically, we cannot use this mixin.
    // we need to manually write the dark-mode CSS variables
    // inside this file: /style/color-palette-extended.css
    //
    // ⚠️ Also note that this mixin only puts styles
    // on the `:root` which means the `<html` level.
    // Therefore, it cannot be used inside components
    // to generate custom dark-mode styles.

    @if $media == dark-mode {
        @media (prefers-color-scheme: dark) {
            :root:not([data-theme='force-light']) {
                @content;
            }
        }
        :root[data-theme='force-dark'] {
            @content;
        }

        // @media (prefers-color-scheme: dark) {
        //     :host(:not([data-theme='force-light'])) & {
        //         @content;
        //     }
        // }
        // :host([data-theme='force-dark']) & {
        //     @content;
        // }
    }
}

$clickable-normal-state-transitions: (
    color var(--limel-clickable-transition-speed, 0.4s) ease,
    background-color var(--limel-clickable-transition-speed, 0.4s) ease,
    box-shadow var(--limel-clickable-transform-speed, 0.4s) ease,
    transform var(--limel-clickable-transform-speed, 0.4s)
        var(--limel-clickable-transform-timing-function, ease)
);

@mixin is-elevated-clickable(
    $color: var(--limel-theme-on-surface-color),
    $color--hovered: var(--limel-theme-on-surface-color),
    $background-color: var(--lime-elevated-surface-background-color),
    $background-color--hovered: var(--lime-elevated-surface-background-color)
) {
    transition: $clickable-normal-state-transitions;

    cursor: pointer;
    color: $color;
    background-color: $background-color;
    box-shadow: var(--button-shadow-normal);

    &:hover,
    &:focus,
    &:focus-visible {
        will-change: color, background-color, box-shadow, transform;
    }

    &:hover,
    &:focus-visible {
        transform: translate3d(0, -0.04rem, 0);
        color: $color--hovered;
        background-color: $background-color--hovered;
        box-shadow: var(--button-shadow-hovered);
    }

    &:active {
        --limel-clickable-transform-timing-function: cubic-bezier(
            0.83,
            -0.15,
            0.49,
            1.16
        );
        transform: translate3d(0, 0.05rem, 0);
        box-shadow: var(--button-shadow-pressed);
    }

    &:hover,
    &:active {
        --limel-clickable-transition-speed: 0.2s;
        --limel-clickable-transform-speed: 0.16s;
    }
}

@mixin is-elevated-inset-clickable(
    $color: var(--limel-theme-on-surface-color),
    $color--hovered: var(--limel-theme-on-surface-color),
    $background-color: var(--lime-elevated-surface-background-color),
    $background-color--hovered: var(--lime-elevated-surface-background-color),
    $background-color--inset: var(--limel-theme-surface-background-color)
) {
    transition: $clickable-normal-state-transitions;

    cursor: pointer;
    color: $color;
    background-color: $background-color;
    box-shadow: var(--button-shadow-normal);

    &:hover,
    &:focus,
    &:focus-visible {
        will-change: color, background-color, box-shadow, transform;
    }

    &:hover,
    &:focus-visible {
        transform: translate3d(0, 0.01rem, 0);
        color: $color--hovered;
        background-color: $background-color--hovered;
    }
    &:hover {
        box-shadow: var(--button-shadow-hovered);
    }

    &:active {
        --limel-clickable-transform-timing-function: cubic-bezier(
            0.83,
            -0.15,
            0.49,
            1.16
        );
        transform: translate3d(0, 0.05rem, 0);
        background-color: $background-color--inset;
        box-shadow: var(--button-shadow-inset-pressed);
    }

    &:hover,
    &:active {
        --limel-clickable-transition-speed: 0.2s;
        --limel-clickable-transform-speed: 0.16s;
    }
}

@mixin is-flat-clickable(
    $color: var(--limel-theme-on-surface-color),
    $background-color: transparent,
    $color--hovered: var(--limel-theme-on-surface-color),
    $background-color--hovered: var(--lime-elevated-surface-background-color)
) {
    transition: $clickable-normal-state-transitions;

    cursor: pointer;
    color: $color;
    background-color: $background-color;

    &:hover,
    &:focus,
    &:focus-visible {
        will-change: color, background-color, box-shadow, transform;
    }

    &:hover,
    &:focus-visible {
        transform: translate3d(0, 0.01rem, 0);
        color: $color--hovered;
        background-color: $background-color--hovered;
    }
    &:hover {
        box-shadow: var(--button-shadow-hovered);
    }

    &:active {
        --limel-clickable-transform-timing-function: cubic-bezier(
            0.83,
            -0.15,
            0.49,
            1.16
        );
        transform: translate3d(0, 0.05rem, 0);
        box-shadow: var(--button-shadow-pressed);
    }

    &:hover,
    &:active {
        --limel-clickable-transition-speed: 0.2s;
        --limel-clickable-transform-speed: 0.16s;
    }
}

@mixin is-flat-inset-clickable(
    $color: var(--limel-theme-on-surface-color),
    $background-color: transparent,
    $color--hovered: var(--limel-theme-on-surface-color),
    $background-color--hovered: var(--lime-elevated-surface-background-color),
    $background-color--inset: var(--limel-theme-surface-background-color)
) {
    transition: $clickable-normal-state-transitions;

    cursor: pointer;
    color: $color;
    background-color: $background-color;

    &:hover,
    &:focus,
    &:focus-visible {
        will-change: color, background-color, box-shadow, transform;
    }

    &:hover,
    &:focus-visible {
        transform: translate3d(0, -0.04rem, 0);
        color: $color--hovered;
        background-color: $background-color--hovered;
    }
    &:hover {
        box-shadow: var(--button-shadow-hovered);
    }

    &:active {
        --limel-clickable-transform-timing-function: cubic-bezier(
            0.83,
            -0.15,
            0.49,
            1.16
        );
        transform: translate3d(0, 0.05rem, 0);
        background-color: $background-color--inset;
        box-shadow: var(--button-shadow-inset-pressed);
    }

    &:hover,
    &:active {
        --limel-clickable-transition-speed: 0.2s;
        --limel-clickable-transform-speed: 0.16s;
    }
}

@mixin clear-all-button() {
    // NOTE: you may need to specify "position: absolute" and align the position where you use this mixin
    @include is-flat-clickable(
        $background-color: rgb(var(--contrast-900)),
        $color--hovered: rgb(var(--color-white)),
        $background-color--hovered: rgb(var(--color-red-default))
    );

    cursor: pointer;

    height: 1.25rem;
    width: 1.25rem;
    border-radius: 50%;

    background: {
        repeat: no-repeat;
        position: center;
        size: 0.75rem;
        image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2032%2032'%3E%3Cdefs/%3E%3Cpath%20fill='rgb(255,255,255)'%20d='M7.219%205.781L5.78%207.22%2014.563%2016%205.78%2024.781%207.22%2026.22%2016%2017.437l8.781%208.782%201.438-1.438L17.437%2016l8.782-8.781L24.78%205.78%2016%2014.563z'/%3E%3C/svg%3E");
    }
}

@mixin truncate-text() {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/**
 * This mixin will mask out the content that is close to
 * the edges of a scrollable area.
 * - If the scrollable content has `overflow-y`, use `vertically`
 * as an argument for `$direction`.
 - If the scrollable content has `overflow-x`, use `horizontally`
 * as an argument for `$direction`.
 *
 * For the visual effect to work smoothly, we need to make sure that
 * the size of the fade-out edge effect is the same as the
 * internal paddings of the scrollable area. Otherwise, content of a
 * scrollable area that does not have a padding will fade out before
 * any scrolling has been done.
 * This is why this mixin already adds paddings, which automatically
 * default to the size of the fade-out effect.
 * This size defaults to `1rem`, but to override the size use
 * `--limel-top-edge-fade-height` & `--limel-bottom-edge-fade-height`
 * when `vertically` argument is set, and use
 * `--limel-left-edge-fade-width` & `--limel-right-edge-fade-width`
 * when `horizontally` argument is set.
 * Of course you can also programmatically increase and decrease the
 * size of these variables for each edge, based on the amount of
 * scrolling that has been done by the user. In this case, make sure
 * to add a custom padding where the mixin is used, to override
 * the paddings that are automatically added by the mixin in the
 * compiled CSS code.
 */
@mixin fade-out-overflowed-content-on-edges($direction) {
    @if $direction == vertically {
        --limel-overflow-mask-vertical: linear-gradient(
            to bottom,
            transparent 0%,
            black calc(0% + var(--limel-top-edge-fade-height, 1rem)),
            black calc(100% - var(--limel-bottom-edge-fade-height, 1rem)),
            transparent 100%
        );

        -webkit-mask-image: var(--limel-overflow-mask-vertical);
        mask-image: var(--limel-overflow-mask-vertical);

        padding-top: var(--limel-top-edge-fade-height, 1rem);
        padding-bottom: var(--limel-bottom-edge-fade-height, 1rem);
    } @else if $direction == horizontally {
        --limel-overflow-mask-horizontal: linear-gradient(
            to right,
            transparent 0%,
            black calc(0% + var(--limel-left-edge-fade-width, 1rem)),
            black calc(100% - var(--limel-right-edge-fade-width, 1rem)),
            transparent 100%
        );

        -webkit-mask-image: var(--limel-overflow-mask-horizontal);
        mask-image: var(--limel-overflow-mask-horizontal);

        padding-left: var(--limel-left-edge-fade-width, 1rem);
        padding-right: var(--limel-right-edge-fade-width, 1rem);
    } @else {
        @error "Please specify the direction #{$direction}!";
    }
}

@mixin hide-helper-line-when-not-needed($component-tag) {
    // This is when:
    // - the component is not focused, or
    // - an interactive element within the component is not focused, or
    // - the component is valid

    :host(#{$component-tag}:focus),
    :host(#{$component-tag}:focus-visible),
    :host(#{$component-tag}:focus-within) {
        --limel-h-l-grid-template-rows-transition-speed: 0.46s;
        --limel-h-l-grid-template-rows: 1fr;
    }

    :host(#{$component-tag}) {
        --limel-h-l-grid-template-rows-transition-speed: 0.3s;
        --limel-h-l-grid-template-rows: 0fr;
    }

    :host(#{$component-tag}:focus),
    :host(#{$component-tag}:focus-visible),
    :host(#{$component-tag}:focus-within),
    :host(#{$component-tag}:hover) {
        limel-helper-line {
            will-change: grid-template-rows;
        }
    }
}

/**
* This mixin will add an animated underline to the bottom of an `a` elements.
* Note that you may need to add `all: unset;` –depending on your use case–
* before using this mixin.
*/
@mixin hyperlink(
    $color: rgb(var(--color-blue-default)),
    $color--hovered: rgb(var(--color-blue-default))
) {
    position: relative;
    cursor: pointer;
    transition: color 0.2s ease;
    color: $color;

    &:before {
        transition:
            opacity 0.2s ease,
            transform 0.3s ease-out;
        content: '';
        position: absolute;
        inset: auto 0 0 0;
        width: calc(100% - 0.5rem);
        margin: auto;
        height: 0.125rem;
        border-radius: 1rem;

        background-color: currentColor;
        opacity: 0;
        transform: scale(0.6);
    }

    &:hover {
        color: $color--hovered;
        &:before {
            opacity: 0.3;
            transform: scale(1);
        }
    }
}

/**
* This mixin creates a cross-browser font stack.
* - `sans-serif` can be used for the UI of the components.
* - `monospace` can be used for code.
*
* ⚠️ If we change the font stacks, we need to update
* 1. the consumer documentation in `README.md`, and
* 2. the CSS variables of `--kompendium-example-font-family`
* in the `<style>` tag of `index.html`.
*/
@mixin font-family($letterform) {
    @if $letterform == sans-serif {
        font-family: ui-sans-serif, system-ui, sans-serif;
    } @else if $letterform == monospace {
        font-family:
            ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas,
            'DejaVu Sans Mono', monospace;
    } @else {
        @error "Please specify the font-family #{$letterform}!";
    }
}

/**
* This mixin is a hack, using old CSS syntax
* to enable you to truncate a piece of text,
* after a certain number of lines.
*/
@mixin truncate-text-on-line($max-lines) {
    display: -webkit-box;
    overflow: hidden;
    white-space: normal;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: $max-lines;
}

/**
* This mixin will add a chessboard background pattern,
* typically used to visualize transparency.
*/
@mixin add-chessboard-background {
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%208%208'%20style='fill-rule:evenodd;'%3E%3Cpath%20fill='rgba(186,186,192,0.16)'%20d='M0%200h4v4H0zM4%204h4v4H4z'/%3E%3C/svg%3E");
    background-size: 0.5rem;
}

/**
* Make a container resizable by the user.
* This is used in the documentations and examples
* of some components, to demonstrate how the component
* behaves in a resizable container.
*/
@mixin is-resizable(
    $direction: both,
    // can be `horizontal`, `vertical`, or `both`
    $width: 100%,
    $min-width: 10rem,
    $max-width: 100%,
    $height: 100%,
    $min-height: 5rem,
    $max-height: 50rem
) {
    box-sizing: border-box;
    position: relative;

    border: 1px dashed rgb(var(--contrast-700));
    padding: 0.75rem;
    padding-bottom: 2rem;

    resize: $direction;
    overflow: auto;

    min-width: $min-width;
    width: $width;
    max-width: $max-width;

    min-height: $min-height;
    height: $height;
    max-height: $max-height;

    border-radius: 0.5rem;

    &::after {
        content: 'Resize me ⤵';
        font-size: 0.75rem;
        position: absolute;
        right: 0.25rem;
        bottom: 0.25rem;
    }
}

// Hide element visually while keeping it accessible to assistive technologies
// See: https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/
// See: https://hugogiraudel.com/2016/10/13/css-hide-and-seek/
@mixin visually-hidden {
    position: absolute;
    width: 0;
    height: 0;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

// These mixins below are designed to apply the necessary visual effects,
// when the `tiltFollowingTheCursor` utility function from `3d-tilt-hover-effect.ts`
// is implemented in a component.
@mixin parent-of-the-3d-element {
    isolation: isolate;
    transform-style: preserve-3d;

    perspective: 1000px;
    @media (prefers-reduced-motion) {
        perspective: 2000px;
    }
}

@mixin the-3d-element {
    position: relative;

    transition-duration: 0.8s;
    transition-property: transform, box-shadow, background-color;
    transition-timing-function: ease-out;
    transform: scale3d(1, 1, 1) rotate3d(0, 0, 0, 0deg);

    &:focus {
        outline: none;
    }

    &:hover,
    &:focus,
    &:focus-visible,
    &:focus-within {
        will-change: background-color, box-shadow, transform;
    }

    &:hover,
    &:focus,
    &:focus-visible,
    &:active {
        transition-duration: 0.2s;
    }

    &:hover,
    &:focus-visible {
        box-shadow: var(--button-shadow-hovered), var(--shadow-depth-16);
    }

    &:hover {
        transform: scale3d(1.01, 1.01, 1.01)
            rotate3d(var(--limel-3d-hover-effect-rotate3d));
    }
    &:focus-visible {
        outline: none;
        transform: scale3d(1.01, 1.01, 1.01);
    }

    &:hover {
        limel-3d-hover-effect-glow {
            --limel-3d-hover-effect-glow-opacity: 0.5;
            @media (prefers-reduced-motion) {
                --limel-3d-hover-effect-glow-opacity: 0.2;
            }
        }
    }
}

@mixin the-3d-element--clickable {
    cursor: pointer;
    box-shadow: var(--button-shadow-normal);

    &:hover,
    &:focus-visible {
        // seems repetitive. But it's required for some scenarios, like for info tiles.
        box-shadow: var(--button-shadow-hovered), var(--shadow-depth-16);
    }

    &:active {
        transform: scale3d(1, 1, 1) rotate3d(0, 0, 0, 0deg);
        box-shadow: var(--button-shadow-pressed);
    }

    &:focus-visible {
        box-shadow: var(--shadow-depth-8-focused), var(--button-shadow-hovered);
    }

    &:focus-visible:active {
        box-shadow: var(--shadow-depth-8-focused), var(--button-shadow-pressed);
    }
}

/**
 * Drag to reorder mixins
 */
@mixin is-draggable-item(
    $border-radius-while-being-dragged: 0.5rem,
    $transform-scale-while-being-dragged: 1.01
) {
    // Note: Since SortableJS add `transitions`s in real-time as styles,
    // we cannot define `transition` properties here,

    &:has(limel-drag-handle:hover),
    &:has(limel-drag-handle:focus-within),
    &.is-elevated,
    &.sortable-chosen // this class is added by SortableJS, when item is being dragged
    {
        will-change: transform, box-shadow, border-radius, background-color;
    }

    &.sortable-ghost {
        border-radius: $border-radius-while-being-dragged;
        box-shadow:
            var(--shadow-depth-64),
            0 0.65rem 0.75rem -0.425rem rgb(var(--color-black), 0.3);
    }

    &.sortable-chosen {
        border-radius: $border-radius-while-being-dragged;
        background-color: var(--lime-elevated-surface-background-color);
    }

    &.is-elevated[draggable='false'] {
        // Components must add this `is-elevated` class on their own,
        // the moment the item is dropped and keep it around briefly
        // so visual transitions (box-shadow, etc.) can fade
        // out without being clipped by neighboring content.
        animation: sit-here 0.8s forwards;
    }

    @keyframes sit-here {
        0% {
            border-radius: $border-radius-while-being-dragged;
            background-color: var(--lime-elevated-surface-background-color);
            box-shadow:
                var(--shadow-depth-64),
                0 0.65rem 0.75rem -0.425rem rgb(var(--color-black), 0.3);
        }

        100% {
            box-shadow: none;
        }
    }
}

/**
 * The breakpoints below are used to create responsive designs
 * in Lime's products. Therefore, they are here to get distributed
 * to all components in other private repos, which rely on this `mixins`
 * file, to create consistent styles.
 *
 * :::important
 * In very rare cases you should used media queries!
 * Nowadays, there are many better ways of achieving responsive design
 * without media queries. For example, using CSS Grid, Flexbox, and their features.
 * :::
 */
$narrow-viewport-breakpoint: 800px;
$medium-viewport-breakpoint: 1023px;
// At this breakpoint, `limel-dialog` switches the layout of `slot="button"`
// and stretches the buttons to full-width.
$narrow-dialog-breakpoint: 760px;

/**
  * Media query mixins for responsive design based on screen width.
  * Note that these mixins do not detect the device type!
  */
@mixin when-viewport-width-is-narrow {
    // What our products consider as mobile (held in portrait mode)
    @media (max-width: #{$narrow-viewport-breakpoint}) {
        @content;
    }
}

@mixin when-viewport-width-is-not-narrow {
    // Wider than what our products consider as mobile (held in portrait mode)
    @media (min-width: #{$narrow-viewport-breakpoint + 1px}) {
        @content;
    }
}

@mixin when-viewport-width-is-medium {
    // What our products consider as to be bigger than a phone,
    // but not bigger than a tablet (held in landscape mode)
    // or a small laptop screen.
    @media (min-width: #{$narrow-viewport-breakpoint + 1px}) and (max-width: #{$medium-viewport-breakpoint}) {
        @content;
    }
}

@mixin when-viewport-width-is-not-large {
    // What our products consider as a phone,
    // or a small tablet (held in landscape mode) or a small laptop screen.
    @media (max-width: #{$medium-viewport-breakpoint}) {
        @content;
    }
}

@mixin when-viewport-width-is-large {
    // What our products consider wider than tablet (held in landscape mode)
    @media (min-width: #{$medium-viewport-breakpoint + 1px}) {
        @content;
    }
}
