From 8ec6ced8c4e032669e813f0068ba2d041bc9bd4a Mon Sep 17 00:00:00 2001 From: kmurphychi247 <82771690+kmurphychi247@users.noreply.github.com> Date: Thu, 6 Jun 2024 10:22:34 -0500 Subject: [PATCH] adds responsive tabs styling (#46) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * adds responsive tabs styling * removes js from library --------- Co-authored-by: “Kerry <“kmurphychi@gmail.com”> --- assets/js/tabs.js | 31 ++++ assets/scss/custom/_overrides.scss | 43 ++++++ saplings_child.libraries.yml | 1 + ...-paragraph--sa-tab-item--sa-tabs.html.twig | 28 ++-- .../paragaphs/paragraph--sa-tabs.html.twig | 139 ++++++++++++++++++ .../nav_tabs/nav_tabs.ui_patterns.yml | 95 ++++++++++++ .../nav_tabs/pattern-nav-tabs.html.twig | 10 ++ 7 files changed, 337 insertions(+), 10 deletions(-) create mode 100644 assets/js/tabs.js create mode 100644 templates/overrides/paragaphs/paragraph--sa-tabs.html.twig create mode 100644 templates/patterns/nav_tabs/nav_tabs.ui_patterns.yml create mode 100644 templates/patterns/nav_tabs/pattern-nav-tabs.html.twig diff --git a/assets/js/tabs.js b/assets/js/tabs.js new file mode 100644 index 0000000..72154ca --- /dev/null +++ b/assets/js/tabs.js @@ -0,0 +1,31 @@ +document.addEventListener('DOMContentLoaded', function () { + const selectElement = document.getElementById('nav-tabs-mobile-js'); + const tabButtons = document.querySelectorAll('[data-bs-target]'); + + selectElement.addEventListener('change', function () { + const selectedTab = selectElement.value; + const tabTrigger = document.querySelector(`button[data-bs-target="${selectedTab}"]`); + if (tabTrigger) { + new bootstrap.Tab(tabTrigger).show(); + } + }); + + tabButtons.forEach(button => { + button.addEventListener('click', function () { + const target = button.getAttribute('data-bs-target'); + const tabTrigger = document.querySelector(`button[data-bs-target="${target}"]`); + if (tabTrigger) { + new bootstrap.Tab(tabTrigger).show(); + } + }); + }); + + // Optional: Change the select dropdown when a tab is clicked + document.querySelectorAll('#myTab button').forEach(button => { + button.addEventListener('shown.bs.tab', function (e) { + const target = e.target.getAttribute('data-bs-target'); // Get the data-bs-target attribute + selectElement.value = target; // Set the select value + }); + }); +}); + diff --git a/assets/scss/custom/_overrides.scss b/assets/scss/custom/_overrides.scss index 7672d8c..5277331 100644 --- a/assets/scss/custom/_overrides.scss +++ b/assets/scss/custom/_overrides.scss @@ -45,3 +45,46 @@ em.placeholder { lite-youtube { max-width: 100%; } + +// Vertical Tabs +.vtabs { + @include media-breakpoint-up(lg) { + display: grid; + grid-template-columns: 50% 50%; + grid-gap: 1rem; + } + + .accordion-item { + border: none; + } + + .nav-tabs .nav-link { + width: 100%; + text-align: left; + border-radius: 0.375rem; + + &.active { + border-bottom: #dee2e6 1px solid; + } + } + + ul.nav-tabs.nav { + display: block; + border-bottom: none; + } +} + +.nav.nav-tabs.nav-tabs-mobile { + display: flex; + width: 100%; + background-color: #fff; + padding: 0.625rem 2.25rem 0.625rem 0.625rem; + margin-bottom: 1.25rem; + margin-top: 1rem; + font-size: 1rem; + font-weight: normal; + line-height: 130%; + border-radius: 3px; + border: 1px solid #000; + cursor: pointer; +} diff --git a/saplings_child.libraries.yml b/saplings_child.libraries.yml index e331063..2762255 100644 --- a/saplings_child.libraries.yml +++ b/saplings_child.libraries.yml @@ -11,3 +11,4 @@ form: am: js: dist/js/js/am.js: { minified: true } + diff --git a/templates/overrides/fields/field--paragraph--sa-tab-item--sa-tabs.html.twig b/templates/overrides/fields/field--paragraph--sa-tab-item--sa-tabs.html.twig index 3c9da74..deb5c00 100644 --- a/templates/overrides/fields/field--paragraph--sa-tab-item--sa-tabs.html.twig +++ b/templates/overrides/fields/field--paragraph--sa-tab-item--sa-tabs.html.twig @@ -1,21 +1,29 @@ +{# Check if the field is part of a paragraph #} +{% if element['#object'] is defined %} + {# Get the parent paragraph entity #} + {% set parent_paragraph = element['#object'].id.value %} +{% endif %} {% set pane_id_base = "pane-" ~ random() %} - -{% set tabs = [] %} -{% for item in items %} - {% set tab = pattern('nav_item', { +
+ {% set tabs = [] %} + {% for item in items %} + {% set tab = pattern('nav_item', { link: item.content|merge({'#view_mode': 'sa_tab_item'}) , - toggle: pane_id_base ~ '-' ~ loop.index, + toggle: parent_paragraph ~ '-' ~ loop.index, active: loop.first }) %} - {% set tabs = tabs|merge([tab])%} -{% endfor %} -{{ pattern('nav', { + {% set tabs = tabs|merge([tab])%} + {% endfor %} + {{ pattern('nav_tabs', { items: tabs -}, 'tabs') }} +}, 'tabs nav-tabs-responsive') }} + +
{% for item in items %} + {% set item = loop.first ? item.content|add_class(["show", "active"]) : item.content %} - {{ item|add_class("tab-pane")|set_attribute("id", pane_id_base ~ '-' ~ loop.index) }} + {{ item|add_class("tab-pane")|set_attribute("id", parent_paragraph ~ '-' ~ loop.index) }} {% endfor %}
diff --git a/templates/overrides/paragaphs/paragraph--sa-tabs.html.twig b/templates/overrides/paragaphs/paragraph--sa-tabs.html.twig new file mode 100644 index 0000000..ce5bb02 --- /dev/null +++ b/templates/overrides/paragaphs/paragraph--sa-tabs.html.twig @@ -0,0 +1,139 @@ +{# +/** + * @file + * Default theme implementation to display a paragraph. + * + * Available variables: + * - paragraph: Full paragraph entity. + * Only method names starting with "get", "has", or "is" and a few common + * methods such as "id", "label", and "bundle" are available. For example: + * - paragraph.getCreatedTime() will return the paragraph creation timestamp. + * - paragraph.id(): The paragraph ID. + * - paragraph.bundle(): The type of the paragraph, for example, "image" or "text". + * - paragraph.getOwnerId(): The user ID of the paragraph author. + * See Drupal\paragraphs\Entity\Paragraph for a full list of public properties + * and methods for the paragraph object. + * - content: All paragraph items. Use {{ content }} to print them all, + * or print a subset such as {{ content.field_example }}. Use + * {{ content|without('field_example') }} to temporarily suppress the printing + * of a given child element. + * - attributes: HTML attributes for the containing element. + * The attributes.class element may contain one or more of the following + * classes: + * - paragraphs: The current template type (also known as a "theming hook"). + * - paragraphs--type-[type]: The current paragraphs type. For example, if the paragraph is an + * "Image" it would result in "paragraphs--type--image". Note that the machine + * name will often be in a short form of the human readable label. + * - paragraphs--view-mode--[view_mode]: The View Mode of the paragraph; for example, a + * preview would result in: "paragraphs--view-mode--preview", and + * default: "paragraphs--view-mode--default". + * - view_mode: View mode; for example, "preview" or "full". + * - logged_in: Flag for authenticated user status. Will be true when the + * current user is a logged-in member. + * - is_admin: Flag for admin user status. Will be true when the current user + * is an administrator. + * + * @see template_preprocess_paragraph() + * + * @ingroup themeable + */ +#} +{% + set classes = [ + 'paragraph', + 'paragraph--type--' ~ paragraph.bundle|clean_class, + view_mode ? 'paragraph--view-mode--' ~ view_mode|clean_class, + not paragraph.isPublished() ? 'paragraph--unpublished', + 'paragraph--id--' ~ paragraph.id.value, + ] +%} +{% set verticaltabs = 0 %} +{% if content.sa_display_as_vertical_tabs['#items'].value == 1 %} + {% set verticaltabs = true %} +{% endif %} + +{% set vtabs = '' %} +{% if verticaltabs %} + {% set vtabs = ' vtabs' %} +{% endif %} + +{% set container_class_string = 'container' %} +{% if paragraph.sa_container.value is not empty %} + {% set container_class_string = ' ' ~ paragraph.sa_container.value %} +{% endif %} + +{% set width_class_string = '' %} +{% if paragraph.sa_width.value is not empty %} + {% set width_class_string = ' ' ~ paragraph.sa_width.value %} +{% endif %} + +{% set margin_class_string = '' %} +{% if paragraph.sa_margin.value is not empty %} + {% set margin_class_string = ' ' ~ paragraph.sa_margin.value %} +{% endif %} + +{% set padding_class_string = '' %} +{% if paragraph.sa_padding.value is not empty %} + {% set padding_class_string = ' ' ~ paragraph.sa_padding.value %} +{% endif %} + +{% set bg_class_string = '' %} +{% if paragraph.sa_background.value is not empty %} + {% set bg_class_string = ' ' ~ paragraph.sa_background.value %} +{% endif %} + +{{ attach_library('saplings_child/tabs') }} + +{% block paragraph %} + +
+ {% if paragraph.sa_width.value is not empty %} +
+
+ {% endif %} +
+
+ {{ content.sa_header}} + {{ content.sa_description}} +
+
+
+
+ {% if content.sa_accordion_mobile['#items'].value == 1 %} +
+ {% endif %} +
+
+ + +
+ {% block content %} + {{ content|without('sa_header', 'sa_description', 'sa_display_as_vertical_tabs', 'sa_accordion_mobile' ) }} + {% endblock %} +
+ {% if content.sa_accordion_mobile['#items'].value == 1 %} +
+ {% endif %} + {% if content.sa_accordion_mobile['#items'].value == 1 %} +
+ {% for i in content.sa_tab_item['#items']|keys %} + {{ content.sa_tab_item[i]['#paragraph']|view('accordion') }} + {% endfor %} +
+ {% endif %} +
+
+ {% if paragraph.sa_width.value is not empty %} +
+
+ {% endif %} +
+ +{% endblock paragraph %} diff --git a/templates/patterns/nav_tabs/nav_tabs.ui_patterns.yml b/templates/patterns/nav_tabs/nav_tabs.ui_patterns.yml new file mode 100644 index 0000000..7218b1e --- /dev/null +++ b/templates/patterns/nav_tabs/nav_tabs.ui_patterns.yml @@ -0,0 +1,95 @@ +nav_tabs: + label: "Nav" + description: "The base .nav component is built with flexbox and provide a strong foundation for building all types of navigation components. It includes some style overrides (for working with lists), some link padding for larger hit areas, and basic disabled styling." + links: + - 'https://getbootstrap.com/docs/5.3/components/navs-tabs/' + category: "Navs and tabs" + variants: + default: + label: "Default" + tabs: + label: "Tabs" + tabs__fill: + label: "Tabs" + tabs__justified: + label: "Tabs" + pills: + label: "Pills" + pills__fill: + label: "Pills filled" + pills__justified: + label: "Pills filled with same width" + underline: + label: "Underline" + settings: + nav_type: + type: "select" + label: "List type" + options: + ul: "ul (Default)" + ol: "ol" + nav: "nav" + preview: "ul" + allow_expose: true + allow_token: true + fields: + items: + type: "list" + label: "Nav items" + description: "Nav items that appear inside the navigation component." + preview: + - type: "pattern" + id: "nav_item" + active: true + link: + type: 'html_tag' + tag: 'a' + value: 'Active' + attributes: + href: 'https://example.com' + - type: "pattern" + id: "dropdown" + button_type: "a" + dropdown_navbar: true + attributes: + class: + 'nav-item' + title: "Dropdown" + content: + - title: "Dropdown header" + link_attributes: + class: [dropdown-header] + - title: "Action" + url: '#' + - title: "Dropdown item text" + - title: "Another action" + url: '#' + - title: "Something else here" + url: '#' + - {} + - title: "Separated link" + url: '#' + - title: "Action (button)" + link_attributes: + class: [dropdown-item] + - title: "Another action (button)" + link_attributes: + class: [dropdown-item] + - title: "Something else here (text)" + - type: "pattern" + id: "nav_item" + link: + type: 'html_tag' + tag: 'a' + value: 'Much longer nav link' + attributes: + href: 'https://example.com' + - type: "pattern" + id: "nav_item" + disabled: true + link: + type: 'html_tag' + tag: 'a' + value: 'Disabled' + attributes: + href: 'https://example.com' diff --git a/templates/patterns/nav_tabs/pattern-nav-tabs.html.twig b/templates/patterns/nav_tabs/pattern-nav-tabs.html.twig new file mode 100644 index 0000000..9a53c51 --- /dev/null +++ b/templates/patterns/nav_tabs/pattern-nav-tabs.html.twig @@ -0,0 +1,10 @@ +{% if variant and variant|lower != 'default' %} + {% set variants = variant|split('__')|map(v => v|lower|replace({(v): 'nav-' ~ v})|replace({'_': '-'})) %} + {% set attributes = attributes.addClass(variants) %} +{% endif %} + +{% set nav_type = nav_type|default('ul') %} + +<{{ nav_type }}{{ attributes.addClass('nav').setAttribute('id', 'nav-tabs-mobile-js') }}> + {{ items }} +