x4js 1.4.43 → 1.4.46

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x4js",
3
- "version": "1.4.43",
3
+ "version": "1.4.46",
4
4
  "description": "X4js core files",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
package/src/action.ts CHANGED
@@ -27,10 +27,10 @@
27
27
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
28
  **/
29
29
 
30
- import { BasicEvent } from './x4events'
30
+ import { BasicEvent, EvChange } from './x4events'
31
31
  import { BaseComponent, BaseComponentEventMap, BaseComponentProps } from './base_component'
32
32
  import { IconID } from "./icon"
33
- import { EvChange, EventHandler } from 'x4js';
33
+ import { EventHandler } from './component';
34
34
 
35
35
  //TODO: implement all
36
36
 
@@ -30,11 +30,9 @@
30
30
  import { x4document } from './x4dom'
31
31
  import { EvMessage } from './x4events'
32
32
  import { BaseComponent, BaseComponentEventMap, BaseComponentProps } from './base_component'
33
- import { Component } from './component'
33
+ import { Component, flyWrap } from './component'
34
34
  import { Settings } from './settings'
35
- import { deferCall } from './tools'
36
35
  import { _tr } from './i18n'
37
- import { flyWrap } from 'x4js'
38
36
 
39
37
  const _x4_touch_time = Symbol( );
40
38
 
@@ -0,0 +1,196 @@
1
+ /**
2
+ * ___ ___ __
3
+ * \ \/ / / _
4
+ * \ / /_| |_
5
+ * / \____ _|
6
+ * /__/\__\ |_|
7
+ *
8
+ * @file autocomplete.ts
9
+ * @author Etienne Cochard
10
+ *
11
+ * Copyright (c) 2019-2022 R-libre ingenierie
12
+ *
13
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ * of this software and associated documentation files (the "Software"), to deal
15
+ * in the Software without restriction, including without limitation the rights
16
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
17
+ * of the Software, and to permit persons to whom the Software is furnished to do so,
18
+ * subject to the following conditions:
19
+ * The above copyright notice and this permission notice shall be included in all copies
20
+ * or substantial portions of the Software.
21
+ *
22
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
23
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
24
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
+ **/
29
+
30
+ import { ListViewItem, PopupListView } from "./listview";
31
+ import { TextEdit, TextEditProps } from './textedit';
32
+
33
+ /**
34
+ *
35
+ */
36
+
37
+ interface EditSugProps extends TextEditProps {
38
+ enumValues: ( filter: string ) => string[];
39
+ }
40
+
41
+ /**
42
+ *
43
+ */
44
+
45
+ export class EditSug extends TextEdit<EditSugProps> {
46
+
47
+ private m_popup: PopupListView;
48
+ private m_popvis: boolean;
49
+ private m_needval: boolean;
50
+ private m_lockpop: boolean;
51
+
52
+ constructor( props: EditSugProps ) {
53
+ super( props );
54
+
55
+ this.setDomEvent( "input", ( ) => this._onChange( ) );
56
+ this.setDomEvent( "focusin", ( ) => this._onFocus( ) );
57
+ this.startTimer( "focus-check", 100, true, () => this._checkFocus() );
58
+
59
+ this.m_popvis = false;
60
+ this.m_needval = false;
61
+ this.m_lockpop = false;
62
+
63
+ this.setDomEvent( "keydown", e => this._onKey(e) );
64
+ }
65
+
66
+ _onKey( e: KeyboardEvent ) {
67
+ if( this.m_popvis ) {
68
+ if( e.key=="ArrowUp" || e.key=="ArrowDown" ) {
69
+ this.m_lockpop = true;
70
+ this.m_popup.handleKey( e );
71
+ this.m_lockpop = false;
72
+
73
+ e.preventDefault( );
74
+ e.stopPropagation( );
75
+ }
76
+ else if( e.key=="Escape" ) {
77
+ this._hidePopup( );
78
+
79
+ e.preventDefault( );
80
+ e.stopPropagation( );
81
+ }
82
+ }
83
+ else if( e.key=="ArrowDown" ) {
84
+ this._onChange( );
85
+ e.preventDefault( );
86
+ e.stopPropagation( );
87
+ }
88
+ }
89
+
90
+ private _onChange( ) {
91
+ const items = this.m_props.enumValues( this.value );
92
+ this.showPopup( items );
93
+ }
94
+
95
+ componentDisposed( ) {
96
+ if( this.m_popup ) {
97
+ this._hidePopup( );
98
+ }
99
+
100
+ super.componentDisposed( );
101
+ }
102
+
103
+ /**
104
+ * display the popup
105
+ */
106
+
107
+ showPopup( items: string[] ) {
108
+
109
+ let props = this.m_props;
110
+ if (props.readOnly || this.hasClass("@disable") ) {
111
+ return;
112
+ }
113
+
114
+ // need creation ?
115
+
116
+ if( !this.m_popup ) {
117
+ let cstyle = this.getComputedStyle( );
118
+ let fontFamily = cstyle.value( 'fontFamily' );
119
+ let fontSize = cstyle.value( 'fontSize' );
120
+
121
+ // prepare the combo listview
122
+ this.m_popup = new PopupListView({
123
+ cls: '@combo-popup',
124
+ attrs: {
125
+ tabindex: 0
126
+ },
127
+ selectionChange: (e) => {
128
+ this.value = (e.selection as ListViewItem).id
129
+ if( !this.m_lockpop ) {
130
+ this._hidePopup( );
131
+ }
132
+ },
133
+ style: {
134
+ fontFamily,
135
+ fontSize
136
+ }
137
+ });
138
+ }
139
+
140
+ this.m_popup.items = items.map( c => ({ id: c, text: c }) );
141
+
142
+ let r1 = this.m_ui_input.getBoundingRect();
143
+ this.m_popup.setStyle({
144
+ minWidth: r1.width,
145
+ });
146
+
147
+ this.m_popup.displayAt(r1.left, r1.bottom);
148
+ this.m_popvis = true;
149
+
150
+ //if( this.value!==undefined ) {
151
+ // this.m_popup.selection = this.value;
152
+ //}
153
+ }
154
+
155
+ protected override _validate(value: string): boolean {
156
+ return true;
157
+ }
158
+
159
+ override validate( ): boolean {
160
+ return super._validate( this.value );
161
+ }
162
+
163
+ private _checkFocus( ) {
164
+ const focus = document.activeElement;
165
+ if( this.dom.contains(focus) ) {
166
+ return;
167
+ }
168
+
169
+ if( this.m_popup && this.m_popup.dom && this.m_popup.dom.contains(focus) ) {
170
+ return;
171
+ }
172
+
173
+ this._hidePopup( );
174
+ }
175
+
176
+ private _hidePopup( ) {
177
+
178
+ if( this.m_popvis ) {
179
+ this.m_popup.close();
180
+ this.m_popvis = false;
181
+ }
182
+
183
+ if( this.m_needval ) {
184
+ this.validate( );
185
+ this.m_needval = false;
186
+ }
187
+ }
188
+
189
+ private _onFocus( ) {
190
+ if( this.value.length==0 ) {
191
+ this._onChange( );
192
+ }
193
+
194
+ this.m_needval = true;
195
+ }
196
+ }
package/src/button.ts CHANGED
@@ -104,6 +104,10 @@ export class BaseButton<P extends ButtonProps = ButtonProps, E extends ButtonEve
104
104
  const ui_label = new Label({ flex: 1, text: text ?? '', align: props.align, ref: 'label' });
105
105
  const ui_ricon = props.rightIcon ? new Icon({ icon: props.rightIcon, cls: 'right', ref: 'r_icon' }) : null;
106
106
 
107
+ if( text===undefined ) {
108
+ ui_label.addClass( "@hidden" );
109
+ }
110
+
107
111
  this.setContent([ui_icon, ui_label, ui_ricon]);
108
112
  this._setTabIndex(props.tabIndex);
109
113
  }
@@ -203,7 +207,7 @@ export class BaseButton<P extends ButtonProps = ButtonProps, E extends ButtonEve
203
207
  this.m_props.text = text;
204
208
 
205
209
  let label = this.itemWithRef<Label>('label');
206
- if (label) { label.text = text; }
210
+ if (label) { label.text = text; label.removeClass("@hidden") }
207
211
  }
208
212
 
209
213
  public get text(): string | HtmlString {
package/src/component.ts CHANGED
@@ -33,7 +33,7 @@
33
33
  * create Container class
34
34
  */
35
35
 
36
- import { pascalCase, Rect, isString, isArray, Size, Point, isNumber, asap, HtmlString, isHtmlString, Constructor, getMousePos } from './tools';
36
+ import { pascalCase, Rect, isString, isArray, Size, isNumber, asap, HtmlString, isHtmlString, Constructor, getMousePos } from './tools';
37
37
  import { x4document } from './x4dom';
38
38
 
39
39
  import { Stylesheet, ComputedStyle } from './styles';
@@ -41,7 +41,6 @@ import { _tr } from './i18n';
41
41
  import { BasicEvent, EventCallback } from './x4events';
42
42
  import { BaseComponent, BaseComponentProps, BaseComponentEventMap } from './base_component';
43
43
  import { IDOMEvents, X4ElementEventMap } from './dom_events';
44
- import { IconID } from 'x4js';
45
44
 
46
45
  export { HtmlString, isHtmlString, html } from './tools'
47
46
 
package/src/form.ts CHANGED
@@ -59,7 +59,7 @@ export interface FormProps extends CProps<FormEventMap> {
59
59
  *
60
60
  */
61
61
 
62
- export class Form extends VLayout<FormProps, FormEventMap>
62
+ export class Form<T extends FormProps = FormProps, E extends FormEventMap = FormEventMap> extends VLayout<T, E>
63
63
  {
64
64
  protected m_height: string | number;
65
65
  protected m_container: Container;
@@ -67,7 +67,7 @@ export class Form extends VLayout<FormProps, FormEventMap>
67
67
  protected m_dirty: boolean;
68
68
  protected m_watchChanges: boolean;
69
69
 
70
- constructor(props: FormProps) {
70
+ constructor(props: T) {
71
71
 
72
72
  let content = props.content;
73
73
  props.content = null;
package/src/gridview.ts CHANGED
@@ -41,7 +41,7 @@ import { Component, ContainerEventMap, EvSize, EvDblClick, CProps, flyWrap, html
41
41
  import { Label } from './label'
42
42
  import { _tr } from './i18n'
43
43
  import * as Formatters from './formatters'
44
- import { downloadData } from './tools'
44
+ import { downloadData, isFunction } from './tools'
45
45
  import { DataView, DataStore, Record } from './datastore'
46
46
 
47
47
  import { EvContextMenu, EvSelectionChange, BasicEvent, EventDisposer } from "./x4events";
@@ -134,10 +134,19 @@ class ColHeader extends Component {
134
134
  return this.m_sorted;
135
135
  }
136
136
 
137
- set sorted( v ) {
137
+ //set sorted( v ) {
138
+ // this.m_sorted = v;
139
+ // this.m_sens = 'dn';
140
+ // this.itemWithRef<Icon>( 'sorter' ).show( v );
141
+ //}
142
+
143
+ sort( v: boolean, sens: "up" | "dn" ) {
138
144
  this.m_sorted = v;
139
- this.m_sens = 'dn';
140
- this.itemWithRef<Icon>( 'sorter' ).show( v );
145
+ this.m_sens = sens;
146
+
147
+ const ic = this.itemWithRef<Icon>('sorter');
148
+ ic.icon = this.m_sens == 'up' ? 'var( --x4-icon-arrow-down )' : 'var( --x4-icon-arrow-up )';
149
+ ic.show(v);
141
150
  }
142
151
 
143
152
  get sens( ) {
@@ -146,7 +155,7 @@ class ColHeader extends Component {
146
155
 
147
156
  toggleSens( ) {
148
157
  this.m_sens = this.m_sens=='up' ? 'dn' : 'up';
149
- this.itemWithRef<Icon>( 'sorter' ).icon = this.m_sens=='dn' ? 'var( --x4-icon-arrow-down )' : 'var( --x4-icon-arrow-up )';
158
+ this.itemWithRef<Icon>( 'sorter' ).icon = this.m_sens=='up' ? 'var( --x4-icon-arrow-down )' : 'var( --x4-icon-arrow-up )';
150
159
  }
151
160
  }
152
161
 
@@ -588,7 +597,21 @@ export class GridView extends VLayout<GridViewProps, GridViewEventMap> {
588
597
  *
589
598
  */
590
599
 
591
- private _sortCol(col: GridColumn ) {
600
+ sortCol( name: string, asc = true ) {
601
+ const col = this.m_columns.find(c => c.id==name );
602
+ if( col===undefined ) {
603
+ console.assert( false, "unknown field "+name+" in grid.sortCol" );
604
+ return;
605
+ }
606
+
607
+ this._sortCol( col, asc ? "dn" : "up" );
608
+ }
609
+
610
+ /**
611
+ *
612
+ */
613
+
614
+ private _sortCol(col: GridColumn, sens: "up" | "dn" = "up" ) {
592
615
 
593
616
  if (col.sortable === false) {
594
617
  return;
@@ -596,7 +619,7 @@ export class GridView extends VLayout<GridViewProps, GridViewEventMap> {
596
619
 
597
620
  this.m_columns.forEach((c) => {
598
621
  if (c !== col) {
599
- (c as GridColumnInternal).$hdr.sorted = false;
622
+ (c as GridColumnInternal).$hdr.sort( false, "dn" );
600
623
  }
601
624
  });
602
625
 
@@ -606,7 +629,7 @@ export class GridView extends VLayout<GridViewProps, GridViewEventMap> {
606
629
  $hdr.toggleSens( );
607
630
  }
608
631
  else {
609
- $hdr.sorted = true;
632
+ $hdr.sort( true, sens );
610
633
  }
611
634
 
612
635
  if (this.m_dataview) {
@@ -1137,17 +1160,24 @@ export class GridView extends VLayout<GridViewProps, GridViewEventMap> {
1137
1160
  if( cid ) {
1138
1161
  let col = this.m_columns[cid];
1139
1162
 
1140
- let fmt = col.formatter;
1141
-
1142
- let text;
1143
- if (fmt && fmt instanceof Function) {
1144
- text = fmt(rec[col.id], rec);
1145
- }
1146
- else {
1147
- text = rec[col.id];
1148
- }
1163
+ let value = rec[col.id];
1164
+ if( value!==undefined ) {
1165
+ if( isFunction(value) ) { // FooterRenderer
1166
+ value( c );
1167
+ }
1168
+ else {
1169
+ let text;
1170
+ const fmt = col.formatter;
1171
+ if (fmt && fmt instanceof Function) {
1172
+ text = fmt(value, rec);
1173
+ }
1174
+ else {
1175
+ text = value;
1176
+ }
1149
1177
 
1150
- c.setContent( text, false );
1178
+ c.setContent( text, false );
1179
+ }
1180
+ }
1151
1181
  }
1152
1182
  });
1153
1183
  }
package/src/index.ts CHANGED
@@ -82,3 +82,5 @@ export * from "./tools"
82
82
  export * from "./tooltips"
83
83
  export * from "./treeview"
84
84
  export * from "./x4events"
85
+
86
+ export * from "./version"
package/src/listview.ts CHANGED
@@ -122,7 +122,7 @@ export class ListView extends VLayout<ListViewProps,ListViewEventMap> {
122
122
  constructor(props: ListViewProps) {
123
123
  super(props);
124
124
 
125
- this.setDomEvent('keydown', (e) => this._handleKey(e));
125
+ this.setDomEvent('keydown', (e) => this.handleKey(e));
126
126
  this.setDomEvent('click', (e) => this._handleClick(e));
127
127
  this.setDomEvent('dblclick', (e) => this._handleClick(e));
128
128
  this.setDomEvent('contextmenu', (e) => this._handleCtxMenu(e));
@@ -198,7 +198,7 @@ export class ListView extends VLayout<ListViewProps,ListViewEventMap> {
198
198
  }
199
199
  }
200
200
 
201
- private _handleKey(ev: KeyboardEvent) {
201
+ public handleKey(ev: KeyboardEvent) {
202
202
 
203
203
  let moveSel = (sens) => {
204
204
 
@@ -237,12 +237,14 @@ export class ListView extends VLayout<ListViewProps,ListViewEventMap> {
237
237
  switch (ev.key) {
238
238
  case 'ArrowDown': {
239
239
  moveSel(1);
240
+ ev.preventDefault( );
240
241
  ev.stopPropagation();
241
242
  break;
242
243
  }
243
244
 
244
245
  case 'ArrowUp': {
245
246
  moveSel(-1);
247
+ ev.preventDefault( );
246
248
  ev.stopPropagation();
247
249
  break;
248
250
  }
@@ -705,7 +707,11 @@ export class PopupListView extends Popup<PopupListViewProps,PopupListViewEventMa
705
707
  this.m_list.items = items;
706
708
  }
707
709
 
708
- // @override
710
+ public handleKey( ev: KeyboardEvent ) {
711
+ this.m_list.handleKey( ev );
712
+ }
713
+
714
+
709
715
  // todo: move into popup
710
716
  private _handleClick = (e: MouseEvent) => {
711
717
  if (!this.dom) {
@@ -27,7 +27,7 @@
27
27
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
28
  **/
29
29
 
30
- import { Component, CProps, Flex } from './component'
30
+ import { Component, CProps, Flex, Separator } from './component'
31
31
  import { HLayout, VLayout } from './layout'
32
32
  import { Button } from './button'
33
33
  import { CardView, CardViewProps, ICardViewItem } from './cardview'
@@ -37,13 +37,14 @@ export interface SideBarItem extends ICardViewItem {
37
37
 
38
38
  export interface SideBarProps extends CardViewProps {
39
39
  bar_sizable?: boolean;
40
+ bar_width?: number;
40
41
  }
41
42
 
42
43
  /**
43
44
  *
44
45
  */
45
46
 
46
- export class SideBarView extends CardView {
47
+ export class SideBarView extends CardView<SideBarProps> {
47
48
  m_sidebar: VLayout;
48
49
  m_content: Component;
49
50
 
@@ -55,7 +56,7 @@ export class SideBarView extends CardView {
55
56
 
56
57
  this.m_sidebar = new VLayout( {
57
58
  cls: '@side-bar',
58
- sizable: props.bar_sizable ? 'right' : undefined,
59
+ width: props.bar_width ?? undefined,
59
60
  });
60
61
 
61
62
  this.m_content = new HLayout( { flex: 1, cls: '@tab-container' } );
@@ -78,6 +79,7 @@ export class SideBarView extends CardView {
78
79
 
79
80
  this.setContent( [
80
81
  this.m_sidebar,
82
+ this.m_props.bar_sizable ? new Separator( { orientation: "horizontal", sizing: "before" }) : undefined,
81
83
  this.m_content
82
84
  ] );
83
85
  }
@@ -192,9 +192,22 @@ abstract class SVGItem {
192
192
  return result+'"';
193
193
  }
194
194
 
195
+ /**
196
+ *
197
+ */
198
+
195
199
  renderContent( ): string {
196
200
  return '';
197
201
  }
202
+
203
+ /**
204
+ *
205
+ */
206
+
207
+ clip( id: string ) {
208
+ this.attr( "clip-path", `url(#${id})` );
209
+ return this;
210
+ }
198
211
  }
199
212
 
200
213
  /**
@@ -391,12 +404,13 @@ class SVGGradient extends SVGItem {
391
404
  *
392
405
  */
393
406
 
394
- export class SVGPathBuilder
395
- {
396
- private m_items: SVGItem[];
407
+ class SVGGroup extends SVGItem {
408
+ protected m_items: SVGItem[];
397
409
 
398
- constructor( ) {
399
- this.m_items = [];
410
+ constructor( tag = "g" ) {
411
+ super( tag )
412
+
413
+ this.m_items = [];
400
414
  }
401
415
 
402
416
  path( ) {
@@ -445,7 +459,7 @@ export class SVGPathBuilder
445
459
  this.m_items = [];
446
460
  }
447
461
 
448
- render( ) {
462
+ renderContent( ) {
449
463
  let result: string[] = [];
450
464
  this.m_items.forEach( i => {
451
465
  result.push( i.render() );
@@ -455,6 +469,41 @@ export class SVGPathBuilder
455
469
  }
456
470
  }
457
471
 
472
+
473
+ /**
474
+ *
475
+ */
476
+
477
+ export class SVGPathBuilder extends SVGGroup
478
+ {
479
+ private static g_clip_id = 1;
480
+
481
+ constructor( ) {
482
+ super( '' );
483
+ }
484
+
485
+ addClip( x: number, y: number, w: number, h: number ) {
486
+
487
+ const id = 'c-'+SVGPathBuilder.g_clip_id++;
488
+ const clip = new SVGGroup( 'clipPath' );
489
+ clip.attr('id', id );
490
+ clip.rect( x, y, w, h );
491
+
492
+ this.m_items.push(clip);
493
+ return id;
494
+ }
495
+
496
+ render() {
497
+
498
+ let result = [];
499
+ this.m_items.forEach(i => {
500
+ result.push(i.render());
501
+ });
502
+
503
+ return result.join('\n');
504
+ }
505
+ }
506
+
458
507
  /**
459
508
  *
460
509
  */
package/src/textedit.ts CHANGED
@@ -87,13 +87,13 @@ export interface TextEditProps extends InputProps<TextEditEventMap> {
87
87
  * TextEdit is a single line editor, it can have a label and an error descriptor.
88
88
  */
89
89
 
90
- export class TextEdit<T extends TextEditProps = TextEditProps> extends Component<TextEditProps, TextEditEventMap> {
90
+ export class TextEdit<T extends TextEditProps = TextEditProps, E extends TextEditEventMap = TextEditEventMap > extends Component<T, E> {
91
91
 
92
92
  private m_cal_popup: PopupCalendar;
93
93
  protected m_ui_input: Input;
94
94
  private m_error_tip: Tooltip;
95
95
 
96
- constructor(props: TextEditProps) {
96
+ constructor(props: T) {
97
97
  super(props);
98
98
  this.addClass( '@hlayout' );
99
99
  this.mapPropEvents( props, 'change', 'click', 'focus' );
@@ -410,7 +410,7 @@ export class TextEdit<T extends TextEditProps = TextEditProps> extends Component
410
410
  return this._validate(this.value);
411
411
  }
412
412
 
413
- private _validate(value: string): boolean {
413
+ protected _validate(value: string): boolean {
414
414
  let props = this.m_props;
415
415
  let update = false;
416
416
 
package/src/tools.ts CHANGED
@@ -496,7 +496,7 @@ export function date_clone(date: Date): Date {
496
496
  export function date_calc_weeknum(date: Date): number {
497
497
  const firstDayOfYear = new Date(date.getFullYear(), 0, 1);
498
498
  const pastDaysOfYear = (date.valueOf() - firstDayOfYear.valueOf()) / 86400000;
499
- return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7);
499
+ return Math.floor((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7);
500
500
  }
501
501
 
502
502
 
@@ -1406,3 +1406,63 @@ function shuffle(str: string, maxlength?: number) {
1406
1406
 
1407
1407
  return shuffled;
1408
1408
  }
1409
+
1410
+
1411
+
1412
+ /**
1413
+ * taken from live-server
1414
+ * https://github.com/tapio/live-server
1415
+ * @param host
1416
+ * @param port
1417
+ */
1418
+
1419
+ export function installHMR(host = "127.0.0.1", port = "9876", reloadCallback?: Function ) {
1420
+
1421
+ let tm;
1422
+
1423
+ function refreshCSS() {
1424
+
1425
+ document.body.style.visibility = "hidden";
1426
+
1427
+ let sheets = [].slice.call(document.getElementsByTagName("link"));
1428
+ let head = document.getElementsByTagName("head")[0];
1429
+
1430
+ for (let i = 0; i < sheets.length; ++i) {
1431
+ let elem = sheets[i];
1432
+ head.removeChild(elem);
1433
+
1434
+ let rel = elem.rel;
1435
+ if (elem.href && typeof rel != "string" || rel.length == 0 || rel.toLowerCase() == "stylesheet") {
1436
+ let url = elem.href.replace(/(&|\?)_cacheOverride=\d+/, '');
1437
+ elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf());
1438
+ }
1439
+
1440
+ head.appendChild(elem);
1441
+ }
1442
+
1443
+ if( tm ) { clearTimeout(tm); }
1444
+ tm = setTimeout( () => {
1445
+ document.body.style.visibility = "unset";
1446
+ }, 50 );
1447
+ }
1448
+
1449
+ const protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
1450
+ const address = `${protocol}${host}:${port}/ws`;
1451
+ const socket = new WebSocket(address);
1452
+
1453
+ socket.onmessage = function (msg) {
1454
+ if (msg.data == 'reload') {
1455
+ if( reloadCallback ) {
1456
+ reloadCallback( );
1457
+ }
1458
+ else {
1459
+ window.location.reload();
1460
+ }
1461
+ }
1462
+ else if (msg.data == 'refreshcss') {
1463
+ refreshCSS();
1464
+ }
1465
+ };
1466
+
1467
+ console.log('Live reload enabled.');
1468
+ }