mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-22 16:56:35 +00:00
Add a drag-scroll controller (#25159)
* Add a drag-scroll controller * simplify and fix
This commit is contained in:
parent
af0854e480
commit
3a0c367f76
@ -56,6 +56,7 @@
|
||||
"@lit-labs/observers": "2.0.5",
|
||||
"@lit-labs/virtualizer": "2.1.0",
|
||||
"@lit/context": "1.1.5",
|
||||
"@lit/reactive-element": "2.1.0",
|
||||
"@material/chips": "=14.0.0-canary.53b3cad2f.0",
|
||||
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
|
||||
"@material/mwc-base": "0.27.0",
|
||||
|
102
src/common/controllers/drag-scroll-controller.ts
Normal file
102
src/common/controllers/drag-scroll-controller.ts
Normal file
@ -0,0 +1,102 @@
|
||||
import type {
|
||||
ReactiveController,
|
||||
ReactiveControllerHost,
|
||||
} from "@lit/reactive-element/reactive-controller";
|
||||
import type { LitElement } from "lit";
|
||||
|
||||
/**
|
||||
* The config options for a DragScrollController.
|
||||
*/
|
||||
export interface DragScrollControllerConfig {
|
||||
selector: string;
|
||||
}
|
||||
|
||||
export class DragScrollController implements ReactiveController {
|
||||
public mouseIsDown = false;
|
||||
|
||||
public scrolled = false;
|
||||
|
||||
public scrolling = false;
|
||||
|
||||
public scrollStartX = 0;
|
||||
|
||||
public scrollLeft = 0;
|
||||
|
||||
private _host: ReactiveControllerHost & LitElement;
|
||||
|
||||
private _selector: string;
|
||||
|
||||
private _scrollContainer?: HTMLElement | null;
|
||||
|
||||
constructor(
|
||||
host: ReactiveControllerHost & LitElement,
|
||||
{ selector }: DragScrollControllerConfig
|
||||
) {
|
||||
this._selector = selector;
|
||||
this._host = host;
|
||||
host.addController(this);
|
||||
}
|
||||
|
||||
hostUpdated() {
|
||||
if (this._scrollContainer) {
|
||||
return;
|
||||
}
|
||||
this._scrollContainer = this._host.renderRoot?.querySelector(
|
||||
this._selector
|
||||
);
|
||||
if (this._scrollContainer) {
|
||||
this._scrollContainer.addEventListener("mousedown", this._mouseDown);
|
||||
}
|
||||
}
|
||||
|
||||
hostDisconnected() {
|
||||
window.removeEventListener("mousemove", this._mouseMove);
|
||||
window.removeEventListener("mouseup", this._mouseUp);
|
||||
}
|
||||
|
||||
private _mouseDown = (event: MouseEvent) => {
|
||||
const scrollContainer = this._scrollContainer;
|
||||
|
||||
if (!scrollContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.scrollStartX = event.pageX - scrollContainer.offsetLeft;
|
||||
this.scrollLeft = scrollContainer.scrollLeft;
|
||||
this.mouseIsDown = true;
|
||||
this.scrolled = false;
|
||||
|
||||
window.addEventListener("mousemove", this._mouseMove);
|
||||
window.addEventListener("mouseup", this._mouseUp, { once: true });
|
||||
};
|
||||
|
||||
private _mouseUp = () => {
|
||||
this.mouseIsDown = false;
|
||||
this.scrolling = false;
|
||||
this._host.requestUpdate();
|
||||
window.removeEventListener("mousemove", this._mouseMove);
|
||||
};
|
||||
|
||||
private _mouseMove = (event: MouseEvent) => {
|
||||
if (!this.mouseIsDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scrollContainer = this._scrollContainer;
|
||||
|
||||
if (!scrollContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const x = event.pageX - scrollContainer.offsetLeft;
|
||||
const scroll = x - this.scrollStartX;
|
||||
|
||||
if (!this.scrolled) {
|
||||
this.scrolled = Math.abs(scroll) > 1;
|
||||
this.scrolling = this.scrolled;
|
||||
this._host.requestUpdate();
|
||||
}
|
||||
|
||||
scrollContainer.scrollLeft = this.scrollLeft - scroll;
|
||||
};
|
||||
}
|
@ -1,30 +1,16 @@
|
||||
import TabGroup from "@shoelace-style/shoelace/dist/components/tab-group/tab-group.component";
|
||||
import TabGroupStyles from "@shoelace-style/shoelace/dist/components/tab-group/tab-group.styles";
|
||||
import "@shoelace-style/shoelace/dist/components/tab/tab";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { DragScrollController } from "../common/controllers/drag-scroll-controller";
|
||||
|
||||
@customElement("sl-tab-group")
|
||||
// @ts-ignore
|
||||
export class HaSlTabGroup extends TabGroup {
|
||||
private _mouseIsDown = false;
|
||||
|
||||
private _scrolled = false;
|
||||
|
||||
private _mouseReleasedAt?: number;
|
||||
|
||||
private _scrollStartX = 0;
|
||||
|
||||
private _scrollLeft = 0;
|
||||
|
||||
@query(".tab-group__nav", true) private _scrollContainer?: HTMLElement;
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
window.removeEventListener("mousemove", this._mouseMove);
|
||||
window.removeEventListener("mouseup", this._mouseUp);
|
||||
}
|
||||
private _dragScrollController = new DragScrollController(this, {
|
||||
selector: ".tab-group__nav",
|
||||
});
|
||||
|
||||
override setAriaLabels() {
|
||||
// Override the method to prevent setting aria-labels, as we don't use panels
|
||||
@ -38,73 +24,15 @@ export class HaSlTabGroup extends TabGroup {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected override firstUpdated(_changedProperties: PropertyValues): void {
|
||||
super.firstUpdated(_changedProperties);
|
||||
|
||||
const scrollContainer = this._scrollContainer;
|
||||
|
||||
if (scrollContainer) {
|
||||
scrollContainer.addEventListener("mousedown", this._mouseDown);
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
protected override handleClick(event: MouseEvent) {
|
||||
if (
|
||||
this._mouseReleasedAt &&
|
||||
new Date().getTime() - this._mouseReleasedAt < 100
|
||||
) {
|
||||
if (this._dragScrollController.scrolled) {
|
||||
return;
|
||||
}
|
||||
// @ts-ignore
|
||||
super.handleClick(event);
|
||||
}
|
||||
|
||||
private _mouseDown = (event: MouseEvent) => {
|
||||
const scrollContainer = this._scrollContainer;
|
||||
|
||||
if (!scrollContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._scrollStartX = event.pageX - scrollContainer.offsetLeft;
|
||||
this._scrollLeft = scrollContainer.scrollLeft;
|
||||
this._mouseIsDown = true;
|
||||
this._scrolled = false;
|
||||
|
||||
window.addEventListener("mousemove", this._mouseMove);
|
||||
window.addEventListener("mouseup", this._mouseUp, { once: true });
|
||||
};
|
||||
|
||||
private _mouseUp = () => {
|
||||
this._mouseIsDown = false;
|
||||
if (this._scrolled) {
|
||||
this._mouseReleasedAt = new Date().getTime();
|
||||
}
|
||||
window.removeEventListener("mousemove", this._mouseMove);
|
||||
};
|
||||
|
||||
private _mouseMove = (event: MouseEvent) => {
|
||||
if (!this._mouseIsDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scrollContainer = this._scrollContainer;
|
||||
|
||||
if (!scrollContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const x = event.pageX - scrollContainer.offsetLeft;
|
||||
const scroll = x - this._scrollStartX;
|
||||
|
||||
if (!this._scrolled) {
|
||||
this._scrolled = Math.abs(scroll) > 1;
|
||||
}
|
||||
|
||||
scrollContainer.scrollLeft = this._scrollLeft - scroll;
|
||||
};
|
||||
|
||||
static override styles = [
|
||||
TabGroupStyles,
|
||||
css`
|
||||
|
@ -29,6 +29,7 @@ import {
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import "../../../components/ha-relative-time";
|
||||
import "../../../components/ha-state-icon";
|
||||
import { DragScrollController } from "../../../common/controllers/drag-scroll-controller";
|
||||
|
||||
@customElement("more-info-weather")
|
||||
class MoreInfoWeather extends LitElement {
|
||||
@ -42,6 +43,11 @@ class MoreInfoWeather extends LitElement {
|
||||
|
||||
@state() private _subscribed?: Promise<() => void>;
|
||||
|
||||
// @ts-ignore
|
||||
private _dragScrollController = new DragScrollController(this, {
|
||||
selector: ".forecast",
|
||||
});
|
||||
|
||||
private _unsubscribeForecastEvents() {
|
||||
if (this._subscribed) {
|
||||
this._subscribed.then((unsub) => unsub());
|
||||
@ -547,6 +553,7 @@ class MoreInfoWeather extends LitElement {
|
||||
black 94%,
|
||||
transparent 100%
|
||||
);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.forecast > div {
|
||||
|
@ -20,6 +20,7 @@ import { replaceView } from "../editor/config-util";
|
||||
import { showEditViewHeaderDialog } from "../editor/view-header/show-edit-view-header-dialog";
|
||||
import type { Lovelace } from "../types";
|
||||
import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog";
|
||||
import { DragScrollController } from "../../../common/controllers/drag-scroll-controller";
|
||||
|
||||
export const DEFAULT_VIEW_HEADER_LAYOUT = "center";
|
||||
export const DEFAULT_VIEW_HEADER_BADGES_POSITION = "bottom";
|
||||
@ -51,6 +52,10 @@ export class HuiViewHeader extends LitElement {
|
||||
this._checkHidden();
|
||||
};
|
||||
|
||||
private _dragScrollController = new DragScrollController(this, {
|
||||
selector: ".scroll",
|
||||
});
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.addEventListener(
|
||||
@ -252,7 +257,12 @@ export class HuiViewHeader extends LitElement {
|
||||
: nothing}
|
||||
${this.lovelace && (editMode || this.badges.length > 0)
|
||||
? html`
|
||||
<div class="badges ${badgesPosition} ${badgesWrap}">
|
||||
<div
|
||||
class="badges ${badgesPosition} ${badgesWrap} ${this
|
||||
._dragScrollController.scrolling
|
||||
? "dragging"
|
||||
: ""}"
|
||||
>
|
||||
<hui-view-badges
|
||||
.badges=${this.badges}
|
||||
.hass=${this.hass}
|
||||
@ -471,6 +481,10 @@ export class HuiViewHeader extends LitElement {
|
||||
.add:focus {
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.dragging {
|
||||
pointer-events: none;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -9364,6 +9364,7 @@ __metadata:
|
||||
"@lit-labs/observers": "npm:2.0.5"
|
||||
"@lit-labs/virtualizer": "npm:2.1.0"
|
||||
"@lit/context": "npm:1.1.5"
|
||||
"@lit/reactive-element": "npm:2.1.0"
|
||||
"@lokalise/node-api": "npm:14.4.0"
|
||||
"@material/chips": "npm:=14.0.0-canary.53b3cad2f.0"
|
||||
"@material/data-table": "npm:=14.0.0-canary.53b3cad2f.0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user