<script setup lang="ts">
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';

import { onKeyStroke } from '@vueuse/core';

const props = withDefaults(
  defineProps<{
    inspectorOpen?: boolean;
    treeLabel?: string;
  }>(),
  {
    inspectorOpen: false,
    treeLabel: '文件',
  },
);

const emit = defineEmits<{
  closeInspector: [];
}>();

const mobileTreeOpen = ref(false);
const mobileTriggerRef = ref<HTMLButtonElement>();
const mobileCloseRef = ref<HTMLButtonElement>();
const inspectorCloseRef = ref<HTMLButtonElement>();
let inspectorReturnFocus: HTMLElement | undefined;

watch(mobileTreeOpen, async (open) => {
  await nextTick();
  if (open) mobileCloseRef.value?.focus();
  else mobileTriggerRef.value?.focus();
});

watch(
  () => props.inspectorOpen,
  async (open) => {
    if (open) {
      inspectorReturnFocus = document.activeElement as HTMLElement;
      await nextTick();
      inspectorCloseRef.value?.focus();
    } else {
      inspectorReturnFocus?.focus();
      inspectorReturnFocus = undefined;
    }
  },
);

onKeyStroke('Escape', () => {
  if (mobileTreeOpen.value) {
    mobileTreeOpen.value = false;
  } else if (props.inspectorOpen) {
    emit('closeInspector');
  }
});

function trapMobileTreeFocus(event: KeyboardEvent) {
  if (!mobileTreeOpen.value || event.key !== 'Tab') return;
  const panel = mobileCloseRef.value?.closest<HTMLElement>(
    '.document-editor-workbench__tree',
  );
  if (!panel) return;
  const focusable = [
    ...panel.querySelectorAll<HTMLElement>(
      'button:not(:disabled), [href], input:not(:disabled), [tabindex]:not([tabindex="-1"])',
    ),
  ].filter((item) => item.offsetParent !== null);
  if (focusable.length === 0) return;
  const first = focusable[0];
  const last = focusable.at(-1);
  if (event.shiftKey && document.activeElement === first) {
    event.preventDefault();
    last?.focus();
  } else if (!event.shiftKey && document.activeElement === last) {
    event.preventDefault();
    first?.focus();
  }
}

onMounted(() => document.addEventListener('keydown', trapMobileTreeFocus));
onBeforeUnmount(() =>
  document.removeEventListener('keydown', trapMobileTreeFocus),
);
</script>

<template>
  <section class="document-editor-workbench">
    <button
      ref="mobileTriggerRef"
      class="document-editor-workbench__mobile-trigger"
      type="button"
      @click="mobileTreeOpen = true"
    >
      {{ treeLabel }}
    </button>
    <aside
      class="document-editor-workbench__tree"
      :class="{ 'is-mobile-open': mobileTreeOpen }"
      :aria-label="treeLabel"
      :aria-modal="mobileTreeOpen || undefined"
      :role="mobileTreeOpen ? 'dialog' : 'region'"
    >
      <div class="document-editor-workbench__tree-mobile-head">
        <strong>{{ treeLabel }}</strong>
        <button
          ref="mobileCloseRef"
          type="button"
          aria-label="关闭文件列表"
          @click="mobileTreeOpen = false"
        >
          关闭
        </button>
      </div>
      <slot name="tree" :close="() => (mobileTreeOpen = false)"></slot>
    </aside>
    <button
      v-if="mobileTreeOpen"
      class="document-editor-workbench__scrim"
      type="button"
      aria-label="关闭文件列表"
      @click="mobileTreeOpen = false"
    ></button>
    <main
      class="document-editor-workbench__main"
      :aria-hidden="mobileTreeOpen || undefined"
      :inert="mobileTreeOpen || undefined"
    >
      <div v-if="$slots.toolbar" class="document-editor-workbench__toolbar">
        <slot name="toolbar"></slot>
      </div>
      <div class="document-editor-workbench__editor">
        <slot></slot>
      </div>
      <slot name="status"></slot>
    </main>
    <Transition name="document-inspector">
      <aside
        v-if="inspectorOpen"
        class="document-editor-workbench__inspector"
        role="complementary"
        aria-label="文档检查面板"
      >
        <button
          ref="inspectorCloseRef"
          class="document-editor-workbench__inspector-close"
          type="button"
          aria-label="关闭检查面板"
          @click="emit('closeInspector')"
        >
          关闭
        </button>
        <slot name="inspector"></slot>
      </aside>
    </Transition>
  </section>
</template>

<style scoped>
.document-editor-workbench {
  position: relative;
  display: grid;
  flex: 1;
  grid-template-columns: minmax(248px, 272px) minmax(0, 1fr);
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  background: hsl(var(--surface-panel));
  border: 1px solid hsl(var(--line-subtle));
  border-radius: var(--radius-toolbar);
}

.document-editor-workbench__tree {
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  background: hsl(var(--surface-subtle));
  border-right: 1px solid hsl(var(--line-subtle));
}

.document-editor-workbench__main {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}

.document-editor-workbench__toolbar {
  z-index: 2;
  min-height: 48px;
  background: hsl(var(--toolbar-bg) / 92%);
  border-bottom: 1px solid hsl(var(--toolbar-border));
  backdrop-filter: blur(var(--glass-blur));
}

.document-editor-workbench__editor {
  display: flex;
  flex: 1;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}

.document-editor-workbench__inspector {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 4;
  width: min(360px, 88%);
  overflow: auto;
  background: hsl(var(--surface-elevated));
  border-left: 1px solid hsl(var(--line-subtle));
  box-shadow: var(--shadow-float);
}

.document-editor-workbench__inspector-close,
.document-editor-workbench__tree-mobile-head,
.document-editor-workbench__mobile-trigger,
.document-editor-workbench__scrim {
  display: none;
}

.document-editor-workbench__inspector-close {
  position: sticky;
  top: var(--space-2);
  z-index: 1;
  float: right;
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  padding: 0 var(--space-3);
  margin: var(--space-2);
  color: hsl(var(--text-muted));
  cursor: pointer;
  background: hsl(var(--surface-subtle));
  border: 0;
  border-radius: var(--radius-control);
}

.document-editor-workbench__inspector-close:hover {
  color: hsl(var(--foreground));
  background: hsl(var(--nav-item-hover));
}

.document-editor-workbench__inspector-close:focus-visible {
  outline: 2px solid hsl(var(--primary) / 72%);
  outline-offset: 2px;
}

.document-inspector-enter-active,
.document-inspector-leave-active {
  transition:
    opacity var(--motion-duration-base) var(--motion-ease-standard),
    transform var(--motion-duration-medium) var(--motion-ease-standard);
}

.document-inspector-enter-from,
.document-inspector-leave-to {
  opacity: 0;
  transform: translateX(12px);
}

@media (max-width: 900px) {
  .document-editor-workbench {
    grid-template-columns: minmax(0, 1fr);
  }

  .document-editor-workbench__mobile-trigger {
    position: absolute;
    top: var(--space-2);
    left: var(--space-2);
    z-index: 3;
    display: inline-flex;
    align-items: center;
    min-height: 32px;
    padding: 0 var(--space-3);
    color: hsl(var(--nav-item-active-foreground));
    cursor: pointer;
    background: hsl(var(--nav-item-active));
    border: 0;
    border-radius: var(--radius-control);
    transition:
      color var(--motion-duration-fast) var(--motion-ease-standard),
      background-color var(--motion-duration-fast) var(--motion-ease-standard);
  }

  .document-editor-workbench__mobile-trigger:hover {
    color: hsl(var(--foreground));
    background: hsl(var(--nav-tool-hover));
  }

  .document-editor-workbench__mobile-trigger:focus-visible,
  .document-editor-workbench__tree-mobile-head button:focus-visible {
    outline: 2px solid hsl(var(--primary) / 72%);
    outline-offset: 2px;
  }

  .document-editor-workbench__tree {
    position: absolute;
    inset: 0 auto 0 0;
    z-index: 6;
    visibility: hidden;
    width: min(320px, 88%);
    box-shadow: var(--shadow-float);
    opacity: 0;
    transform: translateX(-12px);
    transition:
      visibility 0s linear var(--motion-duration-medium),
      opacity var(--motion-duration-base) var(--motion-ease-standard),
      transform var(--motion-duration-medium) var(--motion-ease-standard);
  }

  .document-editor-workbench__tree.is-mobile-open {
    visibility: visible;
    opacity: 1;
    transform: translateX(0);
    transition-delay: 0s;
  }

  .document-editor-workbench__tree-mobile-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    min-height: 48px;
    padding: 0 var(--space-4);
    border-bottom: 1px solid hsl(var(--line-subtle));
  }

  .document-editor-workbench__tree-mobile-head button {
    color: hsl(var(--text-muted));
    cursor: pointer;
    background: transparent;
    border: 0;
    border-radius: var(--radius-control);
  }

  .document-editor-workbench__scrim {
    position: absolute;
    inset: 0;
    z-index: 5;
    display: block;
    cursor: pointer;
    background: hsl(var(--overlay));
    border: 0;
  }

  .document-editor-workbench__toolbar {
    padding-left: calc(var(--space-8) * 3 + var(--space-2));
  }
}

@media (prefers-reduced-motion: reduce) {
  .document-editor-workbench__tree,
  .document-inspector-enter-active,
  .document-inspector-leave-active {
    transition-duration: 0.01ms;
  }
}
</style>
