WIP on Et2LinkList, should be working now

This commit is contained in:
nathan 2022-05-11 14:00:23 -06:00
parent b735b0f218
commit 875a3996aa
4 changed files with 119 additions and 53 deletions

View File

@ -59,6 +59,13 @@ export class Et2LinkList extends Et2LinkString
background-color: var(--highlight-background-color);
}
div.zip_highlight {
animation-name: new_entry_pulse, new_entry_clear;
animation-duration: 5s;
animation-delay: 0s, 30s;
animation-fill-mode: forwards;
}
/* CSS for child elements */
::slotted(*):after {
/* Reset from Et2LinkString */
@ -70,9 +77,14 @@ export class Et2LinkList extends Et2LinkString
::slotted(et2-link) {
flex: 1 1 auto;
}
::slotted(.remark) {
flex: 1 1 auto;
width: 20%;
}
::slotted(.delete_button) {
display: none;
visibility: hidden;
width: 16px;
order: 5;
}
`
];
@ -99,8 +111,6 @@ export class Et2LinkList extends Et2LinkString
this._handleRowHover = this._handleRowHover.bind(this);
this._handleRowContext = this._handleRowContext.bind(this);
this.addEventListener("mouseover", this._handleRowHover);
this.addEventListener("mouseout", this._handleRowHover);
}
connectedCallback()
@ -128,13 +138,13 @@ export class Et2LinkList extends Et2LinkString
protected _addLinks(links : LinkInfo[])
{
this._link_list = links;
super._addLinks(links);
this.requestUpdate();
this.updateComplete.then(() => super._addLinks(links));
}
/**
* Render one link
* These elements are slotted and are found in the light DOM (use this.querySelector(...) to find them)
*
* @param link
* @returns {TemplateResult}
@ -145,14 +155,18 @@ export class Et2LinkList extends Et2LinkString
return html`
${this._thumbnailTemplate(link)}
<et2-link slot="${this._get_row_id(link)}" app="${link.app}" entry_id="${link.id}"
@contextmenu=${this._handleRowContext}
._parent=${this}
.value=${link}></et2-link>
<et2-description slot="${this._get_row_id(link)}" ._parent=${this} class="remark"
value="${link.remark}"></et2-description>
${this._deleteButtonTemplate(link)}
`;
}
/**
* Render one link
* Render the row for one link.
* This is just the structure and slot, actual row contents are done in _linkTemplate.
* These rows are found in the shadowRoot. Use this.shadowRoot.querySelector(...) to find them.
*
* @param link
* @returns {TemplateResult}
@ -160,13 +174,11 @@ export class Et2LinkList extends Et2LinkString
*/
protected _rowTemplate(link) : TemplateResult
{
let hover = () =>
{
console.log(link);
debugger;
}
return html`
<div id="${this._get_row_id(link)}">
<div id="${this._get_row_id(link)}"
@mouseover=${this._handleRowHover}
@mouseout=${this._handleRowHover}
@contextmenu=${this._handleRowContext}>
<slot name="${this._get_row_id(link)}"></slot>
</div>`;
}
@ -178,19 +190,38 @@ export class Et2LinkList extends Et2LinkString
*/
protected _handleRowHover(_ev)
{
// Ignore delete button
if(_ev.relatedTarget.classList.contains("delete_button") || _ev.relatedTarget.parentElement.classList.contains("delete_button"))
let slot_name = "";
let target = _ev.target;
// Fist check if target is the row div
if(target.firstElementChild?.localName == "slot")
{
slot_name = target.firstElementChild.name;
}
do
{
// Look up tree for the slot
if(target.slot)
{
slot_name = target.slot;
}
target = target.parentNode;
}
while(!slot_name && target.parentNode)
if(!slot_name)
{
return;
}
if(_ev.type == "mouseout")
{
this.querySelectorAll(".delete_button").forEach(b => b.style.display = "");
this.querySelectorAll(".delete_button").forEach(b => b.style.visibility = "");
}
if(_ev.type == "mouseover" && _ev.target.parentNode == this)
if(_ev.type == "mouseover")
{
_ev.target.parentNode.querySelector(".delete_button[slot='" + _ev.target.slot + "']").style.display = "initial";
this.querySelector(".delete_button[slot='" + slot_name + "']").style.visibility = "initial";
}
}
@ -251,7 +282,7 @@ export class Et2LinkList extends Et2LinkString
*/
protected _get_row_id(link : any) : string
{
return "link_" + (link.dom_id ? link.dom_id : (typeof link.link_id == "string" ? link.link_id.replace(/[:\.]/g, '_') : link.link_id || link.id));
return "link_" + (link.dom_id ? link.dom_id : (typeof link.link_id == "string" ? link.link_id.replace(/[:.]/g, '_') : link.link_id || link.id));
}
@ -343,10 +374,8 @@ export class Et2LinkList extends Et2LinkString
this.context = new egwMenu();
this.context.addItem("comment", this.egw().lang("Comment"), "", () =>
{
let link_id = typeof this.context.data.link_id == 'number' ? this.context.data.link_id : this.context.data.link_id.replace(/[:\.]/g, '_');
Et2Dialog.show_prompt(
function(button, comment)
(button, comment) =>
{
if(button != Et2Dialog.OK_BUTTON)
{
@ -358,9 +387,9 @@ export class Et2LinkList extends Et2LinkString
);
});
this.context.addItem("file_info", this.egw().lang("File information"), this.egw().image("edit"), (menu_item) =>
this.context.addItem("file_info", this.egw().lang("File information"), this.egw().image("edit"), () =>
{
var link_data = this.context.data;
let link_data = this.context.data;
if(link_data.app == 'file')
{
// File info is always the same
@ -374,9 +403,9 @@ export class Et2LinkList extends Et2LinkString
}
});
this.context.addItem("-", "-");
this.context.addItem("save", this.egw().lang("Save as"), this.egw().image('save'), (menu_item) =>
this.context.addItem("save", this.egw().lang("Save as"), this.egw().image('save'), () =>
{
var link_data = this.context.data;
let link_data = this.context.data;
// Download file
if(link_data.download_url)
{
@ -394,12 +423,11 @@ export class Et2LinkList extends Et2LinkString
}
// Multiple file download for those that support it
a = jQuery(a)
.prop('href', url)
.prop('download', link_data.title || "")
.appendTo(this.getInstanceManager().DOMContainer);
a.setAttribute("href",url);
a.setAttribute("download",link_data.title || "");
this.getInstanceManager().DOMContainer.appendChild(a);
var evt = document.createEvent('MouseEvent');
let evt = document.createEvent('MouseEvent');
evt.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
a[0].dispatchEvent(evt);
a.remove();
@ -408,30 +436,30 @@ export class Et2LinkList extends Et2LinkString
this.egw().open(link_data, "", "view", 'download', link_data.target ? link_data.target : link_data.app, link_data.app);
});
this.context.addItem("zip", this.egw().lang("Save as Zip"), this.egw().image('save_zip'), (menu_item) =>
this.context.addItem("zip", this.egw().lang("Save as Zip"), this.egw().image('save_zip'), () =>
{
// Highlight files for nice UI indicating what will be in the zip.
// Files have negative IDs.
jQuery('[id^="link_-"]', this.list).effect('highlight', {}, 2000);
this.shadowRoot.querySelectorAll('div[id^="link_-"]').forEach((row) => row.classList.add("zip_highlight"));
// Download ZIP
window.location = this.egw().link('/index.php', {
window.location.href = this.egw().link('/index.php', {
menuaction: 'api.EGroupware\\Api\\Etemplate\\Widget\\Link.download_zip',
app: this.to_app,
id: this.to_id
app: this.application,
id: this.entry_id
});
});
// Only allow this option if the entry has been saved, and has a real ID
if(this.to_id && typeof this.to_id != 'object')
{
this.context.addItem("copy_to", this.egw().lang("Copy to"), this.egw().image('copy'), (menu_item) =>
this.context.addItem("copy_to", this.egw().lang("Copy to"), this.egw().image('copy'), () =>
{
// Highlight files for nice UI indicating what will be copied
jQuery('[id="link_' + this.context.data.link_id + ']', this.list).effect('highlight', {}, 2000);
this.shadowRoot.querySelectorAll('div[id^="link_-"]').forEach((row) => row.classList.add("zip_highlight"));
// Get target
var select_attrs : any = {
let select_attrs : any = {
mode: "select-dir",
button_caption: '',
button_icon: 'copy',
@ -449,16 +477,14 @@ export class Et2LinkList extends Et2LinkString
});
}
this.context.addItem("-", "-");
this.context.addItem("delete", this.egw().lang("Delete link"), this.egw().image("delete"), (menu_item) =>
this.context.addItem("delete", this.egw().lang("Delete link"), this.egw().image("delete"), () =>
{
var link_id = isNaN(this.context.data.link_id) ? this.context.data : this.context.data.link_id;
var row = jQuery('#link_' + (this.context.data.dom_id ? this.context.data.dom_id : this.context.data.link_id), this);
Et2Dialog.show_dialog(
function(button)
(button) =>
{
if(button == Et2Dialog.YES_BUTTON)
{
this._delete_link(link_id, row);
this._delete_link(this.context.data);
}
},
egw.lang('Delete link?')
@ -469,7 +495,15 @@ export class Et2LinkList extends Et2LinkString
protected _handleRowContext(_ev)
{
let _link_data = Object.assign({app: _ev.target.app, id: _ev.target.id}, _ev.target.dataset);
// Do not trigger expose view if one of the operator keys are held
if(_ev.altKey || _ev.ctrlKey || _ev.shiftKey || _ev.metaKey)
{
return;
}
// Find the link
let link = this.querySelector("et2-link[slot='" + _ev.currentTarget.id + "']");
let _link_data = Object.assign({app: link.app, id: link.entry_id}, link.dataset);
// Comment only available if link_id is there and not readonly
this.context.getItem("comment").set_enabled(typeof _link_data.link_id != 'undefined' && !this.readonly);
// File info only available for existing files
@ -488,7 +522,12 @@ export class Et2LinkList extends Et2LinkString
protected _set_comment(link, comment)
{
let remark = this.querySelector(".comment[slot='" + this._get_row_id(link) + "']");
let remark = this.querySelector(".remark[slot='" + this._get_row_id(link) + "']");
if(!remark)
{
console.warn("Could not find link to comment on", link);
return;
}
/* // TODO
if(isNaN(link.link_id)) // new entry, not yet stored
{
@ -518,7 +557,7 @@ export class Et2LinkList extends Et2LinkString
}
*/
remark.addClass("loading");
remark.classList.add("loading");
egw.json("EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_link_comment",
[link.link_id, comment]).sendRequest()
.then(() =>
@ -526,9 +565,10 @@ export class Et2LinkList extends Et2LinkString
if(remark)
{
// Append "" to make sure it's a string, not undefined
remark.removeClass("loading").text(comment + "");
remark.classList.remove("loading");
// Update internal data
remark.textContent = comment + "";
link.comment = comment + "";
remark.value = link.comment;
}
});
}

View File

@ -10,7 +10,7 @@
*/
import {css, html, LitElement, render, repeat, TemplateResult, until} from "@lion/core";
import {css, html, LitElement, render, TemplateResult, until} from "@lion/core";
import {Et2Widget} from "../Et2Widget/Et2Widget";
import {Et2Link, LinkInfo} from "./Et2Link";
import {et2_IDetachedDOM} from "../et2_core_interfaces";
@ -127,7 +127,7 @@ export class Et2LinkString extends Et2Widget(LitElement) implements et2_IDetache
{
this.application = _value.to_app;
this.entry_id = _value.to_id;
this._get_links();
this.get_links();
return;
}
if(typeof _value === "string")
@ -195,18 +195,40 @@ export class Et2LinkString extends Et2Widget(LitElement) implements et2_IDetache
*/
protected _addLinks(links : LinkInfo[])
{
// Remove anything there right now
while(this.lastChild)
{
this.removeChild(this.lastChild);
}
links.forEach((link) =>
{
let temp = document.createElement("div");
render(this._linkTemplate(link), temp);
temp.childNodes.forEach((node) => this.appendChild(node));
})
/*
This should work, and it does, but only once.
It fails if you try and update then run it again - none of the children get added
Something about how lit renders
render(html`${repeat(links,
(link) => link.app + ":" + link.id,
(link) => this._linkTemplate(link))}`,
<HTMLElement><unknown>this
);
*/
}
/**
* Starts the request for link list to the server
*
* Called internally to fetch the list. May be called externally to trigger a refresh if a link is added.
* @protected
*/
protected _get_links()
public get_links()
{
let _value = {
to_app: this.application,

View File

@ -36,6 +36,8 @@ import {et2_IDetachedDOM, et2_IExposable} from "./et2_core_interfaces";
import {expose} from "./expose";
import {egwMenu} from "../egw_action/egw_menu.js";
import {Et2Dialog} from "./Et2Dialog/Et2Dialog";
import {et2_DOMWidget} from "./et2_core_DOMWidget";
import {Et2LinkList} from "./Et2Link/Et2LinkList";
/**
* UI widgets for Egroupware linking system
@ -444,6 +446,8 @@ export class et2_link_to extends et2_inputWidget
},
this, et2_link_list
);
// Update any neighbouring link lists
(<Et2LinkList><unknown>(<et2_DOMWidget>this.getParent()).getDOMNode().querySelector('et2-link-list'))?.get_links();
// If there's an array of data (entry is not yet saved), updating the list will
// not work, so add them in explicitly.

View File

@ -1274,7 +1274,7 @@ ul.et2_link_string {
cursor: pointer;
}
.et2_link_list td.remark {
.et2_link_list td.remark, et2-link-list .remark {
font-style: italic;
}