<template>
  <div class="graphics">
    <div class="input-search">
      <div class="btn">
        <t-icon name="search" />
      </div>
      <t-input
        v-model="search"
        @change="onSearch"
        @enter="onSearch"
        placeholder="搜索"
      />

      <div class="ml-16">
        <t-tooltip content="展开/折叠">
          <t-icon
            name="menu-fold"
            class="hover"
            style="font-size: 16px"
            @click="onFold"
          />
        </t-tooltip>
      </div>
    </div>
    <div class="groups-panel">
      <div class="groups">
        <div
          v-for="group in groups"
          :class="group.name === activedGroup ? 'active' : ''"
          @click="groupChange(group.name)"
        >
          <t-icon :name="group.icon" />
          {{ group.name }}
        </div>
      </div>
      <div class="list" :class="groupType ? 'two-columns' : ''">
        <div v-if="loading" class="center mt-16">
          <t-loading text="加载中..." size="small" :indicator="false" />
        </div>
        <template v-else>
          <div v-if="activedGroup === '我的'" class="px-16 mt-12 mb-8 ml-4">
            <a @click="onCreateFolder">+ 新建文件夹</a>
          </div>
          <t-collapse v-model="activedPanels[activedGroup]">
            <t-collapse-panel
              :value="item.name"
              v-for="item in subGroups"
              :key="item.name"
            >
              <template #header>
                <div class="flex middle">
                  <div v-if="item.edited" @click.stop>
                    <t-input
                      v-model="item.label"
                      style="width: 140px"
                      @blur="createFoder"
                      @enter="createFoder"
                      @keyup="onKeyHeader"
                      :autofocus="true"
                    />
                  </div>
                  <div v-else>
                    {{ item.name }}
                  </div>
                </div>
              </template>
              <template #headerRightContent>
                <t-space size="small" @click.stop tabindex="0">
                  <t-upload
                    v-if="item.canEdited || item.name === '我的组件'"
                    action="/api/image/upload"
                    accept="image/*"
                    :headers="headers"
                    :data="updataData"
                    :auto-upload="true"
                    :upload-all-files-in-one-request="false"
                    @selectChange="onSelectFiles(item)"
                    @success="fileSuccessed"
                    theme="custom"
                  >
                    <t-icon name="image" class="hover" />
                  </t-upload>

                  <template v-if="item.canEdited">
                    <t-icon
                      name="edit"
                      class="hover"
                      @click="onEditHeader(item)"
                    />
                    <t-popconfirm
                      content="确认删除该文件夹吗"
                      placement="left"
                      @confirm="delFolder(item)"
                    >
                      <t-icon name="delete" class="hover" />
                    </t-popconfirm>
                  </template>
                </t-space>
              </template>

              <template v-for="elem in item.list">
                <div
                  v-show="elem.visible !== false"
                  class="graphic"
                  :draggable="true"
                  @dragstart="dragStart($event, elem)"
                  @click.prevent="dragStart($event, elem)"
                  @dblclick.stop="open(elem)"
                  @contextmenu="onContextMenu($event, item, elem)"
                >
                  <t-image
                    v-if="!elem.svg && elem.image"
                    :src="elem.image"
                    :lazy="true"
                    fit="contain"
                    @load="loadImage(elem)"
                  />
                  <div class="svg-box" v-else-if="elem.svg" v-html="elem.svg" />
                  <svg v-else class="l-icon" aria-hidden="true">
                    <use :xlink:href="'#' + elem.icon"></use>
                  </svg>
                  <p :title="elem.name">{{ elem.name }}</p>
                  <div class="price" v-if="elem.price > 0">
                    ￥{{ elem.price }}
                  </div>
                </div>
              </template>
              <div
                v-if="!item.list || !item.list.length"
                class="gray"
                style="white-space: nowrap; margin-left: 34px"
              >
                暂无数据
              </div>
            </t-collapse-panel>
          </t-collapse>
        </template>
      </div>
    </div>

    <div
      class="context-menu-box"
      ref="contextmenuDom"
      v-show="contextmenu.visible"
      tabindex="0"
      :style="contextmenu.style"
      @blur="hideContextmenu"
    >
      <t-menu class="context-menu" @change="onMenu" expandType="popup">
        <t-submenu
          value="move"
          title="移动到"
          v-if="contextmenu.subMenus.length"
          :disabled="contextmenu.component['3d']"
        >
          <t-menu-item
            v-for="subMenu in contextmenu.subMenus"
            :value="'move:' + subMenu.name"
          >
            {{ subMenu.name }}
          </t-menu-item>
        </t-submenu>
        <t-menu-item value="edit" :disabled="!contextmenu.component.component">
          编辑
        </t-menu-item>
        <t-menu-item value="del" :disabled="contextmenu.component['3d']">
          删除
        </t-menu-item>
      </t-menu>
    </div>

    <t-dialog
      v-if="delDialog.show"
      theme="danger"
      header="删除"
      :visible="true"
      @close="delDialog.show = false"
      @confirm="delComponet"
    >
      确定删除该数据吗？删除后不可恢复！
    </t-dialog>
    <t-dialog
      v-if="chargeDialog.show"
      :header="chargeDialog.data.name"
      :visible="true"
      @close="chargeDialog.show = false"
      width="70%"
      :top="8"
    >
      <t-image :src="chargeDialog.data.image" />
      <template #footer>
        <div class="flex between" style="margin-top: -7px">
          <p>付费项目，购买后查看并使用</p>
          <div>
            <label>金额：</label>
            <label class="bland">￥{{ chargeDialog.data.price }}</label>
            <t-button class="primary ml-12" @click="onSubmitOrder"
              >购买</t-button
            >
          </div>
        </div>
      </template>
    </t-dialog>

    <t-dialog
      v-if="wechatPayDialog.show"
      v-model:visible="wechatPayDialog.show"
      class="pay-dialog"
      header="乐吾乐收银台"
      :close-on-overlay-click="false"
      :top="95"
      :width="700"
      confirm-btn="支付完成"
      :cancel-btn="null"
      @close="getPayResult"
    >
      <WechatPay
        :order="wechatPayDialog.order"
        :code-url="wechatPayDialog.order.codeUrl"
        @success="onChargeSuccess"
      />
    </t-dialog>
  </div>
</template>

<script lang="ts" setup>
import { onMounted, onUnmounted, reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { MessagePlugin } from 'tdesign-vue-next';
import axios from 'axios';
import { deepClone } from '@meta2d/core';

import { cases, shapes, formComponents, templates } from '@/services/defaults';
import { charts } from '@/services/echarts';
import { getFolders, makeSvg } from '@/services/png';
import {
  addCollection,
  getComponentsList,
  getLe5leV,
  updateCollection,
} from '@/services/api';
import { convertPen } from '@/services/upgrade';
import { isGif } from '@/services/utils';
import { autoSave, delAttrs } from '@/services/common';
import { debounce, throttle } from '@/services/debouce';
import { searchObjectPinyin } from '@/services/pinyin';
import { getCookie } from '@/services/cookie';

import WechatPay from './WechatPay.vue';
import { filename } from '@/services/file';

const router = useRouter();

const activedGroup = ref('');

const groups = reactive([
  {
    icon: 'desktop',
    name: '场景',
    key: '',
    class: 'tow',
  },
  {
    icon: 'root-list',
    name: '模板',
    key: '',
  },
  {
    icon: 'chart',
    name: '图表',
    key: 'chart',
  },
  {
    icon: 'image',
    name: '素材',
    key: '',
  },
  {
    icon: 'control-platform',
    name: '图元',
    key: '',
  },
  {
    icon: 'relativity',
    name: '控件',
    key: '',
  },
  {
    icon: 'chart-bubble',
    name: '图形',
    key: 'shape',
  },
  {
    icon: 'app',
    name: '我的',
    key: '',
  },
]);
const subGroups = ref<any[]>([]);
const groupType = ref(0);
const activedPanels = reactive<any>({});

const caseCaches = [];
const templateCaches = [];
const materials = [];
const pngs = [];

let dropped = false;
const chargeDialog = reactive<any>({});
const wechatPayDialog = reactive<any>({
  show: false,
});

const search = ref('');

const loading = ref(false);

const headers = {
  Authorization: 'Bearer ' + (localStorage.token || getCookie('token') || ''),
};
const updataData = { directory: '/项目' };

const groupChange = async (name: string) => {
  activedGroup.value = name;
  groupType.value = 0;
  switch (name) {
    case '场景':
      if (!caseCaches.length) {
        loading.value = true;
        caseCaches.push(...(await getCaseProjects(name)));
        for (const group of cases) {
          group.list = [];
          for (const item of caseCaches) {
            if (item.case === group.name) {
              group.list.push(item);
            }
          }
        }
        loading.value = false;
      }

      groupType.value = 1;
      subGroups.value = cases;

      break;
    case '模板':
      if (!templateCaches.length) {
        loading.value = true;
        templateCaches.push(...(await getCaseProjects(name)));
        for (const group of templates) {
          group.list = [];
          for (const item of templateCaches) {
            if (item.case === group.name) {
              group.list.push(item);
            }
          }
        }
        loading.value = false;
      }

      groupType.value = 1;
      subGroups.value = templates;
      break;
    case '图表':
      subGroups.value = charts;
      break;
    case '控件':
      subGroups.value = formComponents;
      break;
    case '素材':
      groupType.value = 1;
      if (!materials.length) {
        loading.value = true;
        materials.push(...(await getFolders('v/material')));
        loading.value = false;
      }
      subGroups.value = materials;
      break;
    case '图元':
      if (!pngs.length) {
        loading.value = true;
        pngs.push(...(await getFolders('png')));
        pngs.push(...(await getFolders('svg', true)));
        loading.value = false;
      }
      subGroups.value = pngs;
      break;
    case '图形':
      subGroups.value = shapes;
      break;
    case '我的':
      subGroups.value = await getPrivateGroups();
      groupType.value = 1;
      await getPrivateGraphics();
      break;
  }

  if (!activedPanels[name]) {
    activedPanels[name] = [];
    for (const item of subGroups.value) {
      activedPanels[name].push(item.name);
    }
  }

  searchGraphics();
};

const getCaseProjects = async (name: string, current = 1, pageSize = 1000) => {
  const query: any = { tags: name };

  const ret: any = await axios.post(
    '/api/data/le5leV/list',
    {
      query,
      shared: 'true',
      projection: {
        id: 1,
        _id: 1,
        name: 1,
        image: 1,
        price: 1,
        case: 1,
      },
      sort: { createdAt: 1 },
    },
    {
      params: {
        current,
        pageSize,
      },
    }
  );

  if (!ret) {
    return [];
  }
  for (const item of ret.list) {
    if (!item.id) {
      item.id = item._id;
    }
    item.draggable = false;
  }
  return ret.list;
};

const getPrivateGroups = async () => {
  const list = [
    {
      name: '我的组件',
      list: [],
    },
  ];
  const config = {
    params: {
      current: 1,
      pageSize: 1000,
    },
  };
  let ret: any = await axios.post(
    '/api/data/folders/list',
    {
      projection: {
        image: 1,
        _id: 1,
        name: 1,
        list: 1,
      },
      query: {
        type: `le5leV-components`,
      },
      sort: { createdAt: 1 },
    },
    config
  );
  if (!ret) {
    ret = { list: [] };
  }

  for (const item of ret.list) {
    item.canEdited = true;
  }

  list.push(...ret.list);
  list.push({
    name: '3D',
    list: [],
  });

  return list;
};

const dragStart = async (event: DragEvent | MouseEvent, item: any) => {
  event.stopPropagation();
  if (!item) {
    return;
  }

  meta2d.canvas.addCaches = [];
  dropped = false;

  let data = null;
  const id = item._id || item.id;
  let isAsync: boolean;
  if (item.draggable === false) {
    // 场景
    data = item.data || item;
  } else if (item['3d']) {
    data = {
      name: 'iframe',
      x: 0,
      y: 0,
      tags: ['meta3d'],
      zIndex: 1,
      width: meta2d.store.data.width || meta2d.store.options.width,
      height: meta2d.store.data.height || meta2d.store.options.height,
      externElement: true,
      iframe: 'https://view3d.le5le.com/?id=' + (item._id || item.id),
    };
  } else if (item.component) {
    // 我的组件
    if (!item.componentDatas && !item.componentData) {
      isAsync = true;
      const ret: any = await axios.post(`/api/data/le5leV-components/get`, {
        id,
      });
      item.componentDatas = ret.componentDatas;
      item.componentData = ret.componentData;
    }
    if (item.componentData) {
      const pens = convertPen([item.componentData]);
      data = deepClone(pens);
    } else if (item.componentDatas) {
      data = deepClone(item.componentDatas);
    }
  } else if (item.data) {
    // 普通图元
    data = item.data;
  } else if (item.image) {
    // 拖拽图片
    let target: any = event.target;
    target.children[0] && (target = target.children[0].children[0]);
    // firefox naturalWidth svg 图片 可能是 0
    const width = target.naturalWidth || target.width;
    const height = target.naturalHeight || target.height;
    const [name, lockedOnCombine] = isGif(item.image)
      ? ['gif', 0]
      : ['image', undefined];

    data = {
      name,
      width,
      height,
      image: item.image,
      imageRatio: true,
      ratio: true,
      lockedOnCombine,
    };
  } else {
    return;
  }

  if (!Array.isArray(data)) {
    data = deepClone([data]);
  }

  !dropped && (meta2d.canvas.addCaches = data);

  if (event instanceof DragEvent) {
    event.dataTransfer.setData(
      'Meta2d',
      isAsync ? undefined : JSON.stringify(data)
    );
  }
};

const dragstart = (event: any) => {
  event.target.style.opacity = 0.5;
};

const dragend = (event: any) => {
  event.target.style.opacity = 1;
};

const open = async (item: any) => {
  if (!item || item.draggable !== false) {
    return;
  }

  const ret: any = await getLe5leV(item._id || item.id);
  if (!ret) {
    if (item.price > 0) {
      chargeDialog.data = item;
      chargeDialog.show = true;
    }
    return;
  }

  sessionStorage.setItem('opening', '1');
  router.push({
    path: '/',
    query: {
      r: Date.now() + '',
    },
  });

  for (const k of delAttrs) {
    delete ret[k];
  }
  autoSave();
  meta2d.open(ret);
  meta2d.fitView();
};

const getPrivateGraphics = async () => {
  for (const item of subGroups.value) {
    if (!item.list.length) {
      item.loading = true;
      if (item.name === '我的组件') {
        const data = {
          query: { folder: '' },
          projection: {
            image: 1,
            _id: 1,
            name: 1,
            component: 1,
          },
        };
        const config = {
          params: {
            current: 1,
            pageSize: 1000,
          },
        };
        const res: any = await getComponentsList(data, config);
        if (res?.list) {
          item.list = res.list;
        }
      } else if (item.name === '3D') {
        const data = {
          projection: {
            image: 1,
            _id: 1,
            name: 1,
          },
        };
        const config = {
          params: {
            current: 1,
            pageSize: 1000,
          },
        };
        const res: any = await axios.post(
          '/api/data/le5le3d/list',
          data,
          config
        );
        if (res?.list) {
          for (const item of res.list) {
            item['3d'] = true;
            item['draggable'] = true;
          }
          item.list = res.list;
        }
      }
      item.loading = false;
    }
  }
};

const editedFolder = ref<any>(undefined);
const onCreateFolder = () => {
  activedPanels[activedGroup.value].splice(
    0,
    activedPanels[activedGroup.value].length
  );

  editedFolder.value = {
    _id: '',
    name: '',
    label: '新建文件夹',
    list: [],
    edited: true,
    canEdited: true,
  };
  subGroups.value.splice(subGroups.value.length - 1, 0, editedFolder.value);
};

const createFoder = async () => {
  if (!editedFolder.value.label) {
    return;
  }
  if (editedFolder.value.label === editedFolder.value.name) {
    editedFolder.value.edited = false;
    return;
  }
  const found = subGroups.value.findIndex(
    (group: any) => group.name === editedFolder.value.label
  );
  if (found >= 0) {
    MessagePlugin.error('已经存在相同名称文件夹');
    return;
  }

  if (editedFolder.value._id) {
    const ret: any = await axios.post('/api/data/folders/update', {
      _id: editedFolder.value._id,
      name: editedFolder.value.label,
    });
    if (ret) {
      editedFolder.value.name = editedFolder.value.label;
      editedFolder.value.edited = false;
    }
  } else {
    const ret: any = await axios.post('/api/data/folders/add', {
      name: editedFolder.value.label,
      type: 'le5leV-components',
      list: [],
    });
    if (ret) {
      editedFolder.value.name = editedFolder.value.label;
      editedFolder.value._id = ret._id;
      editedFolder.value.edited = false;
    }
  }
};

const onEditHeader = (item: any) => {
  item.label = item.name;
  item.edited = true;
  editedFolder.value = item;
};

const onKeyHeader = (text: string, event: any) => {
  if (event.e.key === 'Escape') {
    editedFolder.value.edited = false;
  }
};

// 我的组件右键菜单
const contextmenu = reactive<any>({
  visible: false,
  style: {},
  // 子分类
  group: undefined,
  // 组件图纸
  component: {},
  // 右键二级子菜单
  subMenus: [],
});
const contextmenuDom = ref<any>(null);
const onContextMenu = async (e: MouseEvent, group: string, item: any) => {
  e.preventDefault();
  e.stopPropagation();

  if (activedGroup.value !== '我的') {
    return;
  }

  contextmenu.group = group;
  contextmenu.component = item;

  if (document.body.clientHeight - e.clientY < 128) {
    contextmenu.style = {
      left: e.clientX + 'px',
      bottom: '4px',
    };
  } else {
    contextmenu.style = {
      left: e.clientX + 'px',
      top: e.clientY + 'px',
    };
  }

  contextmenu.subMenus = [];
  for (const elem of subGroups.value) {
    if (elem === group || elem.name === '3D') {
      continue;
    }
    contextmenu.subMenus.push(elem);
  }
  contextmenu.visible = true;
  setTimeout(() => {
    if (contextmenuDom.value) {
      contextmenuDom.value.focus();
    }
  }, 500);
};

const delDialog = reactive<any>({});

const onMenu = async (val: string) => {
  const id = contextmenu.component._id || contextmenu.component.id;
  setTimeout(() => {
    contextmenu.group = '';
    contextmenu.component = {};
    contextmenu.subMenus = [];
  }, 500);
  switch (val) {
    case 'edit':
      if (contextmenu.component.component) {
        autoSave();
        router.push({
          path: '/',
          query: {
            id,
            c: 1,
            r: Date.now() + '',
          },
        });
      } else {
        let url = 'https://3d.le5le.com/?id=';
        if (import.meta.env.VITE_TRIAL == 0 && (window as any).url3D) {
          url = (window as any).url3D;
        }
        window.open(url + id, '_blank');
      }

      break;
    case 'del':
      delDialog.show = true;
      break;
    default:
      if (val.indexOf('move:')) {
        return;
      }

      val = val.replace('move:', '');
      const group = contextmenu.subMenus.find(
        (element: any) => element.name === val
      );
      if (!group) {
        return;
      }
      // 前端: 添加组件到目标文件夹
      group.list.push(contextmenu.component);
      // 前端：从源文件夹移出组件
      contextmenu.group.list.forEach((item: any, index: number, arr: any[]) => {
        if (id === item._id || id === item.id) {
          arr.splice(index, 1);
        }
      });

      // 更新后端组件信息
      let ret = await updateCollection('le5leV-components', {
        _id: id,
        folder: val === '我的组件' ? '' : val,
      });
      if (!ret) {
        return;
      }

      // 更新后端源文件夹列表
      if (contextmenu.group.name !== '我的组件') {
        await axios.post('/api/data/folders/update', {
          _id: contextmenu.group._id || contextmenu.group.id,
          list: contextmenu.group.list,
        });
      }

      // 更新后端目标文件夹列表
      if (group.name !== '我的组件') {
        await axios.post('/api/data/folders/update', {
          _id: group._id || group.id,
          list: group.list,
        });
      }
      break;
  }
};

const hideContextmenu = () => {
  contextmenu.visible = false;
};

const delComponet = async () => {
  const id = contextmenu.component._id || contextmenu.component.id;
  await axios.post(`/api/data/le5leV-components/delete`, {
    id,
  });

  // 前端：从源文件夹移出组件
  contextmenu.group.list.forEach((item: any, index: number, arr: any[]) => {
    if (id === item._id || id === item.id) {
      arr.splice(index, 1);
    }
  });

  // 更新后端源文件夹列表
  if (contextmenu.group.name !== '我的组件') {
    await axios.post('/api/data/folders/update', {
      _id: contextmenu.group._id || contextmenu.group.id,
      list: contextmenu.group.list,
    });
  }

  delDialog.show = false;
};

const drop = (obj: any) => {
  dropped = true;

  if (obj) {
    Array.isArray(obj) && open(obj[0]);
  } else {
    MessagePlugin.warning('正在请求网络数据中，请稍后重试');
  }
};

const onSubmitOrder = async () => {
  const result: any = await axios.post('/api/order/datas/submit', {
    collection: 'le5leV',
    id: chargeDialog.data._id,
  });
  if (result) {
    wechatPayDialog.order = result;
    wechatPayDialog.show = true;
  }
};

const getPayResult = async () => {
  const result: { state: number } = await axios.post('/api/order/pay/state', {
    id: wechatPayDialog.order.id || wechatPayDialog.order._id,
  });
  if (result && result.state) {
    return true;
  }
};

const onChargeSuccess = () => {
  MessagePlugin.success('支付成功！');
  wechatPayDialog.show = false;
  chargeDialog.show = false;
};

const onSearch = () => {
  debounce(searchGraphics, 300);
};

const searchGraphics = async () => {
  if (search.value) {
    activedPanels[activedGroup.value].splice(
      0,
      activedPanels[activedGroup.value].length
    );
  }

  for (const group of subGroups.value) {
    for (const item of group.list) {
      if (search.value) {
        item.visible = searchObjectPinyin(item, 'name', search.value);
      } else {
        item.visible = true;
      }
    }

    if (search.value) {
      activedPanels[activedGroup.value].push(group.name);
    }
  }
};

const onFold = () => {
  if (!activedPanels[activedGroup.value]) {
    return;
  }

  if (activedPanels[activedGroup.value].length) {
    activedPanels[activedGroup.value] = [];
  } else {
    activedPanels[activedGroup.value] = [];
    for (const item of subGroups.value) {
      activedPanels[activedGroup.value].push(item.name);
    }
  }
};

const loadImage = (elem: any) => {
  if (elem.isSvg) {
    makeSvg(elem);
    if (activedGroup.value === '图元') {
      throttle(renderPngGroup, 100);
    }
  }
};

const renderPngGroup = () => {
  subGroups.value = pngs;
};

let uploadGroup: any;
const onSelectFiles = (item: any) => {
  uploadGroup = item;
};

const fileSuccessed = async (content: any) => {
  const c: any = {
    name: filename(content.file.name),
    image: content.response.url,
    folder: uploadGroup.name === '我的组件' ? '' : uploadGroup.name,
  };

  const ret: any = await addCollection('le5leV-components', c);

  if (ret && uploadGroup.name !== '我的组件') {
    c._id = ret._id || ret.id;
    if (!uploadGroup.list) {
      uploadGroup.list = [];
    }
    uploadGroup.list.push(c);
    await axios.post('/api/data/folders/update', {
      _id: uploadGroup._id || uploadGroup.id,
      list: uploadGroup.list,
    });
  }
};

const delFolder = async (item: any) => {
  if (item.list?.length) {
    MessagePlugin.error('文件夹不为空！');
    return;
  }

  const id = item._id || item.id;
  const ret: any = await axios.post('/api/data/folders/delete', {
    id,
  });
  if (ret) {
    const i = subGroups.value.findIndex(
      (elem: any) => id === elem._id || id === elem.id
    );
    i >= 0 && subGroups.value.splice(i, 1);
  }
};

onMounted(() => {
  groupChange('场景');
  document.addEventListener('dragstart', dragstart, false);
  document.addEventListener('dragend', dragend, false);

  setTimeout(() => {
    meta2d.on('drop', drop);
  }, 2000);
});

onUnmounted(() => {
  document.removeEventListener('dragstart', dragstart);
  document.removeEventListener('dragend', dragend);

  meta2d.off('drop', drop);
});
</script>
<style lang="postcss" scoped>
.graphics {
  display: flex;
  flex-direction: column;
  background-color: var(--color-background);
  z-index: 2;
  .input-search {
    flex-shrink: 0;
    height: 40px;
  }

  .groups-panel {
    display: grid;
    grid-template-columns: 50px 1fr;
    border-top: 1px solid var(--color-background-input);
    flex-grow: 1;
    overflow-y: auto;
    font-size: 12px;
    z-index: 7;

    .groups {
      & > div {
        display: flex;
        flex-direction: column;
        align-items: center;
        padding: 16px 4px;
        line-height: 1;
        cursor: pointer;

        .t-icon {
          font-size: 20px;
          margin-bottom: 8px;
        }

        &:hover {
          color: var(--color-primary);
        }
      }

      .active {
        background-color: var(--color-background-active);
        color: var(--color-primary);
        border-left: 2px solid var(--color-primary);
      }
    }

    .list {
      overflow-y: auto;
      max-height: calc(100vh - 100px);
      background-color: var(--color-background-active);
      padding-top: 8px;

      * {
        background-color: var(--color-background-active);
      }

      :deep(.t-collapse) {
        min-height: 100vh;
        border: none;
      }

      :deep(.t-collapse-panel__header) {
        border: none;
        font-size: 12px;
        font-weight: 400;
        padding: 8px 8px 8px 16px;

        & > .t-space {
          display: none;
        }

        & > .t-space:focus {
          display: inline-flex;
        }

        &:hover .t-collapse-panel__icon,
        &:hover > .flex {
          color: var(--color-primary);
        }

        .t-collapse-panel__icon,
        .t-collapse-panel__icon * {
          transition: none;
        }

        .t-icon {
          font-size: 13px;
        }
      }
      :deep(.t-collapse-panel__wrapper:hover) {
        .t-collapse-panel__header > .t-space {
          display: inline-flex;
        }
      }

      :deep(.t-collapse-panel__body) {
        border: none;
      }
      :deep(.t-collapse-panel__content) {
        background-color: var(--color-background-active);
        padding: 4px 4px 20px 4px;
        display: grid;
        grid-template-columns: 1fr 1fr 1fr;
        grid-row-gap: 12px;
      }

      :deep(.t-loading--center) {
        width: 100px;
        .t-loading__text {
          margin-left: 8px;
          height: 24px;
        }
      }

      :deep(.t-image__error) {
        .t-space-item:last-child {
          display: none;
        }
      }

      :deep(.t-image__loading) {
        .t-space-item:last-child {
          display: none;
        }
      }

      .graphic {
        position: relative;
        padding: 10px 0;
        border-radius: 2px;
        border: 1px solid transparent;

        &:hover {
          cursor: pointer;
          border-color: var(--color-primary);
        }
        p {
          margin-top: 10px;
          padding: 0 8px;
          text-align: center;
          font-size: 12px;
          height: 12px;
          line-height: 1;
          overflow: hidden;
          text-overflow: ellipsis;
          display: -webkit-box;
          -webkit-line-clamp: 1;
          word-break: break-all;
          -webkit-box-orient: vertical;
        }
        .t-image__wrapper {
          height: 32px;
          width: 32px;
          margin: auto;
          :deep(.t-image) {
            border-radius: 2px;
          }
        }

        svg {
          color: var(--color);
          height: 32px;
          width: 100%;
          margin: auto;
        }

        .svg-box {
          height: 32px;
          width: 32px;
          margin-left: calc(50% - 16px);
          margin-top: 10px;
          margin-bottom: 10px;
          &:deep(svg) {
            height: 100%;
            width: 100%;
            .cls-1 {
              stroke: var(--color) !important;
              stroke-width: 4px;
              fill: none;
              stroke-dasharray: none;
              opacity: 1;
            }
          }
        }

        .price {
          position: absolute;
          top: 8px;
          right: 8px;
          display: inline-block;
          z-index: 1;
          border-radius: 2px;
          background-color: #ff400060;
          color: var(--color-bland);
          font-size: 10px;
          line-height: 1;
          padding: 3px;
        }
      }
    }

    .two-columns {
      :deep(.t-collapse-panel__content) {
        grid-template-columns: 1fr 1fr;
      }
      .graphic {
        .t-image__wrapper {
          width: 100px;
          height: 56.25px;
          background-color: var(--color-background);
        }
      }
    }
  }

  .context-menu-box {
    position: fixed;
    z-index: 200;
    & > div {
      width: 140px !important;
      position: static;
    }

    :deep(.t-menu) {
      .t-menu__item {
        &.t-is-opened {
          background-color: var(--color-background-popup-hover);
          transition: none !important;
        }
      }
      .t-fake-arrow {
        transform: rotate(-90deg) !important;
      }

      .t-fake-arrow--active {
        transform: rotate(90deg) !important;
      }
    }
  }
}
</style>
