mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-01-03 04:29:28 +01:00
remove legacy widgets dropdown_button and favorites
This commit is contained in:
parent
d9d14f1e2a
commit
2017bd8261
@ -270,7 +270,7 @@ function send_template()
|
|||||||
$str = preg_replace('#<passwd ([^>]+)(/|></passwd)>#', '<et2-password $1></et2-password>', $str);
|
$str = preg_replace('#<passwd ([^>]+)(/|></passwd)>#', '<et2-password $1></et2-password>', $str);
|
||||||
|
|
||||||
// fix <(button|buttononly|timestamper).../> --> <et2-(button|image|button-timestamp) (noSubmit="true")?.../>
|
// fix <(button|buttononly|timestamper).../> --> <et2-(button|image|button-timestamp) (noSubmit="true")?.../>
|
||||||
$str = preg_replace_callback('#<(button|buttononly|timestamper|button-timestamp)\s(.*?)(/|></(button|buttononly|timestamper|button-timestamp))>#s', function ($matches) use ($name)
|
$str = preg_replace_callback('#<(button|buttononly|timestamper|button-timestamp|dropdown_button)\s(.*?)(/|></(button|buttononly|timestamper|button-timestamp|dropdown_button))>#s', function ($matches) use ($name)
|
||||||
{
|
{
|
||||||
$tag = 'et2-button';
|
$tag = 'et2-button';
|
||||||
$attrs = parseAttrs($matches[2]);
|
$attrs = parseAttrs($matches[2]);
|
||||||
@ -284,6 +284,9 @@ function send_template()
|
|||||||
$tag .= '-timestamp';
|
$tag .= '-timestamp';
|
||||||
$attrs['background_image'] = 'true';
|
$attrs['background_image'] = 'true';
|
||||||
break;
|
break;
|
||||||
|
case 'dropdown_button':
|
||||||
|
$tag = 'et2-dropdown-button';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// novalidation --> noValidation
|
// novalidation --> noValidation
|
||||||
if (!empty($attrs['novalidation']) && in_array($attrs['novalidation'], ['true', '1'], true))
|
if (!empty($attrs['novalidation']) && in_array($attrs['novalidation'], ['true', '1'], true))
|
||||||
|
@ -1,439 +0,0 @@
|
|||||||
/**
|
|
||||||
* EGroupware eTemplate2 - JS Dropdown Button object
|
|
||||||
*
|
|
||||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
||||||
* @package etemplate
|
|
||||||
* @subpackage api
|
|
||||||
* @link https://www.egroupware.org
|
|
||||||
* @author Nathan Gray
|
|
||||||
* @copyright Nathan Gray 2013
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*egw:uses
|
|
||||||
/vendor/bower-asset/jquery/dist/jquery.js;
|
|
||||||
et2_baseWidget;
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {et2_inputWidget} from './et2_core_inputWidget';
|
|
||||||
import {WidgetConfig, et2_register_widget} from "./et2_core_widget";
|
|
||||||
import {ClassWithAttributes} from "./et2_core_inheritance";
|
|
||||||
import {et2_no_init} from "./et2_core_common";
|
|
||||||
import {egw} from "../jsapi/egw_global";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A split button - a button with a dropdown list
|
|
||||||
*
|
|
||||||
* There are several parts to the button UI:
|
|
||||||
* - Container: This is what is percieved as the dropdown button, the whole package together
|
|
||||||
* - Button: The part on the left that can be clicked
|
|
||||||
* - Arrow: The button to display the choices
|
|
||||||
* - Menu: The list of choices
|
|
||||||
*
|
|
||||||
* Menu options are passed via the select_options. They are normally ID => Title pairs,
|
|
||||||
* as for a select box, but the title can also be full HTML if needed.
|
|
||||||
*
|
|
||||||
* @augments et2_inputWidget
|
|
||||||
*/
|
|
||||||
export class et2_dropdown_button extends et2_inputWidget
|
|
||||||
{
|
|
||||||
static readonly attributes : any = {
|
|
||||||
"label": {
|
|
||||||
"name": "caption",
|
|
||||||
"type": "string",
|
|
||||||
"description": "Label of the button",
|
|
||||||
"translate": true,
|
|
||||||
"default": "Select..."
|
|
||||||
},
|
|
||||||
"label_updates": {
|
|
||||||
"name": "Label updates",
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Button label updates when an option is selected from the menu",
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"image": {
|
|
||||||
"name": "Icon",
|
|
||||||
"type": "string",
|
|
||||||
"description": "Add an icon"
|
|
||||||
},
|
|
||||||
"ro_image": {
|
|
||||||
"name": "Read-only Icon",
|
|
||||||
"type": "string",
|
|
||||||
"description": "Use this icon instead of hiding for read-only"
|
|
||||||
},
|
|
||||||
"onclick": {
|
|
||||||
"description": "JS code which gets executed when the button is clicked"
|
|
||||||
},
|
|
||||||
"select_options": {
|
|
||||||
"type": "any",
|
|
||||||
"name": "Select options",
|
|
||||||
"default": {},
|
|
||||||
"description": "Select options for dropdown. Can be a simple key => value list, or value can be full HTML",
|
|
||||||
// Skip normal initialization for this one
|
|
||||||
"ignore": true
|
|
||||||
},
|
|
||||||
"accesskey": {
|
|
||||||
"name": "Access Key",
|
|
||||||
"type": "string",
|
|
||||||
"default": et2_no_init,
|
|
||||||
"description": "Alt + <key> activates widget"
|
|
||||||
},
|
|
||||||
"tabindex": {
|
|
||||||
"name": "Tab index",
|
|
||||||
"type": "integer",
|
|
||||||
"default": et2_no_init,
|
|
||||||
"description": "Specifies the tab order of a widget when the 'tab' button is used for navigating."
|
|
||||||
},
|
|
||||||
// No such thing as a required button
|
|
||||||
"required": {
|
|
||||||
"ignore": true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
internal_ids : any = {
|
|
||||||
div: "",
|
|
||||||
button: "",
|
|
||||||
menu: ""
|
|
||||||
};
|
|
||||||
|
|
||||||
div : JQuery = null;
|
|
||||||
buttons : JQuery = null;
|
|
||||||
button : JQuery = null;
|
|
||||||
arrow : JQuery = null;
|
|
||||||
menu : JQuery = null;
|
|
||||||
image : JQuery = null;
|
|
||||||
clicked : boolean = false;
|
|
||||||
label_updates : boolean = true;
|
|
||||||
value : any = null;
|
|
||||||
/**
|
|
||||||
* Default menu, so there is something for the widget browser / editor to show
|
|
||||||
*/
|
|
||||||
readonly default_menu : string = '<ul> \
|
|
||||||
<li data-id="opt_1.1"><a href="#">Option-1.1</a></li>\
|
|
||||||
<li data-id="opt_1.2"><a href="#">Option-1.2</a></li>\
|
|
||||||
<li data-id="opt_1.3"><a href="#">Option-1.3</a></li>\
|
|
||||||
<li data-id="opt_1.4"><a href="#">Option-1.4<br>\
|
|
||||||
<small>with second line</small>\
|
|
||||||
</a></li>\
|
|
||||||
<li data-id="opt_1.5"><a href="#">Option-1.5</a></li>\
|
|
||||||
</ul>';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @memberOf et2_dropdown_button
|
|
||||||
*/
|
|
||||||
constructor(_parent?, _attrs? : WidgetConfig, _child? : object) {
|
|
||||||
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_dropdown_button._attributes, _child || {}));
|
|
||||||
|
|
||||||
this.clicked = false;
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
// Create the individual UI elements
|
|
||||||
|
|
||||||
// Menu is a UL
|
|
||||||
this.menu = jQuery(this.default_menu).attr("id",this.internal_ids.menu)
|
|
||||||
.hide()
|
|
||||||
.menu({
|
|
||||||
select: function(event,ui) {
|
|
||||||
self.onselect.call(self,event,ui.item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.buttons = jQuery(document.createElement("div"))
|
|
||||||
.addClass("et2_dropdown");
|
|
||||||
|
|
||||||
// Main "wrapper" div
|
|
||||||
this.div = jQuery(document.createElement("div"))
|
|
||||||
.attr("id", this.internal_ids.div)
|
|
||||||
.append(this.buttons)
|
|
||||||
.append(this.menu);
|
|
||||||
|
|
||||||
// Left side - activates click action
|
|
||||||
this.button = jQuery(document.createElement("button"))
|
|
||||||
.attr("id", this.internal_ids.button)
|
|
||||||
.attr("type", "button")
|
|
||||||
.addClass("ui-widget ui-corner-left").removeClass("ui-corner-all")
|
|
||||||
.appendTo(this.buttons);
|
|
||||||
|
|
||||||
// Right side - shows dropdown
|
|
||||||
this.arrow = jQuery(document.createElement("button"))
|
|
||||||
.addClass("ui-widget ui-corner-right").removeClass("ui-corner-all")
|
|
||||||
.attr("type", "button")
|
|
||||||
.click(function() {
|
|
||||||
// ignore click on readonly button
|
|
||||||
if (self.options.readonly) return false;
|
|
||||||
// Clicking it again hides menu
|
|
||||||
if(self.menu.is(":visible"))
|
|
||||||
{
|
|
||||||
self.menu.hide();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Show menu dropdown
|
|
||||||
var menu = self.menu.show().position({
|
|
||||||
my: "left top",
|
|
||||||
at: "left bottom",
|
|
||||||
of: self.buttons
|
|
||||||
});
|
|
||||||
// Hide menu if clicked elsewhere
|
|
||||||
jQuery( document ).one( "click", function() {
|
|
||||||
menu.hide();
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
})
|
|
||||||
// This is the actual down arrow icon
|
|
||||||
.append("<div class='ui-icon ui-icon-triangle-1-s'/>")
|
|
||||||
.appendTo(this.buttons);
|
|
||||||
|
|
||||||
// Common button UI
|
|
||||||
this.buttons.children("button")
|
|
||||||
.addClass("ui-state-default")
|
|
||||||
.hover(
|
|
||||||
function() {jQuery(this).addClass("ui-state-hover");},
|
|
||||||
function() {jQuery(this).removeClass("ui-state-hover");}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Icon
|
|
||||||
this.image = jQuery(document.createElement("img"));
|
|
||||||
|
|
||||||
this.setDOMNode(this.div[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
// Destroy widget
|
|
||||||
if(this.menu && this.menu.data('ui-menu')) this.menu.menu("destroy");
|
|
||||||
|
|
||||||
// Null children
|
|
||||||
this.image = null;
|
|
||||||
this.button = null;
|
|
||||||
this.arrow = null;
|
|
||||||
this.buttons = null;
|
|
||||||
this.menu = null;
|
|
||||||
|
|
||||||
// Remove
|
|
||||||
this.div.empty().remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
set_id(_id) {
|
|
||||||
super.set_id(_id);
|
|
||||||
|
|
||||||
// Update internal IDs - not really needed since we refer by internal
|
|
||||||
// javascript reference, but good to keep up to date
|
|
||||||
this.internal_ids = {
|
|
||||||
div: this.dom_id + "_wrapper",
|
|
||||||
button: this.dom_id,
|
|
||||||
menu: this.dom_id + "_menu"
|
|
||||||
};
|
|
||||||
for(let key in this.internal_ids)
|
|
||||||
{
|
|
||||||
if(this[key] == null) continue;
|
|
||||||
this[key].attr("id", this.internal_ids[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set if the button label changes to match the selected option
|
|
||||||
*
|
|
||||||
* @param updates boolean Turn updating on or off
|
|
||||||
*/
|
|
||||||
set_label_updates(updates)
|
|
||||||
{
|
|
||||||
this.label_updates = updates;
|
|
||||||
}
|
|
||||||
|
|
||||||
set_accesskey(key)
|
|
||||||
{
|
|
||||||
jQuery(this.node).attr("accesskey", key);
|
|
||||||
}
|
|
||||||
|
|
||||||
set_ro_image(_image)
|
|
||||||
{
|
|
||||||
if(this.options.readonly)
|
|
||||||
{
|
|
||||||
this.set_image(_image);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set_image(_image)
|
|
||||||
{
|
|
||||||
if(!this.isInTree() || this.image == null) return;
|
|
||||||
if(!_image.trim())
|
|
||||||
{
|
|
||||||
this.image.hide();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.image.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
let src = this.egw().image(_image);
|
|
||||||
if(src)
|
|
||||||
{
|
|
||||||
this.image.attr("src", src);
|
|
||||||
}
|
|
||||||
// allow url's too
|
|
||||||
else if (_image[0] == '/' || _image.substr(0,4) == 'http')
|
|
||||||
{
|
|
||||||
this.image.attr('src', _image);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.image.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overwritten to maintain an internal clicked attribute
|
|
||||||
*
|
|
||||||
* @param _ev
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
click(_ev)
|
|
||||||
{
|
|
||||||
// ignore click on readonly button
|
|
||||||
if (this.options.readonly) return false;
|
|
||||||
|
|
||||||
this.clicked = true;
|
|
||||||
|
|
||||||
if (!super.click(_ev))
|
|
||||||
{
|
|
||||||
this.clicked = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.clicked = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
onselect(event, selected_node)
|
|
||||||
{
|
|
||||||
this.set_value(selected_node.attr("data-id"));
|
|
||||||
this.change(selected_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
attachToDOM()
|
|
||||||
{
|
|
||||||
let res = super.attachToDOM();
|
|
||||||
|
|
||||||
// Move the parent's handler to the button, or we can't tell the difference between the clicks
|
|
||||||
jQuery(this.node).unbind("click.et2_baseWidget");
|
|
||||||
this.button.off().bind("click.et2_baseWidget", this, function(e) {
|
|
||||||
return e.data.click.call(e.data, this);
|
|
||||||
});
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
set_label(_value)
|
|
||||||
{
|
|
||||||
if (this.button)
|
|
||||||
{
|
|
||||||
this.label = _value;
|
|
||||||
|
|
||||||
this.button.text(_value)
|
|
||||||
.prepend(this.image);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the options for the dropdown
|
|
||||||
*
|
|
||||||
* @param options Object ID => Label pairs
|
|
||||||
*/
|
|
||||||
set_select_options(options) {
|
|
||||||
this.menu.first().empty();
|
|
||||||
|
|
||||||
// Allow more complicated content, if passed
|
|
||||||
if(typeof options == "string")
|
|
||||||
{
|
|
||||||
this.menu.append(options);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
let add_complex = function(node, options)
|
|
||||||
{
|
|
||||||
for(let key in options)
|
|
||||||
{
|
|
||||||
let item;
|
|
||||||
if(typeof options[key] == "string")
|
|
||||||
{
|
|
||||||
item = jQuery("<li data-id='"+key+"'><a href='#'>"+options[key]+"</a></li>");
|
|
||||||
}
|
|
||||||
else if (options[key]["label"])
|
|
||||||
{
|
|
||||||
item =jQuery("<li data-id='"+key+"'><a href='#'>"+options[key]["label"]+"</a></li>");
|
|
||||||
}
|
|
||||||
// Optgroup
|
|
||||||
else
|
|
||||||
{
|
|
||||||
item = jQuery("<li><a href='#'>"+key+"</a></li>");
|
|
||||||
add_complex(node.append("<ul>"), options[key]);
|
|
||||||
}
|
|
||||||
node.append(item);
|
|
||||||
if(item && options[key].icon)
|
|
||||||
{
|
|
||||||
// we supply a applicable class for item images
|
|
||||||
jQuery('a',item).prepend('<img class="et2_button_icon" src="' + (options[key].icon.match(/^(http|https|\/)/) ? options[key].icon : egw.image(options[key].icon)) +'"/>');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
add_complex(this.menu.first(), options);
|
|
||||||
}
|
|
||||||
this.menu.menu("refresh");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set tab index
|
|
||||||
*/
|
|
||||||
set_tabindex(index)
|
|
||||||
{
|
|
||||||
jQuery(this.button).attr("tabindex", index);
|
|
||||||
}
|
|
||||||
|
|
||||||
set_value(new_value)
|
|
||||||
{
|
|
||||||
let menu_item = jQuery("[data-id='"+new_value+"']",this.menu);
|
|
||||||
if(menu_item.length)
|
|
||||||
{
|
|
||||||
this.value = new_value;
|
|
||||||
if(this.label_updates)
|
|
||||||
{
|
|
||||||
this.set_label(menu_item.text());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.value = null;
|
|
||||||
if(this.label_updates)
|
|
||||||
{
|
|
||||||
this.set_label(this.options.label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getValue()
|
|
||||||
{
|
|
||||||
return this.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set options.readonly
|
|
||||||
*
|
|
||||||
* @param {boolean} _ro
|
|
||||||
*/
|
|
||||||
set_readonly(_ro : boolean)
|
|
||||||
{
|
|
||||||
if (_ro != this.options.readonly)
|
|
||||||
{
|
|
||||||
this.options.readonly = _ro;
|
|
||||||
|
|
||||||
// don't make readonly dropdown buttons clickable
|
|
||||||
if (this.buttons)
|
|
||||||
{
|
|
||||||
this.buttons.find('button')
|
|
||||||
.toggleClass('et2_clickable', !_ro)
|
|
||||||
.toggleClass('et2_button_ro', _ro)
|
|
||||||
.css('cursor', _ro ? 'default' : 'pointer');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
et2_register_widget(et2_dropdown_button, ["dropdown_button"]);
|
|
@ -1,433 +0,0 @@
|
|||||||
/**
|
|
||||||
* EGroupware eTemplate2 - JS Favorite widget
|
|
||||||
*
|
|
||||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
||||||
* @package etemplate
|
|
||||||
* @subpackage api
|
|
||||||
* @link https://www.egroupware.org
|
|
||||||
* @author Nathan Gray
|
|
||||||
* @copyright Nathan Gray 2013
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*egw:uses
|
|
||||||
et2_dropdown_button;
|
|
||||||
et2_extension_nextmatch;
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {WidgetConfig} from "./et2_core_widget";
|
|
||||||
import {et2_INextmatchHeader} from "./et2_extension_nextmatch";
|
|
||||||
import {et2_dropdown_button} from "./et2_widget_dropdown_button";
|
|
||||||
import {ClassWithAttributes} from "./et2_core_inheritance";
|
|
||||||
import {egw, egw_getFramework} from "../jsapi/egw_global";
|
|
||||||
import Sortable from 'sortablejs/modular/sortable.complete.esm.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Favorites widget, designed for use with a nextmatch widget
|
|
||||||
*
|
|
||||||
* The primary control is a split/dropdown button. Clicking on the left side of the button filters the
|
|
||||||
* nextmatch list by the user's default filter. The right side of the button gives a list of
|
|
||||||
* saved filters, pulled from preferences. Clicking a filter from the dropdown list sets the
|
|
||||||
* filters as saved.
|
|
||||||
*
|
|
||||||
* Favorites can also automatically be shown in the sidebox, using the special ID favorite_sidebox.
|
|
||||||
* Use the following code to generate the sidebox section:
|
|
||||||
* display_sidebox($appname,lang('Favorites'),array(
|
|
||||||
* array(
|
|
||||||
* 'no_lang' => true,
|
|
||||||
* 'text'=>'<span id="favorite_sidebox"/>',
|
|
||||||
* 'link'=>false,
|
|
||||||
* 'icon' => false
|
|
||||||
* )
|
|
||||||
* ));
|
|
||||||
* This sidebox list will be automatically generated and kept up to date.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Favorites are implemented by saving the values for [column] filters. Filters are stored
|
|
||||||
* in preferences, with the name favorite_<name>. The favorite favorite used for clicking on
|
|
||||||
* the filter button is stored in nextmatch-<columnselection_pref>-favorite.
|
|
||||||
*
|
|
||||||
* @augments et2_dropdown_button
|
|
||||||
*/
|
|
||||||
export class et2_favorites extends et2_dropdown_button implements et2_INextmatchHeader
|
|
||||||
{
|
|
||||||
static readonly _attributes : any = {
|
|
||||||
"default_pref": {
|
|
||||||
"name": "Default preference key",
|
|
||||||
"type": "string",
|
|
||||||
"description": "The preference key where default favorite is stored (not the value)"
|
|
||||||
},
|
|
||||||
"sidebox_target": {
|
|
||||||
"name": "Sidebox target",
|
|
||||||
"type": "string",
|
|
||||||
"description": "ID of element to insert favorite list into",
|
|
||||||
"default": "favorite_sidebox"
|
|
||||||
},
|
|
||||||
"app": {
|
|
||||||
"name": "Application",
|
|
||||||
"type": "string",
|
|
||||||
"description": "Application to show favorites for"
|
|
||||||
},
|
|
||||||
"filters": {
|
|
||||||
"name": "Extra filters",
|
|
||||||
"type": "any",
|
|
||||||
"description": "Array of extra filters to include in the saved favorite"
|
|
||||||
},
|
|
||||||
|
|
||||||
// These are particular to favorites
|
|
||||||
id: {"default": "favorite"},
|
|
||||||
label: {"default": ""},
|
|
||||||
label_updates: { "default": false},
|
|
||||||
image: {"default": egw().image('fav_filter')},
|
|
||||||
statustext: {"default": "Favorite queries", "type": "string"}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Some convenient variables, used in closures / event handlers
|
|
||||||
header = null;
|
|
||||||
nextmatch = null;
|
|
||||||
public static readonly PREFIX = "favorite_";
|
|
||||||
private stored_filters: {};
|
|
||||||
private favSortedList : any = null;
|
|
||||||
private sidebox_target : JQuery = null;
|
|
||||||
private preferred;
|
|
||||||
static is_admin : boolean;
|
|
||||||
private filters : any;
|
|
||||||
|
|
||||||
// If filter was set server side, we need to remember it until nm is created
|
|
||||||
nm_filter = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @memberOf et2_favorites
|
|
||||||
*/
|
|
||||||
constructor(_parent?, _attrs? : WidgetConfig, _child? : object)
|
|
||||||
{
|
|
||||||
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_favorites._attributes, _child || {}));
|
|
||||||
this.sidebox_target = jQuery("#"+this.options.sidebox_target);
|
|
||||||
if(this.sidebox_target.length == 0 && egw_getFramework() != null)
|
|
||||||
{
|
|
||||||
let egw_fw = egw_getFramework();
|
|
||||||
this.sidebox_target = jQuery("#"+this.options.sidebox_target,egw_fw.sidemenuDiv);
|
|
||||||
}
|
|
||||||
// Store array of sorted items
|
|
||||||
this.favSortedList = ['blank'];
|
|
||||||
|
|
||||||
let apps = egw().user('apps');
|
|
||||||
et2_favorites.is_admin = (typeof apps['admin'] != "undefined");
|
|
||||||
|
|
||||||
// Make sure we have an app
|
|
||||||
if(!this.options.app)
|
|
||||||
{
|
|
||||||
this.options.app = this.getInstanceManager().app;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.stored_filters = this.load_favorites(this.options.app);
|
|
||||||
|
|
||||||
this.preferred = egw.preference(this.options.default_pref,this.options.app);
|
|
||||||
if(!this.preferred || typeof this.stored_filters[this.preferred] == "undefined")
|
|
||||||
{
|
|
||||||
this.preferred = "blank";
|
|
||||||
}
|
|
||||||
|
|
||||||
// It helps to have the ID properly set before we get too far
|
|
||||||
this.set_id(this.id);
|
|
||||||
|
|
||||||
this.init_filters(this);
|
|
||||||
|
|
||||||
this.menu.addClass("favorites");
|
|
||||||
|
|
||||||
// Set the default (button) value
|
|
||||||
this.set_value(this.preferred,true);
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
// Add a listener on the radio buttons to set default filter
|
|
||||||
jQuery(this.menu).on("click","input:radio", function(event){
|
|
||||||
// Don't do the menu
|
|
||||||
event.stopImmediatePropagation();
|
|
||||||
|
|
||||||
// Save as default favorite - used when you click the button
|
|
||||||
self.egw().set_preference(self.options.app,self.options.default_pref,jQuery(this).val());
|
|
||||||
self.preferred = jQuery(this).val();
|
|
||||||
|
|
||||||
// Update sidebox, if there
|
|
||||||
if(self.sidebox_target.length)
|
|
||||||
{
|
|
||||||
jQuery("div.ui-icon-heart", self.sidebox_target)
|
|
||||||
.replaceWith("<div class='sideboxstar'/>");
|
|
||||||
jQuery("li[data-id='"+self.preferred+"'] div.sideboxstar",self.sidebox_target)
|
|
||||||
.replaceWith("<div class='ui-icon ui-icon-heart'/>");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the menu
|
|
||||||
self.menu.hide();
|
|
||||||
|
|
||||||
// Some user feedback
|
|
||||||
self.button.addClass("ui-state-active", 500,"swing",function(){
|
|
||||||
self.button.removeClass("ui-state-active",2000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//Sort DomNodes of sidebox fav. menu
|
|
||||||
let sideBoxDOMNodeSort = function (_favSList) {
|
|
||||||
let favS = jQuery.isArray(_favSList)?_favSList.slice(0).reverse():[];
|
|
||||||
|
|
||||||
for (let i=0; i < favS.length;i++)
|
|
||||||
{
|
|
||||||
self.sidebox_target.children().find('[data-id$="' + favS[i] + '"]').prependTo(self.sidebox_target.children());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* todo (@todo-jquery-ui): the sorting does not work at the moment becuase of jquery-ui menu being used in order to create dropdown
|
|
||||||
* buttons menu. Once we replace the et2_widget_dropdown_button with web component this should be adapted
|
|
||||||
* and working again.
|
|
||||||
**/
|
|
||||||
let sortablejs = Sortable.create(this.menu[0], {
|
|
||||||
ghostClass: 'ui-fav-sortable-placeholder',
|
|
||||||
draggable: 'li:not([data-id$="add"])',
|
|
||||||
delay: 25,
|
|
||||||
dataIdAttr:'data-id',
|
|
||||||
onSort: function(event){
|
|
||||||
self.favSortedList = sortablejs.toArray();
|
|
||||||
self.egw.set_preference(self.options.app,'fav_sort_pref', self.favSortedList );
|
|
||||||
sideBoxDOMNodeSort(self.favSortedList);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add a listener on the delete to remove
|
|
||||||
this.menu.on("click","div.ui-icon-trash", app[self.options.app], function() {
|
|
||||||
// App instance might not be ready yet, so don't bind directly
|
|
||||||
app[self.options.app].delete_favorite.apply(this,arguments);
|
|
||||||
})
|
|
||||||
// Wrap and unwrap because jQueryUI styles use a parent, and we don't want to change the state of the menu item
|
|
||||||
// Wrap in a span instead of a div because div gets a border
|
|
||||||
.on("mouseenter","div.ui-icon-trash", function() {jQuery(this).wrap("<span class='ui-state-active'/>");})
|
|
||||||
.on("mouseleave","div.ui-icon-trash", function() {jQuery(this).unwrap();});
|
|
||||||
|
|
||||||
// Trigger refresh of menu options now that events are registered
|
|
||||||
// to update sidebox
|
|
||||||
if(this.sidebox_target.length > 0)
|
|
||||||
{
|
|
||||||
this.init_filters(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load favorites from preferences
|
|
||||||
*
|
|
||||||
* @param app String Load favorites from this application
|
|
||||||
*/
|
|
||||||
load_favorites(app)
|
|
||||||
{
|
|
||||||
|
|
||||||
// Default blank filter
|
|
||||||
let stored_filters : any = {
|
|
||||||
'blank': {
|
|
||||||
name: this.egw().lang("No filters"),
|
|
||||||
state: {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load saved favorites
|
|
||||||
let preferences : any = egw.preference("*",app);
|
|
||||||
for(let pref_name in preferences)
|
|
||||||
{
|
|
||||||
if(pref_name.indexOf(et2_favorites.PREFIX) == 0 && typeof preferences[pref_name] == 'object')
|
|
||||||
{
|
|
||||||
let name = pref_name.substr(et2_favorites.PREFIX.length);
|
|
||||||
stored_filters[name] = preferences[pref_name];
|
|
||||||
// Keep older favorites working - they used to store nm filters in 'filters',not state
|
|
||||||
if(preferences[pref_name]["filters"])
|
|
||||||
{
|
|
||||||
stored_filters[pref_name]["state"] = preferences[pref_name]["filters"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pref_name == 'fav_sort_pref')
|
|
||||||
{
|
|
||||||
this.favSortedList = preferences[pref_name];
|
|
||||||
//Make sure sorted list is always an array, seems some old fav are not array
|
|
||||||
if (!jQuery.isArray(this.favSortedList)) this.favSortedList = this.favSortedList.split(',');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(typeof stored_filters == "undefined" || !stored_filters)
|
|
||||||
{
|
|
||||||
stored_filters = {};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for(let name in stored_filters)
|
|
||||||
{
|
|
||||||
if (this.favSortedList.indexOf(name) < 0)
|
|
||||||
{
|
|
||||||
this.favSortedList.push(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.egw().set_preference (this.options.app,'fav_sort_pref',this.favSortedList);
|
|
||||||
if (this.favSortedList.length > 0)
|
|
||||||
{
|
|
||||||
let sortedListObj = {};
|
|
||||||
|
|
||||||
for (let i=0; i < this.favSortedList.length; i++)
|
|
||||||
{
|
|
||||||
if (typeof stored_filters[this.favSortedList[i]] != 'undefined')
|
|
||||||
{
|
|
||||||
sortedListObj[this.favSortedList[i]] = stored_filters[this.favSortedList[i]];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.favSortedList.splice(i,1);
|
|
||||||
this.egw().set_preference (this.options.app,'fav_sort_pref',this.favSortedList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stored_filters = jQuery.extend(sortedListObj,stored_filters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return stored_filters;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create & set filter options for dropdown menu
|
|
||||||
init_filters(widget, filters?)
|
|
||||||
{
|
|
||||||
if(typeof filters == "undefined")
|
|
||||||
{
|
|
||||||
filters = this.stored_filters;
|
|
||||||
}
|
|
||||||
|
|
||||||
let options = {};
|
|
||||||
for(let name in filters)
|
|
||||||
{
|
|
||||||
options[name] = "<input type='radio' name='"+this.internal_ids.menu+"[button][favorite]' value='"+name+"' title='" +
|
|
||||||
this.egw().lang('Set as default') + "'/>"+
|
|
||||||
(filters[name].name != undefined ? filters[name].name : name) +
|
|
||||||
(filters[name].group != false && !et2_favorites.is_admin || name == 'blank' ? "" :
|
|
||||||
"<div class='ui-icon ui-icon-trash' title='" + this.egw().lang('Delete') + "'/>");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only add 'Add current' if we have a nextmatch
|
|
||||||
if(this.nextmatch)
|
|
||||||
{
|
|
||||||
options["add"] = "<img src='"+this.egw().image("new") +"'/>"+this.egw().lang('Add current');
|
|
||||||
}
|
|
||||||
widget.set_select_options.call(widget,options);
|
|
||||||
|
|
||||||
// Set radio to current value
|
|
||||||
jQuery("input[value='"+ this.preferred +"']:radio", this.menu).attr("checked",1);
|
|
||||||
}
|
|
||||||
|
|
||||||
set_nm_filters(filters)
|
|
||||||
{
|
|
||||||
if(this.nextmatch)
|
|
||||||
{
|
|
||||||
this.nextmatch.applyFilters(filters);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
console.log(filters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onclick(node)
|
|
||||||
{
|
|
||||||
// Apply preferred filter - make sure it's an object, and not a reference
|
|
||||||
if(this.preferred && this.stored_filters[this.preferred])
|
|
||||||
{
|
|
||||||
// use app[appname].setState if available to allow app to overwrite it (eg. change to non-listview in calendar)
|
|
||||||
if (typeof app[this.options.app] != 'undefined')
|
|
||||||
{
|
|
||||||
app[this.options.app].setState(this.stored_filters[this.preferred]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.set_nm_filters(jQuery.extend({},this.stored_filters[this.preferred].state));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
alert(this.egw().lang("No default set"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply the favorite when you pick from the list
|
|
||||||
change(selected_node)
|
|
||||||
{
|
|
||||||
this.value = jQuery(selected_node).attr("data-id");
|
|
||||||
if(this.value == "add" && this.nextmatch)
|
|
||||||
{
|
|
||||||
// Get current filters
|
|
||||||
let current_filters = jQuery.extend({},this.nextmatch.activeFilters);
|
|
||||||
|
|
||||||
// Add in extras
|
|
||||||
for(let extra in this.options.filters)
|
|
||||||
{
|
|
||||||
// Don't overwrite what nm has, chances are nm has more up-to-date value
|
|
||||||
if(typeof current_filters == 'undefined')
|
|
||||||
{
|
|
||||||
current_filters[extra] = this.nextmatch.options.settings[extra];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip columns for now
|
|
||||||
delete current_filters.selcolumns;
|
|
||||||
|
|
||||||
// Add in application's settings
|
|
||||||
if(this.filters != true)
|
|
||||||
{
|
|
||||||
for(let i = 0; i < this.filters.length; i++)
|
|
||||||
{
|
|
||||||
current_filters[this.options.filters[i]] = this.nextmatch.options.settings[this.options.filters[i]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call framework
|
|
||||||
app[this.options.app].add_favorite(current_filters);
|
|
||||||
|
|
||||||
// Reset value
|
|
||||||
this.set_value(this.preferred,true);
|
|
||||||
}
|
|
||||||
else if (this.value == 'blank')
|
|
||||||
{
|
|
||||||
// Reset filters when select no filters
|
|
||||||
this.set_nm_filters({});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set_value(filter_name, parent? : boolean) : void | boolean
|
|
||||||
{
|
|
||||||
if(parent)
|
|
||||||
{
|
|
||||||
return super.set_value(filter_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(filter_name == 'add') return false;
|
|
||||||
|
|
||||||
app[this.options.app].setState(this.stored_filters[filter_name]);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
getValue()
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the nextmatch to filter
|
|
||||||
* From et2_INextmatchHeader interface
|
|
||||||
*
|
|
||||||
* @param {et2_nextmatch} nextmatch
|
|
||||||
*/
|
|
||||||
setNextmatch(nextmatch)
|
|
||||||
{
|
|
||||||
this.nextmatch = nextmatch;
|
|
||||||
|
|
||||||
if(this.nm_filter)
|
|
||||||
{
|
|
||||||
this.set_value(this.nm_filter);
|
|
||||||
this.nm_filter = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-generate filter list so we can add 'Add current'
|
|
||||||
this.init_filters(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -112,9 +112,7 @@ import './et2_widget_radiobox';
|
|||||||
import './et2_widget_date';
|
import './et2_widget_date';
|
||||||
import './et2_widget_dialog';
|
import './et2_widget_dialog';
|
||||||
import './et2_widget_diff';
|
import './et2_widget_diff';
|
||||||
import './et2_widget_dropdown_button';
|
|
||||||
import './et2_widget_styles';
|
import './et2_widget_styles';
|
||||||
import './et2_widget_favorites';
|
|
||||||
import './et2_widget_html';
|
import './et2_widget_html';
|
||||||
import './et2_widget_htmlarea';
|
import './et2_widget_htmlarea';
|
||||||
import './et2_widget_taglist';
|
import './et2_widget_taglist';
|
||||||
|
@ -8,19 +8,15 @@
|
|||||||
* @author Nathan Gray
|
* @author Nathan Gray
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*egw:uses
|
|
||||||
egw_inheritance;
|
|
||||||
/api/js/es6-promise.min.js;
|
|
||||||
*/
|
|
||||||
import {EgwApp} from "./egw_app";
|
import {EgwApp} from "./egw_app";
|
||||||
import './egw_inheritance.js';
|
import './egw_inheritance.js';
|
||||||
import {etemplate2} from "../etemplate/etemplate2";
|
import {etemplate2} from "../etemplate/etemplate2";
|
||||||
import {et2_createWidget} from "../etemplate/et2_core_widget";
|
import {et2_createWidget} from "../etemplate/et2_core_widget";
|
||||||
import {Et2Dialog} from "../etemplate/Et2Dialog/Et2Dialog";
|
import {Et2Dialog} from "../etemplate/Et2Dialog/Et2Dialog";
|
||||||
import {et2_nextmatch} from "../etemplate/et2_extension_nextmatch";
|
import {et2_nextmatch} from "../etemplate/et2_extension_nextmatch";
|
||||||
import {et2_favorites} from "../etemplate/et2_widget_favorites";
|
|
||||||
import {EGW_KEY_ENTER} from "../egw_action/egw_action_constants";
|
import {EGW_KEY_ENTER} from "../egw_action/egw_action_constants";
|
||||||
import "sortablejs/Sortable.min.js";
|
import "sortablejs/Sortable.min.js";
|
||||||
|
import {Et2Favorites} from "../etemplate/Et2Favorites/Et2Favorites";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common base class for application javascript
|
* Common base class for application javascript
|
||||||
@ -736,7 +732,7 @@ export const AppJS = (function(){ "use strict"; return Class.extend(
|
|||||||
et2[i].widgetContainer.iterateOver(function(_widget) {
|
et2[i].widgetContainer.iterateOver(function(_widget) {
|
||||||
_widget.stored_filters = _widget.load_favorites(self.appname);
|
_widget.stored_filters = _widget.load_favorites(self.appname);
|
||||||
_widget.init_filters(_widget);
|
_widget.init_filters(_widget);
|
||||||
}, self, et2_favorites);
|
}, self, Et2Favorites);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -14,13 +14,13 @@ import {etemplate2} from "../etemplate/etemplate2";
|
|||||||
import type {et2_container} from "../etemplate/et2_core_baseWidget";
|
import type {et2_container} from "../etemplate/et2_core_baseWidget";
|
||||||
import {et2_nextmatch} from "../etemplate/et2_extension_nextmatch";
|
import {et2_nextmatch} from "../etemplate/et2_extension_nextmatch";
|
||||||
import {et2_createWidget} from "../etemplate/et2_core_widget";
|
import {et2_createWidget} from "../etemplate/et2_core_widget";
|
||||||
import {et2_favorites} from "../etemplate/et2_widget_favorites";
|
|
||||||
import type {IegwAppLocal} from "./egw_global";
|
import type {IegwAppLocal} from "./egw_global";
|
||||||
import Sortable from 'sortablejs/modular/sortable.complete.esm.js';
|
import Sortable from 'sortablejs/modular/sortable.complete.esm.js';
|
||||||
import {et2_valueWidget} from "../etemplate/et2_core_valueWidget";
|
import {et2_valueWidget} from "../etemplate/et2_core_valueWidget";
|
||||||
import {nm_action} from "../etemplate/et2_extension_nextmatch_actions";
|
import {nm_action} from "../etemplate/et2_extension_nextmatch_actions";
|
||||||
import {Et2Dialog} from "../etemplate/Et2Dialog/Et2Dialog";
|
import {Et2Dialog} from "../etemplate/Et2Dialog/Et2Dialog";
|
||||||
import {EGW_KEY_ENTER} from "../egw_action/egw_action_constants";
|
import {EGW_KEY_ENTER} from "../egw_action/egw_action_constants";
|
||||||
|
import {Et2Favorites} from "../etemplate/Et2Favorites/Et2Favorites";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type for push-message
|
* Type for push-message
|
||||||
@ -973,7 +973,7 @@ export abstract class EgwApp
|
|||||||
{
|
{
|
||||||
_widget.stored_filters = _widget.load_favorites(self.appname);
|
_widget.stored_filters = _widget.load_favorites(self.appname);
|
||||||
_widget.init_filters(_widget);
|
_widget.init_filters(_widget);
|
||||||
}, self, et2_favorites);
|
}, self, Et2Favorites);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -57,7 +57,7 @@
|
|||||||
<buttononly statustext="Tile view" id="button[change_view]" onclick="app.filemanager.change_view" image="list_tile" background_image="true"/>
|
<buttononly statustext="Tile view" id="button[change_view]" onclick="app.filemanager.change_view" image="list_tile" background_image="true"/>
|
||||||
</template>
|
</template>
|
||||||
<template id="filemanager.index.header_right" template="" lang="" group="0" version="1.9.003">
|
<template id="filemanager.index.header_right" template="" lang="" group="0" version="1.9.003">
|
||||||
<et2-dropdown-button id="new" onchange="app.filemanager.create_new" label="New"
|
<dropdown_button id="new" onchange="app.filemanager.create_new" label="New"
|
||||||
onclick="app.filemanager.create_new"/>
|
onclick="app.filemanager.create_new"/>
|
||||||
<buttononly statustext="Rename, change permissions or ownership" id="button[edit]" onclick="app.filemanager.editprefs();" image="edit" background_image="true"/>
|
<buttononly statustext="Rename, change permissions or ownership" id="button[edit]" onclick="app.filemanager.editprefs();" image="edit" background_image="true"/>
|
||||||
<buttononly statustext="Create directory" id="button[createdir]" onclick="app.filemanager.createdir();" image="button_createdir" background_image="true"/>
|
<buttononly statustext="Create directory" id="button[createdir]" onclick="app.filemanager.createdir();" image="button_createdir" background_image="true"/>
|
||||||
|
@ -505,7 +505,7 @@ div#stylite-filemanager-upload .dialogFooterToolbar button:active {
|
|||||||
height: 60px !important;
|
height: 60px !important;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
background-image: url(../../../pixelegg/images/topmenu_items/mobile/plus_white.svg) !important;
|
background-image: url(../../../api/templates/default/images/topmenu_items/mobile/plus_white.svg) !important;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: 24px 24px;
|
background-size: 24px 24px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -108,7 +108,7 @@
|
|||||||
height: 60px !important;
|
height: 60px !important;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
background-image: url(../../../pixelegg/images/topmenu_items/mobile/plus_white.svg) !important;
|
background-image: url(../../../api/templates/default/images/topmenu_items/mobile/plus_white.svg) !important;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: 24px 24px;
|
background-size: 24px 24px;
|
||||||
border: none;
|
border: none;
|
||||||
|
Loading…
Reference in New Issue
Block a user