From 696f1a9a82826ea49e97a89811abf231dc62da88 Mon Sep 17 00:00:00 2001 From: Mykola Zahorulko Date: Wed, 13 Aug 2025 15:45:07 +0200 Subject: [PATCH] Add tabs feature with two options --- .gitignore | 24 +++++++ README.md | 0 mobile-scrollbar/index.xslt | 66 +++++++++++++++++++ mobile-scrollbar/src/main.js | 26 ++++++++ mobile-scrollbar/src/style.less | 112 +++++++++++++++++++++++++++++++ mobile-tabs /index.xslt | 66 +++++++++++++++++++ mobile-tabs /src/main.js | 106 ++++++++++++++++++++++++++++++ mobile-tabs /src/style.less | 113 ++++++++++++++++++++++++++++++++ 8 files changed, 513 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 mobile-scrollbar/index.xslt create mode 100644 mobile-scrollbar/src/main.js create mode 100644 mobile-scrollbar/src/style.less create mode 100644 mobile-tabs /index.xslt create mode 100644 mobile-tabs /src/main.js create mode 100644 mobile-tabs /src/style.less diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/mobile-scrollbar/index.xslt b/mobile-scrollbar/index.xslt new file mode 100644 index 0000000..1eece70 --- /dev/null +++ b/mobile-scrollbar/index.xslt @@ -0,0 +1,66 @@ + +
+
+ + + +
+ +
+
+ + + +
+ +
+
+ + + +
+ +
+
+ + + +
+ +
+
+ + + +
+ +
+
+ + +
+ +
+ + + +
+ +
+
+ + + +
+ +
+
+ +
+
+ +
\ No newline at end of file diff --git a/mobile-scrollbar/src/main.js b/mobile-scrollbar/src/main.js new file mode 100644 index 0000000..c5df2d6 --- /dev/null +++ b/mobile-scrollbar/src/main.js @@ -0,0 +1,26 @@ +const tabButtons = document.querySelectorAll('.tabs__item'); +// Leave only necessary sections id +const sectionIds = [ + 'projector_longdescription', + 'projector_dictionary', + 'projector_enclosures', + 'projector_warranty', + 'product_questions_list', + 'opinions_section', + 'projector_blog', + 'products_associated_zone1' +]; + +const tabContents = sectionIds.map(id => document.getElementById(id)); + +tabButtons.forEach(btn => { + btn.addEventListener('click', () => { + tabButtons.forEach(b => b?.classList.remove('idm-active')); + btn?.classList.add('idm-active'); + + tabContents.forEach(content => content?.classList.remove('idm-active')); + const tabId = btn?.dataset.tab; + + document.getElementById(tabId)?.classList.add('idm-active'); + }); +}); diff --git a/mobile-scrollbar/src/style.less b/mobile-scrollbar/src/style.less new file mode 100644 index 0000000..d45a053 --- /dev/null +++ b/mobile-scrollbar/src/style.less @@ -0,0 +1,112 @@ +@keyframes slide-in { + from { + transform: scaleX(0); + } + to { + transform: scaleX(1); + } +} + +@keyframes slide-in-opacity { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.tabs { + border-bottom: 3px solid #efefef; + + @media(max-width: 978px) { + border: none; + } + + &__container { + display: flex; + align-items: center; + gap: 1rem; + + &::-webkit-scrollbar { + height: 3px; + } + + &::-webkit-scrollbar-track { + background: #efefef; + border-radius: 8px; + } + + &::-webkit-scrollbar-thumb { + background-color: black; + min-width: 30%; + border-radius: 8px; + } + + &::-webkit-scrollbar-thumb:hover { + background-color: #2e8b57; + } + + @media(max-width: 978px) { + overflow-y: hidden; + overflow-x: scroll; + } + + } + + &__item { + white-space: nowrap; + position: relative; + padding: 8px 12px; + cursor: pointer; + transition: color .3s ease; + color: #888; + font-weight: 600; + + &:hover { + color: #000; + } + + &.idm-active { + color: #000; + + &::after { + display: block; + } + } + + &::after { + content: ""; + display: none; + position: absolute; + bottom: -3px; + left: 0; + width: 100%; + height: 2.5px; + background-color: black; + border-radius: 2px; + animation: slide-in .3s ease; + @media(max-width: 978px) { + display: none; + } + } + + } +} + +// Leave only necessary sections id +#projector_longdescription, +#projector_dictionary, +#projector_enclosures, +#projector_warranty, +#product_questions_list, +#opinions_section, +#projector_blog, +#products_associated_zone1 { + display: none; + animation: slide-in-opacity .3s ease; + padding-top: 5rem; + &.idm-active { + display: block; + } +} \ No newline at end of file diff --git a/mobile-tabs /index.xslt b/mobile-tabs /index.xslt new file mode 100644 index 0000000..1eece70 --- /dev/null +++ b/mobile-tabs /index.xslt @@ -0,0 +1,66 @@ + +
+
+ + + +
+ +
+
+ + + +
+ +
+
+ + + +
+ +
+
+ + + +
+ +
+
+ + + +
+ +
+
+ + +
+ +
+ + + +
+ +
+
+ + + +
+ +
+
+ +
+
+ +
\ No newline at end of file diff --git a/mobile-tabs /src/main.js b/mobile-tabs /src/main.js new file mode 100644 index 0000000..3216907 --- /dev/null +++ b/mobile-tabs /src/main.js @@ -0,0 +1,106 @@ +// Leave only necessary sections id +const sectionIds = [ + 'projector_longdescription', + 'projector_dictionary', + 'projector_enclosures', + 'projector_warranty', + 'product_questions_list', + 'opinions_section', + 'projector_blog', + 'products_associated_zone1' +]; +const tabContents = sectionIds.map(id => document.getElementById(id)); +const tabButtons = document.querySelectorAll('.tabs__item'); +let isMobileView = false; + +// Engine +(() => { + let resizeTimeout + let prevView = app_shop.vars.view + const container = document.querySelector('#container') + + const VIEW_PHONE = [1, 2] + const VIEW_TABLET_DESKTOP = [3, 4] + + const handleResize = () => { + if (!container?.classList.contains('projector_page')) return + + clearTimeout(resizeTimeout) + resizeTimeout = setTimeout(() => { + const currentView = app_shop.vars.view + + if (VIEW_PHONE.includes(currentView)) { + isMobileView = true + placeContentsInBetween() + } else if (VIEW_TABLET_DESKTOP.includes(currentView)) { + isMobileView = false + } + + if (VIEW_TABLET_DESKTOP.includes(prevView) && VIEW_PHONE.includes(currentView)) { + placeContentsInBetween() + } else if (VIEW_PHONE.includes(prevView) && VIEW_TABLET_DESKTOP.includes(currentView)) { + placeContentsDefault() + } + + prevView = currentView + + initTabs() + }, 200) + } + + window.addEventListener('DOMContentLoaded', () => { + handleResize() + }) + + window.addEventListener('resize', handleResize) +})() + +// Utils +function handleTabClick(e) { + const btn = e.currentTarget + + if (isMobileView) { + tabButtons.forEach(b => { + if (b !== btn) b?.classList.remove('idm-active') + }) + btn?.classList.toggle('idm-active') + } else { + tabButtons.forEach(b => b?.classList.remove('idm-active')) + btn?.classList.add('idm-active') + } + + const tabId = btn?.dataset.tab + if (isMobileView) { + tabContents.forEach(content => { + if(content.id !== tabId) content?.classList.remove('idm-active') + }) + document.getElementById(tabId)?.classList.toggle('idm-active') + } else { + tabContents.forEach(content => content?.classList.remove('idm-active')) + document.getElementById(tabId)?.classList.add('idm-active') + } +} + +function initTabs() { + tabButtons.forEach(btn => { + btn.removeEventListener('click', handleTabClick) + btn.addEventListener('click', handleTabClick) + }) +} + +function placeContentsInBetween() { + tabButtons.forEach(btn => { + const tabId = btn?.dataset.tab + const relevantTab = document.getElementById(tabId) + btn.insertAdjacentElement('afterend', relevantTab) + }) +} + +function placeContentsDefault() { + const tabs = document.querySelector('.tabs') + tabButtons.forEach(btn => { + const tabId = btn?.dataset.tab + const relevantTab = document.getElementById(tabId) + tabs.insertAdjacentElement('afterend', relevantTab) + }) +} diff --git a/mobile-tabs /src/style.less b/mobile-tabs /src/style.less new file mode 100644 index 0000000..dc82720 --- /dev/null +++ b/mobile-tabs /src/style.less @@ -0,0 +1,113 @@ +@keyframes slide-in-opacity { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.tabs { + border-bottom: 3px solid #efefef; + + @media (max-width: 978px) { + border: none; + } + + &__container { + display: flex; + align-items: center; + gap: 1rem; + + @media (max-width: 978px) { + gap: 2rem; + flex-direction: column; + align-items: flex-start; + } + + } + + &__item { + white-space: nowrap; + position: relative; + padding: 8px 12px; + cursor: pointer; + transition: color .3s ease; + color: #888; + font-weight: 600; + + &:hover { + color: #000; + } + + &.idm-active { + color: #000; + + &::after { + opacity: 1; + } + + @media (max-width: 978px) { + &::before { + opacity: 0; + transform: translateY(50%); + } + } + } + + @media (max-width: 978px) { + width: 100%; + } + + &::after, &::before { + content: ""; + display: block; + opacity: 0; + position: absolute; + bottom: -3px; + left: 0; + width: 100%; + height: 2.5px; + background-color: black; + border-radius: 2px; + transition: all 0.3s ease; + } + + // Mobile plus&minus + @media (max-width: 978px) { + padding: 0; + + &::after, &::before { + opacity: 1; + width: 2rem; + left: calc(100% - 2rem); + bottom: 50%; + transform: translateY(50%); + } + + &::before { + opacity: 1; + transform: rotate(90deg) translateY(0) translateX(5%); + } + } + + } +} + +// Leave only necessary sections id +#projector_longdescription, +#projector_dictionary, +#projector_enclosures, +#projector_warranty, +#product_questions_list, +#opinions_section, +#projector_blog, +#products_associated_zone1 { + display: none; + animation: slide-in-opacity .3s ease; + padding-top: 5rem; + + &.idm-active { + display: block; + } +} \ No newline at end of file