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 CHANGED
@@ -47,7 +47,8 @@ export const akaMethods = {
47
47
  '📝': 'log',
48
48
  '⚠️': 'warn',
49
49
  '🚨': 'error',
50
- '🤱': 'appendChild'
50
+ '🤱': 'appendChild',
51
+ '😣': 'focus',
51
52
  //'🧩': 'includes'
52
53
  };
53
54
  export const aka = {
package/DX/emojis.ts CHANGED
@@ -49,7 +49,8 @@ export const akaMethods = {
49
49
  '📝': 'log',
50
50
  '⚠️': 'warn',
51
51
  '🚨': 'error',
52
- '🤱': 'appendChild'
52
+ '🤱': 'appendChild',
53
+ '😣': 'focus',
53
54
  //'🧩': 'includes'
54
55
  };
55
56
 
package/DX/paths.js CHANGED
@@ -19,8 +19,8 @@
19
19
  * const value = sp`${$.lastName}, ${$.firstName}`;
20
20
  * // ['?.lastName', ', ', '?.firstName']
21
21
  *
22
- * // Use .path for raw string contexts (object keys, plain arrays):
23
- * const key = $.textContent.path; // '?.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' || 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
- * .path returns the #[varName] prefix (optionally with further ?. path).
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 === 'path' || prop === 'Path' || prop === PATH_SYMBOL) {
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' && 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 === 'path' || prop === 'Path' || prop === PATH_SYMBOL) {
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' || 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.path // '?.lastName'
189
- * $.address.city.path // '?.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.path // '?.q?..user?.textContent'
193
+ * $.querySelector('.user').textContent.Path // '?.q?..user?.textContent'
194
194
  *
195
- * // Inside sp template literals, .path is not needed:
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.path]: rhs.path }.
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 `.path` call needed inside sp template literals.
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 .path for raw string contexts (object keys, plain arrays):
23
- * const key = $.textContent.path; // '?.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' | 'path' | undefined {
61
- if (prop === 'Path' || prop === 'path') return prop;
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 path: string;
76
- readonly Each: PathProxy<any>;
77
- readonly EqNot: PathProxy<any>;
78
- readonly PlusEq: PathProxy<any>;
79
- readonly QMEq: PathProxy<any>;
80
- readonly YEq: PathProxy<any>;
81
- readonly MinusEq: PathProxy<any>;
82
- readonly Arrow: PathProxy<any>;
83
- };
84
-
85
- export type PathProxy<T> = {
86
- [K in keyof T]-?: T[K] extends ((...args: any[]) => infer R)
87
- ? ((...args: any[]) => PathProxy<NonNullable<R>> & PathProxyCore) & PathProxy<NonNullable<R>> & PathProxyCore
88
- : T[K] extends (object | undefined | null)
89
- ? PathProxy<NonNullable<T[K]>> & PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore)
90
- : PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore);
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
- * .path returns the #[varName] prefix (optionally with further ?. path).
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 === 'path' || prop === 'Path' || prop === PATH_SYMBOL) {
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' && 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 === 'path' || prop === 'Path' || prop === PATH_SYMBOL) {
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' || 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.path // '?.lastName'
236
- * $.address.city.path // '?.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.path // '?.q?..user?.textContent'
239
+ * $.querySelector('.user').textContent.Path // '?.q?..user?.textContent'
241
240
  *
242
- * // Inside sp template literals, .path is not needed:
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.path]: rhs.path }.
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 `.path` call needed inside sp template literals.
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 ElementProps {
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 ElementProps {
72
+ export interface AllProps extends EndUserProps {
73
73
  idx: number;
74
74
  item: any;
75
75
  }
76
76
 
77
- export type T = AllProps;
77
+ export type AP = AllProps;
78
+
79
+ export interface RunTimeProps extends AllProps, HTMLElement
80
+
78
81
  ```
79
82
 
80
83
  **Key points:**
81
- - `ElementProps` — the public API specific to this element
84
+ - `EndUserProps` — the public API specific to this element
82
85
  - `AllProps` — includes internal/computed state managed by roundabout
83
- - Export `T` as a convenience alias for use in `defRef.mjs` type annotations
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<AP>} */ (/** @type {any} */(paths)))({withMethods});
247
+ const $ = (/** @type {typeof paths<RuntimeProps>} */ (/** @type {any} */(paths)))({withMethods});
248
248
 
249
249
 
250
250
  /**
251
- * @type {RoundaboutOptions<EndUserProps>}
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.path]: true,
266
- [$.collapseButton.hidden.path]: false,
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.path]: false,
273
- [$.collapseButton.hidden.path]: true,
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
- assign: {
280
- expandButton: $.clone.querySelector('[name=expand]'),
281
- collapseButton: $.clone.querySelector('[name=collapse]'),
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(`${$.ariaControlsElements.path}?.@each?.hidden =!`).to($.expanded)
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<EndUserProps>} */
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assign-gingerly",
3
- "version": "0.0.81",
3
+ "version": "0.0.82",
4
4
  "description": "This package provides a utility function for carefully merging one object into another.",
5
5
  "homepage": "https://github.com/bahrus/assign-gingerly#readme",
6
6
  "bugs": {