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

Rewrite useOverflowItems utility as vanilla JS #6883

Open
2 tasks
elycheea opened this issue Feb 11, 2025 · 1 comment · May be fixed by carbon-design-system/carbon#18644
Open
2 tasks

Rewrite useOverflowItems utility as vanilla JS #6883

elycheea opened this issue Feb 11, 2025 · 1 comment · May be fixed by carbon-design-system/carbon#18644
Assignees
Labels
adopter: automation Issues connected to work that accelerates IBM's automation offerings. area: utilities component: TagSet role: dev web component
Milestone

Comments

@elycheea
Copy link
Contributor

elycheea commented Feb 11, 2025

Currently, useOverflowItems is written as a React hook, but to support the many acquisitions teams looking to use the TagSet, we’d want to rewrite this as a vanilla utility provided through @carbon/utilities.

Acceptance criteria

  • Rewrite useOverflowItems as framework agnostic
  • Provide through @carbon/utilities package
@elycheea elycheea added adopter: automation Issues connected to work that accelerates IBM's automation offerings. area: utilities component: TagSet web component labels Feb 11, 2025
@elycheea elycheea added this to the 2025 Q1 milestone Feb 11, 2025
@github-project-automation github-project-automation bot moved this to Needs triage 🧐 in Carbon for IBM Products Feb 11, 2025
@elycheea elycheea moved this to Backlog 🌋 in Carbon for IBM Products Feb 11, 2025
@elycheea elycheea changed the title Rewrite useOverflowItems utility as vanilla Rewrite useOverflowItems utility as vanilla JS Feb 11, 2025
@devadula-nandan
Copy link
Contributor

devadula-nandan commented Feb 12, 2025

some of my opinions and initial explorations

it is better to have the utility responsible only for calculations, by taking widths of the items mapped to their ids in dom, size of the container, and overflow at end or start

since these are purely a bunch of calculations. we can use this on vertically stacked elements as well, in that case we send height of the elements and total height of the container.

  /**
   * Determines which items fit into a given maximum size.
   *
   * @param {Object} items - An object with keys and numerical values representing size. Size could be either height or width.
   * @param {number} size - The maximum allowed total size. Size could be either height or width.
   * @param {("start"|"end")} at - Which side to start hiding items from.
   *                               "end" (default) hides items from the end,
   *                               while "start" hides items from the beginning.
   * @returns {Object} An object with "visible" and "hidden" items.
   */
  function getOverflow(items, size, at = "end") {
    const itemEntries = Object.entries(items);
    let visible = {};
    let hidden = {};

    const entries = itemEntries.slice();
    if (at === "start") {
      // if overflowing from the start, accumulate from the end and iterate backwards
      let totalSize = 0;
      for (let i = entries.length - 1; i >= 0; i--) {
        const [key, itemSize] = entries[i];
        totalSize += itemSize;
        if (totalSize <= size) {
          visible[key] = itemSize;
        } else {
          hidden[key] = itemSize;
        }
      }
      // reverse both since we accumulated from the end
      visible = Object.fromEntries(Object.entries(visible).reverse());
      hidden = Object.fromEntries(Object.entries(hidden).reverse());
    } else {
      // default overflowing at end, accumulate from the beginning
      let totalSize = 0;
      for (const [key, itemSize] of itemEntries) {
        totalSize += itemSize;
        if (totalSize <= size) {
          visible[key] = itemSize;
        } else {
          hidden[key] = itemSize;
        }
      }
    }

    // TODO: one more prop `gap` to account for gap between items?
    // TODO: take more/overflow button size into account.
    // TODO: test if hidden items order is correct for horizontal and vertical start and end cases.
    // TODO: test ltr, rtl

    return { visible, hidden };
  }

  // example usage
  const items = {
    "id-1": 40,
    "id-2": 40,
    "id-3": 40,
    "id-4": 40,
    "id-5": 40,
    "id-6": 40,
    "id-7": 40,
    "id-8": 40,
    "id-9": 40,
    "id-10": 40,
  };

  const overflowDetailsEnd = getOverflow(items, 200);
  console.log("hide at end:\n", JSON.stringify(overflowDetailsEnd));
//   hide at end:
//  {"visible":{"id-1":40,"id-2":40,"id-3":40,"id-4":40,"id-5":40},"hidden":{"id-6":40,"id-7":40,"id-8":40,"id-9":40,"id-10":40}}

  const overflowDetailsStart = getOverflow(items, 200, "start");
  console.log("hide at start:\n", JSON.stringify(overflowDetailsStart));
//   hide at start:
//  {"visible":{"id-6":40,"id-7":40,"id-8":40,"id-9":40,"id-10":40},"hidden":{"id-1":40,"id-2":40,"id-3":40,"id-4":40,"id-5":40}}

  // export default getOverflow;

this approach requires us to provide the sizes of the items and container size. and the utility is responsible only for making those calculations.

in a react application we can get widths by passing a ref and getting offsetWidth
in a vanilla/webcomponent we can get the widths by document.getElementById("id-1").offsetWidth
etc

i feel getting the widths of the elements is framework dependent task and should not be in the scope of this utility.
also there are different strategies in getting the widths of these elements in different environments.
react strategy
one strategy we all have seen is to render a duplicate but visually hide them in the dom, this way we can have access to all the elements sizes

a different strategy from thoughts
on render 1 do not apply any overflow, let all the items render in the dom and get their sizes and container size, and make a stable reference of them, maybe a reference outside of the component.
pass these information to the hook and get the overflowing data
on render 2 settle the items according to the data.
we can make the items visually hidden on render 1, and show a skeleton. until this utility returns

these are only my initial thoughts and a lot of things need to taken into consideration. hope these are useful in implementing this utility.

@ljcarot ljcarot mentioned this issue Feb 17, 2025
6 tasks
@makafsal makafsal moved this from Backlog 🌋 to In progress in Carbon for IBM Products Feb 19, 2025
@devadula-nandan devadula-nandan moved this from In progress to Needs review 👋 in Carbon for IBM Products Feb 25, 2025
@devadula-nandan devadula-nandan moved this from Needs review 👋 to In review 👀 in Carbon for IBM Products Mar 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
adopter: automation Issues connected to work that accelerates IBM's automation offerings. area: utilities component: TagSet role: dev web component
Projects
Status: In review 👀
Development

Successfully merging a pull request may close this issue.

3 participants