Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
v8tenko committed Oct 7, 2024
1 parent 1ffe79f commit e124c46
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 70 deletions.
11 changes: 5 additions & 6 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,18 @@ export const TABS_DROPDOWN_MENU_CLASSNAME = 'yfm-tabs-dropdown-menu';
export const TABS_DROPDOWN_SELECT = 'yfm-tabs-dropdown-select';

export const TABS_ACCORDION_CLASSNAME = 'yfm-tabs-accordion';
export const TABS_ACCORDION_CLIENT_HEIGHT = 'data-yfm-tabs-accordion-client-heights'
export const TABS_ACCORDION_CLIENT_HEIGHT = 'data-yfm-tabs-accordion-client-heights';

export const TABS_RADIO_CLASSNAME = 'yfm-tabs-vertical';
export const VERTICAL_TAB_CLASSNAME = 'yfm-vertical-tab';
export const TAB_DATA_VERTICAL_TAB = 'data-diplodoc-vertical-tab';
export const TAB_RADIO_KEY = 'data-diplodoc-input';


export enum TabsVariants {
Regular = 'regular',
Radio = 'radio',
Dropdown = 'dropdown',
Accordion = 'accordion'
Regular = 'regular',
Radio = 'radio',
Dropdown = 'dropdown',
Accordion = 'accordion',
}

export interface Tab {
Expand Down
13 changes: 3 additions & 10 deletions src/plugin/variants/accordion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
TAB_PANEL_CLASSNAME,
TabsVariants,
} from '../../common';
import {generateID,getName, getTabId, getTabKey, isTabSelected} from '../utils';
import {generateID, getName, getTabId, getTabKey, isTabSelected} from '../utils';
import {type RuntimeTab} from '../types';

import {type TabsTokensGenerator} from './types';
Expand Down Expand Up @@ -91,10 +91,7 @@ export const accordion: TabsTokensGenerator = (
tabPanelClose.block = true;
tabOpen.attrSet(TAB_DATA_ID, tabId);
tabOpen.attrSet(TAB_DATA_KEY, tabKey);
tabOpen.attrSet(
'class',
[TAB_CLASSNAME, TAB_GROUP_CLASSNAME].join(' '),
);
tabOpen.attrSet('class', [TAB_CLASSNAME, TAB_GROUP_CLASSNAME].join(' '));
tabOpen.attrSet('role', 'tab');
tabOpen.attrSet('aria-controls', tabPanelId);
tabOpen.attrSet('aria-selected', 'false');
Expand All @@ -111,11 +108,7 @@ export const accordion: TabsTokensGenerator = (
tabPanelOpen.attrJoin('class', ACTIVE_CLASSNAME);
}

tabsTokens.push(
tabOpen,
tabInline,
tabClose,
);
tabsTokens.push(tabOpen, tabInline, tabClose);

tabsTokens.push(tabPanelOpen, ...tabs[i].tokens, tabPanelClose);
}
Expand Down
11 changes: 5 additions & 6 deletions src/plugin/variants/dropdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
TAB_DATA_VARIANT,
TAB_PANEL_CLASSNAME,
} from '../../common';
import {generateID,getName, getTabId, getTabKey, isTabSelected} from '../utils';
import {generateID, getName, getTabId, getTabKey, isTabSelected} from '../utils';
import {type RuntimeTab} from '../types';

import {type TabsTokensGenerator} from './types';
Expand All @@ -32,7 +32,6 @@ export const dropdown: TabsTokensGenerator = (
const dropdownSelectText = new state.Token('text', '', 0);
const dropdownSelectClose = new state.Token('dropdown-select_open', 'div', -1);


if (tabs.length) {
const [start] = tabs[0].listItem.map ?? [null];
// eslint-disable-next-line no-eq-null, eqeqeq
Expand Down Expand Up @@ -68,13 +67,14 @@ export const dropdown: TabsTokensGenerator = (
dropdownSelectOpen.attrSet('role', 'tablist');
dropdownSelectOpen.attrSet('class', TABS_DROPDOWN_SELECT);


dropdownSelectText.content = activeTab ? activeTab.name : '-';
dropdownSelectInline.children = [dropdownSelectText];

dropdownTokens.push(
dropdownOpen,
dropdownSelectOpen, dropdownSelectInline, dropdownSelectClose
dropdownOpen,
dropdownSelectOpen,
dropdownSelectInline,
dropdownSelectClose,
);

const dropdownMenuOpen = new state.Token('dropdown-menu_open', 'ul', 1);
Expand Down Expand Up @@ -130,7 +130,6 @@ export const dropdown: TabsTokensGenerator = (
tabPanelOpen.attrJoin('class', ACTIVE_CLASSNAME);
}


dropdownTokens.push(tabPanelOpen, ...tabs[i].tokens, tabPanelClose);
}

Expand Down
7 changes: 3 additions & 4 deletions src/plugin/variants/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@

import { TabsVariants } from '../../common';
import {TabsVariants} from '../../common';

import {regular} from './regular';
import {radio} from './radio';
import {TabsTokensGenerator} from './types';
import { dropdown } from './dropdown';
import { accordion } from './accordion';
import {dropdown} from './dropdown';
import {accordion} from './accordion';

const generateByType: Record<TabsVariants, TabsTokensGenerator> = {
regular,
Expand Down
2 changes: 1 addition & 1 deletion src/plugin/variants/radio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
TAB_PANEL_CLASSNAME,
VERTICAL_TAB_CLASSNAME,
} from '../../common';
import {generateID,getName, getTabId, getTabKey, isTabSelected} from '../utils';
import {generateID, getName, getTabId, getTabKey, isTabSelected} from '../utils';
import {type RuntimeTab} from '../types';

import {type TabsTokensGenerator} from './types';
Expand Down
9 changes: 2 additions & 7 deletions src/plugin/variants/regular.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,7 @@ export const regular: TabsTokensGenerator = (

const hasDefaultOpenTab = activeTabsCount !== 0;

tabsOpen.attrSet(
'class',
[TABS_CLASSNAME, containerClasses].filter(Boolean).join(' '),
);
tabsOpen.attrSet('class', [TABS_CLASSNAME, containerClasses].filter(Boolean).join(' '));
tabsOpen.attrSet(GROUP_DATA_KEY, tabsGroup);
tabsOpen.attrSet(TAB_DATA_VARIANT, 'regular');

Expand Down Expand Up @@ -93,9 +90,7 @@ export const regular: TabsTokensGenerator = (
const tabKey = getTabKey(tab);
const didTabHasActiveAttr = isTabSelected(tab);
/* if user did not provide default open tab we fallback to first tab (in default tabs only) */
const isTabActive = hasDefaultOpenTab
? didTabHasActiveAttr
: i === 0;
const isTabActive = hasDefaultOpenTab ? didTabHasActiveAttr : i === 0;

tab.name = getName(tab);

Expand Down
2 changes: 1 addition & 1 deletion src/plugin/variants/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type Token from 'markdown-it/lib/token';
import type StateCore from 'markdown-it/lib/rules_core/state_core';
import type {RuntimeTab} from '../types';

import { TabsVariants } from '../../common';
import {TabsVariants} from '../../common';

export type TokensRange = {
start: number;
Expand Down
71 changes: 36 additions & 35 deletions src/runtime/TabsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ type TabSwitchDirection = 'left' | 'right';

export class TabsController {
private _document: Document;

private _onSelectTabHandlers: Set<Handler> = new Set();

// TODO: remove side effects from constructor
constructor(document: Document) {
this._document = document;

this._document.addEventListener('click', (event) => {
const target = getEventTarget(event) as HTMLElement;

Expand All @@ -61,18 +61,15 @@ export class TabsController {
}

if (this.isElementDropdownSelect(target)) {
target.classList.toggle(ACTIVE_CLASSNAME)

target.classList.toggle(ACTIVE_CLASSNAME);

return;
}

if (!(this.isValidTabElement(target))) {
if (!this.isValidTabElement(target)) {
return;
}

console.log('tab is valid');

const tab = this.getTabDataFromHTMLElement(target);

if (tab) {
Expand Down Expand Up @@ -181,12 +178,7 @@ export class TabsController {
}
}

private updateHTML(
tab: Required<Tab>,
target: HTMLElement | undefined,
variant: TabsVariants,
) {
console.log(tab);
private updateHTML(tab: Required<Tab>, target: HTMLElement | undefined, variant: TabsVariants) {
switch (variant) {
case TabsVariants.Radio: {
return this.updateHTMLRadio(tab, target);
Expand All @@ -210,9 +202,7 @@ export class TabsController {

const {isForced, root} = this.didTabOpenForce(target);

const singleTabSelector = isForced
? `.yfm-vertical-tab[${TAB_FORCED_OPEN}="true"]`
: '';
const singleTabSelector = isForced ? `.yfm-vertical-tab[${TAB_FORCED_OPEN}="true"]` : '';

const tabs = this._document.querySelectorAll(
`${Selector.TABS}[${GROUP_DATA_KEY}="${group}"] ${Selector.TAB}[${TAB_DATA_KEY}="${key}"]${singleTabSelector}`,
Expand Down Expand Up @@ -311,7 +301,7 @@ export class TabsController {

private updateHTMLDropdown(tab: Required<Tab>) {
const {group, key} = tab;

const tabs = this._document.querySelectorAll(
`${Selector.TABS}[${GROUP_DATA_KEY}="${group}"] ${Selector.TAB}[${TAB_DATA_KEY}="${key}"]`,
);
Expand All @@ -320,14 +310,14 @@ export class TabsController {

tabs.forEach((tab) => {
const dropdown = tab.closest(`[${TAB_DATA_VARIANT}=${TabsVariants.Dropdown}]`);

if (!dropdown?.children) {
return;
}

const select = dropdown.children.item(0);
const menu = dropdown.children.item(1);

select?.classList.remove(ACTIVE_CLASSNAME);

/* first and second elements are select / menu, skipping them */
Expand All @@ -350,60 +340,69 @@ export class TabsController {
menuItem.style.fontWeight = '500';
item?.classList.remove(ACTIVE_CLASSNAME);
}
})
});

return changed;
}

private updateHTMLAccordion(tab: Required<Tab>, target: HTMLElement | undefined) {
const {group, key} = tab;

const tabs = this._document.querySelectorAll(
`${Selector.TABS}[${GROUP_DATA_KEY}="${group}"] ${Selector.TAB}[${TAB_DATA_KEY}="${key}"]`,
);

let changed = 0;

(() => target)()

tabs.forEach((tab) => {
const accordion = tab.closest(`[${TAB_DATA_VARIANT}=${TabsVariants.Accordion}]`);


if (!accordion?.children) {
return;
}


for (let i = 0; i < accordion.children.length; i += 2) {
const title = accordion.children.item(i);
const currentTab = accordion.children.item(i + 1);
const title = accordion.children.item(i);
const currentTab = accordion.children.item(i + 1);

changed++
changed++;

if (tab === title) {
if (tab === title) {
title?.classList.toggle(ACTIVE_CLASSNAME);
currentTab?.classList.toggle(ACTIVE_CLASSNAME);

continue;
}
}

title?.classList.remove(ACTIVE_CLASSNAME);
currentTab?.classList.remove(ACTIVE_CLASSNAME);
title?.classList.remove(ACTIVE_CLASSNAME);
currentTab?.classList.remove(ACTIVE_CLASSNAME);
}
})
});

if (target && !this.checkVisible(target)) {
setTimeout(() => {
target.scrollIntoView({block: 'nearest'});
});
}

return changed;
}

private checkVisible(element: HTMLElement) {
const rect = element.getBoundingClientRect();
const viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight);

return !(rect.bottom < 0 || rect.top - viewHeight >= 0);
}

private hideAllDropdown(target: HTMLElement) {
const dropdowns = this._document.querySelectorAll('.yfm-tabs-dropdown-select.active');

dropdowns.forEach((menu) => {
if (!menu.contains(target)) {
menu.classList.remove(ACTIVE_CLASSNAME);
}
})
});
}

private resetScroll(
Expand Down Expand Up @@ -441,7 +440,9 @@ export class TabsController {
private fireSelectTabEvent(tab: Required<Tab>, diplodocId?: string) {
const {group, key, variant: align} = tab;

const eventTab: Tab = group.startsWith(DEFAULT_TABS_GROUP_PREFIX) ? {key, variant: align} : tab;
const eventTab: Tab = group.startsWith(DEFAULT_TABS_GROUP_PREFIX)
? {key, variant: align}
: tab;

this._onSelectTabHandlers.forEach((handler) => {
handler({tab: eventTab, currentTabId: diplodocId});
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/scss/tabs.scss
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@

transition: all 200ms;

scroll-margin-top: 20px;

&::after {
content: '';
position: absolute;
Expand Down

0 comments on commit e124c46

Please sign in to comment.