Et2VfsPath: Better overflow handling, including arrow buttons when needed

This commit is contained in:
nathan 2024-04-11 08:07:00 -06:00
parent 83cf428077
commit ee5f202f35
2 changed files with 83 additions and 14 deletions

View File

@ -3,6 +3,7 @@ import {css} from 'lit';
export default css` export default css`
.form-control-input { .form-control-input {
position: relative;
min-width: 15em; min-width: 15em;
flex: 1; flex: 1;
@ -61,14 +62,31 @@ export default css`
visibility: visible; visibility: visible;
} }
/* Breadcrumb directories */ .vfs-path__scroll {
sl-breadcrumb {
flex: 1 1 auto; flex: 1 1 auto;
overflow: hidden; overflow: hidden;
min-width: 10em; min-width: 10em;
} }
.form-control-input sl-icon-button[name*="caret"] {
display: none;
position: absolute;
background: var(--sl-input-background-color);
}
.form-control-input sl-icon-button[name*="caret"]:last-of-type {
right: 2em;
}
:host(:hover) .form-control-input.vfs-path__overflow sl-icon-button[name*="caret"] {
display: initial;
}
/* Breadcrumb directories */
sl-breadcrumb {
}
et2-image { et2-image {
flex: none; flex: none;
height: 1.5em; height: 1.5em;
@ -76,8 +94,6 @@ export default css`
sl-breadcrumb::part(base) { sl-breadcrumb::part(base) {
flex-wrap: nowrap; flex-wrap: nowrap;
flex-direction: row-reverse;
justify-content: flex-start;
} }
sl-breadcrumb-item::part(base) { sl-breadcrumb-item::part(base) {
@ -95,15 +111,15 @@ export default css`
} }
sl-breadcrumb-item:first-of-type { sl-breadcrumb-item:first-of-type {
margin-right: auto;
} }
sl-breadcrumb-item:first-of-type::part(separator) { sl-breadcrumb-item:first-of-type::part(separator) {
display: none; display: initial;
} }
sl-breadcrumb-item:last-of-type::part(separator) { sl-breadcrumb-item:last-of-type::part(separator) {
display: initial; /* Trailing / */
display: none;
} }
/* Sizes */ /* Sizes */

View File

@ -8,7 +8,7 @@
*/ */
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
import {html, LitElement, nothing, TemplateResult} from "lit"; import {html, LitElement, nothing, PropertyValues, TemplateResult} from "lit";
import shoelace from "../Styles/shoelace"; import shoelace from "../Styles/shoelace";
import styles from "./Et2VfsPath.styles"; import styles from "./Et2VfsPath.styles";
import {property} from "lit/decorators/property.js"; import {property} from "lit/decorators/property.js";
@ -71,6 +71,13 @@ export class Et2VfsPath extends Et2InputWidget(LitElement)
this.handlePathClick = this.handlePathClick.bind(this); this.handlePathClick = this.handlePathClick.bind(this);
} }
updated(changedProperties : PropertyValues)
{
super.updated(changedProperties);
this.checkPathOverflow();
}
@property() @property()
set value(_value : string) set value(_value : string)
{ {
@ -138,6 +145,30 @@ export class Et2VfsPath extends Et2InputWidget(LitElement)
}) })
} }
protected checkPathOverflow()
{
const wrapper = this.shadowRoot.querySelector(".vfs-path__scroll");
const path = wrapper?.querySelector("sl-breadcrumb");
const scroll = path?.shadowRoot.querySelector("nav");
if(!wrapper || !scroll)
{
return;
}
path.updateComplete.then(() =>
{
if(wrapper.clientWidth < scroll.scrollWidth)
{
// Too small
wrapper.scrollLeft = scroll.scrollWidth - wrapper.clientWidth;
wrapper.parentElement.classList.add("vfs-path__overflow");
}
else
{
wrapper.parentElement.classList.remove("vfs-path__overflow");
}
});
}
protected handleLabelClick() protected handleLabelClick()
{ {
this.edit(); this.edit();
@ -185,7 +216,7 @@ export class Et2VfsPath extends Et2InputWidget(LitElement)
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
const dirs = Array.from(target.parentElement.querySelectorAll('sl-breadcrumb-item')).reverse() ?? []; const dirs = Array.from(target.parentElement.querySelectorAll('sl-breadcrumb-item')) ?? [];
let stopIndex = dirs.indexOf(target) + 1; let stopIndex = dirs.indexOf(target) + 1;
let newPath = dirs.slice(0, stopIndex) let newPath = dirs.slice(0, stopIndex)
// Strip out any extra space // Strip out any extra space
@ -228,7 +259,7 @@ export class Et2VfsPath extends Et2InputWidget(LitElement)
protected handleScroll(event : WheelEvent) protected handleScroll(event : WheelEvent)
{ {
this.shadowRoot.querySelector("sl-breadcrumb").scrollLeft += event.deltaY; this.shadowRoot.querySelector(".vfs-path__scroll").scrollLeft += event.deltaY;
} }
protected _getIcon(pathParts) protected _getIcon(pathParts)
@ -242,9 +273,8 @@ export class Et2VfsPath extends Et2InputWidget(LitElement)
return image; return image;
} }
protected pathPartTemplate(pathParts, path, i) protected pathPartTemplate(pathParts, path, index)
{ {
let index = pathParts.length - 1 - i;
let pathName : string | TemplateResult<1> = path.trim(); let pathName : string | TemplateResult<1> = path.trim();
if(pathParts.length > 1 && pathParts[1] == "apps") if(pathParts.length > 1 && pathParts[1] == "apps")
{ {
@ -310,6 +340,13 @@ export class Et2VfsPath extends Et2InputWidget(LitElement)
</label> </label>
<div part="form-control-input" class="form-control-input" <div part="form-control-input" class="form-control-input"
@click=${() => this.focus()} @click=${() => this.focus()}
@mouseout=${(e) =>
{
if(e.target.classList.contains("form-control-input"))
{
this.checkPathOverflow();
}
}}
> >
<slot part="prefix" name="prefix"> <slot part="prefix" name="prefix">
${icon ? html` ${icon ? html`
@ -334,14 +371,30 @@ export class Et2VfsPath extends Et2InputWidget(LitElement)
@keydown=${this.handleKeyDown} @keydown=${this.handleKeyDown}
/> />
<div class="vfs-path__edit"/>` : html` <div class="vfs-path__edit"/>` : html`
<sl-icon-button name="caret-left"
@click=${(e) =>
{
e.stopPropagation();
this.handleScroll({deltaY: -20})
}}></sl-icon-button>
<div class="vfs-path__scroll"
@wheel=${this.handleScroll}
>
<sl-breadcrumb <sl-breadcrumb
label=${this.label || this.egw().lang("path")} label=${this.label || this.egw().lang("path")}
class="vfs-path__breadcrumb" class="vfs-path__breadcrumb"
@click=${this.handlePathClick} @click=${this.handlePathClick}
> >
<span slot="separator">/</span> <span slot="separator">/</span>
${repeat(pathParts.toReversed(), (part, i) => this.pathPartTemplate(pathParts, part, i))} ${repeat(pathParts, (part, i) => this.pathPartTemplate(pathParts, part, i))}
</sl-breadcrumb> </sl-breadcrumb>
</div>
<sl-icon-button name="caret-right"
@click=${(e) =>
{
e.stopPropagation();
this.handleScroll({deltaY: 20})
}}></sl-icon-button>
${!isEditable ? nothing : html` ${!isEditable ? nothing : html`
<button <button
part="edit-button" part="edit-button"