kopia lustrzana https://github.com/shoelace-style/shoelace
Add ability to automatically hide tab-group scroll control (#2128)
* Add ability to automatically hide tab-group scroll control when there are no longer any tabs to show * code review updates * update and document how scroll buttons are hidden * AUTO-HIDE: Simplify * AUTO-HIDE: extract to constant * update changelog * include pr number in changelog update * add line * apply suggested changes * Prevent tab-group scroll buttons from being focusable * prettier fix * Set default for 'auto-hide-scroll-buttons' to true * Make auto hiding scroll buttons the default behavior * Update changelog * update changelog --------- Co-authored-by: Shmuel Leider <shmuel.leider@chabad.org> Co-authored-by: Yehuda Ringler <yehuda.ringler@chabad.org> Co-authored-by: Cory LaViska <cory@abeautifulsite.net>konnorrogers/fix-morphing
rodzic
6ce6e22466
commit
65126e875d
|
@ -411,6 +411,148 @@ const App = () => (
|
|||
);
|
||||
```
|
||||
|
||||
### Fixed scroll controls
|
||||
|
||||
When tabs are scrolled all the way to one side, the scroll button on that side can't be clicked. Set the `fixed-scroll-controls` attribute to keep the effected button visible in that case.
|
||||
|
||||
```html:preview
|
||||
<sl-tab-group fixed-scroll-controls>
|
||||
<sl-tab slot="nav" panel="tab-1">Tab 1</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-2">Tab 2</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-3">Tab 3</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-4">Tab 4</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-5">Tab 5</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-6">Tab 6</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-7">Tab 7</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-8">Tab 8</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-9">Tab 9</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-10">Tab 10</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-11">Tab 11</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-12">Tab 12</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-13">Tab 13</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-14">Tab 14</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-15">Tab 15</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-16">Tab 16</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-17">Tab 17</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-18">Tab 18</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-19">Tab 19</sl-tab>
|
||||
<sl-tab slot="nav" panel="tab-20">Tab 20</sl-tab>
|
||||
|
||||
<sl-tab-panel name="tab-1">Tab panel 1</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-2">Tab panel 2</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-3">Tab panel 3</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-4">Tab panel 4</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-5">Tab panel 5</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-6">Tab panel 6</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-7">Tab panel 7</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-8">Tab panel 8</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-9">Tab panel 9</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-10">Tab panel 10</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-11">Tab panel 11</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-12">Tab panel 12</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-13">Tab panel 13</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-14">Tab panel 14</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-15">Tab panel 15</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-16">Tab panel 16</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-17">Tab panel 17</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-18">Tab panel 18</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-19">Tab panel 19</sl-tab-panel>
|
||||
<sl-tab-panel name="tab-20">Tab panel 20</sl-tab-panel>
|
||||
</sl-tab-group>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import SlTab from '@shoelace-style/shoelace/dist/react/tab';
|
||||
import SlTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
|
||||
import SlTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
|
||||
|
||||
const App = () => (
|
||||
<SlTabGroup auto-hide-scroll-buttons>
|
||||
<SlTab slot="nav" panel="tab-1">
|
||||
Tab 1
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-2">
|
||||
Tab 2
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-3">
|
||||
Tab 3
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-4">
|
||||
Tab 4
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-5">
|
||||
Tab 5
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-6">
|
||||
Tab 6
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-7">
|
||||
Tab 7
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-8">
|
||||
Tab 8
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-9">
|
||||
Tab 9
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-10">
|
||||
Tab 10
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-11">
|
||||
Tab 11
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-12">
|
||||
Tab 12
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-13">
|
||||
Tab 13
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-14">
|
||||
Tab 14
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-15">
|
||||
Tab 15
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-16">
|
||||
Tab 16
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-17">
|
||||
Tab 17
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-18">
|
||||
Tab 18
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-19">
|
||||
Tab 19
|
||||
</SlTab>
|
||||
<SlTab slot="nav" panel="tab-20">
|
||||
Tab 20
|
||||
</SlTab>
|
||||
|
||||
<SlTabPanel name="tab-1">Tab panel 1</SlTabPanel>
|
||||
<SlTabPanel name="tab-2">Tab panel 2</SlTabPanel>
|
||||
<SlTabPanel name="tab-3">Tab panel 3</SlTabPanel>
|
||||
<SlTabPanel name="tab-4">Tab panel 4</SlTabPanel>
|
||||
<SlTabPanel name="tab-5">Tab panel 5</SlTabPanel>
|
||||
<SlTabPanel name="tab-6">Tab panel 6</SlTabPanel>
|
||||
<SlTabPanel name="tab-7">Tab panel 7</SlTabPanel>
|
||||
<SlTabPanel name="tab-8">Tab panel 8</SlTabPanel>
|
||||
<SlTabPanel name="tab-9">Tab panel 9</SlTabPanel>
|
||||
<SlTabPanel name="tab-10">Tab panel 10</SlTabPanel>
|
||||
<SlTabPanel name="tab-11">Tab panel 11</SlTabPanel>
|
||||
<SlTabPanel name="tab-12">Tab panel 12</SlTabPanel>
|
||||
<SlTabPanel name="tab-13">Tab panel 13</SlTabPanel>
|
||||
<SlTabPanel name="tab-14">Tab panel 14</SlTabPanel>
|
||||
<SlTabPanel name="tab-15">Tab panel 15</SlTabPanel>
|
||||
<SlTabPanel name="tab-16">Tab panel 16</SlTabPanel>
|
||||
<SlTabPanel name="tab-17">Tab panel 17</SlTabPanel>
|
||||
<SlTabPanel name="tab-18">Tab panel 18</SlTabPanel>
|
||||
<SlTabPanel name="tab-19">Tab panel 19</SlTabPanel>
|
||||
<SlTabPanel name="tab-20">Tab panel 20</SlTabPanel>
|
||||
</SlTabGroup>
|
||||
);
|
||||
```
|
||||
|
||||
### Manual Activation
|
||||
|
||||
When focused, keyboard users can press [[Left]] or [[Right]] to select the desired tab. By default, the corresponding tab panel will be shown immediately (automatic activation). You can change this behavior by setting `activation="manual"` which will require the user to press [[Space]] or [[Enter]] before showing the tab panel (manual activation).
|
||||
|
|
|
@ -14,6 +14,7 @@ New versions of Shoelace are released as-needed and generally occur when a criti
|
|||
|
||||
## Next
|
||||
|
||||
- Scroll buttons for `<sl-tab-group>` auto hide when they are not clickable. The `fixed-scroll-controls` attribute can be included to prevent this behavior. [#2128]
|
||||
- Added support for using `<sl-dropdown>` in `<sl-breadcrumb-item>` default slot [#2015]
|
||||
- Added the `countdown` attribute to `<sl-alert>` to show a visual indicator before the toast disappears [#1899]
|
||||
- Fixed a bug that caused errors to show in the console when components disconnect before before `firstUpdated()` executes [#2127]
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import '../../internal/scrollend-polyfill.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { eventOptions, property, query, state } from 'lit/decorators.js';
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { scrollIntoView } from '../../internal/scroll.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
@ -61,6 +62,9 @@ export default class SlTabGroup extends ShoelaceElement {
|
|||
|
||||
@state() private hasScrollControls = false;
|
||||
|
||||
@state() private shouldHideScrollStartButton = false;
|
||||
@state() private shouldHideScrollEndButton = false;
|
||||
|
||||
/** The placement of the tabs. */
|
||||
@property() placement: 'top' | 'bottom' | 'start' | 'end' = 'top';
|
||||
|
||||
|
@ -73,6 +77,9 @@ export default class SlTabGroup extends ShoelaceElement {
|
|||
/** Disables the scroll arrows that appear when tabs overflow. */
|
||||
@property({ attribute: 'no-scroll-controls', type: Boolean }) noScrollControls = false;
|
||||
|
||||
/** Prevent scroll buttons from being hidden when inactive. */
|
||||
@property({ attribute: 'fixed-scroll-controls', type: Boolean }) fixedScrollControls = false;
|
||||
|
||||
connectedCallback() {
|
||||
const whenAllDefined = Promise.all([
|
||||
customElements.whenDefined('sl-tab'),
|
||||
|
@ -366,6 +373,28 @@ export default class SlTabGroup extends ShoelaceElement {
|
|||
return nextTab;
|
||||
}
|
||||
|
||||
/**
|
||||
* The reality of the browser means that we can't expect the scroll position to be exactly what we want it to be, so
|
||||
* we add one pixel of wiggle room to our calculations.
|
||||
*/
|
||||
private scrollOffset = 1;
|
||||
|
||||
@eventOptions({ passive: true })
|
||||
private updateScrollButtons() {
|
||||
if (this.hasScrollControls && !this.fixedScrollControls) {
|
||||
this.shouldHideScrollStartButton = this.scrollFromStart() <= this.scrollOffset;
|
||||
this.shouldHideScrollEndButton = this.isScrolledToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private isScrolledToEnd() {
|
||||
return this.scrollFromStart() + this.nav.clientWidth >= this.nav.scrollWidth - this.scrollOffset;
|
||||
}
|
||||
|
||||
private scrollFromStart() {
|
||||
return this.localize.dir() === 'rtl' ? -this.nav.scrollLeft : this.nav.scrollLeft;
|
||||
}
|
||||
|
||||
@watch('noScrollControls', { waitUntilFirstUpdate: true })
|
||||
updateScrollControls() {
|
||||
if (this.noScrollControls) {
|
||||
|
@ -379,6 +408,8 @@ export default class SlTabGroup extends ShoelaceElement {
|
|||
this.hasScrollControls =
|
||||
['top', 'bottom'].includes(this.placement) && this.nav.scrollWidth > this.nav.clientWidth + 1;
|
||||
}
|
||||
|
||||
this.updateScrollButtons();
|
||||
}
|
||||
|
||||
@watch('placement', { waitUntilFirstUpdate: true })
|
||||
|
@ -426,16 +457,22 @@ export default class SlTabGroup extends ShoelaceElement {
|
|||
<sl-icon-button
|
||||
part="scroll-button scroll-button--start"
|
||||
exportparts="base:scroll-button__base"
|
||||
class="tab-group__scroll-button tab-group__scroll-button--start"
|
||||
class=${classMap({
|
||||
'tab-group__scroll-button': true,
|
||||
'tab-group__scroll-button--start': true,
|
||||
'tab-group__scroll-button--start--hidden': this.shouldHideScrollStartButton
|
||||
})}
|
||||
name=${isRtl ? 'chevron-right' : 'chevron-left'}
|
||||
library="system"
|
||||
tabindex="-1"
|
||||
aria-hidden="true"
|
||||
label=${this.localize.term('scrollToStart')}
|
||||
@click=${this.handleScrollToStart}
|
||||
></sl-icon-button>
|
||||
`
|
||||
: ''}
|
||||
|
||||
<div class="tab-group__nav">
|
||||
<div class="tab-group__nav" @scrollend=${this.updateScrollButtons}>
|
||||
<div part="tabs" class="tab-group__tabs" role="tablist">
|
||||
<div part="active-tab-indicator" class="tab-group__indicator"></div>
|
||||
<sl-resize-observer @sl-resize=${this.syncIndicator}>
|
||||
|
@ -449,9 +486,15 @@ export default class SlTabGroup extends ShoelaceElement {
|
|||
<sl-icon-button
|
||||
part="scroll-button scroll-button--end"
|
||||
exportparts="base:scroll-button__base"
|
||||
class="tab-group__scroll-button tab-group__scroll-button--end"
|
||||
class=${classMap({
|
||||
'tab-group__scroll-button': true,
|
||||
'tab-group__scroll-button--end': true,
|
||||
'tab-group__scroll-button--end--hidden': this.shouldHideScrollEndButton
|
||||
})}
|
||||
name=${isRtl ? 'chevron-left' : 'chevron-right'}
|
||||
library="system"
|
||||
tabindex="-1"
|
||||
aria-hidden="true"
|
||||
label=${this.localize.term('scrollToEnd')}
|
||||
@click=${this.handleScrollToEnd}
|
||||
></sl-icon-button>
|
||||
|
|
|
@ -31,6 +31,11 @@ export default css`
|
|||
padding: 0 var(--sl-spacing-x-large);
|
||||
}
|
||||
|
||||
.tab-group--has-scroll-controls .tab-group__scroll-button--start--hidden,
|
||||
.tab-group--has-scroll-controls .tab-group__scroll-button--end--hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.tab-group__body {
|
||||
display: block;
|
||||
overflow: auto;
|
||||
|
|
Ładowanie…
Reference in New Issue