mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 04:09:12 +00:00
Scroller component (#907)
* add term * add scroller component * update color in docs * prettier * add vertical example * use CSS * add word * rework shadows and add opt outs * add examples * update docs * Add missing closing tag in 'Adding Content' example * fix jsdoc * fix safari pixels * add changelog entry --------- Co-authored-by: lindsaym-fa <dev@lindsaym.design>
This commit is contained in:
@@ -148,6 +148,7 @@
|
||||
"scrollbars",
|
||||
"scrollend",
|
||||
"scroller",
|
||||
"Scrollers",
|
||||
"Segoe",
|
||||
"semibold",
|
||||
"shadowrootmode",
|
||||
|
||||
31
docs/_includes/svgs/scroller.njk
Normal file
31
docs/_includes/svgs/scroller.njk
Normal file
@@ -0,0 +1,31 @@
|
||||
<svg width="96" height="64" viewBox="0 0 96 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 1H84C90.0751 1 95 5.92487 95 12V52C95 58.0751 90.0751 63 84 63H12C5.92487 63 1 58.0751 1 52V12C1 5.92487 5.92487 1 12 1Z" fill="var(--wa-color-surface-default)"/>
|
||||
<path d="M12 1H84C90.0751 1 95 5.92487 95 12V52C95 58.0751 90.0751 63 84 63H12C5.92487 63 1 58.0751 1 52V12C1 5.92487 5.92487 1 12 1Z" stroke="var(--wa-color-surface-border)" stroke-width="2"/>
|
||||
<rect x="12" y="12" width="24" height="4" rx="2" fill="var(--wa-color-neutral-fill-quiet)"/>
|
||||
<rect x="12" y="21" width="24" height="4" rx="2" fill="var(--wa-color-neutral-fill-quiet)"/>
|
||||
<rect x="12" y="30" width="24" height="4" rx="2" fill="var(--wa-color-neutral-fill-quiet)"/>
|
||||
<rect x="44" y="12" width="24" height="4" rx="2" fill="var(--wa-color-neutral-fill-quiet)"/>
|
||||
<rect x="44" y="21" width="24" height="4" rx="2" fill="var(--wa-color-neutral-fill-quiet)"/>
|
||||
<rect x="44" y="30" width="24" height="4" rx="2" fill="var(--wa-color-neutral-fill-quiet)"/>
|
||||
<rect x="76" y="12" width="20" height="4" rx="2" fill="url(#paint0_linear_302_2)"/>
|
||||
<rect x="76" y="21" width="20" height="4" rx="2" fill="url(#paint1_linear_302_2)"/>
|
||||
<rect x="76" y="30" width="20" height="4" rx="2" fill="url(#paint2_linear_302_2)"/>
|
||||
<rect x="4" y="44" width="88" height="16" rx="8" fill="var(--wa-color-neutral-fill-normal)"/>
|
||||
<path d="M85.8438 51.6562C86.0469 51.8438 86.0469 52.1719 85.8438 52.3594L82.8438 55.3594C82.6562 55.5625 82.3281 55.5625 82.1406 55.3594C81.9375 55.1719 81.9375 54.8438 82.1406 54.6562L84.7812 52L82.1406 49.3594C81.9375 49.1719 81.9375 48.8438 82.1406 48.6562C82.3281 48.4531 82.6562 48.4531 82.8438 48.6562L85.8438 51.6562Z" fill="var(--wa-color-neutral-border-loud)"/>
|
||||
<path d="M10.1406 51.6562L13.1406 48.6562C13.3281 48.4531 13.6562 48.4531 13.8438 48.6562C14.0469 48.8438 14.0469 49.1719 13.8438 49.3594L11.2031 52L13.8438 54.6562C14.0469 54.8438 14.0469 55.1719 13.8438 55.3594C13.6562 55.5625 13.3281 55.5625 13.1406 55.3594L10.1406 52.3594C9.9375 52.1719 9.9375 51.8438 10.1406 51.6562Z" fill="var(--wa-color-neutral-border-loud)"/>
|
||||
<path d="M20 52C20 49.7909 21.7909 48 24 48H48C50.2091 48 52 49.7909 52 52V52C52 54.2091 50.2091 56 48 56H24C21.7909 56 20 54.2091 20 52V52Z" fill="var(--wa-color-neutral-border-loud)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_302_2" x1="76" y1="14" x2="96" y2="14" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.533654" stop-color="var(--wa-color-neutral-fill-quiet)"/>
|
||||
<stop offset="1" stop-color="var(--wa-color-neutral-fill-quiet)" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_302_2" x1="76" y1="23" x2="96" y2="23" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.533654" stop-color="var(--wa-color-neutral-fill-quiet)"/>
|
||||
<stop offset="1" stop-color="var(--wa-color-neutral-fill-quiet)" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_302_2" x1="76" y1="32" x2="96" y2="32" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.533654" stop-color="var(--wa-color-neutral-fill-quiet)"/>
|
||||
<stop offset="1" stop-color="var(--wa-color-neutral-fill-quiet)" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
151
docs/docs/components/scroller.md
Normal file
151
docs/docs/components/scroller.md
Normal file
@@ -0,0 +1,151 @@
|
||||
---
|
||||
title: Scroller
|
||||
description: Scrollers create an accessible container while providing visual cues that help users identify and navigate through content that scrolls.
|
||||
layout: component
|
||||
tags: [organization]
|
||||
icon: scroller
|
||||
---
|
||||
|
||||
```html {.example}
|
||||
<wa-scroller id="scroller__overview">
|
||||
<table>
|
||||
<tr>
|
||||
<th>Party Role</th>
|
||||
<th>Combat Style</th>
|
||||
<th>Group Size</th>
|
||||
<th>Campaign Setting</th>
|
||||
<th>Signature Traits</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Warrior</td>
|
||||
<td>Melee Tank</td>
|
||||
<td>1-2</td>
|
||||
<td>Forgotten Realms</td>
|
||||
<td>Plate-armored swordmaster who taunts foes.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Rogue</td>
|
||||
<td>Stealth Striker</td>
|
||||
<td>1</td>
|
||||
<td>Eberron</td>
|
||||
<td>Shadowy lockpick with daggers and a secret gold stash.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Wizard</td>
|
||||
<td>Spell Slinger</td>
|
||||
<td>1</td>
|
||||
<td>Greyhawk</td>
|
||||
<td>Robe-clad mage hurling fireballs from a messy spellbook.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Cleric</td>
|
||||
<td>Divine Support</td>
|
||||
<td>1</td>
|
||||
<td>Ravnica</td>
|
||||
<td>Holy healer with a glowing amulet and sneaky ale habit.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bard</td>
|
||||
<td>Charisma King</td>
|
||||
<td>1</td>
|
||||
<td>Dragonlance</td>
|
||||
<td>Lute-playing charmer with magical songs and bad puns.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</wa-scroller>
|
||||
|
||||
<style>
|
||||
#scroller__overview {
|
||||
table {
|
||||
margin-block: 0;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
th:nth-child(5),
|
||||
td:nth-child(5) {
|
||||
min-width: 50ch;
|
||||
white-space: wrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Adding Content
|
||||
|
||||
The scroller component automatically provides a scrollable container for any content that exceeds the available space. Simply add your content as children of the `<wa-scroller>` element, and it will handle the rest.
|
||||
|
||||
```html {.example}
|
||||
<wa-scroller>
|
||||
<div style="width: 1200px; padding: 1rem;">
|
||||
<h3>Superhero Team Roles Guide</h3>
|
||||
<div class="wa-grid" style="--wa-grid-columns: 4; --wa-grid-gap: var(--wa-spacing-l);">
|
||||
<div>
|
||||
<h4>Team Leaders</h4>
|
||||
<p>Charismatic captains like Captain America or Cyclops are the heart of any superteam, rallying everyone with epic speeches and killer strategies. They’re the ones calling the shots in a cosmic showdown, keeping the squad focused when Thanos or Magneto crashes the party.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Heavy Hitters</h4>
|
||||
<p>Powerhouses like Thor or Hulk bring the boom, smashing through villain lairs or alien armadas. Their job is to land the big punches, but they gotta pace themselves to avoid stealing the spotlight from sneakier teammates.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Tech Geniuses</h4>
|
||||
<p>Brainiacs like Iron Man or Mr. Fantastic keep the team one step ahead with gadgets and gizmos. They’re crafting quinjets or hacking evil AI, always ready with a snarky quip while saving the day from a computer terminal.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Stealth Specialists</h4>
|
||||
<p>Ninja-like heroes like Black Widow or Nightcrawler slip through the shadows, gathering intel or pulling off surprise attacks. They’re the glue that makes risky plans work, coordinating with the team to flip a losing fight into a win.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</wa-scroller>
|
||||
```
|
||||
|
||||
### Orientation
|
||||
|
||||
Set the `orientation` attribute to `vertical` and provide a height to create a vertical scroller.
|
||||
|
||||
```html {.example}
|
||||
<wa-scroller orientation="vertical" style="max-height: 300px;">
|
||||
<p>Superhero movies are the ultimate popcorn-fueled thrill rides, turning comic book pages into cinematic rollercoasters. Back in the early 2000s, films like X-Men and Spider-Man kicked open the door, proving tights and teamwork could pack theaters. Those early flicks leaned on practical effects and heart—like Tobey Maguire’s earnest web-slinger saving a train—making us believe a guy in spandex could be a hero. They weren’t perfect, but they set the stage for the genre to become a cultural juggernaut.</p>
|
||||
<p>By the 2010s, the Marvel Cinematic Universe turned superhero films into a shared saga, like a comic crossover event on steroids. The Avengers in 2012 was a game-changer, tossing Iron Man’s snark, Thor’s hammer, and Cap’s shield into one epic brawl. Directors learned to balance humor, heart, and explosions, while studios figured out how to make every movie feel like a chapter in a bigger story. Even standalone hits like Wonder Woman brought fresh vibes, with Gal Gadot’s lasso-wielding warrior stealing hearts and smashing box office records.</p>
|
||||
<p>Today, superhero flicks are a global obsession, from Deadpool’s chimichanga-fueled chaos to Black Panther’s Wakandan pride. They’re not just about powers—they’re about characters we root for, like Rocket Raccoon’s scrappy loyalty or Harley Quinn’s wild energy. Fans dissect trailers like detectives, theorizing about multiverses and cameos, while memes of sad Affleck or dancing Groot flood the internet. Whether it’s a gritty Joker origin or a cosmic Guardians adventure, these movies keep us glued to our seats, dreaming of heroism and one-liners that’d make even Tony Stark jealous.</p>
|
||||
</wa-scroller>
|
||||
```
|
||||
|
||||
### Without a Shadow
|
||||
|
||||
Use the `without-shadow` attribute to remove the fading shadow effect at the edges of the scroller, which typically indicates more content is available.
|
||||
|
||||
```html {.example}
|
||||
<wa-scroller without-shadow>
|
||||
<div style="width: 1500px;">
|
||||
<p>Gaming consoles are like time machines for nerds, zapping us from pixelated 2D adventures to jaw-dropping cinematic worlds. Back in the ‘90s, the Super Nintendo was the cool kid on the block, using a 16-bit chip to pull off tricks like Mode 7, which made Mario Kart’s tracks feel like they were zooming right at you. It was like wizardry for a kid with a chunky controller, turning flat sprites into pseudo-3D races that had us yelling at our TVs when we got hit by a red shell.</p>
|
||||
<p>Fast-forward to today, and consoles like the PlayStation 5 and Xbox Series X are basically supercomputers in sleek boxes. They’re packing enough power to make games look like Hollywood blockbusters, with lighting so real you can practically feel the sun glare in Spider-Man: Miles Morales. These machines can handle massive open worlds, like the sprawling lands of Elden Ring, without breaking a sweat, letting you swing swords or race cars while your living room feels like a sci-fi movie set. It’s a far cry from the SNES days, but the vibe’s the same: pure, controller-gripping fun.</p>
|
||||
<p>What makes consoles the heart of gaming culture is how they bring everyone together, from casual players to hardcore speedrunners. Whether it’s your uncle fumbling through Super Mario World in ‘92 or your best friend screaming during a late-night Call of Duty match, consoles are the ultimate couch co-op machines. Modern systems even let you stream your clutch Fortnite wins to the world or jump into crossplay with PC pals. From the GameCube’s quirky handle to the Switch’s grab-and-go joy-cons, every console’s got its own personality, making every era of gaming feel like a legendary chapter in a never-ending quest.</p>
|
||||
</div>
|
||||
</wa-scroller>
|
||||
```
|
||||
|
||||
### Without a Scrollbar
|
||||
|
||||
Use the `without-scrollbar` attribute to hide the scrollbar while maintaining scroll functionality. This creates a cleaner visual appearance but may reduce usability on content that needs a clear scrolling indicator.
|
||||
|
||||
```html {.example}
|
||||
<wa-scroller without-scrollbar>
|
||||
<div style="width: 1500px;">
|
||||
<p>Dungeons & Dragons 5e is the blockbuster superhero flick of tabletop RPGs, turning every session into an epic tavern brawl or dragon-slaying saga. Unlike the old 3.5e days, where you’d stack +30 bonuses like a mathlete on a mission, 5e keeps things chill with skill checks capping around +11—like a +5 from your slick Charisma and +6 from being a pro at persuasion. This means even a squad of scrappy kobolds can give your level 10 barbarian a bad day if you roll poorly. It’s like the game’s saying, “Sure, you’re a hero, but don’t get cocky!”</p>
|
||||
<p>The advantage and disadvantage system is 5e’s secret sauce, making every dice roll feel like a movie cliffhanger. Instead of juggling a dozen modifiers, you just roll two d20s and take the better (or worse) one, which shakes out to about a +5 or -5 vibe shift. It’s like your rogue’s got a lucky charm when sneaking past guards or a cursed boot when dodging a fireball. This keeps the game’s flow snappy, so you’re not stuck crunching numbers when you could be roleplaying a dramatic speech to charm a dragon or bluffing your way out of a bandit ambush.</p>
|
||||
<p>5e’s world is built for storytelling, not just stat sheets, and that’s why it’s the king of game nights. The rules are flexible enough for your DM to whip up a haunted forest crawl or a pirate ship heist without needing a PhD in game design. Classes like the warlock let you make shady pacts with cosmic entities, while feats like Tavern Brawler turn your monk into a bar-fighting legend who can knock out goblins with a chair. Whether you’re a newbie rolling your first d20 or a veteran plotting a castle siege, 5e’s vibe is all about epic moments—like when your party’s wizard crits on a fireball and you all cheer like you just won the Super Bowl.</p>
|
||||
</div>
|
||||
</wa-scroller>
|
||||
```
|
||||
|
||||
:::warning
|
||||
Hiding scrollbars can negatively impact accessibility. Users who rely on visible scrollbars to navigate content may have difficulty recognizing that content is scrollable or controlling their scrolling position. Consider the needs of all users when implementing this option.
|
||||
:::
|
||||
@@ -16,6 +16,7 @@ During the alpha period, things might break! We take breaking changes very serio
|
||||
|
||||
- 🚨 BREAKING: Renamed `<image-comparer>` to `<wa-comparer>` and improved compatibility for non-image content
|
||||
- 🚨 BREAKING: Added slot detection to `<wa-dialog>` and `<wa-drawer>` so you don't need to specify `with-header` and `with-footer`; headers are on by default now, but you can use the `without-header` attribute to turn them off
|
||||
- Added a new free component: `<wa-scroller>` (#1 of 14 per stretch goals)
|
||||
- Added support for Duotone Thin, Light, and Regular styles and the Sharp Duotone family of styles to `<wa-icon>`
|
||||
- Fixed a bug that caused `<wa-radio-group>` to have an undesired margin below it
|
||||
- Fixed a bug in the Matter theme that prevented clicks on form control labels to not focus the control
|
||||
|
||||
128
src/components/scroller/scroller.css
Normal file
128
src/components/scroller/scroller.css
Normal file
@@ -0,0 +1,128 @@
|
||||
:host {
|
||||
--shadow-color: var(--wa-color-surface-default);
|
||||
--shadow-size: 2rem;
|
||||
|
||||
/* private (defined dynamically) */
|
||||
--start-shadow-opacity: 0;
|
||||
--end-shadow-opacity: 0;
|
||||
|
||||
display: block;
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
isolation: isolate;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:host([orientation='vertical']) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#content {
|
||||
border-radius: inherit;
|
||||
scroll-behavior: smooth;
|
||||
scrollbar-width: thin;
|
||||
|
||||
/* Prevent text in mobile Safari from being larger when the container width larger than the viewport */
|
||||
-webkit-text-size-adjust: 100%;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: var(--wa-focus-ring);
|
||||
outline-offset: var(--wa-focus-ring-offset);
|
||||
}
|
||||
}
|
||||
|
||||
:host([without-scrollbar]) #content {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
:host([orientation='horizontal']) #content {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
:host([orientation='vertical']) #content {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0; /* This is crucial for flex children to respect overflow */
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* Horizontal shadows */
|
||||
:host([orientation='horizontal']) {
|
||||
#start-shadow,
|
||||
#end-shadow {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: var(--shadow-size);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#start-shadow {
|
||||
opacity: var(--start-shadow-opacity);
|
||||
}
|
||||
|
||||
#end-shadow {
|
||||
opacity: var(--end-shadow-opacity);
|
||||
}
|
||||
|
||||
#start-shadow {
|
||||
&:dir(ltr) {
|
||||
left: 0;
|
||||
background: linear-gradient(to right, var(--shadow-color), transparent 100%);
|
||||
}
|
||||
|
||||
&:dir(rtl) {
|
||||
right: 0;
|
||||
background: linear-gradient(to left, var(--shadow-color), transparent 100%);
|
||||
}
|
||||
}
|
||||
|
||||
#end-shadow {
|
||||
&:dir(ltr) {
|
||||
right: 0;
|
||||
background: linear-gradient(to left, var(--shadow-color), transparent 100%);
|
||||
}
|
||||
|
||||
&:dir(rtl) {
|
||||
left: 0;
|
||||
background: linear-gradient(to right, var(--shadow-color), transparent 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Vertical shadows */
|
||||
:host([orientation='vertical']) {
|
||||
#start-shadow,
|
||||
#end-shadow {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: var(--shadow-size);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#start-shadow {
|
||||
opacity: var(--start-shadow-opacity);
|
||||
}
|
||||
|
||||
#end-shadow {
|
||||
opacity: var(--end-shadow-opacity);
|
||||
}
|
||||
|
||||
#start-shadow {
|
||||
top: 0;
|
||||
background: linear-gradient(to bottom, var(--shadow-color), transparent 100%);
|
||||
}
|
||||
|
||||
#end-shadow {
|
||||
bottom: 0;
|
||||
background: linear-gradient(to top, var(--shadow-color), transparent 100%);
|
||||
}
|
||||
}
|
||||
10
src/components/scroller/scroller.test.ts
Normal file
10
src/components/scroller/scroller.test.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { expect, fixture, html } from '@open-wc/testing';
|
||||
import '../../../dist/webawesome.js';
|
||||
|
||||
describe('<wa-scroller>', () => {
|
||||
it('should render a component', async () => {
|
||||
const el = await fixture(html` <wa-scroller></wa-scroller> `);
|
||||
|
||||
expect(el).to.exist;
|
||||
});
|
||||
});
|
||||
139
src/components/scroller/scroller.ts
Normal file
139
src/components/scroller/scroller.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import { html } from 'lit';
|
||||
import { customElement, eventOptions, property, query, state } from 'lit/decorators.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import styles from './scroller.css';
|
||||
|
||||
/**
|
||||
* @summary Scrollers create an accessible container while providing visual cues that help users identify and navigate
|
||||
* through content that scrolls.
|
||||
* @documentation https://backers.webawesome.com/docs/components/card
|
||||
* @status stable
|
||||
* @since 3.0
|
||||
*
|
||||
* @slot - The content to show inside the scroller.
|
||||
*
|
||||
* @cssproperty [--shadow-color=var(--wa-color-surface-default)] - The base color of the shadow.
|
||||
* @cssproperty [--shadow-size=2rem] - The size of the shadow.
|
||||
*
|
||||
* @csspart content - The container that wraps the slotted content.
|
||||
*/
|
||||
@customElement('wa-scroller')
|
||||
export default class WaScroller extends WebAwesomeElement {
|
||||
static shadowStyle = [styles];
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private resizeObserver = new ResizeObserver(() => this.updateScroll());
|
||||
|
||||
@query('#content') content: HTMLElement;
|
||||
|
||||
@state() canScroll = false;
|
||||
|
||||
/** The scroller's orientation. */
|
||||
@property({ reflect: true }) orientation: 'horizontal' | 'vertical' = 'horizontal';
|
||||
|
||||
/** Removes the visible scrollbar. */
|
||||
@property({ attribute: 'without-scrollbar', type: Boolean, reflect: true }) withoutScrollbar = false;
|
||||
|
||||
/** Removes the shadows. */
|
||||
@property({ attribute: 'without-shadow', type: Boolean, reflect: true }) withoutShadow = false;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.resizeObserver.observe(this);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.resizeObserver.disconnect();
|
||||
}
|
||||
|
||||
private handleKeyDown(event: KeyboardEvent) {
|
||||
if (event.key === 'Home') {
|
||||
event.preventDefault();
|
||||
this.content.scrollTo({
|
||||
left: this.orientation === 'horizontal' ? 0 : undefined,
|
||||
top: this.orientation === 'vertical' ? 0 : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
if (event.key === 'End') {
|
||||
event.preventDefault();
|
||||
this.content.scrollTo({
|
||||
left: this.orientation === 'horizontal' ? this.content.scrollWidth : undefined,
|
||||
top: this.orientation === 'vertical' ? this.content.scrollHeight : undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private handleSlotChange() {
|
||||
this.updateScroll();
|
||||
}
|
||||
|
||||
@eventOptions({ passive: true })
|
||||
private updateScroll() {
|
||||
if (this.orientation === 'horizontal') {
|
||||
const clientWidth = Math.ceil(this.content.clientWidth);
|
||||
const scrollLeft = Math.abs(Math.ceil(this.content.scrollLeft));
|
||||
const scrollWidth = Math.ceil(this.content.scrollWidth);
|
||||
|
||||
// Calculate total scrollable width
|
||||
const maxScroll = scrollWidth - clientWidth;
|
||||
this.canScroll = maxScroll > 0;
|
||||
|
||||
// Calculate shadow opacities based on first/last 2% of scroll
|
||||
const startShadowOpacity = Math.min(1, scrollLeft / (maxScroll * 0.05));
|
||||
const endShadowOpacity = Math.min(1, (maxScroll - scrollLeft) / (maxScroll * 0.05));
|
||||
|
||||
// Update CSS custom properties
|
||||
this.style.setProperty('--start-shadow-opacity', String(startShadowOpacity || 0));
|
||||
this.style.setProperty('--end-shadow-opacity', String(endShadowOpacity || 0));
|
||||
} else {
|
||||
const clientHeight = Math.ceil(this.content.clientHeight);
|
||||
const scrollTop = Math.abs(Math.ceil(this.content.scrollTop));
|
||||
const scrollHeight = Math.ceil(this.content.scrollHeight);
|
||||
|
||||
// Calculate total scrollable height
|
||||
const maxScroll = scrollHeight - clientHeight;
|
||||
this.canScroll = maxScroll > 0;
|
||||
|
||||
// Calculate shadow opacities based on first/last 2% of scroll
|
||||
const startShadowOpacity = Math.min(1, scrollTop / (maxScroll * 0.05));
|
||||
const endShadowOpacity = Math.min(1, (maxScroll - scrollTop) / (maxScroll * 0.05));
|
||||
|
||||
// Update CSS custom properties
|
||||
this.style.setProperty('--start-shadow-opacity', String(startShadowOpacity || 0));
|
||||
this.style.setProperty('--end-shadow-opacity', String(endShadowOpacity || 0));
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
${this.withoutShadow
|
||||
? ''
|
||||
: html`
|
||||
<div id="start-shadow" part="start-shadow" aria-hidden="true"></div>
|
||||
<div id="end-shadow" part="end-shadow" aria-hidden="true"></div>
|
||||
`}
|
||||
|
||||
<div
|
||||
id="content"
|
||||
part="content"
|
||||
role="region"
|
||||
aria-label=${this.localize.term('scrollableRegion')}
|
||||
aria-orientation=${this.orientation}
|
||||
tabindex=${this.canScroll ? '0' : '-1'}
|
||||
@keydown=${this.handleKeyDown}
|
||||
@scroll=${this.updateScroll}
|
||||
>
|
||||
<slot @slotchange=${this.handleSlotChange}></slot>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'wa-scroller': WaScroller;
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ const translation: Translation = {
|
||||
progress: 'مقدار التقدم',
|
||||
remove: 'حذف',
|
||||
resize: 'تغيير الحجم',
|
||||
scrollableRegion: 'منطقة قابلة للتمرير',
|
||||
scrollToEnd: 'الانتقال الى النهاية',
|
||||
scrollToStart: 'الانتقال الى البداية',
|
||||
selectAColorFromTheScreen: 'اختر لون من الشاشة',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Průběh',
|
||||
remove: 'Odstranit',
|
||||
resize: 'Změnit velikost',
|
||||
scrollableRegion: 'Posunovatelná oblast',
|
||||
scrollToEnd: 'Scrollovat na konec',
|
||||
scrollToStart: 'Scrollovat na začátek',
|
||||
selectAColorFromTheScreen: 'Vybrat barvu z obrazovky',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Status',
|
||||
remove: 'Fjern',
|
||||
resize: 'Tilpas størrelse',
|
||||
scrollableRegion: 'Rullebar region',
|
||||
scrollToEnd: 'Scroll til slut',
|
||||
scrollToStart: 'Scroll til start',
|
||||
selectAColorFromTheScreen: 'Vælg en farve fra skærmen',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Fortschritt',
|
||||
remove: 'Entfernen',
|
||||
resize: 'Größe ändern',
|
||||
scrollableRegion: 'Scrollbarer Bereich',
|
||||
scrollToEnd: 'Zum Ende scrollen',
|
||||
scrollToStart: 'Zum Anfang scrollen',
|
||||
selectAColorFromTheScreen: 'Farbe vom Bildschirm auswählen',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Progress',
|
||||
remove: 'Remove',
|
||||
resize: 'Resize',
|
||||
scrollableRegion: 'Scrollable region',
|
||||
scrollToEnd: 'Scroll to end',
|
||||
scrollToStart: 'Scroll to start',
|
||||
selectAColorFromTheScreen: 'Select a color from the screen',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Progreso',
|
||||
remove: 'Eliminar',
|
||||
resize: 'Cambiar el tamaño',
|
||||
scrollableRegion: 'Región desplazable',
|
||||
scrollToEnd: 'Desplazarse hasta el final',
|
||||
scrollToStart: 'Desplazarse al inicio',
|
||||
selectAColorFromTheScreen: 'Seleccione un color de la pantalla',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'پیشرفت',
|
||||
remove: 'حذف',
|
||||
resize: 'تغییر اندازه',
|
||||
scrollableRegion: 'ناحیه قابل اسکرول',
|
||||
scrollToEnd: 'پیمایش به انتها',
|
||||
scrollToStart: 'پیمایش به ابتدا',
|
||||
selectAColorFromTheScreen: 'انتخاب یک رنگ از صفحه نمایش',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Edistyminen',
|
||||
remove: 'Poista',
|
||||
resize: 'Muuta kokoa',
|
||||
scrollableRegion: 'Vieritettävä alue',
|
||||
scrollToEnd: 'Vieritä loppuun',
|
||||
scrollToStart: 'Vieritä alkuun',
|
||||
selectAColorFromTheScreen: 'Valitse väri näytöltä',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Progrès',
|
||||
remove: 'Retirer',
|
||||
resize: 'Redimensionner',
|
||||
scrollableRegion: 'Région défilante',
|
||||
scrollToEnd: `Faire défiler jusqu'à la fin`,
|
||||
scrollToStart: `Faire défiler jusqu'au début`,
|
||||
selectAColorFromTheScreen: `Sélectionnez une couleur à l'écran`,
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'התקדמות',
|
||||
remove: 'לְהַסִיר',
|
||||
resize: 'שנה גודל',
|
||||
scrollableRegion: 'אזור גלילה',
|
||||
scrollToEnd: 'גלול עד הסוף',
|
||||
scrollToStart: 'גלול להתחלה',
|
||||
selectAColorFromTheScreen: 'בחור צבע מהמסך',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Napredak',
|
||||
remove: 'Makni',
|
||||
resize: 'Promijeni veličinu',
|
||||
scrollableRegion: 'Područje s mogućnošću pomicanja',
|
||||
scrollToEnd: 'Skrolaj do kraja',
|
||||
scrollToStart: 'Skrolaj na početak',
|
||||
selectAColorFromTheScreen: 'Odaberi boju sa ekrana',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Folyamat',
|
||||
remove: 'Eltávolítás',
|
||||
resize: 'Átméretezés',
|
||||
scrollableRegion: 'Görgethető terület',
|
||||
scrollToEnd: 'Görgessen a végére',
|
||||
scrollToStart: 'Görgessen az elejére',
|
||||
selectAColorFromTheScreen: 'Szín választása a képernyőről',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Kemajuan',
|
||||
remove: 'Hapus',
|
||||
resize: 'Ubah ukuran',
|
||||
scrollableRegion: 'Area yang dapat digulir',
|
||||
scrollToEnd: 'Gulir ke akhir',
|
||||
scrollToStart: 'Gulir ke awal',
|
||||
selectAColorFromTheScreen: 'Pilih warna dari layar',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Avanzamento',
|
||||
remove: 'Rimuovi',
|
||||
resize: 'Ridimensiona',
|
||||
scrollableRegion: 'Area scorrevole',
|
||||
scrollToEnd: 'Scorri alla fine',
|
||||
scrollToStart: "Scorri all'inizio",
|
||||
selectAColorFromTheScreen: 'Seleziona un colore dalla schermo',
|
||||
|
||||
@@ -25,6 +25,7 @@ const translation: Translation = {
|
||||
progress: '進行',
|
||||
remove: '削除',
|
||||
resize: 'サイズ変更',
|
||||
scrollableRegion: 'スクロール可能領域',
|
||||
scrollToEnd: '最後にスクロールする',
|
||||
scrollToStart: '最初にスクロールする',
|
||||
selectAColorFromTheScreen: '画面から色を選択してください',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Fremdrift',
|
||||
remove: 'Fjern',
|
||||
resize: 'Endre størrelse',
|
||||
scrollableRegion: 'Rullbar region',
|
||||
scrollToEnd: 'Rull til slutten',
|
||||
scrollToStart: 'Rull til starten',
|
||||
selectAColorFromTheScreen: 'Velg en farge fra skjermen',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Voortgang',
|
||||
remove: 'Verwijderen',
|
||||
resize: 'Formaat wijzigen',
|
||||
scrollableRegion: 'Scrollbaar gebied',
|
||||
scrollToEnd: 'Scroll naar einde',
|
||||
scrollToStart: 'Scroll naar begin',
|
||||
selectAColorFromTheScreen: 'Selecteer een kleur van het scherm',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Framdrift',
|
||||
remove: 'Fjern',
|
||||
resize: 'Endre storleik',
|
||||
scrollableRegion: 'Rullbar region',
|
||||
scrollToEnd: 'Rull til slutten',
|
||||
scrollToStart: 'Rull til starten',
|
||||
selectAColorFromTheScreen: 'Vel ein farge frå skjermen',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Postęp',
|
||||
remove: 'Usunąć',
|
||||
resize: 'Zmień rozmiar',
|
||||
scrollableRegion: 'Obszar przewijalny',
|
||||
scrollToEnd: 'Przewiń do końca',
|
||||
scrollToStart: 'Przewiń do początku',
|
||||
selectAColorFromTheScreen: 'Próbkuj z ekranu',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Progresso',
|
||||
remove: 'Remover',
|
||||
resize: 'Mudar o tamanho',
|
||||
scrollableRegion: 'Região rolável',
|
||||
scrollToEnd: 'Rolar até o final',
|
||||
scrollToStart: 'Rolar até o início',
|
||||
selectAColorFromTheScreen: 'Selecionar uma cor da tela',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Прогресс',
|
||||
remove: 'Удалить',
|
||||
resize: 'Изменить размер',
|
||||
scrollableRegion: 'Scrollable region',
|
||||
scrollToEnd: 'Пролистать до конца',
|
||||
scrollToStart: 'Пролистать к началу',
|
||||
selectAColorFromTheScreen: 'Выберите цвет на экране',
|
||||
|
||||
@@ -28,6 +28,7 @@ const translation: Translation = {
|
||||
progress: 'Napredek',
|
||||
remove: 'Odstrani',
|
||||
resize: 'Spremeni velikost',
|
||||
scrollableRegion: 'Področje za drsenje',
|
||||
scrollToEnd: 'Pomakni se na konec',
|
||||
scrollToStart: 'Pomakni se na začetek',
|
||||
selectAColorFromTheScreen: 'Izberite barvo z zaslona',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'Framsteg',
|
||||
remove: 'Ta bort',
|
||||
resize: 'Ändra storlek',
|
||||
scrollableRegion: 'Scrollbart område',
|
||||
scrollToEnd: 'Skrolla till slutet',
|
||||
scrollToStart: 'Skrolla till början',
|
||||
selectAColorFromTheScreen: 'Välj en färg från skärmen',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: 'İlerleme',
|
||||
remove: 'Kaldır',
|
||||
resize: 'Yeniden boyutlandır',
|
||||
scrollableRegion: 'Kaydırılabilir alan',
|
||||
scrollToEnd: 'Sona kay',
|
||||
scrollToStart: 'Başa kay',
|
||||
selectAColorFromTheScreen: 'Ekrandan bir renk seçin',
|
||||
|
||||
@@ -28,6 +28,7 @@ const translation: Translation = {
|
||||
progress: 'Поступ',
|
||||
remove: 'Видалити',
|
||||
resize: 'Змінити розмір',
|
||||
scrollableRegion: 'Область з можливістю прокрутки',
|
||||
scrollToEnd: 'Прокрутити в кінець',
|
||||
scrollToStart: 'Прокрутити на початок',
|
||||
selectAColorFromTheScreen: 'Виберіть колір на екрані',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: '进度',
|
||||
remove: '删除',
|
||||
resize: '调整大小',
|
||||
scrollableRegion: '可滚动区域',
|
||||
scrollToEnd: '滚动至页尾',
|
||||
scrollToStart: '滚动至页首',
|
||||
selectAColorFromTheScreen: '从屏幕中选择一种颜色',
|
||||
|
||||
@@ -26,6 +26,7 @@ const translation: Translation = {
|
||||
progress: '進度',
|
||||
remove: '移除',
|
||||
resize: '調整大小',
|
||||
scrollableRegion: '可捲動区域',
|
||||
scrollToEnd: '捲至頁尾',
|
||||
scrollToStart: '捲至頁首',
|
||||
selectAColorFromTheScreen: '從螢幕中選擇一種顏色',
|
||||
|
||||
@@ -37,6 +37,7 @@ export interface Translation extends DefaultTranslation {
|
||||
progress: string;
|
||||
remove: string;
|
||||
resize: string;
|
||||
scrollableRegion: string;
|
||||
scrollToEnd: string;
|
||||
scrollToStart: string;
|
||||
selectAColorFromTheScreen: string;
|
||||
|
||||
Reference in New Issue
Block a user