@mintjamsinc/ichigojs 0.1.74 → 0.1.75

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.
@@ -173,6 +173,8 @@ var StandardDirectiveName;
173
173
  StandardDirectiveName["V_TEXT"] = "v-text";
174
174
  /** Focus management directive. */
175
175
  StandardDirectiveName["V_FOCUS"] = "v-focus";
176
+ /** Template reference directive (Vue's `ref` / `$refs`). */
177
+ StandardDirectiveName["REF"] = "ref";
176
178
  })(StandardDirectiveName || (StandardDirectiveName = {}));
177
179
 
178
180
  // This file was generated. Do not modify manually!
@@ -13341,6 +13343,240 @@ class VFocusDirective {
13341
13343
  }
13342
13344
  }
13343
13345
 
13346
+ // Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
13347
+ /**
13348
+ * Directive for registering template references (Vue's `ref` / `$refs`).
13349
+ *
13350
+ * Usage:
13351
+ * <input ref="search"> Static name. $refs.search === the element.
13352
+ * <my-card ref="card"></my-card> On a component: $refs.card === the host element.
13353
+ * <li v-for="i in items" ref="rows"> Inside v-for: $refs.rows === array of elements.
13354
+ * <input :ref="dynamicName"> Dynamic name from an expression (evaluated at mount).
13355
+ * <input :ref="el => collect(el)"> Function ref: called with the element on mount,
13356
+ * and with null on unmount (great for v-for / dynamic).
13357
+ *
13358
+ * Behavior notes:
13359
+ * - The reference is registered during the mount phase (before any `@mounted` hook fires) and
13360
+ * removed during the unmount phase, mirroring Vue's lifecycle for `$refs`.
13361
+ * - `$refs` is intentionally NON-reactive: registering or clearing a ref never schedules a render
13362
+ * and must not be used to drive reactive template output (this matches Vue).
13363
+ * - When the same `ref` name appears inside a `v-for` (i.e. one of the element's ancestors is a
13364
+ * `v-for` template), the entries are collected into an array, in registration order. Reused
13365
+ * v-for rows are not re-registered, so the array order is not guaranteed to match DOM order after
13366
+ * reordering — the same caveat Vue documents.
13367
+ * - Cleanup is identity-safe: a ref slot (or array entry) is only cleared when it still points at
13368
+ * THIS element, so a freshly mounted element that reused the name is never clobbered by the
13369
+ * teardown of the element it replaced.
13370
+ *
13371
+ * The directive is purely lifecycle-driven: it has no DOM updater and never templatizes the node.
13372
+ */
13373
+ class VRefDirective {
13374
+ /**
13375
+ * The virtual node to which this directive is applied.
13376
+ */
13377
+ #vNode;
13378
+ /**
13379
+ * The static ref name parsed from `ref="name"`. Undefined for the dynamic `:ref` form.
13380
+ */
13381
+ #staticName;
13382
+ /**
13383
+ * Expression evaluator for the dynamic `:ref` / `v-bind:ref` form. Undefined for static refs.
13384
+ */
13385
+ #evaluator;
13386
+ /**
13387
+ * The name under which this element was actually registered, retained so unmount removes the
13388
+ * exact same slot it created (the dynamic name is evaluated once at mount and may differ from
13389
+ * the source expression). Undefined when nothing was registered (function ref, or empty value).
13390
+ */
13391
+ #registeredName;
13392
+ /**
13393
+ * The function supplied by a function ref (`:ref="el => ..."`), retained so it can be invoked
13394
+ * with null on unmount. Undefined unless a function ref was used.
13395
+ */
13396
+ #functionRef;
13397
+ /**
13398
+ * Whether this ref is registered in array mode (collected because it lives inside a v-for).
13399
+ * Resolved lazily at mount time via {@link #isInsideForLoop}.
13400
+ */
13401
+ #asArray = false;
13402
+ /**
13403
+ * @param context The context for parsing the directive.
13404
+ */
13405
+ constructor(context) {
13406
+ this.#vNode = context.vNode;
13407
+ const attrName = context.attribute.name;
13408
+ const isDynamic = attrName !== StandardDirectiveName.REF; // ":ref" or "v-bind:ref"
13409
+ if (isDynamic) {
13410
+ // Dynamic / function ref: the value is an expression.
13411
+ const expression = context.attribute.value;
13412
+ if (expression && context.vNode.bindings) {
13413
+ this.#evaluator = ExpressionEvaluator.create(expression, context.vNode.bindings, context.vNode.vApplication.functionDependencies);
13414
+ }
13415
+ }
13416
+ else {
13417
+ // Static ref: the value is the literal name.
13418
+ const name = context.attribute.value.trim();
13419
+ this.#staticName = name.length > 0 ? name : undefined;
13420
+ }
13421
+ // Remove the directive attribute from the element so it does not linger in the DOM.
13422
+ this.#vNode.node.removeAttribute(attrName);
13423
+ }
13424
+ /**
13425
+ * @inheritdoc
13426
+ */
13427
+ get name() {
13428
+ return StandardDirectiveName.REF;
13429
+ }
13430
+ /**
13431
+ * @inheritdoc
13432
+ */
13433
+ get vNode() {
13434
+ return this.#vNode;
13435
+ }
13436
+ /**
13437
+ * @inheritdoc
13438
+ */
13439
+ get needsAnchor() {
13440
+ return false;
13441
+ }
13442
+ /**
13443
+ * @inheritdoc
13444
+ */
13445
+ get bindingsPreparer() {
13446
+ return undefined;
13447
+ }
13448
+ /**
13449
+ * @inheritdoc
13450
+ */
13451
+ get domUpdater() {
13452
+ // Refs are resolved once at mount; they are deliberately not reactive (see class docs).
13453
+ return undefined;
13454
+ }
13455
+ /**
13456
+ * @inheritdoc
13457
+ */
13458
+ get templatize() {
13459
+ return false;
13460
+ }
13461
+ /**
13462
+ * @inheritdoc
13463
+ */
13464
+ get dependentIdentifiers() {
13465
+ // This directive never drives reactive updates, so it contributes no dependencies.
13466
+ return [];
13467
+ }
13468
+ /**
13469
+ * @inheritdoc
13470
+ */
13471
+ get onMount() {
13472
+ // Register during the mount phase so the reference is available before any `@mounted`
13473
+ // hook runs (those fire a frame later via requestAnimationFrame).
13474
+ return () => this.#register();
13475
+ }
13476
+ /**
13477
+ * @inheritdoc
13478
+ */
13479
+ get onMounted() {
13480
+ return undefined;
13481
+ }
13482
+ /**
13483
+ * @inheritdoc
13484
+ */
13485
+ get onUpdate() {
13486
+ return undefined;
13487
+ }
13488
+ /**
13489
+ * @inheritdoc
13490
+ */
13491
+ get onUpdated() {
13492
+ return undefined;
13493
+ }
13494
+ /**
13495
+ * @inheritdoc
13496
+ */
13497
+ get onUnmount() {
13498
+ return undefined;
13499
+ }
13500
+ /**
13501
+ * @inheritdoc
13502
+ */
13503
+ get onUnmounted() {
13504
+ // Clear during the unmount phase. The element reference is still valid here.
13505
+ return () => this.#unregister();
13506
+ }
13507
+ /**
13508
+ * @inheritdoc
13509
+ */
13510
+ destroy() {
13511
+ // No additional cleanup beyond onUnmounted; kept for interface completeness.
13512
+ }
13513
+ /**
13514
+ * Registers this element under its resolved ref name (or invokes its function ref).
13515
+ */
13516
+ #register() {
13517
+ const element = this.#vNode.node;
13518
+ if (this.#evaluator) {
13519
+ // Dynamic / function ref.
13520
+ let value;
13521
+ try {
13522
+ value = this.#evaluator.evaluate();
13523
+ }
13524
+ catch {
13525
+ // Evaluation errors are already logged by ExpressionEvaluator; nothing to register.
13526
+ return;
13527
+ }
13528
+ if (typeof value === 'function') {
13529
+ this.#functionRef = value;
13530
+ this.#functionRef(element);
13531
+ return;
13532
+ }
13533
+ if (value === null || value === undefined || value === '') {
13534
+ return; // Nothing to register under an empty dynamic name.
13535
+ }
13536
+ this.#registeredName = String(value);
13537
+ }
13538
+ else if (this.#staticName) {
13539
+ this.#registeredName = this.#staticName;
13540
+ }
13541
+ if (!this.#registeredName) {
13542
+ return;
13543
+ }
13544
+ this.#asArray = this.#isInsideForLoop();
13545
+ this.#vNode.vApplication.registerRef(this.#registeredName, element, this.#asArray);
13546
+ }
13547
+ /**
13548
+ * Removes this element's reference (or invokes its function ref with null).
13549
+ */
13550
+ #unregister() {
13551
+ if (this.#functionRef) {
13552
+ this.#functionRef(null);
13553
+ this.#functionRef = undefined;
13554
+ return;
13555
+ }
13556
+ if (this.#registeredName) {
13557
+ this.#vNode.vApplication.unregisterRef(this.#registeredName, this.#vNode.node);
13558
+ this.#registeredName = undefined;
13559
+ }
13560
+ }
13561
+ /**
13562
+ * Determines whether this element is rendered inside a `v-for` by walking up the VNode tree.
13563
+ * A `v-for` multiplies its template into N rows, so a static ref name on (or within) a row is
13564
+ * shared by N elements and must be collected into an array — matching Vue's documented
13565
+ * "ref inside v-for yields an array" behavior. The walk inspects ancestors only: the directive
13566
+ * lives on a cloned row (or a descendant of one), and the clone's parent chain leads back to the
13567
+ * `v-for` template node, which still carries the `v-for` directive.
13568
+ */
13569
+ #isInsideForLoop() {
13570
+ for (let ancestor = this.#vNode.parentVNode; ancestor; ancestor = ancestor.parentVNode) {
13571
+ const hasFor = ancestor.directiveManager?.directives?.some(d => d.name === StandardDirectiveName.V_FOR);
13572
+ if (hasFor) {
13573
+ return true;
13574
+ }
13575
+ }
13576
+ return false;
13577
+ }
13578
+ }
13579
+
13344
13580
  // Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
13345
13581
  /**
13346
13582
  * The directive parser for standard directives.
@@ -13385,15 +13621,33 @@ class VStandardDirectiveParser {
13385
13621
  context.attribute.name === StandardDirectiveName.V_TEXT ||
13386
13622
  // v-focus, v-focus.<modifier>
13387
13623
  context.attribute.name === StandardDirectiveName.V_FOCUS ||
13388
- context.attribute.name.startsWith(StandardDirectiveName.V_FOCUS + ".")) {
13624
+ context.attribute.name.startsWith(StandardDirectiveName.V_FOCUS + ".") ||
13625
+ // ref, :ref, v-bind:ref
13626
+ this.#isRefAttribute(context.attribute.name)) {
13389
13627
  return true;
13390
13628
  }
13391
13629
  return false;
13392
13630
  }
13631
+ /**
13632
+ * Determines whether the attribute is a template reference (`ref`, `:ref`, or `v-bind:ref`).
13633
+ * The dynamic forms also match the generic v-bind shorthand, so {@link parse} dispatches them
13634
+ * to VRefDirective explicitly before the v-bind branch is reached.
13635
+ */
13636
+ #isRefAttribute(name) {
13637
+ return name === StandardDirectiveName.REF ||
13638
+ name === ":" + StandardDirectiveName.REF ||
13639
+ name === StandardDirectiveName.V_BIND + ":" + StandardDirectiveName.REF;
13640
+ }
13393
13641
  /**
13394
13642
  * @inheritdoc
13395
13643
  */
13396
13644
  parse(context) {
13645
+ // ref, :ref, v-bind:ref
13646
+ // Checked before the v-bind branch below, since ":ref" / "v-bind:ref" also match the
13647
+ // generic v-bind shorthand but must be handled as template references, not attribute binds.
13648
+ if (this.#isRefAttribute(context.attribute.name)) {
13649
+ return new VRefDirective(context);
13650
+ }
13397
13651
  if (context.attribute.name === StandardDirectiveName.V_IF) {
13398
13652
  return new VIfDirective(context);
13399
13653
  }
@@ -13830,6 +14084,17 @@ class VApplication {
13830
14084
  * The watcher manager for this application.
13831
14085
  */
13832
14086
  #watcher;
14087
+ /**
14088
+ * Template references registered by the `ref` directive, exposed to data(), methods, and
14089
+ * expressions as `$refs` (Vue's `$refs`). Each entry is either a single Element or, for a
14090
+ * `ref` used inside `v-for`, an array of Elements.
14091
+ *
14092
+ * This object is intentionally marked raw (non-reactive): mutating it when an element mounts or
14093
+ * unmounts must never schedule a render, and it must not be used to drive reactive template
14094
+ * output — matching Vue's semantics for `$refs`. It is held as a direct field (in addition to
14095
+ * being injected as the `$refs` binding) so registration is a plain property write.
14096
+ */
14097
+ #refs = {};
13833
14098
  /**
13834
14099
  * Flag to indicate if an update is already scheduled.
13835
14100
  */
@@ -13930,6 +14195,13 @@ class VApplication {
13930
14195
  get functionDependencies() {
13931
14196
  return this.#functionDependencies;
13932
14197
  }
14198
+ /**
14199
+ * Gets the template references registered via the `ref` directive (Vue's `$refs`). The same
14200
+ * object is exposed to data(), methods, and expressions as `$refs`. Non-reactive by design.
14201
+ */
14202
+ get refs() {
14203
+ return this.#refs;
14204
+ }
13933
14205
  /**
13934
14206
  * Mounts the application.
13935
14207
  * @param target The CSS selector string or HTMLElement to mount the application to.
@@ -14058,6 +14330,56 @@ class VApplication {
14058
14330
  }
14059
14331
  return identifiers;
14060
14332
  }
14333
+ /**
14334
+ * Registers a template reference under the given name. Called by the `ref` directive during the
14335
+ * mount phase. When {@param asArray} is true (the element lives inside a `v-for`), references
14336
+ * accumulate into an array in registration order; otherwise the name holds a single element.
14337
+ *
14338
+ * Mutates the raw (non-reactive) `$refs` object directly, so this never schedules a render.
14339
+ *
14340
+ * @param name The ref name.
14341
+ * @param element The element (or component host element) to register.
14342
+ * @param asArray Whether to collect into an array (refs used inside a v-for).
14343
+ */
14344
+ registerRef(name, element, asArray) {
14345
+ if (asArray) {
14346
+ const current = this.#refs[name];
14347
+ if (Array.isArray(current)) {
14348
+ if (!current.includes(element)) {
14349
+ current.push(element);
14350
+ }
14351
+ }
14352
+ else {
14353
+ this.#refs[name] = [element];
14354
+ }
14355
+ return;
14356
+ }
14357
+ this.#refs[name] = element;
14358
+ }
14359
+ /**
14360
+ * Removes a template reference registered under the given name. Called by the `ref` directive
14361
+ * during the unmount phase. The removal is identity-safe: an entry is only cleared when it still
14362
+ * points at {@param element}, so a newly mounted element that reused the name is never clobbered
14363
+ * by the teardown of the element it replaced.
14364
+ *
14365
+ * @param name The ref name.
14366
+ * @param element The element whose registration should be removed.
14367
+ */
14368
+ unregisterRef(name, element) {
14369
+ const current = this.#refs[name];
14370
+ if (Array.isArray(current)) {
14371
+ const index = current.indexOf(element);
14372
+ if (index !== -1) {
14373
+ current.splice(index, 1);
14374
+ }
14375
+ if (current.length === 0) {
14376
+ delete this.#refs[name];
14377
+ }
14378
+ }
14379
+ else if (current === element) {
14380
+ delete this.#refs[name];
14381
+ }
14382
+ }
14061
14383
  /**
14062
14384
  * Initializes bindings from data, computed properties, and methods.
14063
14385
  * @returns The initialized bindings object.
@@ -14078,6 +14400,10 @@ class VApplication {
14078
14400
  this.#bindings.set('$nextTick', (callback) => this.#nextTick(callback));
14079
14401
  this.#bindings.set('$markRaw', (obj) => ReactiveProxy.markRaw(obj));
14080
14402
  this.#bindings.set('$emit', (name, detail, options) => this.#emit(name, detail, options));
14403
+ // Inject the (non-reactive) $refs container. Marking it raw prevents VBindings from wrapping
14404
+ // it in a reactive proxy, so populating it from the ref directive never schedules a render.
14405
+ ReactiveProxy.markRaw(this.#refs);
14406
+ this.#bindings.set('$refs', this.#refs);
14081
14407
  // Add methods
14082
14408
  if (this.#options.methods) {
14083
14409
  for (const [key, method] of Object.entries(this.#options.methods)) {