HEX
Server: nginx/1.18.0
System: Linux test-ipsremont 5.4.0-214-generic #234-Ubuntu SMP Fri Mar 14 23:50:27 UTC 2025 x86_64
User: ips (1000)
PHP: 8.0.30
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //var/www/quadcode.com/src/components/blocks/blog/Anchors.svelte
<script lang="ts">
  import { onMount } from 'svelte';
  import { get } from 'svelte/store';
  import { blogAnchors, isMobile, postActiveAnchor } from '../../../store';
  import { t } from '$lib/translations';

  export let className: string | undefined = '';
  export let notUseH3 = false;
  export let divideTitles: boolean = false;
  export let title: string = '';
  export let toTopButton: boolean = false;

  let mobile = false;
  // let activeAnchor: HTMLElement | null = null;

  isMobile.subscribe((value) => {
    mobile = value;
  });
  const toTopClickHandler = () => {
    window.scrollTo(0,0);
    blogAnchors.set(false);
  };
  onMount(() => {
    const titles = document.querySelectorAll(
      notUseH3 ? '.anchors-content h2' : '.anchors-content h2, .anchors-content h3'
    );
    const tocContainer = document.querySelector('#tocContainer');
    const block = document.querySelector('.anchors__block');
    const list = document.querySelector('.anchors__list');
    const mobileList = document.querySelector('#tocListMobile');
    const inner = document.querySelector('.anchors__inner');
    const progress: HTMLElement | null = document.querySelector('#progressBar');
    const content = document.querySelector('.anchors-content');
    const prev = document.querySelector('.anchors-prev');
    const anchors = document.querySelector('.anchors-link');
    const tocMobileToggle = document.querySelector('#tocMobileTrigger');
    const tocToggle = document.querySelector('#tocToggle');
    const scrollEl = document.getElementById('tocScroll');

    if (tocToggle && scrollEl) {
        const tr = get(t);
        const showAllText = tr('blog.showAllToc');
        const hideAllText = tr('blog.hideAllToc');
        if (titles.length <= 5) tocToggle.classList.add('hidden');
        tocToggle.addEventListener('click', function () {
            const expanded = scrollEl.classList.toggle('toc-expanded');
            tocToggle.textContent = expanded ? hideAllText : showAllText;
            tocToggle.setAttribute('aria-label', expanded ? hideAllText : showAllText);
        });
    }


    if (list && titles.length) {
      titles.forEach((title, index) => {
        title.querySelector('.copyButton')?.remove();
        title.id = 'anchor' + (index + 1);
        if (divideTitles) {
          list.innerHTML += `
              <li class="anchors__item ${title.tagName === 'H2' ? '' : 'toc-item--sub'}">
                  <a aria-label="${title.textContent}" data-target="anchor${index + 1}" href="${window.location.pathname}#anchor${index + 1}" class="anchors__link ${title.tagName === 'H2' ? 'title' : ''}" data-index="${index + 1}">
                      ${title.innerHTML}
                  </a>
              </li>
          `;
        }
        else {
          list.innerHTML += `
                <li class="anchors__item ${title.tagName === 'H2' ? '' : 'toc-item--sub'}">
                    <a aria-label="${title.textContent}" href="${window.location.pathname}#anchor${index + 1}" class="anchors__link" data-index="${index + 1}">
                        ${title.innerHTML}
                    </a>
                </li>
            `;
        }

        if (mobileList) {
            mobileList.innerHTML = list.innerHTML;
        }

        const links = document.querySelectorAll<HTMLElement>('.anchors__link');

        links.forEach((link) => {
          const index = link.dataset.index;
          const scrollIntoView = () => {
            if (!window.location.hash) return;
            const div = document.querySelector(window.location.hash);
            if (!div) return;
            window.scrollTo({
              behavior: 'smooth',
              top: div.getBoundingClientRect().top - document.body.getBoundingClientRect().top - 40,
            });
          };

          scrollIntoView();

          link.addEventListener('click', (e) => {
            e.preventDefault();

            window.location.hash = 'anchor' + (index);

            // if (mobile && anchors) {
            //   blogAnchors.set(false);
            // }

            scrollIntoView();
          });
        });
      });
      block?.classList.remove('hide');
    } else {
        tocContainer?.remove();
        tocMobileToggle?.remove();
    }
    if (progress && content) {
      window.addEventListener('scroll', () => {
        const winScroll = document.body.scrollTop || document.documentElement.scrollTop;
        const heightContent = document.documentElement.scrollHeight - content.clientHeight;
        const height = document.documentElement.scrollHeight - heightContent;
        const scrolled = (winScroll / height) * 100;

        if(scrolled > 50){
          inner?.scrollTo({
            top: inner.clientHeight * scrolled / 100,
          });
        }
        else {
          inner?.scrollTo({
            top: 0,
          });
        }

        progress.style.width = scrolled + '%';
      });
    }
    postActiveAnchor.subscribe((value) => {
      const titles = document.querySelectorAll('.anchors__link');
      titles.forEach((title) => {
        title.classList.remove('active');
      });
      if(value === null) return;
      document.querySelectorAll(`[data-target="anchor${value + 1}"]`).forEach((item) => {
          item?.classList.add('active');
      })
    });
  });

  let open = false;

  blogAnchors.subscribe((value: boolean) => {
    open = value;
  });
</script>

<div class="sidebar-toc-wrap" id="tocContainer">
  <div class="toc-title">{title || $t('Table of contents')}</div>
  <div class="toc-scroll" id="tocScroll">
    <ul class="toc-list anchors__list" id="tocList">
    </ul>
  </div>
  <button type="button" class="toc-toggle" id="tocToggle" aria-label={$t('blog.showAllToc')}>{$t('blog.showAllToc')}</button>
</div>

<style lang="scss">
  @import 'src/scss/media';
  @import 'src/scss/mixins';
  @import 'src/scss/variables';

  .sidebar-toc-wrap { margin-bottom: 32px; }
  .toc-title {
    font-size: 0.8125rem; font-weight: 700; color: #141414;
    text-transform: uppercase; letter-spacing: 0.02em;
    margin-bottom: 12px;
  }
  .toc-scroll {
    max-height: 165px; overflow-y: auto;
    scrollbar-width: thin; scrollbar-color: #c5cfd6 #f1f5f9;
  }
  .toc-scroll::-webkit-scrollbar { width: 6px; }
  .toc-scroll::-webkit-scrollbar-track { background: #f1f5f9; border-radius: 3px; }
  :global(.toc-scroll.toc-expanded) { max-height: calc(100vh - 350px); }
  .toc-list { list-style: none; padding: 0; margin: 0; }
  .toc-list :global(li) { margin-bottom: 2px; }
  .toc-list :global(a) {
    font-size: 0.8125rem; font-weight: 500; color: #5a6872;
    display: block; padding: 6px 0 6px 12px;
    border-left: 2px solid transparent; margin-left: 0;
    transition: all 0.15s; line-height: 1.4;
  }
  .toc-list :global(a:hover) { color: #E62334; border-left-color: #E62334; }
  .toc-list :global(a.active) { color: #E62334; font-weight: 600; border-left-color: #E62334; }
  .toc-list :global(.toc-item--sub a) { padding-left: 24px; font-size: 0.75rem; color: #6b7280; }
  .toc-list :global(.toc-item--sub a:hover), .toc-list :global(.toc-item--sub a.active) { color: #E62334; }
  .toc-toggle {
    display: block; margin-top: 10px; font-size: 0.8125rem; font-weight: 600;
    color: #E62334; cursor: pointer; background: none; border: none;
    font-family: inherit; padding: 0; text-align: left; text-decoration: underline;
  }
  .toc-toggle:hover { color: #c41e2a; }
  :global(.toc-toggle.hidden) { display: none; }
</style>