x4js 2.0.28 → 2.0.31
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/.vscode/launch.json +14 -0
- package/.vscode/settings.json +2 -0
- package/ai-comments.txt +97 -0
- package/demo/assets/house-light.svg +1 -0
- package/demo/assets/radio.svg +4 -0
- package/demo/index.html +12 -0
- package/demo/main.scss +23 -0
- package/demo/main.ts +324 -0
- package/demo/package.json +26 -0
- package/demo/scss.d.ts +4 -0
- package/demo/svg.d.ts +1 -0
- package/demo/tsconfig.json +14 -0
- package/lib/types/x4js.d.ts +0 -2374
- package/package.json +23 -47
- package/src/colors.scss +246 -0
- package/src/components/boxes/boxes.module.scss +1 -1
- package/src/components/boxes/boxes.ts +139 -28
- package/src/components/button/button.ts +76 -29
- package/src/components/combobox/combobox.ts +1 -1
- package/src/components/dialog/dialog.ts +4 -0
- package/src/components/gridview/gridview.ts +104 -6
- package/src/components/icon/icon.ts +42 -14
- package/src/components/input/input.ts +146 -74
- package/src/components/keyboard/keyboard.module.scss +1 -1
- package/src/components/keyboard/keyboard.ts +31 -9
- package/src/components/label/label.module.scss +9 -0
- package/src/components/label/label.ts +10 -6
- package/src/components/link/link.module.scss +44 -0
- package/src/components/link/link.ts +7 -1
- package/src/components/listbox/listbox.module.scss +18 -4
- package/src/components/listbox/listbox.ts +32 -12
- package/src/components/menu/menu.module.scss +14 -2
- package/src/components/menu/menu.ts +1 -1
- package/src/components/messages/messages.ts +13 -5
- package/src/components/panel/panel.module.scss +7 -0
- package/src/components/popup/popup.ts +14 -10
- package/src/components/propgrid/propgrid.ts +1 -1
- package/src/components/shared.scss +4 -0
- package/src/components/spreadsheet/spreadsheet.ts +81 -34
- package/src/components/tabs/tabs.module.scss +1 -0
- package/src/components/textarea/textarea.ts +8 -2
- package/src/components/textedit/textedit.ts +7 -0
- package/src/components/themes.scss +2 -0
- package/src/components/tooltips/tooltips.ts +15 -3
- package/src/core/component.ts +358 -162
- package/src/core/core_application.ts +129 -32
- package/src/core/core_colors.ts +382 -119
- package/src/core/core_data.ts +73 -86
- package/src/core/core_dom.ts +10 -0
- package/src/core/core_dragdrop.ts +32 -7
- package/src/core/core_element.ts +111 -4
- package/src/core/core_events.ts +48 -11
- package/src/core/core_i18n.ts +2 -0
- package/src/core/core_pdf.ts +454 -0
- package/src/core/core_router.ts +64 -5
- package/src/core/core_state.ts +1 -0
- package/src/core/core_styles.ts +11 -12
- package/src/core/core_svg.ts +346 -51
- package/src/core/core_tools.ts +105 -17
- package/src/x4.d.ts +10 -0
- package/src/x4.ts +1 -0
- package/src/x4tsx.d.ts +2 -1
- package/tsconfig.json +11 -0
- package/lib/README.txt +0 -20
- package/lib/cjs/x4.css +0 -1
- package/lib/cjs/x4.js +0 -2
- package/lib/esm/x4.css +0 -1
- package/lib/esm/x4.mjs +0 -2
- package/lib/styles/x4.css +0 -1
|
@@ -20,75 +20,156 @@ import { class_ns, formatIntlDate, IComponentInterface, IFormElement, isString }
|
|
|
20
20
|
|
|
21
21
|
import "./input.module.scss"
|
|
22
22
|
|
|
23
|
+
|
|
24
|
+
function getRadioOwner( el: Element ) {
|
|
25
|
+
|
|
26
|
+
while( el!=document.body ) {
|
|
27
|
+
const comp = componentFromDOM(el);
|
|
28
|
+
const ifx = comp.queryInterface( "tab-handler");
|
|
29
|
+
if( ifx ) {
|
|
30
|
+
return el;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
el = el.parentElement;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return document;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Base properties for all input types.
|
|
42
|
+
*/
|
|
43
|
+
|
|
23
44
|
export interface BaseProps extends ComponentProps {
|
|
45
|
+
/**
|
|
46
|
+
* Input field name.
|
|
47
|
+
* required if you want to use form getValues/setValues
|
|
48
|
+
*/
|
|
49
|
+
|
|
24
50
|
name?: string;
|
|
51
|
+
/** Automatically focus the input on page load. */
|
|
25
52
|
autofocus?: boolean;
|
|
53
|
+
/** Marks the input as required. */
|
|
26
54
|
required?: boolean;
|
|
55
|
+
/** Makes the input read-only. */
|
|
27
56
|
readonly?: boolean;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
focus
|
|
31
|
-
|
|
57
|
+
/** Placeholder text displayed when empty. */
|
|
58
|
+
placeholder?: string;
|
|
59
|
+
/** Fired when the input receives/loses focus. */
|
|
60
|
+
focus?: EventCallback<EvFocus>;
|
|
61
|
+
/** Fired when the input value changes. */
|
|
62
|
+
change?: EventCallback<EvChange>;
|
|
32
63
|
}
|
|
33
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Checkbox-specific input properties.
|
|
67
|
+
*/
|
|
68
|
+
|
|
34
69
|
interface CheckboxProps extends BaseProps {
|
|
35
70
|
type: "checkbox";
|
|
71
|
+
/** Checkbox value (submitted when checked). */
|
|
36
72
|
value?: boolean | number | string;
|
|
73
|
+
/** Initial checked state. */
|
|
37
74
|
checked?: boolean;
|
|
38
75
|
}
|
|
39
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Radio button-specific input properties.
|
|
79
|
+
*/
|
|
80
|
+
|
|
40
81
|
interface RadioProps extends BaseProps {
|
|
41
82
|
type: "radio";
|
|
83
|
+
/** Radio value (submitted when selected). */
|
|
42
84
|
value?: boolean | number | string;
|
|
85
|
+
/** Initial checked state. */
|
|
43
86
|
checked?: boolean;
|
|
44
87
|
}
|
|
45
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Range slider input properties.
|
|
91
|
+
*/
|
|
92
|
+
|
|
46
93
|
export interface RangeProps extends BaseProps {
|
|
47
94
|
type: "range";
|
|
95
|
+
/** Current slider value. */
|
|
48
96
|
value?: number;
|
|
97
|
+
/** Minimum allowed value. */
|
|
49
98
|
min: number;
|
|
99
|
+
/** Maximum allowed value. */
|
|
50
100
|
max: number;
|
|
101
|
+
/** Step increment. */
|
|
51
102
|
step?: number;
|
|
52
103
|
}
|
|
53
104
|
|
|
105
|
+
/**
|
|
106
|
+
* File upload input properties.
|
|
107
|
+
*/
|
|
54
108
|
export interface FileProps extends BaseProps {
|
|
55
109
|
type: "file";
|
|
110
|
+
/** Allowed file types (e.g., `"image/*"` or `[".pdf", ".doc"]`). */
|
|
56
111
|
accept: string | string[];
|
|
57
112
|
value?: never;
|
|
58
113
|
}
|
|
59
114
|
|
|
60
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Date picker input properties.
|
|
117
|
+
*/
|
|
61
118
|
export interface DateProps extends BaseProps {
|
|
62
119
|
type: "date";
|
|
63
|
-
value
|
|
120
|
+
/** Current date value (Date object or ISO string). */
|
|
121
|
+
value?: Date | string;
|
|
64
122
|
}
|
|
65
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Time picker input properties.
|
|
126
|
+
*/
|
|
127
|
+
|
|
66
128
|
export interface TimeProps extends BaseProps {
|
|
67
129
|
type: "time";
|
|
68
130
|
readonly?: boolean;
|
|
69
131
|
required?: boolean;
|
|
70
|
-
value
|
|
132
|
+
/** Current time value (e.g., `"12:30"`). */
|
|
133
|
+
value?: string;
|
|
71
134
|
}
|
|
72
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Numeric input properties.
|
|
138
|
+
*/
|
|
139
|
+
|
|
73
140
|
export interface NumberProps extends BaseProps {
|
|
74
141
|
type: "number";
|
|
75
142
|
readonly?: boolean;
|
|
76
143
|
required?: boolean;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
144
|
+
/** Current numeric value. */
|
|
145
|
+
value?: number | string;
|
|
146
|
+
/** Minimum allowed value. */
|
|
147
|
+
min?: number;
|
|
148
|
+
/** Maximum allowed value. */
|
|
149
|
+
max?: number;
|
|
150
|
+
/** Step increment. */
|
|
151
|
+
step?: number;
|
|
81
152
|
}
|
|
82
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Text/email/password input properties.
|
|
156
|
+
*/
|
|
157
|
+
|
|
83
158
|
export interface TextInputProps extends BaseProps {
|
|
84
159
|
type?: "text" | "email" | "password";
|
|
85
160
|
readonly?: boolean;
|
|
86
161
|
required?: boolean;
|
|
162
|
+
/** Regex pattern for validation. */
|
|
87
163
|
pattern?: string;
|
|
164
|
+
/** Input value. */
|
|
88
165
|
value?: string | number;
|
|
166
|
+
/** Enables/disables spellcheck. */
|
|
89
167
|
spellcheck?: boolean;
|
|
168
|
+
/** Minimum input length. */
|
|
90
169
|
minlength?: number;
|
|
170
|
+
/** Maximum input length. */
|
|
91
171
|
maxlength?: number;
|
|
172
|
+
trim?: boolean;
|
|
92
173
|
}
|
|
93
174
|
|
|
94
175
|
|
|
@@ -102,7 +183,21 @@ interface InputEvents extends ComponentEvent {
|
|
|
102
183
|
|
|
103
184
|
|
|
104
185
|
/**
|
|
105
|
-
*
|
|
186
|
+
* Customizable input component supporting multiple types (text, number, date, etc.).
|
|
187
|
+
* Auto-generates CSS class: `x4input`.
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```ts
|
|
191
|
+
* // Text input
|
|
192
|
+
* const nameInput = new Input({ type: "text", placeholder: "Enter name" });
|
|
193
|
+
*
|
|
194
|
+
* // Checkbox
|
|
195
|
+
* const agreeCheckbox = new Input({
|
|
196
|
+
* type: "checkbox",
|
|
197
|
+
* checked: true,
|
|
198
|
+
* change: (e) => console.log("Checked:", e.value)
|
|
199
|
+
* });
|
|
200
|
+
* ```
|
|
106
201
|
*/
|
|
107
202
|
|
|
108
203
|
@class_ns( "x4" )
|
|
@@ -262,27 +357,31 @@ export class Input extends Component<InputProps,InputEvents> {
|
|
|
262
357
|
}
|
|
263
358
|
}
|
|
264
359
|
|
|
265
|
-
/**
|
|
266
|
-
|
|
267
|
-
*/
|
|
268
|
-
|
|
360
|
+
/** Gets the current input value as a string. */
|
|
361
|
+
|
|
269
362
|
public getValue( ) {
|
|
270
|
-
|
|
363
|
+
let v = (this.dom as HTMLInputElement).value;
|
|
364
|
+
if( (this.props as any).trim!==false ) {
|
|
365
|
+
v = v.trim( );
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return v;
|
|
271
369
|
}
|
|
272
370
|
|
|
273
371
|
/**
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
372
|
+
* Sets the input value.
|
|
373
|
+
* @param value - New value (converted to string).
|
|
374
|
+
*/
|
|
277
375
|
|
|
278
376
|
public setValue( value: string ) {
|
|
279
377
|
(this.dom as HTMLInputElement).value = value+"";
|
|
280
378
|
}
|
|
281
379
|
|
|
282
380
|
/**
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
381
|
+
* Gets the numeric value (for `type="number"` or `type="range"`).
|
|
382
|
+
* @param defNan - Default value if parsing fails (default: `NaN`).
|
|
383
|
+
* @returns Parsed number or `defNan`.
|
|
384
|
+
*/
|
|
286
385
|
|
|
287
386
|
public getNumValue( defNan?: number ) {
|
|
288
387
|
const v = parseFloat( this.getValue() );
|
|
@@ -293,11 +392,13 @@ export class Input extends Component<InputProps,InputEvents> {
|
|
|
293
392
|
}
|
|
294
393
|
|
|
295
394
|
/**
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
395
|
+
* Sets a numeric value with optional decimal precision.
|
|
396
|
+
* @param value - Numeric value to set.
|
|
397
|
+
* @param ndec - Decimal places:
|
|
398
|
+
* `-1` = auto,
|
|
399
|
+
* `-2` = use `step` prop,
|
|
400
|
+
* `≥0` = fixed decimals.
|
|
401
|
+
*/
|
|
301
402
|
|
|
302
403
|
public setNumValue( value: number, ndec = -1 ) {
|
|
303
404
|
|
|
@@ -319,48 +420,38 @@ export class Input extends Component<InputProps,InputEvents> {
|
|
|
319
420
|
this.setValue( value+"" );
|
|
320
421
|
}
|
|
321
422
|
|
|
322
|
-
/**
|
|
323
|
-
|
|
324
|
-
*/
|
|
325
|
-
|
|
326
|
-
public getCheck() {
|
|
423
|
+
/** Gets the checked state (for checkboxes/radio buttons). */
|
|
424
|
+
public getCheck() {
|
|
327
425
|
const d = this.dom as HTMLInputElement;
|
|
328
426
|
return d.checked;
|
|
329
427
|
}
|
|
330
428
|
|
|
331
|
-
/**
|
|
332
|
-
|
|
333
|
-
* @param {boolean} ck new checked value
|
|
334
|
-
*/
|
|
335
|
-
|
|
429
|
+
/** Sets the checked state (for checkboxes/radio buttons). */
|
|
430
|
+
|
|
336
431
|
public setCheck(ck: boolean) {
|
|
337
432
|
const d = this.dom as HTMLInputElement;
|
|
338
433
|
d.checked = ck;
|
|
339
434
|
}
|
|
340
435
|
|
|
341
|
-
/**
|
|
342
|
-
|
|
343
|
-
*/
|
|
344
|
-
|
|
436
|
+
/** Toggles read-only mode. */
|
|
437
|
+
|
|
345
438
|
public setReadOnly( ro: boolean ) {
|
|
346
439
|
const d = this.dom as HTMLInputElement;
|
|
347
440
|
d.readOnly = ro;
|
|
348
441
|
}
|
|
349
442
|
|
|
350
|
-
/**
|
|
351
|
-
|
|
352
|
-
*/
|
|
353
|
-
|
|
443
|
+
/** Selects all text in the input. */
|
|
444
|
+
|
|
354
445
|
public selectAll( ) {
|
|
355
446
|
const d = this.dom as HTMLInputElement;
|
|
356
447
|
d.select();
|
|
357
448
|
}
|
|
358
449
|
|
|
359
450
|
/**
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
451
|
+
* Selects a text range.
|
|
452
|
+
* @param start - Start position
|
|
453
|
+
* @param length - Length of selection
|
|
454
|
+
*/
|
|
364
455
|
|
|
365
456
|
public select( start: number, length: number = 9999 ) : void {
|
|
366
457
|
const d = this.dom as HTMLInputElement;
|
|
@@ -368,9 +459,9 @@ export class Input extends Component<InputProps,InputEvents> {
|
|
|
368
459
|
}
|
|
369
460
|
|
|
370
461
|
/**
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
462
|
+
* Gets the current text selection.
|
|
463
|
+
* @returns Object with `start` and `length` properties.
|
|
464
|
+
*/
|
|
374
465
|
public getSelection( ) {
|
|
375
466
|
const d = this.dom as HTMLInputElement;
|
|
376
467
|
|
|
@@ -380,10 +471,8 @@ export class Input extends Component<InputProps,InputEvents> {
|
|
|
380
471
|
};
|
|
381
472
|
}
|
|
382
473
|
|
|
383
|
-
/**
|
|
384
|
-
|
|
385
|
-
*/
|
|
386
|
-
|
|
474
|
+
/** Validates the input (checks `required` constraint). */
|
|
475
|
+
|
|
387
476
|
public isValid( ) {
|
|
388
477
|
|
|
389
478
|
if( (this.props as any).required ) {
|
|
@@ -446,20 +535,3 @@ export class Input extends Component<InputProps,InputEvents> {
|
|
|
446
535
|
}
|
|
447
536
|
}
|
|
448
537
|
|
|
449
|
-
|
|
450
|
-
function getRadioOwner( el: Element ) {
|
|
451
|
-
|
|
452
|
-
while( el!=document.body ) {
|
|
453
|
-
const comp = componentFromDOM(el);
|
|
454
|
-
const ifx = comp.queryInterface( "tab-handler");
|
|
455
|
-
if( ifx ) {
|
|
456
|
-
return el;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
el = el.parentElement;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
return document;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
|
|
@@ -121,14 +121,14 @@ export class Keyboard extends HBox<KeyboardProps>
|
|
|
121
121
|
this.hide( );
|
|
122
122
|
|
|
123
123
|
this.addDOMEvent( "mousedown", (e) => {
|
|
124
|
-
this.
|
|
124
|
+
this.handleKeyEvent( e );
|
|
125
125
|
e.preventDefault( );
|
|
126
126
|
e.stopPropagation( );
|
|
127
127
|
});
|
|
128
128
|
|
|
129
129
|
// for rapid people
|
|
130
130
|
this.addDOMEvent( "dblclick", (e) => {
|
|
131
|
-
this.
|
|
131
|
+
this.handleKeyEvent( e );
|
|
132
132
|
e.preventDefault( );
|
|
133
133
|
e.stopPropagation( );
|
|
134
134
|
});
|
|
@@ -148,7 +148,7 @@ export class Keyboard extends HBox<KeyboardProps>
|
|
|
148
148
|
*
|
|
149
149
|
*/
|
|
150
150
|
|
|
151
|
-
private
|
|
151
|
+
private handleKeyEvent( e: UIEvent ) {
|
|
152
152
|
let target = e.target as HTMLElement;
|
|
153
153
|
let key;
|
|
154
154
|
|
|
@@ -165,6 +165,10 @@ export class Keyboard extends HBox<KeyboardProps>
|
|
|
165
165
|
return;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
this._handleKey( key );
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private _handleKey( key: number ) {
|
|
168
172
|
switch( key ) {
|
|
169
173
|
// bk space
|
|
170
174
|
case 2: {
|
|
@@ -326,11 +330,16 @@ export class Keyboard extends HBox<KeyboardProps>
|
|
|
326
330
|
private handleFocus( target: Element, enter: boolean ) {
|
|
327
331
|
|
|
328
332
|
if( enter ) {
|
|
329
|
-
if( target.tagName=='INPUT'
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
333
|
+
if( target.tagName=='INPUT' ) {
|
|
334
|
+
const input = target as HTMLInputElement;
|
|
335
|
+
if( !input.readOnly &&
|
|
336
|
+
input.type!='checkbox' && input.type!='radio' &&
|
|
337
|
+
input.type!='range' && input.type!='file' ) {
|
|
338
|
+
this.input = input;
|
|
339
|
+
this.visible = true;
|
|
340
|
+
this.setTimeout( "vis", 200, this._updateVis );
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
334
343
|
}
|
|
335
344
|
}
|
|
336
345
|
|
|
@@ -430,6 +439,7 @@ export class Keyboard extends HBox<KeyboardProps>
|
|
|
430
439
|
let content = line[i];
|
|
431
440
|
let key;
|
|
432
441
|
let icon = null;
|
|
442
|
+
let repeat = false;
|
|
433
443
|
|
|
434
444
|
if( content.length>2 && content[0]=='{' && content[content.length-1]=='}') {
|
|
435
445
|
|
|
@@ -452,6 +462,7 @@ export class Keyboard extends HBox<KeyboardProps>
|
|
|
452
462
|
|
|
453
463
|
case 2:
|
|
454
464
|
{
|
|
465
|
+
repeat = true;
|
|
455
466
|
content = undefined;
|
|
456
467
|
icon = icon_bksp;
|
|
457
468
|
cls += ' cdel';
|
|
@@ -516,7 +527,18 @@ export class Keyboard extends HBox<KeyboardProps>
|
|
|
516
527
|
key = line[i].charCodeAt(0);
|
|
517
528
|
}
|
|
518
529
|
|
|
519
|
-
let el = new Button( {
|
|
530
|
+
let el = new Button( {
|
|
531
|
+
cls,
|
|
532
|
+
label: content,
|
|
533
|
+
attrs: {'data-key': key},
|
|
534
|
+
icon,
|
|
535
|
+
autorepeat: repeat,
|
|
536
|
+
click: ( e ) => {
|
|
537
|
+
if( e.repeat ) {
|
|
538
|
+
this._handleKey( key )
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
} );
|
|
520
542
|
tl.push( el );
|
|
521
543
|
}
|
|
522
544
|
|
|
@@ -30,16 +30,25 @@
|
|
|
30
30
|
background-color: var( --label-background );
|
|
31
31
|
gap: 0.2em;
|
|
32
32
|
|
|
33
|
+
&.al-right
|
|
33
34
|
&.right {
|
|
34
35
|
text-align: right;
|
|
35
36
|
justify-content: end;
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
&.al-center,
|
|
38
40
|
&.center {
|
|
39
41
|
text-align: center;
|
|
40
42
|
justify-content: center;
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
&.al-right {
|
|
46
|
+
padding-left: 8px;
|
|
47
|
+
#icon {
|
|
48
|
+
order: 2;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
43
52
|
#text {
|
|
44
53
|
&:empty {
|
|
45
54
|
display: none;
|
|
@@ -24,6 +24,7 @@ export interface LabelProps extends ComponentProps {
|
|
|
24
24
|
text?: string | UnsafeHtml;
|
|
25
25
|
icon?: string;
|
|
26
26
|
labelFor?: string;
|
|
27
|
+
align?: "left" | "center" | "right";
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
/**
|
|
@@ -32,29 +33,32 @@ export interface LabelProps extends ComponentProps {
|
|
|
32
33
|
|
|
33
34
|
@class_ns( "x4" )
|
|
34
35
|
export class Label extends Component<LabelProps> {
|
|
35
|
-
#text: Component;
|
|
36
36
|
|
|
37
37
|
constructor( p: LabelProps ) {
|
|
38
38
|
super( { ...p, content: null } );
|
|
39
39
|
|
|
40
40
|
this.setContent( [
|
|
41
41
|
new Icon( { id:"icon", iconId: this.props.icon } ),
|
|
42
|
-
|
|
42
|
+
new Component( { tag: 'span', id: 'text' } )
|
|
43
43
|
] );
|
|
44
44
|
|
|
45
45
|
// small hack for react:
|
|
46
46
|
// p.content may be the text
|
|
47
|
-
|
|
48
|
-
this.setText( text );
|
|
47
|
+
this.setText( this.props.text );
|
|
49
48
|
|
|
50
49
|
if( p.labelFor ) {
|
|
51
50
|
this.setAttribute( "for", p.labelFor );
|
|
52
51
|
}
|
|
52
|
+
|
|
53
|
+
if( p.align ) {
|
|
54
|
+
this.addClass( "al-"+p.align );
|
|
55
|
+
}
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
setText( text: string | UnsafeHtml ) {
|
|
56
|
-
this
|
|
57
|
-
|
|
59
|
+
const lab = this.query<Icon>( "#text" );
|
|
60
|
+
lab.setContent( text );
|
|
61
|
+
lab.setClass( "empty", !text );
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
setIcon( icon: string ) {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ___ ___ __
|
|
3
|
+
* \ \/ / / _
|
|
4
|
+
* \ / /_| |_
|
|
5
|
+
* / \____ _|
|
|
6
|
+
* /__/\__\ |_|
|
|
7
|
+
*
|
|
8
|
+
* @file link.module.scss
|
|
9
|
+
* @author Etienne Cochard
|
|
10
|
+
*
|
|
11
|
+
* @copyright (c) 2026 R-libre ingenierie
|
|
12
|
+
*
|
|
13
|
+
* Use of this source code is governed by an MIT-style license
|
|
14
|
+
* that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
|
|
15
|
+
**/
|
|
16
|
+
|
|
17
|
+
.x4link {
|
|
18
|
+
cursor: pointer;
|
|
19
|
+
|
|
20
|
+
#text {
|
|
21
|
+
text-decoration: underline;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&[disabled] {
|
|
25
|
+
cursor: not-allowed;
|
|
26
|
+
border-color: var( --border );
|
|
27
|
+
color: var( --listbox-item-color-disabled );
|
|
28
|
+
|
|
29
|
+
&>.body {
|
|
30
|
+
.x4viewport {
|
|
31
|
+
pointer-events: none;
|
|
32
|
+
.x4item {
|
|
33
|
+
&.selected {
|
|
34
|
+
background-color: var( --listbox-item-color-sel-disabled );
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&:focus-within {
|
|
42
|
+
border-color: var( --listbox-border-focus );
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -19,6 +19,7 @@ import { EventCallback } from '../../core/core_events';
|
|
|
19
19
|
import { class_ns, UnsafeHtml } from '../../core/core_tools';
|
|
20
20
|
import { Label } from '../label/label';
|
|
21
21
|
|
|
22
|
+
import "./link.module.scss"
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Link events
|
|
@@ -36,6 +37,7 @@ interface LinkProps extends ComponentProps {
|
|
|
36
37
|
href: string;
|
|
37
38
|
text?: string | UnsafeHtml; // you can also use content for complexe content
|
|
38
39
|
icon?: string;
|
|
40
|
+
align?: "left" | "center" | "right";
|
|
39
41
|
click?: EventCallback<EvClick>;
|
|
40
42
|
}
|
|
41
43
|
|
|
@@ -44,12 +46,16 @@ export class Link extends Component<LinkProps,LinkEvents> {
|
|
|
44
46
|
constructor( props: LinkProps ) {
|
|
45
47
|
super( { tag: "a", ...props} );
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
if( props.href ) {
|
|
50
|
+
this.setAttribute( "href", props.href );
|
|
51
|
+
}
|
|
52
|
+
|
|
48
53
|
this.mapPropEvents( props, "click" );
|
|
49
54
|
|
|
50
55
|
this.setContent( new Label( {
|
|
51
56
|
text: props.text,
|
|
52
57
|
icon: props.icon,
|
|
58
|
+
align: props.align,
|
|
53
59
|
} ) );
|
|
54
60
|
|
|
55
61
|
this.addDOMEvent('click', (e) => this._on_click(e));
|
|
@@ -39,16 +39,20 @@
|
|
|
39
39
|
border: 1px solid var( --listbox-border );
|
|
40
40
|
background-color: var( --listbox-background );
|
|
41
41
|
|
|
42
|
-
&:focus-within {
|
|
43
|
-
border-color: var( --listbox-border-focus );
|
|
44
|
-
}
|
|
45
|
-
|
|
46
42
|
outline: none;
|
|
47
43
|
|
|
48
44
|
&>.x4header {
|
|
49
45
|
border-bottom: 1px solid var( --border );
|
|
50
46
|
}
|
|
51
47
|
|
|
48
|
+
&>.title {
|
|
49
|
+
background: white;
|
|
50
|
+
padding: 4px;
|
|
51
|
+
font-weight: bold;
|
|
52
|
+
color: black;
|
|
53
|
+
border-bottom: 1px solid var(--border);
|
|
54
|
+
}
|
|
55
|
+
|
|
52
56
|
&>.body {
|
|
53
57
|
width: 100%;
|
|
54
58
|
flex-basis: 0;
|
|
@@ -94,6 +98,12 @@
|
|
|
94
98
|
}
|
|
95
99
|
}
|
|
96
100
|
|
|
101
|
+
.ref-c1 { width: var( --ref-c1-width); }
|
|
102
|
+
.ref-c2 { width: var( --ref-c2-width); }
|
|
103
|
+
.ref-c3 { width: var( --ref-c3-width); }
|
|
104
|
+
.ref-c4 { width: var( --ref-c4-width); }
|
|
105
|
+
.ref-c5 { width: var( --ref-c5-width); }
|
|
106
|
+
|
|
97
107
|
//&:active{
|
|
98
108
|
//background-color: var( --color-80 );
|
|
99
109
|
//color: var(--color-0);
|
|
@@ -162,4 +172,8 @@
|
|
|
162
172
|
}
|
|
163
173
|
}
|
|
164
174
|
}
|
|
175
|
+
|
|
176
|
+
&:focus-within {
|
|
177
|
+
border-color: var( --listbox-border-focus );
|
|
178
|
+
}
|
|
165
179
|
}
|