x4js 1.4.22 → 1.4.27

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.22",
3
+ "version": "1.4.27",
4
4
  "description": "X4js core files",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -14,7 +14,7 @@
14
14
  "framework"
15
15
  ],
16
16
  "scripts": {
17
- "build": "build.sh",
17
+ "build": "tsc && lessc src/x4.less >lib/x4.css",
18
18
  "mkdoc": "typedoc --tsconfig tsconfig.json --readme none"
19
19
  },
20
20
  "author": "etienne cochard",
package/src/action.ts ADDED
@@ -0,0 +1,88 @@
1
+ /**
2
+ * ___ ___ __
3
+ * \ \/ / / _
4
+ * \ / /_| |_
5
+ * / \____ _|
6
+ * /__/\__\ |_|
7
+ *
8
+ * @file action.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 { BasicEvent } from './x4events'
31
+ import { BaseComponent, BaseComponentEventMap, BaseComponentProps } from './base_component'
32
+ import { IconID } from "./icon"
33
+ import { EvChange, EventHandler } from 'x4js';
34
+
35
+ //TODO: implement all
36
+
37
+
38
+ export interface EvAction extends BasicEvent {
39
+ }
40
+
41
+ export function EvAction( source: Action ) {
42
+ return BasicEvent<EvAction>({source});
43
+ }
44
+
45
+ interface ActionEventMap extends BaseComponentEventMap {
46
+ run: EvAction;
47
+ change: EvChange;
48
+ }
49
+
50
+ interface ActionProps extends BaseComponentProps<ActionEventMap> {
51
+ id?: any;
52
+ text?: string;
53
+ icon?: IconID;
54
+ disabled?: boolean;
55
+ hidden?: boolean;
56
+ checked?: boolean;
57
+
58
+ run: EventHandler<EvAction>;
59
+ change?: EventHandler<EvChange>;
60
+ }
61
+
62
+ export class Action extends BaseComponent<ActionProps,ActionEventMap> {
63
+
64
+ constructor( props: ActionProps ) {
65
+ super( props );
66
+
67
+ this.mapPropEvents( props, "run" );
68
+ }
69
+
70
+ get props( ) {
71
+ return this.m_props;
72
+ }
73
+
74
+ set text( t: string ) {
75
+ this.m_props.text = t;
76
+ this.emit( "change", EvChange(this) );
77
+ }
78
+
79
+ set icon( i: IconID ) {
80
+ this.m_props.icon = i;
81
+ this.emit( "change", EvChange(this) );
82
+ }
83
+
84
+ fire( ) {
85
+ this.emit( "run", EvAction(this) );
86
+ }
87
+ }
88
+
@@ -34,6 +34,7 @@ import { Component } from './component'
34
34
  import { Settings } from './settings'
35
35
  import { deferCall } from './tools'
36
36
  import { _tr } from './i18n'
37
+ import { flyWrap } from 'x4js'
37
38
 
38
39
  const _x4_touch_time = Symbol( );
39
40
 
@@ -127,6 +128,7 @@ export class Application<P extends ApplicationProps = ApplicationProps, E extend
127
128
  }
128
129
 
129
130
  ApplicationCreated( ) {
131
+ this.setTitle( '' );
130
132
  }
131
133
 
132
134
  public get app_name( ) {
@@ -165,21 +167,33 @@ export class Application<P extends ApplicationProps = ApplicationProps, E extend
165
167
  *
166
168
  * let myApp = new Application( ... );
167
169
  * let mainView = new VLayout( ... );
168
- * myApp.setMainView( mainView );
170
+ * myApp.mainView = mainView;
169
171
  */
170
172
 
171
- public set mainView( root: Component ) {
173
+ public setMainView( root: Component, clearBefore: boolean ) {
174
+
175
+ const ddom = this.m_props.renderTo ?? x4document.body;
176
+ const dest = flyWrap( ddom );
177
+
178
+ if( !this.m_props.renderTo ) {
179
+ dest.setStyleValue( 'position', 'absolute' );
180
+ }
181
+
182
+ dest.addClass( 'x4-root-element' );
183
+ if( clearBefore ) {
184
+ dest._empty( );
185
+ }
172
186
 
173
187
  this.m_mainView = root;
174
- this.mainView.addClass( 'x4-root-element' );
175
188
 
176
- deferCall( ( ) => {
177
- const dest = this.m_props.renderTo ?? x4document.body;
178
- while (dest.firstChild) {
179
- dest.removeChild(dest.firstChild);
180
- }
181
- dest.appendChild(root._build());
182
- } );
189
+ root.setStyleValue( 'position', 'absolute' );
190
+ root._build();
191
+
192
+ ddom.appendChild( root.dom );
193
+ }
194
+
195
+ set mainView( root: Component ) {
196
+ this.setMainView( root, false );
183
197
  }
184
198
 
185
199
  public get mainView( ) : Component {
@@ -187,7 +201,7 @@ export class Application<P extends ApplicationProps = ApplicationProps, E extend
187
201
  }
188
202
 
189
203
  public setTitle( title: string ) {
190
- x4document.title = this.m_app_name + ' > ' + title;
204
+ x4document.title = this.m_app_name + (title ? (' > ' + title) : '');
191
205
  }
192
206
 
193
207
  public disableZoomWheel( ) {
@@ -132,4 +132,19 @@ export class BaseComponent< P extends BaseComponentProps<BaseComponentEventMap>,
132
132
  singleShot( callback: TimerCallback, timeout = 0 ) {
133
133
  setTimeout( callback, timeout );
134
134
  }
135
+
136
+ /**
137
+ *
138
+ * @param props
139
+ * @param elements
140
+ */
141
+
142
+ public mapPropEvents<N extends keyof E>(props: P, ...elements: N[] ) {
143
+ elements.forEach( name => {
144
+ const n = name as string;
145
+ if (props[n]) {
146
+ this._on(n, props[n]);
147
+ }
148
+ })
149
+ }
135
150
  }
package/src/button.ts CHANGED
@@ -31,6 +31,7 @@
31
31
  import { Component, CProps, CEventMap, HtmlString } from './component'
32
32
  import { EventCallback, EvClick, EvChange } from './x4events'
33
33
 
34
+ import { Action } from './action'
34
35
  import { Icon, IconID } from './icon'
35
36
  import { Label } from './label'
36
37
  import { Menu, MenuItem, MenuOrSep } from './menu'
@@ -57,8 +58,9 @@ interface ButtonProps<E extends ButtonEventMap = ButtonEventMap> extends CProps<
57
58
  align?: 'center' | 'left' | 'right'; // text alignment
58
59
  autoRepeat?: number; // time in ms or 0/undefined for none
59
60
  menu?: MenuOrSep[] | MenuCallBack;
60
-
61
61
  click?: EventCallback<EvClick>; // shortcut to events: { click: ... }
62
+
63
+ action?: Action;
62
64
  }
63
65
 
64
66
 
@@ -83,11 +85,26 @@ export class BaseButton<P extends ButtonProps = ButtonProps, E extends ButtonEve
83
85
 
84
86
  render(props: ButtonProps) {
85
87
 
86
- let icon = props.icon ? new Icon({ icon: props.icon, cls: 'left', ref: 'l_icon' }) : null;
87
- let label = new Label({ flex: 1, text: props.text ?? '', align: props.align, ref: 'label' });
88
- let ricon = props.rightIcon ? new Icon({ icon: props.rightIcon, cls: 'right', ref: 'r_icon' }) : null;
88
+ const action = props.action;
89
+
90
+ let icon = props.icon;
91
+ let text = props.text;
92
+
93
+ if( action ) {
94
+ if( !icon && action.props.icon ) {
95
+ icon = action.props.icon;
96
+ }
97
+
98
+ if( text===undefined && action.props.text ) {
99
+ text = action.props.text;
100
+ }
101
+ }
89
102
 
90
- this.setContent([icon, label, ricon]);
103
+ const ui_icon = icon ? new Icon({ icon, cls: 'left', ref: 'l_icon' }) : null;
104
+ const ui_label = new Label({ flex: 1, text: text ?? '', align: props.align, ref: 'label' });
105
+ const ui_ricon = props.rightIcon ? new Icon({ icon: props.rightIcon, cls: 'right', ref: 'r_icon' }) : null;
106
+
107
+ this.setContent([ui_icon, ui_label, ui_ricon]);
91
108
  this._setTabIndex(props.tabIndex);
92
109
  }
93
110
 
@@ -164,6 +181,9 @@ export class BaseButton<P extends ButtonProps = ButtonProps, E extends ButtonEve
164
181
  }
165
182
  else {
166
183
  this.emit('click', EvClick());
184
+ if( this.m_props.action ) {
185
+ this.m_props.action.fire( );
186
+ }
167
187
  }
168
188
  }
169
189
 
package/src/component.ts CHANGED
@@ -41,6 +41,7 @@ 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';
44
45
 
45
46
  export { HtmlString, isHtmlString, html } from './tools'
46
47
 
@@ -1143,15 +1144,6 @@ export class Component<P extends CProps<BaseComponentEventMap> = CProps<BaseComp
1143
1144
  }
1144
1145
  }
1145
1146
 
1146
- public mapPropEvents<N extends keyof E>(props: P, ...elements: N[] ) {
1147
- elements.forEach( name => {
1148
- const n = name as string;
1149
- if (props[n]) {
1150
- this._on(n, props[n]);
1151
- }
1152
- })
1153
- }
1154
-
1155
1147
  /**
1156
1148
  *
1157
1149
  * @param name
@@ -2333,4 +2325,6 @@ export class Container<P extends ContainerProps = ContainerProps, E extends Cont
2333
2325
  }
2334
2326
  }
2335
2327
 
2336
- export type ComponentConstructor<T> = new (props: CProps) => T;
2328
+ export type ComponentConstructor<T> = new (props: CProps) => T;
2329
+
2330
+
package/src/dialog.ts CHANGED
@@ -469,16 +469,16 @@ export class Dialog<P extends DialogProps = DialogProps, E extends DialogBoxEven
469
469
 
470
470
  let cstyle = this.getComputedStyle();
471
471
 
472
- let topw = cstyle.parse('marginTop') + cstyle.parse('paddingTop') + cstyle.parse('borderTopWidth');
473
- let botw = cstyle.parse('marginBottom') + cstyle.parse('paddingBottom') + cstyle.parse('borderBottomWidth');
474
- let lftw = cstyle.parse('marginLeft') + cstyle.parse('paddingLeft') + cstyle.parse('borderLeftWidth');
475
- let rgtw = cstyle.parse('marginRight') + cstyle.parse('paddingRight') + cstyle.parse('borderRightWidth');
472
+ //let topw = cstyle.parse('marginTop') + cstyle.parse('paddingTop') + cstyle.parse('borderTopWidth');
473
+ //let botw = cstyle.parse('marginBottom') + cstyle.parse('paddingBottom') + cstyle.parse('borderBottomWidth');
474
+ //let lftw = cstyle.parse('marginLeft') + cstyle.parse('paddingLeft') + cstyle.parse('borderLeftWidth');
475
+ //let rgtw = cstyle.parse('marginRight') + cstyle.parse('paddingRight') + cstyle.parse('borderRightWidth');
476
476
 
477
- wrc.top += topw - trc.height;
478
- wrc.height -= topw + botw - trc.height;
477
+ //wrc.top += topw - trc.height;
478
+ //wrc.height -= topw + botw - trc.height;
479
479
 
480
- wrc.left += lftw;
481
- wrc.width -= lftw + rgtw;
480
+ //wrc.left += lftw;
481
+ //wrc.width -= lftw + rgtw;
482
482
 
483
483
  // custom handling double click
484
484
  const now = Date.now();
@@ -496,23 +496,38 @@ export class Dialog<P extends DialogProps = DialogProps, E extends DialogBoxEven
496
496
  }
497
497
 
498
498
  let __move = (ex, ey) => {
499
- let x = ex - dx,
500
- y = ey - dy;
501
-
502
- if (x + rc.width < wrc.left) {
503
- x = wrc.left - rc.width;
499
+
500
+ if( ex>wrc.right ) {
501
+ ex = wrc.right;
504
502
  }
505
- else if (x > wrc.right) {
506
- x = wrc.right;
503
+ else if( ex<wrc.left ) {
504
+ ex = wrc.left;
507
505
  }
508
506
 
509
- if (y < wrc.top) { // title grip is on top
510
- y = wrc.top;
507
+ if( ey>wrc.bottom ) {
508
+ ey = wrc.bottom;
511
509
  }
512
- else if (y > wrc.bottom) {
513
- y = wrc.bottom;
510
+ else if( ey<wrc.top ) {
511
+ ey = wrc.top;
514
512
  }
515
513
 
514
+ let x = ex - dx,
515
+ y = ey - dy;
516
+
517
+ //if (x + rc.width < wrc.left) {
518
+ // x = wrc.left - rc.width;
519
+ //}
520
+ //else if (x > wrc.right) {
521
+ // x = wrc.right;
522
+ //}
523
+ //
524
+ //if (y < wrc.top) { // title grip is on top
525
+ // y = wrc.top;
526
+ //}
527
+ //else if (y > wrc.bottom) {
528
+ // y = wrc.bottom;
529
+ //}
530
+
516
531
  this.setStyle({
517
532
  left: x,
518
533
  top: y
package/src/fileupload.ts CHANGED
@@ -171,7 +171,7 @@ function _createFileInput() {
171
171
  * @param cb - callback to call when user select a file
172
172
  */
173
173
 
174
- export function openFile(extensions: string, cb: (filename: FileList) => void, multiple = false) {
174
+ export function openFileDialog(extensions: string, cb: (filename: FileList) => void, multiple = false) {
175
175
 
176
176
  let fi = _createFileInput();
177
177
 
@@ -195,7 +195,7 @@ export function openFile(extensions: string, cb: (filename: FileList) => void, m
195
195
  * @param cb - callback to call when user choose the destination
196
196
  */
197
197
 
198
- export function saveFile(defFileName: string, extensions: string, cb: (filename: File) => void) {
198
+ export function saveFileDialog(defFileName: string, extensions: string, cb: (filename: File) => void) {
199
199
 
200
200
  let fi = _createFileInput();
201
201
  fi.setAttribute('nwsaveas', defFileName);
package/src/gridview.ts CHANGED
@@ -1126,19 +1126,21 @@ export class GridView extends VLayout<GridViewProps, GridViewEventMap> {
1126
1126
 
1127
1127
  this.m_footer.enumChilds( (c) => {
1128
1128
  let cid = c.getData( 'col' );
1129
- let col = this.m_columns[cid];
1129
+ if( cid ) {
1130
+ let col = this.m_columns[cid];
1130
1131
 
1131
- let fmt = col.formatter;
1132
-
1133
- let text;
1134
- if (fmt && fmt instanceof Function) {
1135
- text = fmt(rec[col.id], rec);
1136
- }
1137
- else {
1138
- text = rec[col.id];
1139
- }
1132
+ let fmt = col.formatter;
1133
+
1134
+ let text;
1135
+ if (fmt && fmt instanceof Function) {
1136
+ text = fmt(rec[col.id], rec);
1137
+ }
1138
+ else {
1139
+ text = rec[col.id];
1140
+ }
1140
1141
 
1141
- c.setContent( text, false );
1142
+ c.setContent( text, false );
1143
+ }
1142
1144
  });
1143
1145
  }
1144
1146
  }
package/src/icon.ts CHANGED
@@ -101,7 +101,8 @@ class Loader extends EventSource<LoadingEventMap> {
101
101
  if( r.ok ) {
102
102
  const svg = await r.text();
103
103
  // check response, must be svg
104
- if( !svg.startsWith("<svg") ) {
104
+ //TODO: find better
105
+ if (!svg.startsWith("<svg") && !svg.startsWith('<?xml') ) {
105
106
  console.error( "svg loading error: ", svg );
106
107
  this.signal( 'loaded', EvLoaded(url,"") );
107
108
  }
@@ -225,13 +226,10 @@ export class Icon extends Component<IconProps>
225
226
  let match_url = reUrl.exec( icon );
226
227
  if( match_url ) {
227
228
  url = trimQuotes( match_url[1].trim( ) );
228
- if( url.substring(0,5)=='data:' ) {
229
- this._setSVG( url );
230
- return;
231
- }
232
- else {
233
- name = url.replace( /[/\\\.\* ]/g, '_' );
234
- }
229
+ // this value is escaped
230
+ url = url.replaceAll( "\\", "" );
231
+ this._setSVG( url );
232
+ return;
235
233
  }
236
234
  else {
237
235
  // todo: deprecated
package/src/listview.ts CHANGED
@@ -551,7 +551,7 @@ export class ListView extends VLayout<ListViewProps,ListViewEventMap> {
551
551
  return this.m_selection ? this.m_selection.item : null;
552
552
  }
553
553
 
554
- public set selection(id) {
554
+ public set selection(id: any) {
555
555
  if (id === null || id === undefined) {
556
556
  this._selectItem(null, null);
557
557
  }
package/src/menu.ts CHANGED
@@ -32,6 +32,8 @@ import { x4document } from './x4dom'
32
32
  import { CEventMap, Component, CProps } from './component'
33
33
  import { EvClick, EventCallback } from './x4events'
34
34
 
35
+ import { Action } from './action'
36
+
35
37
  import { Popup, PopupProps } from './popup'
36
38
  import { Icon, IconID } from './icon'
37
39
  import { Label } from './label'
@@ -244,8 +246,9 @@ export interface MenuItemProps extends CProps {
244
246
  items?: MenuOrSep[];
245
247
  checked?: boolean;
246
248
  cls?: string;
247
-
248
249
  click?: EventCallback<EvClick>; // shortcut to events: { click ... }
250
+
251
+ action?: Action;
249
252
  }
250
253
 
251
254
 
@@ -254,12 +257,21 @@ export class MenuItem extends Component<MenuItemProps, MenuItemEventMap> {
254
257
 
255
258
  private m_menu: Menu;
256
259
  private m_isOpen: boolean;
260
+ private m_action: Action;
257
261
 
262
+ constructor( action: Action );
258
263
  constructor( text: string, click: EventCallback<EvClick> );
259
264
  constructor( props: MenuItemProps);
260
265
  constructor( a, b? ) {
261
266
 
262
- if( isString(a) ) {
267
+ if( a instanceof Action ) {
268
+ super( {
269
+ click: ( ) => { a.fire(); }
270
+ });
271
+
272
+ this.m_action = a;
273
+ }
274
+ else if( isString(a) ) {
263
275
  super( {
264
276
  text: a,
265
277
  click: b
@@ -281,17 +293,27 @@ export class MenuItem extends Component<MenuItemProps, MenuItemEventMap> {
281
293
  /** @ignore */
282
294
  render(props: MenuItemProps) {
283
295
 
284
- let icon = props.icon ?? 0x20;
296
+ let icon = props.icon ?? 0;
285
297
  let text = props.text;
286
298
 
287
299
  if (props.checked !== undefined) {
288
- icon = props.checked ? 'cls(far fa-check)' : 0; //todo: use stylesheet
300
+ icon = props.checked ? 'var( --x4-icon-check )' : 0;
301
+ }
302
+
303
+ if( this.m_action ) {
304
+ if( !icon ) {
305
+ icon = this.m_action.props.icon;
306
+ }
307
+
308
+ if( text===undefined ) {
309
+ text = this.m_action.props.text;
310
+ }
289
311
  }
290
312
 
291
313
  let popIco = null;
292
314
  if (this.isPopup) {
293
315
  this.addClass('@popup-menu-item');
294
- popIco = new Icon( "var( --x4-icon-chevron-right )" );
316
+ popIco = new Icon( { icon: "var( --x4-icon-chevron-right )", cls: "pop-mark" } );
295
317
  }
296
318
 
297
319
  if (!text && !icon) {
package/src/popup.ts CHANGED
@@ -169,8 +169,11 @@ export class Popup<P extends PopupProps = PopupProps, E extends PopupEventMap =
169
169
  centerOnScreen( ) {
170
170
  let rc = this.getBoundingRect();
171
171
 
172
- let x = (x4document.body.clientWidth - rc.width) / 2,
173
- y = (x4document.body.clientHeight - rc.height) / 2;
172
+ //let x = (x4document.body.clientWidth - rc.width) / 2,
173
+ // y = (x4document.body.clientHeight - rc.height) / 2;
174
+
175
+ const x = `max( 0px, 50vw - ${rc.width/2}px )`; //(x4dom_1.x4document.body.clientWidth - rc.width) / 2;
176
+ const y = `max( 0px, 50vh - ${rc.height/2}px )`; //(x4dom_1.x4document.body.clientHeight - rc.height) / 2;
174
177
 
175
178
  this.setStyleValue('left', x);
176
179
  this.setStyleValue('top', y);
package/src/settings.ts CHANGED
@@ -50,7 +50,7 @@ export class Settings {
50
50
 
51
51
  private _save( ) {
52
52
  let data = JSON.stringify(this.m_data);
53
- localStorage.write( this.m_name, data );
53
+ localStorage.setItem( this.m_name, data );
54
54
  }
55
55
 
56
56
  private _load( ) {
@@ -60,7 +60,7 @@ export class Settings {
60
60
 
61
61
  this.m_data = {};
62
62
 
63
- let data = localStorage.read( this.m_name );
63
+ let data = localStorage.getItem( this.m_name );
64
64
  if( data!==null ) {
65
65
  data = JSON.parse( data );
66
66
  if( data ) {
@@ -546,6 +546,7 @@ export class Spreadsheet extends VLayout<SpreadsheetProps, SpreadsheetEventSet>
546
546
  if (this.m_recycler.length) {
547
547
  cell = this.m_recycler.pop();
548
548
  cell.clearClasses();
549
+ cell.addClass( '@comp' ); // todo: find better to reset to default
549
550
  }
550
551
  else {
551
552
  cell = new Component({
@@ -681,7 +682,8 @@ export class Spreadsheet extends VLayout<SpreadsheetProps, SpreadsheetEventSet>
681
682
  cell.setStyle({
682
683
  left: x,
683
684
  top: top + y,
684
- width: calcw[colIdx]
685
+ width: calcw[colIdx],
686
+ height: this.m_itemHeight
685
687
  });
686
688
 
687
689
  if (this.m_selection.row == rowIdx && this.m_selection.col == colIdx) {
package/src/tools.ts CHANGED
@@ -369,10 +369,11 @@ export function escapeHtml(unsafe: string, nl_br = false): string {
369
369
  */
370
370
 
371
371
  export function removeHtmlTags(unsafe: string, nl_br = false): string {
372
- if (!unsafe || unsafe.length == 0) {
373
- return unsafe;
374
- }
375
- debugger;
372
+
373
+ if ( unsafe===undefined || unsafe===null || !isString(unsafe) || unsafe.length == 0 ) {
374
+ return "";
375
+ }
376
+
376
377
  let ret_val = '';
377
378
  for (let i = 0; i < unsafe.length; i++) {
378
379
  const ch = unsafe.codePointAt(i);
package/src/x4.less CHANGED
@@ -70,7 +70,8 @@
70
70
  --x4-icon-xmark: 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" fill="currentColor"><path d="M310.6 361.4c12.5 12.5 12.5 32.75 0 45.25C304.4 412.9 296.2 416 288 416s-16.38-3.125-22.62-9.375L160 301.3L54.63 406.6C48.38 412.9 40.19 416 32 416S15.63 412.9 9.375 406.6c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z"/></svg>';
71
71
  --x4-icon-angle-down: 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" fill="currentColor"><path d="M192 384c-8.188 0-16.38-3.125-22.62-9.375l-160-160c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L192 306.8l137.4-137.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-160 160C208.4 380.9 200.2 384 192 384z"/></svg>';
72
72
  --x4-icon-calendar-days: 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" fill="currentColor"><path d="M160 32V64H288V32C288 14.33 302.3 0 320 0C337.7 0 352 14.33 352 32V64H400C426.5 64 448 85.49 448 112V160H0V112C0 85.49 21.49 64 48 64H96V32C96 14.33 110.3 0 128 0C145.7 0 160 14.33 160 32zM0 192H448V464C448 490.5 426.5 512 400 512H48C21.49 512 0 490.5 0 464V192zM64 304C64 312.8 71.16 320 80 320H112C120.8 320 128 312.8 128 304V272C128 263.2 120.8 256 112 256H80C71.16 256 64 263.2 64 272V304zM192 304C192 312.8 199.2 320 208 320H240C248.8 320 256 312.8 256 304V272C256 263.2 248.8 256 240 256H208C199.2 256 192 263.2 192 272V304zM336 256C327.2 256 320 263.2 320 272V304C320 312.8 327.2 320 336 320H368C376.8 320 384 312.8 384 304V272C384 263.2 376.8 256 368 256H336zM64 432C64 440.8 71.16 448 80 448H112C120.8 448 128 440.8 128 432V400C128 391.2 120.8 384 112 384H80C71.16 384 64 391.2 64 400V432zM208 384C199.2 384 192 391.2 192 400V432C192 440.8 199.2 448 208 448H240C248.8 448 256 440.8 256 432V400C256 391.2 248.8 384 240 384H208zM320 432C320 440.8 327.2 448 336 448H368C376.8 448 384 440.8 384 432V400C384 391.2 376.8 384 368 384H336C327.2 384 320 391.2 320 400V432z"/></svg>';
73
- --x4-icon-tip: 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor"><path d="M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 464c-114.7 0-208-93.31-208-208S141.3 48 256 48s208 93.31 208 208S370.7 464 256 464zM296 336h-16V248C280 234.8 269.3 224 256 224H224C210.8 224 200 234.8 200 248S210.8 272 224 272h8v64h-16C202.8 336 192 346.8 192 360S202.8 384 216 384h80c13.25 0 24-10.75 24-24S309.3 336 296 336zM256 192c17.67 0 32-14.33 32-32c0-17.67-14.33-32-32-32S224 142.3 224 160C224 177.7 238.3 192 256 192z"/></svg>';
73
+ --x4-icon-tip: 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor"><path d="M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 464c-114.7 0-208-93.31-208-208S141.3 48 256 48s208 93.31 208 208S370.7 464 256 464zM296 336h-16V248C280 234.8 269.3 224 256 224H224C210.8 224 200 234.8 200 248S210.8 272 224 272h8v64h-16C202.8 336 192 346.8 192 360S202.8 384 216 384h80c13.25 0 24-10.75 24-24S309.3 336 296 336zM256 192c17.67 0 32-14.33 32-32c0-17.67-14.33-32-32-32S224 142.3 224 160C224 177.7 238.3 192 256 192z"/></svg>';
74
+ --x4-check: 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" fill="currentColor"><path d="M440.1 103C450.3 112.4 450.3 127.6 440.1 136.1L176.1 400.1C167.6 410.3 152.4 410.3 143 400.1L7.029 264.1C-2.343 255.6-2.343 240.4 7.029 231C16.4 221.7 31.6 221.7 40.97 231L160 350.1L407 103C416.4 93.66 431.6 93.66 440.1 103V103z"/></svg>';
74
75
  }
75
76
 
76
77
  @BLACK10: rgba(0,0,0,0.1);
@@ -862,6 +863,10 @@ textarea {
862
863
  width: 1em;
863
864
  margin-right: 8px;
864
865
  color: var( --gray-700 );
866
+
867
+ &.pop-mark {
868
+ height: 0.8em;
869
+ }
865
870
  }
866
871
 
867
872
  .x-label {
@@ -1329,9 +1334,12 @@ textarea {
1329
1334
  overflow: hidden;
1330
1335
  padding: 4px;
1331
1336
  white-space: nowrap;
1332
- //min-width: 50px;
1333
1337
  color: var( --gray-900 );
1334
1338
  height: @def-height;
1339
+
1340
+ span {
1341
+ width: 100%;
1342
+ }
1335
1343
  }
1336
1344
 
1337
1345
  .x-footer,
@@ -1368,6 +1376,15 @@ textarea {
1368
1376
  .x-cell {
1369
1377
  display: flex;
1370
1378
  align-items: center;
1379
+ span {
1380
+ width: unset;
1381
+ }
1382
+
1383
+ .sort {
1384
+ width: 1em;
1385
+ height: 1em;
1386
+ padding-left: 4px;
1387
+ }
1371
1388
  }
1372
1389
  }
1373
1390
 
@@ -1441,6 +1458,8 @@ textarea {
1441
1458
  border-right: 1px solid rgba(0,0,0,0.1);
1442
1459
  border-bottom: 1px solid rgba(0,0,0,0.1);
1443
1460
  height: 2em;
1461
+ align-items: center;
1462
+ display: flex;
1444
1463
  }
1445
1464
 
1446
1465
  .x-cell.x-selected {
package/tsconfig.json CHANGED
@@ -12,10 +12,6 @@
12
12
  "include": [
13
13
  "./src/*",
14
14
  ],
15
- "exclude": [
16
- "src/texthiliter.ts",
17
- "src/smartedit.ts"
18
- ],
19
15
  "typedocOptions": {
20
16
  "entryPoints": ["src/index.ts"],
21
17
  "out": "../doc/api"