Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
448 changes: 448 additions & 0 deletions examples/web-components/toolbar/index.html

Large diffs are not rendered by default.

2,706 changes: 2,706 additions & 0 deletions examples/web-components/toolbar/package-lock.json
Copy link
Contributor

Choose a reason for hiding this comment

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

Let’s remove the package-lock since we don’t need to include these in examples.

Copy link
Contributor Author

@devadula-nandan devadula-nandan May 16, 2025

Choose a reason for hiding this comment

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

@elycheea, i had to refactor a lot of this structure, to be consistent with how we are delivering patterns. taking reference from ibm products.
also adds a complete lit example instead html, being consistent with ibm-products
i also had to add a lit plugin for vite config to load lit example styles in storybook.

pls have a second look when you get some time. 😄

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions examples/web-components/toolbar/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "carbon-web-components-icon-button-example",
"version": "0.1.0",
"private": true,
"description": "Sample project for getting started with the Web Components from Carbon.",
"license": "Apache-2",
"main": "index.html",
"scripts": {
"build": "vite build",
"clean": "rimraf node_modules dist .cache",
"dev": "vite"
},
"dependencies": {
"@carbon/styles": "^1.34.0",
"@carbon/web-components": "latest",
"sass": "^1.64.1"
},
"devDependencies": {
"vite": "5.2.13",
"rimraf": "^3.0.2"
}
}
83 changes: 83 additions & 0 deletions examples/web-components/toolbar/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* @license
*
* Copyright IBM Corp. 2025
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import '@carbon/web-components/es/components/stack/index.js';
import '@carbon/web-components/es/components/icon-button/index.js';
import '@carbon/web-components/es/components/dropdown/index.js';
import '@carbon/web-components/es/components/overflow-menu/index.js';

document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.toolbar cds-dropdown').forEach((component) => {
if (component.shadowRoot) {
const style = document.createElement('style');
style.textContent = `
.cds--list-box {
border-block-end: none !important;
}
`;
component.shadowRoot.appendChild(style);
}
});
});

// roving keyboard navigation utility
/**
* Initializes roving tabindex for keyboard navigation within a container.
* @param {HTMLElement} container - The container element containing the buttons.
*/
function initRovingTabIndex(container) {
const buttons = Array.from(container.children).flatMap((el) =>
Array.from(el.children)
);
if (buttons.length === 0) return;

buttons.forEach((btn, index) =>
btn.setAttribute('tabindex', index === 0 ? '0' : '-1')
);

/**
* Updates the active index for keyboard navigation and sets focus on the corresponding button.
* @param {number} newIndex - The index of the button to be activated.
*/
function updateActiveIndex(newIndex) {
buttons.forEach((btn, i) =>
btn.setAttribute('tabindex', i === newIndex ? '0' : '-1')
);
buttons[newIndex].focus();
}

container.addEventListener('keydown', (event) => {
const currentIndex = buttons.findIndex(
(btn) => btn.getAttribute('tabindex') === '0'
);

let newIndex = currentIndex;
if (event.key === 'ArrowRight') {
newIndex = (currentIndex + 1) % buttons.length;
} else if (event.key === 'ArrowLeft') {
newIndex = (currentIndex - 1 + buttons.length) % buttons.length;
} else {
return;
}

updateActiveIndex(newIndex);
event.preventDefault();
});

container.addEventListener('click', (event) => {
const clickedIndex = buttons.indexOf(event.target);
if (clickedIndex !== -1) {
updateActiveIndex(clickedIndex);
}
});
}

document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.toolbar').forEach(initRovingTabIndex);
});
64 changes: 64 additions & 0 deletions examples/web-components/toolbar/src/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// Copyright IBM Corp. 2024
//
// This source code is licensed under the Apache-2.0 license found in the
// LICENSE file in the root directory of this source tree.
//

@use '@carbon/styles/scss/reset';
@use '@carbon/styles/scss/theme';
@use '@carbon/styles/scss/themes';

// :root {
// @include theme.theme(themes.$g100); // change this to check all themes
// }
.toolbar {
display: inline-flex;
background: var(--cds-layer);

svg {
pointer-events: none;
}

&[orientation='vertical'] {
max-inline-size: 2.5rem;
}
}

.toolbar-group {
flex-direction: row;
border-block-end: 1px solid var(--cds-border-subtle-01, #c6c6c6);
border-inline-end: 1px solid var(--cds-border-subtle-01, #c6c6c6);

&[orientation='vertical'] {
border-inline-end: none;
max-inline-size: 2.5rem;

&:last-child {
border-block-end: none;
}
}

&:last-child {
border-inline-end: none;
}
}

// css for adding caret through class name
.toolbar-button-caret {
position: relative;

&::before {
position: absolute;
background: linear-gradient(
to right bottom,
transparent 50%,
var(--cds-icon-primary, #161616) 50%
);
block-size: calc(0.125rem + 0.25rem);
content: '';
inline-size: calc(0.125rem + 0.25rem);
inset-block-end: 0.175rem;
inset-inline-end: 0.175rem;
}
}
20 changes: 20 additions & 0 deletions examples/web-components/toolbar/vite.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @license
*
* Copyright IBM Corp. 2025
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
import { resolve } from 'path';
import { defineConfig } from 'vite';

export default defineConfig({
build: {
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html'),
},
},
},
});
Loading