assign-gingerly 0.0.30 → 0.0.31
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 +44 -329
- package/package.json +3 -7
- package/playwright.config.ts +1 -1
- package/types/assign-gingerly/types.d.ts +1 -0
- package/Infer.js +0 -154
- package/Infer.ts +0 -190
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
# assign-gingerly and assign-tentatively
|
|
2
2
|
|
|
3
3
|
[](https://github.com/bahrus/assign-gingerly/actions/workflows/CI.yml)
|
|
4
4
|
[](http://badge.fury.io/js/assign-gingerly)
|
|
@@ -1460,19 +1460,19 @@ element.remove();
|
|
|
1460
1460
|
|
|
1461
1461
|
// Case 1: Temporarily removed, will be re-added
|
|
1462
1462
|
setTimeout(() => document.body.append(element), 1000);
|
|
1463
|
-
//
|
|
1463
|
+
// ? Don't dispose - enhancement should persist
|
|
1464
1464
|
|
|
1465
1465
|
// Case 2: Moved to another location
|
|
1466
1466
|
otherContainer.append(element);
|
|
1467
|
-
//
|
|
1467
|
+
// ? Don't dispose - enhancement should persist
|
|
1468
1468
|
|
|
1469
1469
|
// Case 3: Cached for reuse
|
|
1470
1470
|
elementCache.set('myElement', element);
|
|
1471
|
-
//
|
|
1471
|
+
// ? Don't dispose - enhancement should persist
|
|
1472
1472
|
|
|
1473
1473
|
// Case 4: Truly done, ready for GC
|
|
1474
1474
|
element = null;
|
|
1475
|
-
//
|
|
1475
|
+
// ? Should dispose, but no way to detect this automatically
|
|
1476
1476
|
```
|
|
1477
1477
|
|
|
1478
1478
|
**Practical disposal strategies:**
|
|
@@ -1566,10 +1566,10 @@ class MyEnhancement {
|
|
|
1566
1566
|
```
|
|
1567
1567
|
|
|
1568
1568
|
**Summary:**
|
|
1569
|
-
-
|
|
1570
|
-
-
|
|
1571
|
-
-
|
|
1572
|
-
-
|
|
1569
|
+
- ? Storage mechanism prevents memory leaks via WeakMap
|
|
1570
|
+
- ?? Enhancement internals need manual cleanup via dispose()
|
|
1571
|
+
- ? No automatic way to detect when disposal should happen
|
|
1572
|
+
- ?? Choose disposal strategy based on your application's lifecycle
|
|
1573
1573
|
|
|
1574
1574
|
### Waiting for Async Initialization with `enh.whenResolved(regItem)`
|
|
1575
1575
|
|
|
@@ -1976,7 +1976,7 @@ console.log(instance.value); // 'test123' (parsed from attribute)
|
|
|
1976
1976
|
|
|
1977
1977
|
</details>
|
|
1978
1978
|
|
|
1979
|
-
> !
|
|
1979
|
+
> [!NOTE]
|
|
1980
1980
|
> `withAttrs` works with or without `enhKey`. When there's no `enhKey`, the parsed attributes are passed directly to the constructor. When there is an `enhKey`, they're merged with any pre-existing values on the enh container.
|
|
1981
1981
|
|
|
1982
1982
|
### The `enh-` Prefix for Attribute Isolation
|
|
@@ -2150,7 +2150,7 @@ The `base` attribute must contain either a dash (`-`) or a non-ASCII character t
|
|
|
2150
2150
|
```TypeScript
|
|
2151
2151
|
// Valid base attributes
|
|
2152
2152
|
const enhConfig1 = { base: 'data-config' }; // Has dash
|
|
2153
|
-
const enhConfig2 = { base: '
|
|
2153
|
+
const enhConfig2 = { base: '??-theme' }); // Has non-ASCII (and dash)
|
|
2154
2154
|
|
|
2155
2155
|
// Invalid - throws error
|
|
2156
2156
|
const enhConig3 = { base: 'config' }; // No dash or non-ASCII
|
|
@@ -2198,6 +2198,33 @@ const result = parseWithAttrs(element, {
|
|
|
2198
2198
|
// Result: { name: 'Alice', age: '30' }
|
|
2199
2199
|
```
|
|
2200
2200
|
|
|
2201
|
+
**Deep Nesting:**
|
|
2202
|
+
|
|
2203
|
+
Template variables can reference other template variables to any depth, creating hierarchical attribute naming patterns:
|
|
2204
|
+
|
|
2205
|
+
```TypeScript
|
|
2206
|
+
// HTML: <div data-app-user-profile-name="Alice" data-app-user-profile-email="alice@example.com"></div>
|
|
2207
|
+
|
|
2208
|
+
const result = parseWithAttrs(element, {
|
|
2209
|
+
base: 'data-',
|
|
2210
|
+
app: '${base}app',
|
|
2211
|
+
user: '${app}-user',
|
|
2212
|
+
profile: '${user}-profile',
|
|
2213
|
+
name: '${profile}-name',
|
|
2214
|
+
email: '${profile}-email'
|
|
2215
|
+
});
|
|
2216
|
+
// Result: { name: 'Alice', email: 'alice@example.com' }
|
|
2217
|
+
|
|
2218
|
+
// The resolution chain: base ? app ? user ? profile ? name/email
|
|
2219
|
+
// Resolves to: data-app-user-profile-name and data-app-user-profile-email
|
|
2220
|
+
```
|
|
2221
|
+
|
|
2222
|
+
**Benefits of hierarchical variables:**
|
|
2223
|
+
- Build complex attribute names from simple parts
|
|
2224
|
+
- Maintain consistency across related attributes
|
|
2225
|
+
- Easy to refactor by changing a single variable
|
|
2226
|
+
- Self-documenting attribute structure
|
|
2227
|
+
|
|
2201
2228
|
Template variables are resolved recursively and cached for performance. Circular references are detected and throw an error.
|
|
2202
2229
|
|
|
2203
2230
|
### Type Parsing with instanceOf
|
|
@@ -2376,12 +2403,12 @@ registerCommonParsers(globalParserRegistry);
|
|
|
2376
2403
|
|
|
2377
2404
|
**Benefits of Named Parsers:**
|
|
2378
2405
|
|
|
2379
|
-
-
|
|
2380
|
-
-
|
|
2381
|
-
-
|
|
2382
|
-
-
|
|
2383
|
-
-
|
|
2384
|
-
-
|
|
2406
|
+
- ? **JSON serializable** - Configs can be stored/transmitted as JSON
|
|
2407
|
+
- ? **Reusable** - Define once, use everywhere
|
|
2408
|
+
- ? **Maintainable** - Update parser logic in one place
|
|
2409
|
+
- ? **Testable** - Test parsers independently
|
|
2410
|
+
- ? **Discoverable** - `globalParserRegistry.getNames()` lists all available parsers
|
|
2411
|
+
- ? **Backward compatible** - Inline functions still work
|
|
2385
2412
|
|
|
2386
2413
|
**Mixing Inline and Named Parsers:**
|
|
2387
2414
|
|
|
@@ -3408,315 +3435,3 @@ ItemScope Managers follow these design principles:
|
|
|
3408
3435
|
|
|
3409
3436
|
This design ensures backward compatibility while providing powerful new capabilities for managing DOM fragments.
|
|
3410
3437
|
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
## Smart Value Assignment with Infer Enhancement
|
|
3415
|
-
|
|
3416
|
-
The Infer enhancement provides a symbol-based 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), the Infer enhancement automatically infers the correct property based on the element type.
|
|
3417
|
-
|
|
3418
|
-
### Why Infer?
|
|
3419
|
-
|
|
3420
|
-
Different HTML elements use different properties to represent their value:
|
|
3421
|
-
- Input text fields use `value`
|
|
3422
|
-
- Checkboxes and radio buttons use `checked`
|
|
3423
|
-
- Time elements use `dateTime`
|
|
3424
|
-
- Divs and spans use `textContent`
|
|
3425
|
-
- Progress and meter elements use `value` but display with `ariaValueText`
|
|
3426
|
-
|
|
3427
|
-
The Infer enhancement eliminates the need to remember these differences by providing two symbols that automatically map to the correct property:
|
|
3428
|
-
|
|
3429
|
-
- `value` symbol - Sets the element's data value
|
|
3430
|
-
- `display` symbol - Sets the element's display/presentation value
|
|
3431
|
-
|
|
3432
|
-
### Basic Usage
|
|
3433
|
-
|
|
3434
|
-
```TypeScript
|
|
3435
|
-
import { value, display, registryItem } from 'assign-gingerly/Infer.js';
|
|
3436
|
-
import 'assign-gingerly/object-extension.js';
|
|
3437
|
-
|
|
3438
|
-
// Register the Infer enhancement
|
|
3439
|
-
customElements.enhancementRegistry.push(registryItem);
|
|
3440
|
-
|
|
3441
|
-
// Use the value symbol - automatically sets the right property
|
|
3442
|
-
const input = document.createElement('input');
|
|
3443
|
-
input.type = 'text';
|
|
3444
|
-
input.set[value] = 'hello';
|
|
3445
|
-
console.log(input.value); // 'hello'
|
|
3446
|
-
|
|
3447
|
-
const checkbox = document.createElement('input');
|
|
3448
|
-
checkbox.type = 'checkbox';
|
|
3449
|
-
checkbox.set[value] = true;
|
|
3450
|
-
console.log(checkbox.checked); // true
|
|
3451
|
-
|
|
3452
|
-
const div = document.createElement('div');
|
|
3453
|
-
div.set[value] = 'content';
|
|
3454
|
-
console.log(div.textContent); // 'content'
|
|
3455
|
-
|
|
3456
|
-
const time = document.createElement('time');
|
|
3457
|
-
time.set[value] = '2024-01-01T00:00:00Z';
|
|
3458
|
-
console.log(time.dateTime); // '2024-01-01T00:00:00Z'
|
|
3459
|
-
```
|
|
3460
|
-
|
|
3461
|
-
### Value Property Inference
|
|
3462
|
-
|
|
3463
|
-
The `value` symbol automatically maps to the most appropriate property for each element type:
|
|
3464
|
-
|
|
3465
|
-
| Element Type | Property Set | Example |
|
|
3466
|
-
|-------------|-------------|---------|
|
|
3467
|
-
| `<input type="text">` | `value` | Text input value |
|
|
3468
|
-
| `<input type="checkbox">` | `checked` | Checkbox state |
|
|
3469
|
-
| `<input type="radio">` | `checked` | Radio button state |
|
|
3470
|
-
| `<textarea>` | `value` | Textarea content |
|
|
3471
|
-
| `<select>` | `value` | Selected option |
|
|
3472
|
-
| `<time>` | `dateTime` | ISO datetime string |
|
|
3473
|
-
| `<data>` | `value` | Machine-readable value |
|
|
3474
|
-
| `<meter>` | `value` | Numeric value |
|
|
3475
|
-
| `<progress>` | `value` | Progress value |
|
|
3476
|
-
| `<output>` | `value` | Output value |
|
|
3477
|
-
| Elements with `itemprop` | `itemprop` value | Custom property name |
|
|
3478
|
-
| Other elements | `textContent` | Text content |
|
|
3479
|
-
|
|
3480
|
-
### Display Property Inference
|
|
3481
|
-
|
|
3482
|
-
The `display` symbol sets the human-readable display value:
|
|
3483
|
-
|
|
3484
|
-
```TypeScript
|
|
3485
|
-
// Time element - display formatted time
|
|
3486
|
-
const time = document.createElement('time');
|
|
3487
|
-
time.set[value] = '2024-01-01T00:00:00Z'; // Machine-readable
|
|
3488
|
-
time.set[display] = 'January 1, 2024'; // Human-readable
|
|
3489
|
-
console.log(time.dateTime); // '2024-01-01T00:00:00Z'
|
|
3490
|
-
console.log(time.textContent); // 'January 1, 2024'
|
|
3491
|
-
|
|
3492
|
-
// Meter element - display with ARIA
|
|
3493
|
-
const meter = document.createElement('meter');
|
|
3494
|
-
meter.min = 0;
|
|
3495
|
-
meter.max = 100;
|
|
3496
|
-
meter.set[value] = 75; // Numeric value
|
|
3497
|
-
meter.set[display] = '75 percent'; // Screen reader text
|
|
3498
|
-
console.log(meter.value); // 75
|
|
3499
|
-
console.log(meter.ariaValueText); // '75 percent'
|
|
3500
|
-
```
|
|
3501
|
-
|
|
3502
|
-
| Element Type | Property Set | Example |
|
|
3503
|
-
|-------------|-------------|---------|
|
|
3504
|
-
| `<input>`, `<textarea>`, `<select>` | `value` | Form control value |
|
|
3505
|
-
| `<time>` | `textContent` | Formatted time string |
|
|
3506
|
-
| `<data>` | `textContent` | Human-readable content |
|
|
3507
|
-
| `<meter>`, `<progress>` | `ariaValueText` | Screen reader text |
|
|
3508
|
-
| Other elements | `textContent` | Text content |
|
|
3509
|
-
|
|
3510
|
-
### Accessing the Enhancement Instance
|
|
3511
|
-
|
|
3512
|
-
The Infer enhancement is accessible via `element.enh.infer`:
|
|
3513
|
-
|
|
3514
|
-
```TypeScript
|
|
3515
|
-
const input = document.createElement('input');
|
|
3516
|
-
input.set[value] = 'test';
|
|
3517
|
-
|
|
3518
|
-
// Access the enhancement instance
|
|
3519
|
-
console.log(input.enh.infer.value); // 'test' (cached value)
|
|
3520
|
-
|
|
3521
|
-
// The instance maintains references to both value and display
|
|
3522
|
-
input.set[display] = 'Test Display';
|
|
3523
|
-
console.log(input.enh.infer.value); // 'test'
|
|
3524
|
-
console.log(input.enh.infer.display); // 'Test Display'
|
|
3525
|
-
```
|
|
3526
|
-
|
|
3527
|
-
### Using with assignGingerly
|
|
3528
|
-
|
|
3529
|
-
The Infer enhancement integrates seamlessly with `assignGingerly`:
|
|
3530
|
-
|
|
3531
|
-
```TypeScript
|
|
3532
|
-
import { value, display } from 'assign-gingerly/Infer.js';
|
|
3533
|
-
|
|
3534
|
-
const element = document.createElement('input');
|
|
3535
|
-
element.type = 'text';
|
|
3536
|
-
|
|
3537
|
-
// Use symbols in assignGingerly
|
|
3538
|
-
element.assignGingerly({
|
|
3539
|
-
[value]: 'hello world',
|
|
3540
|
-
style: {
|
|
3541
|
-
color: 'blue'
|
|
3542
|
-
}
|
|
3543
|
-
});
|
|
3544
|
-
|
|
3545
|
-
console.log(element.value); // 'hello world'
|
|
3546
|
-
console.log(element.style.color); // 'blue'
|
|
3547
|
-
```
|
|
3548
|
-
|
|
3549
|
-
### Itemprop Support
|
|
3550
|
-
|
|
3551
|
-
Elements with an `itemprop` attribute use that attribute's value as the property name:
|
|
3552
|
-
|
|
3553
|
-
```html
|
|
3554
|
-
<span itemprop="title"></span>
|
|
3555
|
-
```
|
|
3556
|
-
|
|
3557
|
-
```TypeScript
|
|
3558
|
-
const span = document.querySelector('[itemprop="title"]');
|
|
3559
|
-
span.set[value] = 'My Title';
|
|
3560
|
-
console.log(span.title); // 'My Title'
|
|
3561
|
-
```
|
|
3562
|
-
|
|
3563
|
-
### Implementation Details
|
|
3564
|
-
|
|
3565
|
-
The Infer enhancement is implemented as a standard enhancement class:
|
|
3566
|
-
|
|
3567
|
-
```TypeScript
|
|
3568
|
-
class Infer<TValue = any, TDisplay = any> {
|
|
3569
|
-
#weakRef: WeakRef<Element>;
|
|
3570
|
-
|
|
3571
|
-
constructor(enhancedElement?: Element) {
|
|
3572
|
-
this.#weakRef = new WeakRef(enhancedElement!);
|
|
3573
|
-
}
|
|
3574
|
-
|
|
3575
|
-
get value(): TValue | undefined { /* ... */ }
|
|
3576
|
-
set value(nv: TValue) {
|
|
3577
|
-
const element = this.#weakRef.deref()!;
|
|
3578
|
-
element[inferValueProperty(element)] = nv;
|
|
3579
|
-
}
|
|
3580
|
-
|
|
3581
|
-
get display(): TDisplay | undefined { /* ... */ }
|
|
3582
|
-
set display(nv: TDisplay) {
|
|
3583
|
-
const element = this.#weakRef.deref()!;
|
|
3584
|
-
element[inferDisplayProperty(element)] = nv;
|
|
3585
|
-
}
|
|
3586
|
-
}
|
|
3587
|
-
```
|
|
3588
|
-
|
|
3589
|
-
**Registry Configuration:**
|
|
3590
|
-
|
|
3591
|
-
```TypeScript
|
|
3592
|
-
export const registryItem: EnhancementConfig = {
|
|
3593
|
-
spawn: Infer,
|
|
3594
|
-
enhKey: 'infer',
|
|
3595
|
-
symlinks: {
|
|
3596
|
-
[value]: 'value',
|
|
3597
|
-
[display]: 'display'
|
|
3598
|
-
}
|
|
3599
|
-
};
|
|
3600
|
-
```
|
|
3601
|
-
|
|
3602
|
-
The `symlinks` mapping connects the symbols to the enhancement's properties, enabling the `element.set[symbol]` syntax.
|
|
3603
|
-
|
|
3604
|
-
### Helper Functions
|
|
3605
|
-
|
|
3606
|
-
The Infer module exports helper functions for manual property and event type inference:
|
|
3607
|
-
|
|
3608
|
-
```TypeScript
|
|
3609
|
-
import { inferValueProperty, inferDisplayProperty, inferEventType } from 'assign-gingerly/Infer.js';
|
|
3610
|
-
|
|
3611
|
-
const input = document.createElement('input');
|
|
3612
|
-
input.type = 'checkbox';
|
|
3613
|
-
|
|
3614
|
-
const valueProp = inferValueProperty(input);
|
|
3615
|
-
console.log(valueProp); // 'checked'
|
|
3616
|
-
|
|
3617
|
-
const displayProp = inferDisplayProperty(input);
|
|
3618
|
-
console.log(displayProp); // 'value'
|
|
3619
|
-
|
|
3620
|
-
const eventType = inferEventType(input);
|
|
3621
|
-
console.log(eventType); // 'input'
|
|
3622
|
-
```
|
|
3623
|
-
|
|
3624
|
-
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.
|
|
3625
|
-
|
|
3626
|
-
**Event Type Inference:**
|
|
3627
|
-
|
|
3628
|
-
The `inferEventType` function returns the most appropriate event type for different element types:
|
|
3629
|
-
|
|
3630
|
-
| Element Type | Event Type | Use Case |
|
|
3631
|
-
|-------------|-----------|----------|
|
|
3632
|
-
| `<input>`, `<textarea>`, `<select>` | `input` | Form control value changes |
|
|
3633
|
-
| `<form>` | `submit` | Form submission |
|
|
3634
|
-
| `<details>` | `toggle` | Details element open/close |
|
|
3635
|
-
| `<dialog>` | `close` | Dialog dismissal |
|
|
3636
|
-
| Other elements | `click` | Default interactive event |
|
|
3637
|
-
|
|
3638
|
-
**Accessing via Enhancement Instance:**
|
|
3639
|
-
|
|
3640
|
-
The inferred event type is also available as a getter on the enhancement instance:
|
|
3641
|
-
|
|
3642
|
-
```TypeScript
|
|
3643
|
-
const input = document.createElement('input');
|
|
3644
|
-
input.set[value] = 'test';
|
|
3645
|
-
|
|
3646
|
-
console.log(input.enh.infer.eventType); // 'input'
|
|
3647
|
-
|
|
3648
|
-
const form = document.createElement('form');
|
|
3649
|
-
form.set[value] = 'test';
|
|
3650
|
-
|
|
3651
|
-
console.log(form.enh.infer.eventType); // 'submit'
|
|
3652
|
-
```
|
|
3653
|
-
|
|
3654
|
-
This is particularly useful when building enhancements that need to attach event listeners but don't know the element type in advance.
|
|
3655
|
-
|
|
3656
|
-
### Benefits
|
|
3657
|
-
|
|
3658
|
-
1. **Type-agnostic code**: Write code that works with any element type without conditionals
|
|
3659
|
-
2. **Cleaner syntax**: No need to remember which property each element type uses
|
|
3660
|
-
3. **Accessibility**: Separate value and display properties support screen readers
|
|
3661
|
-
4. **Framework-friendly**: Symbols work well with reactive frameworks and data binding
|
|
3662
|
-
5. **Extensible**: Based on the enhancement registry system, can be customized or extended
|
|
3663
|
-
|
|
3664
|
-
### Complete Example
|
|
3665
|
-
|
|
3666
|
-
```html
|
|
3667
|
-
<!DOCTYPE html>
|
|
3668
|
-
<html>
|
|
3669
|
-
<head>
|
|
3670
|
-
<script type="module">
|
|
3671
|
-
import { value, display, registryItem } from './Infer.js';
|
|
3672
|
-
import './object-extension.js';
|
|
3673
|
-
|
|
3674
|
-
// Register the enhancement
|
|
3675
|
-
customElements.enhancementRegistry.push(registryItem);
|
|
3676
|
-
|
|
3677
|
-
// Create various elements
|
|
3678
|
-
const input = document.createElement('input');
|
|
3679
|
-
input.type = 'text';
|
|
3680
|
-
|
|
3681
|
-
const checkbox = document.createElement('input');
|
|
3682
|
-
checkbox.type = 'checkbox';
|
|
3683
|
-
|
|
3684
|
-
const time = document.createElement('time');
|
|
3685
|
-
|
|
3686
|
-
const meter = document.createElement('meter');
|
|
3687
|
-
meter.min = 0;
|
|
3688
|
-
meter.max = 100;
|
|
3689
|
-
|
|
3690
|
-
// Set values using the same symbol - each element handles it correctly
|
|
3691
|
-
input.set[value] = 'Hello World';
|
|
3692
|
-
checkbox.set[value] = true;
|
|
3693
|
-
time.set[value] = '2024-01-01T00:00:00Z';
|
|
3694
|
-
time.set[display] = 'January 1, 2024';
|
|
3695
|
-
meter.set[value] = 75;
|
|
3696
|
-
meter.set[display] = '75 percent';
|
|
3697
|
-
|
|
3698
|
-
// Add to document
|
|
3699
|
-
document.body.append(input, checkbox, time, meter);
|
|
3700
|
-
|
|
3701
|
-
console.log('Input value:', input.value); // 'Hello World'
|
|
3702
|
-
console.log('Checkbox checked:', checkbox.checked); // true
|
|
3703
|
-
console.log('Time dateTime:', time.dateTime); // '2024-01-01T00:00:00Z'
|
|
3704
|
-
console.log('Time display:', time.textContent); // 'January 1, 2024'
|
|
3705
|
-
console.log('Meter value:', meter.value); // 75
|
|
3706
|
-
console.log('Meter display:', meter.ariaValueText); // '75 percent'
|
|
3707
|
-
</script>
|
|
3708
|
-
</head>
|
|
3709
|
-
<body>
|
|
3710
|
-
<h1>Infer Enhancement Demo</h1>
|
|
3711
|
-
</body>
|
|
3712
|
-
</html>
|
|
3713
|
-
```
|
|
3714
|
-
|
|
3715
|
-
### Browser Support
|
|
3716
|
-
|
|
3717
|
-
The Infer enhancement requires:
|
|
3718
|
-
- Chrome 146+ (for scoped custom element registries)
|
|
3719
|
-
- Modern browsers with Symbol support
|
|
3720
|
-
- WeakRef support (all modern browsers)
|
|
3721
|
-
|
|
3722
|
-
For browsers without scoped registry support, the enhancement falls back to the global `customElements.enhancementRegistry`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assign-gingerly",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.31",
|
|
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": {
|
|
@@ -55,10 +55,6 @@
|
|
|
55
55
|
"./getHost.js": {
|
|
56
56
|
"default": "./getHost.js",
|
|
57
57
|
"types": "./getHost.ts"
|
|
58
|
-
},
|
|
59
|
-
"./Infer.js": {
|
|
60
|
-
"default": "./Infer.js",
|
|
61
|
-
"types": "./Infer.ts"
|
|
62
58
|
}
|
|
63
59
|
},
|
|
64
60
|
"main": "index.js",
|
|
@@ -73,7 +69,7 @@
|
|
|
73
69
|
"devDependencies": {
|
|
74
70
|
"@playwright/test": "1.59.1",
|
|
75
71
|
"spa-ssi": "0.0.27",
|
|
76
|
-
"@types/node": "25.
|
|
77
|
-
"typescript": "6.0.
|
|
72
|
+
"@types/node": "25.6.0",
|
|
73
|
+
"typescript": "6.0.3"
|
|
78
74
|
}
|
|
79
75
|
}
|
package/playwright.config.ts
CHANGED
|
@@ -20,7 +20,7 @@ export default defineConfig({
|
|
|
20
20
|
/* Opt out of parallel tests on CI. */
|
|
21
21
|
workers: process.env.CI ? 1 : undefined,
|
|
22
22
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
|
23
|
-
reporter: [ ['html', { open: 'never' }] ],
|
|
23
|
+
reporter: [ ['html', { open: 'never' }] ],
|
|
24
24
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
|
25
25
|
use: {
|
|
26
26
|
/* Base URL to use in actions like `await page.goto('/')`. */
|
package/Infer.js
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Symbol for smart value assignment
|
|
3
|
-
* When used with element.set[value], it infers and sets the appropriate value property
|
|
4
|
-
*/
|
|
5
|
-
export const value = Symbol.for('assign-gingerly:value');
|
|
6
|
-
/**
|
|
7
|
-
* Symbol for smart display assignment
|
|
8
|
-
* When used with element.set[display], it infers and sets the appropriate display property
|
|
9
|
-
*/
|
|
10
|
-
export const display = Symbol.for('assign-gingerly:display');
|
|
11
|
-
/**
|
|
12
|
-
* Enhancement class that provides smart value and display property inference
|
|
13
|
-
* Automatically determines the correct property to set based on element type
|
|
14
|
-
*/
|
|
15
|
-
export class Infer {
|
|
16
|
-
#weakRef;
|
|
17
|
-
get enhancedElement() {
|
|
18
|
-
return this.#weakRef.deref();
|
|
19
|
-
}
|
|
20
|
-
constructor(enhancedElement) {
|
|
21
|
-
this.#weakRef = new WeakRef(enhancedElement);
|
|
22
|
-
}
|
|
23
|
-
#value;
|
|
24
|
-
get value() {
|
|
25
|
-
return this.#value;
|
|
26
|
-
}
|
|
27
|
-
set value(nv) {
|
|
28
|
-
this.#value = nv;
|
|
29
|
-
const { enhancedElement } = this;
|
|
30
|
-
enhancedElement[inferValueProperty(enhancedElement)] = nv;
|
|
31
|
-
}
|
|
32
|
-
#display;
|
|
33
|
-
get display() {
|
|
34
|
-
return this.#display;
|
|
35
|
-
}
|
|
36
|
-
set display(nv) {
|
|
37
|
-
this.#display = nv;
|
|
38
|
-
const { enhancedElement } = this;
|
|
39
|
-
enhancedElement[inferDisplayProperty(enhancedElement)] = nv;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Get the inferred event type for the element
|
|
43
|
-
* @returns The most appropriate event type for this element
|
|
44
|
-
*/
|
|
45
|
-
get eventType() {
|
|
46
|
-
return inferEventType(this.enhancedElement);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Registry item for the Infer enhancement
|
|
51
|
-
* Register this with customElements.enhancementRegistry to enable smart value/display assignment
|
|
52
|
-
*/
|
|
53
|
-
export const registryItem = {
|
|
54
|
-
spawn: Infer,
|
|
55
|
-
enhKey: 'infer',
|
|
56
|
-
symlinks: {
|
|
57
|
-
[value]: 'value',
|
|
58
|
-
[display]: 'display'
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
/**
|
|
62
|
-
* Infer the most appropriate value property for an element
|
|
63
|
-
* @param element - The element to infer the property for
|
|
64
|
-
* @returns The property name to use for value assignment
|
|
65
|
-
*/
|
|
66
|
-
export function inferValueProperty(element) {
|
|
67
|
-
const tagName = element.localName;
|
|
68
|
-
// Input elements - check type attribute
|
|
69
|
-
if (tagName === 'input') {
|
|
70
|
-
const type = element.getAttribute('type')?.toLowerCase();
|
|
71
|
-
if (type === 'checkbox' || type === 'radio') {
|
|
72
|
-
return 'checked';
|
|
73
|
-
}
|
|
74
|
-
return 'value';
|
|
75
|
-
}
|
|
76
|
-
// Form controls with value property
|
|
77
|
-
if (tagName === 'textarea' || tagName === 'select') {
|
|
78
|
-
return 'value';
|
|
79
|
-
}
|
|
80
|
-
// Semantic HTML elements with specific properties
|
|
81
|
-
if (tagName === 'time') {
|
|
82
|
-
return 'dateTime';
|
|
83
|
-
}
|
|
84
|
-
if (tagName === 'data') {
|
|
85
|
-
return 'value';
|
|
86
|
-
}
|
|
87
|
-
if (tagName === 'meter' || tagName === 'progress') {
|
|
88
|
-
return 'value';
|
|
89
|
-
}
|
|
90
|
-
if (tagName === 'output') {
|
|
91
|
-
return 'value';
|
|
92
|
-
}
|
|
93
|
-
// Check for itemprop attribute as a hint
|
|
94
|
-
const itemprop = element.getAttribute('itemprop');
|
|
95
|
-
if (itemprop) {
|
|
96
|
-
return itemprop;
|
|
97
|
-
}
|
|
98
|
-
// Default fallback
|
|
99
|
-
return 'textContent';
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Infer the most appropriate display property for an element
|
|
103
|
-
* @param element - The element to infer the property for
|
|
104
|
-
* @returns The property name to use for display assignment
|
|
105
|
-
*/
|
|
106
|
-
export function inferDisplayProperty(element) {
|
|
107
|
-
const tagName = element.localName;
|
|
108
|
-
// Form controls display their value
|
|
109
|
-
if (tagName === 'input' || tagName === 'textarea' || tagName === 'select') {
|
|
110
|
-
return 'value';
|
|
111
|
-
}
|
|
112
|
-
// Time elements display formatted time
|
|
113
|
-
if (tagName === 'time') {
|
|
114
|
-
return 'textContent';
|
|
115
|
-
}
|
|
116
|
-
// Data elements display human-readable content
|
|
117
|
-
if (tagName === 'data') {
|
|
118
|
-
return 'textContent';
|
|
119
|
-
}
|
|
120
|
-
// Progress/meter elements use ARIA for display
|
|
121
|
-
if (tagName === 'meter' || tagName === 'progress') {
|
|
122
|
-
return 'ariaValueText';
|
|
123
|
-
}
|
|
124
|
-
// Default fallback
|
|
125
|
-
return 'textContent';
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Infer the most appropriate event type for an element
|
|
129
|
-
* Used when no explicit event type is provided
|
|
130
|
-
* @param element - The element to infer the event type for
|
|
131
|
-
* @returns The event type name like 'input', 'change', 'click', 'submit'
|
|
132
|
-
*/
|
|
133
|
-
export function inferEventType(element) {
|
|
134
|
-
const tagName = element.localName;
|
|
135
|
-
// Form controls that support input event
|
|
136
|
-
if (tagName === 'input' || tagName === 'textarea' || tagName === 'select') {
|
|
137
|
-
return 'input';
|
|
138
|
-
}
|
|
139
|
-
// Form submission
|
|
140
|
-
if (tagName === 'form') {
|
|
141
|
-
return 'submit';
|
|
142
|
-
}
|
|
143
|
-
// Details element
|
|
144
|
-
if (tagName === 'details') {
|
|
145
|
-
return 'toggle';
|
|
146
|
-
}
|
|
147
|
-
// Dialog element
|
|
148
|
-
if (tagName === 'dialog') {
|
|
149
|
-
return 'close';
|
|
150
|
-
}
|
|
151
|
-
// Default fallback for interactive elements
|
|
152
|
-
return 'click';
|
|
153
|
-
}
|
|
154
|
-
export default registryItem;
|
package/Infer.ts
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import type { EnhancementConfig } from "./types/assign-gingerly/types";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Symbol for smart value assignment
|
|
5
|
-
* When used with element.set[value], it infers and sets the appropriate value property
|
|
6
|
-
*/
|
|
7
|
-
export const value = Symbol.for('assign-gingerly:value');
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Symbol for smart display assignment
|
|
11
|
-
* When used with element.set[display], it infers and sets the appropriate display property
|
|
12
|
-
*/
|
|
13
|
-
export const display = Symbol.for('assign-gingerly:display');
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Enhancement class that provides smart value and display property inference
|
|
17
|
-
* Automatically determines the correct property to set based on element type
|
|
18
|
-
*/
|
|
19
|
-
export class Infer<TValue = any, TDisplay = any> {
|
|
20
|
-
#weakRef: WeakRef<Element>;
|
|
21
|
-
|
|
22
|
-
get enhancedElement(){
|
|
23
|
-
return this.#weakRef.deref()!;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
constructor(enhancedElement?: Element){
|
|
27
|
-
this.#weakRef = new WeakRef(enhancedElement!);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
#value: TValue | undefined;
|
|
31
|
-
|
|
32
|
-
get value(): TValue | undefined {
|
|
33
|
-
return this.#value;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
set value(nv: TValue){
|
|
37
|
-
this.#value = nv;
|
|
38
|
-
const {enhancedElement} = this;
|
|
39
|
-
(enhancedElement as any)[inferValueProperty(enhancedElement)] = nv;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
#display: TDisplay | undefined;
|
|
43
|
-
|
|
44
|
-
get display(): TDisplay | undefined {
|
|
45
|
-
return this.#display;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
set display(nv: TDisplay){
|
|
49
|
-
this.#display = nv;
|
|
50
|
-
const {enhancedElement} = this;
|
|
51
|
-
(enhancedElement as any)[inferDisplayProperty(enhancedElement)] = nv;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Get the inferred event type for the element
|
|
56
|
-
* @returns The most appropriate event type for this element
|
|
57
|
-
*/
|
|
58
|
-
get eventType(): string {
|
|
59
|
-
return inferEventType(this.enhancedElement);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Registry item for the Infer enhancement
|
|
65
|
-
* Register this with customElements.enhancementRegistry to enable smart value/display assignment
|
|
66
|
-
*/
|
|
67
|
-
export const registryItem: EnhancementConfig = {
|
|
68
|
-
spawn: Infer,
|
|
69
|
-
enhKey: 'infer',
|
|
70
|
-
symlinks: {
|
|
71
|
-
[value]: 'value',
|
|
72
|
-
[display]: 'display'
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Infer the most appropriate value property for an element
|
|
78
|
-
* @param element - The element to infer the property for
|
|
79
|
-
* @returns The property name to use for value assignment
|
|
80
|
-
*/
|
|
81
|
-
export function inferValueProperty(element: Element): string {
|
|
82
|
-
const tagName = element.localName;
|
|
83
|
-
|
|
84
|
-
// Input elements - check type attribute
|
|
85
|
-
if (tagName === 'input') {
|
|
86
|
-
const type = element.getAttribute('type')?.toLowerCase();
|
|
87
|
-
if (type === 'checkbox' || type === 'radio') {
|
|
88
|
-
return 'checked';
|
|
89
|
-
}
|
|
90
|
-
return 'value';
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Form controls with value property
|
|
94
|
-
if (tagName === 'textarea' || tagName === 'select') {
|
|
95
|
-
return 'value';
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Semantic HTML elements with specific properties
|
|
99
|
-
if (tagName === 'time') {
|
|
100
|
-
return 'dateTime';
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (tagName === 'data') {
|
|
104
|
-
return 'value';
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (tagName === 'meter' || tagName === 'progress') {
|
|
108
|
-
return 'value';
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (tagName === 'output') {
|
|
112
|
-
return 'value';
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Check for itemprop attribute as a hint
|
|
116
|
-
const itemprop = element.getAttribute('itemprop');
|
|
117
|
-
if (itemprop) {
|
|
118
|
-
return itemprop;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Default fallback
|
|
122
|
-
return 'textContent';
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Infer the most appropriate display property for an element
|
|
127
|
-
* @param element - The element to infer the property for
|
|
128
|
-
* @returns The property name to use for display assignment
|
|
129
|
-
*/
|
|
130
|
-
export function inferDisplayProperty(element: Element): string {
|
|
131
|
-
const tagName = element.localName;
|
|
132
|
-
|
|
133
|
-
// Form controls display their value
|
|
134
|
-
if (tagName === 'input' || tagName === 'textarea' || tagName === 'select') {
|
|
135
|
-
return 'value';
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Time elements display formatted time
|
|
139
|
-
if (tagName === 'time') {
|
|
140
|
-
return 'textContent';
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Data elements display human-readable content
|
|
144
|
-
if (tagName === 'data') {
|
|
145
|
-
return 'textContent';
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Progress/meter elements use ARIA for display
|
|
149
|
-
if (tagName === 'meter' || tagName === 'progress') {
|
|
150
|
-
return 'ariaValueText';
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Default fallback
|
|
154
|
-
return 'textContent';
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Infer the most appropriate event type for an element
|
|
159
|
-
* Used when no explicit event type is provided
|
|
160
|
-
* @param element - The element to infer the event type for
|
|
161
|
-
* @returns The event type name like 'input', 'change', 'click', 'submit'
|
|
162
|
-
*/
|
|
163
|
-
export function inferEventType(element: Element): string {
|
|
164
|
-
const tagName = element.localName;
|
|
165
|
-
|
|
166
|
-
// Form controls that support input event
|
|
167
|
-
if (tagName === 'input' || tagName === 'textarea' || tagName === 'select') {
|
|
168
|
-
return 'input';
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Form submission
|
|
172
|
-
if (tagName === 'form') {
|
|
173
|
-
return 'submit';
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Details element
|
|
177
|
-
if (tagName === 'details') {
|
|
178
|
-
return 'toggle';
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Dialog element
|
|
182
|
-
if (tagName === 'dialog') {
|
|
183
|
-
return 'close';
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Default fallback for interactive elements
|
|
187
|
-
return 'click';
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export default registryItem;
|