x4js 1.4.14 → 1.4.15

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.
@@ -107,7 +107,6 @@ export declare class Component<P extends CProps<BaseComponentEventMap> = CProps<
107
107
  private static __privateEvents;
108
108
  private static __sizeObserver;
109
109
  private static __createObserver;
110
- private static __intersectionObserver;
111
110
  private static __capture;
112
111
  private static __capture_mask;
113
112
  private static __css;
package/lib/component.js CHANGED
@@ -89,7 +89,7 @@ class Component extends base_component_1.BaseComponent {
89
89
  static __privateEvents = {};
90
90
  static __sizeObserver; // resize observer
91
91
  static __createObserver; // creation observer
92
- static __intersectionObserver; // visibility observer
92
+ //private static __intersectionObserver: IntersectionObserver; // visibility observer
93
93
  static __capture = null;
94
94
  static __capture_mask = null;
95
95
  static __css = null;
@@ -101,6 +101,10 @@ class Component extends base_component_1.BaseComponent {
101
101
  uid: Component.__comp_guid++,
102
102
  inrender: false,
103
103
  };
104
+ // prepare iprops
105
+ if (this.m_props.cls) {
106
+ this.addClass(this.m_props.cls);
107
+ }
104
108
  }
105
109
  /**
106
110
  *
@@ -1042,7 +1046,8 @@ class Component extends base_component_1.BaseComponent {
1042
1046
  this.addClass('@' + (0, tools_1.pascalCase)(clsname));
1043
1047
  me = Object.getPrototypeOf(me);
1044
1048
  }
1045
- this.addClass(this.m_props.cls);
1049
+ //done in ctor now
1050
+ //this.addClass(this.m_props.cls);
1046
1051
  }
1047
1052
  /**
1048
1053
  * prepend the system class name prefix on a name if needed (if class starts with @)
package/lib/layout.js CHANGED
@@ -278,10 +278,17 @@ exports.ScrollView = ScrollView;
278
278
  // https://medium.com/@andybarefoot/a-masonry-style-layout-using-css-grid-8c663d355ebb
279
279
  class Masonry extends component_1.Container {
280
280
  constructor(props) {
281
+ const items = props.items;
282
+ props.items = undefined;
281
283
  super(props);
282
284
  this.setDomEvent('sizechange', () => {
283
285
  this.resizeAllItems();
284
286
  });
287
+ if (items) {
288
+ items.forEach(i => {
289
+ this.addItem(i);
290
+ });
291
+ }
285
292
  }
286
293
  resizeItem(item) {
287
294
  const style = this.getComputedStyle();
package/lib/listview.d.ts CHANGED
@@ -83,7 +83,7 @@ export interface ListViewProps<E extends ListViewEventMap = ListViewEventMap> ex
83
83
  /**
84
84
  * Standard listview class
85
85
  */
86
- export declare class ListView<T extends ListViewProps = ListViewProps, E extends ListViewEventMap = ListViewEventMap> extends VLayout<T, E> {
86
+ export declare class ListView extends VLayout<ListViewProps, ListViewEventMap> {
87
87
  protected m_selection: {
88
88
  item: ListViewItem;
89
89
  citem: Component;
@@ -94,7 +94,7 @@ export declare class ListView<T extends ListViewProps = ListViewProps, E extends
94
94
  protected m_topIndex: number;
95
95
  protected m_itemHeight: number;
96
96
  protected m_cache: Map<number, Component>;
97
- constructor(props: T);
97
+ constructor(props: ListViewProps);
98
98
  componentCreated(): void;
99
99
  render(props: ListViewProps): void;
100
100
  /**
package/lib/router.d.ts CHANGED
@@ -26,9 +26,17 @@
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 class Router {
29
+ import { EventSource, EvError, EventMap } from "./x4_events";
30
+ declare type RouteHandler = (params: any, path: string) => void;
31
+ interface RouterEventMap extends EventMap {
32
+ error: EvError;
33
+ }
34
+ export declare class Router extends EventSource<RouterEventMap> {
30
35
  private routes;
31
36
  constructor();
32
- get(uri: any, callback: any): void;
37
+ get(uri: string | RegExp, handler: RouteHandler): void;
33
38
  init(): void;
39
+ navigate(uri: string, notify?: boolean): void;
40
+ private _find;
34
41
  }
42
+ export {};
package/lib/router.js CHANGED
@@ -29,31 +29,111 @@
29
29
  **/
30
30
  Object.defineProperty(exports, "__esModule", { value: true });
31
31
  exports.Router = void 0;
32
- class Router {
32
+ const x4_events_1 = require("./x4_events");
33
+ function parseRoute(str, loose = false) {
34
+ if (str instanceof RegExp) {
35
+ return {
36
+ keys: null,
37
+ pattern: str
38
+ };
39
+ }
40
+ const arr = str.split('/');
41
+ let keys = [];
42
+ let pattern = '';
43
+ if (arr[0] == '') {
44
+ arr.shift();
45
+ }
46
+ for (const tmp of arr) {
47
+ const c = tmp[0];
48
+ if (c === '*') {
49
+ keys.push('wild');
50
+ pattern += '/(.*)';
51
+ }
52
+ else if (c === ':') {
53
+ const o = tmp.indexOf('?', 1);
54
+ const ext = tmp.indexOf('.', 1);
55
+ keys.push(tmp.substring(1, o >= 0 ? o : ext >= 0 ? ext : tmp.length));
56
+ pattern += o < 0 && ext < 0 ? '(?:/([^/]+?))?' : '/([^/]+?)';
57
+ if (ext >= 0) {
58
+ pattern += (o >= 0 ? '?' : '') + '\\' + tmp.substring(ext);
59
+ }
60
+ }
61
+ else {
62
+ pattern += '/' + tmp;
63
+ }
64
+ }
65
+ return {
66
+ keys,
67
+ pattern: new RegExp(`^${pattern}${loose ? '(?=$|\/)' : '\/?$'}`, 'i')
68
+ };
69
+ }
70
+ class Router extends x4_events_1.EventSource {
33
71
  routes;
34
72
  constructor() {
73
+ super();
35
74
  this.routes = [];
36
- }
37
- get(uri, callback) {
38
- // throw an error if the route uri already exists to avoid confilicting routes
39
- this.routes.forEach(route => {
40
- if (route.uri === uri) {
41
- throw new Error(`the uri ${route.uri} already exists`);
42
- }
43
- });
44
- this.routes.push({
45
- uri,
46
- callback
75
+ window.addEventListener('popstate', (event) => {
76
+ const url = document.location.pathname;
77
+ const found = this._find(url);
78
+ found.handlers.forEach(h => {
79
+ h(found.params, url);
80
+ });
47
81
  });
48
82
  }
83
+ get(uri, handler) {
84
+ let { keys, pattern } = parseRoute(uri);
85
+ this.routes.push({ keys, pattern, handler });
86
+ }
49
87
  init() {
50
- this.routes.some(route => {
51
- let regEx = new RegExp(`^${route.uri}$`);
52
- let path = window.location.pathname;
53
- if (path.match(regEx)) {
54
- return route.callback({ path });
88
+ this.navigate(window.location.pathname);
89
+ }
90
+ navigate(uri, notify = true) {
91
+ const found = this._find(uri);
92
+ if (!found || found.handlers.length == 0) {
93
+ //window.history.pushState({}, '', 'error')
94
+ console.log('route not found: ' + uri);
95
+ this.signal("error", (0, x4_events_1.EvError)(404, "route not found"));
96
+ return;
97
+ }
98
+ window.history.pushState({}, '', uri);
99
+ if (notify) {
100
+ found.handlers.forEach(h => {
101
+ h(found.params, uri);
102
+ });
103
+ }
104
+ }
105
+ _find(url) {
106
+ let matches = [];
107
+ let params = {};
108
+ let handlers = [];
109
+ for (const tmp of this.routes) {
110
+ if (!tmp.keys) {
111
+ matches = tmp.pattern.exec(url);
112
+ if (!matches) {
113
+ continue;
114
+ }
115
+ if (matches['groups']) {
116
+ for (const k in matches['groups']) {
117
+ params[k] = matches['groups'][k];
118
+ }
119
+ }
120
+ handlers = [...handlers, tmp.handler];
55
121
  }
56
- });
122
+ else if (tmp.keys.length > 0) {
123
+ matches = tmp.pattern.exec(url);
124
+ if (matches === null) {
125
+ continue;
126
+ }
127
+ for (let j = 0; j < tmp.keys.length;) {
128
+ params[tmp.keys[j]] = matches[++j];
129
+ }
130
+ handlers = [...handlers, tmp.handler];
131
+ }
132
+ else if (tmp.pattern.test(url)) {
133
+ handlers = [...handlers, tmp.handler];
134
+ }
135
+ }
136
+ return { params, handlers };
57
137
  }
58
138
  }
59
139
  exports.Router = Router;
package/lib/tabbar.d.ts CHANGED
@@ -48,9 +48,11 @@ export declare class TabBar extends Container<TabBarProps, TabBarEventMap> {
48
48
  private m_pages;
49
49
  private m_curPage;
50
50
  constructor(props: TabBarProps);
51
+ componentCreated(): void;
51
52
  addPage(page: ITabPage): void;
52
53
  render(): void;
53
- select(id: string): void;
54
+ select(id: string | null, notify?: boolean): void;
54
55
  private _select;
56
+ get selection(): Component<CProps<import("./base_component").BaseComponentEventMap>, import("./base_component").BaseComponentEventMap>;
55
57
  }
56
58
  export {};
package/lib/tabbar.js CHANGED
@@ -47,8 +47,10 @@ class TabBar extends component_1.Container {
47
47
  this.addClass('@hlayout');
48
48
  }
49
49
  this.m_props.pages?.forEach(p => this.addPage(p));
50
+ }
51
+ componentCreated() {
50
52
  if (this.m_props.default) {
51
- this.select(this.m_props.default);
53
+ this.select(this.m_props.default, true);
52
54
  }
53
55
  }
54
56
  addPage(page) {
@@ -58,28 +60,45 @@ class TabBar extends component_1.Container {
58
60
  render() {
59
61
  let buttons = [];
60
62
  this.m_pages.forEach(p => {
61
- p.btn = new button_1.Button({ cls: p === this.m_curPage ? 'selected' : '', text: p.title, icon: p.icon, click: () => this._select(p) });
63
+ p.btn = new button_1.Button({ cls: p === this.m_curPage ? 'selected' : '', text: p.title, icon: p.icon, click: () => this._select(p, true) });
62
64
  buttons.push(p.btn);
63
65
  });
64
66
  this.setContent(buttons);
65
67
  }
66
- select(id) {
67
- let page = this.m_pages.find(x => x.id === id);
68
- if (page) {
69
- this._select(page);
68
+ select(id, notify = false) {
69
+ if (!id) {
70
+ this._select(null, notify);
71
+ }
72
+ else {
73
+ let page = this.m_pages.find(x => x.id === id);
74
+ if (page) {
75
+ this._select(page, notify);
76
+ }
70
77
  }
71
78
  }
72
- _select(p) {
73
- if (this.dom && this.m_curPage && this.m_curPage.page) {
79
+ _select(p, notify) {
80
+ if (this.m_curPage == p) {
81
+ return;
82
+ }
83
+ if (this.dom && this.m_curPage) {
74
84
  this.m_curPage.btn.removeClass('selected');
75
- this.m_curPage.page.hide();
85
+ if (this.m_curPage.page) {
86
+ this.m_curPage.page.hide();
87
+ }
76
88
  }
77
89
  this.m_curPage = p;
78
- this.signal('change', (0, x4_events_1.EvChange)(p ? p.id : null));
79
- if (this.dom && this.m_curPage && this.m_curPage.page) {
90
+ if (notify) {
91
+ this.signal('change', (0, x4_events_1.EvChange)(p ? p.id : null));
92
+ }
93
+ if (this.dom && this.m_curPage) {
80
94
  this.m_curPage.btn.addClass('selected');
81
- this.m_curPage.page.show();
95
+ if (this.m_curPage.page) {
96
+ this.m_curPage.page.show();
97
+ }
82
98
  }
83
99
  }
100
+ get selection() {
101
+ return this.m_curPage?.page;
102
+ }
84
103
  }
85
104
  exports.TabBar = TabBar;
@@ -106,6 +106,14 @@ export interface EvDrag extends BasicEvent {
106
106
  data: any;
107
107
  }
108
108
  export declare function EvDrag(element: unknown, data: any, ctx: any): EvDrag;
109
+ /**
110
+ * Errors
111
+ */
112
+ export interface EvError extends BasicEvent {
113
+ code: number;
114
+ message: string;
115
+ }
116
+ export declare function EvError(code: number, message: string): EvError;
109
117
  /**
110
118
  * this Base interface is used to describe available events & their types
111
119
  *
package/lib/x4_events.js CHANGED
@@ -28,7 +28,7 @@
28
28
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29
29
  **/
30
30
  Object.defineProperty(exports, "__esModule", { value: true });
31
- exports.EventSource = exports.EvDrag = exports.EvMessage = exports.EvTimer = exports.EvContextMenu = exports.EvSelectionChange = exports.EvChange = exports.EvClick = exports.BasicEvent = void 0;
31
+ exports.EventSource = exports.EvError = exports.EvDrag = exports.EvMessage = exports.EvTimer = exports.EvContextMenu = exports.EvSelectionChange = exports.EvChange = exports.EvClick = exports.BasicEvent = void 0;
32
32
  // default stopPropagation implementation for Events
33
33
  const stopPropagation = function () {
34
34
  this.propagationStopped = true;
@@ -81,6 +81,10 @@ function EvDrag(element, data, ctx) {
81
81
  return BasicEvent({ element, data, context: ctx });
82
82
  }
83
83
  exports.EvDrag = EvDrag;
84
+ function EvError(code, message) {
85
+ return BasicEvent({ code, message });
86
+ }
87
+ exports.EvError = EvError;
84
88
  /**
85
89
  * Event emitter class
86
90
  * this class allow you to emit and handle events
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x4js",
3
- "version": "1.4.14",
3
+ "version": "1.4.15",
4
4
  "description": "X4js core files",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
package/src/component.ts CHANGED
@@ -188,7 +188,7 @@ export class Component<P extends CProps<BaseComponentEventMap> = CProps<BaseComp
188
188
  private static __privateEvents: any = {};
189
189
  private static __sizeObserver: ResizeObserver; // resize observer
190
190
  private static __createObserver: MutationObserver; // creation observer
191
- private static __intersectionObserver: IntersectionObserver; // visibility observer
191
+ //private static __intersectionObserver: IntersectionObserver; // visibility observer
192
192
 
193
193
  private static __capture: ICaptureInfo = null;
194
194
  private static __capture_mask = null;
@@ -202,6 +202,11 @@ export class Component<P extends CProps<BaseComponentEventMap> = CProps<BaseComp
202
202
  dom_events: {},
203
203
  uid: Component.__comp_guid++,
204
204
  inrender: false,
205
+ };
206
+
207
+ // prepare iprops
208
+ if( this.m_props.cls ) {
209
+ this.addClass( this.m_props.cls );
205
210
  }
206
211
  }
207
212
 
@@ -1389,7 +1394,8 @@ export class Component<P extends CProps<BaseComponentEventMap> = CProps<BaseComp
1389
1394
  me = Object.getPrototypeOf(me);
1390
1395
  }
1391
1396
 
1392
- this.addClass(this.m_props.cls);
1397
+ //done in ctor now
1398
+ //this.addClass(this.m_props.cls);
1393
1399
  }
1394
1400
 
1395
1401
  /**
package/src/layout.ts CHANGED
@@ -378,11 +378,20 @@ export class ScrollView extends Component<ScrollViewProps> {
378
378
  export class Masonry extends Container {
379
379
 
380
380
  constructor(props) {
381
+ const items = props.items;
382
+ props.items = undefined;
383
+
381
384
  super(props);
382
385
 
383
386
  this.setDomEvent('sizechange', () => {
384
387
  this.resizeAllItems( );
385
388
  });
389
+
390
+ if( items ) {
391
+ items.forEach( i => {
392
+ this.addItem( i );
393
+ });
394
+ }
386
395
  }
387
396
 
388
397
  resizeItem(item: Component) {
package/src/listview.ts CHANGED
@@ -100,7 +100,7 @@ export interface ListViewProps<E extends ListViewEventMap = ListViewEventMap> ex
100
100
  * Standard listview class
101
101
  */
102
102
 
103
- export class ListView<T extends ListViewProps = ListViewProps, E extends ListViewEventMap = ListViewEventMap> extends VLayout<T,E> {
103
+ export class ListView extends VLayout<ListViewProps,ListViewEventMap> {
104
104
 
105
105
  protected m_selection: {
106
106
  item: ListViewItem;
@@ -117,7 +117,7 @@ export class ListView<T extends ListViewProps = ListViewProps, E extends ListVie
117
117
 
118
118
  protected m_cache: Map<number, Component>; // recycling elements
119
119
 
120
- constructor(props: T) {
120
+ constructor(props: ListViewProps) {
121
121
  super(props);
122
122
 
123
123
  this.setDomEvent('keydown', (e) => this._handleKey(e));
package/src/router.ts CHANGED
@@ -27,45 +27,157 @@
27
27
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
28
  **/
29
29
 
30
- type Callback = (request: { path: string }) => void;
30
+ import { EventSource, EvError, EventMap } from "./x4_events"
31
+
32
+ type RouteHandler = ( params: any, path: string ) => void;
33
+
34
+ interface Segment {
35
+ keys: string[],
36
+ pattern: RegExp;
37
+ }
31
38
 
32
39
  interface Route {
33
- uri: string;
34
- callback: Callback;
40
+ keys: string[],
41
+ pattern: RegExp;
42
+ handler: RouteHandler;
35
43
  }
36
44
 
37
- export class Router {
45
+ function parseRoute(str: string | RegExp, loose = false): Segment {
38
46
 
39
- private routes: Route[];
47
+ if (str instanceof RegExp) {
48
+ return {
49
+ keys: null,
50
+ pattern: str
51
+ };
52
+ }
40
53
 
41
- constructor() {
42
- this.routes = [];
54
+ const arr = str.split('/');
55
+
56
+ let keys = [];
57
+ let pattern = '';
58
+
59
+ if( arr[0]=='' ) {
60
+ arr.shift();
43
61
  }
44
62
 
45
- get(uri, callback) {
63
+ for (const tmp of arr) {
64
+ const c = tmp[0];
46
65
 
47
- // throw an error if the route uri already exists to avoid confilicting routes
48
- this.routes.forEach(route => {
49
- if (route.uri === uri) {
50
- throw new Error(`the uri ${route.uri} already exists`);
66
+ if (c === '*') {
67
+ keys.push('wild');
68
+ pattern += '/(.*)';
69
+ }
70
+ else if (c === ':') {
71
+ const o = tmp.indexOf('?', 1);
72
+ const ext = tmp.indexOf('.', 1);
73
+
74
+ keys.push(tmp.substring(1, o >= 0 ? o : ext >= 0 ? ext : tmp.length));
75
+ pattern += o < 0 && ext < 0 ? '(?:/([^/]+?))?' : '/([^/]+?)';
76
+ if (ext >= 0) {
77
+ pattern += (o >= 0 ? '?' : '') + '\\' + tmp.substring(ext);
51
78
  }
52
- });
79
+ }
80
+ else {
81
+ pattern += '/' + tmp;
82
+ }
83
+ }
84
+
85
+ return {
86
+ keys,
87
+ pattern: new RegExp( `^${pattern}${loose ? '(?=$|\/)' : '\/?$'}`, 'i' )
88
+ };
89
+ }
90
+
91
+ interface RouterEventMap extends EventMap {
92
+ error: EvError;
93
+ }
94
+
95
+ export class Router extends EventSource< RouterEventMap > {
96
+
97
+ private routes: Route[];
53
98
 
54
- this.routes.push({
55
- uri,
56
- callback
99
+ constructor() {
100
+ super( );
101
+
102
+ this.routes = [];
103
+
104
+ window.addEventListener('popstate', (event) => {
105
+ const url = document.location.pathname;
106
+ const found = this._find(url);
107
+
108
+ found.handlers.forEach(h => {
109
+ h(found.params,url);
110
+ });
57
111
  });
58
112
  }
59
113
 
114
+ get(uri: string | RegExp, handler: RouteHandler ) {
115
+ let { keys, pattern } = parseRoute(uri);
116
+ this.routes.push({ keys, pattern, handler });
117
+ }
118
+
60
119
  init() {
61
- this.routes.some(route => {
120
+ this.navigate( window.location.pathname );
121
+ }
122
+
123
+ navigate( uri: string, notify = true ) {
124
+
125
+ const found = this._find( uri );
62
126
 
63
- let regEx = new RegExp(`^${route.uri}$`);
64
- let path = window.location.pathname;
127
+ if( !found || found.handlers.length==0 ) {
128
+ //window.history.pushState({}, '', 'error')
129
+ console.log( 'route not found: '+uri );
130
+ this.signal( "error", EvError( 404, "route not found" ) );
131
+ return;
132
+ }
65
133
 
66
- if (path.match(regEx)) {
67
- return route.callback({ path });
134
+ window.history.pushState({}, '', uri )
135
+
136
+ if( notify ) {
137
+ found.handlers.forEach( h => {
138
+ h( found.params, uri );
139
+ } );
140
+ }
141
+ }
142
+
143
+ private _find( url: string ): { params: any, handlers: RouteHandler[] } {
144
+
145
+ let matches = [];
146
+ let params = {};
147
+ let handlers = [];
148
+
149
+ for (const tmp of this.routes ) {
150
+ if (!tmp.keys ) {
151
+ matches = tmp.pattern.exec(url);
152
+ if (!matches) {
153
+ continue;
154
+ }
155
+
156
+ if (matches['groups']) {
157
+ for (const k in matches['groups']) {
158
+ params[k] = matches['groups'][k];
159
+ }
160
+ }
161
+
162
+ handlers = [...handlers, tmp.handler];
163
+ }
164
+ else if (tmp.keys.length > 0) {
165
+ matches = tmp.pattern.exec(url);
166
+ if (matches === null) {
167
+ continue;
168
+ }
169
+
170
+ for ( let j = 0; j < tmp.keys.length;) {
171
+ params[tmp.keys[j]] = matches[++j];
172
+ }
173
+
174
+ handlers = [...handlers, tmp.handler];
175
+ }
176
+ else if (tmp.pattern.test(url)) {
177
+ handlers = [...handlers, tmp.handler];
68
178
  }
69
- })
179
+ }
180
+
181
+ return { params, handlers };
70
182
  }
71
183
  }
package/src/tabbar.ts CHANGED
@@ -75,9 +75,12 @@ export class TabBar extends Container<TabBarProps,TabBarEventMap> {
75
75
  }
76
76
 
77
77
  this.m_props.pages?.forEach( p => this.addPage(p) );
78
+ }
79
+
80
+ componentCreated(): void {
78
81
  if( this.m_props.default ) {
79
- this.select( this.m_props.default );
80
- }
82
+ this.select( this.m_props.default, true );
83
+ }
81
84
  }
82
85
 
83
86
  addPage( page: ITabPage ) {
@@ -88,33 +91,53 @@ export class TabBar extends Container<TabBarProps,TabBarEventMap> {
88
91
  render( ) {
89
92
  let buttons = [];
90
93
  this.m_pages.forEach( p => {
91
- p.btn = new Button( { cls: p===this.m_curPage ? 'selected' : '', text: p.title, icon: p.icon, click: () => this._select(p) } );
94
+ p.btn = new Button( { cls: p===this.m_curPage ? 'selected' : '', text: p.title, icon: p.icon, click: () => this._select(p,true) } );
92
95
  buttons.push( p.btn );
93
96
  });
94
97
 
95
98
  this.setContent( buttons );
96
99
  }
97
100
 
98
- select( id: string ) {
99
- let page = this.m_pages.find( x => x.id===id );
100
- if( page ) {
101
- this._select( page );
101
+ select( id: string | null, notify = false ) {
102
+ if( !id ) {
103
+ this._select( null, notify );
104
+ }
105
+ else {
106
+ let page = this.m_pages.find( x => x.id===id );
107
+ if( page ) {
108
+ this._select( page, notify );
109
+ }
102
110
  }
103
111
  }
104
112
 
105
- private _select( p: TabPage ) {
113
+ private _select( p: TabPage, notify: boolean ) {
106
114
 
107
- if( this.dom && this.m_curPage && this.m_curPage.page ) {
115
+ if( this.m_curPage==p ) {
116
+ return;
117
+ }
118
+
119
+ if( this.dom && this.m_curPage ) {
108
120
  this.m_curPage.btn.removeClass( 'selected' );
109
- this.m_curPage.page.hide( );
121
+ if( this.m_curPage.page ) {
122
+ this.m_curPage.page.hide( );
123
+ }
110
124
  }
111
125
 
112
126
  this.m_curPage = p;
113
- this.signal( 'change', EvChange(p ? p.id : null) );
114
127
 
115
- if( this.dom && this.m_curPage && this.m_curPage.page ) {
128
+ if( notify ) {
129
+ this.signal( 'change', EvChange(p ? p.id : null) );
130
+ }
131
+
132
+ if( this.dom && this.m_curPage ) {
116
133
  this.m_curPage.btn.addClass( 'selected' );
117
- this.m_curPage.page.show( );
134
+ if( this.m_curPage.page ) {
135
+ this.m_curPage.page.show( );
136
+ }
118
137
  }
119
138
  }
139
+
140
+ get selection( ) {
141
+ return this.m_curPage?.page;
142
+ }
120
143
  }
package/src/x4_events.ts CHANGED
@@ -168,6 +168,19 @@ export function EvDrag(element: unknown, data: any, ctx: any ) {
168
168
  return BasicEvent<EvDrag>({ element, data, context: ctx });
169
169
  }
170
170
 
171
+ /**
172
+ * Errors
173
+ */
174
+
175
+ export interface EvError extends BasicEvent {
176
+ code: number;
177
+ message: string;
178
+ }
179
+
180
+ export function EvError( code: number, message: string ) : EvError {
181
+ return BasicEvent<EvError>( {code, message} );
182
+ }
183
+
171
184
 
172
185
  /**
173
186
  * this Base interface is used to describe available events & their types