assign-gingerly 0.0.53 → 0.0.55
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/README.md +847 -5
- package/assignFrom.js +229 -9
- package/assignFrom.ts +338 -9
- package/assignGingerly.js +34 -1
- package/assignGingerly.ts +54 -1
- package/beVigilant.js +73 -0
- package/beVigilant.ts +85 -0
- package/enhanceAll.js +106 -0
- package/enhanceAll.ts +138 -0
- package/handlers/join.js +74 -0
- package/handlers/join.ts +80 -0
- package/handlers/lazyLoad.js +212 -0
- package/handlers/lazyLoad.ts +307 -0
- package/handlers/lazyLoadSwitch.js +58 -0
- package/handlers/lazyLoadSwitch.ts +63 -0
- package/handlers/microDataJoin.js +184 -0
- package/handlers/microDataJoin.ts +270 -0
- package/inferencer/.gitmodules +3 -0
- package/inferencer/.vscode/settings.json +2 -0
- package/inferencer/InferencedPropagator.js +230 -0
- package/inferencer/InferencedPropagator.ts +269 -0
- package/inferencer/LICENSE +21 -0
- package/inferencer/README.md +524 -0
- package/inferencer/Requirements/SupportForPropagator.md +368 -0
- package/inferencer/imports.html +7 -0
- package/inferencer/inferencer.js +254 -0
- package/inferencer/inferencer.ts +292 -0
- package/inferencer/package-lock.json +129 -0
- package/inferencer/package.json +60 -0
- package/inferencer/playwright-report/data/507ad515125e13390ea07de92f22331c913fa068.md +55 -0
- package/inferencer/playwright-report/index.html +90 -0
- package/inferencer/playwright.config.ts +54 -0
- package/inferencer/test-results/.last-run.json +6 -0
- package/inferencer/test-results/inferencer-Inferencer-Enha-535bc-inferencer-tests-in-browser-chromium/error-context.md +55 -0
- package/inferencer/tests/inferencedPropagator.html +428 -0
- package/inferencer/tests/inferencedPropagator.spec.ts +18 -0
- package/inferencer/tests/inferencer.html +355 -0
- package/inferencer/tests/inferencer.spec.ts +19 -0
- package/inferencer/tsconfig.json +19 -0
- package/inferencer/types/.kiro/specs/conversion-template/README.md +128 -0
- package/inferencer/types/.kiro/specs/conversion-template/design.md +360 -0
- package/inferencer/types/.kiro/specs/conversion-template/requirements.md +191 -0
- package/inferencer/types/.kiro/specs/conversion-template/tasks.md +174 -0
- package/inferencer/types/.kiro/steering/coding-standards.md +53 -0
- package/inferencer/types/.kiro/steering/conversion-guide.md +108 -0
- package/inferencer/types/.kiro/steering/declarative-configuration.md +108 -0
- package/inferencer/types/.kiro/steering/emc-json-serializability.md +306 -0
- package/inferencer/types/EnhancementConversionInstructions.md +1854 -0
- package/inferencer/types/LICENSE +21 -0
- package/inferencer/types/NewCustomElement.md +388 -0
- package/inferencer/types/NewCustomElementFeature.md +683 -0
- package/inferencer/types/NewEnhancementInstructions.md +705 -0
- package/inferencer/types/README.md +2 -0
- package/inferencer/types/agrace/types.d.ts +11 -0
- package/inferencer/types/assign-gingerly/types.d.ts +572 -0
- package/inferencer/types/be-a-beacon/types.d.ts +17 -0
- package/inferencer/types/be-bound/types.d.ts +66 -0
- package/inferencer/types/be-buttoned-up/types.d.ts +19 -0
- package/inferencer/types/be-calculating/types.d.ts +54 -0
- package/inferencer/types/be-clonable/types.d.ts +38 -0
- package/inferencer/types/be-committed/types.d.ts +22 -0
- package/inferencer/types/be-consoling/types.d.ts +24 -0
- package/inferencer/types/be-decked-with/types.d.ts +26 -0
- package/inferencer/types/be-delible/types.d.ts +27 -0
- package/inferencer/types/be-dispatching/types.d.ts +34 -0
- package/inferencer/types/be-evanescent/types.d.ts +20 -0
- package/inferencer/types/be-flashy/types.d.ts +21 -0
- package/inferencer/types/be-gone/types.d.ts +25 -0
- package/inferencer/types/be-observing/types.d.ts +55 -0
- package/inferencer/types/be-reflective/types.d.ts +78 -0
- package/inferencer/types/be-reformable/types.d.ts +49 -0
- package/inferencer/types/be-render-neutral/types.d.ts +32 -0
- package/inferencer/types/be-switched/types.d.ts +146 -0
- package/inferencer/types/be-typed/types.d.ts +32 -0
- package/inferencer/types/be-valued/types.d.ts +22 -0
- package/inferencer/types/data-props/types.d.ts +34 -0
- package/inferencer/types/do-inc/types.d.ts +56 -0
- package/inferencer/types/do-invoke/types.d.ts +38 -0
- package/inferencer/types/do-merge/types.d.ts +28 -0
- package/inferencer/types/do-toggle/types.d.ts +31 -0
- package/inferencer/types/face-up/types.d.ts +100 -0
- package/inferencer/types/fetch-for/types.d.ts +36 -0
- package/inferencer/types/folder-picker/types.d.ts +21 -0
- package/inferencer/types/global.d.ts +29 -0
- package/inferencer/types/id-generation/types.d.ts +26 -0
- package/inferencer/types/inferencer/types.d.ts +46 -0
- package/inferencer/types/mount-observer/types.d.ts +363 -0
- package/inferencer/types/nested-regex-groups/types.d.ts +107 -0
- package/inferencer/types/pipe-in/types.d.ts +52 -0
- package/inferencer/types/roundabout/types.d.ts +268 -0
- package/inferencer/types/soak-up/types.d.ts +40 -0
- package/inferencer/types/templ-maker/types.d.ts +43 -0
- package/inferencer/types/time-ticker/types.d.ts +62 -0
- package/inferencer/types/truth-sourcer/types.d.ts +44 -0
- package/inferencer/upSearch.js +27 -0
- package/inferencer/upSearch.ts +26 -0
- package/inferencer/withScopePerimeter.js +27 -0
- package/inferencer/withScopePerimeter.ts +33 -0
- package/inferredAssignments.js +38 -0
- package/inferredAssignments.ts +65 -0
- package/isAllowedImportPath.js +42 -0
- package/isAllowedImportPath.ts +53 -0
- package/package.json +57 -3
- package/paths.js +231 -0
- package/paths.ts +413 -0
- package/processHandlerCommands.js +188 -0
- package/processHandlerCommands.ts +217 -0
- package/resolveIdRef.js +144 -0
- package/resolveIdRef.ts +170 -0
- package/resolveValues.js +41 -2
- package/resolveValues.ts +41 -1
- package/transitionHelper.js +109 -0
- package/transitionHelper.ts +132 -0
- package/types/assign-gingerly/types.d.ts +89 -0
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
# inferencer
|
|
2
|
+
|
|
3
|
+
DOM Element Enhancement that makes commonly used inferences for value, display, and event type properties.
|
|
4
|
+
|
|
5
|
+
[](http://badge.fury.io/js/inferencer)
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install inferencer assign-gingerly
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
The `inferencer` enhancement provides an attribute-less [element enhancement](https://github.com/bahrus/assign-gingerly#object-and-element-enhancements-via-assign-gingerly) API for smart value and display property assignment. Instead of manually determining which property to set on different element types (e.g., `value` for inputs, `checked` for checkboxes, `textContent` for divs), *inferencer* automatically infers the correct property based on the element type.
|
|
16
|
+
|
|
17
|
+
### Why inferencer?
|
|
18
|
+
|
|
19
|
+
Different HTML elements use different properties to represent their value:
|
|
20
|
+
- Input text fields use `value`
|
|
21
|
+
- Checkboxes and radio buttons use `checked`
|
|
22
|
+
- Time elements use `dateTime`
|
|
23
|
+
- Divs and spans use `textContent`
|
|
24
|
+
- Progress and meter elements use `value` but display with `ariaValueText`
|
|
25
|
+
|
|
26
|
+
The inferencer enhancement eliminates the need to remember these differences by providing symbols that automatically map to the correct property:
|
|
27
|
+
|
|
28
|
+
- `value` symbol - Sets the element's data value
|
|
29
|
+
- `display` symbol - Sets the element's display/presentation value
|
|
30
|
+
- `eventType` getter - Returns the most appropriate event type for the element
|
|
31
|
+
|
|
32
|
+
## Basic Usage
|
|
33
|
+
|
|
34
|
+
```TypeScript
|
|
35
|
+
import { value, display, registryItem } from 'inferencer/inferencer.js';
|
|
36
|
+
import 'assign-gingerly/object-extension.js';
|
|
37
|
+
|
|
38
|
+
// Use the value symbol - automatically sets the right property
|
|
39
|
+
const input = document.createElement('input');
|
|
40
|
+
input.type = 'text';
|
|
41
|
+
input.set[value] = 'hello';
|
|
42
|
+
console.log(input.value); // 'hello'
|
|
43
|
+
|
|
44
|
+
// alternatively:
|
|
45
|
+
|
|
46
|
+
const inputInference = input.enh.get(registryItem);
|
|
47
|
+
inputInference.value = 'goodbye';
|
|
48
|
+
|
|
49
|
+
console.log(input.value); // 'goodbye'
|
|
50
|
+
|
|
51
|
+
const checkbox = document.createElement('input');
|
|
52
|
+
checkbox.type = 'checkbox';
|
|
53
|
+
checkbox.set[value] = true;
|
|
54
|
+
console.log(checkbox.checked); // true
|
|
55
|
+
|
|
56
|
+
const div = document.createElement('div');
|
|
57
|
+
div.set[value] = 'content';
|
|
58
|
+
console.log(div.textContent); // 'content'
|
|
59
|
+
|
|
60
|
+
const time = document.createElement('time');
|
|
61
|
+
time.set[value] = '2024-01-01T00:00:00Z';
|
|
62
|
+
console.log(time.dateTime); // '2024-01-01T00:00:00Z'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Value Property Inference
|
|
66
|
+
|
|
67
|
+
The `value` symbol automatically maps to the most appropriate property for each element type:
|
|
68
|
+
|
|
69
|
+
| Element Type | Property Set | Example |
|
|
70
|
+
|-------------|-------------|---------|
|
|
71
|
+
| `<input type="text">` | `value` | Text input value |
|
|
72
|
+
| `<input type="checkbox">` | `checked` | Checkbox state |
|
|
73
|
+
| `<input type="radio">` | `checked` | Radio button state |
|
|
74
|
+
| `<textarea>` | `value` | Textarea content |
|
|
75
|
+
| `<select>` | `value` | Selected option |
|
|
76
|
+
| `<time>` | `dateTime` | ISO datetime string |
|
|
77
|
+
| `<data>` | `value` | Machine-readable value |
|
|
78
|
+
| `<meter>` | `value` | Numeric value |
|
|
79
|
+
| `<progress>` | `value` | Progress value |
|
|
80
|
+
| `<output>` | `value` | Output value |
|
|
81
|
+
| Elements with `itemprop` | `itemprop` value | Custom property name |
|
|
82
|
+
| Other elements | `textContent` | Text content |
|
|
83
|
+
|
|
84
|
+
## Display Property Inference
|
|
85
|
+
|
|
86
|
+
The `display` symbol sets the human-readable display value:
|
|
87
|
+
|
|
88
|
+
```TypeScript
|
|
89
|
+
// Time element - display formatted time
|
|
90
|
+
const time = document.createElement('time');
|
|
91
|
+
time.set[value] = '2024-01-01T00:00:00Z'; // Machine-readable
|
|
92
|
+
time.set[display] = 'January 1, 2024'; // Human-readable
|
|
93
|
+
console.log(time.dateTime); // '2024-01-01T00:00:00Z'
|
|
94
|
+
console.log(time.textContent); // 'January 1, 2024'
|
|
95
|
+
|
|
96
|
+
// Meter element - display with ARIA
|
|
97
|
+
const meter = document.createElement('meter');
|
|
98
|
+
meter.min = 0;
|
|
99
|
+
meter.max = 100;
|
|
100
|
+
meter.set[value] = 75; // Numeric value
|
|
101
|
+
meter.set[display] = '75 percent'; // Screen reader text
|
|
102
|
+
console.log(meter.value); // 75
|
|
103
|
+
console.log(meter.ariaValueText); // '75 percent'
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
| Element Type | Property Set | Example |
|
|
107
|
+
|-------------|-------------|---------|
|
|
108
|
+
| `<input>`, `<textarea>`, `<select>` | `value` | Form control value |
|
|
109
|
+
| `<time>` | `textContent` | Formatted time string |
|
|
110
|
+
| `<data>` | `textContent` | Human-readable content |
|
|
111
|
+
| `<meter>`, `<progress>` | `ariaValueText` | Screen reader text |
|
|
112
|
+
| Other elements | `textContent` | Text content |
|
|
113
|
+
|
|
114
|
+
## Event Type Inference
|
|
115
|
+
|
|
116
|
+
The `inferEventType` function returns the most appropriate event type for different element types:
|
|
117
|
+
|
|
118
|
+
```TypeScript
|
|
119
|
+
import { inferEventType } from 'inferencer';
|
|
120
|
+
|
|
121
|
+
const input = document.createElement('input');
|
|
122
|
+
console.log(inferEventType(input)); // 'input'
|
|
123
|
+
|
|
124
|
+
const form = document.createElement('form');
|
|
125
|
+
console.log(inferEventType(form)); // 'submit'
|
|
126
|
+
|
|
127
|
+
const button = document.createElement('button');
|
|
128
|
+
console.log(inferEventType(button)); // 'click'
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
| Element Type | Event Type | Use Case |
|
|
132
|
+
|-------------|-----------|----------|
|
|
133
|
+
| `<input>`, `<textarea>`, `<select>` | `input` | Form control value changes |
|
|
134
|
+
| `<form>` | `submit` | Form submission |
|
|
135
|
+
| `<details>` | `toggle` | Details element open/close |
|
|
136
|
+
| `<dialog>` | `close` | Dialog dismissal |
|
|
137
|
+
| Other elements | `click` | Default interactive event |
|
|
138
|
+
|
|
139
|
+
**Accessing via Enhancement Instance:**
|
|
140
|
+
|
|
141
|
+
```TypeScript
|
|
142
|
+
const input = document.createElement('input');
|
|
143
|
+
input.set[value] = 'test';
|
|
144
|
+
|
|
145
|
+
console.log(input.enh.infer.eventType); // 'input'
|
|
146
|
+
|
|
147
|
+
const form = document.createElement('form');
|
|
148
|
+
form.set[value] = 'test';
|
|
149
|
+
|
|
150
|
+
console.log(form.enh.infer.eventType); // 'submit'
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
This is particularly useful when building enhancements that need to attach event listeners but don't know the element type in advance.
|
|
154
|
+
|
|
155
|
+
## Propagator Support
|
|
156
|
+
|
|
157
|
+
The inferencer provides a `getPropagator()` method that returns an `EventTarget` which emits events when element properties change. This solves a common challenge: subscribing to property changes across different element types, whether the change is user-driven or programmatic.
|
|
158
|
+
|
|
159
|
+
### Basic Usage
|
|
160
|
+
|
|
161
|
+
```TypeScript
|
|
162
|
+
import { Infer } from 'inferencer/inferencer.js';
|
|
163
|
+
|
|
164
|
+
const input = document.createElement('input');
|
|
165
|
+
const infer = new Infer(input);
|
|
166
|
+
const propagator = await infer.getPropagator();
|
|
167
|
+
|
|
168
|
+
// Listen for value changes — works for both user input and programmatic sets
|
|
169
|
+
propagator.addEventListener('value', () => {
|
|
170
|
+
console.log('value changed to:', input.value);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
input.value = 'programmatic change'; // fires the listener
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
The event name matches the property name being observed. Observation is lazy — wiring only happens for properties you actually subscribe to.
|
|
177
|
+
|
|
178
|
+
### How It Works
|
|
179
|
+
|
|
180
|
+
For **custom elements with a [roundabout](https://github.com/bahrus/roundabout) propagator**, `getPropagator()` returns the element's native propagator directly — no inference needed.
|
|
181
|
+
|
|
182
|
+
For all other elements, it creates an `InferencedPropagator` that uses the best available strategy to detect changes:
|
|
183
|
+
|
|
184
|
+
| Strategy | When Used | Mechanism |
|
|
185
|
+
|----------|-----------|-----------|
|
|
186
|
+
| Attribute observation | Properties that reflect to attributes (aria-*, href, src) | MutationObserver |
|
|
187
|
+
| Hybrid event + setter | Form controls (input, textarea, select) | Native event + setter interception |
|
|
188
|
+
| Setter interception | Custom elements with prototype setters | Instance property override |
|
|
189
|
+
| Polling | Everything else (last resort) | requestAnimationFrame dirty check |
|
|
190
|
+
|
|
191
|
+
### Attribute-Reflected Properties
|
|
192
|
+
|
|
193
|
+
Properties like `ariaLabel`, `href`, and `src` that reflect to DOM attributes are observed via MutationObserver:
|
|
194
|
+
|
|
195
|
+
```TypeScript
|
|
196
|
+
const anchor = document.createElement('a');
|
|
197
|
+
const infer = new Infer(anchor);
|
|
198
|
+
const propagator = await infer.getPropagator();
|
|
199
|
+
|
|
200
|
+
propagator.addEventListener('href', () => {
|
|
201
|
+
console.log('href changed to:', anchor.href);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
anchor.href = 'https://example.com'; // fires the listener
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Form Controls (Hybrid Strategy)
|
|
208
|
+
|
|
209
|
+
For `<input>`, `<textarea>`, and `<select>`, changes can come from user interaction (which fires native events) or programmatic assignment (which doesn't). The propagator handles both:
|
|
210
|
+
|
|
211
|
+
```TypeScript
|
|
212
|
+
const input = document.createElement('input');
|
|
213
|
+
const infer = new Infer(input);
|
|
214
|
+
const propagator = await infer.getPropagator();
|
|
215
|
+
|
|
216
|
+
propagator.addEventListener('value', () => {
|
|
217
|
+
console.log('value is now:', input.value);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// User types → native 'input' event → propagator fires
|
|
221
|
+
// Programmatic: input.value = 'hello' → setter intercept → propagator fires
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Custom Elements
|
|
225
|
+
|
|
226
|
+
For custom elements with standard getters/setters on the prototype, setter interception catches all programmatic changes:
|
|
227
|
+
|
|
228
|
+
```TypeScript
|
|
229
|
+
class MyCounter extends HTMLElement {
|
|
230
|
+
#count = 0;
|
|
231
|
+
get count() { return this.#count; }
|
|
232
|
+
set count(v) { this.#count = v; }
|
|
233
|
+
}
|
|
234
|
+
customElements.define('my-counter', MyCounter);
|
|
235
|
+
|
|
236
|
+
const el = document.createElement('my-counter');
|
|
237
|
+
const infer = new Infer(el);
|
|
238
|
+
const propagator = await infer.getPropagator();
|
|
239
|
+
|
|
240
|
+
propagator.addEventListener('count', () => {
|
|
241
|
+
console.log('count changed to:', el.count);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
el.count = 5; // fires the listener
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Roundabout Elements
|
|
248
|
+
|
|
249
|
+
Custom elements that implement a [roundabout property](https://github.com/bahrus/roundabout) already have a native propagator. `getPropagator()` detects this and returns it directly:
|
|
250
|
+
|
|
251
|
+
```TypeScript
|
|
252
|
+
// Element with native propagator — no inference needed
|
|
253
|
+
const el = document.createElement('my-roundabout-element');
|
|
254
|
+
const infer = new Infer(el);
|
|
255
|
+
const propagator = await infer.getPropagator();
|
|
256
|
+
|
|
257
|
+
// This IS the element's own propagator, not an inferred one
|
|
258
|
+
propagator.addEventListener('someProperty', handler);
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Cleanup
|
|
262
|
+
|
|
263
|
+
Call `destroy()` to tear down all watchers when you're done:
|
|
264
|
+
|
|
265
|
+
```TypeScript
|
|
266
|
+
const propagator = await infer.getPropagator();
|
|
267
|
+
propagator.addEventListener('value', handler);
|
|
268
|
+
|
|
269
|
+
// Later, when no longer needed:
|
|
270
|
+
propagator.destroy();
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Using InferencedPropagator Directly
|
|
274
|
+
|
|
275
|
+
You can also import and use `InferencedPropagator` directly if you already have an `Infer` instance:
|
|
276
|
+
|
|
277
|
+
```TypeScript
|
|
278
|
+
import { InferencedPropagator } from 'inferencer/InferencedPropagator.js';
|
|
279
|
+
import { Infer } from 'inferencer/inferencer.js';
|
|
280
|
+
|
|
281
|
+
const element = document.createElement('input');
|
|
282
|
+
const infer = new Infer(element);
|
|
283
|
+
const propagator = new InferencedPropagator(infer);
|
|
284
|
+
|
|
285
|
+
propagator.addEventListener('value', () => {
|
|
286
|
+
console.log('changed:', element.value);
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Accessing the Enhancement Instance
|
|
291
|
+
|
|
292
|
+
The inferencer enhancement is accessible via `element.enh.infer`:
|
|
293
|
+
|
|
294
|
+
```TypeScript
|
|
295
|
+
const input = document.createElement('input');
|
|
296
|
+
input.set[value] = 'test';
|
|
297
|
+
|
|
298
|
+
// Access the enhancement instance
|
|
299
|
+
console.log(input.enh.infer.value); // 'test' (cached value)
|
|
300
|
+
|
|
301
|
+
// The instance maintains references to both value and display
|
|
302
|
+
input.set[display] = 'Test Display';
|
|
303
|
+
console.log(input.enh.infer.value); // 'test'
|
|
304
|
+
console.log(input.enh.infer.display); // 'Test Display'
|
|
305
|
+
console.log(input.enh.infer.eventType); // 'input'
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Using with assignGingerly
|
|
309
|
+
|
|
310
|
+
The inferencer enhancement integrates seamlessly with `assignGingerly`:
|
|
311
|
+
|
|
312
|
+
```TypeScript
|
|
313
|
+
import { value, display } from 'inferencer';
|
|
314
|
+
import 'assign-gingerly/object-extension.js';
|
|
315
|
+
|
|
316
|
+
const element = document.createElement('input');
|
|
317
|
+
element.type = 'text';
|
|
318
|
+
|
|
319
|
+
// Use symbols in assignGingerly
|
|
320
|
+
element.assignGingerly({
|
|
321
|
+
[value]: 'hello world',
|
|
322
|
+
style: {
|
|
323
|
+
color: 'blue'
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
console.log(element.value); // 'hello world'
|
|
328
|
+
console.log(element.style.color); // 'blue'
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Itemprop Support
|
|
332
|
+
|
|
333
|
+
Elements with an `itemprop` attribute use that attribute's value as the property name:
|
|
334
|
+
|
|
335
|
+
```html
|
|
336
|
+
<span itemprop="title"></span>
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
```TypeScript
|
|
340
|
+
const span = document.querySelector('[itemprop="title"]');
|
|
341
|
+
span.set[value] = 'My Title';
|
|
342
|
+
console.log(span.title); // 'My Title'
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Helper Functions
|
|
346
|
+
|
|
347
|
+
The inferencer module exports helper functions for manual property and event type inference:
|
|
348
|
+
|
|
349
|
+
```TypeScript
|
|
350
|
+
import { inferValueProperty, inferDisplayProperty, inferEventType } from 'inferencer';
|
|
351
|
+
|
|
352
|
+
const input = document.createElement('input');
|
|
353
|
+
input.type = 'checkbox';
|
|
354
|
+
|
|
355
|
+
const valueProp = inferValueProperty(input);
|
|
356
|
+
console.log(valueProp); // 'checked'
|
|
357
|
+
|
|
358
|
+
const displayProp = inferDisplayProperty(input);
|
|
359
|
+
console.log(displayProp); // 'value'
|
|
360
|
+
|
|
361
|
+
const eventType = inferEventType(input);
|
|
362
|
+
console.log(eventType); // 'input'
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
These functions can be useful when you need to determine the property or event type name without actually setting a value or attaching a listener.
|
|
366
|
+
|
|
367
|
+
## Implementation Details
|
|
368
|
+
|
|
369
|
+
The inferencer enhancement is implemented as a standard enhancement class:
|
|
370
|
+
|
|
371
|
+
```TypeScript
|
|
372
|
+
class Infer<TValue = any, TDisplay = any> {
|
|
373
|
+
#weakRef: WeakRef<Element>;
|
|
374
|
+
|
|
375
|
+
constructor(enhancedElement?: Element) {
|
|
376
|
+
this.#weakRef = new WeakRef(enhancedElement!);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
get value(): TValue | undefined { /* ... */ }
|
|
380
|
+
set value(nv: TValue) {
|
|
381
|
+
const element = this.#weakRef.deref()!;
|
|
382
|
+
element[inferValueProperty(element)] = nv;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
get display(): TDisplay | undefined { /* ... */ }
|
|
386
|
+
set display(nv: TDisplay) {
|
|
387
|
+
const element = this.#weakRef.deref()!;
|
|
388
|
+
element[inferDisplayProperty(element)] = nv;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
get eventType(): string {
|
|
392
|
+
return inferEventType(this.enhancedElement);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
**Registry Configuration:**
|
|
398
|
+
|
|
399
|
+
```TypeScript
|
|
400
|
+
export const registryItem: EnhancementConfig = {
|
|
401
|
+
spawn: Infer,
|
|
402
|
+
enhKey: 'infer',
|
|
403
|
+
symlinks: {
|
|
404
|
+
[value]: 'value',
|
|
405
|
+
[display]: 'display'
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
The `symlinks` mapping connects the symbols to the enhancement's properties, enabling the `element.set[symbol]` syntax.
|
|
411
|
+
|
|
412
|
+
## Benefits
|
|
413
|
+
|
|
414
|
+
1. **Type-agnostic code**: Write code that works with any element type without conditionals
|
|
415
|
+
2. **Cleaner syntax**: No need to remember which property each element type uses
|
|
416
|
+
3. **Accessibility**: Separate value and display properties support screen readers
|
|
417
|
+
4. **Framework-friendly**: Symbols work well with reactive frameworks and data binding
|
|
418
|
+
5. **Extensible**: Based on the enhancement registry system, can be customized or extended
|
|
419
|
+
6. **Event inference**: Automatically determine the most appropriate event type for any element
|
|
420
|
+
|
|
421
|
+
## Complete Example
|
|
422
|
+
|
|
423
|
+
```html
|
|
424
|
+
<!DOCTYPE html>
|
|
425
|
+
<html>
|
|
426
|
+
<head>
|
|
427
|
+
<script type="module">
|
|
428
|
+
import { value, display, registryItem } from 'inferencer';
|
|
429
|
+
import 'assign-gingerly/object-extension.js';
|
|
430
|
+
|
|
431
|
+
// Register the enhancement
|
|
432
|
+
customElements.enhancementRegistry.push(registryItem);
|
|
433
|
+
|
|
434
|
+
// Create various elements
|
|
435
|
+
const input = document.createElement('input');
|
|
436
|
+
input.type = 'text';
|
|
437
|
+
|
|
438
|
+
const checkbox = document.createElement('input');
|
|
439
|
+
checkbox.type = 'checkbox';
|
|
440
|
+
|
|
441
|
+
const time = document.createElement('time');
|
|
442
|
+
|
|
443
|
+
const meter = document.createElement('meter');
|
|
444
|
+
meter.min = 0;
|
|
445
|
+
meter.max = 100;
|
|
446
|
+
|
|
447
|
+
// Set values using the same symbol - each element handles it correctly
|
|
448
|
+
input.set[value] = 'Hello World';
|
|
449
|
+
checkbox.set[value] = true;
|
|
450
|
+
time.set[value] = '2024-01-01T00:00:00Z';
|
|
451
|
+
time.set[display] = 'January 1, 2024';
|
|
452
|
+
meter.set[value] = 75;
|
|
453
|
+
meter.set[display] = '75 percent';
|
|
454
|
+
|
|
455
|
+
// Add to document
|
|
456
|
+
document.body.append(input, checkbox, time, meter);
|
|
457
|
+
|
|
458
|
+
console.log('Input value:', input.value); // 'Hello World'
|
|
459
|
+
console.log('Checkbox checked:', checkbox.checked); // true
|
|
460
|
+
console.log('Time dateTime:', time.dateTime); // '2024-01-01T00:00:00Z'
|
|
461
|
+
console.log('Time display:', time.textContent); // 'January 1, 2024'
|
|
462
|
+
console.log('Meter value:', meter.value); // 75
|
|
463
|
+
console.log('Meter display:', meter.ariaValueText); // '75 percent'
|
|
464
|
+
|
|
465
|
+
// Event type inference
|
|
466
|
+
console.log('Input event:', input.enh.infer.eventType); // 'input'
|
|
467
|
+
console.log('Meter event:', meter.enh.infer.eventType); // 'click'
|
|
468
|
+
</script>
|
|
469
|
+
</head>
|
|
470
|
+
<body>
|
|
471
|
+
<h1>Inferencer Enhancement Demo</h1>
|
|
472
|
+
</body>
|
|
473
|
+
</html>
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
## Browser Support
|
|
477
|
+
|
|
478
|
+
The inferencer enhancement requires:
|
|
479
|
+
- Chrome 146+ (for scoped custom element registries)
|
|
480
|
+
- Modern browsers with Symbol support
|
|
481
|
+
- WeakRef support (all modern browsers)
|
|
482
|
+
|
|
483
|
+
For browsers without scoped registry support, the enhancement falls back to the global `customElements.enhancementRegistry`.
|
|
484
|
+
|
|
485
|
+
## API Reference
|
|
486
|
+
|
|
487
|
+
### Exports
|
|
488
|
+
|
|
489
|
+
- `value: symbol` - Symbol for smart value assignment
|
|
490
|
+
- `display: symbol` - Symbol for smart display assignment
|
|
491
|
+
- `registryItem: EnhancementConfig` - Registry configuration for the enhancement
|
|
492
|
+
- `Infer: class` - The enhancement class
|
|
493
|
+
- `inferValueProperty(element: Element): string` - Helper function to infer value property name
|
|
494
|
+
- `inferDisplayProperty(element: Element): string` - Helper function to infer display property name
|
|
495
|
+
- `inferEventType(element: Element): string` - Helper function to infer event type name
|
|
496
|
+
- `InferencedPropagator: class` - (from `inferencer/InferencedPropagator.js`) EventTarget that infers property change detection
|
|
497
|
+
|
|
498
|
+
## License
|
|
499
|
+
|
|
500
|
+
MIT
|
|
501
|
+
|
|
502
|
+
## Related Packages
|
|
503
|
+
|
|
504
|
+
- [assign-gingerly](https://www.npmjs.com/package/assign-gingerly) - Required dependency for enhancement registry support
|
|
505
|
+
|
|
506
|
+
## Viewing Locally
|
|
507
|
+
|
|
508
|
+
Any web server that serves static files with server-side includes will do but...
|
|
509
|
+
|
|
510
|
+
1. Install git
|
|
511
|
+
2. Fork/clone this repo
|
|
512
|
+
3. Install node.js
|
|
513
|
+
4. Open command window to folder where you cloned this repo
|
|
514
|
+
5. > git submodule add https://github.com/bahrus/types.git types
|
|
515
|
+
6. > git submodule update --init --recursive
|
|
516
|
+
7. > npm install
|
|
517
|
+
8. > npm run serve
|
|
518
|
+
9. Open http://localhost:8000/demo/ in a modern browser
|
|
519
|
+
|
|
520
|
+
## Running Tests
|
|
521
|
+
|
|
522
|
+
```
|
|
523
|
+
> npm run test
|
|
524
|
+
```
|