Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: toggle #3822

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions rocky/assets/css/components/toggle-button.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
button,
a,
input[type="button"],
input[type="submit"],
input[type="reset"] {
$breakpoint: 24rem !default;

&.toggle-button {
/* Alignment */
display: flex;
align-items: center;
justify-content: center;
align-self: flex-start;
gap: 0;
margin: 0;
box-sizing: border-box;

/* Styling */
border-radius: var(--border-radius-l);
padding: var(--spacing-grid-150);
width: 2.75rem;
height: 2.75rem;
border: 1px solid var(--colors-purrple-300);

/* Text and icon styling */
background-color: var(--colors-white);
color: var(--colors-purrple-500);
text-decoration: none;
font-size: 0;

&::before {
color: var(--colors-purrple-500);
padding: 0;
}

/* Behaviour */
cursor: pointer;
overflow-wrap: break-word;

/* States */
&[aria-current="page"] {
background-color: var(--colors-purrple-200);
border-color: var(--colors-purrple-200);

&::before {
color: var(--colors-purrple-700);
}
}

&:hover,
&:active {
border-color: var(--colors-purrple-500);
}

&:focus {
outline: 2px solid var(--colors-purrple-500);
outline-offset: 0.125rem;
z-index: 2;
position: relative;
}
}
}

a {
&.toggle-button {
&:visited {
color: var(--colors-purrple-500);
}
}
}
100 changes: 47 additions & 53 deletions rocky/assets/css/components/toggle.scss
Original file line number Diff line number Diff line change
@@ -1,70 +1,64 @@
button,
a,
input[type="button"],
input[type="submit"],
input[type="reset"] {
$breakpoint: 24rem !default;
/* Toggle */

&.toggle-button {
/* Alignment */
display: flex;
align-items: center;
justify-content: center;
align-self: flex-start;
gap: 0;
margin: 0;
box-sizing: border-box;

/* Styling */
border-radius: var(--border-radius-l);
padding: var(--spacing-grid-150);
width: 2.75rem;
height: 2.75rem;
border: 1px solid var(--colors-purrple-300);
.toggle-switch {
display: flex;
flex-direction: row;
padding: var(--spacing-grid-050);
box-sizing: border-box;
width: auto;
border-radius: var(--border-radius-s);
margin-top: 0;
line-height: 1.5rem;
border: 1px solid var(--colors-purrple-300);
gap: var(--spacing-grid-100);
max-width: 100%;
overflow: auto;

/* Text and icon styling */
background-color: var(--colors-white);
color: var(--colors-purrple-500);
text-decoration: none;
font-size: 0;
li {
padding: var(--spacing-grid-050) var(--spacing-grid-100);
border-radius: var(--border-radius-s);
background-color: transparent;
cursor: pointer;
list-style: none;
box-sizing: border-box;
position: relative;
white-space: nowrap;

&::before {
color: var(--colors-purrple-500);
padding: 0;
a {
color: var(--colors-grey-900);
text-decoration: none;
}

/* Behaviour */
cursor: pointer;
overflow-wrap: break-word;

/* States */
&[aria-current="page"] {
&[aria-current="true"] {
background-color: var(--colors-purrple-200);
border-color: var(--colors-purrple-200);

&::before {
color: var(--colors-purrple-700);
a {
font-weight: bold;
}

&:hover {
background-color: var(--colors-purrple-300);
}
}

&:hover,
&:active {
border-color: var(--colors-purrple-500);
&:hover {
background-color: var(--colors-purrple-50);
}

&:focus {
outline: 2px solid var(--colors-purrple-500);
outline-offset: 0.125rem;
z-index: 2;
position: relative;
&::after {
content: "";
border-right: 1px solid var(--colors-purrple-300);
margin-left: var(--spacing-grid-100);
display: block;
position: absolute;
height: 60%;
top: 20%;
right: calc((1px + var(--spacing-grid-050)) * -1);
width: 1px;
}
}
}

a {
&.toggle-button {
&:visited {
color: var(--colors-purrple-500);
&:last-child::after {
content: none;
}
}
}
1 change: 1 addition & 0 deletions rocky/assets/css/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
@import "components/state-tags";
@import "components/table";
@import "components/toggle";
@import "components/toggle-button";
@import "components/toolbar";
@import "components/tree-tables";
@import "components/user-icon";
Expand Down
51 changes: 51 additions & 0 deletions rocky/assets/js/toggleSwitch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { onDomReady } from "../js/imports/utils.js";

onDomReady(initToggleSwitches);

function initToggleSwitches() {
// Switches get stitches ;-)
let toggle_switches = document.querySelectorAll(".toggle-switch");

for (let i = 0; i < toggle_switches.length; i++) {
let options = toggle_switches[i].querySelectorAll(".toggle-switch-button");
TwistMeister marked this conversation as resolved.
Show resolved Hide resolved

for (let j = 0; j < options.length; j++) {
let option = options[j];

// Hide all elements linked to toggle switch options.
document
.getElementById(option.getAttribute("data-target-id"))
.classList.add("hidden");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say, Hide all of them by applyting a className to it.
And then use the :target selector and the url to show the one being active. that moves the state-management from JS to Css for most of this.
It would not be compatible with nested versions of this component, as selecting a nested 'tab' would hide the parent tab, But I don't think we have that usecase anyway.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While it would be nice if state management didn't require JS, it's unfortunately not possible to make this fully accessible without using JS to manage ARIA attributes.

@underdarknl could you please verify whether the APG's Tabs pattern aligns with the expected use here, as I suspect? If so I would implore you to follow one of the APG's example implementations. If not, let's look together for fitting ARIA roles and behaviours.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do think the Tabs pattern fits this nicely. Do you have a preference on which example to follow? Especially if we want to move this to Manon upstream at some point?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know the exact context. If switching accidentally would be a problem, manual activation would make sense. For upstream we could add both and make it configurable.


// Add click listener to switch options.
option.addEventListener("click", (event) => {
toggle(event.target, options);
});
}

// Initially show first option contents from toggle-switch.
document
.getElementById(options[0].getAttribute("data-target-id"))
TwistMeister marked this conversation as resolved.
Show resolved Hide resolved
.classList.remove("hidden");
}
}

function toggle(target, options) {
let target_li = target.closest("li");

// Check if target isn't already the active one.
if (!target_li.hasAttribute("aria-current", "true")) {
for (let i = 0; i < options.length; i++) {
// Toggle all options to "non active" state.
options[i].closest("li").removeAttribute("aria-current", "false");
document
.getElementById(options[i].getAttribute("data-target-id"))
.classList.add("hidden");
}
// Toggle selected option (target) to active state.
target_li.setAttribute("aria-current", "true");
document
.getElementById(target.getAttribute("data-target-id"))
.classList.remove("hidden");
}
}
Loading