Skip to content

Commit fa50164

Browse files
merge collect 3.0 changes (#2679)
* feat(tabs): implement tabs controller feat(tabs): implement tabs controller * fix(tabs): remove BaseTab from test * fix(tabs): correct disabled test to check child button * fix(tabs): dispatch event when active property changes on tab * fix(tabs): dispatch event only when changing from false to true * fix(tabs): add dynamic tab demo * fix(tabs): update RTI if tab is disabled * refactor(tabs): mutation observer * fix(tabs): demo js link * fix(tabs): add TabsController to exports remove Base classes * fix(tabs): update active tab onTabExpand * fix(core): update overflow when childList updates * fix(tabs): reduce mutation observer scope * fix(tabs): add missing activeIndex getter * fix(tabs): fix bug with tab button before styles on box variant * chore(tabs): remove whitespace * fix(core): undo uneeded change to RTI controller * chore(tabs): add changeset * chore(core): add changeset * fix(tabs): remove base styles files * docs(tabs): add additional documentation * chore(tabs): update changeset * docs(tabs): add set activeIndex jsDoc * fix(tabs): refactor mutations callback * fix(tabs): add override keyword * fix(tabs): add override keyword to panel * test(tabs): improve disabled tab testing * refactor(tabs): check event instanceof type in static method * docs(tabs): add active state to nested demo * refactor(tabs): making validation methods required * fix(tabs): additional public api * test(tabs): refactor tab tests using nested describes * feat(core): add tabs-controller * fix(tabs): relink tabs controller from core * fix(tabs): remove TabsController * chore(tabs): update changeset * chore(core): update changeset * fix(tabs): remove TabsController from element package exports * fix(core): add tabs-controller to package exports * chore(tabs): update changeset * fix(tabs): revert removal of base classes * fix(tabs): re-add isExpandEvent * chore(tabs): add changeset * docs: update changesets * feat(core): loosen logger host type * docs: changeset * docs(tabs): split up demos * fix(tabs): loosen type of host for tabscontroller * refactor(tabs): docs and ordering * chore(tabs): remove commented out code * test(tabs): remove unused aTimeout * docs(tabs): fix inset demo * fix(tabs): linting no aria- on slots * docs(tabs): unify demo styles, fixing overflow --------- Co-authored-by: Benny Powers <web@bennypowers.com>
1 parent 52076cb commit fa50164

29 files changed

+1233
-237
lines changed

.changeset/hip-bags-live.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@patternfly/elements": minor
3+
---
4+
5+
`<pf-tabs>`: improved overflow handling, added dynamic tab creation support

.changeset/logger-host-loose.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
"@patternfly/pfe-core": minor
3+
---
4+
`Logger`: loosen the type of allowed controller hosts

.changeset/overflow-controller.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@patternfly/core": minor
3+
---
4+
5+
`OverflowController`: recalculate overflow when the window size changes and when tabs are dynamically created.

.changeset/tabs-controller.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
"@patternfly/core": minor
3+
---
4+
5+
`TabsController`: Added TabsController. This controller is used to manage the state of the tabs and panels.
6+
7+
```ts
8+
#tabs = new TabsController(this, {
9+
isTab: (x: Node): x is PfTab => x instanceof PfTab,
10+
isPanel: (x: Node): x is PfTabPanel => x instanceof PfTabPanel,
11+
});
12+
```
13+
14+
Please review the [Tabs 2.4 to 3.0 migration guide](https://patternflyelements.org/migration/3.0/tabs) for more
15+
information.

core/pfe-core/controllers/overflow-controller.ts

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,44 @@
1-
import type { ReactiveController, ReactiveControllerHost } from 'lit';
1+
import type { ReactiveController, ReactiveElement } from 'lit';
22

33
import { isElementInView } from '@patternfly/pfe-core/functions/isElementInView.js';
44

55
export interface Options {
6+
/**
7+
* Force hide the scroll buttons regardless of overflow
8+
*/
69
hideOverflowButtons?: boolean;
10+
/**
11+
* Delay in ms to wait before checking for overflow
12+
*/
13+
scrollTimeoutDelay?: number;
714
}
815

916
export class OverflowController implements ReactiveController {
17+
static #instances = new Set<OverflowController>();
18+
19+
static {
20+
// on resize check for overflows to add or remove scroll buttons
21+
window.addEventListener('resize', () => {
22+
for (const instance of this.#instances) {
23+
instance.onScroll();
24+
}
25+
}, { capture: false });
26+
}
27+
28+
#host: ReactiveElement;
1029
/** Overflow container */
1130
#container?: HTMLElement;
1231
/** Children that can overflow */
1332
#items: HTMLElement[] = [];
1433

15-
#scrollTimeoutDelay = 0;
34+
#scrollTimeoutDelay: number;
1635
#scrollTimeout?: ReturnType<typeof setTimeout>;
1736

1837
/** Default state */
19-
#hideOverflowButtons = false;
38+
#hideOverflowButtons: boolean;
39+
40+
#mo = new MutationObserver(this.#mutationsCallback.bind(this));
41+
2042
showScrollButtons = false;
2143
overflowLeft = false;
2244
overflowRight = false;
@@ -29,17 +51,32 @@ export class OverflowController implements ReactiveController {
2951
return this.#items.at(-1);
3052
}
3153

32-
constructor(public host: ReactiveControllerHost & Element, private options?: Options) {
33-
this.host.addController(this);
34-
if (options?.hideOverflowButtons) {
35-
this.#hideOverflowButtons = options?.hideOverflowButtons;
54+
constructor(public host: ReactiveElement, private options?: Options) {
55+
this.#hideOverflowButtons = options?.hideOverflowButtons ?? false;
56+
this.#scrollTimeoutDelay = options?.scrollTimeoutDelay ?? 0;
57+
if (host.isConnected) {
58+
OverflowController.#instances.add(this);
59+
}
60+
(this.#host = host).addController(this);
61+
this.#mo.observe(host, { attributes: false, childList: true, subtree: true });
62+
}
63+
64+
async #mutationsCallback(mutations: MutationRecord[]): Promise<void> {
65+
for (const mutation of mutations) {
66+
if (mutation.type === 'childList') {
67+
this.#setOverflowState();
68+
this.#host.requestUpdate();
69+
}
3670
}
3771
}
3872

3973
#setOverflowState(): void {
4074
if (!this.firstItem || !this.lastItem || !this.#container) {
4175
return;
4276
}
77+
const prevLeft = this.overflowLeft;
78+
const prevRight = this.overflowRight;
79+
4380
this.overflowLeft = !this.#hideOverflowButtons && !isElementInView(this.#container, this.firstItem);
4481
this.overflowRight = !this.#hideOverflowButtons && !isElementInView(this.#container, this.lastItem);
4582
let scrollButtonsWidth = 0;
@@ -48,7 +85,11 @@ export class OverflowController implements ReactiveController {
4885
}
4986
this.showScrollButtons = !this.#hideOverflowButtons &&
5087
this.#container.scrollWidth > (this.#container.clientWidth + scrollButtonsWidth);
51-
this.host.requestUpdate();
88+
89+
// only request update if there has been a change
90+
if ((prevLeft !== this.overflowLeft) || (prevRight !== this.overflowRight)) {
91+
this.host.requestUpdate();
92+
}
5293
}
5394

5495
init(container: HTMLElement, items: HTMLElement[]) {

0 commit comments

Comments
 (0)