@mintjamsinc/ichigojs 0.1.60 → 0.1.62

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.
@@ -8237,6 +8237,13 @@ class VBindings {
8237
8237
  * their own alias (e.g. "file" -> "files[0]") without overwriting each other.
8238
8238
  */
8239
8239
  #localAliases = new Map();
8240
+ /**
8241
+ * Setters for writable computed properties. When a key registered here is assigned
8242
+ * through the bindings proxy (e.g. via v-model or `bindings.set`), the setter is invoked
8243
+ * instead of writing directly to the local store. The cached value of the computed property
8244
+ * is updated by the recompute cycle through `setSilent`, which bypasses this routing.
8245
+ */
8246
+ #writableComputeds = new Map();
8240
8247
  /**
8241
8248
  * Creates a new instance of VBindings.
8242
8249
  * @param parent The parent bindings, if any.
@@ -8256,6 +8263,14 @@ class VBindings {
8256
8263
  return this.#parent?.raw[key];
8257
8264
  },
8258
8265
  set: (obj, key, value) => {
8266
+ // If this key is a writable computed, route the assignment through its setter.
8267
+ // `setSilent` (used to update the cached value during recompute) sets `suppressOnChange`,
8268
+ // which bypasses this routing so the cached value can be written directly.
8269
+ if (!this.#suppressOnChange && this.#writableComputeds.has(key)) {
8270
+ const setter = this.#writableComputeds.get(key);
8271
+ setter(value);
8272
+ return true;
8273
+ }
8259
8274
  let target = obj;
8260
8275
  if (!Reflect.has(target, key)) {
8261
8276
  for (let parent = this.#parent; parent; parent = parent.#parent) {
@@ -8442,6 +8457,16 @@ class VBindings {
8442
8457
  this.#suppressOnChange = false;
8443
8458
  }
8444
8459
  }
8460
+ /**
8461
+ * Registers a setter for a writable computed property. When the given key is assigned
8462
+ * through the bindings proxy, the setter will be invoked instead of writing directly to
8463
+ * the local store.
8464
+ * @param key The computed property name.
8465
+ * @param setter The setter function to invoke on assignment.
8466
+ */
8467
+ registerWritableComputed(key, setter) {
8468
+ this.#writableComputeds.set(key, setter);
8469
+ }
8445
8470
  /**
8446
8471
  * Manually adds an identifier to the set of changed identifiers.
8447
8472
  * This is useful for computed properties that need to mark themselves as changed
@@ -11291,6 +11316,10 @@ class VModelDirective {
11291
11316
  * The `v-on` directive supports event modifiers such as `.stop`, `.prevent`, `.capture`, `.self`, and `.once` to modify the behavior of the event listener.
11292
11317
  * For example, `v-on:click.stop="handleClick"` will stop the event from propagating up the DOM tree.
11293
11318
  *
11319
+ * Key modifiers (KeyboardEvent): `.enter`, `.tab`, `.delete` (Delete/Backspace), `.esc` / `.escape`, `.space`, `.up`, `.down`, `.left`, `.right`.
11320
+ * Mouse button modifiers (MouseEvent): `.left`, `.middle`, `.right`.
11321
+ * System modifiers (KeyboardEvent and MouseEvent): `.shift`, `.ctrl`, `.alt`, `.meta`, plus `.exact` to require that no other system modifiers are held.
11322
+ *
11294
11323
  * Additionally, this directive supports lifecycle hooks:
11295
11324
  * @mount="onMount" - Called before the VNode is mounted to the DOM element
11296
11325
  * @mounted="onMounted" - Called after the VNode is mounted to the DOM element
@@ -11483,14 +11512,14 @@ class VOnDirective {
11483
11512
  const eventName = this.#eventName;
11484
11513
  const useCapture = this.#modifiers.has('capture');
11485
11514
  const isOnce = this.#modifiers.has('once');
11515
+ // System modifier keys (held during the event) shared by KeyboardEvent and MouseEvent.
11516
+ const systemModifiers = ['shift', 'ctrl', 'alt', 'meta'];
11486
11517
  // Create the event listener function
11487
11518
  this.#listener = (event) => {
11488
- // Check key modifiers for keyboard events
11519
+ // Check key modifiers for keyboard events.
11520
+ // The `.left` / `.right` aliases here mean ArrowLeft / ArrowRight; the same tokens
11521
+ // map to mouse buttons further below, dispatched by event type to avoid ambiguity.
11489
11522
  if (event instanceof KeyboardEvent) {
11490
- // Map of modifier alias -> KeyboardEvent.key values it matches.
11491
- // Multiple values allow a single modifier to match several physical keys
11492
- // (e.g. `.delete` matches both Delete and Backspace, matching Vue's behavior).
11493
- // Multiple aliases pointing to the same key are allowed (e.g. `.esc` / `.escape`).
11494
11523
  const keyMap = {
11495
11524
  'enter': ['Enter'],
11496
11525
  'tab': ['Tab'],
@@ -11518,6 +11547,46 @@ class VOnDirective {
11518
11547
  }
11519
11548
  }
11520
11549
  }
11550
+ // Check mouse button modifiers for mouse events.
11551
+ // `.left` / `.middle` / `.right` map to MouseEvent.button values 0 / 1 / 2.
11552
+ if (event instanceof MouseEvent) {
11553
+ const buttonMap = {
11554
+ 'left': 0,
11555
+ 'middle': 1,
11556
+ 'right': 2
11557
+ };
11558
+ const hasButtonModifier = Object.keys(buttonMap).some(b => this.#modifiers.has(b));
11559
+ if (hasButtonModifier) {
11560
+ let buttonMatched = false;
11561
+ for (const [modifier, buttonValue] of Object.entries(buttonMap)) {
11562
+ if (this.#modifiers.has(modifier) && event.button === buttonValue) {
11563
+ buttonMatched = true;
11564
+ break;
11565
+ }
11566
+ }
11567
+ if (!buttonMatched) {
11568
+ return;
11569
+ }
11570
+ }
11571
+ }
11572
+ // Check system modifier keys (shift / ctrl / alt / meta) and `.exact`.
11573
+ // These properties exist on both KeyboardEvent and MouseEvent.
11574
+ if (event instanceof KeyboardEvent || event instanceof MouseEvent) {
11575
+ // Required system modifiers must be held.
11576
+ for (const mod of systemModifiers) {
11577
+ if (this.#modifiers.has(mod) && !event[`${mod}Key`]) {
11578
+ return;
11579
+ }
11580
+ }
11581
+ // `.exact` rejects events with extra system modifiers held that weren't listed.
11582
+ if (this.#modifiers.has('exact')) {
11583
+ for (const mod of systemModifiers) {
11584
+ if (event[`${mod}Key`] && !this.#modifiers.has(mod)) {
11585
+ return;
11586
+ }
11587
+ }
11588
+ }
11589
+ }
11521
11590
  // Apply event modifiers
11522
11591
  if (this.#modifiers.has('stop')) {
11523
11592
  event.stopPropagation();
@@ -13118,8 +13187,15 @@ class VApplication {
13118
13187
  this.#logger = this.#logManager.getLogger('VApplication');
13119
13188
  // Analyze function dependencies
13120
13189
  this.#functionDependencies = ExpressionUtils.analyzeFunctionDependencies(options.methods || {});
13121
- // Analyze computed dependencies
13122
- this.#computedDependencies = ExpressionUtils.analyzeFunctionDependencies(options.computed || {});
13190
+ // Analyze computed dependencies based on getter functions only.
13191
+ // Writable computeds (defined as { get, set }) contribute their getter for dependency analysis.
13192
+ const computedGetters = {};
13193
+ if (options.computed) {
13194
+ for (const [key, def] of Object.entries(options.computed)) {
13195
+ computedGetters[key] = VApplication.#getComputedGetter(def);
13196
+ }
13197
+ }
13198
+ this.#computedDependencies = ExpressionUtils.analyzeFunctionDependencies(computedGetters);
13123
13199
  // Initialize watcher manager
13124
13200
  this.#watcher = new VWatcher(this.#logger);
13125
13201
  // Initialize bindings from data, computed, and methods
@@ -13262,6 +13338,26 @@ class VApplication {
13262
13338
  }
13263
13339
  }
13264
13340
  }
13341
+ /**
13342
+ * Extracts the getter function from a computed property definition.
13343
+ * Supports both bare function form and { get, set } object form.
13344
+ */
13345
+ static #getComputedGetter(def) {
13346
+ if (typeof def === 'function') {
13347
+ return def;
13348
+ }
13349
+ return def.get;
13350
+ }
13351
+ /**
13352
+ * Extracts the setter function from a computed property definition, if any.
13353
+ * Returns undefined for read-only (function-form) computed properties.
13354
+ */
13355
+ static #getComputedSetter(def) {
13356
+ if (typeof def === 'function') {
13357
+ return undefined;
13358
+ }
13359
+ return def.set;
13360
+ }
13265
13361
  /**
13266
13362
  * Computes dependent identifiers for a given computed property and value.
13267
13363
  * This is used to track dependencies in directives like v-for.
@@ -13324,6 +13420,20 @@ class VApplication {
13324
13420
  }
13325
13421
  }
13326
13422
  }
13423
+ // Register setters for writable computed properties so that assignments to them
13424
+ // (e.g. via v-model or direct mutation through bindings.raw) route through the user-provided
13425
+ // setter, which typically writes back to underlying reactive properties.
13426
+ if (this.#options.computed) {
13427
+ for (const [key, def] of Object.entries(this.#options.computed)) {
13428
+ const setter = VApplication.#getComputedSetter(def);
13429
+ if (setter) {
13430
+ const bindings = this.#bindings;
13431
+ this.#bindings.registerWritableComputed(key, (value) => {
13432
+ setter.call(bindings.raw, value);
13433
+ });
13434
+ }
13435
+ }
13436
+ }
13327
13437
  // Add computed properties (initialization mode)
13328
13438
  this.#recomputeProperties(true);
13329
13439
  }
@@ -13482,7 +13592,7 @@ class VApplication {
13482
13592
  return;
13483
13593
  }
13484
13594
  // Now compute this property
13485
- const computedFn = this.#options.computed[key];
13595
+ const computedFn = VApplication.#getComputedGetter(this.#options.computed[key]);
13486
13596
  try {
13487
13597
  const oldValue = this.#bindings?.get(key);
13488
13598
  const newValue = computedFn.call(this.#bindings?.raw);