implement et2-searchbox and using it in NM

reorganized preprocessor a bit to fix some not replaced size attributes
also pass on constructor args in all Et2Url widgets

There is still a JS error in new searchbox, causing admin searchbox to not render :(
This commit is contained in:
ralf 2022-07-22 20:43:09 +02:00
parent 3cb8b1ecce
commit ac0867ab77
13 changed files with 133 additions and 64 deletions

View File

@ -13,11 +13,12 @@
use EGroupware\Api;
// add et2- prefix to following widgets/tags, if NO <overlay legacy="true"
const ADD_ET2_PREFIX_REGEXP = '#<((/?)([vh]?box|date(-time[^\s]*|-duration|-since)?|textbox|textarea|button|colorpicker|url(-email|-phone|-fax)?))(/?|\s[^>]*)>#m';
const ADD_ET2_PREFIX_REGEXP = '#<((/?)([vh]?box|date(-time[^\s]*|-duration|-since)?|button|colorpicker|url(-email|-phone|-fax)?))(/?|\s[^>]*)>#m';
const ADD_ET2_PREFIX_LAST_GROUP = 6;
// unconditional of legacy add et2- prefix to this widgets
const ADD_ET2_PREFIX_LEGACY_REGEXP = '#<(description|label|avatar|lavatar|image|vfs-mime|vfs-uid|vfs-gid|link|link-[a-z]+|favorites)\s([^>]+)/>#m';
const ADD_ET2_PREFIX_LEGACY_REGEXP = '#<(description|searchbox|label|avatar|lavatar|image|vfs-mime|vfs-uid|vfs-gid|link|link-[a-z]+|favorites)\s([^>]+)/>#m';
const ADD_ET2_PREFIX_LEGACY_LAST_GROUP = 2;
// switch evtl. set output-compression off, as we can't calculate a Content-Length header with transparent compression
ini_set('zlib.output_compression', 0);
@ -160,8 +161,26 @@ function send_template()
$str = preg_replace('/<(image|description)\s([^><]*)expose_view="true"\s([^><]*)\\/>/',
'<et2-$1-expose $2 $3></et2-$1-expose>', $str);
// fix <textbox multiline="true" .../> --> <et2-textarea .../>
$str = preg_replace('#<textbox(.*?)\smultiline="true"(.*?)/>#', '<et2-textarea$1$2></et2-textarea>', $str);
// fix <(textbox|int(eger)?|float) precision="int(eger)?|float" .../> --> <et2-number precision=.../> or <et2-textbox .../>
$str = preg_replace_callback('#<(textbox|int(eger)?|float|number).*?\s(type="(int(eger)?|float)")?.*?(/|></textbox)>#',
static function ($matches)
{
if ($matches[1] === 'textbox' && !in_array($matches[4], ['float', 'int', 'integer'], true))
{
return '<et2-'.substr($matches[0], 1, -strlen($matches[6])-1).'></et2-textbox>'; // regular textbox --> nothing to do
}
$type = $matches[1] === 'float' || $matches[4] === 'float' ? 'float' : 'int';
$tag = str_replace('<' . $matches[1], '<et2-number', substr($matches[0], 0, -2));
if (!empty($matches[3])) $tag = str_replace($matches[3], '', $tag);
if ($type !== 'float') $tag .= ' precision="0"';
return $tag . '></et2-number>';
}, $str);
// modify <(vfs-mime|link-string|link-list) --> <et2-*
$str = preg_replace(ADD_ET2_PREFIX_LEGACY_REGEXP, '<et2-$1 $2></et2-$1>',
$str = preg_replace(ADD_ET2_PREFIX_LEGACY_REGEXP, '<et2-$1 $'.ADD_ET2_PREFIX_LEGACY_LAST_GROUP.'></et2-$1>',
str_replace('<description/>', '<et2-description></et2-description>', $str));
// change link attribute only_app to et2-link attribute app and map r/o link-entry to link
@ -278,33 +297,6 @@ function send_template()
}
else
{
// fix deprecated attributes: needed, blur, ...
static $deprecated = [
'needed' => 'required',
'blur' => 'placeholder',
];
$str = preg_replace_callback('#<[^ ]+[^>]* (' . implode('|', array_keys($deprecated)) . ')="([^"]+)"[ />]#',
static function ($matches) use ($deprecated) {
return str_replace($matches[1] . '="', $deprecated[$matches[1]] . '="', $matches[0]);
}, $str);
// fix <textbox multiline="true" .../> --> <textarea .../> (et2-prefix and self-closing is handled below)
$str = preg_replace('#<textbox(.*?)\smultiline="true"(.*?)/>#u', '<textarea$1$2/>', $str);
// fix <(textbox|int(eger)?|float) precision="int(eger)?|float" .../> --> <et2-number precision=...></et2-number>
$str = preg_replace_callback('#<(textbox|int(eger)?|float|number).*?\s(type="(int(eger)?|float)")?.*?/>#u',
static function ($matches) {
if ($matches[1] === 'textbox' && !in_array($matches[4], ['float', 'int', 'integer'], true))
{
return $matches[0]; // regular textbox --> nothing to do
}
$type = $matches[1] === 'float' || $matches[4] === 'float' ? 'float' : 'int';
$tag = str_replace('<' . $matches[1], '<et2-number', substr($matches[0], 0, -2));
if (!empty($matches[3])) $tag = str_replace($matches[3], '', $tag);
if ($type !== 'float') $tag .= ' precision="0"';
return $tag . '></et2-number>';
}, $str);
// fix <button(only)?.../> --> <et2-button(-image)? noSubmit="true".../>
$str = preg_replace_callback('#<button(only)?\s(.*?)/>#u', function ($matches) use ($name) {
$tag = 'et2-button';
@ -354,8 +346,18 @@ function send_template()
preg_match_all('/(^| )([a-z\d_-]+)="([^"]+)"/i', $matches[3], $attrs, PREG_PATTERN_ORDER);
$attrs = array_combine($attrs[2], $attrs[3]);
// fix deprecated attributes: needed, blur, ...
static $deprecated = [
'needed' => 'required',
'blur' => 'placeholder',
];
foreach($attrs as $name => $value)
{
if (isset($deprecated[$name]))
{
unset($attrs[$name]);
$attrs[$name = $deprecated[$name]] = $value;
}
if (count($parts = preg_split('/[_-]/', $name)) > 1)
{
if ($name === 'parent_node') $parts[1] = 'Id'; // we can not use DOM property parentNode --> parentId

View File

@ -38,9 +38,9 @@ export class Et2Password extends Et2InvokerMixin(Et2Textbox)
};
}
constructor()
constructor(...args : any[])
{
super();
super(...args);
this.plaintext = true;
this.suggest = 0;

View File

@ -0,0 +1,81 @@
/**
* EGroupware eTemplate2 - Searchbox widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @link https://www.egroupware.org
* @author Ralf Becker
*/
/* eslint-disable import/no-extraneous-dependencies */
import {Et2Textbox} from "./Et2Textbox";
/**
* @customElement et2-searchbox
*/
export class Et2Searchbox extends Et2Textbox
{
/** @type {any} */
static get properties()
{
return {
...super.properties,
/**
* Define whether the searchbox overlays while it's open (true) or stay as solid box in front of the search button (false). Default is false.
* @todo implement again
*/
overlay: Boolean,
/**
* Define whether the searchbox should be a fix input field or flexible search button. Default is true (fix).
* @todo implement again
*/
fix: Boolean,
};
}
constructor(...args : any[])
{
super(...args);
this.overlay = false;
this.fix = true;
this.clearable = true;
this.type = 'search';
this.enterkeyhint = 'search';
}
/**
* Overwritten to trigger a change/search
*
* @param event
*/
handleKeyDown(event: KeyboardEvent)
{
const hasModifier = event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
// Pressing enter when focused on an input should submit the form like a native input, but we wait a tick before
// submitting to allow users to cancel the keydown event if they need to
if (event.key === 'Enter' && !hasModifier)
{
event.preventDefault();
this._oldChange(event);
}
}
/**
* Overwritten to trigger a change/search
*
* @param event
*/
handleClearClick(event : MouseEvent)
{
event.preventDefault();
this.value = '';
this._oldChange(event);
}
}
// @ts-ignore TypeScript is not recognizing that this is a LitElement
customElements.define("et2-searchbox", Et2Searchbox);

View File

@ -56,9 +56,10 @@ export class Et2Url extends Et2InvokerMixin(Et2Textbox)
];
}
constructor()
constructor(...args : any[])
{
super();
super(...args);
this._invokerLabel = '⎆';
this._invokerTitle = 'Open';
this._invokerAction = () => {

View File

@ -34,9 +34,10 @@ export class Et2UrlEmail extends Et2InvokerMixin(Et2Textbox)
];
}
constructor()
constructor(...args : any[])
{
super();
super(...args);
this.defaultValidators.push(new IsEmail());
this._invokerLabel = '@';
this._invokerTitle = 'Compose mail to';

View File

@ -73,11 +73,6 @@ export class Et2UrlEmailReadonly extends Et2UrlReadonly
return super.value;
}
constructor()
{
super();
}
transformAttributes(attrs)
{
if (typeof attrs.onclick === 'undefined')

View File

@ -34,9 +34,10 @@ export class Et2UrlFax extends Et2UrlPhone
];
}
constructor()
constructor(...args : any[])
{
super();
super(...args);
//this.defaultValidators.push(...);
this._invokerLabel = '📠';
this._invokerTitle = 'Send';

View File

@ -16,11 +16,6 @@ import {Et2UrlReadonly} from "./Et2UrlReadonly";
*/
export class Et2UrlFaxReadonly extends Et2UrlReadonly
{
constructor()
{
super();
}
transformAttributes(attrs)
{
if (typeof attrs.onclick === 'undefined')

View File

@ -32,9 +32,10 @@ export class Et2UrlPhone extends Et2InvokerMixin(Et2Textbox)
];
}
constructor()
constructor(...args : any[])
{
super();
super(...args);
//this.defaultValidators.push(...);
this._invokerLabel = '✆';
this._invokerTitle = 'Call';

View File

@ -16,11 +16,6 @@ import {Et2UrlReadonly} from "./Et2UrlReadonly";
*/
export class Et2UrlPhoneReadonly extends Et2UrlReadonly
{
constructor()
{
super();
}
transformAttributes(attrs)
{
if (typeof attrs.onclick === 'undefined')

View File

@ -19,11 +19,6 @@ import {Et2Url} from "./Et2Url";
*/
export class Et2UrlReadonly extends Et2Description
{
constructor()
{
super();
}
static get styles()
{
return [

View File

@ -77,6 +77,7 @@ import {loadWebComponent} from "./Et2Widget/Et2Widget";
import {Et2AccountFilterHeader} from "./Nextmatch/Headers/AccountFilterHeader";
import {Et2SelectCategory} from "./Et2Select/Et2SelectCategory";
import {Et2ColumnSelection} from "./Et2Nextmatch/ColumnSelection";
import {Et2Searchbox} from "./Et2Textbox/Et2Searchbox";
//import {et2_selectAccount} from "./et2_widget_SelectAccount";
let keep_import : Et2AccountFilterHeader
@ -3208,7 +3209,7 @@ export class et2_nextmatch_header_bar extends et2_DOMWidget implements et2_INext
}
};
headers : { id : string }[] | et2_widget[];
et2_searchbox : et2_inputWidget;
et2_searchbox : Et2Searchbox;
private favorites : et2_DOMWidget; // Actually favorite
private nextmatch : et2_nextmatch;
@ -3324,14 +3325,14 @@ export class et2_nextmatch_header_bar extends et2_DOMWidget implements et2_INext
{
self.nextmatch.applyFilters({search: this.get_value()});
},
value: settings.search,
value: settings.search || '',
fix: !egwIsMobile()
};
// searchbox widget
this.et2_searchbox = <et2_searchbox>et2_createWidget('searchbox', searchbox_options, this);
this.et2_searchbox = <Et2Searchbox>loadWebComponent('et2-searchbox', searchbox_options, this);
// Set activeFilters to current value
this.nextmatch.activeFilters.search = settings.search;
this.nextmatch.activeFilters.search = settings.search || '';
this.et2_searchbox.set_value(settings.search);
jQuery(this.et2_searchbox.getInputNode()).attr("aria-label", egw.lang("search"));

View File

@ -86,6 +86,7 @@ import "./Layout/RowLimitedMixin";
import "./Et2Vfs/Et2VfsMime";
import "./Et2Vfs/Et2VfsUid";
import "./Et2Textbox/Et2Password";
import './Et2Textbox/Et2Searchbox';
/* Include all widget classes here, we only care about them registering, not importing anything*/
import './et2_widget_vfs'; // Vfs must be first (before et2_widget_file) due to import cycle