<template>
  <div
    ref="containerRef"
    :class="[
      { '--expand-show': isExpandOpened },
      { '--alphabet': alphabet },
      { '--mobile-expand': mobileExpand },
      { '--micro': micro },
    ]"
    class="ui-expand-row"
  >
    <div
      ref="wrapperRef"
      :class="[{ '--more-show': isElementsOverflow }]"
      class="ui-expand-row__wrapper"
    >
      <slot name="prepend">
        <span
          v-if="preText"
          class="ui-expand-row__pre-text"
        >{{ preText }}</span>
      </slot>
      <slot>
        <UiChips
          v-for="(item, index) in items"
          :key="`${index}-${item.title}`"
          :item="item"
          :alphabet="alphabet"
          is-link
          :mini="mini"
          :micro="micro"
        />
      </slot>
      <div
        v-if="!hideMore"
        v-show="isElementsOverflow"
        :class="{'--small': mini, '--micro': micro}"
        class="ui-expand-row__more-wrapper"
      >
        <UiChips
          :item="{
            title: moreButtonName,
            icon: isExpandOpened ? 'minus' : 'plus',
          }"
          :mini="mini"
          :micro="micro"
          class="ui-expand-row__button --more"
          @click="onMoreClick"
        />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import type { IChipsItem } from '../../types';

defineProps<{
  items?: IChipsItem[];
  alphabet?: boolean;
  hideMore?: boolean;
  mini?: boolean;
  micro?: boolean;
  mobileExpand?: boolean;
  preText?: string;
}>();

const { t } = useI18n();

const emit = defineEmits<{
  (eventName: 'more', eventValue: boolean): void
}>();

const containerRef = ref<HTMLElement>();

const wrapperRef = ref<HTMLElement>();

const isExpandOpened = ref(false);

const moreButtonName = computed(() => isExpandOpened.value ? t('less') : t('more'));

const isElementsOverflow = ref(false);

/**
 * Обрезку делает CSS (`max-height: var(--chips-height)` + `overflow: hidden`), здесь только
 * определяем, вылез ли контент за одну строку, — от этого зависит кнопка «еще».
 */
function checkIsElementOverflow() {
  // в раскрытом виде контейнер тянется под контент, замер дал бы ложное «переполнения нет»
  if (!containerRef.value || isExpandOpened.value) {
    return;
  }

  isElementsOverflow.value = containerRef.value.scrollHeight > containerRef.value.clientHeight;
}

/**
 * Обрезанные строки остаются в разметке (нужны краулеру), но не должны ловить таб-фокус
 * и попадать в дерево доступности — гасим их через `inert`.
 */
function updateHiddenItemsInertness() {
  if (!containerRef.value || !wrapperRef.value) {
    return;
  }

  const visibleHeight = containerRef.value.clientHeight;

  for (const child of Array.from(wrapperRef.value.children) as HTMLElement[]) {
    if (child.classList.contains('ui-expand-row__more-wrapper')) {
      continue;
    }

    child.inert = !isExpandOpened.value && child.offsetTop + child.offsetHeight > visibleHeight + 1;
  }
}

function measure() {
  checkIsElementOverflow();
  updateHiddenItemsInertness();
}

function onMoreClick() {
  isExpandOpened.value = !isExpandOpened.value;
  emit('more', isExpandOpened.value);
}

watch(isExpandOpened, () => nextTick(updateHiddenItemsInertness));

let resizeObserver: ResizeObserver | null = null;

onMounted(() => {
  if (!wrapperRef.value) {
    return;
  }

  // следим за внутренней обёрткой: её высота меняется и от ресайза, и от смены набора чипсов
  resizeObserver = new ResizeObserver(() => measure());
  resizeObserver.observe(wrapperRef.value);
});

onBeforeUnmount(() => {
  resizeObserver?.disconnect();
  resizeObserver = null;
});
</script>

<style lang="scss">
.ui-expand-row {
  display: grid;
  max-width: 100%;
  position: relative;
  min-width: 0;
  flex-shrink: 0;
  max-height: var(--chips-height);
  overflow: hidden;

  @include from-br(sm) {
    max-width: 100%;
  }
}

.ui-expand-row.--alphabet {
  @include to-br(sm) {
    max-height: 100%;
  }
}

.ui-expand-row.--mobile-expand {
  @include to-br(sm) {
    max-height: 100%;
  }
}

/* Обрезка идёт по высоте одной строки, а она зависит от размера чипса — иначе из-под края
   выглядывает следующий ряд. Правило стоит до `--expand-show`: у них равная специфичность. */
.ui-expand-row.--micro {
  max-height: var(--chips-height-micro);
}

.ui-expand-row__wrapper {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.ui-expand-row.--alphabet .ui-expand-row__wrapper {
  gap: 8px;

  @include from-br(sm) {
    gap: 4px;
  }
}

.ui-expand-row.--expand-show {
  max-height: 100%;
}

.ui-expand-row__more-wrapper {
  position: absolute;
  bottom: 0;
  right: 0;
  height: var(--chips-height);
  z-index: $z-index-plus;
  padding-left: 40px;
  background: linear-gradient(90deg, $expand-row-fade-start 0%, var(--expand-row-fade-end, #{$expand-row-fade-end}) 22.64%);
}

.ui-expand-row__more-wrapper.--small {
  top: 0;

  @include from-br(sm) {
    height: var(--chips-height-small);
  }
}

.ui-expand-row__more-wrapper.--micro {
  top: 0;
  height: var(--chips-height-micro);
}

.ui-expand-row__button.--more {
  text-transform: capitalize;
}

.ui-expand-row.--expand-show .ui-expand-row__more-wrapper {
  position: static;
  text-transform: capitalize;
  padding-left: 0;
}

.ui-expand-row.--expand-show .ui-expand-row__wrapper.--more-show::after {
  content: none;
}

.ui-expand-row__wrapper > .category-nav__links {
  padding-right: 4px;
  border-right: 1px solid $expand-row-link-border;
}

.ui-expand-row__pre-text {
  @include font-sm;

  color: $expand-row-pre-text;
}
</style>
