assign-gingerly 0.0.81 → 0.0.82
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/DX/emojis.js +2 -1
- package/DX/emojis.ts +2 -1
- package/DX/paths.js +14 -14
- package/DX/paths.ts +155 -156
- package/inferencer/types/NewCustomElement.md +8 -5
- package/inferencer/types/NewHTMLFirstCustomElement.md +48 -13
- package/inferencer/types/roundabout/types.d.ts +2 -1
- package/package.json +1 -1
package/DX/emojis.js
CHANGED
package/DX/emojis.ts
CHANGED
package/DX/paths.js
CHANGED
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
* const value = sp`${$.lastName}, ${$.firstName}`;
|
|
20
20
|
* // ['?.lastName', ', ', '?.firstName']
|
|
21
21
|
*
|
|
22
|
-
* // Use .
|
|
23
|
-
* const key = $.textContent.
|
|
22
|
+
* // Use .Path for raw string contexts (object keys, plain arrays):
|
|
23
|
+
* const key = $.textContent.Path; // '?.textContent'
|
|
24
24
|
*/
|
|
25
25
|
/**
|
|
26
26
|
* Symbol used internally to detect path proxy objects.
|
|
@@ -49,7 +49,7 @@ function appendCommandSuffix(prefix, token) {
|
|
|
49
49
|
return `${prefix}${COMMAND_TOKEN_SUFFIXES[token]}`;
|
|
50
50
|
}
|
|
51
51
|
function getReservedToken(prop) {
|
|
52
|
-
if (prop === 'Path'
|
|
52
|
+
if (prop === 'Path')
|
|
53
53
|
return prop;
|
|
54
54
|
if (prop in COMMAND_TOKEN_SUFFIXES)
|
|
55
55
|
return prop;
|
|
@@ -58,14 +58,14 @@ function getReservedToken(prop) {
|
|
|
58
58
|
/**
|
|
59
59
|
* Create a proxy for id-ref paths (#[varName]).
|
|
60
60
|
* After the initial #[varName], further property access chains with ?. from the resolved element.
|
|
61
|
-
* .
|
|
61
|
+
* .Path returns the #[varName] prefix (optionally with further ?. path).
|
|
62
62
|
*/
|
|
63
63
|
function createIdRefProxy(idRef, options) {
|
|
64
64
|
function handler() { }
|
|
65
65
|
Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
|
|
66
66
|
return new Proxy(handler, {
|
|
67
67
|
get(_, prop) {
|
|
68
|
-
if (prop === '
|
|
68
|
+
if (prop === 'Path' || prop === PATH_SYMBOL) {
|
|
69
69
|
return idRef;
|
|
70
70
|
}
|
|
71
71
|
if (typeof prop === 'symbol')
|
|
@@ -74,7 +74,7 @@ function createIdRefProxy(idRef, options) {
|
|
|
74
74
|
if (reservedToken === 'Each') {
|
|
75
75
|
return createIdRefProxy(appendPathSegment(idRef, '@each'), options);
|
|
76
76
|
}
|
|
77
|
-
if (reservedToken && reservedToken !== 'Path'
|
|
77
|
+
if (reservedToken && reservedToken !== 'Path') {
|
|
78
78
|
return createIdRefProxy(appendCommandSuffix(idRef, reservedToken), options);
|
|
79
79
|
}
|
|
80
80
|
// Chain further path segments after the id ref
|
|
@@ -116,14 +116,14 @@ function createPathProxy(prefix, options) {
|
|
|
116
116
|
Object.defineProperty(handler, PATH_SYMBOL, { value: prefix.length > 0 ? `?.${prefix}` : '?.' });
|
|
117
117
|
return new Proxy(handler, {
|
|
118
118
|
get(_, prop) {
|
|
119
|
-
if (prop === '
|
|
119
|
+
if (prop === 'Path' || prop === PATH_SYMBOL) {
|
|
120
120
|
return serializePath(prefix);
|
|
121
121
|
}
|
|
122
122
|
// Ignore symbol access (Symbol.iterator, Symbol.toPrimitive, etc.)
|
|
123
123
|
if (typeof prop === 'symbol')
|
|
124
124
|
return undefined;
|
|
125
125
|
const reservedToken = getReservedToken(String(prop));
|
|
126
|
-
if (reservedToken === 'Path'
|
|
126
|
+
if (reservedToken === 'Path') {
|
|
127
127
|
return serializePath(prefix);
|
|
128
128
|
}
|
|
129
129
|
if (reservedToken === 'Each') {
|
|
@@ -185,21 +185,21 @@ function createPathProxy(prefix, options) {
|
|
|
185
185
|
*
|
|
186
186
|
* @example
|
|
187
187
|
* const $ = paths<Person>();
|
|
188
|
-
* $.lastName.
|
|
189
|
-
* $.address.city.
|
|
188
|
+
* $.lastName.Path // '?.lastName'
|
|
189
|
+
* $.address.city.Path // '?.address?.city'
|
|
190
190
|
*
|
|
191
191
|
* // With aka (reverse alias applied):
|
|
192
192
|
* const $ = paths<MyEl>({ aka: { q: 'querySelector' } });
|
|
193
|
-
* $.querySelector('.user').textContent.
|
|
193
|
+
* $.querySelector('.user').textContent.Path // '?.q?..user?.textContent'
|
|
194
194
|
*
|
|
195
|
-
* // Inside sp template literals, .
|
|
195
|
+
* // Inside sp template literals, .Path is not needed:
|
|
196
196
|
* sp`${$.lastName}, ${$.firstName}` // ['?.lastName', ', ', '?.firstName']
|
|
197
197
|
*/
|
|
198
198
|
export function paths(options) {
|
|
199
199
|
return createPathProxy('', options);
|
|
200
200
|
}
|
|
201
201
|
/**
|
|
202
|
-
* Create an assignment pair: { [lhs.
|
|
202
|
+
* Create an assignment pair: { [lhs.Path]: rhs.Path }.
|
|
203
203
|
* Used to express "set this target to this source value" in a spreadable form.
|
|
204
204
|
*
|
|
205
205
|
* @example
|
|
@@ -318,7 +318,7 @@ export function forEachKeyIn(keys, factory, options) {
|
|
|
318
318
|
* Interleaves static string segments with interpolated values.
|
|
319
319
|
*
|
|
320
320
|
* Path proxy objects are auto-detected and converted to their `?.`-prefixed
|
|
321
|
-
* string representation — no `.
|
|
321
|
+
* string representation — no `.Path` call needed inside sp template literals.
|
|
322
322
|
*
|
|
323
323
|
* Arrays passed as interpolations are preserved as nested arrays (for
|
|
324
324
|
* all-or-nothing optional segments in builtIns.join).
|
package/DX/paths.ts
CHANGED
|
@@ -19,76 +19,75 @@
|
|
|
19
19
|
* const value = sp`${$.lastName}, ${$.firstName}`;
|
|
20
20
|
* // ['?.lastName', ', ', '?.firstName']
|
|
21
21
|
*
|
|
22
|
-
* // Use .
|
|
23
|
-
* const key = $.textContent.
|
|
22
|
+
* // Use .Path for raw string contexts (object keys, plain arrays):
|
|
23
|
+
* const key = $.textContent.Path; // '?.textContent'
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Symbol used internally to detect path proxy objects.
|
|
28
28
|
* The sp tag function uses this to auto-extract path strings from proxies.
|
|
29
29
|
*/
|
|
30
|
-
const PATH_SYMBOL = Symbol('assign-gingerly-path');
|
|
31
|
-
|
|
32
|
-
function isPathProxy(value: unknown): value is { [PATH_SYMBOL]: string } {
|
|
33
|
-
return !!value && (typeof value === 'object' || typeof value === 'function') && PATH_SYMBOL in value;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const COMMAND_TOKEN_SUFFIXES = {
|
|
37
|
-
Each: '?.@each',
|
|
38
|
-
EqNot: ' =!',
|
|
39
|
-
PlusEq: ' +=',
|
|
40
|
-
QMEq: ' ?=',
|
|
41
|
-
YEq: ' Y=',
|
|
42
|
-
MinusEq: ' -=',
|
|
43
|
-
Arrow: ' =>',
|
|
44
|
-
} as const;
|
|
45
|
-
|
|
46
|
-
type CommandToken = keyof typeof COMMAND_TOKEN_SUFFIXES;
|
|
47
|
-
|
|
48
|
-
function serializePath(prefix: string): string {
|
|
49
|
-
return prefix.length > 0 ? `?.${prefix}` : '?.';
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function appendPathSegment(prefix: string, segment: string): string {
|
|
53
|
-
return prefix ? `${prefix}?.${segment}` : segment;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function appendCommandSuffix(prefix: string, token: CommandToken): string {
|
|
57
|
-
return `${prefix}${COMMAND_TOKEN_SUFFIXES[token]}`;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function getReservedToken(prop: string): CommandToken | 'Path' |
|
|
61
|
-
if (prop === 'Path'
|
|
62
|
-
if (prop in COMMAND_TOKEN_SUFFIXES) return prop as CommandToken;
|
|
63
|
-
return undefined;
|
|
64
|
-
}
|
|
30
|
+
const PATH_SYMBOL = Symbol('assign-gingerly-path');
|
|
31
|
+
|
|
32
|
+
function isPathProxy(value: unknown): value is { [PATH_SYMBOL]: string } {
|
|
33
|
+
return !!value && (typeof value === 'object' || typeof value === 'function') && PATH_SYMBOL in value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const COMMAND_TOKEN_SUFFIXES = {
|
|
37
|
+
Each: '?.@each',
|
|
38
|
+
EqNot: ' =!',
|
|
39
|
+
PlusEq: ' +=',
|
|
40
|
+
QMEq: ' ?=',
|
|
41
|
+
YEq: ' Y=',
|
|
42
|
+
MinusEq: ' -=',
|
|
43
|
+
Arrow: ' =>',
|
|
44
|
+
} as const;
|
|
45
|
+
|
|
46
|
+
type CommandToken = keyof typeof COMMAND_TOKEN_SUFFIXES;
|
|
47
|
+
|
|
48
|
+
function serializePath(prefix: string): string {
|
|
49
|
+
return prefix.length > 0 ? `?.${prefix}` : '?.';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function appendPathSegment(prefix: string, segment: string): string {
|
|
53
|
+
return prefix ? `${prefix}?.${segment}` : segment;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function appendCommandSuffix(prefix: string, token: CommandToken): string {
|
|
57
|
+
return `${prefix}${COMMAND_TOKEN_SUFFIXES[token]}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getReservedToken(prop: string): CommandToken | 'Path' | undefined {
|
|
61
|
+
if (prop === 'Path') return prop;
|
|
62
|
+
if (prop in COMMAND_TOKEN_SUFFIXES) return prop as CommandToken;
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
|
-
* Type that maps an object type to a proxy where every property access
|
|
68
|
-
* returns either a deeper proxy (for object properties) or a terminal
|
|
69
|
-
* with a serialized path string accessor — while providing full autocomplete.
|
|
70
|
-
* Enhanced: also callable (for method call syntax) and includes reserved
|
|
71
|
-
* command markers such as `Each`, `EqNot`, and `PlusEq`.
|
|
72
|
-
*/
|
|
73
|
-
export type PathProxyCore = {
|
|
74
|
-
readonly Path: string;
|
|
75
|
-
readonly
|
|
76
|
-
readonly
|
|
77
|
-
readonly
|
|
78
|
-
readonly
|
|
79
|
-
readonly
|
|
80
|
-
readonly
|
|
81
|
-
readonly
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
} & PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore);
|
|
67
|
+
* Type that maps an object type to a proxy where every property access
|
|
68
|
+
* returns either a deeper proxy (for object properties) or a terminal
|
|
69
|
+
* with a serialized path string accessor — while providing full autocomplete.
|
|
70
|
+
* Enhanced: also callable (for method call syntax) and includes reserved
|
|
71
|
+
* command markers such as `Each`, `EqNot`, and `PlusEq`.
|
|
72
|
+
*/
|
|
73
|
+
export type PathProxyCore = {
|
|
74
|
+
readonly Path: string;
|
|
75
|
+
readonly Each: PathProxy<any>;
|
|
76
|
+
readonly EqNot: PathProxy<any>;
|
|
77
|
+
readonly PlusEq: PathProxy<any>;
|
|
78
|
+
readonly QMEq: PathProxy<any>;
|
|
79
|
+
readonly YEq: PathProxy<any>;
|
|
80
|
+
readonly MinusEq: PathProxy<any>;
|
|
81
|
+
readonly Arrow: PathProxy<any>;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export type PathProxy<T> = {
|
|
85
|
+
[K in keyof T]-?: T[K] extends ((...args: any[]) => infer R)
|
|
86
|
+
? ((...args: any[]) => PathProxy<NonNullable<R>> & PathProxyCore) & PathProxy<NonNullable<R>> & PathProxyCore
|
|
87
|
+
: T[K] extends (object | undefined | null)
|
|
88
|
+
? PathProxy<NonNullable<T[K]>> & PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore)
|
|
89
|
+
: PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore);
|
|
90
|
+
} & PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore);
|
|
92
91
|
|
|
93
92
|
/**
|
|
94
93
|
* Options for paths proxy creation.
|
|
@@ -103,33 +102,33 @@ export interface PathsOptions {
|
|
|
103
102
|
/**
|
|
104
103
|
* Create a proxy for id-ref paths (#[varName]).
|
|
105
104
|
* After the initial #[varName], further property access chains with ?. from the resolved element.
|
|
106
|
-
* .
|
|
105
|
+
* .Path returns the #[varName] prefix (optionally with further ?. path).
|
|
107
106
|
*/
|
|
108
|
-
function createIdRefProxy(idRef: string, options?: PathsOptions): any {
|
|
109
|
-
function handler() {}
|
|
110
|
-
Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
|
|
111
|
-
return new Proxy(handler, {
|
|
112
|
-
get(_, prop: string | symbol) {
|
|
113
|
-
if (prop === '
|
|
107
|
+
function createIdRefProxy(idRef: string, options?: PathsOptions): any {
|
|
108
|
+
function handler() {}
|
|
109
|
+
Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
|
|
110
|
+
return new Proxy(handler, {
|
|
111
|
+
get(_, prop: string | symbol) {
|
|
112
|
+
if (prop === 'Path' || prop === PATH_SYMBOL) {
|
|
114
113
|
return idRef;
|
|
115
114
|
}
|
|
116
|
-
if (typeof prop === 'symbol') return undefined;
|
|
117
|
-
|
|
118
|
-
const reservedToken = getReservedToken(prop);
|
|
119
|
-
if (reservedToken === 'Each') {
|
|
120
|
-
return createIdRefProxy(appendPathSegment(idRef, '@each'), options);
|
|
121
|
-
}
|
|
122
|
-
if (reservedToken && reservedToken !== 'Path'
|
|
115
|
+
if (typeof prop === 'symbol') return undefined;
|
|
116
|
+
|
|
117
|
+
const reservedToken = getReservedToken(prop);
|
|
118
|
+
if (reservedToken === 'Each') {
|
|
119
|
+
return createIdRefProxy(appendPathSegment(idRef, '@each'), options);
|
|
120
|
+
}
|
|
121
|
+
if (reservedToken && reservedToken !== 'Path') {
|
|
123
122
|
return createIdRefProxy(appendCommandSuffix(idRef, reservedToken), options);
|
|
124
123
|
}
|
|
125
|
-
|
|
126
|
-
// Chain further path segments after the id ref
|
|
127
|
-
const chained = appendPathSegment(idRef, String(prop));
|
|
128
|
-
return createIdRefProxy(chained, options);
|
|
129
|
-
},
|
|
130
|
-
apply(_, __, args) {
|
|
131
|
-
if (args.length > 0) {
|
|
132
|
-
const arg = args[0];
|
|
124
|
+
|
|
125
|
+
// Chain further path segments after the id ref
|
|
126
|
+
const chained = appendPathSegment(idRef, String(prop));
|
|
127
|
+
return createIdRefProxy(chained, options);
|
|
128
|
+
},
|
|
129
|
+
apply(_, __, args) {
|
|
130
|
+
if (args.length > 0) {
|
|
131
|
+
const arg = args[0];
|
|
133
132
|
let argStr: string;
|
|
134
133
|
if (arg === true) argStr = 'true';
|
|
135
134
|
else if (arg === false) argStr = 'false';
|
|
@@ -154,72 +153,72 @@ function createIdRefProxy(idRef: string, options?: PathsOptions): any {
|
|
|
154
153
|
* When `aka` is provided, property names that match an alias *value* are output
|
|
155
154
|
* using the alias *key* instead (reverse alias).
|
|
156
155
|
*/
|
|
157
|
-
function createPathProxy(prefix: string, options?: PathsOptions): any {
|
|
158
|
-
const aliasMap = options?.aka;
|
|
159
|
-
|
|
160
|
-
// Use a function as the target to enable the apply trap
|
|
161
|
-
function handler() {}
|
|
162
|
-
Object.defineProperty(handler, PATH_SYMBOL, { value: prefix.length > 0 ? `?.${prefix}` : '?.' });
|
|
163
|
-
|
|
164
|
-
return new Proxy(handler, {
|
|
165
|
-
get(_, prop: string | symbol) {
|
|
166
|
-
if (prop === '
|
|
156
|
+
function createPathProxy(prefix: string, options?: PathsOptions): any {
|
|
157
|
+
const aliasMap = options?.aka;
|
|
158
|
+
|
|
159
|
+
// Use a function as the target to enable the apply trap
|
|
160
|
+
function handler() {}
|
|
161
|
+
Object.defineProperty(handler, PATH_SYMBOL, { value: prefix.length > 0 ? `?.${prefix}` : '?.' });
|
|
162
|
+
|
|
163
|
+
return new Proxy(handler, {
|
|
164
|
+
get(_, prop: string | symbol) {
|
|
165
|
+
if (prop === 'Path' || prop === PATH_SYMBOL) {
|
|
167
166
|
return serializePath(prefix);
|
|
168
167
|
}
|
|
169
|
-
// Ignore symbol access (Symbol.iterator, Symbol.toPrimitive, etc.)
|
|
170
|
-
if (typeof prop === 'symbol') return undefined;
|
|
171
|
-
|
|
172
|
-
const reservedToken = getReservedToken(String(prop));
|
|
173
|
-
if (reservedToken === 'Path'
|
|
168
|
+
// Ignore symbol access (Symbol.iterator, Symbol.toPrimitive, etc.)
|
|
169
|
+
if (typeof prop === 'symbol') return undefined;
|
|
170
|
+
|
|
171
|
+
const reservedToken = getReservedToken(String(prop));
|
|
172
|
+
if (reservedToken === 'Path') {
|
|
174
173
|
return serializePath(prefix);
|
|
175
174
|
}
|
|
176
|
-
if (reservedToken === 'Each') {
|
|
177
|
-
return createPathProxy(appendPathSegment(prefix, '@each'), options);
|
|
178
|
-
}
|
|
179
|
-
if (reservedToken) {
|
|
180
|
-
return createPathProxy(appendCommandSuffix(prefix, reservedToken), options);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
let segment = String(prop);
|
|
184
|
-
|
|
185
|
-
// #-prefix: $['#firstName'] → '#[firstName]' (cached element ref)
|
|
186
|
-
if (segment.startsWith('#')) {
|
|
187
|
-
const varName = segment.substring(1);
|
|
188
|
-
const idRef = `#[${varName}]`;
|
|
175
|
+
if (reservedToken === 'Each') {
|
|
176
|
+
return createPathProxy(appendPathSegment(prefix, '@each'), options);
|
|
177
|
+
}
|
|
178
|
+
if (reservedToken) {
|
|
179
|
+
return createPathProxy(appendCommandSuffix(prefix, reservedToken), options);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let segment = String(prop);
|
|
183
|
+
|
|
184
|
+
// #-prefix: $['#firstName'] → '#[firstName]' (cached element ref)
|
|
185
|
+
if (segment.startsWith('#')) {
|
|
186
|
+
const varName = segment.substring(1);
|
|
187
|
+
const idRef = `#[${varName}]`;
|
|
189
188
|
// Return a proxy that starts from this id ref (can chain further with ?.)
|
|
190
189
|
return createIdRefProxy(idRef, options);
|
|
191
190
|
}
|
|
192
191
|
|
|
193
192
|
// Apply reverse alias: if prop matches an alias value, use the alias key
|
|
194
|
-
if (aliasMap) {
|
|
195
|
-
for (const [alias, target] of Object.entries(aliasMap)) {
|
|
196
|
-
if (target === segment) { segment = alias; break; }
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const newPath = appendPathSegment(prefix, segment);
|
|
201
|
-
return createPathProxy(newPath, options);
|
|
202
|
-
},
|
|
203
|
-
apply(_, __, args) {
|
|
204
|
-
// Method call syntax: $.querySelector('.username') → extends path with the argument
|
|
205
|
-
if (args.length > 0) {
|
|
193
|
+
if (aliasMap) {
|
|
194
|
+
for (const [alias, target] of Object.entries(aliasMap)) {
|
|
195
|
+
if (target === segment) { segment = alias; break; }
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const newPath = appendPathSegment(prefix, segment);
|
|
200
|
+
return createPathProxy(newPath, options);
|
|
201
|
+
},
|
|
202
|
+
apply(_, __, args) {
|
|
203
|
+
// Method call syntax: $.querySelector('.username') → extends path with the argument
|
|
204
|
+
if (args.length > 0) {
|
|
206
205
|
const arg = args[0];
|
|
207
206
|
let argStr: string;
|
|
208
207
|
if (arg === true) argStr = 'true';
|
|
209
208
|
else if (arg === false) argStr = 'false';
|
|
210
|
-
else if (isPathProxy(arg)) {
|
|
211
|
-
// Proxy arg — extract path without '?.' prefix
|
|
212
|
-
const fullPath = arg[PATH_SYMBOL] as string;
|
|
213
|
-
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
214
|
-
}
|
|
215
|
-
else argStr = String(arg);
|
|
216
|
-
|
|
217
|
-
const newPath = appendPathSegment(prefix, argStr);
|
|
218
|
-
return createPathProxy(newPath, options);
|
|
219
|
-
}
|
|
220
|
-
// No args — method called with no arguments, return self
|
|
221
|
-
return createPathProxy(prefix, options);
|
|
222
|
-
}
|
|
209
|
+
else if (isPathProxy(arg)) {
|
|
210
|
+
// Proxy arg — extract path without '?.' prefix
|
|
211
|
+
const fullPath = arg[PATH_SYMBOL] as string;
|
|
212
|
+
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
213
|
+
}
|
|
214
|
+
else argStr = String(arg);
|
|
215
|
+
|
|
216
|
+
const newPath = appendPathSegment(prefix, argStr);
|
|
217
|
+
return createPathProxy(newPath, options);
|
|
218
|
+
}
|
|
219
|
+
// No args — method called with no arguments, return self
|
|
220
|
+
return createPathProxy(prefix, options);
|
|
221
|
+
}
|
|
223
222
|
});
|
|
224
223
|
}
|
|
225
224
|
|
|
@@ -232,14 +231,14 @@ function createPathProxy(prefix: string, options?: PathsOptions): any {
|
|
|
232
231
|
*
|
|
233
232
|
* @example
|
|
234
233
|
* const $ = paths<Person>();
|
|
235
|
-
* $.lastName.
|
|
236
|
-
* $.address.city.
|
|
234
|
+
* $.lastName.Path // '?.lastName'
|
|
235
|
+
* $.address.city.Path // '?.address?.city'
|
|
237
236
|
*
|
|
238
237
|
* // With aka (reverse alias applied):
|
|
239
238
|
* const $ = paths<MyEl>({ aka: { q: 'querySelector' } });
|
|
240
|
-
* $.querySelector('.user').textContent.
|
|
239
|
+
* $.querySelector('.user').textContent.Path // '?.q?..user?.textContent'
|
|
241
240
|
*
|
|
242
|
-
* // Inside sp template literals, .
|
|
241
|
+
* // Inside sp template literals, .Path is not needed:
|
|
243
242
|
* sp`${$.lastName}, ${$.firstName}` // ['?.lastName', ', ', '?.firstName']
|
|
244
243
|
*/
|
|
245
244
|
export function paths<T>(options?: PathsOptions): PathProxy<T> {
|
|
@@ -247,7 +246,7 @@ export function paths<T>(options?: PathsOptions): PathProxy<T> {
|
|
|
247
246
|
}
|
|
248
247
|
|
|
249
248
|
/**
|
|
250
|
-
* Create an assignment pair: { [lhs.
|
|
249
|
+
* Create an assignment pair: { [lhs.Path]: rhs.Path }.
|
|
251
250
|
* Used to express "set this target to this source value" in a spreadable form.
|
|
252
251
|
*
|
|
253
252
|
* @example
|
|
@@ -261,10 +260,10 @@ export function paths<T>(options?: PathsOptions): PathProxy<T> {
|
|
|
261
260
|
* count: 1
|
|
262
261
|
* }
|
|
263
262
|
*/
|
|
264
|
-
export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
265
|
-
const lhsStr = isPathProxy(lhs)
|
|
266
|
-
? lhs[PATH_SYMBOL]
|
|
267
|
-
: String(lhs);
|
|
263
|
+
export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
264
|
+
const lhsStr = isPathProxy(lhs)
|
|
265
|
+
? lhs[PATH_SYMBOL]
|
|
266
|
+
: String(lhs);
|
|
268
267
|
return {
|
|
269
268
|
to(rhs: any): Record<string, any> {
|
|
270
269
|
const rhsStr = isPathProxy(rhs)
|
|
@@ -293,9 +292,9 @@ export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
|
293
292
|
* });
|
|
294
293
|
* // { assign: { incrementButton: '?.clone?.q?..increment', ... } }
|
|
295
294
|
*/
|
|
296
|
-
export function smoothOver(value: any): any {
|
|
297
|
-
if (isPathProxy(value)) {
|
|
298
|
-
return value[PATH_SYMBOL];
|
|
295
|
+
export function smoothOver(value: any): any {
|
|
296
|
+
if (isPathProxy(value)) {
|
|
297
|
+
return value[PATH_SYMBOL];
|
|
299
298
|
}
|
|
300
299
|
if (Array.isArray(value)) {
|
|
301
300
|
return value.map(smoothOver);
|
|
@@ -374,7 +373,7 @@ export function forEachKeyIn<T>(
|
|
|
374
373
|
* Interleaves static string segments with interpolated values.
|
|
375
374
|
*
|
|
376
375
|
* Path proxy objects are auto-detected and converted to their `?.`-prefixed
|
|
377
|
-
* string representation — no `.
|
|
376
|
+
* string representation — no `.Path` call needed inside sp template literals.
|
|
378
377
|
*
|
|
379
378
|
* Arrays passed as interpolations are preserved as nested arrays (for
|
|
380
379
|
* all-or-nothing optional segments in builtIns.join).
|
|
@@ -416,12 +415,12 @@ export function sp(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
|
416
415
|
* Extract the last segment from a `?.`-prefixed path string.
|
|
417
416
|
* e.g., '?.address?.city' → 'city', '?.firstName' → 'firstName'
|
|
418
417
|
*/
|
|
419
|
-
function extractPropName(pathStr: string): string {
|
|
420
|
-
const withoutCommand = pathStr.replace(/(?: \+=| =!| \?=| Y=| -=| =>)$/, '');
|
|
421
|
-
const parts = withoutCommand.split('?.');
|
|
422
|
-
const last = parts[parts.length - 1];
|
|
423
|
-
return last === '@each' ? 'Each' : last;
|
|
424
|
-
}
|
|
418
|
+
function extractPropName(pathStr: string): string {
|
|
419
|
+
const withoutCommand = pathStr.replace(/(?: \+=| =!| \?=| Y=| -=| =>)$/, '');
|
|
420
|
+
const parts = withoutCommand.split('?.');
|
|
421
|
+
const last = parts[parts.length - 1];
|
|
422
|
+
return last === '@each' ? 'Each' : last;
|
|
423
|
+
}
|
|
425
424
|
|
|
426
425
|
/**
|
|
427
426
|
* Tagged template literal that produces an array of {prop, val} objects + literal strings.
|
|
@@ -60,7 +60,7 @@ Create `types/[project-name]/types.d.ts` with the element's property interface:
|
|
|
60
60
|
/**
|
|
61
61
|
* Properties specific to this custom element
|
|
62
62
|
*/
|
|
63
|
-
export interface
|
|
63
|
+
export interface EndUserProps {
|
|
64
64
|
// Properties unique to this element
|
|
65
65
|
myProp: string;
|
|
66
66
|
disabled: boolean;
|
|
@@ -69,18 +69,21 @@ export interface ElementProps {
|
|
|
69
69
|
/**
|
|
70
70
|
* Full property set including internal state
|
|
71
71
|
*/
|
|
72
|
-
export interface AllProps extends
|
|
72
|
+
export interface AllProps extends EndUserProps {
|
|
73
73
|
idx: number;
|
|
74
74
|
item: any;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
export type
|
|
77
|
+
export type AP = AllProps;
|
|
78
|
+
|
|
79
|
+
export interface RunTimeProps extends AllProps, HTMLElement
|
|
80
|
+
|
|
78
81
|
```
|
|
79
82
|
|
|
80
83
|
**Key points:**
|
|
81
|
-
- `
|
|
84
|
+
- `EndUserProps` — the public API specific to this element
|
|
82
85
|
- `AllProps` — includes internal/computed state managed by roundabout
|
|
83
|
-
- Export `
|
|
86
|
+
- Export `AP` as a convenience alias
|
|
84
87
|
|
|
85
88
|
|
|
86
89
|
## Step 4: Create imports.html
|
|
@@ -238,17 +238,17 @@ import { fileURLToPath } from 'url';
|
|
|
238
238
|
import {akaMethods as m, aka, builtInEmoji} from 'assign-gingerly/DX/emojis.js';
|
|
239
239
|
import {paths, doAssign, set, smoothOver} from 'assign-gingerly/DX/paths.js';
|
|
240
240
|
|
|
241
|
-
/** @import {EndUserProps, AP} from './types'; */
|
|
241
|
+
/** @import {EndUserProps, AP, RuntimeProps} from './types'; */
|
|
242
242
|
/** @import {RoundaboutOptions} from './types/roundabout/types' */
|
|
243
243
|
/** @import {ElMakerConfig} from './types/el-maker/types' */
|
|
244
244
|
|
|
245
245
|
const withMethods = [m['🔍']];
|
|
246
246
|
|
|
247
|
-
const $ = (/** @type {typeof paths<
|
|
247
|
+
const $ = (/** @type {typeof paths<RuntimeProps>} */ (/** @type {any} */(paths)))({withMethods});
|
|
248
248
|
|
|
249
249
|
|
|
250
250
|
/**
|
|
251
|
-
* @type {RoundaboutOptions<
|
|
251
|
+
* @type {RoundaboutOptions<AP>}
|
|
252
252
|
*/
|
|
253
253
|
const raConfig = {
|
|
254
254
|
weakRef: {
|
|
@@ -262,30 +262,30 @@ const raConfig = {
|
|
|
262
262
|
},
|
|
263
263
|
compacts: {
|
|
264
264
|
on_click_of_expandButton_assign: {
|
|
265
|
-
[$.expandButton.hidden.
|
|
266
|
-
[$.collapseButton.hidden.
|
|
265
|
+
[$.expandButton.hidden.Path]: true,
|
|
266
|
+
[$.collapseButton.hidden.Path]: false,
|
|
267
267
|
expanded: true,
|
|
268
268
|
|
|
269
269
|
},
|
|
270
270
|
on_click_of_collapseButton_assign: {
|
|
271
271
|
expanded: false,
|
|
272
|
-
[$.expandButton.hidden.
|
|
273
|
-
[$.collapseButton.hidden.
|
|
272
|
+
[$.expandButton.hidden.Path]: false,
|
|
273
|
+
[$.collapseButton.hidden.Path]: true,
|
|
274
274
|
}
|
|
275
275
|
},
|
|
276
276
|
merges: smoothOver([
|
|
277
277
|
{
|
|
278
278
|
ifKeyIn: ['clone'],
|
|
279
|
-
|
|
280
|
-
expandButton
|
|
281
|
-
collapseButton
|
|
282
|
-
|
|
279
|
+
...doAssign(
|
|
280
|
+
set($.expandButton).to($.clone.querySelector('[name=expand]')),
|
|
281
|
+
set($.collapseButton).to($.clone.querySelector('[name=collapse]')),
|
|
282
|
+
)
|
|
283
283
|
},
|
|
284
284
|
{
|
|
285
285
|
ifKeyIn: ['expanded'],
|
|
286
286
|
...doAssign(
|
|
287
287
|
set($.ariaExpanded).to($.expanded),
|
|
288
|
-
set(
|
|
288
|
+
set($.ariaControlsElements.Each.hidden.QMEq).to([$.expanded, false, 'until-found'])
|
|
289
289
|
)
|
|
290
290
|
},
|
|
291
291
|
{
|
|
@@ -303,7 +303,7 @@ const raConfig = {
|
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
-
/** @type {ElMakerConfig<
|
|
306
|
+
/** @type {ElMakerConfig<AP>} */
|
|
307
307
|
const features = {
|
|
308
308
|
assignFeatures: {
|
|
309
309
|
roundabout: {
|
|
@@ -326,6 +326,41 @@ writeFileSync(outputFile, render(), 'utf8');
|
|
|
326
326
|
|
|
327
327
|
</details>
|
|
328
328
|
|
|
329
|
+
Some non obvious scenarios:
|
|
330
|
+
|
|
331
|
+
### How can I perform an action when the escape key is pressed somewhere on the document?
|
|
332
|
+
|
|
333
|
+
Answer:
|
|
334
|
+
|
|
335
|
+
From the side-burger example.
|
|
336
|
+
|
|
337
|
+
```JS
|
|
338
|
+
const raConfig = {
|
|
339
|
+
...
|
|
340
|
+
compacts: {
|
|
341
|
+
on_keydown_of_ownerDocument_assignFromEvent: {
|
|
342
|
+
[$.escapeKeyPressed.QMEq.Path]: [['?.key', 'Escape'], true, false]
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
merges: smoothOver([
|
|
346
|
+
{
|
|
347
|
+
ifKeyIn: ['expanded'],
|
|
348
|
+
...doAssign(
|
|
349
|
+
...
|
|
350
|
+
set($.escapeKeyPressed).to(false)
|
|
351
|
+
)
|
|
352
|
+
},
|
|
353
|
+
...
|
|
354
|
+
{
|
|
355
|
+
ifAllOf: ['escapeKeyPressed'],
|
|
356
|
+
assign: {
|
|
357
|
+
expanded: false
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
]),
|
|
361
|
+
};
|
|
362
|
+
```
|
|
363
|
+
|
|
329
364
|
## Step 8
|
|
330
365
|
|
|
331
366
|
Run `node el-maker.mjs` (or `npm run build-el-maker` if your `package.json` includes a watch script) to regenerate `el-maker.json`.
|
|
@@ -54,6 +54,7 @@ export type Compacts<TProps = any, TActions = TProps, TEvents extends string = s
|
|
|
54
54
|
| Partial<{[key in `on_${TEvents}_of_${keyof TProps & string}_inc_${keyof TProps & string}_by`]: number}>
|
|
55
55
|
| Partial<{[key in `on_${TEvents}_of_${keyof TProps & string}_set_${keyof TProps & string}_to`]: any}>
|
|
56
56
|
| Partial<{[key in `on_${TEvents}_of_${keyof TProps & string}_assign`]: Record<string, any>}>
|
|
57
|
+
| Partial<{[key in `on_${TEvents}_of_${keyof TProps & string}_assignFromEvent`]: Record<string, any>}>
|
|
57
58
|
;
|
|
58
59
|
|
|
59
60
|
export type Hitches<TProps = any, TActions = TProps> =
|
|
@@ -160,7 +161,7 @@ export interface RAConfig<
|
|
|
160
161
|
initialPropVals?: Partial<{[key in keyof TProps & string]: unknown}>,
|
|
161
162
|
}
|
|
162
163
|
|
|
163
|
-
export interface RoundaboutOptions<TProps = unknown, TActions = TProps, ETProps = TProps> extends RAConfig<TProps, TActions, ETProps> {
|
|
164
|
+
export interface RoundaboutOptions<TProps = unknown, TActions = TProps, ETProps = TProps, EventTypes extends string = string> extends RAConfig<TProps, TActions, ETProps, unknown, EventTypes> {
|
|
164
165
|
vm?: TProps & TActions & RoundaboutReady,
|
|
165
166
|
//for enhanced elements, pass in the container, referenced via $0.
|
|
166
167
|
container?: EventTarget,
|
package/package.json
CHANGED