x4js 1.5.27 → 1.5.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -102,7 +102,7 @@ export class AutoComplete extends TextEdit<AutoCompleteProps> {
102
102
  return;
103
103
  }
104
104
 
105
- this.showPopup( items );
105
+ this._showPopup( items );
106
106
  }
107
107
 
108
108
  componentDisposed( ) {
@@ -113,11 +113,15 @@ export class AutoComplete extends TextEdit<AutoCompleteProps> {
113
113
  super.componentDisposed( );
114
114
  }
115
115
 
116
+ showPopup( ) {
117
+ this._onChange( );
118
+ }
119
+
116
120
  /**
117
121
  * display the popup
118
122
  */
119
123
 
120
- showPopup( items: string[] ) {
124
+ private _showPopup( items: string[] ) {
121
125
 
122
126
  let props = this.m_props;
123
127
  if (props.readOnly || this.hasClass("@disable") ) {
@@ -156,7 +160,9 @@ export class AutoComplete extends TextEdit<AutoCompleteProps> {
156
160
  });
157
161
  }
158
162
 
159
- this.m_popup.items = items.map( c => ({ id: c, text: c }) );
163
+ if( items ) {
164
+ this.m_popup.items = items.map( c => ({ id: c, text: c }) );
165
+ }
160
166
 
161
167
  let r1 = this.m_ui_input.getBoundingRect();
162
168
  this.m_popup.setStyle({
@@ -31,6 +31,8 @@
31
31
  * TODO: replace custom combo list by listview or gridview
32
32
  */
33
33
 
34
+ import { x4document } from './x4dom'
35
+
34
36
  import { Component, CProps, ContainerEventMap } from './component'
35
37
  import { EvChange, EvSelectionChange, EventCallback } from './x4events'
36
38
 
@@ -41,6 +43,8 @@ import { HLayout } from './layout'
41
43
  import { PopupListView, ListViewItem, EvCancel, PopulateItems } from './listview';
42
44
  import { DataStore, DataView, Record } from './datastore'
43
45
  import { isFunction, HtmlString } from './tools'
46
+ import { Tooltip } from './tooltips'
47
+ import { _tr } from './i18n'
44
48
 
45
49
  export interface ComboStoreProxyProps {
46
50
  store: DataView | DataStore;
@@ -65,6 +69,7 @@ export interface ComboBoxProps extends CProps<ComboBoxEventMap> {
65
69
  tabIndex?: number | boolean;
66
70
  name?: string;
67
71
  readOnly?: boolean;
72
+ required?: true;
68
73
 
69
74
  label?: string;
70
75
  labelWidth?: number; // < 0 for flex
@@ -94,6 +99,7 @@ export class ComboBox extends HLayout<ComboBoxProps,ComboBoxEventMap> {
94
99
  private m_lockchg: boolean;
95
100
  private m_popvis: boolean;
96
101
  private m_selection: ListViewItem;
102
+ private m_error_tip: Tooltip;
97
103
 
98
104
  constructor(props: ComboBoxProps) {
99
105
  super(props);
@@ -147,6 +153,8 @@ export class ComboBox extends HLayout<ComboBoxProps,ComboBoxEventMap> {
147
153
 
148
154
  if( !props.renderer ) {
149
155
 
156
+ this.setClass('@required', props.required);
157
+
150
158
  const input = new Input( {
151
159
  flex : 1,
152
160
  readOnly : this.m_props.editable ? false : true,
@@ -158,6 +166,7 @@ export class ComboBox extends HLayout<ComboBoxProps,ComboBoxEventMap> {
158
166
  },
159
167
  dom_events: {
160
168
  focus: () => {
169
+ this.clearError();
161
170
  if( this.m_props.editable && input.value.length==0 ) {
162
171
  this.showPopup( );
163
172
  }
@@ -220,7 +229,10 @@ export class ComboBox extends HLayout<ComboBoxProps,ComboBoxEventMap> {
220
229
  this.showPopup( false )
221
230
  },
222
231
  dom_events: {
223
- focus: () => { this.dom.focus(); },
232
+ focus: () => {
233
+ this.clearError();
234
+ this.dom.focus();
235
+ },
224
236
  }
225
237
  })
226
238
  ]
@@ -233,6 +245,10 @@ export class ComboBox extends HLayout<ComboBoxProps,ComboBoxEventMap> {
233
245
  }
234
246
 
235
247
  componentDisposed( ) {
248
+ if (this.m_error_tip) {
249
+ this.m_error_tip.dispose();
250
+ }
251
+
236
252
  if( this.m_popup ) {
237
253
  this.m_popup.close( );
238
254
  }
@@ -310,6 +326,40 @@ export class ComboBox extends HLayout<ComboBoxProps,ComboBoxEventMap> {
310
326
  return items;
311
327
  }
312
328
 
329
+ /**
330
+ *
331
+ */
332
+
333
+ validate( ) {
334
+ if( this.m_props.required && !this.m_selection ) {
335
+ this.showError(_tr.global.required_field);
336
+ return false;
337
+ }
338
+
339
+ return true;
340
+ }
341
+
342
+ public showError(text: string) {
343
+
344
+ if (!this.m_error_tip) {
345
+ this.m_error_tip = new Tooltip({ cls: 'error' });
346
+ x4document.body.appendChild(this.m_error_tip._build());
347
+ }
348
+
349
+ let rc = this.m_ui_input.getBoundingRect();
350
+ this.m_error_tip.text = text;
351
+ this.m_error_tip.displayAt(rc.right, rc.top-8, 'top right');
352
+
353
+ this.addClass('@error');
354
+ }
355
+
356
+ public clearError() {
357
+ if (this.m_error_tip) {
358
+ this.m_error_tip.hide();
359
+ this.removeClass('@error');
360
+ }
361
+ }
362
+
313
363
  /** @ignore
314
364
  */
315
365
 
@@ -433,7 +433,11 @@ export class Record {
433
433
  return undefined;
434
434
  }
435
435
  }
436
- else if( name>=0 && name<fields.length ) {
436
+ else if( name<fields.length ) {
437
+ if( name<0 ) {
438
+ return undefined
439
+ }
440
+
437
441
  idx = name;
438
442
  }
439
443
  else {
package/lib/src/form.ts CHANGED
@@ -27,17 +27,23 @@
27
27
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
28
  **/
29
29
 
30
- import { Component, Container, CProps, ContainerEventMap, ComponentContent, flyWrap } from './component'
30
+ import { Component, Container, CProps, ContainerEventMap, ComponentContent, flyWrap, EventHandler } from './component'
31
31
  import { HLayout, VLayout } from './layout'
32
32
  import { Button } from './button'
33
33
  import { Input } from './input'
34
34
  import { TextEdit } from './textedit'
35
35
  import { ajaxRequest, RequestProps } from './request'
36
- import { EventCallback } from './x4events'
36
+ import { BasicEvent } from './x4events'
37
37
  import { EvBtnClick } from './dialog'
38
+ import { ComboBox } from './combobox'
38
39
 
39
40
  import { _tr } from './i18n'
40
41
 
42
+
43
+ export interface EvDirty extends BasicEvent {
44
+ dirty: boolean;
45
+ }
46
+
41
47
  // ============================================================================
42
48
  // [FORM]
43
49
  // ============================================================================
@@ -47,12 +53,14 @@ export type FormButtons = (FormBtn | Button | Component)[];
47
53
 
48
54
  export interface FormEventMap extends ContainerEventMap {
49
55
  btnClick?: EvBtnClick;
56
+ dirty?: EvDirty;
50
57
  }
51
58
 
52
59
  export interface FormProps extends CProps<FormEventMap> {
53
60
  disableSuggestions?: boolean;
54
61
  buttons?: FormButtons;
55
- btnClick?: EventCallback<EvBtnClick>; // shortcut for events: { btnClick: ... }
62
+ btnClick?: EventHandler<EvBtnClick>; // shortcut for events: { btnClick: ... }
63
+ dirty?: EventHandler<EvDirty>;
56
64
  }
57
65
 
58
66
  /**
@@ -79,7 +87,7 @@ export class Form<T extends FormProps = FormProps, E extends FormEventMap = Form
79
87
  super(props);
80
88
 
81
89
  this.setTag( props.disableSuggestions ? 'section' : 'form');
82
- this.mapPropEvents(props, 'btnClick');
90
+ this.mapPropEvents(props, 'btnClick', 'dirty');
83
91
  this.updateContent(content, props.buttons, height);
84
92
 
85
93
  this.m_dirty = false;
@@ -221,10 +229,17 @@ export class Form<T extends FormProps = FormProps, E extends FormEventMap = Form
221
229
  result = true;
222
230
 
223
231
  for (let i = 0; i < inputs.length; i++) {
224
- let input = Component.getElement(inputs[i], TextEdit);
232
+ const input = Component.getElement(inputs[i], TextEdit);
225
233
  if (input && !input.validate()) {
226
234
  result = false;
227
235
  }
236
+ else {
237
+ const combo = Component.getElement(inputs[i], ComboBox);
238
+ if( combo && !combo.validate() ) {
239
+ result = false;
240
+ }
241
+ }
242
+
228
243
  }
229
244
 
230
245
  return result;
@@ -278,6 +293,28 @@ export class Form<T extends FormProps = FormProps, E extends FormEventMap = Form
278
293
  this.setDirty(false);
279
294
  }
280
295
 
296
+ /**
297
+ *
298
+ */
299
+
300
+ public clearValues() {
301
+
302
+ let elements = this._getElements();
303
+ for (let e = 0; e < elements.length; e++) {
304
+
305
+ let input = <HTMLInputElement>elements[e];
306
+
307
+ let item = Component.getElement(input);
308
+ if (!item.hasAttribute("name")) {
309
+ continue;
310
+ }
311
+
312
+ (<Input>item).setStoreValue( null );
313
+ }
314
+
315
+ this.setDirty(false);
316
+ }
317
+
281
318
 
282
319
  /**
283
320
  * values are not escaped
@@ -363,6 +400,7 @@ export class Form<T extends FormProps = FormProps, E extends FormEventMap = Form
363
400
 
364
401
  setDirty(set = true) {
365
402
  this.m_dirty = set;
403
+ this.emit( 'dirty', { dirty: set } );
366
404
  }
367
405
 
368
406
  /**
package/lib/src/icon.ts CHANGED
@@ -58,12 +58,20 @@ interface LoadingEventMap extends EventMap {
58
58
 
59
59
  function trimQuotes( str:string ): string {
60
60
  const l = str.length;
61
+
62
+ //chrome
61
63
  if( str[0]=='"' && str[l-1]=='"' ) {
62
64
  str = str.substring( 1, l-1 )
63
65
  str = str.replaceAll( '\\"', "'" );
64
66
  return str;
65
67
  }
66
68
 
69
+ //firefox
70
+ if( str[0]=="'" && str[l-1]=="'" ) {
71
+ str = str.substring( 1, l-1 )
72
+ return str;
73
+ }
74
+
67
75
  return str;
68
76
  }
69
77
 
package/lib/src/image.ts CHANGED
@@ -28,25 +28,30 @@
28
28
  **/
29
29
 
30
30
  import { x4document } from './x4dom'
31
-
32
31
  import { Component, CProps, html } from './component'
33
32
 
33
+ interface LazyLoad {
34
+ el: Component;
35
+ src: string;
36
+ }
37
+
34
38
  // ============================================================================
35
39
  // [IMAGE]
36
40
  // ============================================================================
37
41
 
38
- interface ImageProps extends CProps
42
+ export interface ImageProps extends CProps
39
43
  {
40
44
  src: string;
41
45
  alt?: string;
42
46
  lazy?: boolean; // mark image as lazy loading
43
47
  alignment?: 'fill' | 'contain' | 'cover' | 'scale-down' | 'none';
48
+ overlays?: Component[];
44
49
  }
45
50
 
46
51
  const emptyImageSrc = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
47
52
 
48
53
  function _isStaticImage( src: string ) {
49
- return src.substr(0,5)=='data:';
54
+ return src.substring(0,5)=='data:';
50
55
  }
51
56
 
52
57
 
@@ -58,6 +63,7 @@ export class Image extends Component<ImageProps>
58
63
  {
59
64
  protected m_created: boolean;
60
65
  protected m_lazysrc: string; // expected
66
+ private m_img: Component;
61
67
 
62
68
  constructor(props: ImageProps) {
63
69
  super(props);
@@ -83,7 +89,7 @@ export class Image extends Component<ImageProps>
83
89
  render( ) {
84
90
  let mp = this.m_props;
85
91
 
86
- const img = new Component( {
92
+ this.m_img = new Component( {
87
93
  tag: 'img',
88
94
  attrs: {
89
95
  draggable: false,
@@ -95,7 +101,13 @@ export class Image extends Component<ImageProps>
95
101
  }
96
102
  });
97
103
 
98
- this.setContent( img );
104
+ if( mp.overlays ) {
105
+ mp.overlays.forEach( x => x.addClass('@fit') );
106
+ this.setContent( [this.m_img,...mp.overlays] );
107
+ }
108
+ else {
109
+ this.setContent( this.m_img );
110
+ }
99
111
  }
100
112
 
101
113
  /**
@@ -107,14 +119,18 @@ export class Image extends Component<ImageProps>
107
119
 
108
120
  if( !src ) {
109
121
  src = emptyImageSrc;
122
+ this.addClass( 'empty' );
123
+ }
124
+ else {
125
+ this.removeClass( 'empty' );
110
126
  }
111
127
 
112
128
  if( !this.m_props.lazy ) {
113
129
  this.m_props.src = src;
114
130
  this.m_lazysrc = src;
115
131
 
116
- if( this.dom ) {
117
- (<HTMLElement>this.dom.firstChild).setAttribute( 'src', src );
132
+ if( this.m_img.dom ) {
133
+ this.m_img.dom.setAttribute( 'src', src );
118
134
  }
119
135
  }
120
136
  else if( force || this.m_lazysrc!=src ) {
@@ -123,15 +139,15 @@ export class Image extends Component<ImageProps>
123
139
  this.m_props.src = src;
124
140
  this.m_lazysrc = src;
125
141
 
126
- if( this.dom ) {
127
- (<HTMLElement>this.dom.firstChild).setAttribute( 'src', this.m_props.src );
142
+ if( this.m_img.dom ) {
143
+ this.m_img.dom.setAttribute( 'src', this.m_props.src );
128
144
  }
129
145
  }
130
146
  else {
131
147
  // clear current image while waiting
132
148
  this.m_props.src = emptyImageSrc;
133
- if( this.dom ) {
134
- (<HTMLElement>this.dom.firstChild).setAttribute( 'src', this.m_props.src );
149
+ if( this.m_img.dom ) {
150
+ this.m_img.dom.setAttribute( 'src', this.m_props.src );
135
151
  }
136
152
 
137
153
  this.m_lazysrc = src;
@@ -148,14 +164,18 @@ export class Image extends Component<ImageProps>
148
164
 
149
165
  if( this.m_lazysrc && !_isStaticImage(this.m_lazysrc) ) {
150
166
  // we do not push Components in a static array...
151
- Image.lazy_images_waiting.push( { dom: this.dom, src: this.m_lazysrc } );
167
+ Image.lazy_images_waiting.push( {
168
+ el: this,
169
+ src: this.m_lazysrc
170
+ } );
171
+
152
172
  if( Image.lazy_image_timer===undefined ) {
153
173
  Image.lazy_image_timer = setInterval( Image.lazyWatch as TimerHandler, 10 );
154
174
  }
155
175
  }
156
176
  }
157
177
 
158
- private static lazy_images_waiting = [];
178
+ private static lazy_images_waiting: LazyLoad[] = [];
159
179
  private static lazy_image_timer: number = undefined;
160
180
 
161
181
  private static lazyWatch( ) {
@@ -163,10 +183,10 @@ export class Image extends Component<ImageProps>
163
183
  let newList = [];
164
184
  let done = 0;
165
185
 
166
- Image.lazy_images_waiting.forEach( ( el ) => {
186
+ Image.lazy_images_waiting.forEach( ( lazy ) => {
167
187
 
168
- let dom = el.dom,
169
- src = el.src;
188
+ let dom = lazy.el.dom,
189
+ src = lazy.src;
170
190
 
171
191
  // skip deleted elements
172
192
  if( !dom || dom.offsetParent === null ) {
@@ -185,11 +205,12 @@ export class Image extends Component<ImageProps>
185
205
  // ok, we load the image
186
206
  let img = <HTMLElement>dom.firstChild;
187
207
  img.setAttribute( 'src', src );
208
+ lazy.el.removeClass( 'empty' );
188
209
  done++;
189
210
  }
190
211
  else {
191
212
  // still not visible: may be next time
192
- newList.push( el );
213
+ newList.push( lazy );
193
214
  }
194
215
  } );
195
216
 
@@ -318,7 +318,7 @@ export class TextEdit<T extends TextEditProps = TextEditProps, E extends TextEdi
318
318
  let rc = this.m_ui_input.getBoundingRect();
319
319
 
320
320
  this.m_error_tip.text = text;
321
- this.m_error_tip.displayAt(rc.right, rc.top, 'top left');
321
+ this.m_error_tip.displayAt(rc.right, rc.top-8, 'top right');
322
322
  this.addClass('@error');
323
323
  }
324
324
 
@@ -27,4 +27,4 @@
27
27
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
28
  **/
29
29
 
30
- export const x4js_version = "1.5.27";
30
+ export const x4js_version = "1.5.29";
@@ -46,10 +46,11 @@ export declare class AutoComplete extends TextEdit<AutoCompleteProps> {
46
46
  _onKey(e: KeyboardEvent): void;
47
47
  private _onChange;
48
48
  componentDisposed(): void;
49
+ showPopup(): void;
49
50
  /**
50
51
  * display the popup
51
52
  */
52
- showPopup(items: string[]): void;
53
+ private _showPopup;
53
54
  protected _validate(value: string): boolean;
54
55
  validate(): boolean;
55
56
  private _checkFocus;
@@ -26,9 +26,6 @@
26
26
  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27
27
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
28
  **/
29
- /**
30
- * TODO: replace custom combo list by listview or gridview
31
- */
32
29
  import { Component, CProps, ContainerEventMap } from './component';
33
30
  import { EvChange, EvSelectionChange, EventCallback } from './x4events';
34
31
  import { Input } from './input';
@@ -52,6 +49,7 @@ export interface ComboBoxProps extends CProps<ComboBoxEventMap> {
52
49
  tabIndex?: number | boolean;
53
50
  name?: string;
54
51
  readOnly?: boolean;
52
+ required?: true;
55
53
  label?: string;
56
54
  labelWidth?: number;
57
55
  labelAlign?: 'left' | 'right';
@@ -72,6 +70,7 @@ export declare class ComboBox extends HLayout<ComboBoxProps, ComboBoxEventMap> {
72
70
  private m_lockchg;
73
71
  private m_popvis;
74
72
  private m_selection;
73
+ private m_error_tip;
75
74
  constructor(props: ComboBoxProps);
76
75
  _onKey(e: any): void;
77
76
  set items(items: ListViewItem[]);
@@ -82,6 +81,12 @@ export declare class ComboBox extends HLayout<ComboBoxProps, ComboBoxEventMap> {
82
81
  * display the popup
83
82
  */
84
83
  showPopup(filter_items?: boolean): ListViewItem[];
84
+ /**
85
+ *
86
+ */
87
+ validate(): boolean;
88
+ showError(text: string): void;
89
+ clearError(): void;
85
90
  /** @ignore
86
91
  */
87
92
  private _selectItem;
@@ -26,21 +26,26 @@
26
26
  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27
27
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
28
  **/
29
- import { Component, Container, CProps, ContainerEventMap, ComponentContent } from './component';
29
+ import { Component, Container, CProps, ContainerEventMap, ComponentContent, EventHandler } from './component';
30
30
  import { HLayout, VLayout } from './layout';
31
31
  import { Button } from './button';
32
32
  import { RequestProps } from './request';
33
- import { EventCallback } from './x4events';
33
+ import { BasicEvent } from './x4events';
34
34
  import { EvBtnClick } from './dialog';
35
+ export interface EvDirty extends BasicEvent {
36
+ dirty: boolean;
37
+ }
35
38
  export type FormBtn = 'ok' | 'cancel' | 'ignore' | 'yes' | 'no' | 'close' | 'save' | 'dontsave';
36
39
  export type FormButtons = (FormBtn | Button | Component)[];
37
40
  export interface FormEventMap extends ContainerEventMap {
38
41
  btnClick?: EvBtnClick;
42
+ dirty?: EvDirty;
39
43
  }
40
44
  export interface FormProps extends CProps<FormEventMap> {
41
45
  disableSuggestions?: boolean;
42
46
  buttons?: FormButtons;
43
- btnClick?: EventCallback<EvBtnClick>;
47
+ btnClick?: EventHandler<EvBtnClick>;
48
+ dirty?: EventHandler<EvDirty>;
44
49
  }
45
50
  /**
46
51
  *
@@ -107,6 +112,10 @@ export declare class Form<T extends FormProps = FormProps, E extends FormEventMa
107
112
  *
108
113
  */
109
114
  setValues(values: any): void;
115
+ /**
116
+ *
117
+ */
118
+ clearValues(): void;
110
119
  /**
111
120
  * values are not escaped
112
121
  * checkbox set true when checked
@@ -27,11 +27,12 @@
27
27
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
28
  **/
29
29
  import { Component, CProps } from './component';
30
- interface ImageProps extends CProps {
30
+ export interface ImageProps extends CProps {
31
31
  src: string;
32
32
  alt?: string;
33
33
  lazy?: boolean;
34
34
  alignment?: 'fill' | 'contain' | 'cover' | 'scale-down' | 'none';
35
+ overlays?: Component[];
35
36
  }
36
37
  /**
37
38
  * Standard image class
@@ -39,6 +40,7 @@ interface ImageProps extends CProps {
39
40
  export declare class Image extends Component<ImageProps> {
40
41
  protected m_created: boolean;
41
42
  protected m_lazysrc: string;
43
+ private m_img;
42
44
  constructor(props: ImageProps);
43
45
  /** @ignore */
44
46
  render(): void;
@@ -52,4 +54,3 @@ export declare class Image extends Component<ImageProps> {
52
54
  private static lazy_image_timer;
53
55
  private static lazyWatch;
54
56
  }
55
- export {};
@@ -26,4 +26,4 @@
26
26
  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27
27
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
28
  **/
29
- export declare const x4js_version = "1.5.27";
29
+ export declare const x4js_version = "1.5.29";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x4js",
3
- "version": "1.5.27",
3
+ "version": "1.5.29",
4
4
  "description": "X4js core files",
5
5
  "main": "lib/cjs/index.js",
6
6
  "types": "lib/types/index.d.ts",