mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-01-02 20:19:27 +01:00
Et2VFSPath UI improvements:
- Label for internal nav - Fix flow, sizing & overflow - Special directory name handling for apps & app entries
This commit is contained in:
parent
7be3530b9d
commit
a100f6bbf6
@ -6,7 +6,7 @@ export default css`
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: nowrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 0.1rem 0.5rem;
|
gap: 0.1rem 0.5rem;
|
||||||
@ -28,8 +28,7 @@ export default css`
|
|||||||
|
|
||||||
.vfs-path__value-input {
|
.vfs-path__value-input {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
order: 10;
|
min-width: 20em;
|
||||||
min-width: 7em;
|
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
color: var(--input-text-color);
|
color: var(--input-text-color);
|
||||||
@ -44,6 +43,7 @@ export default css`
|
|||||||
.vfs-path__edit {
|
.vfs-path__edit {
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
visibility: hidden;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
@ -53,11 +53,34 @@ export default css`
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
transition: var(--sl-transition-fast) color;
|
transition: var(--sl-transition-fast) color;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-left: auto;
|
}
|
||||||
|
|
||||||
|
:host(:hover) .vfs-path__edit {
|
||||||
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Breadcrumb directories */
|
/* Breadcrumb directories */
|
||||||
|
|
||||||
|
sl-breadcrumb {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
min-width: 20em;
|
||||||
|
}
|
||||||
|
|
||||||
|
et2-image {
|
||||||
|
flex: none;
|
||||||
|
height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sl-breadcrumb::part(base) {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
sl-breadcrumb-item::part(base) {
|
||||||
|
font-size: var(--sl-font-size-medium);
|
||||||
|
}
|
||||||
sl-breadcrumb-item::part(label) {
|
sl-breadcrumb-item::part(label) {
|
||||||
color: var(--input-text-color);
|
color: var(--input-text-color);
|
||||||
}
|
}
|
||||||
@ -69,6 +92,18 @@ export default css`
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sl-breadcrumb-item:first-of-type {
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
sl-breadcrumb-item:first-of-type::part(separator) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sl-breadcrumb-item:last-of-type::part(separator) {
|
||||||
|
display: initial;
|
||||||
|
}
|
||||||
|
|
||||||
/* Sizes */
|
/* Sizes */
|
||||||
|
|
||||||
.form-control--medium, .form-control--medium .form-control-input {
|
.form-control--medium, .form-control--medium .form-control-input {
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
||||||
import {html, LitElement, nothing} from "lit";
|
import {html, LitElement, nothing, 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";
|
||||||
@ -18,6 +18,7 @@ import {repeat} from "lit/directives/repeat.js";
|
|||||||
import {FileInfo} from "./Et2VfsSelectDialog";
|
import {FileInfo} from "./Et2VfsSelectDialog";
|
||||||
import {SlBreadcrumbItem} from "@shoelace-style/shoelace";
|
import {SlBreadcrumbItem} from "@shoelace-style/shoelace";
|
||||||
import {HasSlotController} from "../Et2Widget/slot";
|
import {HasSlotController} from "../Et2Widget/slot";
|
||||||
|
import {until} from "lit/directives/until.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Display an editable path from the VFS
|
* @summary Display an editable path from the VFS
|
||||||
@ -144,6 +145,8 @@ export class Et2VfsPath extends Et2InputWidget(LitElement)
|
|||||||
|
|
||||||
protected handleEditMouseDown(event : MouseEvent)
|
protected handleEditMouseDown(event : MouseEvent)
|
||||||
{
|
{
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
this.edit();
|
this.edit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,8 +165,6 @@ export class Et2VfsPath extends Et2InputWidget(LitElement)
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.blur();
|
this.blur();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,16 +175,21 @@ export class Et2VfsPath extends Et2InputWidget(LitElement)
|
|||||||
{
|
{
|
||||||
target = target.parentNode;
|
target = target.parentNode;
|
||||||
}
|
}
|
||||||
|
else if(target instanceof Image && target.parentElement.slot == "prefix")
|
||||||
|
{
|
||||||
|
// Icon
|
||||||
|
target = target.parentElement.parentElement;
|
||||||
|
}
|
||||||
if(target instanceof SlBreadcrumbItem && event.composedPath().includes(this))
|
if(target instanceof SlBreadcrumbItem && event.composedPath().includes(this))
|
||||||
{
|
{
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
const dirs = Array.from(target.parentElement.querySelectorAll('sl-breadcrumb-item')) ?? [];
|
const dirs = Array.from(target.parentElement.querySelectorAll('sl-breadcrumb-item')).reverse() ?? [];
|
||||||
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
|
||||||
.map(d => d.textContent.trim().replace(/\/*$/, '').trim() + "/")
|
.map(d => (d.dataset.value ?? "").trim().replace(/\/*$/, '').trim() + "/")
|
||||||
.filter(p => p);
|
.filter(p => p);
|
||||||
if(newPath[0] !== '/')
|
if(newPath[0] !== '/')
|
||||||
{
|
{
|
||||||
@ -220,6 +226,45 @@ export class Et2VfsPath extends Et2InputWidget(LitElement)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected _getIcon(pathParts)
|
||||||
|
{
|
||||||
|
let image = this.egw().image("filemanager", "api");
|
||||||
|
if(pathParts.length > 2 && pathParts[1] == "apps")
|
||||||
|
{
|
||||||
|
image = this.egw().image('navbar', pathParts[2].toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected pathPartTemplate(pathParts, path, i)
|
||||||
|
{
|
||||||
|
let index = pathParts.length - 1 - i;
|
||||||
|
let pathName : string | TemplateResult<1> = path.trim();
|
||||||
|
if(pathParts.length > 1 && pathParts[1] == "apps")
|
||||||
|
{
|
||||||
|
switch(index)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
pathName = this.egw().lang("applications");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
pathName = this.egw().lang(pathName);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if(!isNaN(<number><unknown>pathName))
|
||||||
|
{
|
||||||
|
pathName = html`${until(this.egw().link_title(pathParts[2], pathParts[3], true) || pathName, pathName)}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<sl-breadcrumb-item class="vfs-path__directory" data-value="${path.trim()}">
|
||||||
|
${pathName}
|
||||||
|
<span slot="separator">/</span>
|
||||||
|
</sl-breadcrumb-item>`;
|
||||||
|
}
|
||||||
|
|
||||||
render()
|
render()
|
||||||
{
|
{
|
||||||
const hasLabelSlot = this.hasSlotController.test('label');
|
const hasLabelSlot = this.hasSlotController.test('label');
|
||||||
@ -227,13 +272,15 @@ export class Et2VfsPath extends Et2InputWidget(LitElement)
|
|||||||
const hasLabel = this.label ? true : !!hasLabelSlot;
|
const hasLabel = this.label ? true : !!hasLabelSlot;
|
||||||
const hasHelpText = this.helpText ? true : !!hasHelpTextSlot;
|
const hasHelpText = this.helpText ? true : !!hasHelpTextSlot;
|
||||||
// No trailing slash in the path
|
// No trailing slash in the path
|
||||||
const pathParts = this.value === "/" ? ["/"] : this.value
|
const pathParts = this.value
|
||||||
// Remove trailing /
|
// Remove trailing /
|
||||||
.replace(/\/*$/, '')
|
.replace(/\/*$/, '')
|
||||||
.split('/');
|
.split('/');
|
||||||
const isEditable = !(this.disabled || this.readonly);
|
const isEditable = !(this.disabled || this.readonly);
|
||||||
const editing = this.editing && isEditable;
|
const editing = this.editing && isEditable;
|
||||||
|
|
||||||
|
let icon = this._getIcon(pathParts);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
part="form-control"
|
part="form-control"
|
||||||
@ -259,7 +306,16 @@ export class Et2VfsPath extends Et2InputWidget(LitElement)
|
|||||||
<div part="form-control-input" class="form-control-input"
|
<div part="form-control-input" class="form-control-input"
|
||||||
@click=${() => this.focus()}
|
@click=${() => this.focus()}
|
||||||
>
|
>
|
||||||
<slot part="prefix" name="prefix"></slot>
|
<slot part="prefix" name="prefix">
|
||||||
|
${icon ? html`
|
||||||
|
<et2-image src="${icon}" slot="prefix"
|
||||||
|
@click=${(e) =>
|
||||||
|
{
|
||||||
|
this.setValue("/");
|
||||||
|
this.updateComplete.then(() => {this.dispatchEvent(new Event("change", {bubbles: true}))});
|
||||||
|
}}
|
||||||
|
></et2-image>` : nothing}
|
||||||
|
</slot>
|
||||||
${editing ? html`
|
${editing ? html`
|
||||||
<input
|
<input
|
||||||
class="vfs-path__value-input"
|
class="vfs-path__value-input"
|
||||||
@ -271,19 +327,15 @@ export class Et2VfsPath extends Et2InputWidget(LitElement)
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@blur=${() => this.blur()}
|
@blur=${() => this.blur()}
|
||||||
@keydown=${this.handleKeyDown}
|
@keydown=${this.handleKeyDown}
|
||||||
/>` : html`
|
/>
|
||||||
|
<div class="vfs-path__edit"/>` : html`
|
||||||
<sl-breadcrumb
|
<sl-breadcrumb
|
||||||
|
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, (path) =>
|
${repeat(pathParts.toReversed(), (part, i) => this.pathPartTemplate(pathParts, part, i))}
|
||||||
{
|
|
||||||
return html`
|
|
||||||
<sl-breadcrumb-item class="vfs-path__directory">${path.trim()}
|
|
||||||
<span slot="separator">/</span>
|
|
||||||
</sl-breadcrumb-item>`;
|
|
||||||
})}
|
|
||||||
</sl-breadcrumb>
|
</sl-breadcrumb>
|
||||||
${!isEditable ? nothing : html`
|
${!isEditable ? nothing : html`
|
||||||
<button
|
<button
|
||||||
@ -291,7 +343,7 @@ export class Et2VfsPath extends Et2InputWidget(LitElement)
|
|||||||
class="vfs-path__edit"
|
class="vfs-path__edit"
|
||||||
type="button"
|
type="button"
|
||||||
aria-label=${this.egw().lang('edit')}
|
aria-label=${this.egw().lang('edit')}
|
||||||
@mousedown=${this.handleEditMouseDown}
|
@click=${this.handleEditMouseDown}
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
<slot name="edit-icon">
|
<slot name="edit-icon">
|
||||||
|
@ -37,10 +37,12 @@ input#filemanager-index_path {
|
|||||||
/* Let path have more space */
|
/* Let path have more space */
|
||||||
#filemanager-index_filemanager-index-header_row {
|
#filemanager-index_filemanager-index-header_row {
|
||||||
flex: 20 1 auto;
|
flex: 20 1 auto;
|
||||||
|
min-width: 20em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#filemanager-index_nm_path {
|
#filemanager-index_nm_path {
|
||||||
flex-grow: 1
|
flex-grow: 1;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user