x4js 2.3.13 → 2.3.15
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 +50 -13
- package/package.json +1 -1
- package/src/components/colorinput/colorinput.module.scss +36 -2
- package/src/components/colorinput/colorinput.ts +205 -32
- package/src/components/colorpicker/colorpicker.module.scss +23 -0
- package/src/components/colorpicker/colorpicker.ts +149 -35
- package/src/components/popup/popup.ts +7 -6
- package/src/components/propgrid/progrid.module.scss +49 -31
- package/src/components/propgrid/propgrid.ts +36 -3
- package/src/core/component.ts +52 -6
- package/src/core/core_application.ts +33 -0
- package/src/core/core_shortcuts.ts +181 -0
- package/src/core/core_state.ts +7 -3
- package/src/core/core_tools.ts +1 -1
- package/src/x4.d.ts +1 -0
- package/src/x4.ts +1 -0
package/aicontext.md
CHANGED
|
@@ -46,10 +46,11 @@ CoreElement<E> named timers + event surface (no DOM)
|
|
|
46
46
|
|
|
47
47
|
EventSource<E> standalone event registry
|
|
48
48
|
├── DataStore<T>
|
|
49
|
-
|
|
49
|
+
├── Router
|
|
50
|
+
└── StateManager<T>
|
|
50
51
|
|
|
51
52
|
CoreElement-based (non-DOM): DataProxy, DataView, Store
|
|
52
|
-
Standalone: Rect, Timer, Color, DataModel,
|
|
53
|
+
Standalone: Rect, Timer, Color, DataModel, Stylesheet, ComputedStyle,
|
|
53
54
|
SvgBuilder, X4PDFBuilder, x4_react, CMover, UnsafeHtml (extends String)
|
|
54
55
|
```
|
|
55
56
|
|
|
@@ -130,7 +131,7 @@ interface ComponentProps {
|
|
|
130
131
|
ns?: string // SVG/MathML namespace
|
|
131
132
|
cls?: string // extra CSS classes
|
|
132
133
|
id?: string
|
|
133
|
-
style?:
|
|
134
|
+
style?: StyleProps // numbers get "px" except unitless
|
|
134
135
|
attrs?: Record<string, string | number | boolean>
|
|
135
136
|
content?: ComponentContent
|
|
136
137
|
dom_events?: GlobalDOMEvents
|
|
@@ -188,7 +189,7 @@ getInternalData<T>(name: string|symbol): T
|
|
|
188
189
|
### Styles
|
|
189
190
|
|
|
190
191
|
```ts
|
|
191
|
-
setStyle(style:
|
|
192
|
+
setStyle(style: StyleProps): this // numbers auto-suffixed "px" except unitless
|
|
192
193
|
setStyleValue<K>(name: K, value): this
|
|
193
194
|
getStyleValue<K>(name: K): CSSStyleDeclaration[K]
|
|
194
195
|
setWidth(w: number|string): void
|
|
@@ -633,7 +634,20 @@ class AutoComplete extends TextEdit {
|
|
|
633
634
|
### `ColorInput`
|
|
634
635
|
|
|
635
636
|
```ts
|
|
636
|
-
|
|
637
|
+
interface ColorInputProps extends BoxProps {
|
|
638
|
+
color: Color | string // null: no color shown
|
|
639
|
+
name?: string
|
|
640
|
+
format?: "rgb" | "hex" // text of the field, "rgb" by default
|
|
641
|
+
picker?: boolean // a click on the swatch opens a ColorPicker
|
|
642
|
+
swatches?: (Color|string)[] | (() => (Color|string)[]) // a function is called each time the picker opens
|
|
643
|
+
nullable?: boolean // "no color" can be chosen
|
|
644
|
+
change?: EventCallback<EvColorChange> // user changes only, not setColor
|
|
645
|
+
}
|
|
646
|
+
class ColorInput extends HBox { // queryInterface("form-element"): "#rrggbb" or null
|
|
647
|
+
getColor(): Color; getValue(): string
|
|
648
|
+
setColor(color: Color | string): void; setSwatches(swatches): void
|
|
649
|
+
}
|
|
650
|
+
interface EvColorChange extends EvChange { color: Color; value: string } // "#rrggbb" or "#rrggbbaa"; both null for "no color"
|
|
637
651
|
```
|
|
638
652
|
|
|
639
653
|
### `FileDrop` / `FileDialog`
|
|
@@ -796,6 +810,9 @@ class PropertyGrid extends VBox {
|
|
|
796
810
|
setItems(groups: PropertyGroup[]): void
|
|
797
811
|
setPropValue(name: string, value): void
|
|
798
812
|
}
|
|
813
|
+
// PropertyValue.type: 'boolean' | 'number' | 'string' | 'password' | 'options' | 'color' | 'label' | 'button'
|
|
814
|
+
// 'color': the editor is a ColorInput (hex, with picker); swatches?: string[] | (() => string[]), nullable?: boolean,
|
|
815
|
+
// callback(name, value) receives "#rrggbb", or null for "no color"
|
|
799
816
|
```
|
|
800
817
|
|
|
801
818
|
---
|
|
@@ -978,7 +995,9 @@ class Video extends Box {
|
|
|
978
995
|
set stream(stream: MediaStream)
|
|
979
996
|
}
|
|
980
997
|
class Calendar extends VBox { getDate(): Date; setDate(date: Date): void }
|
|
981
|
-
class ColorPicker extends VBox
|
|
998
|
+
class ColorPicker extends VBox { // props: color, swatches?, nullable?; "change": EvColorChange, also fired once when built
|
|
999
|
+
getColor(): Color; setColor(color: Color | string): void; setSwatches(swatches): void
|
|
1000
|
+
}
|
|
982
1001
|
class Panel extends VBox { setTitle(title: string): void }
|
|
983
1002
|
class Breadcrumbs extends HBox { setItems(elements): void }
|
|
984
1003
|
class Keyboard extends HBox // virtual keyboard
|
|
@@ -1259,17 +1278,36 @@ initTooltips(): void // initialize the global
|
|
|
1259
1278
|
|
|
1260
1279
|
---
|
|
1261
1280
|
|
|
1262
|
-
## State (`core_state`)
|
|
1281
|
+
## State (`core_state`)
|
|
1263
1282
|
|
|
1264
1283
|
```ts
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1284
|
+
makeState<T>(initialState: T): StateProxy<T> // the data itself + on / off / once / watch
|
|
1285
|
+
|
|
1286
|
+
const state = makeState({ count: 0, user: { name: '' }, items: [1, 2, 3] })
|
|
1287
|
+
state.on('change', e => ...) // EvStateChange { path, value }
|
|
1288
|
+
state.watch('user', e => ...) // 'user', anything below it, or a parent replaced; returns { off() }
|
|
1289
|
+
state.count++ // path "count"
|
|
1290
|
+
state.user.name = 'x' // path "user.name"
|
|
1291
|
+
state.items.push(4) // path "items[3]"
|
|
1292
|
+
|
|
1293
|
+
class StateManager<T> extends EventSource<StateEvents> {
|
|
1294
|
+
constructor(initialState: T)
|
|
1295
|
+
proxify(): StateProxy<T>
|
|
1296
|
+
on(name, listener): { off(): void }
|
|
1297
|
+
off(name, listener): void
|
|
1298
|
+
once(name, listener): { off(): void }
|
|
1299
|
+
watch(path: string, cb: (ev: EvStateChange) => void): { off(): void }
|
|
1269
1300
|
}
|
|
1270
|
-
|
|
1301
|
+
|
|
1302
|
+
// Component (protected): a state kept in localStorage under "x4@persist:<name>",
|
|
1303
|
+
// saved 500 ms after a change
|
|
1304
|
+
loadPState(name: string, defaults: Record<string, any>): any
|
|
1271
1305
|
```
|
|
1272
1306
|
|
|
1307
|
+
- Plain objects and arrays are proxied when read; Date, Map, Set and class instances are not.
|
|
1308
|
+
- Setting a property to the value it already has fires nothing.
|
|
1309
|
+
- Reading a property that does not exist logs an error and returns `undefined`.
|
|
1310
|
+
|
|
1273
1311
|
---
|
|
1274
1312
|
|
|
1275
1313
|
## Common patterns
|
|
@@ -1469,7 +1507,6 @@ Optional keys: `external`, `define`, `esbuild`, `dev` (`host`, `port`, `https`,
|
|
|
1469
1507
|
- The default language is `fr`. Call `selectLanguage('en')` to switch to English.
|
|
1470
1508
|
- `Gridview` is backed by a `DataStore`; `Spreadsheet` is backed by a `Store` (different, cell-based).
|
|
1471
1509
|
- `DataRecordID` is currently typed `any`; `strictNullChecks` is intentionally off in the recommended `tsconfig.json`.
|
|
1472
|
-
- `StateManager` (`core_state`) is marked **Experimental** — verify before relying on it.
|
|
1473
1510
|
- `MonacoEditor` requires `MonacoEditor.start()` (async) before use; register extra typings with `MonacoEditor.addTypelib(name, code)`.
|
|
1474
1511
|
- `x4_react.create_element` is the JSX pragma;
|
|
1475
1512
|
- `unbubbleEvents` is the set of DOM events that do not bubble through the x4 component tree.
|
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,199 @@ 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
|
+
private _closedAt = -1; // when it closed for the last time (performance.now)
|
|
90
|
+
|
|
53
91
|
constructor( props: ColorInputProps ) {
|
|
54
92
|
super( props );
|
|
55
93
|
|
|
56
|
-
|
|
57
|
-
|
|
94
|
+
this.mapPropEvents( props, "change" );
|
|
95
|
+
this._swatches = props.swatches;
|
|
96
|
+
|
|
97
|
+
if( props.name ) {
|
|
98
|
+
this.setAttribute( "name", props.name );
|
|
99
|
+
}
|
|
58
100
|
|
|
59
101
|
this.setContent( [
|
|
60
|
-
|
|
61
|
-
|
|
102
|
+
this._swatch = new Component( { cls: "swatch" } ),
|
|
103
|
+
this._edit = new Input( { type: "text", value: "", spellcheck: false } ),
|
|
62
104
|
|
|
63
105
|
isFeatureAvailable("eyedropper") ? new Button( { icon: icon, click: ( ) => {
|
|
64
106
|
const eyeDropper = new (window as any).EyeDropper();
|
|
65
107
|
eyeDropper.open( ).then( ( result: any ) => {
|
|
66
|
-
|
|
67
|
-
updateColor( color );
|
|
108
|
+
this._changed( new Color( result.sRGBHex ) );
|
|
68
109
|
}).catch( (_: any ) => {
|
|
69
110
|
/* silence */
|
|
70
111
|
});
|
|
71
112
|
} } ) : null
|
|
72
113
|
])
|
|
73
114
|
|
|
74
|
-
|
|
75
|
-
const txt =
|
|
115
|
+
this._edit.addDOMEvent( "input", ( ) => {
|
|
116
|
+
const txt = this._edit.getValue( );
|
|
117
|
+
|
|
118
|
+
if( this.props.nullable && txt.trim( )=="" ) {
|
|
119
|
+
this._changed( null, false );
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
76
123
|
const clr = new Color( txt );
|
|
77
124
|
if( !clr.isInvalid() ) {
|
|
78
|
-
|
|
79
|
-
updateColor( color );
|
|
125
|
+
this._changed( clr );
|
|
80
126
|
}
|
|
81
127
|
});
|
|
82
128
|
|
|
83
|
-
|
|
84
|
-
swatch
|
|
85
|
-
|
|
129
|
+
if( props.picker ) {
|
|
130
|
+
// A click on the swatch opens the picker, a second one closes it.
|
|
131
|
+
// The picker closes by itself as soon as the button goes down
|
|
132
|
+
// outside of it, before the swatch hears of it: it was open if
|
|
133
|
+
// it closed since this very press began.
|
|
134
|
+
let wasOpen = false;
|
|
135
|
+
|
|
136
|
+
this._swatch.addClass( "picker" );
|
|
137
|
+
this._swatch.addDOMEvent( "pointerdown", ev => { wasOpen = this._closedAt >= ev.timeStamp; } );
|
|
138
|
+
this._swatch.addDOMEvent( "click", ( ) => {
|
|
139
|
+
if( !wasOpen ) {
|
|
140
|
+
this._openPicker( );
|
|
141
|
+
}
|
|
142
|
+
} );
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
this._show( this._parse( props.color ) );
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private _parse( color: Color | string ): Color {
|
|
149
|
+
if( color instanceof Color ) {
|
|
150
|
+
return color;
|
|
86
151
|
}
|
|
87
152
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
153
|
+
// Nothing to show (a color that is not known, or several ones). Whether
|
|
154
|
+
// the user may choose "no color" is another matter: props.nullable.
|
|
155
|
+
if( color===null || color===undefined || color==="" ) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return new Color( color );
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private _format( color: Color ): string {
|
|
163
|
+
return this.props.format=="hex" ? color.toHexString( ) : color.toRgbString( false );
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* show a color: swatch and text
|
|
168
|
+
* @param text false while the user empties the field: the text is left alone
|
|
169
|
+
*/
|
|
170
|
+
|
|
171
|
+
private _show( color: Color, text = true ) {
|
|
172
|
+
this._color = color;
|
|
173
|
+
|
|
174
|
+
this._swatch.setClass( "none", !color );
|
|
175
|
+
this._swatch.setStyleValue( "backgroundColor", color ? color.toRgbString(false) : null );
|
|
176
|
+
|
|
177
|
+
if( text ) {
|
|
178
|
+
this._edit.setValue( color ? this._format( color ) : "" );
|
|
91
179
|
}
|
|
92
|
-
|
|
93
|
-
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* the user changed the color
|
|
184
|
+
*/
|
|
185
|
+
|
|
186
|
+
private _changed( color: Color, text = true ) {
|
|
187
|
+
this._show( color, text );
|
|
188
|
+
this.fire( "change", { color, value: color ? color.toHexString( ) : null } );
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* The color picker, with the swatches, under the field. It closes on
|
|
193
|
+
* a click outside of it.
|
|
194
|
+
*/
|
|
195
|
+
|
|
196
|
+
private _openPicker( ) {
|
|
197
|
+
const swatches = isFunction( this._swatches ) ? this._swatches( ) : this._swatches;
|
|
198
|
+
|
|
199
|
+
// the picker works on its own color: it changes it in place
|
|
200
|
+
const picker = new ColorPicker( {
|
|
201
|
+
color: this._color ? new Color( this._color.toHexString( ) ) : new Color( 255, 255, 255 ),
|
|
202
|
+
swatches,
|
|
203
|
+
nullable: this.props.nullable,
|
|
204
|
+
} );
|
|
205
|
+
|
|
206
|
+
const popup = this._popup = new Popup( { cls: "x4colorinput-popup", autoClose: true, content: picker } );
|
|
207
|
+
popup.on( "closed", ( ) => { this._closedAt = performance.now( ); } );
|
|
208
|
+
|
|
209
|
+
// after the construction: the picker fires a first change when it is built
|
|
210
|
+
picker.on( "change", ev => {
|
|
211
|
+
this._changed( ev.color ? new Color( ev.value ) : null );
|
|
212
|
+
|
|
213
|
+
if( !ev.color ) {
|
|
214
|
+
popup.close( );
|
|
215
|
+
}
|
|
216
|
+
} );
|
|
217
|
+
|
|
218
|
+
popup.displayNear( this.getBoundingRect( ), "top left", "bottom left", { x: 0, y: 6 } );
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* The current color, null when there is none.
|
|
223
|
+
*/
|
|
224
|
+
|
|
225
|
+
getColor( ): Color {
|
|
226
|
+
return this._color;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* The current color as "#rrggbb" ("#rrggbbaa" when it is not opaque),
|
|
231
|
+
* null when there is none.
|
|
232
|
+
*/
|
|
233
|
+
|
|
234
|
+
getValue( ): string {
|
|
235
|
+
return this._color ? this._color.toHexString( ) : null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Shows another color, or none (null). Does not fire `change`.
|
|
240
|
+
*/
|
|
241
|
+
|
|
242
|
+
setColor( color: Color | string ) {
|
|
243
|
+
this._show( this._parse( color ) );
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Changes the colors offered in the picker (see props.swatches).
|
|
248
|
+
*/
|
|
249
|
+
|
|
250
|
+
setSwatches( swatches: Swatches | ( ( ) => Swatches ) ) {
|
|
251
|
+
this._swatches = swatches;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
*
|
|
256
|
+
*/
|
|
257
|
+
|
|
258
|
+
override queryInterface<T extends IComponentInterface>( name: string ): T {
|
|
259
|
+
if( name=="form-element" ) {
|
|
260
|
+
const i: IFormElement = {
|
|
261
|
+
getRawValue: ( ): any => { return this.getValue( ); },
|
|
262
|
+
setRawValue: ( v: any ) => { this.setColor( v ); },
|
|
263
|
+
isValid: ( ) => { return true; }
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
return i as unknown as T;
|
|
94
267
|
}
|
|
95
268
|
|
|
96
|
-
|
|
269
|
+
return super.queryInterface( name );
|
|
97
270
|
}
|
|
98
|
-
}
|
|
271
|
+
}
|
|
@@ -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
|
}
|