mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-22 14:41:29 +01:00
Start of a link-to widget using jQuery-UI's autocomplete
This commit is contained in:
parent
65a37234c4
commit
d9f321a413
109
etemplate/inc/class.etemplate_widget_link.inc.php
Normal file
109
etemplate/inc/class.etemplate_widget_link.inc.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/**
|
||||
* EGroupware - eTemplate serverside of linking widgets
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package etemplate
|
||||
* @subpackage api
|
||||
* @link http://www.egroupware.org
|
||||
* @author Nathan Gray
|
||||
* @copyright 2011 Nathan Gray
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* eTemplate link widgets
|
||||
* Deals with creation and display of links between entries in various participating egw applications
|
||||
*/
|
||||
class etemplate_widget_link extends etemplate_widget
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string|XMLReader $xml string with xml or XMLReader positioned on the element to construct
|
||||
* @throws egw_exception_wrong_parameter
|
||||
*/
|
||||
public function __construct($xml = '')
|
||||
{
|
||||
if($xml) {
|
||||
parent::__construct($xml);
|
||||
|
||||
// TODO: probably a better way to do this
|
||||
egw_framework::includeCSS('/phpgwapi/js/jquery/jquery-ui/smoothness/jquery-ui-1.8.16.custom.css');
|
||||
}
|
||||
}
|
||||
|
||||
/* Changes all link widgets to template
|
||||
protected static $transformation = array(
|
||||
'type' => array(
|
||||
'link-list'=>array(
|
||||
'value' => array('__callback__'=>'get_links'),
|
||||
'type' => 'template',
|
||||
'id' => 'etemplate.link_widget.list'
|
||||
)
|
||||
),
|
||||
);
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set up what we know on the server side.
|
||||
*
|
||||
* Set the options for the application select.
|
||||
*
|
||||
* @param string $cname
|
||||
*/
|
||||
public function beforeSendToClient($cname)
|
||||
{
|
||||
$attrs = $this->attrs;
|
||||
$form_name = self::form_name($cname, $this->id);
|
||||
$value =& self::get_array(self::$request->content, $form_name, true);
|
||||
|
||||
if(!is_array($value))
|
||||
{
|
||||
throw new egw_exception_wrong_parameter("Wrong value sent to link widget, needs to be an array. " + array2string($value));
|
||||
}
|
||||
|
||||
$app = $value['to_app'];
|
||||
$id = $value['to_id'];
|
||||
|
||||
$help = $attrs['help'] ? ($value['help'] ? $value['help'] : $attrs['help']) : lang('view this linked entry in its application');
|
||||
self::setElementAttribute($cname, 'help', $help);
|
||||
if($this->type == 'link-to') {
|
||||
if (!is_array(self::$request->sel_options[$form_name])) self::$request->sel_options[$form_name] = array();
|
||||
$apps = egw_link::app_list($cell['size'] ? $cell['size'] : 'query');
|
||||
asort($apps);
|
||||
self::$request->sel_options[$form_name] += $apps;
|
||||
}
|
||||
|
||||
if($attrs['type'] == 'link-list') {
|
||||
$links = egw_link::get_links($app,$id,'','link_lastmod DESC',true, $value['show_deleted']);
|
||||
_debug_array($links);
|
||||
foreach($links as $link) {
|
||||
$value[] = $link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find links that match the given parameters
|
||||
*/
|
||||
public static function ajax_link_search($app, $type, $pattern, $options=array()) {
|
||||
$options['type'] = $type ? $type : $options['type'];
|
||||
error_log("$app, $pattern, $options");
|
||||
$links = egw_link::query($app, $pattern, $options);
|
||||
|
||||
$response = egw_json_response::get();
|
||||
$response->data($links);
|
||||
}
|
||||
|
||||
public function get_links($value) {
|
||||
|
||||
$app = $value['to_app'];
|
||||
$id = $value['to_id'];
|
||||
|
||||
$links = egw_link::get_links($app,$id,'','link_lastmod DESC',true, $value['show_deleted']);
|
||||
_debug_array($links);
|
||||
return $links;
|
||||
}
|
||||
}
|
203
etemplate/js/et2_widget_link.js
Normal file
203
etemplate/js/et2_widget_link.js
Normal file
@ -0,0 +1,203 @@
|
||||
/**
|
||||
* eGroupWare eTemplate2 - JS Link object
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package etemplate
|
||||
* @subpackage api
|
||||
* @link http://www.egroupware.org
|
||||
* @author Nathan Gray
|
||||
* @copyright 2011 Nathan Gray
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/*egw:uses
|
||||
jquery.jquery;
|
||||
jquery.jquery-ui;
|
||||
et2_core_inputWidget;
|
||||
et2_core_valueWidget;
|
||||
*/
|
||||
|
||||
/**
|
||||
* UI widgets for Egroupware linking system
|
||||
*/
|
||||
var et2_link_to = et2_inputWidget.extend({
|
||||
|
||||
attributes: {
|
||||
"application": {
|
||||
"name": "Application",
|
||||
"type": "string",
|
||||
"default": egw_getAppName(),
|
||||
"description": "Limit to the listed application or applications (comma seperated)"
|
||||
},
|
||||
"blur": {
|
||||
"name": "Placeholder",
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "This text get displayed if an input-field is empty and does not have the input-focus (blur). It can be used to show a default value or a kind of help-text."
|
||||
},
|
||||
},
|
||||
|
||||
search_timeout: 200, //ms after change to send query
|
||||
minimum_characters: 2, // Don't send query unless there's at least this many chars
|
||||
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
this.div = null;
|
||||
this.search = null;
|
||||
this.app_select = null;
|
||||
|
||||
this.cache = {};
|
||||
|
||||
this.createInputWidget();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
this.div = null;
|
||||
this.search.autocomplete("destroy");
|
||||
this.search = null;
|
||||
this.app_select = null;
|
||||
|
||||
this.createInputWidget();
|
||||
},
|
||||
|
||||
createInputWidget: function() {
|
||||
var self = this;
|
||||
this.div = $j(document.createElement("div"));
|
||||
|
||||
// Application selection
|
||||
this.app_select = $j(document.createElement("select")).appendTo(this.div)
|
||||
.change(function(e) {
|
||||
self.cache = {}; // Clear cache when app changes
|
||||
self.options.value.app = this.val();
|
||||
});
|
||||
for(var key in this.options.select_options) {
|
||||
var option = $j(document.createElement("option"))
|
||||
.attr("value", key)
|
||||
.text(this.options.select_options[key]);
|
||||
option.appendTo(this.app_select);
|
||||
}
|
||||
self.options.value.app = this.app_select.val();
|
||||
|
||||
// Search input
|
||||
this.search = $j(document.createElement("input")).attr("type", "search").appendTo(this.div);
|
||||
|
||||
// Link button
|
||||
this.link_button = $j(document.createElement("button"))
|
||||
.text(egw.lang("link"))
|
||||
.appendTo(this.div).hide()
|
||||
.click(this, this.createLink);
|
||||
|
||||
this.set_blur(this.blur ? this.blur : egw.lang("search"));
|
||||
|
||||
this.search.autocomplete({
|
||||
source: function(request, response) { return self.query(request, response);},
|
||||
select: function(event, item) { event.data = self; self.select(event,item); return false;},
|
||||
focus: function(event, item) {
|
||||
event.stopPropagation();
|
||||
self.search.val(item.item.label);
|
||||
return false;
|
||||
},
|
||||
minLength: self.minimum_characters,
|
||||
disabled: self.options.disabled
|
||||
});
|
||||
this.setDOMNode(this.div[0]);
|
||||
},
|
||||
|
||||
transformAttributes: function(_attrs) {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
// Try to find the options inside the "sel-options" array
|
||||
_attrs["select_options"] = this.getArrayMgr("sel_options").getValueForID(this.id);
|
||||
|
||||
// Check whether the options entry was found, if not read it from the
|
||||
// content array.
|
||||
if (_attrs["select_options"] == null)
|
||||
{
|
||||
_attrs["select_options"] = this.getArrayMgr('content')
|
||||
.getValueForID("options-" + this.id)
|
||||
}
|
||||
|
||||
// Default to an empty object
|
||||
if (_attrs["select_options"] == null)
|
||||
{
|
||||
_attrs["select_options"] = {};
|
||||
}
|
||||
},
|
||||
|
||||
getValue: function() {
|
||||
return this.options.value;
|
||||
},
|
||||
|
||||
set_blur: function(_value) {
|
||||
if(_value) {
|
||||
this.search.attr("placeholder", _value); // HTML5
|
||||
if(!this.search[0].placeholder) {
|
||||
// Not HTML5
|
||||
if(this.search.val() == "") this.search.val(this.options.blur);
|
||||
this.search.focus(this,function(e) {
|
||||
if(e.data.search.val() == e.data.options.blur) e.data.search.val("");
|
||||
}).blur(this, function(e) {
|
||||
if(e.data.search.val() == "") e.data.search.val(e.data.options.blur);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.search.removeAttr("placeholder");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Ask server for entries matching selected app/type and filtered by search string
|
||||
*/
|
||||
query: function(request, response) {
|
||||
if(request.term in this.cache) {
|
||||
return response(this.cache[request.term]);
|
||||
}
|
||||
this.search.addClass("loading");
|
||||
this.link_button.hide();
|
||||
var request = new egw_json_request("etemplate_widget_link::ajax_link_search::etemplate",
|
||||
[this.app_select.val(), '', request.term],
|
||||
this
|
||||
);
|
||||
this.response = response;
|
||||
request.sendRequest(true, this._results, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* User selected a value
|
||||
*/
|
||||
select: function(event, selected) {
|
||||
event.data.options.value.id = selected.item.value;
|
||||
event.data.search.val(selected.item.label);
|
||||
event.data.link_button.show();
|
||||
},
|
||||
|
||||
/**
|
||||
* Server found some results
|
||||
*/
|
||||
_results: function(data) {
|
||||
this.search.removeClass("loading");
|
||||
var result = [];
|
||||
for(var id in data) {
|
||||
result.push({"value": id, "label":data[id]});
|
||||
}
|
||||
this.cache[this.search.val()] = result;
|
||||
this.response(result);
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a link using the current internal values
|
||||
*/
|
||||
createLink: function(event) {
|
||||
console.info("Link: ",event.data.options.value);
|
||||
event.data.link_button.hide();
|
||||
}
|
||||
});
|
||||
|
||||
et2_register_widget(et2_link_to, ["link-to"]);
|
||||
|
||||
|
@ -30,8 +30,8 @@
|
||||
et2_widget_tabs;
|
||||
et2_widget_hrule;
|
||||
et2_widget_image;
|
||||
et2_widget_upload;
|
||||
et2_widget_file;
|
||||
et2_widget_link;
|
||||
|
||||
et2_extension_nextmatch;
|
||||
|
||||
|
@ -190,6 +190,13 @@ span.et2_date span {
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
/** Display a loading icon **/
|
||||
.loading {
|
||||
background-position: center right;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url('gfx/ajax-loader.gif');
|
||||
}
|
||||
|
||||
/**
|
||||
* File upload
|
||||
*/
|
||||
@ -237,6 +244,19 @@ span.et2_date span {
|
||||
.et2_file .progress li.success > span.progressBar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Autocomplete - used in link widget
|
||||
* Restricting result size
|
||||
*/
|
||||
.ui-autocomplete {
|
||||
max-height: 20ex;
|
||||
overflow-y: auto;
|
||||
/* prevent horizontal scrollbar */
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.egw_tooltip
|
||||
{
|
||||
position: fixed;
|
||||
|
Loading…
Reference in New Issue
Block a user