x4js 2.3.17 → 2.3.19
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/aicli.md +3 -3
- package/aicontext.md +211 -47
- package/cli/config.mjs +14 -7
- package/cli/copy-plugin.mjs +8 -0
- package/cli/create.mjs +8 -2
- package/cli/dev.mjs +4 -1
- package/cli/less-plugin.mjs +5 -1
- package/cli/main.mjs +15 -5
- package/cli/rawfile-plugin.mjs +23 -3
- package/cli/templates.mjs +5 -1
- package/package.json +1 -1
- package/src/components/calendar/calendar.module.scss +16 -1
- package/src/components/calendar/calendar.ts +52 -9
- package/src/components/checkbox/checkbox.ts +1 -0
- package/src/components/combobox/combobox.ts +51 -23
- package/src/components/dialog/dialog.ts +4 -1
- package/src/components/form/form.ts +9 -5
- package/src/components/gridview/gridview.ts +352 -176
- package/src/components/input/input.ts +63 -38
- package/src/components/keyboard/keyboard.module.scss +35 -4
- package/src/components/keyboard/keyboard.ts +295 -80
- package/src/components/listbox/listbox.ts +6 -1
- package/src/components/messages/messages.ts +12 -3
- package/src/components/popup/popup.ts +19 -20
- package/src/components/radio/radio.ts +8 -4
- package/src/components/rating/rating.ts +38 -48
- package/src/components/slider/slider.ts +38 -8
- package/src/components/spreadsheet/spreadsheet.ts +167 -132
- package/src/components/switch/switch.ts +2 -1
- package/src/components/tag/tag.ts +1 -2
- package/src/components/textarea/textarea.module.scss +6 -2
- package/src/components/textarea/textarea.ts +50 -11
- package/src/components/textedit/textedit.ts +12 -2
- package/src/core/component.ts +16 -9
- package/src/core/core_application.ts +8 -4
- package/src/core/core_clipboard.ts +239 -0
- package/src/core/core_data.ts +118 -49
- package/src/core/core_dom.ts +36 -6
- package/src/core/core_dragdrop.ts +11 -5
- package/src/core/core_element.ts +1 -1
- package/src/core/core_pdf.ts +696 -46
- package/src/core/core_router.ts +42 -44
- package/src/core/core_shortcuts.ts +1 -1
- package/src/core/core_state.ts +22 -5
- package/src/x4.d.ts +1 -0
- package/src/x4.ts +1 -0
|
@@ -14,12 +14,11 @@
|
|
|
14
14
|
* that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
|
|
15
15
|
**/
|
|
16
16
|
|
|
17
|
-
import { Component, ComponentEvents,
|
|
17
|
+
import { Component, ComponentEvents, ComponentProps, EvChange } from '../../core/component';
|
|
18
18
|
import { EventCallback } from '../../core/core_events';
|
|
19
19
|
import { HBox } from '../boxes/boxes';
|
|
20
|
-
import { Input } from '../input/input';
|
|
21
20
|
import { Icon } from '../icon/icon';
|
|
22
|
-
import { class_ns } from '../../core/core_tools';
|
|
21
|
+
import { class_ns, IComponentInterface, IFormElement } from '../../core/core_tools';
|
|
23
22
|
|
|
24
23
|
import icons from "../assets/icons"
|
|
25
24
|
|
|
@@ -54,50 +53,33 @@ export interface RatingProps extends ComponentProps {
|
|
|
54
53
|
export class Rating extends HBox<RatingProps,RatingEventMap> {
|
|
55
54
|
|
|
56
55
|
private m_els: Component[];
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
|
|
59
57
|
constructor( props: RatingProps ) {
|
|
60
58
|
super( props );
|
|
61
59
|
|
|
62
|
-
props
|
|
60
|
+
// this.props: a copy of props (Box adds its tag)
|
|
61
|
+
this.props.steps = this.props.steps ?? 5;
|
|
62
|
+
|
|
63
|
+
// the name is on the rating: a Form finds it, and reads its value as a number (queryInterface)
|
|
64
|
+
if( props.name ) {
|
|
65
|
+
this.setAttribute( "name", props.name );
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.addDOMEvent( 'click', (e) => this._on_click(e) );
|
|
63
69
|
this._update( );
|
|
64
70
|
}
|
|
65
71
|
|
|
72
|
+
// value: the count of stars that are lit, 0 for none
|
|
66
73
|
private _update( ) {
|
|
67
|
-
|
|
68
74
|
const props = this.props;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
let value = props.value ?? 0;
|
|
72
|
-
|
|
73
|
-
this.m_input = new Input( {
|
|
74
|
-
type: "text",
|
|
75
|
-
hidden: true,
|
|
76
|
-
name: props.name,
|
|
77
|
-
value: ''+value
|
|
78
|
-
} );
|
|
79
|
-
|
|
80
|
-
this.addDOMEvent( 'click', (e) => this._on_click(e) );
|
|
75
|
+
const shape = props.icon ?? icons.star.solid;
|
|
76
|
+
const value = props.value ?? 0;
|
|
81
77
|
|
|
82
78
|
this.m_els = [];
|
|
83
79
|
for( let i=0; i<props.steps; i++ ) {
|
|
84
|
-
|
|
85
|
-
let cls = 'item';
|
|
86
|
-
if( i+1 <= value ) {
|
|
87
|
-
cls += ' checked';
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
let c = new Icon( {
|
|
91
|
-
cls,
|
|
92
|
-
iconId: shape,
|
|
93
|
-
} );
|
|
94
|
-
|
|
95
|
-
c.setInternalData( "value", i );
|
|
96
|
-
|
|
97
|
-
this.m_els.push( c );
|
|
80
|
+
this.m_els.push( new Icon( { cls: i < value ? 'item checked' : 'item', iconId: shape } ) );
|
|
98
81
|
}
|
|
99
82
|
|
|
100
|
-
this.m_els.push( this.m_input );
|
|
101
83
|
this.setContent( this.m_els );
|
|
102
84
|
}
|
|
103
85
|
|
|
@@ -107,12 +89,7 @@ export class Rating extends HBox<RatingProps,RatingEventMap> {
|
|
|
107
89
|
|
|
108
90
|
setValue( v: number ) {
|
|
109
91
|
this.props.value = v;
|
|
110
|
-
|
|
111
|
-
for( let c=0; c<this.props.steps; c++ ) {
|
|
112
|
-
this.m_els[c].setClass( 'checked', this.m_els[c].getInternalData('value')<=v );
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
this.m_input.setValue( ''+this.props.value );
|
|
92
|
+
this.m_els.forEach( ( c, i ) => c.setClass( 'checked', i < v ) );
|
|
116
93
|
}
|
|
117
94
|
|
|
118
95
|
setSteps( n: number ) {
|
|
@@ -121,18 +98,31 @@ export class Rating extends HBox<RatingProps,RatingEventMap> {
|
|
|
121
98
|
}
|
|
122
99
|
|
|
123
100
|
setShape( icon: string ) {
|
|
124
|
-
this.removeClass( this.props.icon );
|
|
125
101
|
this.props.icon = icon;
|
|
102
|
+
this._update( );
|
|
126
103
|
}
|
|
127
104
|
|
|
105
|
+
// the star clicked: its svg, or the Icon itself
|
|
128
106
|
private _on_click( ev: MouseEvent ) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
this.
|
|
107
|
+
const n = this.m_els.findIndex( c => c.dom.contains( ev.target as Node ) );
|
|
108
|
+
if( n>=0 ) {
|
|
109
|
+
// the star of the value, clicked again: none is lit
|
|
110
|
+
this.setValue( n+1===this.props.value ? 0 : n+1 );
|
|
111
|
+
this.fire( 'change', { value: this.props.value } );
|
|
134
112
|
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
override queryInterface<T extends IComponentInterface>( name: string ): T {
|
|
116
|
+
if( name=="form-element" ) {
|
|
117
|
+
const i: IFormElement = {
|
|
118
|
+
getRawValue: ( ) => this.getValue( ),
|
|
119
|
+
setRawValue: ( v: any ) => { this.setValue( +v ); },
|
|
120
|
+
isValid: ( ) => true,
|
|
121
|
+
};
|
|
135
122
|
|
|
136
|
-
|
|
137
|
-
|
|
123
|
+
return i as unknown as T;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return super.queryInterface( name );
|
|
127
|
+
}
|
|
138
128
|
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { EventCallback } from '../../core/core_events';
|
|
18
18
|
import { Component, ComponentEvents, ComponentProps, EvChange } from '../../core/component';
|
|
19
|
-
import { class_ns, Rect } from '../../core/core_tools';
|
|
19
|
+
import { class_ns, IComponentInterface, IFormElement, Rect } from '../../core/core_tools';
|
|
20
20
|
|
|
21
21
|
import { HBox } from '../boxes/boxes';
|
|
22
22
|
import { Input } from '../input/input';
|
|
@@ -33,6 +33,8 @@ interface SliderProps extends ComponentProps {
|
|
|
33
33
|
max: number;
|
|
34
34
|
step?: number;
|
|
35
35
|
vertical?: boolean;
|
|
36
|
+
/** for a Form: it reads the value as a number */
|
|
37
|
+
name?: string;
|
|
36
38
|
change?: EventCallback<EvChange>;
|
|
37
39
|
}
|
|
38
40
|
|
|
@@ -83,6 +85,12 @@ export class Slider extends Component<SliderProps,SliderEvents> {
|
|
|
83
85
|
|
|
84
86
|
this.setAttribute( "tabindex", 0 );
|
|
85
87
|
|
|
88
|
+
// the name is on the slider: a Form finds it (queryInterface). The hidden range keeps
|
|
89
|
+
// the value: the browser brings it between min and max, on the step
|
|
90
|
+
if( props.name ) {
|
|
91
|
+
this.setAttribute( "name", props.name );
|
|
92
|
+
}
|
|
93
|
+
|
|
86
94
|
this.setDOMEvents( {
|
|
87
95
|
pointerdown: ( ev ) => this._on_mousedown( ev ),
|
|
88
96
|
pointermove: ( ev ) => this._on_mousemove( ev ),
|
|
@@ -106,8 +114,8 @@ export class Slider extends Component<SliderProps,SliderEvents> {
|
|
|
106
114
|
}
|
|
107
115
|
|
|
108
116
|
private _getMinMax( ) {
|
|
109
|
-
const min =
|
|
110
|
-
const max =
|
|
117
|
+
const min = parseFloat( this._range.getAttribute( 'min' ) );
|
|
118
|
+
const max = parseFloat( this._range.getAttribute( 'max' ) );
|
|
111
119
|
return [min,max];
|
|
112
120
|
}
|
|
113
121
|
|
|
@@ -134,15 +142,17 @@ export class Slider extends Component<SliderProps,SliderEvents> {
|
|
|
134
142
|
const [min,max] = this._getMinMax( );
|
|
135
143
|
|
|
136
144
|
this._range.setNumValue( min + (perc * (max-min) ) );
|
|
137
|
-
this._update( );
|
|
145
|
+
this._update( true );
|
|
138
146
|
}
|
|
139
147
|
}
|
|
140
148
|
|
|
141
|
-
|
|
149
|
+
// notify: a change made by the user (drag, arrows); not by the code, nor at the construction
|
|
150
|
+
private _update( notify = false ) {
|
|
142
151
|
const value = this._range.getNumValue( );
|
|
143
152
|
const [min,max] = this._getMinMax( );
|
|
144
153
|
|
|
145
|
-
|
|
154
|
+
// from min: min is at 0%, max at 100%
|
|
155
|
+
let perc = max>min ? (value-min) / (max-min) * 100 : 0;
|
|
146
156
|
|
|
147
157
|
if( !this.props.vertical ) {
|
|
148
158
|
this._thumb.setStyleValue( "left", perc+"%" );
|
|
@@ -155,7 +165,9 @@ export class Slider extends Component<SliderProps,SliderEvents> {
|
|
|
155
165
|
this._bar.setStyleValue( "height", perc+"%" );
|
|
156
166
|
}
|
|
157
167
|
|
|
158
|
-
|
|
168
|
+
if( notify ) {
|
|
169
|
+
this.fire( "change", { value } );
|
|
170
|
+
}
|
|
159
171
|
}
|
|
160
172
|
|
|
161
173
|
private _on_mouseup( ev: PointerEvent ) {
|
|
@@ -184,7 +196,7 @@ export class Slider extends Component<SliderProps,SliderEvents> {
|
|
|
184
196
|
}
|
|
185
197
|
|
|
186
198
|
this._range.setNumValue( this._range.getNumValue()+inc );
|
|
187
|
-
this._update( );
|
|
199
|
+
this._update( true );
|
|
188
200
|
}
|
|
189
201
|
}
|
|
190
202
|
|
|
@@ -202,4 +214,22 @@ export class Slider extends Component<SliderProps,SliderEvents> {
|
|
|
202
214
|
this._range.setNumValue( v );
|
|
203
215
|
this._update( );
|
|
204
216
|
}
|
|
217
|
+
|
|
218
|
+
getValue( ) {
|
|
219
|
+
return this._range.getNumValue( );
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
override queryInterface<T extends IComponentInterface>( name: string ): T {
|
|
223
|
+
if( name=="form-element" ) {
|
|
224
|
+
const i: IFormElement = {
|
|
225
|
+
getRawValue: ( ) => this.getValue( ),
|
|
226
|
+
setRawValue: ( v: any ) => { this.setValue( +v ); },
|
|
227
|
+
isValid: ( ) => true,
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
return i as unknown as T;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return super.queryInterface( name );
|
|
234
|
+
}
|
|
205
235
|
}
|
|
@@ -348,6 +348,11 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
348
348
|
this.lock(false);
|
|
349
349
|
});
|
|
350
350
|
|
|
351
|
+
// out of the document: nothing is drawn, as before its first insertion
|
|
352
|
+
this.addDOMEvent("removed", () => {
|
|
353
|
+
this.lock(true);
|
|
354
|
+
});
|
|
355
|
+
|
|
351
356
|
this.addDOMEvent("resized", () => {
|
|
352
357
|
this._updateFlexs();
|
|
353
358
|
this._computeFullSize();
|
|
@@ -358,6 +363,139 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
358
363
|
this._on_key(e);
|
|
359
364
|
})
|
|
360
365
|
|
|
366
|
+
// the handlers of the grid itself are put once, here. _init builds what is
|
|
367
|
+
// inside the grid, each time it comes in the document
|
|
368
|
+
|
|
369
|
+
// WHEEL
|
|
370
|
+
this.addDOMEvent("wheel", (ev: WheelEvent) => {
|
|
371
|
+
// many rows: one pixel of the scroll bar is one row, wherever the pointer is
|
|
372
|
+
if (ev.deltaY && this._store && this._store.getRowCount() >= SCROLL_LIMIT) {
|
|
373
|
+
this._viewport.dom.scrollBy(0, ev.deltaY < 0 ? -1 : 1);
|
|
374
|
+
ev.stopPropagation();
|
|
375
|
+
ev.preventDefault();
|
|
376
|
+
}
|
|
377
|
+
else if (this._has_fixed && ev.deltaY) {
|
|
378
|
+
// wheel on fixed part
|
|
379
|
+
// fixed part do not have scrollbar, so we need to handle it by hand
|
|
380
|
+
let t = ev.target as Node;
|
|
381
|
+
while (t != this.dom) {
|
|
382
|
+
if (t == this._vheader.dom) {
|
|
383
|
+
this._viewport.dom.scrollBy(0, ev.deltaY < 0 ? -this._row_height : this._row_height);
|
|
384
|
+
ev.stopPropagation();
|
|
385
|
+
ev.preventDefault();
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
t = t.parentNode;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
const targetCell = (e: MouseEvent) => {
|
|
395
|
+
let el = e.target as Element;
|
|
396
|
+
while (el && !el.classList.contains("cell")) {
|
|
397
|
+
el = el.parentElement;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (el) {
|
|
401
|
+
const cel = componentFromDOM(el);
|
|
402
|
+
return {
|
|
403
|
+
ref: cel.getIntData("ref"),
|
|
404
|
+
row: cel.getInternalData("row"),
|
|
405
|
+
col: cel.getInternalData("col"),
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
// MOUSEDOWN
|
|
414
|
+
this.addDOMEvent("mousedown", (e) => {
|
|
415
|
+
if( !this._edit || this._inEditor(e) ) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// click on another cell while editing:
|
|
420
|
+
// validate now, rows may be rebuilt before the click event (and the click lost)
|
|
421
|
+
const ref = targetCell(e);
|
|
422
|
+
if( ref && ref.row!==undefined ) {
|
|
423
|
+
this._endEdit( true, true );
|
|
424
|
+
|
|
425
|
+
if (!this._selection.has(ref.ref)) {
|
|
426
|
+
this._clearSelection( false );
|
|
427
|
+
this._addSelection(ref.ref,true);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
e.preventDefault( );
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// CLICK
|
|
435
|
+
this.addDOMEvent("click", (e) => {
|
|
436
|
+
if( this._inEditor(e) ) {
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const ref = targetCell(e);
|
|
441
|
+
if (ref) {
|
|
442
|
+
//TODO: multiselection
|
|
443
|
+
if (!this._selection.has(ref.ref)) {
|
|
444
|
+
this._clearSelection( false );
|
|
445
|
+
this._addSelection(ref.ref,true);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// DBLCLICK
|
|
451
|
+
this.addDOMEvent("dblclick", (e) => {
|
|
452
|
+
if( this._inEditor(e) ) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const ref = targetCell(e);
|
|
457
|
+
if (ref) {
|
|
458
|
+
//TODO: multiselection
|
|
459
|
+
if (!this._selection.has(ref.ref)) {
|
|
460
|
+
this._clearSelection(false);
|
|
461
|
+
this._addSelection(ref.ref,true);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const dev: EvDblClick = { context: { row: ref.row, col: ref.col } };
|
|
465
|
+
this.fire( "dblClick", dev );
|
|
466
|
+
|
|
467
|
+
// checkboxes are toggled, other cells are edited
|
|
468
|
+
if( !dev.defaultPrevented && !this._toggleCell( ref.row, ref.col ) ) {
|
|
469
|
+
this.editCell( ref.row, ref.col );
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// CONTEXT
|
|
475
|
+
this.addDOMEvent("contextmenu", (e) => {
|
|
476
|
+
// keep the native menu of the editor (copy/paste)
|
|
477
|
+
if( this._inEditor(e) ) {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const ref = targetCell(e);
|
|
482
|
+
if (ref) {
|
|
483
|
+
//TODO: multiselection
|
|
484
|
+
if (!this._selection.has(ref.ref)) {
|
|
485
|
+
this._clearSelection( false );
|
|
486
|
+
this._addSelection(ref.ref, true );
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
this.fire( "contextMenu", { uievent: e, context: { row: ref.row, col: ref.col } } );
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
this.fire( "contextMenu", { uievent: e, context: null } );
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
e.preventDefault();
|
|
496
|
+
e.stopPropagation();
|
|
497
|
+
});
|
|
498
|
+
|
|
361
499
|
if (props.store) {
|
|
362
500
|
this.setStore(props.store);
|
|
363
501
|
}
|
|
@@ -643,7 +781,8 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
643
781
|
if (lock) {
|
|
644
782
|
this._lock++;
|
|
645
783
|
}
|
|
646
|
-
|
|
784
|
+
// an unlock too many leaves it at 0: the grid goes on drawing
|
|
785
|
+
else if (this._lock > 0) {
|
|
647
786
|
if (--this._lock == 0 && this._dirty) {
|
|
648
787
|
this._update(true);
|
|
649
788
|
}
|
|
@@ -676,7 +815,9 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
676
815
|
const sizer = new CSizer("right");
|
|
677
816
|
|
|
678
817
|
sizer.on("stop", () => {
|
|
818
|
+
// the flex columns took what was left: the body gets the width of its columns
|
|
679
819
|
this._updateFlexs();
|
|
820
|
+
this._computeFullSize();
|
|
680
821
|
})
|
|
681
822
|
|
|
682
823
|
sizer.on("resize", (ev) => {
|
|
@@ -698,6 +839,24 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
698
839
|
}
|
|
699
840
|
})
|
|
700
841
|
|
|
842
|
+
// a double click on the sizer: the column takes its share of the place
|
|
843
|
+
// that is left (flex 1). To stop taking it, the sizer is dragged
|
|
844
|
+
sizer.addDOMEvent("dblclick", (ev) => {
|
|
845
|
+
// both: the grid has a double click of its own
|
|
846
|
+
ev.stopPropagation();
|
|
847
|
+
ev.preventDefault();
|
|
848
|
+
|
|
849
|
+
// a fixed column is not in the place that is shared
|
|
850
|
+
if (fixed || cdata.flex) {
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
cdata.flex = 1;
|
|
855
|
+
|
|
856
|
+
this._updateFlexs();
|
|
857
|
+
this._computeFullSize();
|
|
858
|
+
});
|
|
859
|
+
|
|
701
860
|
const cell = new Component({
|
|
702
861
|
cls: `cell`,
|
|
703
862
|
attrs: { "data-col": col },
|
|
@@ -956,6 +1115,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
956
1115
|
|
|
957
1116
|
const el = new Component({
|
|
958
1117
|
cls: "cell",
|
|
1118
|
+
attrs: { "data-col": col }, // the sizer of the column finds its cells by it
|
|
959
1119
|
style: { width: cdata?.width ? cdata.width + "px" : undefined, justifyContent: align },
|
|
960
1120
|
content
|
|
961
1121
|
});
|
|
@@ -993,7 +1153,12 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
993
1153
|
for (let x = 0; x < ccount; x++) {
|
|
994
1154
|
const cdata = this._getCol(x);
|
|
995
1155
|
|
|
996
|
-
|
|
1156
|
+
// the fixed columns are not in the viewport: their width is not taken from its own
|
|
1157
|
+
if (cdata.fixed) {
|
|
1158
|
+
continue;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
if (cdata.flex) {
|
|
997
1162
|
flexc += cdata.flex;
|
|
998
1163
|
}
|
|
999
1164
|
else {
|
|
@@ -1095,136 +1260,6 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
1095
1260
|
this._update()
|
|
1096
1261
|
});
|
|
1097
1262
|
|
|
1098
|
-
// WHEEL
|
|
1099
|
-
this.addDOMEvent("wheel", (ev: WheelEvent) => {
|
|
1100
|
-
if (ev.deltaY && this._store && this._store.getRowCount() >= SCROLL_LIMIT) {
|
|
1101
|
-
this._viewport.dom.scrollBy(0, ev.deltaY < 0 ? -1 : 1);
|
|
1102
|
-
ev.stopPropagation();
|
|
1103
|
-
ev.preventDefault();
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
if (this._has_fixed && ev.deltaY) {
|
|
1107
|
-
// wheel on fixed part
|
|
1108
|
-
// fixed part do not have scrollbar, so we need to handle it by hand
|
|
1109
|
-
let t = ev.target as Node;
|
|
1110
|
-
while (t != this.dom) {
|
|
1111
|
-
if (t == this._vheader.dom) {
|
|
1112
|
-
this._viewport.dom.scrollBy(0, ev.deltaY < 0 ? -this._row_height : this._row_height);
|
|
1113
|
-
ev.stopPropagation();
|
|
1114
|
-
ev.preventDefault();
|
|
1115
|
-
break;
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
t = t.parentNode;
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
})
|
|
1122
|
-
|
|
1123
|
-
const targetCell = (e: MouseEvent) => {
|
|
1124
|
-
let el = e.target as Element;
|
|
1125
|
-
while (el && !el.classList.contains("cell")) {
|
|
1126
|
-
el = el.parentElement;
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
if (el) {
|
|
1130
|
-
const cel = componentFromDOM(el);
|
|
1131
|
-
return {
|
|
1132
|
-
ref: cel.getIntData("ref"),
|
|
1133
|
-
row: cel.getInternalData("row"),
|
|
1134
|
-
col: cel.getInternalData("col"),
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
return undefined;
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
// MOUSEDOWN
|
|
1143
|
-
this.addDOMEvent("mousedown", (e) => {
|
|
1144
|
-
if( !this._edit || this._inEditor(e) ) {
|
|
1145
|
-
return;
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
// click on another cell while editing:
|
|
1149
|
-
// validate now, rows may be rebuilt before the click event (and the click lost)
|
|
1150
|
-
const ref = targetCell(e);
|
|
1151
|
-
if( ref && ref.row!==undefined ) {
|
|
1152
|
-
this._endEdit( true, true );
|
|
1153
|
-
|
|
1154
|
-
if (!this._selection.has(ref.ref)) {
|
|
1155
|
-
this._clearSelection( false );
|
|
1156
|
-
this._addSelection(ref.ref,true);
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
e.preventDefault( );
|
|
1160
|
-
}
|
|
1161
|
-
});
|
|
1162
|
-
|
|
1163
|
-
// CLICK
|
|
1164
|
-
this.addDOMEvent("click", (e) => {
|
|
1165
|
-
if( this._inEditor(e) ) {
|
|
1166
|
-
return;
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
const ref = targetCell(e);
|
|
1170
|
-
if (ref) {
|
|
1171
|
-
//TODO: multiselection
|
|
1172
|
-
if (!this._selection.has(ref.ref)) {
|
|
1173
|
-
this._clearSelection( false );
|
|
1174
|
-
this._addSelection(ref.ref,true);
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
});
|
|
1178
|
-
|
|
1179
|
-
// DBLCLICK
|
|
1180
|
-
this.addDOMEvent("dblclick", (e) => {
|
|
1181
|
-
if( this._inEditor(e) ) {
|
|
1182
|
-
return;
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
const ref = targetCell(e);
|
|
1186
|
-
if (ref) {
|
|
1187
|
-
//TODO: multiselection
|
|
1188
|
-
if (!this._selection.has(ref.ref)) {
|
|
1189
|
-
this._clearSelection(false);
|
|
1190
|
-
this._addSelection(ref.ref,true);
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
const dev: EvDblClick = { context: { row: ref.row, col: ref.col } };
|
|
1194
|
-
this.fire( "dblClick", dev );
|
|
1195
|
-
|
|
1196
|
-
// checkboxes are toggled, other cells are edited
|
|
1197
|
-
if( !dev.defaultPrevented && !this._toggleCell( ref.row, ref.col ) ) {
|
|
1198
|
-
this.editCell( ref.row, ref.col );
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
});
|
|
1202
|
-
|
|
1203
|
-
// CONTEXT
|
|
1204
|
-
this.addDOMEvent("contextmenu", (e) => {
|
|
1205
|
-
// keep the native menu of the editor (copy/paste)
|
|
1206
|
-
if( this._inEditor(e) ) {
|
|
1207
|
-
return;
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
const ref = targetCell(e);
|
|
1211
|
-
if (ref) {
|
|
1212
|
-
//TODO: multiselection
|
|
1213
|
-
if (!this._selection.has(ref.ref)) {
|
|
1214
|
-
this._clearSelection( false );
|
|
1215
|
-
this._addSelection(ref.ref, true );
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
this.fire( "contextMenu", { uievent: e, context: { row: ref.row, col: ref.col } } );
|
|
1219
|
-
}
|
|
1220
|
-
else {
|
|
1221
|
-
this.fire( "contextMenu", { uievent: e, context: null } );
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
e.preventDefault();
|
|
1225
|
-
e.stopPropagation();
|
|
1226
|
-
});
|
|
1227
|
-
|
|
1228
1263
|
this._updateFlexs();
|
|
1229
1264
|
|
|
1230
1265
|
this._fheader = this._buildColHeader(true);
|
|
@@ -53,7 +53,8 @@ export class Switch extends HBox<SwitchProps> {
|
|
|
53
53
|
new Component( {
|
|
54
54
|
cls: "switch",
|
|
55
55
|
content: [
|
|
56
|
-
|
|
56
|
+
// value: sent by a <form> of the browser when checked; a Form of x4 reads a boolean
|
|
57
|
+
new Input( { type: "checkbox", id: inputId, name: props.name, value: props.value, checked: props.checked } ),
|
|
57
58
|
new Component( { cls: "track" } ),
|
|
58
59
|
new Component( { cls: "thumb" } ),
|
|
59
60
|
]
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
|
|
25
25
|
.x4simpletextarea {
|
|
26
26
|
|
|
27
|
+
flex-shrink: 0; // alone in a box, it keeps its height: the box does not crush it
|
|
28
|
+
resize: none; // no handle unless the prop resize asks for one
|
|
27
29
|
padding: 4px;
|
|
28
30
|
outline: none;
|
|
29
31
|
font: inherit;
|
|
@@ -39,8 +41,10 @@
|
|
|
39
41
|
color: white;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
// as the one of an input
|
|
45
|
+
&::placeholder {
|
|
46
|
+
color: var( --input-placeholder );
|
|
47
|
+
font-style: italic;
|
|
44
48
|
}
|
|
45
49
|
}
|
|
46
50
|
|