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 CHANGED
@@ -1,4 +1,4 @@
1
- # assign-gingerly and assign-tentatively
1
+ # assign-gingerly and assign-tentatively
2
2
 
3
3
  [![Playwright Tests](https://github.com/bahrus/assign-gingerly/actions/workflows/CI.yml/badge.svg?branch=baseline)](https://github.com/bahrus/assign-gingerly/actions/workflows/CI.yml)
4
4
  [![NPM version](https://badge.fury.io/js/assign-gingerly.png)](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
- // Don't dispose - enhancement should persist
1463
+ // ? Don't dispose - enhancement should persist
1464
1464
 
1465
1465
  // Case 2: Moved to another location
1466
1466
  otherContainer.append(element);
1467
- // Don't dispose - enhancement should persist
1467
+ // ? Don't dispose - enhancement should persist
1468
1468
 
1469
1469
  // Case 3: Cached for reuse
1470
1470
  elementCache.set('myElement', element);
1471
- // Don't dispose - enhancement should persist
1471
+ // ? Don't dispose - enhancement should persist
1472
1472
 
1473
1473
  // Case 4: Truly done, ready for GC
1474
1474
  element = null;
1475
- // Should dispose, but no way to detect this automatically
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
- - 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
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
- > ![NOTE]
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: '🎨-theme' }); // Has non-ASCII (and dash)
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
- - **JSON serializable** - Configs can be stored/transmitted as JSON
2380
- - **Reusable** - Define once, use everywhere
2381
- - **Maintainable** - Update parser logic in one place
2382
- - **Testable** - Test parsers independently
2383
- - **Discoverable** - `globalParserRegistry.getNames()` lists all available parsers
2384
- - **Backward compatible** - Inline functions still work
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.30",
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.5.2",
77
- "typescript": "6.0.2"
72
+ "@types/node": "25.6.0",
73
+ "typescript": "6.0.3"
78
74
  }
79
75
  }
@@ -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('/')`. */
@@ -298,6 +298,7 @@ export declare function assignGingerly(
298
298
  export default assignGingerly;
299
299
 
300
300
  export declare class ElementEnhancementGateway{
301
+ //TODO: this isn't right
301
302
  enh: ElementEnhancement;
302
303
  }
303
304
 
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;