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
package/src/core/core_router.ts
CHANGED
|
@@ -92,6 +92,23 @@ export function parseRoute(str: string | RegExp, loose = false): Segment {
|
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* the parameters of the url are encoded (%C3%A9): decoded; a lone % is left as it is
|
|
97
|
+
*/
|
|
98
|
+
|
|
99
|
+
function decode( s: string ) {
|
|
100
|
+
if( s===undefined ) {
|
|
101
|
+
return s; // an optional parameter that is not there
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
return decodeURIComponent( s );
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return s;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
95
112
|
/**
|
|
96
113
|
* Defines the events that the `Router` can emit.
|
|
97
114
|
*/
|
|
@@ -144,15 +161,13 @@ export class Router extends EventSource< RouterEvents > {
|
|
|
144
161
|
const url = this._getLocation( );
|
|
145
162
|
const found = this._find(url);
|
|
146
163
|
|
|
147
|
-
if( found
|
|
164
|
+
if( !found ) {
|
|
148
165
|
console.log( 'route not found: '+url );
|
|
149
166
|
this.fire( "error", {code: 404, message: "route not found" } );
|
|
150
167
|
return;
|
|
151
168
|
}
|
|
152
169
|
|
|
153
|
-
found.
|
|
154
|
-
h(found.params,url);
|
|
155
|
-
});
|
|
170
|
+
found.handler( found.params, url );
|
|
156
171
|
|
|
157
172
|
this.fire( "change", { value: this._getLocation() } );
|
|
158
173
|
});
|
|
@@ -201,7 +216,8 @@ export class Router extends EventSource< RouterEvents > {
|
|
|
201
216
|
* Behavior summary:
|
|
202
217
|
* - If no matching route or no handlers are found, logs a message, fires an "error" event with
|
|
203
218
|
* { code: 404, message: "route not found" } and returns false.
|
|
204
|
-
* - If notify is true, invokes the
|
|
219
|
+
* - If notify is true, invokes the handler of the first route that matches, in the order of get,
|
|
220
|
+
* with (params, path): the params decoded, the path as the back button gives it ("/detail/5").
|
|
205
221
|
* - Always fires a "change" event with { value: this._getLocation() } after performing the navigation.
|
|
206
222
|
*
|
|
207
223
|
* @param uri - Target URI to navigate to. If it does not start with '/', a leading '/' will be added.
|
|
@@ -227,7 +243,7 @@ export class Router extends EventSource< RouterEvents > {
|
|
|
227
243
|
|
|
228
244
|
const found = this._find( uri );
|
|
229
245
|
|
|
230
|
-
if( !found
|
|
246
|
+
if( !found ) {
|
|
231
247
|
//window.history.pushState({}, '', 'error')
|
|
232
248
|
console.log( 'route not found: '+uri );
|
|
233
249
|
this.fire( "error", {code: 404, message: "route not found" } );
|
|
@@ -249,12 +265,9 @@ export class Router extends EventSource< RouterEvents > {
|
|
|
249
265
|
window.history.pushState({}, '', uri );
|
|
250
266
|
}
|
|
251
267
|
|
|
268
|
+
// the path as popstate gives it: "/detail/5", encoded as in the address
|
|
252
269
|
if( notify ) {
|
|
253
|
-
|
|
254
|
-
// h( found.params, uri );
|
|
255
|
-
//} );
|
|
256
|
-
|
|
257
|
-
found.handlers[0]( found.params, uri );
|
|
270
|
+
found.handler( found.params, this._getLocation( ) );
|
|
258
271
|
}
|
|
259
272
|
|
|
260
273
|
this.fire( "change", { value: this._getLocation() } );
|
|
@@ -262,47 +275,32 @@ export class Router extends EventSource< RouterEvents > {
|
|
|
262
275
|
}
|
|
263
276
|
|
|
264
277
|
/**
|
|
265
|
-
*
|
|
278
|
+
* the first route that matches, in the order of get, and its parameters decoded; null when none
|
|
266
279
|
*/
|
|
267
280
|
|
|
268
|
-
private _find( url: string ): { params: Record<string,any>,
|
|
269
|
-
|
|
270
|
-
let matches = [];
|
|
271
|
-
let params: Record<string,any> = {};
|
|
272
|
-
let handlers: RouteHandler[] = [];
|
|
281
|
+
private _find( url: string ): { params: Record<string,any>, handler: RouteHandler } {
|
|
273
282
|
|
|
274
283
|
for (const tmp of this.m_routes ) {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if (matches['groups']) {
|
|
282
|
-
for (const k in matches['groups']) {
|
|
283
|
-
params[k] = matches['groups'][k];
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
handlers = [...handlers, tmp.handler];
|
|
288
|
-
}
|
|
289
|
-
else if (tmp.keys.length > 0) {
|
|
290
|
-
matches = tmp.pattern.exec(url);
|
|
291
|
-
if (matches === null) {
|
|
292
|
-
continue;
|
|
293
|
-
}
|
|
284
|
+
const matches = tmp.pattern.exec( url );
|
|
285
|
+
if( !matches ) {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
294
288
|
|
|
295
|
-
|
|
296
|
-
|
|
289
|
+
const params: Record<string,any> = {};
|
|
290
|
+
if( !tmp.keys ) {
|
|
291
|
+
for (const k in matches.groups ) {
|
|
292
|
+
params[k] = decode( matches.groups[k] );
|
|
297
293
|
}
|
|
298
|
-
|
|
299
|
-
handlers = [...handlers, tmp.handler];
|
|
300
|
-
}
|
|
301
|
-
else if (tmp.pattern.test(url)) {
|
|
302
|
-
handlers = [...handlers, tmp.handler];
|
|
303
294
|
}
|
|
295
|
+
else {
|
|
296
|
+
tmp.keys.forEach( ( k, j ) => {
|
|
297
|
+
params[k] = decode( matches[j+1] );
|
|
298
|
+
} );
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return { params, handler: tmp.handler };
|
|
304
302
|
}
|
|
305
303
|
|
|
306
|
-
return
|
|
304
|
+
return null;
|
|
307
305
|
}
|
|
308
306
|
}
|
|
@@ -51,7 +51,7 @@ interface Keys {
|
|
|
51
51
|
key: string;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
const isMac =
|
|
54
|
+
const isMac = /mac|iphone|ipad/i.test( navigator.platform ?? "" );
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* "Shift+Mod+K" -> shift, ctrl (cmd on a Mac), k
|
package/src/core/core_state.ts
CHANGED
|
@@ -75,8 +75,11 @@ function _path_matches( path: string, watched: string ): boolean {
|
|
|
75
75
|
return c==='.' || c==='[';
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
// asked to a proxy of the state, it gives its raw object
|
|
79
|
+
const RAW = Symbol( "raw" );
|
|
80
|
+
|
|
78
81
|
/**
|
|
79
|
-
*
|
|
82
|
+
*
|
|
80
83
|
*/
|
|
81
84
|
|
|
82
85
|
export class StateManager<T extends State> extends EventSource<StateEvents> {
|
|
@@ -90,9 +93,11 @@ export class StateManager<T extends State> extends EventSource<StateEvents> {
|
|
|
90
93
|
* - guarantees referential identity: state.user === state.user
|
|
91
94
|
* - WeakMap: entries are collected with their objects, and the whole cache is collected with the manager
|
|
92
95
|
* - instance-scoped (not module-level) so the same raw object referenced by two different managers never gets a proxy bound to the wrong manager/path
|
|
96
|
+
* - a proxy by object, made for the path it was read at: an object moved
|
|
97
|
+
* in an array (shift, sort...) gets a proxy at its new path
|
|
93
98
|
*/
|
|
94
99
|
|
|
95
|
-
private _cache = new WeakMap<object, any>( );
|
|
100
|
+
private _cache = new WeakMap<object, { path: string, proxy: any }>( );
|
|
96
101
|
|
|
97
102
|
constructor( initialState: T ) {
|
|
98
103
|
super( );
|
|
@@ -127,12 +132,17 @@ export class StateManager<T extends State> extends EventSource<StateEvents> {
|
|
|
127
132
|
private _mk_proxy( obj: any, path: string ): any {
|
|
128
133
|
|
|
129
134
|
const cached = this._cache.get( obj );
|
|
130
|
-
if( cached ) return cached;
|
|
135
|
+
if( cached && cached.path===path ) return cached.proxy;
|
|
131
136
|
|
|
132
137
|
const proxy = new Proxy( obj, {
|
|
133
138
|
|
|
134
139
|
get: ( target, prop, receiver ) => {
|
|
135
140
|
|
|
141
|
+
// the raw object, for set
|
|
142
|
+
if( prop === RAW ) {
|
|
143
|
+
return target;
|
|
144
|
+
}
|
|
145
|
+
|
|
136
146
|
// symbols (Symbol.iterator, Symbol.toPrimitive, devtools inspection...):
|
|
137
147
|
// pass-through, never an error nor a wrap
|
|
138
148
|
if( typeof prop === "symbol" ) {
|
|
@@ -161,9 +171,16 @@ export class StateManager<T extends State> extends EventSource<StateEvents> {
|
|
|
161
171
|
|
|
162
172
|
set: ( target, prop, value, receiver ) => {
|
|
163
173
|
|
|
174
|
+
// shift, sort, reverse... read the elements through the proxy and write them
|
|
175
|
+
// back: the raw object is stored, never a proxy
|
|
176
|
+
if( value && value[RAW] ) {
|
|
177
|
+
value = value[RAW];
|
|
178
|
+
}
|
|
179
|
+
|
|
164
180
|
// no notification when the value doesn't change
|
|
165
181
|
// (avoids noise and listener → set → listener loops)
|
|
166
|
-
|
|
182
|
+
// read without the receiver: through the proxy, an object would be a proxy, never the raw value
|
|
183
|
+
if( Reflect.get( target, prop ) === value ) {
|
|
167
184
|
return true;
|
|
168
185
|
}
|
|
169
186
|
|
|
@@ -191,7 +208,7 @@ export class StateManager<T extends State> extends EventSource<StateEvents> {
|
|
|
191
208
|
}
|
|
192
209
|
});
|
|
193
210
|
|
|
194
|
-
this._cache.set( obj, proxy );
|
|
211
|
+
this._cache.set( obj, { path, proxy } );
|
|
195
212
|
return proxy;
|
|
196
213
|
}
|
|
197
214
|
|
package/src/x4.d.ts
CHANGED