From 3e9abc1e8cafd7c47034fedfb021ace5a27c7c8e Mon Sep 17 00:00:00 2001 From: Jesse Winton Date: Wed, 19 Feb 2025 13:03:03 -0500 Subject: [PATCH 1/8] updates --- v2/pink-sb/src/lib/index.ts | 1 + .../src/lib/lab/overflow-tabs/Button.svelte | 40 +++++++++ .../src/lib/lab/overflow-tabs/Link.svelte | 43 +++++++++ .../src/lib/lab/overflow-tabs/Root.svelte | 78 ++++++++++++++++ .../src/lib/lab/overflow-tabs/_tabs.scss | 89 +++++++++++++++++++ v2/pink-sb/src/lib/lab/overflow-tabs/index.ts | 11 +++ v2/pink-sb/src/lib/lab/overflow-tabs/types.ts | 1 + .../src/stories/OverflowTabs.stories.svelte | 25 ++++++ 8 files changed, 288 insertions(+) create mode 100644 v2/pink-sb/src/lib/lab/overflow-tabs/Button.svelte create mode 100644 v2/pink-sb/src/lib/lab/overflow-tabs/Link.svelte create mode 100644 v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte create mode 100644 v2/pink-sb/src/lib/lab/overflow-tabs/_tabs.scss create mode 100644 v2/pink-sb/src/lib/lab/overflow-tabs/index.ts create mode 100644 v2/pink-sb/src/lib/lab/overflow-tabs/types.ts create mode 100644 v2/pink-sb/src/stories/OverflowTabs.stories.svelte diff --git a/v2/pink-sb/src/lib/index.ts b/v2/pink-sb/src/lib/index.ts index 8b18865b63..2e07825ee0 100644 --- a/v2/pink-sb/src/lib/index.ts +++ b/v2/pink-sb/src/lib/index.ts @@ -19,6 +19,7 @@ export { default as Layout } from './layout/index.js'; export { default as Input } from './input/index.js'; export { default as Selector } from './selector/index.js'; export { default as Tabs } from './tabs/index.js'; +export { default as OverflowTabs } from './lab/overflow-tabs/index.js'; export { default as ActionList } from './action-list/index.js'; export { default as ActionMenu } from './action-menu/index.js'; export { default as Upload } from './upload/index.js'; diff --git a/v2/pink-sb/src/lib/lab/overflow-tabs/Button.svelte b/v2/pink-sb/src/lib/lab/overflow-tabs/Button.svelte new file mode 100644 index 0000000000..74b8e5e52e --- /dev/null +++ b/v2/pink-sb/src/lib/lab/overflow-tabs/Button.svelte @@ -0,0 +1,40 @@ + + + + + diff --git a/v2/pink-sb/src/lib/lab/overflow-tabs/Link.svelte b/v2/pink-sb/src/lib/lab/overflow-tabs/Link.svelte new file mode 100644 index 0000000000..6c2383ca1d --- /dev/null +++ b/v2/pink-sb/src/lib/lab/overflow-tabs/Link.svelte @@ -0,0 +1,43 @@ + + + + + + + diff --git a/v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte b/v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte new file mode 100644 index 0000000000..cd6b006e3c --- /dev/null +++ b/v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte @@ -0,0 +1,78 @@ + + + + +
+ + + {#if overflowItems.length} + + {/if} +
+ + diff --git a/v2/pink-sb/src/lib/lab/overflow-tabs/_tabs.scss b/v2/pink-sb/src/lib/lab/overflow-tabs/_tabs.scss new file mode 100644 index 0000000000..65844fb579 --- /dev/null +++ b/v2/pink-sb/src/lib/lab/overflow-tabs/_tabs.scss @@ -0,0 +1,89 @@ +@use '../../../scss/mixins/transitions'; + +@mixin base { + @include transitions.common; + cursor: pointer; + display: inline-flex; + padding: var(--space-3) var(--space-6); + justify-content: center; + align-items: center; + gap: var(--space-3); + outline-offset: var(--border-width-m); + border: var(--border-width-s) solid transparent; + white-space: nowrap; + + color: var(--color-fgcolor-neutral-secondary); + text-align: center; + font-family: var(--font-family-sansserif); + font-size: var(--font-size-s); + font-style: normal; + font-weight: 400; + line-height: 140%; /* 19.6px */ + letter-spacing: -0.063px; + + &.tab- { + &primary { + background: var(--color-bgcolor-neutral-secondary); + border-radius: var(--border-radius-s); + + &:not(.active):not(&:disabled):not(&[aria-disabled='true']) { + &:hover, + &:active { + background: var(--color-overlay-neutral-hover); + } + } + + &.active { + color: var(--color-fgcolor-neutral-primary); + border: var(--border-width-s) solid var(--color-border-neutral); + background: var(--color-bgcolor-neutral-primary); + box-shadow: + 0px 1px 3px 0px rgba(0, 0, 0, 0.03), + 0px 4px 4px 0px rgba(0, 0, 0, 0.04); + } + + &:disabled, + &[aria-disabled='true'] { + cursor: default; + opacity: 0.4; + background: var(--color-bgcolor-neutral-secondary); + } + + &:focus-visible { + outline: var(--border-width-l) solid var(--color-border-focus); + z-index: 1000; + } + } + + &secondary { + background: transparent; + &:not(.active):not(&:disabled):not(&[aria-disabled='true']) { + &:hover, + &:active { + color: var(--color-fgcolor-neutral-primary); + } + } + + &.active { + color: var(--color-fgcolor-neutral-primary); + font-weight: 500; + // border: var(--border-width-s) solid var(--color-border-neutral); + // background: var(--color-bgcolor-neutral-primary); + border-block-end: 1px solid var(--color-border-neutral-strongest); + } + + &:disabled, + &[aria-disabled='true'] { + cursor: default; + opacity: 0.4; + } + + &:focus-visible { + outline: var(--border-width-l) solid var(--color-border-focus); + } + } + &stretch { + flex: 1; + } + } +} diff --git a/v2/pink-sb/src/lib/lab/overflow-tabs/index.ts b/v2/pink-sb/src/lib/lab/overflow-tabs/index.ts new file mode 100644 index 0000000000..32de2336fa --- /dev/null +++ b/v2/pink-sb/src/lib/lab/overflow-tabs/index.ts @@ -0,0 +1,11 @@ +import Root from './Root.svelte'; +import Link from './Link.svelte'; +import Button from './Button.svelte'; + +export default { + Root, + Item: { + Link, + Button + } +}; diff --git a/v2/pink-sb/src/lib/lab/overflow-tabs/types.ts b/v2/pink-sb/src/lib/lab/overflow-tabs/types.ts new file mode 100644 index 0000000000..5e331d4a4a --- /dev/null +++ b/v2/pink-sb/src/lib/lab/overflow-tabs/types.ts @@ -0,0 +1 @@ +export type Variant = 'primary' | 'secondary'; diff --git a/v2/pink-sb/src/stories/OverflowTabs.stories.svelte b/v2/pink-sb/src/stories/OverflowTabs.stories.svelte new file mode 100644 index 0000000000..36602f8d92 --- /dev/null +++ b/v2/pink-sb/src/stories/OverflowTabs.stories.svelte @@ -0,0 +1,25 @@ + + + + + + + Tab Item + Tab Item + Tab Item + Tab Item + Tab Item + Tab Item + + From 20be4a08901b6759b5ac777688d3ac80c9643dc8 Mon Sep 17 00:00:00 2001 From: Jesse Winton Date: Wed, 19 Feb 2025 13:09:13 -0500 Subject: [PATCH 2/8] updates --- .../src/lib/lab/overflow-tabs/Root.svelte | 65 ++++++++++--------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte b/v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte index cd6b006e3c..c96c9766ec 100644 --- a/v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte +++ b/v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte @@ -8,17 +8,21 @@ export let maxHeight = 33; $: clientHeight = 0; + $: overflowItems = [] as Array; let tablist: HTMLDivElement; - let overflowItems: Array = []; const autocollapse = () => { - if (clientHeight >= maxHeight) return; + if (clientHeight >= maxHeight) { + console.log('all good'); + } while (clientHeight > maxHeight) { + console.log('BIGGER'); const lastItem = tablist.lastElementChild; - overflowItems.unshift(lastItem as unknown as typeof Link | typeof Button); - tablist.removeChild(lastItem!); + + overflowItems.push('lastItem'); + clientHeight -= lastItem!.clientHeight; } @@ -27,7 +31,7 @@ } }; - $: console.log(clientHeight, maxHeight); + $: console.log(clientHeight, maxHeight, overflowItems); @@ -41,38 +45,35 @@ class:tabs-stretch={stretch} > - - {#if overflowItems.length} - - {/if} +{#if overflowItems.length} +
+ MORE ITEMS + {#each overflowItems as item} + {item} + {/each} +
+{/if} From d026c21cd967b2c554019931c25ee628b6709b31 Mon Sep 17 00:00:00 2001 From: Jesse Winton Date: Wed, 19 Feb 2025 14:03:55 -0500 Subject: [PATCH 3/8] some work --- v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte b/v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte index c96c9766ec..9f4b72232c 100644 --- a/v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte +++ b/v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte @@ -2,13 +2,16 @@ import Link from './Link.svelte'; import Button from './Button.svelte'; import type { Variant } from './types.js'; + import type { SvelteComponent } from 'svelte'; + + type ComponentType = new (...args: any[]) => SvelteComponent; export let variant: Variant = 'primary'; export let stretch: boolean = false; export let maxHeight = 33; $: clientHeight = 0; - $: overflowItems = [] as Array; + $: overflowItems = [] as Array; let tablist: HTMLDivElement; @@ -18,10 +21,9 @@ } while (clientHeight > maxHeight) { - console.log('BIGGER'); - const lastItem = tablist.lastElementChild; + const lastItem = tablist.lastChild as HTMLDivElement; - overflowItems.push('lastItem'); + overflowItems.push(lastItem as unknown as ComponentType); clientHeight -= lastItem!.clientHeight; } @@ -30,8 +32,6 @@ clientHeight = tablist.clientHeight; } }; - - $: console.log(clientHeight, maxHeight, overflowItems); @@ -49,8 +49,8 @@ {#if overflowItems.length}
MORE ITEMS - {#each overflowItems as item} - {item} + {#each overflowItems as Item} + {/each}
{/if} From e24b5d261e611ce319e31ebffa8181b181953eaf Mon Sep 17 00:00:00 2001 From: Jesse Winton Date: Wed, 19 Feb 2025 17:15:16 -0500 Subject: [PATCH 4/8] updates --- .../src/lib/lab/overflow-tabs/Button.svelte | 14 +- .../src/lib/lab/overflow-tabs/Link.svelte | 12 +- .../src/lib/lab/overflow-tabs/Root.svelte | 155 ++++++++++++++---- v2/pink-sb/src/lib/lab/overflow-tabs/types.ts | 6 + 4 files changed, 151 insertions(+), 36 deletions(-) diff --git a/v2/pink-sb/src/lib/lab/overflow-tabs/Button.svelte b/v2/pink-sb/src/lib/lab/overflow-tabs/Button.svelte index 74b8e5e52e..fb346f0b08 100644 --- a/v2/pink-sb/src/lib/lab/overflow-tabs/Button.svelte +++ b/v2/pink-sb/src/lib/lab/overflow-tabs/Button.svelte @@ -1,9 +1,10 @@ + {/if} diff --git a/v2/pink-sb/src/lib/lab/overflow-tabs/types.ts b/v2/pink-sb/src/lib/lab/overflow-tabs/types.ts index 5e331d4a4a..dde07fb0af 100644 --- a/v2/pink-sb/src/lib/lab/overflow-tabs/types.ts +++ b/v2/pink-sb/src/lib/lab/overflow-tabs/types.ts @@ -1 +1,7 @@ export type Variant = 'primary' | 'secondary'; + +export type RootContext = { + variant: Variant; + stretch: boolean; + updateTabWidth: (size: number, node: HTMLElement) => void; +}; From a452cc607e615c799237bf0a45269778898e8930 Mon Sep 17 00:00:00 2001 From: Jesse Winton Date: Thu, 20 Feb 2025 15:05:54 -0500 Subject: [PATCH 5/8] update --- .../src/lib/lab/overflow-tabs/Button.svelte | 6 +- .../src/lib/lab/overflow-tabs/Root.svelte | 76 ++++--------------- v2/pink-sb/src/lib/lab/overflow-tabs/types.ts | 2 +- 3 files changed, 21 insertions(+), 63 deletions(-) diff --git a/v2/pink-sb/src/lib/lab/overflow-tabs/Button.svelte b/v2/pink-sb/src/lib/lab/overflow-tabs/Button.svelte index fb346f0b08..4c8e72f4d8 100644 --- a/v2/pink-sb/src/lib/lab/overflow-tabs/Button.svelte +++ b/v2/pink-sb/src/lib/lab/overflow-tabs/Button.svelte @@ -17,7 +17,9 @@ let buttonNode: HTMLButtonElement; onMount(() => { - root.updateTabWidth(width, buttonNode); + if (buttonNode) { + root.updateTabWidth(width, buttonNode, buttonNode.innerText); + } }); @@ -29,6 +31,7 @@ class:tab-primary={root.variant === 'primary'} class:tab-secondary={root.variant === 'secondary'} class:tab-stretch={root.stretch} + class:active on:click on:dblclick on:mousedown @@ -36,7 +39,6 @@ on:keydown {...$$restProps} {disabled} - class:active > diff --git a/v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte b/v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte index d3ef74e747..6556e9b7d0 100644 --- a/v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte +++ b/v2/pink-sb/src/lib/lab/overflow-tabs/Root.svelte @@ -1,4 +1,5 @@ @@ -69,7 +72,9 @@ [attr.name, attr.value]) + Array.from(tab.attributes).map((attr) => { + return [attr.name, attr.value]; + }) )} > {tab.textContent} @@ -80,14 +85,14 @@ {#if dropdownTabs.length} - + { + return { + value: tab.textContent, + label: String(tab.textContent) + }; + })} + /> {/if} diff --git a/v2/pink-sb/src/lib/lab/overflow-tabs/types.ts b/v2/pink-sb/src/lib/lab/overflow-tabs/types.ts index dde07fb0af..3b9c0b94a8 100644 --- a/v2/pink-sb/src/lib/lab/overflow-tabs/types.ts +++ b/v2/pink-sb/src/lib/lab/overflow-tabs/types.ts @@ -3,5 +3,5 @@ export type Variant = 'primary' | 'secondary'; export type RootContext = { variant: Variant; stretch: boolean; - updateTabWidth: (size: number, node: HTMLElement) => void; + updateTabWidth: (size: number, node: HTMLElement, text: string) => void; }; From ac9297b71d4de2844fe41ff39b6ae7df4e74ab94 Mon Sep 17 00:00:00 2001 From: Jesse Winton Date: Tue, 25 Feb 2025 12:26:47 -0500 Subject: [PATCH 6/8] mostly working --- .../src/lib/lab/overflow-tabs/Button.svelte | 9 +- .../src/lib/lab/overflow-tabs/Link.svelte | 15 +- .../src/lib/lab/overflow-tabs/Root.svelte | 135 +++++++++++------- v2/pink-sb/src/lib/lab/overflow-tabs/types.ts | 5 +- 4 files changed, 101 insertions(+), 63 deletions(-) diff --git a/v2/pink-sb/src/lib/lab/overflow-tabs/Button.svelte b/v2/pink-sb/src/lib/lab/overflow-tabs/Button.svelte index 4c8e72f4d8..ed0056831c 100644 --- a/v2/pink-sb/src/lib/lab/overflow-tabs/Button.svelte +++ b/v2/pink-sb/src/lib/lab/overflow-tabs/Button.svelte @@ -13,19 +13,17 @@ export let active: $$Props['active'] = false; export let disabled: $$Props['disabled'] = false; - let width = 0; let buttonNode: HTMLButtonElement; onMount(() => { if (buttonNode) { - root.updateTabWidth(width, buttonNode, buttonNode.innerText); + root.updateTabWidths(buttonNode.getBoundingClientRect().width); + return root.registerTabNode(buttonNode); } }); diff --git a/v2/pink-sb/src/lib/lab/overflow-tabs/Link.svelte b/v2/pink-sb/src/lib/lab/overflow-tabs/Link.svelte index 8ddb615233..41e22067fc 100644 --- a/v2/pink-sb/src/lib/lab/overflow-tabs/Link.svelte +++ b/v2/pink-sb/src/lib/lab/overflow-tabs/Link.svelte @@ -1,9 +1,9 @@ import { Input } from '$lib/index.ts'; import type { RootContext } from './types.js'; - import { onMount, SvelteComponent } from 'svelte'; + import { onMount, tick } from 'svelte'; + import { writable } from 'svelte/store'; export let variant: RootContext['variant'] = 'primary'; export let stretch: RootContext['stretch'] = false; + export let showOverflowIndicator = true; - let navWidth: number; + let tabsList: HTMLElement; let tabWidths: number[] = []; + + const tabNodesStore = writable([]); let tabNodes: HTMLElement[] = []; - let tabs: string[] = []; + tabNodesStore.subscribe((value) => { + tabNodes = value; + }); + + const overflowedItems = writable<{ text: string; disabled: boolean; active: boolean }[]>([]); + let visibleBreakIndex = tabNodes.length; + let hasOverflow = false; - const updateTabWidth = (width: number, node: HTMLElement, text: string) => { - tabWidths = [...tabWidths, width]; - tabNodes = [...tabNodes, node]; + const registerTabNode = (node: HTMLElement) => { + tabNodesStore.update((nodes) => [...nodes, node]); + return { + destroy: () => { + tabNodesStore.update((nodes) => nodes.filter((n) => n !== node)); + } + }; }; - $: tabWidthTotal = tabWidths.reduce((acc, cur) => acc + cur, 0); + const updateTabWidths = (tabWidth: number) => { + tabWidths = [...tabWidths, tabWidth]; + }; - let displayedTabs: HTMLElement[] = []; - let dropdownTabs: HTMLElement[] = []; + const calculateOverflow = async () => { + if (!tabsList || tabNodes.length === 0) return; - const handleResize = () => { - if (!navWidth) return; + await tick(); // Wait for DOM updates - const DROPDOWN_WIDTH = 80; - const availableWidth = navWidth - (tabWidthTotal > navWidth ? DROPDOWN_WIDTH : 0); + const navWidth = tabsList.getBoundingClientRect().width; + const DROPDOWN_WIDTH = showOverflowIndicator ? 120 : 0; + const availableWidth = navWidth - DROPDOWN_WIDTH; let runningWidth = 0; - let breakIndex = tabNodes.length; + visibleBreakIndex = tabNodes.length; for (let i = 0; i < tabWidths.length; i++) { runningWidth += tabWidths[i]; if (runningWidth > availableWidth) { - breakIndex = i; + visibleBreakIndex = i; break; } } - if (runningWidth > availableWidth) { - displayedTabs = tabNodes.slice(0, breakIndex); - dropdownTabs = tabNodes.slice(breakIndex); + hasOverflow = runningWidth > availableWidth; + + if (hasOverflow) { + const overflowed = tabNodes.slice(visibleBreakIndex).map((node) => { + return { + text: node.innerText, + disabled: node.hasAttribute('disabled'), + active: node.classList.contains('active') + }; + }); + + overflowedItems.set(overflowed); + + tabNodes.forEach((node, index) => { + if (index >= visibleBreakIndex) { + node.style.display = 'none'; + } else { + node.style.display = ''; + } + }); } else { - displayedTabs = tabNodes; - dropdownTabs = []; + tabNodes.forEach((node) => { + node.style.display = ''; + }); + overflowedItems.set([]); } }; - $: if (navWidth) { - handleResize(); - } + const handleResize = () => { + calculateOverflow(); + }; onMount(() => { - handleResize(); + calculateOverflow(); }); - $: console.log({ displayedTabs }, { dropdownTabs }, { tabs }); + $: if (tabWidths.length > 0 && tabNodes.length > 0) { + calculateOverflow(); + }
- {#each displayedTabs as tab} - { - return [attr.name, attr.value]; - }) - )} - > - {tab.textContent} - - {/each} - - -
- -{#if dropdownTabs.length} - { - return { - value: tab.textContent, - label: String(tab.textContent) - }; - })} + -{/if} + + {#if hasOverflow && showOverflowIndicator} + { + return { + label: item.text, + value: item.text.toLocaleLowerCase() + }; + })} + /> + {/if} +