@mintjamsinc/ichigojs 0.1.63 → 0.1.65
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/dist/ichigo.cjs +180 -23
- package/dist/ichigo.cjs.map +1 -1
- package/dist/ichigo.esm.js +180 -23
- package/dist/ichigo.esm.js.map +1 -1
- package/dist/ichigo.esm.min.js +1 -1
- package/dist/ichigo.min.cjs +1 -1
- package/dist/ichigo.umd.js +180 -23
- package/dist/ichigo.umd.js.map +1 -1
- package/dist/ichigo.umd.min.js +1 -1
- package/dist/types/ichigo/directives/VBindDirective.d.ts +5 -0
- package/dist/types/ichigo/directives/VDirectiveManager.d.ts +9 -0
- package/package.json +1 -1
package/dist/ichigo.esm.js
CHANGED
|
@@ -7437,6 +7437,13 @@ class VBindDirective {
|
|
|
7437
7437
|
get expression() {
|
|
7438
7438
|
return this.#expression;
|
|
7439
7439
|
}
|
|
7440
|
+
/**
|
|
7441
|
+
* Evaluates the bound expression and returns the current value.
|
|
7442
|
+
* Returns undefined when no evaluator is available (e.g., empty expression).
|
|
7443
|
+
*/
|
|
7444
|
+
evaluate() {
|
|
7445
|
+
return this.#evaluator?.evaluate();
|
|
7446
|
+
}
|
|
7440
7447
|
/**
|
|
7441
7448
|
* @inheritdoc
|
|
7442
7449
|
*/
|
|
@@ -7597,18 +7604,25 @@ class VBindDirective {
|
|
|
7597
7604
|
* Returns true when the target is a custom element and the binding should be
|
|
7598
7605
|
* delivered as a property rather than an HTML attribute.
|
|
7599
7606
|
*
|
|
7600
|
-
*
|
|
7607
|
+
* Three conditions trigger property delivery:
|
|
7601
7608
|
* 1. The value is an object or array — serialising these to a string attribute
|
|
7602
7609
|
* would lose type information.
|
|
7603
7610
|
* 2. The element exposes a matching property accessor (e.g. a prop declared via
|
|
7604
7611
|
* defineComponent), even when the value is a primitive.
|
|
7612
|
+
* 3. The element declares a matching prop in its static _props (case-insensitive
|
|
7613
|
+
* and kebab-case → camelCase tolerant), so primitive values reach the
|
|
7614
|
+
* reactive binding via the generated setter instead of being lost as a
|
|
7615
|
+
* plain HTML attribute that no attributeChangedCallback observes.
|
|
7605
7616
|
*/
|
|
7606
7617
|
#isCustomElementProperty(element, name, value) {
|
|
7607
7618
|
if (!element.tagName.includes('-')) {
|
|
7608
7619
|
return false;
|
|
7609
7620
|
}
|
|
7610
7621
|
const isObjectOrArray = Array.isArray(value) || (typeof value === 'object' && value !== null);
|
|
7611
|
-
|
|
7622
|
+
if (isObjectOrArray || name in element) {
|
|
7623
|
+
return true;
|
|
7624
|
+
}
|
|
7625
|
+
return this.#findDeclaredProp(element, name) !== undefined;
|
|
7612
7626
|
}
|
|
7613
7627
|
/**
|
|
7614
7628
|
* Resolves the actual property name on a custom element for a given
|
|
@@ -7624,20 +7638,25 @@ class VBindDirective {
|
|
|
7624
7638
|
if (name in element) {
|
|
7625
7639
|
return name;
|
|
7626
7640
|
}
|
|
7627
|
-
|
|
7641
|
+
return this.#findDeclaredProp(element, name) ?? name;
|
|
7642
|
+
}
|
|
7643
|
+
/**
|
|
7644
|
+
* Looks up a declared prop on a custom element constructor whose name matches
|
|
7645
|
+
* the given attribute name, treating the comparison as case-insensitive and
|
|
7646
|
+
* tolerant of kebab-case → camelCase conversion. Returns the canonical prop
|
|
7647
|
+
* name (as declared) when a match is found, or undefined otherwise.
|
|
7648
|
+
*/
|
|
7649
|
+
#findDeclaredProp(element, name) {
|
|
7628
7650
|
const props = element.constructor._props;
|
|
7629
|
-
if (Array.isArray(props)) {
|
|
7630
|
-
|
|
7631
|
-
const camelName = this.#kebabToCamel(lowerName);
|
|
7632
|
-
const match = props.find(p => {
|
|
7633
|
-
const lowerProp = p.toLowerCase();
|
|
7634
|
-
return lowerProp === lowerName || lowerProp === camelName.toLowerCase() || p === camelName;
|
|
7635
|
-
});
|
|
7636
|
-
if (match) {
|
|
7637
|
-
return match;
|
|
7638
|
-
}
|
|
7651
|
+
if (!Array.isArray(props)) {
|
|
7652
|
+
return undefined;
|
|
7639
7653
|
}
|
|
7640
|
-
|
|
7654
|
+
const lowerName = name.toLowerCase();
|
|
7655
|
+
const camelLower = this.#kebabToCamel(lowerName).toLowerCase();
|
|
7656
|
+
return props.find(p => {
|
|
7657
|
+
const lowerProp = p.toLowerCase();
|
|
7658
|
+
return lowerProp === lowerName || lowerProp === camelLower;
|
|
7659
|
+
});
|
|
7641
7660
|
}
|
|
7642
7661
|
/**
|
|
7643
7662
|
* Converts kebab-case to camelCase (e.g. `user-name` → `userName`).
|
|
@@ -8637,6 +8656,29 @@ class VDirectiveManager {
|
|
|
8637
8656
|
get keyDirective() {
|
|
8638
8657
|
return this.#keyDirective;
|
|
8639
8658
|
}
|
|
8659
|
+
/**
|
|
8660
|
+
* Finds a VBindDirective that binds the given attribute name (e.g. "value",
|
|
8661
|
+
* "true-value", "false-value"). Returns undefined if no matching v-bind
|
|
8662
|
+
* directive is registered on this node.
|
|
8663
|
+
*
|
|
8664
|
+
* Used by directives such as v-model that need to read the typed value of a
|
|
8665
|
+
* sibling v-bind without depending on the attribute order in the source HTML.
|
|
8666
|
+
*/
|
|
8667
|
+
findBindDirective(attrName) {
|
|
8668
|
+
if (!this.#directives || this.#directives.length === 0) {
|
|
8669
|
+
return undefined;
|
|
8670
|
+
}
|
|
8671
|
+
for (const directive of this.#directives) {
|
|
8672
|
+
if (directive.name !== StandardDirectiveName.V_BIND) {
|
|
8673
|
+
continue;
|
|
8674
|
+
}
|
|
8675
|
+
const bindDirective = directive;
|
|
8676
|
+
if (bindDirective.attributeName === attrName) {
|
|
8677
|
+
return bindDirective;
|
|
8678
|
+
}
|
|
8679
|
+
}
|
|
8680
|
+
return undefined;
|
|
8681
|
+
}
|
|
8640
8682
|
/**
|
|
8641
8683
|
* Gets the VBindDirective for options specific to the given directive name.
|
|
8642
8684
|
* Searches in order: `:options.{directive}` -> `:options`
|
|
@@ -11090,14 +11132,13 @@ class VModelDirective {
|
|
|
11090
11132
|
* @inheritdoc
|
|
11091
11133
|
*/
|
|
11092
11134
|
get domUpdater() {
|
|
11093
|
-
const
|
|
11094
|
-
// Create and return the DOM updater
|
|
11135
|
+
const self = this;
|
|
11095
11136
|
const updater = {
|
|
11096
11137
|
get dependentIdentifiers() {
|
|
11097
|
-
return
|
|
11138
|
+
return self.#collectDependentIdentifiers();
|
|
11098
11139
|
},
|
|
11099
11140
|
applyToDOM: () => {
|
|
11100
|
-
|
|
11141
|
+
self.#render();
|
|
11101
11142
|
}
|
|
11102
11143
|
};
|
|
11103
11144
|
return updater;
|
|
@@ -11112,7 +11153,27 @@ class VModelDirective {
|
|
|
11112
11153
|
* @inheritdoc
|
|
11113
11154
|
*/
|
|
11114
11155
|
get dependentIdentifiers() {
|
|
11115
|
-
return this.#
|
|
11156
|
+
return this.#collectDependentIdentifiers();
|
|
11157
|
+
}
|
|
11158
|
+
/**
|
|
11159
|
+
* Collects identifiers this directive's render depends on. For checkboxes
|
|
11160
|
+
* this includes the v-model expression itself plus the expressions bound to
|
|
11161
|
+
* `:value`, `:true-value`, and `:false-value`, since the rendered checked
|
|
11162
|
+
* state changes when any of these change.
|
|
11163
|
+
*/
|
|
11164
|
+
#collectDependentIdentifiers() {
|
|
11165
|
+
const ids = new Set(this.#evaluator?.dependentIdentifiers ?? []);
|
|
11166
|
+
const element = this.#vNode.node;
|
|
11167
|
+
if (element instanceof HTMLInputElement && element.type === 'checkbox') {
|
|
11168
|
+
const manager = this.#vNode.directiveManager;
|
|
11169
|
+
for (const attrName of ['value', 'true-value', 'false-value']) {
|
|
11170
|
+
const bindDirective = manager?.findBindDirective(attrName);
|
|
11171
|
+
if (bindDirective) {
|
|
11172
|
+
bindDirective.dependentIdentifiers.forEach(id => ids.add(id));
|
|
11173
|
+
}
|
|
11174
|
+
}
|
|
11175
|
+
}
|
|
11176
|
+
return Array.from(ids);
|
|
11116
11177
|
}
|
|
11117
11178
|
/**
|
|
11118
11179
|
* @inheritdoc
|
|
@@ -11192,7 +11253,7 @@ class VModelDirective {
|
|
|
11192
11253
|
// Update the element based on its type
|
|
11193
11254
|
if (element instanceof HTMLInputElement) {
|
|
11194
11255
|
if (element.type === 'checkbox') {
|
|
11195
|
-
element
|
|
11256
|
+
this.#renderCheckbox(element, value);
|
|
11196
11257
|
}
|
|
11197
11258
|
else if (element.type === 'radio') {
|
|
11198
11259
|
// Prefer the original typed value stored by VBindDirective (:value binding)
|
|
@@ -11225,7 +11286,7 @@ class VModelDirective {
|
|
|
11225
11286
|
// Get the new value based on element type
|
|
11226
11287
|
if (target instanceof HTMLInputElement) {
|
|
11227
11288
|
if (target.type === 'checkbox') {
|
|
11228
|
-
newValue = target
|
|
11289
|
+
newValue = this.#computeCheckboxNewValue(target);
|
|
11229
11290
|
}
|
|
11230
11291
|
else if (target.type === 'radio') {
|
|
11231
11292
|
// Prefer the original typed value stored by VBindDirective (:value binding)
|
|
@@ -11241,13 +11302,109 @@ class VModelDirective {
|
|
|
11241
11302
|
else if (target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement) {
|
|
11242
11303
|
newValue = target.value;
|
|
11243
11304
|
}
|
|
11244
|
-
// Apply modifiers to the value
|
|
11245
|
-
|
|
11305
|
+
// Apply modifiers to the value (skip for checkboxes: their value
|
|
11306
|
+
// is either boolean, a custom true/false value, or an array, none
|
|
11307
|
+
// of which should be coerced by .trim or .number).
|
|
11308
|
+
const isCheckbox = target instanceof HTMLInputElement && target.type === 'checkbox';
|
|
11309
|
+
if (!isCheckbox) {
|
|
11310
|
+
newValue = this.#applyModifiers(newValue);
|
|
11311
|
+
}
|
|
11246
11312
|
// Update the binding
|
|
11247
11313
|
this.#updateBinding(newValue);
|
|
11248
11314
|
};
|
|
11249
11315
|
element.addEventListener(eventName, this.#listener);
|
|
11250
11316
|
}
|
|
11317
|
+
/**
|
|
11318
|
+
* Renders a checkbox in one of three modes (Vue-compatible):
|
|
11319
|
+
* 1. Array binding: the bound value is an array; the checkbox is checked
|
|
11320
|
+
* when its element-value is a member of that array.
|
|
11321
|
+
* 2. true-value/false-value binding: when `:true-value` (and optionally
|
|
11322
|
+
* `:false-value`) is provided via v-bind, the checkbox is checked
|
|
11323
|
+
* when the bound value strictly equals the resolved true-value.
|
|
11324
|
+
* 3. Boolean binding (default): the bound value is coerced to boolean.
|
|
11325
|
+
*/
|
|
11326
|
+
#renderCheckbox(element, value) {
|
|
11327
|
+
if (Array.isArray(value)) {
|
|
11328
|
+
const elementValue = this.#resolveCheckboxElementValue(element);
|
|
11329
|
+
element.checked = value.indexOf(elementValue) !== -1;
|
|
11330
|
+
return;
|
|
11331
|
+
}
|
|
11332
|
+
const trueValueDescriptor = this.#resolveCheckboxTrueFalseValues(element);
|
|
11333
|
+
if (trueValueDescriptor) {
|
|
11334
|
+
element.checked = value === trueValueDescriptor.trueValue;
|
|
11335
|
+
return;
|
|
11336
|
+
}
|
|
11337
|
+
element.checked = !!value;
|
|
11338
|
+
}
|
|
11339
|
+
/**
|
|
11340
|
+
* Computes the value to write back to the bound expression when a checkbox
|
|
11341
|
+
* change event fires. Mirrors the three-mode logic of #renderCheckbox.
|
|
11342
|
+
*
|
|
11343
|
+
* For array binding, the current value of the bound expression is read so
|
|
11344
|
+
* that a fresh array can be returned (the existing array is not mutated,
|
|
11345
|
+
* which preserves reactivity semantics).
|
|
11346
|
+
*/
|
|
11347
|
+
#computeCheckboxNewValue(target) {
|
|
11348
|
+
const currentValue = this.#evaluator?.evaluate();
|
|
11349
|
+
if (Array.isArray(currentValue)) {
|
|
11350
|
+
const elementValue = this.#resolveCheckboxElementValue(target);
|
|
11351
|
+
const next = currentValue.slice();
|
|
11352
|
+
const index = next.indexOf(elementValue);
|
|
11353
|
+
if (target.checked) {
|
|
11354
|
+
if (index === -1) {
|
|
11355
|
+
next.push(elementValue);
|
|
11356
|
+
}
|
|
11357
|
+
}
|
|
11358
|
+
else {
|
|
11359
|
+
if (index !== -1) {
|
|
11360
|
+
next.splice(index, 1);
|
|
11361
|
+
}
|
|
11362
|
+
}
|
|
11363
|
+
return next;
|
|
11364
|
+
}
|
|
11365
|
+
const trueValueDescriptor = this.#resolveCheckboxTrueFalseValues(target);
|
|
11366
|
+
if (trueValueDescriptor) {
|
|
11367
|
+
return target.checked ? trueValueDescriptor.trueValue : trueValueDescriptor.falseValue;
|
|
11368
|
+
}
|
|
11369
|
+
return target.checked;
|
|
11370
|
+
}
|
|
11371
|
+
/**
|
|
11372
|
+
* Resolves the typed element value for a checkbox. Prefers the value bound
|
|
11373
|
+
* via `:value` (evaluated through the sibling VBindDirective so type is
|
|
11374
|
+
* preserved), then the typed value previously stored on the element by
|
|
11375
|
+
* VBindDirective, and finally the raw string `value` attribute.
|
|
11376
|
+
*/
|
|
11377
|
+
#resolveCheckboxElementValue(element) {
|
|
11378
|
+
const bindDirective = this.#vNode.directiveManager?.findBindDirective('value');
|
|
11379
|
+
if (bindDirective) {
|
|
11380
|
+
return bindDirective.evaluate();
|
|
11381
|
+
}
|
|
11382
|
+
if (element._value !== undefined) {
|
|
11383
|
+
return element._value;
|
|
11384
|
+
}
|
|
11385
|
+
return element.value;
|
|
11386
|
+
}
|
|
11387
|
+
/**
|
|
11388
|
+
* Resolves the (true-value, false-value) pair for a checkbox if either is
|
|
11389
|
+
* bound via `:true-value` or `:false-value`. Returns undefined when no
|
|
11390
|
+
* true/false value binding is present, signalling that the default boolean
|
|
11391
|
+
* mode should be used.
|
|
11392
|
+
*
|
|
11393
|
+
* If only one of the two is bound, the other defaults match Vue: an unbound
|
|
11394
|
+
* true-value defaults to literal `true`, an unbound false-value to `false`.
|
|
11395
|
+
*/
|
|
11396
|
+
#resolveCheckboxTrueFalseValues(element) {
|
|
11397
|
+
const manager = this.#vNode.directiveManager;
|
|
11398
|
+
const trueBind = manager?.findBindDirective('true-value');
|
|
11399
|
+
const falseBind = manager?.findBindDirective('false-value');
|
|
11400
|
+
if (!trueBind && !falseBind) {
|
|
11401
|
+
return undefined;
|
|
11402
|
+
}
|
|
11403
|
+
return {
|
|
11404
|
+
trueValue: trueBind ? trueBind.evaluate() : true,
|
|
11405
|
+
falseValue: falseBind ? falseBind.evaluate() : false,
|
|
11406
|
+
};
|
|
11407
|
+
}
|
|
11251
11408
|
/**
|
|
11252
11409
|
* Applies modifiers to the input value.
|
|
11253
11410
|
* @param value The value to process.
|