x4js 2.3.13 → 2.3.14
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/aicontext.md +22 -4
- package/package.json +1 -1
- package/src/components/colorinput/colorinput.module.scss +36 -2
- package/src/components/colorinput/colorinput.ts +202 -32
- package/src/components/colorpicker/colorpicker.module.scss +23 -0
- package/src/components/colorpicker/colorpicker.ts +149 -35
- package/src/components/propgrid/progrid.module.scss +49 -31
- package/src/components/propgrid/propgrid.ts +36 -3
- package/src/core/component.ts +14 -5
- package/src/core/core_tools.ts +1 -1
package/aicontext.md
CHANGED
|
@@ -130,7 +130,7 @@ interface ComponentProps {
|
|
|
130
130
|
ns?: string // SVG/MathML namespace
|
|
131
131
|
cls?: string // extra CSS classes
|
|
132
132
|
id?: string
|
|
133
|
-
style?:
|
|
133
|
+
style?: StyleProps // numbers get "px" except unitless
|
|
134
134
|
attrs?: Record<string, string | number | boolean>
|
|
135
135
|
content?: ComponentContent
|
|
136
136
|
dom_events?: GlobalDOMEvents
|
|
@@ -188,7 +188,7 @@ getInternalData<T>(name: string|symbol): T
|
|
|
188
188
|
### Styles
|
|
189
189
|
|
|
190
190
|
```ts
|
|
191
|
-
setStyle(style:
|
|
191
|
+
setStyle(style: StyleProps): this // numbers auto-suffixed "px" except unitless
|
|
192
192
|
setStyleValue<K>(name: K, value): this
|
|
193
193
|
getStyleValue<K>(name: K): CSSStyleDeclaration[K]
|
|
194
194
|
setWidth(w: number|string): void
|
|
@@ -633,7 +633,20 @@ class AutoComplete extends TextEdit {
|
|
|
633
633
|
### `ColorInput`
|
|
634
634
|
|
|
635
635
|
```ts
|
|
636
|
-
|
|
636
|
+
interface ColorInputProps extends BoxProps {
|
|
637
|
+
color: Color | string // null: no color shown
|
|
638
|
+
name?: string
|
|
639
|
+
format?: "rgb" | "hex" // text of the field, "rgb" by default
|
|
640
|
+
picker?: boolean // a click on the swatch opens a ColorPicker
|
|
641
|
+
swatches?: (Color|string)[] | (() => (Color|string)[]) // a function is called each time the picker opens
|
|
642
|
+
nullable?: boolean // "no color" can be chosen
|
|
643
|
+
change?: EventCallback<EvColorChange> // user changes only, not setColor
|
|
644
|
+
}
|
|
645
|
+
class ColorInput extends HBox { // queryInterface("form-element"): "#rrggbb" or null
|
|
646
|
+
getColor(): Color; getValue(): string
|
|
647
|
+
setColor(color: Color | string): void; setSwatches(swatches): void
|
|
648
|
+
}
|
|
649
|
+
interface EvColorChange extends EvChange { color: Color; value: string } // "#rrggbb" or "#rrggbbaa"; both null for "no color"
|
|
637
650
|
```
|
|
638
651
|
|
|
639
652
|
### `FileDrop` / `FileDialog`
|
|
@@ -796,6 +809,9 @@ class PropertyGrid extends VBox {
|
|
|
796
809
|
setItems(groups: PropertyGroup[]): void
|
|
797
810
|
setPropValue(name: string, value): void
|
|
798
811
|
}
|
|
812
|
+
// PropertyValue.type: 'boolean' | 'number' | 'string' | 'password' | 'options' | 'color' | 'label' | 'button'
|
|
813
|
+
// 'color': the editor is a ColorInput (hex, with picker); swatches?: string[] | (() => string[]), nullable?: boolean,
|
|
814
|
+
// callback(name, value) receives "#rrggbb", or null for "no color"
|
|
799
815
|
```
|
|
800
816
|
|
|
801
817
|
---
|
|
@@ -978,7 +994,9 @@ class Video extends Box {
|
|
|
978
994
|
set stream(stream: MediaStream)
|
|
979
995
|
}
|
|
980
996
|
class Calendar extends VBox { getDate(): Date; setDate(date: Date): void }
|
|
981
|
-
class ColorPicker extends VBox
|
|
997
|
+
class ColorPicker extends VBox { // props: color, swatches?, nullable?; "change": EvColorChange, also fired once when built
|
|
998
|
+
getColor(): Color; setColor(color: Color | string): void; setSwatches(swatches): void
|
|
999
|
+
}
|
|
982
1000
|
class Panel extends VBox { setTitle(title: string): void }
|
|
983
1001
|
class Breadcrumbs extends HBox { setItems(elements): void }
|
|
984
1002
|
class Keyboard extends HBox // virtual keyboard
|
package/package.json
CHANGED
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
:root {
|
|
18
18
|
--colorinput-btn-background: inherit;
|
|
19
19
|
--colorinput-btn-color: var( --text-secondary );
|
|
20
|
-
--colorinput-btn-color-hover: var( --text-primary );
|
|
20
|
+
--colorinput-btn-color-hover: var( --text-primary );
|
|
21
|
+
--colorinput-popup-background: var( --background-primary );
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
.x4colorinput {
|
|
@@ -33,6 +34,27 @@
|
|
|
33
34
|
margin: 4px;
|
|
34
35
|
width: 1rem;
|
|
35
36
|
height: 1rem;
|
|
37
|
+
|
|
38
|
+
// props.picker: a click opens the color picker. It is a button:
|
|
39
|
+
// wide enough to be aimed at.
|
|
40
|
+
&.picker {
|
|
41
|
+
flex-shrink: 0;
|
|
42
|
+
width: 2.4rem;
|
|
43
|
+
height: 1.4rem;
|
|
44
|
+
border: 1px solid var( --border );
|
|
45
|
+
border-radius: 3px;
|
|
46
|
+
cursor: pointer;
|
|
47
|
+
|
|
48
|
+
&:hover {
|
|
49
|
+
border-color: var( --border-focus );
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// props.nullable, no color: crossed out
|
|
54
|
+
&.none {
|
|
55
|
+
border: 1px solid var( --border );
|
|
56
|
+
background: linear-gradient( to top right, transparent 45%, #d04040 45%, #d04040 55%, transparent 55% );
|
|
57
|
+
}
|
|
36
58
|
}
|
|
37
59
|
|
|
38
60
|
input {
|
|
@@ -42,14 +64,19 @@
|
|
|
42
64
|
flex-grow: 1;
|
|
43
65
|
}
|
|
44
66
|
|
|
67
|
+
// the eye dropper: as high as the field, and wide enough to be aimed at
|
|
45
68
|
.x4button {
|
|
69
|
+
flex-shrink: 0;
|
|
70
|
+
align-self: stretch;
|
|
46
71
|
margin: 0;
|
|
47
|
-
padding: 0;
|
|
72
|
+
padding: 0 6px;
|
|
48
73
|
outline: none;
|
|
49
74
|
background-color: var( --combobox-btn-background );
|
|
50
75
|
color: var( --colorinput-btn-color );
|
|
51
76
|
|
|
52
77
|
#icon {
|
|
78
|
+
width: 1.3em;
|
|
79
|
+
height: 1.3em;
|
|
53
80
|
color: var( --colorinput-btn-color );
|
|
54
81
|
}
|
|
55
82
|
|
|
@@ -62,4 +89,11 @@
|
|
|
62
89
|
//color: var( --color-10 );
|
|
63
90
|
}
|
|
64
91
|
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// the color picker opened from the swatch (props.picker)
|
|
95
|
+
.x4colorinput-popup {
|
|
96
|
+
background-color: var( --colorinput-popup-background );
|
|
97
|
+
border: 1px solid var( --border );
|
|
98
|
+
box-shadow: 0 4px 12px rgba( 0, 0, 0, 0.3 );
|
|
65
99
|
}
|
|
@@ -1,40 +1,70 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
2
|
* ___ ___ __
|
|
3
3
|
* \ \/ / / _
|
|
4
4
|
* \ / /_| |_
|
|
5
|
-
* / \____ _|
|
|
5
|
+
* / \____ _|
|
|
6
6
|
* /__/\__\ |_|
|
|
7
|
-
*
|
|
7
|
+
*
|
|
8
8
|
* @file colorinput.ts
|
|
9
|
-
* @author Etienne Cochard
|
|
10
|
-
*
|
|
9
|
+
* @author Etienne Cochard
|
|
10
|
+
*
|
|
11
11
|
* @copyright (c) 2026 R-libre ingenierie
|
|
12
12
|
*
|
|
13
|
-
* Use of this source code is governed by an MIT-style license
|
|
13
|
+
* Use of this source code is governed by an MIT-style license
|
|
14
14
|
* that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
|
|
15
15
|
**/
|
|
16
16
|
|
|
17
|
-
import { Component } from '../../core/component';
|
|
18
|
-
import {
|
|
17
|
+
import { Component, ComponentEvents } from '../../core/component';
|
|
18
|
+
import { EventCallback } from '../../core/core_events';
|
|
19
|
+
import { isFeatureAvailable, class_ns, isFunction, IComponentInterface, IFormElement } from '../../core/core_tools';
|
|
19
20
|
import { Color } from '../../core/core_colors';
|
|
20
21
|
|
|
21
22
|
import { BoxProps, HBox } from '../boxes/boxes';
|
|
22
23
|
import { Input } from '../input/input';
|
|
23
24
|
import { Button } from '../button/button';
|
|
25
|
+
import { Popup } from '../popup/popup';
|
|
26
|
+
import { ColorPicker, EvColorChange } from '../colorpicker/colorpicker';
|
|
24
27
|
|
|
25
28
|
|
|
26
29
|
import "./colorinput.module.scss"
|
|
27
30
|
import icon from "./crosshairs-simple-sharp-light.svg"
|
|
28
31
|
|
|
29
|
-
//TODO: add swatches
|
|
30
32
|
//TODO: better keyboard handling (selection after cursor)
|
|
31
33
|
|
|
34
|
+
type Swatches = ( Color | string )[];
|
|
35
|
+
|
|
32
36
|
/**
|
|
33
|
-
*
|
|
37
|
+
*
|
|
34
38
|
*/
|
|
35
39
|
|
|
36
|
-
interface ColorInputProps extends BoxProps {
|
|
40
|
+
export interface ColorInputProps extends BoxProps {
|
|
41
|
+
/** The color at start. null: no color is shown. */
|
|
37
42
|
color: Color | string;
|
|
43
|
+
|
|
44
|
+
/** Name of the field: with it, a Form reads and writes the color (as "#rrggbb"). */
|
|
45
|
+
name?: string;
|
|
46
|
+
|
|
47
|
+
/** How the field writes the color. "rgb" by default: rgb(r,g,b). "hex": #rrggbb. */
|
|
48
|
+
format?: "rgb" | "hex";
|
|
49
|
+
|
|
50
|
+
/** A click on the swatch opens a color picker. */
|
|
51
|
+
picker?: boolean;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Colors offered in the picker. A function is called each time the
|
|
55
|
+
* picker opens: the list may change with time.
|
|
56
|
+
*/
|
|
57
|
+
swatches?: Swatches | ( ( ) => Swatches );
|
|
58
|
+
|
|
59
|
+
/** The field may have no color: emptied, or "no color" picked. */
|
|
60
|
+
nullable?: boolean;
|
|
61
|
+
|
|
62
|
+
/** The user changed the color (not fired by setColor). */
|
|
63
|
+
change?: EventCallback<EvColorChange>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface ColorInputEvents extends ComponentEvents {
|
|
67
|
+
change: EvColorChange;
|
|
38
68
|
}
|
|
39
69
|
|
|
40
70
|
/**
|
|
@@ -43,56 +73,196 @@ interface ColorInputProps extends BoxProps {
|
|
|
43
73
|
* --colorinput-btn-background
|
|
44
74
|
* --colorinput-btn-color
|
|
45
75
|
* --colorinput-btn-color-hover
|
|
76
|
+
* --colorinput-popup-background
|
|
46
77
|
* ```
|
|
47
78
|
*/
|
|
48
79
|
|
|
49
80
|
@class_ns( "x4" )
|
|
50
|
-
export class ColorInput extends HBox<ColorInputProps> {
|
|
81
|
+
export class ColorInput extends HBox<ColorInputProps,ColorInputEvents> {
|
|
51
82
|
static "$cls-ns" = "x4";
|
|
52
83
|
|
|
84
|
+
private _color: Color; // null: no color
|
|
85
|
+
private _swatch: Component;
|
|
86
|
+
private _edit: Input;
|
|
87
|
+
private _swatches: Swatches | ( ( ) => Swatches );
|
|
88
|
+
private _popup: Popup; // the color picker, once it was opened
|
|
89
|
+
|
|
53
90
|
constructor( props: ColorInputProps ) {
|
|
54
91
|
super( props );
|
|
55
92
|
|
|
56
|
-
|
|
57
|
-
|
|
93
|
+
this.mapPropEvents( props, "change" );
|
|
94
|
+
this._swatches = props.swatches;
|
|
95
|
+
|
|
96
|
+
if( props.name ) {
|
|
97
|
+
this.setAttribute( "name", props.name );
|
|
98
|
+
}
|
|
58
99
|
|
|
59
100
|
this.setContent( [
|
|
60
|
-
|
|
61
|
-
|
|
101
|
+
this._swatch = new Component( { cls: "swatch" } ),
|
|
102
|
+
this._edit = new Input( { type: "text", value: "", spellcheck: false } ),
|
|
62
103
|
|
|
63
104
|
isFeatureAvailable("eyedropper") ? new Button( { icon: icon, click: ( ) => {
|
|
64
105
|
const eyeDropper = new (window as any).EyeDropper();
|
|
65
106
|
eyeDropper.open( ).then( ( result: any ) => {
|
|
66
|
-
|
|
67
|
-
updateColor( color );
|
|
107
|
+
this._changed( new Color( result.sRGBHex ) );
|
|
68
108
|
}).catch( (_: any ) => {
|
|
69
109
|
/* silence */
|
|
70
110
|
});
|
|
71
111
|
} } ) : null
|
|
72
112
|
])
|
|
73
113
|
|
|
74
|
-
|
|
75
|
-
const txt =
|
|
114
|
+
this._edit.addDOMEvent( "input", ( ) => {
|
|
115
|
+
const txt = this._edit.getValue( );
|
|
116
|
+
|
|
117
|
+
if( this.props.nullable && txt.trim( )=="" ) {
|
|
118
|
+
this._changed( null, false );
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
76
122
|
const clr = new Color( txt );
|
|
77
123
|
if( !clr.isInvalid() ) {
|
|
78
|
-
|
|
79
|
-
updateColor( color );
|
|
124
|
+
this._changed( clr );
|
|
80
125
|
}
|
|
81
126
|
});
|
|
82
127
|
|
|
83
|
-
|
|
84
|
-
swatch
|
|
85
|
-
|
|
128
|
+
if( props.picker ) {
|
|
129
|
+
// A click on the swatch opens the picker, a second one closes it.
|
|
130
|
+
// The picker closes by itself as soon as the button goes down
|
|
131
|
+
// outside of it: whether it was open is to be known before that.
|
|
132
|
+
let wasOpen = false;
|
|
133
|
+
|
|
134
|
+
this._swatch.addClass( "picker" );
|
|
135
|
+
this._swatch.addDOMEvent( "pointerdown", ( ) => { wasOpen = !!this._popup?.isOpen( ); } );
|
|
136
|
+
this._swatch.addDOMEvent( "click", ( ) => {
|
|
137
|
+
if( !wasOpen ) {
|
|
138
|
+
this._openPicker( );
|
|
139
|
+
}
|
|
140
|
+
} );
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this._show( this._parse( props.color ) );
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private _parse( color: Color | string ): Color {
|
|
147
|
+
if( color instanceof Color ) {
|
|
148
|
+
return color;
|
|
86
149
|
}
|
|
87
150
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
151
|
+
// Nothing to show (a color that is not known, or several ones). Whether
|
|
152
|
+
// the user may choose "no color" is another matter: props.nullable.
|
|
153
|
+
if( color===null || color===undefined || color==="" ) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return new Color( color );
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private _format( color: Color ): string {
|
|
161
|
+
return this.props.format=="hex" ? color.toHexString( ) : color.toRgbString( false );
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* show a color: swatch and text
|
|
166
|
+
* @param text false while the user empties the field: the text is left alone
|
|
167
|
+
*/
|
|
168
|
+
|
|
169
|
+
private _show( color: Color, text = true ) {
|
|
170
|
+
this._color = color;
|
|
171
|
+
|
|
172
|
+
this._swatch.setClass( "none", !color );
|
|
173
|
+
this._swatch.setStyleValue( "backgroundColor", color ? color.toRgbString(false) : null );
|
|
174
|
+
|
|
175
|
+
if( text ) {
|
|
176
|
+
this._edit.setValue( color ? this._format( color ) : "" );
|
|
91
177
|
}
|
|
92
|
-
|
|
93
|
-
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* the user changed the color
|
|
182
|
+
*/
|
|
183
|
+
|
|
184
|
+
private _changed( color: Color, text = true ) {
|
|
185
|
+
this._show( color, text );
|
|
186
|
+
this.fire( "change", { color, value: color ? color.toHexString( ) : null } );
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* The color picker, with the swatches, under the field. It closes on
|
|
191
|
+
* a click outside of it.
|
|
192
|
+
*/
|
|
193
|
+
|
|
194
|
+
private _openPicker( ) {
|
|
195
|
+
const swatches = isFunction( this._swatches ) ? this._swatches( ) : this._swatches;
|
|
196
|
+
|
|
197
|
+
// the picker works on its own color: it changes it in place
|
|
198
|
+
const picker = new ColorPicker( {
|
|
199
|
+
color: this._color ? new Color( this._color.toHexString( ) ) : new Color( 255, 255, 255 ),
|
|
200
|
+
swatches,
|
|
201
|
+
nullable: this.props.nullable,
|
|
202
|
+
} );
|
|
203
|
+
|
|
204
|
+
const popup = this._popup = new Popup( { cls: "x4colorinput-popup", autoClose: true, content: picker } );
|
|
205
|
+
|
|
206
|
+
// after the construction: the picker fires a first change when it is built
|
|
207
|
+
picker.on( "change", ev => {
|
|
208
|
+
this._changed( ev.color ? new Color( ev.value ) : null );
|
|
209
|
+
|
|
210
|
+
if( !ev.color ) {
|
|
211
|
+
popup.close( );
|
|
212
|
+
}
|
|
213
|
+
} );
|
|
214
|
+
|
|
215
|
+
popup.displayNear( this.getBoundingRect( ), "top left", "bottom left", { x: 0, y: 6 } );
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* The current color, null when there is none.
|
|
220
|
+
*/
|
|
221
|
+
|
|
222
|
+
getColor( ): Color {
|
|
223
|
+
return this._color;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* The current color as "#rrggbb" ("#rrggbbaa" when it is not opaque),
|
|
228
|
+
* null when there is none.
|
|
229
|
+
*/
|
|
230
|
+
|
|
231
|
+
getValue( ): string {
|
|
232
|
+
return this._color ? this._color.toHexString( ) : null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Shows another color, or none (null). Does not fire `change`.
|
|
237
|
+
*/
|
|
238
|
+
|
|
239
|
+
setColor( color: Color | string ) {
|
|
240
|
+
this._show( this._parse( color ) );
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Changes the colors offered in the picker (see props.swatches).
|
|
245
|
+
*/
|
|
246
|
+
|
|
247
|
+
setSwatches( swatches: Swatches | ( ( ) => Swatches ) ) {
|
|
248
|
+
this._swatches = swatches;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
*
|
|
253
|
+
*/
|
|
254
|
+
|
|
255
|
+
override queryInterface<T extends IComponentInterface>( name: string ): T {
|
|
256
|
+
if( name=="form-element" ) {
|
|
257
|
+
const i: IFormElement = {
|
|
258
|
+
getRawValue: ( ): any => { return this.getValue( ); },
|
|
259
|
+
setRawValue: ( v: any ) => { this.setColor( v ); },
|
|
260
|
+
isValid: ( ) => { return true; }
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
return i as unknown as T;
|
|
94
264
|
}
|
|
95
265
|
|
|
96
|
-
|
|
266
|
+
return super.queryInterface( name );
|
|
97
267
|
}
|
|
98
|
-
}
|
|
268
|
+
}
|
|
@@ -130,4 +130,27 @@
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
+
|
|
134
|
+
// the colors offered under the sliders (props.swatches)
|
|
135
|
+
.swatches {
|
|
136
|
+
flex-wrap: wrap;
|
|
137
|
+
gap: 4px;
|
|
138
|
+
|
|
139
|
+
.item {
|
|
140
|
+
width: 18px;
|
|
141
|
+
height: 18px;
|
|
142
|
+
border: 1px solid var( --border );
|
|
143
|
+
border-radius: 3px;
|
|
144
|
+
cursor: pointer;
|
|
145
|
+
|
|
146
|
+
&:hover {
|
|
147
|
+
border-color: var( --border-focus );
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// no color: crossed out
|
|
151
|
+
&.none {
|
|
152
|
+
background: linear-gradient( to top right, transparent 45%, #d04040 45%, #d04040 55%, transparent 55% );
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
133
156
|
}
|
|
@@ -17,13 +17,19 @@
|
|
|
17
17
|
import { Color, Hsv } from '../../core/core_colors';
|
|
18
18
|
import { Rect, clamp, class_ns, isFeatureAvailable } from '../../core/core_tools';
|
|
19
19
|
|
|
20
|
-
import { Component, ComponentEvent, ComponentEvents, ComponentProps } from '../../core/component';
|
|
20
|
+
import { Component, ComponentEvent, ComponentEvents, ComponentProps, EvChange } from '../../core/component';
|
|
21
21
|
import { Box, BoxProps, HBox, VBox } from '../boxes/boxes';
|
|
22
22
|
|
|
23
23
|
import "./colorpicker.module.scss"
|
|
24
24
|
|
|
25
|
-
interface ColorPickerProps extends ComponentProps {
|
|
25
|
+
export interface ColorPickerProps extends ComponentProps {
|
|
26
26
|
color: string | Color;
|
|
27
|
+
|
|
28
|
+
/** Colors offered under the sliders, to be picked with a click. */
|
|
29
|
+
swatches?: ( string | Color )[];
|
|
30
|
+
|
|
31
|
+
/** Adds a "no color" swatch: picking it fires `change` with a null color. */
|
|
32
|
+
nullable?: boolean;
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
interface HueChangeEvent extends ComponentEvent {
|
|
@@ -127,6 +133,18 @@ export class Saturation extends Box<BoxProps,CommonEvents> {
|
|
|
127
133
|
this.color.setStyleValue( "backgroundColor", base.toRgbString(false) );
|
|
128
134
|
}
|
|
129
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Shows another color: base color and thumb. Does not fire `sat_change`.
|
|
138
|
+
*/
|
|
139
|
+
|
|
140
|
+
setColor( hsv: Hsv ) {
|
|
141
|
+
this.hsv.saturation = hsv.saturation;
|
|
142
|
+
this.hsv.value = hsv.value;
|
|
143
|
+
|
|
144
|
+
this.updateBaseColor( hsv );
|
|
145
|
+
this.updateThumbMarker( );
|
|
146
|
+
}
|
|
147
|
+
|
|
130
148
|
move( sens: string, delta: number ) {
|
|
131
149
|
switch( sens ) {
|
|
132
150
|
case 'saturation': {
|
|
@@ -335,31 +353,41 @@ class AlphaSlider extends Box<BoxProps,CommonEvents> {
|
|
|
335
353
|
*
|
|
336
354
|
*/
|
|
337
355
|
|
|
338
|
-
|
|
356
|
+
/**
|
|
357
|
+
* Fired by ColorPicker and ColorInput when the user changes the color.
|
|
358
|
+
*
|
|
359
|
+
* `value` is the color as text: "#rrggbb", or "#rrggbbaa" when it is not
|
|
360
|
+
* opaque. Both are null when the user picked "no color" (`nullable`).
|
|
361
|
+
*/
|
|
362
|
+
|
|
363
|
+
export interface EvColorChange extends EvChange {
|
|
339
364
|
color: Color;
|
|
365
|
+
readonly value: string;
|
|
340
366
|
}
|
|
341
367
|
|
|
342
368
|
interface ColorPickerChangeEvents extends ComponentEvents {
|
|
343
|
-
change:
|
|
369
|
+
change: EvColorChange
|
|
344
370
|
}
|
|
345
371
|
|
|
346
372
|
/**
|
|
347
|
-
*
|
|
373
|
+
*
|
|
348
374
|
*/
|
|
349
375
|
|
|
350
376
|
@class_ns( "x4" )
|
|
351
377
|
export class ColorPicker extends VBox<ColorPickerProps,ColorPickerChangeEvents> {
|
|
352
378
|
|
|
353
379
|
private _base: Color;
|
|
380
|
+
private _hsv: Hsv;
|
|
354
381
|
private _sat: Saturation;
|
|
355
382
|
private _swatch: Component;
|
|
356
383
|
private _hue: HueSlider;
|
|
357
384
|
private _alpha: AlphaSlider;
|
|
385
|
+
private _swatches: HBox;
|
|
358
386
|
|
|
359
387
|
|
|
360
388
|
constructor( props: ColorPickerProps ) {
|
|
361
389
|
super( props );
|
|
362
|
-
|
|
390
|
+
|
|
363
391
|
if( props.color instanceof Color ) {
|
|
364
392
|
this._base = props.color;
|
|
365
393
|
}
|
|
@@ -367,7 +395,7 @@ export class ColorPicker extends VBox<ColorPickerProps,ColorPickerChangeEvents>
|
|
|
367
395
|
this._base = new Color( props.color );
|
|
368
396
|
}
|
|
369
397
|
|
|
370
|
-
|
|
398
|
+
const hsv = this._hsv = this._base.toHsv( );
|
|
371
399
|
|
|
372
400
|
this.setAttribute( "tabindex", 0 );
|
|
373
401
|
|
|
@@ -385,48 +413,34 @@ export class ColorPicker extends VBox<ColorPickerProps,ColorPickerChangeEvents>
|
|
|
385
413
|
this._swatch = new Component( { cls: "overlay" } ),
|
|
386
414
|
] } )
|
|
387
415
|
]
|
|
388
|
-
})
|
|
416
|
+
}),
|
|
417
|
+
this._swatches = new HBox( { cls: "swatches" } ),
|
|
389
418
|
]);
|
|
390
419
|
|
|
391
420
|
this._sat.on( "sat_change", ( ev ) => {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
this._alpha.updateBaseColor(
|
|
421
|
+
this._hsv.saturation = ev.saturation;
|
|
422
|
+
this._hsv.value = ev.value;
|
|
423
|
+
this._changed( );
|
|
424
|
+
this._alpha.updateBaseColor( this._hsv );
|
|
396
425
|
} );
|
|
397
426
|
|
|
398
427
|
this._hue.on( 'hue_change', ( ev ) => {
|
|
399
|
-
|
|
400
|
-
this._sat.updateBaseColor(
|
|
401
|
-
this._alpha.updateBaseColor(
|
|
402
|
-
|
|
428
|
+
this._hsv.hue = ev.hue;
|
|
429
|
+
this._sat.updateBaseColor( this._hsv );
|
|
430
|
+
this._alpha.updateBaseColor( this._hsv );
|
|
431
|
+
this._changed( );
|
|
403
432
|
} );
|
|
404
433
|
|
|
405
434
|
this._alpha.on( 'alpha_change', ( ev ) => {
|
|
406
|
-
|
|
407
|
-
|
|
435
|
+
this._hsv.alpha = ev.alpha;
|
|
436
|
+
this._changed( );
|
|
408
437
|
} );
|
|
409
438
|
|
|
410
|
-
const updateColor = ( ) => {
|
|
411
|
-
this._base.setHsv( hsv.hue, hsv.saturation, hsv.value, hsv.alpha );
|
|
412
|
-
this._swatch.setStyleValue( "backgroundColor", this._base.toRgbString() );
|
|
413
|
-
this._swatch.setAttribute( "tooltip", this._base.toRgbString() );
|
|
414
|
-
|
|
415
|
-
this.fire( "change", { color: this._base } );
|
|
416
|
-
}
|
|
417
|
-
|
|
418
439
|
if( isFeatureAvailable("eyedropper") ) {
|
|
419
440
|
this._swatch.addDOMEvent( "click", ( e ) => {
|
|
420
441
|
const eyeDropper = new (window as any).EyeDropper();
|
|
421
442
|
eyeDropper.open( ).then( ( result: any ) => {
|
|
422
|
-
|
|
423
|
-
hsv = color.toHsv( );
|
|
424
|
-
|
|
425
|
-
this._alpha.setColor( hsv );
|
|
426
|
-
|
|
427
|
-
this._sat.updateBaseColor( hsv );
|
|
428
|
-
this._hue.updateHue( hsv );
|
|
429
|
-
updateColor( );
|
|
443
|
+
this._pick( new Color( result.sRGBHex ) );
|
|
430
444
|
}).catch( (_: any )=> {
|
|
431
445
|
/* silence */
|
|
432
446
|
} );
|
|
@@ -435,7 +449,107 @@ export class ColorPicker extends VBox<ColorPickerProps,ColorPickerChangeEvents>
|
|
|
435
449
|
|
|
436
450
|
this.addDOMEvent( "keydown", ( ev ) => this._onkey( ev ) );
|
|
437
451
|
|
|
438
|
-
|
|
452
|
+
this.setSwatches( props.swatches );
|
|
453
|
+
this._changed( );
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* the sliders moved: compute the color, show it, tell it
|
|
458
|
+
*/
|
|
459
|
+
|
|
460
|
+
private _changed( ) {
|
|
461
|
+
const hsv = this._hsv;
|
|
462
|
+
|
|
463
|
+
this._base.setHsv( hsv.hue, hsv.saturation, hsv.value, hsv.alpha );
|
|
464
|
+
this._refresh( );
|
|
465
|
+
this._notify( );
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
private _refresh( ) {
|
|
469
|
+
this._swatch.setStyleValue( "backgroundColor", this._base.toRgbString() );
|
|
470
|
+
this._swatch.setAttribute( "tooltip", this._base.toRgbString() );
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
private _notify( ) {
|
|
474
|
+
this.fire( "change", { color: this._base, value: this._base.toHexString() } );
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* the user took a color as a whole (eye dropper, swatch)
|
|
479
|
+
*/
|
|
480
|
+
|
|
481
|
+
private _pick( color: Color ) {
|
|
482
|
+
this.setColor( color );
|
|
483
|
+
this._notify( );
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* The current color. It is the object the `change` event gives, and it
|
|
488
|
+
* changes with the picker: copy it to keep a value.
|
|
489
|
+
*/
|
|
490
|
+
|
|
491
|
+
getColor( ): Color {
|
|
492
|
+
return this._base;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Shows another color. Does not fire `change`.
|
|
497
|
+
*/
|
|
498
|
+
|
|
499
|
+
setColor( color: Color | string ) {
|
|
500
|
+
const rgb = ( color instanceof Color ? color : new Color( color ) ).toRgb( );
|
|
501
|
+
|
|
502
|
+
// the color itself is kept as given: going through hsv could shift it
|
|
503
|
+
this._base.setRgb( rgb.red, rgb.green, rgb.blue, rgb.alpha );
|
|
504
|
+
|
|
505
|
+
const hsv = this._base.toHsv( );
|
|
506
|
+
this._hsv.hue = hsv.hue;
|
|
507
|
+
this._hsv.saturation = hsv.saturation;
|
|
508
|
+
this._hsv.value = hsv.value;
|
|
509
|
+
this._hsv.alpha = hsv.alpha;
|
|
510
|
+
|
|
511
|
+
this._sat.setColor( this._hsv );
|
|
512
|
+
this._hue.updateHue( this._hsv );
|
|
513
|
+
this._alpha.setColor( this._hsv );
|
|
514
|
+
|
|
515
|
+
this._refresh( );
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Colors offered under the sliders. A click on one of them takes it
|
|
520
|
+
* and fires `change`. Nothing is shown without swatches.
|
|
521
|
+
*/
|
|
522
|
+
|
|
523
|
+
setSwatches( swatches: ( string | Color )[] ) {
|
|
524
|
+
const items: Component[] = [];
|
|
525
|
+
|
|
526
|
+
if( this.props.nullable ) {
|
|
527
|
+
items.push( new Component( {
|
|
528
|
+
cls: "item none",
|
|
529
|
+
dom_events: {
|
|
530
|
+
click: ( ) => this.fire( "change", { color: null, value: null } ),
|
|
531
|
+
},
|
|
532
|
+
} ) );
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
for( const swatch of swatches ?? [] ) {
|
|
536
|
+
const color = swatch instanceof Color ? swatch : new Color( swatch );
|
|
537
|
+
if( color.isInvalid( ) ) {
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
items.push( new Component( {
|
|
542
|
+
cls: "item",
|
|
543
|
+
tooltip: color.toHexString( ),
|
|
544
|
+
style: { backgroundColor: color.toRgbString( ) },
|
|
545
|
+
dom_events: {
|
|
546
|
+
click: ( ) => this._pick( color ),
|
|
547
|
+
},
|
|
548
|
+
} ) );
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
this._swatches.setContent( items );
|
|
552
|
+
this._swatches.show( items.length>0 );
|
|
439
553
|
}
|
|
440
554
|
|
|
441
555
|
private _onkey( ev: KeyboardEvent ) {
|
|
@@ -12,8 +12,12 @@
|
|
|
12
12
|
--propertygrid-odd-background: var(--color-primary-a5);
|
|
13
13
|
--propertygrid-even-background: transparent;
|
|
14
14
|
--propertygrid-disabled-background: rgb(170 170 170 / 14%);
|
|
15
|
+
--propertygrid-hover-background: var(--color-primary-a10);
|
|
16
|
+
--propertygrid-focus-background: var(--color-primary-a20);
|
|
17
|
+
--propertygrid-input-background: rgba(255,255,255,0.6);
|
|
15
18
|
|
|
16
19
|
--propertygrid-group-background: var( --color-primary-a5 );
|
|
20
|
+
--propertygrid-group-color: var( --color-primary-a50 );
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
.x4propertygrid {
|
|
@@ -36,19 +40,23 @@
|
|
|
36
40
|
margin: 0;
|
|
37
41
|
}
|
|
38
42
|
|
|
43
|
+
// Two columns shared by all the rows: the titles, as wide as the longest
|
|
44
|
+
// of them but never more than half of the grid, and the editors, which
|
|
45
|
+
// take the rest.
|
|
39
46
|
.root {
|
|
40
47
|
display: grid;
|
|
41
|
-
grid-template-columns: 1fr;
|
|
48
|
+
grid-template-columns: fit-content( 50% ) 1fr;
|
|
42
49
|
grid-template-rows: auto;
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
.group {
|
|
53
|
+
grid-column: 1 / -1;
|
|
46
54
|
padding: 6px 4px 4px 4px;
|
|
47
55
|
min-height: 2em;
|
|
48
56
|
background-color: var( --propertygrid-group-background );
|
|
49
57
|
margin-bottom: 8px;
|
|
50
58
|
box-shadow: 4px 3px 8px rgba(0, 0, 0, 0.4);
|
|
51
|
-
color: var(--
|
|
59
|
+
color: var( --propertygrid-group-color );
|
|
52
60
|
display: flex;
|
|
53
61
|
align-items: center;
|
|
54
62
|
padding: 8px;
|
|
@@ -75,14 +83,14 @@
|
|
|
75
83
|
|
|
76
84
|
&>.x4button {
|
|
77
85
|
background-color: transparent;
|
|
78
|
-
color: var(--
|
|
86
|
+
color: var( --propertygrid-group-color );
|
|
79
87
|
}
|
|
80
|
-
|
|
88
|
+
|
|
81
89
|
&>.x4label {
|
|
82
90
|
.x4icon {
|
|
83
91
|
.fa-secondary,
|
|
84
92
|
.fa-primary {
|
|
85
|
-
fill: var( --
|
|
93
|
+
fill: var( --propertygrid-group-color );
|
|
86
94
|
}
|
|
87
95
|
|
|
88
96
|
width: 1em;
|
|
@@ -103,52 +111,62 @@
|
|
|
103
111
|
}
|
|
104
112
|
|
|
105
113
|
&:not( :last-child ) {
|
|
106
|
-
border-bottom: 1px solid var(--
|
|
114
|
+
border-bottom: 1px solid var( --propertygrid-group-color );
|
|
107
115
|
//box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.2);
|
|
108
116
|
}
|
|
109
117
|
}
|
|
110
118
|
|
|
119
|
+
// A row has no box of its own: its two cells go straight on the columns
|
|
120
|
+
// of the grid, which is how all the titles get the same width. (A row is
|
|
121
|
+
// a fieldset: it cannot be a subgrid.) What a row looked like, background
|
|
122
|
+
// and bottom line, is carried by its cells.
|
|
111
123
|
.row {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
124
|
+
display: contents;
|
|
125
|
+
|
|
126
|
+
> .cell {
|
|
127
|
+
min-width: 0;
|
|
128
|
+
padding: 4px;
|
|
129
|
+
border-bottom: 1px solid var( --border-light );
|
|
130
|
+
|
|
131
|
+
input {
|
|
132
|
+
background: var( --propertygrid-input-background );
|
|
133
|
+
}
|
|
134
|
+
}
|
|
116
135
|
|
|
117
|
-
&.even:not(:disabled) {
|
|
136
|
+
&.even:not(:disabled) > .cell {
|
|
118
137
|
background-color: var(--propertygrid-even-background)
|
|
119
138
|
}
|
|
120
|
-
|
|
121
|
-
&.odd:not(:disabled) {
|
|
139
|
+
|
|
140
|
+
&.odd:not(:disabled) > .cell {
|
|
122
141
|
background-color: var(--propertygrid-odd-background)
|
|
123
142
|
}
|
|
124
|
-
|
|
125
|
-
&.odd:disabled {
|
|
143
|
+
|
|
144
|
+
&.odd:disabled > .cell {
|
|
126
145
|
background-color: var(--propertygrid-disabled-background)
|
|
127
146
|
}
|
|
128
|
-
|
|
129
|
-
&:has(:focus) {
|
|
130
|
-
background-color: var(--color-primary-a20);
|
|
131
|
-
}
|
|
132
147
|
|
|
133
|
-
&:
|
|
134
|
-
background-color: var(--
|
|
148
|
+
&:has(:focus) > .cell {
|
|
149
|
+
background-color: var( --propertygrid-focus-background );
|
|
135
150
|
}
|
|
136
|
-
|
|
137
|
-
.cell {
|
|
138
|
-
flex-grow: 1;
|
|
139
|
-
flex-basis: 10px;
|
|
140
|
-
padding: 4px;
|
|
141
151
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
152
|
+
&:hover > .cell {
|
|
153
|
+
background-color: var( --propertygrid-hover-background );
|
|
145
154
|
}
|
|
146
155
|
|
|
147
|
-
.cell:first-child {
|
|
156
|
+
> .cell:first-child {
|
|
148
157
|
border-right: 1px solid var( --border );
|
|
149
158
|
}
|
|
150
159
|
|
|
151
|
-
|
|
160
|
+
// a row without title (a button): its editor takes the two columns
|
|
161
|
+
> .cell:only-child {
|
|
162
|
+
grid-column: 1 / -1;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// The title, centered in the height of the row. Wider than half of
|
|
166
|
+
// the grid, it is cut by the column and goes on several lines.
|
|
167
|
+
> .hdr {
|
|
168
|
+
display: flex;
|
|
169
|
+
align-items: center;
|
|
152
170
|
padding: 8px;
|
|
153
171
|
padding-left: 34px;
|
|
154
172
|
}
|
|
@@ -23,6 +23,8 @@ import { ListboxID, ListItem } from "../listbox/listbox"
|
|
|
23
23
|
import { Label, SimpleText } from "../label/label"
|
|
24
24
|
import { asap, class_ns, isFunction, isNumber } from '../../core/core_tools';
|
|
25
25
|
import { Icon, ScrollView } from '../components'
|
|
26
|
+
import { ColorInput } from '../colorinput/colorinput'
|
|
27
|
+
import { EvColorChange } from '../colorpicker/colorpicker'
|
|
26
28
|
|
|
27
29
|
import icons from "../assets/icons"
|
|
28
30
|
|
|
@@ -32,12 +34,14 @@ type IValue = boolean | number | string;
|
|
|
32
34
|
type IValueCB = ( name: string) => IValue;
|
|
33
35
|
|
|
34
36
|
export interface PropertyValue {
|
|
35
|
-
type: 'boolean' | 'number' | 'string' | 'password' | 'options' | 'label' | 'button';
|
|
37
|
+
type: 'boolean' | 'number' | 'string' | 'password' | 'options' | 'color' | 'label' | 'button';
|
|
36
38
|
title?: string;
|
|
37
39
|
desc?: string;
|
|
38
40
|
name?: string;
|
|
39
41
|
value: IValue | IValueCB;
|
|
40
42
|
options?: ListItem[];
|
|
43
|
+
swatches?: string[] | ( ( ) => string[] ); // for colors: offered in the picker; a function is called when it opens
|
|
44
|
+
nullable?: boolean; // for colors: "no color" can be chosen, the value is then null
|
|
41
45
|
callback?: ( name: string, value: any ) => void;
|
|
42
46
|
live?: boolean; // for live update
|
|
43
47
|
cls?: string;
|
|
@@ -67,6 +71,16 @@ export interface PropertyProps extends ComponentProps {
|
|
|
67
71
|
* @cssvar
|
|
68
72
|
* ```
|
|
69
73
|
* --propertygrid-background
|
|
74
|
+
* --propertygrid-odd-background
|
|
75
|
+
* --propertygrid-even-background
|
|
76
|
+
* --propertygrid-disabled-background
|
|
77
|
+
* --propertygrid-hover-background
|
|
78
|
+
* --propertygrid-focus-background
|
|
79
|
+
* --propertygrid-input-background
|
|
80
|
+
* --propertygrid-group-background
|
|
81
|
+
* --propertygrid-group-color
|
|
82
|
+
* --propertygrid-gadget-background
|
|
83
|
+
* --propertygrid-gadget-color
|
|
70
84
|
* ```
|
|
71
85
|
*/
|
|
72
86
|
|
|
@@ -260,10 +274,23 @@ export class PropertyGrid extends VBox {
|
|
|
260
274
|
}
|
|
261
275
|
});
|
|
262
276
|
}
|
|
277
|
+
else if (item.type === 'color') {
|
|
278
|
+
editor = new ColorInput({
|
|
279
|
+
id: item.name,
|
|
280
|
+
color: value as string,
|
|
281
|
+
format: "hex",
|
|
282
|
+
picker: true,
|
|
283
|
+
swatches: item.swatches,
|
|
284
|
+
nullable: item.nullable,
|
|
285
|
+
change: ( e: EvColorChange ) => {
|
|
286
|
+
item.callback?.( item.name, e.value );
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
}
|
|
263
290
|
else if (item.type === 'label') {
|
|
264
|
-
editor = new Label({
|
|
291
|
+
editor = new Label({
|
|
265
292
|
id: item.name,
|
|
266
|
-
text: value as string,
|
|
293
|
+
text: value as string,
|
|
267
294
|
});
|
|
268
295
|
}
|
|
269
296
|
else if (item.type === 'button') {
|
|
@@ -347,6 +374,12 @@ export class PropertyGrid extends VBox {
|
|
|
347
374
|
editor.setNumValue( value as number, -2 );
|
|
348
375
|
}
|
|
349
376
|
}
|
|
377
|
+
else if (item.type === 'color') {
|
|
378
|
+
const editor = this.root.query<ColorInput>( '#'+item.name );
|
|
379
|
+
if( editor ) {
|
|
380
|
+
editor.setColor( value as string );
|
|
381
|
+
}
|
|
382
|
+
}
|
|
350
383
|
else if (item.type === 'label') {
|
|
351
384
|
const label = this.root.query<Label>( '#'+item.name );
|
|
352
385
|
if( label ) {
|
package/src/core/component.ts
CHANGED
|
@@ -70,6 +70,15 @@ export const makeUniqueComponentId = ( ) => {
|
|
|
70
70
|
return `x4-${gen_id++}`;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* CSS properties. A number gets "px", except for the unitless properties
|
|
75
|
+
* (opacity, lineHeight, flexGrow...).
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
export type StyleProps = {
|
|
79
|
+
[K in keyof CSSStyleDeclaration]?: CSSStyleDeclaration[K] | number;
|
|
80
|
+
};
|
|
81
|
+
|
|
73
82
|
/**
|
|
74
83
|
* Base properties for all components.
|
|
75
84
|
*/
|
|
@@ -80,7 +89,7 @@ export interface ComponentProps {
|
|
|
80
89
|
/** Namespace for SVG/MathML elements. */
|
|
81
90
|
ns?: string;
|
|
82
91
|
/** Inline CSS styles. */
|
|
83
|
-
style?:
|
|
92
|
+
style?: StyleProps;
|
|
84
93
|
/** HTML attributes. */
|
|
85
94
|
attrs?: Record<string,string|number|boolean>;
|
|
86
95
|
/** Child content (components, strings, or HTML). */
|
|
@@ -685,17 +694,17 @@ export class Component<P extends ComponentProps = ComponentProps, E extends Comp
|
|
|
685
694
|
* @returns The component instance for chaining.
|
|
686
695
|
*/
|
|
687
696
|
|
|
688
|
-
setStyle( style:
|
|
697
|
+
setStyle( style: StyleProps ): this {
|
|
689
698
|
const _style = (this.dom as HTMLElement).style;
|
|
690
699
|
|
|
691
700
|
for( const name in style ) {
|
|
692
|
-
|
|
693
|
-
let value = style[name];
|
|
701
|
+
|
|
702
|
+
let value: any = style[name];
|
|
694
703
|
if( !unitless[name] && (isNumber(value) || RE_NUMBER.test(value)) ) {
|
|
695
704
|
value += "px";
|
|
696
705
|
}
|
|
697
706
|
|
|
698
|
-
_style[name] = value;
|
|
707
|
+
(_style as any)[name] = value;
|
|
699
708
|
}
|
|
700
709
|
|
|
701
710
|
return this;
|