x4js 1.4.13 → 1.4.16
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/lib/application.d.ts +1 -0
- package/lib/application.js +28 -0
- package/lib/component.d.ts +0 -1
- package/lib/component.js +7 -2
- package/lib/gridview.js +52 -16
- package/lib/layout.js +7 -0
- package/lib/listview.d.ts +2 -2
- package/lib/router.d.ts +10 -2
- package/lib/router.js +98 -18
- package/lib/tabbar.d.ts +3 -1
- package/lib/tabbar.js +38 -12
- package/lib/x4.css +21 -1
- package/lib/x4_events.d.ts +8 -0
- package/lib/x4_events.js +5 -1
- package/package.json +1 -1
- package/src/application.ts +36 -0
- package/src/component.ts +8 -2
- package/src/gridview.ts +66 -23
- package/src/layout.ts +9 -0
- package/src/listview.ts +2 -2
- package/src/router.ts +134 -22
- package/src/tabbar.ts +44 -13
- package/src/x4.less +30 -4
- package/src/x4_events.ts +13 -0
package/lib/application.d.ts
CHANGED
package/lib/application.js
CHANGED
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
31
|
exports.Application = void 0;
|
|
32
32
|
const base_component_1 = require("./base_component");
|
|
33
|
+
const component_1 = require("./component");
|
|
33
34
|
const settings_1 = require("./settings");
|
|
34
35
|
const tools_1 = require("./tools");
|
|
35
36
|
/**
|
|
@@ -145,6 +146,33 @@ class Application extends base_component_1.BaseComponent {
|
|
|
145
146
|
}
|
|
146
147
|
enterModal(enter) {
|
|
147
148
|
}
|
|
149
|
+
handleTouchEvents() {
|
|
150
|
+
document.addEventListener('touchstart', (ev) => {
|
|
151
|
+
let me = this;
|
|
152
|
+
let now = new Date().getTime();
|
|
153
|
+
if (!me.__last_touch || (me.__last_touch - now) > 700) {
|
|
154
|
+
me.__touch_cnt = 1;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
me.__touch_cnt++;
|
|
158
|
+
}
|
|
159
|
+
me.__last_touch = now;
|
|
160
|
+
if (me.__touch_cnt == 2) {
|
|
161
|
+
me.__touch_cnt = 0;
|
|
162
|
+
let fake = {
|
|
163
|
+
type: "dblclick",
|
|
164
|
+
};
|
|
165
|
+
const tch = ev.touches[0];
|
|
166
|
+
for (const n in tch) {
|
|
167
|
+
fake[n] = tch[n];
|
|
168
|
+
}
|
|
169
|
+
// ts-ignore -> private
|
|
170
|
+
component_1.Component._dispatchEvent(fake);
|
|
171
|
+
ev.preventDefault();
|
|
172
|
+
ev.stopPropagation();
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
148
176
|
}
|
|
149
177
|
exports.Application = Application;
|
|
150
178
|
;
|
package/lib/component.d.ts
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
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/gridview.js
CHANGED
|
@@ -289,6 +289,7 @@ class GridView extends layout_1.VLayout {
|
|
|
289
289
|
},
|
|
290
290
|
content: this.m_container
|
|
291
291
|
});
|
|
292
|
+
let flex = false;
|
|
292
293
|
let cols = this.m_columns.map((col, index) => {
|
|
293
294
|
let cls = '@cell';
|
|
294
295
|
if (col.cls) {
|
|
@@ -325,10 +326,16 @@ class GridView extends layout_1.VLayout {
|
|
|
325
326
|
sens: 'right',
|
|
326
327
|
events: { resize: (e) => resizeCol(e) }
|
|
327
328
|
});
|
|
328
|
-
col
|
|
329
|
+
if (col.flex) {
|
|
330
|
+
flex = true;
|
|
331
|
+
}
|
|
332
|
+
col.$hdr = comp;
|
|
329
333
|
return comp;
|
|
330
334
|
});
|
|
331
|
-
cols.push(new component_1.Flex({
|
|
335
|
+
cols.push(new component_1.Flex({
|
|
336
|
+
ref: 'flex',
|
|
337
|
+
cls: flex ? '@hidden' : ''
|
|
338
|
+
}));
|
|
332
339
|
// compute full width
|
|
333
340
|
let full_width = 0;
|
|
334
341
|
this.m_columns.forEach((col) => {
|
|
@@ -358,9 +365,13 @@ class GridView extends layout_1.VLayout {
|
|
|
358
365
|
width: col.width
|
|
359
366
|
}
|
|
360
367
|
});
|
|
368
|
+
col.$ftr = comp;
|
|
361
369
|
return comp;
|
|
362
370
|
});
|
|
363
|
-
foots.push(new component_1.Flex({
|
|
371
|
+
foots.push(new component_1.Flex({
|
|
372
|
+
ref: 'flex',
|
|
373
|
+
cls: flex ? '@hidden' : ''
|
|
374
|
+
}));
|
|
364
375
|
this.m_footer = new layout_1.HLayout({
|
|
365
376
|
cls: '@footer',
|
|
366
377
|
content: foots,
|
|
@@ -380,8 +391,33 @@ class GridView extends layout_1.VLayout {
|
|
|
380
391
|
]);
|
|
381
392
|
}
|
|
382
393
|
_on_col_resize(col, width) {
|
|
383
|
-
this.m_columns[col]
|
|
384
|
-
|
|
394
|
+
const _col = this.m_columns[col];
|
|
395
|
+
let updateFlex = false;
|
|
396
|
+
if (width >= 0) {
|
|
397
|
+
_col.width = width;
|
|
398
|
+
if (_col.flex) {
|
|
399
|
+
_col.$hdr.removeClass('@flex');
|
|
400
|
+
_col.flex = undefined;
|
|
401
|
+
updateFlex = true;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
else if (width < 0 && !_col.flex) {
|
|
405
|
+
_col.$hdr.addClass('@flex');
|
|
406
|
+
_col.flex = 1;
|
|
407
|
+
updateFlex = true;
|
|
408
|
+
}
|
|
409
|
+
if (updateFlex) {
|
|
410
|
+
let flex = false;
|
|
411
|
+
this.m_columns.forEach(c => {
|
|
412
|
+
if (c.flex) {
|
|
413
|
+
flex = true;
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
this.m_header.itemWithRef('flex')?.show(flex ? false : true);
|
|
417
|
+
if (this.m_footer) {
|
|
418
|
+
this.m_footer.itemWithRef('flex')?.show(flex ? false : true);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
385
421
|
this._updateScroll(true);
|
|
386
422
|
}
|
|
387
423
|
/**
|
|
@@ -393,19 +429,19 @@ class GridView extends layout_1.VLayout {
|
|
|
393
429
|
}
|
|
394
430
|
this.m_columns.forEach((c) => {
|
|
395
431
|
if (c !== col) {
|
|
396
|
-
c.$
|
|
432
|
+
c.$hdr.sorted = false;
|
|
397
433
|
}
|
|
398
434
|
});
|
|
399
|
-
const $
|
|
400
|
-
if ($
|
|
401
|
-
$
|
|
435
|
+
const $hdr = col.$hdr;
|
|
436
|
+
if ($hdr.sorted) {
|
|
437
|
+
$hdr.toggleSens();
|
|
402
438
|
}
|
|
403
439
|
else {
|
|
404
|
-
$
|
|
440
|
+
$hdr.sorted = true;
|
|
405
441
|
}
|
|
406
442
|
if (this.m_dataview) {
|
|
407
443
|
this.m_dataview.sort([
|
|
408
|
-
{ field: col.id, ascending: $
|
|
444
|
+
{ field: col.id, ascending: $hdr.sens == 'dn' ? false : true }
|
|
409
445
|
]);
|
|
410
446
|
}
|
|
411
447
|
}
|
|
@@ -457,7 +493,7 @@ class GridView extends layout_1.VLayout {
|
|
|
457
493
|
let cidx = 0;
|
|
458
494
|
let index = this.m_topIndex;
|
|
459
495
|
let count = this.m_dataview ? this.m_dataview.count : 0;
|
|
460
|
-
let full_width = 0;
|
|
496
|
+
let full_width = 0; // todo: +4 pixel of left border
|
|
461
497
|
let even = this.m_topIndex & 1 ? true : false;
|
|
462
498
|
// compute full width
|
|
463
499
|
this.m_columns.forEach((col) => {
|
|
@@ -581,16 +617,16 @@ class GridView extends layout_1.VLayout {
|
|
|
581
617
|
}
|
|
582
618
|
this.m_empty_msg.show(show);
|
|
583
619
|
if (full_width < rc.width) {
|
|
584
|
-
|
|
585
|
-
|
|
620
|
+
this.m_header.setStyleValue('width', null);
|
|
621
|
+
this.m_footer?.setStyleValue('width', null);
|
|
586
622
|
this.m_container.setStyle({
|
|
587
623
|
height: count * this.m_itemHeight,
|
|
588
624
|
width: null
|
|
589
625
|
});
|
|
590
626
|
}
|
|
591
627
|
else {
|
|
592
|
-
this.m_header.setStyleValue('width', full_width +
|
|
593
|
-
this.m_footer?.setStyleValue('width', full_width +
|
|
628
|
+
this.m_header.setStyleValue('width', full_width + 1000);
|
|
629
|
+
this.m_footer?.setStyleValue('width', full_width + 1000);
|
|
594
630
|
this.m_container.setStyle({
|
|
595
631
|
height: count * this.m_itemHeight,
|
|
596
632
|
width: full_width
|
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
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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):
|
|
54
|
+
select(id: string | null, notify?: boolean): boolean;
|
|
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,52 @@ 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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
select(id, notify = false) {
|
|
69
|
+
if (!id) {
|
|
70
|
+
this._select(null, notify);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
let page = this.m_pages.find(x => x.id === id);
|
|
75
|
+
if (page) {
|
|
76
|
+
this._select(page, notify);
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
70
80
|
}
|
|
71
81
|
}
|
|
72
|
-
_select(p) {
|
|
73
|
-
if (this.
|
|
82
|
+
_select(p, notify) {
|
|
83
|
+
if (this.m_curPage == p) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!this.dom) {
|
|
87
|
+
this.m_props.default = p.id;
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (this.m_curPage) {
|
|
74
91
|
this.m_curPage.btn.removeClass('selected');
|
|
75
|
-
this.m_curPage.page
|
|
92
|
+
if (this.m_curPage.page) {
|
|
93
|
+
this.m_curPage.page.hide();
|
|
94
|
+
}
|
|
76
95
|
}
|
|
77
96
|
this.m_curPage = p;
|
|
78
|
-
|
|
79
|
-
|
|
97
|
+
if (notify) {
|
|
98
|
+
this.signal('change', (0, x4_events_1.EvChange)(p ? p.id : null));
|
|
99
|
+
}
|
|
100
|
+
if (this.m_curPage) {
|
|
80
101
|
this.m_curPage.btn.addClass('selected');
|
|
81
|
-
this.m_curPage.page
|
|
102
|
+
if (this.m_curPage.page) {
|
|
103
|
+
this.m_curPage.page.show();
|
|
104
|
+
}
|
|
82
105
|
}
|
|
83
106
|
}
|
|
107
|
+
get selection() {
|
|
108
|
+
return this.m_curPage?.page;
|
|
109
|
+
}
|
|
84
110
|
}
|
|
85
111
|
exports.TabBar = TabBar;
|
package/lib/x4.css
CHANGED
|
@@ -306,6 +306,8 @@ textarea::selection {
|
|
|
306
306
|
align-items: center;
|
|
307
307
|
outline: none;
|
|
308
308
|
cursor: pointer;
|
|
309
|
+
font-family: var(--x4-font);
|
|
310
|
+
font-size: var(--x4-font-size);
|
|
309
311
|
height: 2rem;
|
|
310
312
|
padding: 8px;
|
|
311
313
|
overflow: hidden;
|
|
@@ -1124,7 +1126,7 @@ textarea::selection {
|
|
|
1124
1126
|
.x-spreadsheet .x-row,
|
|
1125
1127
|
.x-grid-view .x-row {
|
|
1126
1128
|
position: absolute;
|
|
1127
|
-
width: 100
|
|
1129
|
+
width: calc(100% - 4px);
|
|
1128
1130
|
border-bottom: 1px solid #f0f0f0;
|
|
1129
1131
|
align-items: center;
|
|
1130
1132
|
height: 2em;
|
|
@@ -1691,3 +1693,21 @@ textarea::selection {
|
|
|
1691
1693
|
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
|
1692
1694
|
grid-auto-rows: 10px;
|
|
1693
1695
|
}
|
|
1696
|
+
.x-tab-bar {
|
|
1697
|
+
border-bottom: 1px solid var(--gray-600);
|
|
1698
|
+
background-color: var(--gray-100);
|
|
1699
|
+
}
|
|
1700
|
+
.x-tab-bar > .x-button {
|
|
1701
|
+
border: none;
|
|
1702
|
+
color: var(--gray-600);
|
|
1703
|
+
}
|
|
1704
|
+
.x-tab-bar > .x-button.selected {
|
|
1705
|
+
font-weight: bold;
|
|
1706
|
+
border-bottom: none;
|
|
1707
|
+
color: var(--x4-selection-color);
|
|
1708
|
+
background-color: transparent;
|
|
1709
|
+
}
|
|
1710
|
+
.x-tab-bar > .x-button:focus:not(.selected) {
|
|
1711
|
+
text-decoration: underline;
|
|
1712
|
+
color: black;
|
|
1713
|
+
}
|
package/lib/x4_events.d.ts
CHANGED
|
@@ -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
package/src/application.ts
CHANGED
|
@@ -194,6 +194,42 @@ export class Application<P extends ApplicationProps = ApplicationProps, E extend
|
|
|
194
194
|
|
|
195
195
|
public enterModal( enter: boolean ) {
|
|
196
196
|
}
|
|
197
|
+
|
|
198
|
+
public handleTouchEvents( ) {
|
|
199
|
+
document.addEventListener( 'touchstart', ( ev: TouchEvent ) => {
|
|
200
|
+
|
|
201
|
+
let me = this as any;
|
|
202
|
+
let now = new Date( ).getTime();
|
|
203
|
+
|
|
204
|
+
if( !me.__last_touch || (me.__last_touch-now) > 700 ) {
|
|
205
|
+
me.__touch_cnt = 1;
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
me.__touch_cnt++;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
me.__last_touch = now;
|
|
212
|
+
|
|
213
|
+
if( me.__touch_cnt==2 ) {
|
|
214
|
+
me.__touch_cnt = 0;
|
|
215
|
+
|
|
216
|
+
let fake = {
|
|
217
|
+
type: "dblclick",
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const tch = ev.touches[0];
|
|
221
|
+
for( const n in tch ) {
|
|
222
|
+
fake[n] = tch[n];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ts-ignore -> private
|
|
226
|
+
(Component as any)._dispatchEvent( fake );
|
|
227
|
+
|
|
228
|
+
ev.preventDefault( );
|
|
229
|
+
ev.stopPropagation( );
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
}
|
|
197
233
|
};
|
|
198
234
|
|
|
199
235
|
|
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
|
-
|
|
1397
|
+
//done in ctor now
|
|
1398
|
+
//this.addClass(this.m_props.cls);
|
|
1393
1399
|
}
|
|
1394
1400
|
|
|
1395
1401
|
/**
|
package/src/gridview.ts
CHANGED
|
@@ -76,7 +76,8 @@ export interface GridColumn {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
interface GridColumnInternal extends GridColumn {
|
|
79
|
-
$
|
|
79
|
+
$hdr: ColHeader;
|
|
80
|
+
$ftr: Component;
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
|
|
@@ -421,6 +422,7 @@ export class GridView extends VLayout<GridViewProps, GridViewEventMap> {
|
|
|
421
422
|
content: this.m_container
|
|
422
423
|
});
|
|
423
424
|
|
|
425
|
+
let flex = false;
|
|
424
426
|
let cols = this.m_columns.map((col, index) => {
|
|
425
427
|
|
|
426
428
|
let cls = '@cell';
|
|
@@ -463,12 +465,19 @@ export class GridView extends VLayout<GridViewProps, GridViewEventMap> {
|
|
|
463
465
|
events: {resize: ( e ) => resizeCol(e )}
|
|
464
466
|
});
|
|
465
467
|
|
|
466
|
-
(
|
|
468
|
+
if( col.flex ) {
|
|
469
|
+
flex = true;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
(<any>col).$hdr = comp;
|
|
467
473
|
return comp;
|
|
468
474
|
});
|
|
469
475
|
|
|
470
|
-
(cols as any).push( new Flex( {
|
|
471
|
-
|
|
476
|
+
(cols as any).push( new Flex( {
|
|
477
|
+
ref: 'flex',
|
|
478
|
+
cls: flex ? '@hidden' : ''
|
|
479
|
+
} ) );
|
|
480
|
+
|
|
472
481
|
// compute full width
|
|
473
482
|
let full_width = 0;
|
|
474
483
|
this.m_columns.forEach((col) => {
|
|
@@ -481,8 +490,7 @@ export class GridView extends VLayout<GridViewProps, GridViewEventMap> {
|
|
|
481
490
|
style: {
|
|
482
491
|
minWidth: full_width
|
|
483
492
|
}
|
|
484
|
-
});
|
|
485
|
-
|
|
493
|
+
});
|
|
486
494
|
|
|
487
495
|
if( this.m_props.hasFooter ) {
|
|
488
496
|
let foots = this.m_columns.map((col, index) => {
|
|
@@ -506,11 +514,15 @@ export class GridView extends VLayout<GridViewProps, GridViewEventMap> {
|
|
|
506
514
|
}
|
|
507
515
|
});
|
|
508
516
|
|
|
517
|
+
(col as GridColumnInternal).$ftr = comp;
|
|
509
518
|
return comp;
|
|
510
519
|
});
|
|
511
520
|
|
|
512
|
-
(foots as any).push( new Flex( {
|
|
513
|
-
|
|
521
|
+
(foots as any).push( new Flex( {
|
|
522
|
+
ref: 'flex',
|
|
523
|
+
cls: flex ? '@hidden' : ''
|
|
524
|
+
} ) );
|
|
525
|
+
|
|
514
526
|
this.m_footer = new HLayout({
|
|
515
527
|
cls: '@footer',
|
|
516
528
|
content: <any>foots,
|
|
@@ -532,9 +544,40 @@ export class GridView extends VLayout<GridViewProps, GridViewEventMap> {
|
|
|
532
544
|
|
|
533
545
|
}
|
|
534
546
|
|
|
535
|
-
private _on_col_resize(col, width) {
|
|
536
|
-
|
|
537
|
-
this.m_columns[col]
|
|
547
|
+
private _on_col_resize(col: number, width: number) {
|
|
548
|
+
|
|
549
|
+
const _col = this.m_columns[col] as GridColumnInternal;
|
|
550
|
+
|
|
551
|
+
let updateFlex = false;
|
|
552
|
+
|
|
553
|
+
if( width>=0 ) {
|
|
554
|
+
_col.width = width;
|
|
555
|
+
if( _col.flex ) {
|
|
556
|
+
_col.$hdr.removeClass( '@flex' );
|
|
557
|
+
_col.flex = undefined;
|
|
558
|
+
updateFlex = true;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
else if( width<0 && !_col.flex ) {
|
|
562
|
+
_col.$hdr.addClass( '@flex' );
|
|
563
|
+
_col.flex = 1;
|
|
564
|
+
updateFlex = true;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if( updateFlex ) {
|
|
568
|
+
let flex = false;
|
|
569
|
+
this.m_columns.forEach( c => {
|
|
570
|
+
if( c.flex ) {
|
|
571
|
+
flex = true;
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
this.m_header.itemWithRef( 'flex' )?.show( flex ? false : true );
|
|
576
|
+
if( this.m_footer ) {
|
|
577
|
+
this.m_footer.itemWithRef( 'flex' )?.show( flex ? false : true );
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
538
581
|
this._updateScroll(true);
|
|
539
582
|
}
|
|
540
583
|
|
|
@@ -550,22 +593,22 @@ export class GridView extends VLayout<GridViewProps, GridViewEventMap> {
|
|
|
550
593
|
|
|
551
594
|
this.m_columns.forEach((c) => {
|
|
552
595
|
if (c !== col) {
|
|
553
|
-
(c as GridColumnInternal).$
|
|
596
|
+
(c as GridColumnInternal).$hdr.sorted = false;
|
|
554
597
|
}
|
|
555
598
|
});
|
|
556
599
|
|
|
557
|
-
const $
|
|
600
|
+
const $hdr = (col as GridColumnInternal).$hdr;
|
|
558
601
|
|
|
559
|
-
if ($
|
|
560
|
-
$
|
|
602
|
+
if ($hdr.sorted) {
|
|
603
|
+
$hdr.toggleSens( );
|
|
561
604
|
}
|
|
562
605
|
else {
|
|
563
|
-
$
|
|
606
|
+
$hdr.sorted = true;
|
|
564
607
|
}
|
|
565
608
|
|
|
566
609
|
if (this.m_dataview) {
|
|
567
610
|
this.m_dataview.sort([
|
|
568
|
-
{ field: col.id, ascending: $
|
|
611
|
+
{ field: col.id, ascending: $hdr.sens=='dn' ? false : true }
|
|
569
612
|
]);
|
|
570
613
|
}
|
|
571
614
|
}
|
|
@@ -630,7 +673,8 @@ export class GridView extends VLayout<GridViewProps, GridViewEventMap> {
|
|
|
630
673
|
let cidx = 0;
|
|
631
674
|
let index = this.m_topIndex;
|
|
632
675
|
let count = this.m_dataview ? this.m_dataview.count : 0;
|
|
633
|
-
|
|
676
|
+
|
|
677
|
+
let full_width = 0; // todo: +4 pixel of left border
|
|
634
678
|
let even = this.m_topIndex & 1 ? true : false;
|
|
635
679
|
|
|
636
680
|
// compute full width
|
|
@@ -783,17 +827,16 @@ export class GridView extends VLayout<GridViewProps, GridViewEventMap> {
|
|
|
783
827
|
this.m_empty_msg.show(show);
|
|
784
828
|
|
|
785
829
|
if (full_width < rc.width) {
|
|
786
|
-
|
|
787
|
-
|
|
830
|
+
this.m_header.setStyleValue('width', null);
|
|
831
|
+
this.m_footer?.setStyleValue('width', null);
|
|
788
832
|
this.m_container.setStyle({
|
|
789
833
|
height: count * this.m_itemHeight,
|
|
790
834
|
width: null
|
|
791
835
|
});
|
|
792
836
|
}
|
|
793
837
|
else {
|
|
794
|
-
this.m_header.setStyleValue('width', full_width +
|
|
795
|
-
this.m_footer?.setStyleValue('width', full_width +
|
|
796
|
-
|
|
838
|
+
this.m_header.setStyleValue('width', full_width + 1000 );
|
|
839
|
+
this.m_footer?.setStyleValue('width', full_width + 1000 );
|
|
797
840
|
this.m_container.setStyle({
|
|
798
841
|
height: count * this.m_itemHeight,
|
|
799
842
|
width: full_width
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
34
|
-
|
|
40
|
+
keys: string[],
|
|
41
|
+
pattern: RegExp;
|
|
42
|
+
handler: RouteHandler;
|
|
35
43
|
}
|
|
36
44
|
|
|
37
|
-
|
|
45
|
+
function parseRoute(str: string | RegExp, loose = false): Segment {
|
|
38
46
|
|
|
39
|
-
|
|
47
|
+
if (str instanceof RegExp) {
|
|
48
|
+
return {
|
|
49
|
+
keys: null,
|
|
50
|
+
pattern: str
|
|
51
|
+
};
|
|
52
|
+
}
|
|
40
53
|
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
63
|
+
for (const tmp of arr) {
|
|
64
|
+
const c = tmp[0];
|
|
46
65
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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.
|
|
120
|
+
this.navigate( window.location.pathname );
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
navigate( uri: string, notify = true ) {
|
|
124
|
+
|
|
125
|
+
const found = this._find( uri );
|
|
62
126
|
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
67
|
-
|
|
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,61 @@ 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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
select( id: string | null, notify = false ): boolean {
|
|
102
|
+
if( !id ) {
|
|
103
|
+
this._select( null, notify );
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
let page = this.m_pages.find( x => x.id===id );
|
|
108
|
+
if( page ) {
|
|
109
|
+
this._select( page, notify );
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
102
113
|
}
|
|
103
114
|
}
|
|
104
115
|
|
|
105
|
-
private _select( p: TabPage ) {
|
|
116
|
+
private _select( p: TabPage, notify: boolean ) {
|
|
117
|
+
|
|
118
|
+
if( this.m_curPage==p ) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
106
121
|
|
|
107
|
-
if( this.dom
|
|
122
|
+
if( !this.dom ) {
|
|
123
|
+
this.m_props.default = p.id;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if( this.m_curPage ) {
|
|
108
128
|
this.m_curPage.btn.removeClass( 'selected' );
|
|
109
|
-
this.m_curPage.page
|
|
129
|
+
if( this.m_curPage.page ) {
|
|
130
|
+
this.m_curPage.page.hide( );
|
|
131
|
+
}
|
|
110
132
|
}
|
|
111
133
|
|
|
112
134
|
this.m_curPage = p;
|
|
113
|
-
this.signal( 'change', EvChange(p ? p.id : null) );
|
|
114
135
|
|
|
115
|
-
if(
|
|
136
|
+
if( notify ) {
|
|
137
|
+
this.signal( 'change', EvChange(p ? p.id : null) );
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if( this.m_curPage ) {
|
|
116
141
|
this.m_curPage.btn.addClass( 'selected' );
|
|
117
|
-
this.m_curPage.page
|
|
142
|
+
if( this.m_curPage.page ) {
|
|
143
|
+
this.m_curPage.page.show( );
|
|
144
|
+
}
|
|
118
145
|
}
|
|
119
146
|
}
|
|
147
|
+
|
|
148
|
+
get selection( ) {
|
|
149
|
+
return this.m_curPage?.page;
|
|
150
|
+
}
|
|
120
151
|
}
|
package/src/x4.less
CHANGED
|
@@ -386,6 +386,8 @@ textarea {
|
|
|
386
386
|
align-items: center;
|
|
387
387
|
outline: none;
|
|
388
388
|
cursor: pointer;
|
|
389
|
+
font-family: var( --x4-font );
|
|
390
|
+
font-size: var( --x4-font-size );
|
|
389
391
|
|
|
390
392
|
height: 2rem;
|
|
391
393
|
padding: 8px;
|
|
@@ -1312,12 +1314,13 @@ textarea {
|
|
|
1312
1314
|
}
|
|
1313
1315
|
}
|
|
1314
1316
|
|
|
1317
|
+
@bwidth: 4px;
|
|
1315
1318
|
|
|
1316
1319
|
.x-spreadsheet,
|
|
1317
1320
|
.x-grid-view {
|
|
1318
1321
|
|
|
1319
1322
|
@def-height: 2em;
|
|
1320
|
-
|
|
1323
|
+
|
|
1321
1324
|
min-height: 0;
|
|
1322
1325
|
overflow: hidden;
|
|
1323
1326
|
background-color: white;
|
|
@@ -1370,7 +1373,7 @@ textarea {
|
|
|
1370
1373
|
|
|
1371
1374
|
.x-row {
|
|
1372
1375
|
position: absolute;
|
|
1373
|
-
width: 100
|
|
1376
|
+
width: calc( 100% - @bwidth ); // todo: border of 1st col
|
|
1374
1377
|
border-bottom: 1px solid #f0f0f0;
|
|
1375
1378
|
align-items: center;
|
|
1376
1379
|
height: @def-height;
|
|
@@ -1400,11 +1403,11 @@ textarea {
|
|
|
1400
1403
|
.x-grid-view {
|
|
1401
1404
|
.x-footer,
|
|
1402
1405
|
.x-header {
|
|
1403
|
-
border-left:
|
|
1406
|
+
border-left: @bwidth solid #f0f0f0;
|
|
1404
1407
|
}
|
|
1405
1408
|
|
|
1406
1409
|
.x-row {
|
|
1407
|
-
border-left:
|
|
1410
|
+
border-left: @bwidth solid transparent;
|
|
1408
1411
|
|
|
1409
1412
|
&:hover {
|
|
1410
1413
|
background-color: rgba(0,0,0,0.1);
|
|
@@ -2133,4 +2136,27 @@ textarea {
|
|
|
2133
2136
|
grid-gap: 10px;
|
|
2134
2137
|
grid-template-columns: repeat(auto-fill, minmax(250px,1fr));
|
|
2135
2138
|
grid-auto-rows: 10px;
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
.x-tab-bar {
|
|
2142
|
+
border-bottom: 1px solid var(--gray-600);
|
|
2143
|
+
background-color: var(--gray-100);
|
|
2144
|
+
|
|
2145
|
+
&> .x-button {
|
|
2146
|
+
border: none;
|
|
2147
|
+
color: var( --gray-600 );
|
|
2148
|
+
|
|
2149
|
+
&.selected {
|
|
2150
|
+
font-weight: bold;
|
|
2151
|
+
//border: 1px solid var( --x4-selection-color );
|
|
2152
|
+
border-bottom: none;
|
|
2153
|
+
color: var( --x4-selection-color );
|
|
2154
|
+
background-color: transparent;
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
&:focus:not(.selected) {
|
|
2158
|
+
text-decoration: underline;
|
|
2159
|
+
color: black;;
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2136
2162
|
}
|
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
|