@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.
- package/README.md +98 -0
- package/dist/ichigo.cjs +327 -1
- package/dist/ichigo.cjs.map +1 -1
- package/dist/ichigo.esm.js +327 -1
- 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 +327 -1
- package/dist/ichigo.umd.js.map +1 -1
- package/dist/ichigo.umd.min.js +1 -1
- package/dist/types/ichigo/VApplication.d.ts +27 -0
- package/dist/types/ichigo/directives/StandardDirectiveName.d.ts +3 -1
- package/dist/types/ichigo/directives/VRefDirective.d.ts +94 -0
- package/dist/types/ichigo/directives/VStandardDirectiveParser.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ A simple and intuitive reactive framework. Lightweight, fast, and user-friendly
|
|
|
15
15
|
- 🔌 **Lifecycle Hooks** - `@mount`, `@mounted`, `@update`, `@updated`, `@unmount`, `@unmounted` with context (`$ctx`)
|
|
16
16
|
- 💾 **userData Storage** - Proxy-free storage for third-party library instances with auto-cleanup
|
|
17
17
|
- 🧩 **Components** - Reusable Web Components via `defineComponent` with `props`, `slot`, and `$emit`
|
|
18
|
+
- 🔗 **Template refs** - Direct access to DOM elements and component hosts via `ref` and `$refs`
|
|
18
19
|
- 📦 **Lightweight** - Minimal bundle size
|
|
19
20
|
- 🚀 **High Performance** - Efficient batched updates via microtask queue
|
|
20
21
|
- 💪 **TypeScript** - Written in TypeScript with full type support
|
|
@@ -823,6 +824,102 @@ methods: {
|
|
|
823
824
|
}
|
|
824
825
|
```
|
|
825
826
|
|
|
827
|
+
### Template refs (`$refs`)
|
|
828
|
+
|
|
829
|
+
Use the `ref` attribute to obtain a direct reference to a DOM element (or a
|
|
830
|
+
component's host element). Referenced elements are collected into the `$refs`
|
|
831
|
+
object, available on `this` in methods and as `$refs` in template expressions —
|
|
832
|
+
the same mechanism as Vue's `$refs`.
|
|
833
|
+
|
|
834
|
+
```html
|
|
835
|
+
<input ref="search">
|
|
836
|
+
<button @click="focusSearch">Focus</button>
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
```javascript
|
|
840
|
+
methods: {
|
|
841
|
+
focusSearch() {
|
|
842
|
+
// this.$refs.search is the <input> element
|
|
843
|
+
this.$refs.search.focus();
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
**When refs are available:**
|
|
849
|
+
|
|
850
|
+
A ref is registered during the element's mount phase and removed during its
|
|
851
|
+
unmount phase, so it is already populated by the time a `@mounted` hook runs.
|
|
852
|
+
After changing data, wait for `$nextTick` before reading refs for elements that
|
|
853
|
+
were just rendered (e.g. by `v-if` / `v-for`).
|
|
854
|
+
|
|
855
|
+
```javascript
|
|
856
|
+
methods: {
|
|
857
|
+
async showAndFocus() {
|
|
858
|
+
this.editing = true; // reveals <input v-if="editing" ref="field">
|
|
859
|
+
this.$nextTick(() => this.$refs.field.focus());
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
**Refs on components:**
|
|
865
|
+
|
|
866
|
+
A `ref` on a component resolves to the component's **host custom element**, so
|
|
867
|
+
you can call DOM methods on it (focus, scroll, `getBoundingClientRect`,
|
|
868
|
+
`dispatchEvent`, etc.).
|
|
869
|
+
|
|
870
|
+
```html
|
|
871
|
+
<my-card ref="card"></my-card>
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
```javascript
|
|
875
|
+
this.$refs.card; // the <my-card> element
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
**Refs inside `v-for` (arrays):**
|
|
879
|
+
|
|
880
|
+
When `ref` is used on (or inside) a `v-for`, the matching elements are collected
|
|
881
|
+
into an **array** under that name.
|
|
882
|
+
|
|
883
|
+
```html
|
|
884
|
+
<ul>
|
|
885
|
+
<li v-for="item in items" :key="item.id" ref="rows">{{ item.name }}</li>
|
|
886
|
+
</ul>
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
```javascript
|
|
890
|
+
this.$refs.rows; // [<li>, <li>, <li>, ...] — one entry per rendered row
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
Entries are stored in registration order. Because reused rows are not
|
|
894
|
+
re-registered, the array order is not guaranteed to match DOM order after a
|
|
895
|
+
reorder — the same caveat Vue documents.
|
|
896
|
+
|
|
897
|
+
**Dynamic and function refs (`:ref`):**
|
|
898
|
+
|
|
899
|
+
Bind `ref` with `:ref` to compute it from an expression. If the expression
|
|
900
|
+
evaluates to a **string**, it is used as the ref name; if it evaluates to a
|
|
901
|
+
**function**, the function is called with the element on mount and with `null`
|
|
902
|
+
on unmount (a function ref). Function refs are convenient for `v-for` or dynamic
|
|
903
|
+
scenarios where you want full control over storage.
|
|
904
|
+
|
|
905
|
+
```html
|
|
906
|
+
<!-- Dynamic name -->
|
|
907
|
+
<input :ref="fieldName">
|
|
908
|
+
|
|
909
|
+
<!-- Function ref -->
|
|
910
|
+
<li v-for="item in items" :key="item.id" :ref="el => registerRow(item.id, el)">
|
|
911
|
+
{{ item.name }}
|
|
912
|
+
</li>
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
`:ref` is evaluated once at mount (it does not reactively re-run when the bound
|
|
916
|
+
value changes).
|
|
917
|
+
|
|
918
|
+
> **Note:** `$refs` is **not reactive**. Registering or clearing a ref never
|
|
919
|
+
> triggers a re-render, and `$refs` should not be used to drive reactive
|
|
920
|
+
> template output — read it from event handlers and lifecycle hooks instead.
|
|
921
|
+
> This matches Vue's behavior.
|
|
922
|
+
|
|
826
923
|
## Components
|
|
827
924
|
|
|
828
925
|
ichigo.js components are real [Custom Elements](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements)
|
|
@@ -996,6 +1093,7 @@ Creates a new application instance.
|
|
|
996
1093
|
- `$markRaw(obj)`: Marks an object as non-reactive (see [Marking Objects as Non-Reactive](#marking-objects-as-non-reactive))
|
|
997
1094
|
- `$nextTick(callback)`: Runs a callback after the next DOM update
|
|
998
1095
|
- `$emit(name, detail?, options?)`: Dispatches a custom event (see [Events](#events-emit))
|
|
1096
|
+
- `$refs`: Object of template references registered via the `ref` attribute (see [Template refs](#template-refs-refs)). Non-reactive.
|
|
999
1097
|
- `$ctx`: Lifecycle/handler context with `element`, `vnode`, and `userData`
|
|
1000
1098
|
|
|
1001
1099
|
### defineComponent(tagName, options)
|
package/dist/ichigo.cjs
CHANGED
|
@@ -179,6 +179,8 @@
|
|
|
179
179
|
StandardDirectiveName["V_TEXT"] = "v-text";
|
|
180
180
|
/** Focus management directive. */
|
|
181
181
|
StandardDirectiveName["V_FOCUS"] = "v-focus";
|
|
182
|
+
/** Template reference directive (Vue's `ref` / `$refs`). */
|
|
183
|
+
StandardDirectiveName["REF"] = "ref";
|
|
182
184
|
})(StandardDirectiveName || (StandardDirectiveName = {}));
|
|
183
185
|
|
|
184
186
|
// This file was generated. Do not modify manually!
|
|
@@ -13347,6 +13349,240 @@
|
|
|
13347
13349
|
}
|
|
13348
13350
|
}
|
|
13349
13351
|
|
|
13352
|
+
// Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
|
|
13353
|
+
/**
|
|
13354
|
+
* Directive for registering template references (Vue's `ref` / `$refs`).
|
|
13355
|
+
*
|
|
13356
|
+
* Usage:
|
|
13357
|
+
* <input ref="search"> Static name. $refs.search === the element.
|
|
13358
|
+
* <my-card ref="card"></my-card> On a component: $refs.card === the host element.
|
|
13359
|
+
* <li v-for="i in items" ref="rows"> Inside v-for: $refs.rows === array of elements.
|
|
13360
|
+
* <input :ref="dynamicName"> Dynamic name from an expression (evaluated at mount).
|
|
13361
|
+
* <input :ref="el => collect(el)"> Function ref: called with the element on mount,
|
|
13362
|
+
* and with null on unmount (great for v-for / dynamic).
|
|
13363
|
+
*
|
|
13364
|
+
* Behavior notes:
|
|
13365
|
+
* - The reference is registered during the mount phase (before any `@mounted` hook fires) and
|
|
13366
|
+
* removed during the unmount phase, mirroring Vue's lifecycle for `$refs`.
|
|
13367
|
+
* - `$refs` is intentionally NON-reactive: registering or clearing a ref never schedules a render
|
|
13368
|
+
* and must not be used to drive reactive template output (this matches Vue).
|
|
13369
|
+
* - When the same `ref` name appears inside a `v-for` (i.e. one of the element's ancestors is a
|
|
13370
|
+
* `v-for` template), the entries are collected into an array, in registration order. Reused
|
|
13371
|
+
* v-for rows are not re-registered, so the array order is not guaranteed to match DOM order after
|
|
13372
|
+
* reordering — the same caveat Vue documents.
|
|
13373
|
+
* - Cleanup is identity-safe: a ref slot (or array entry) is only cleared when it still points at
|
|
13374
|
+
* THIS element, so a freshly mounted element that reused the name is never clobbered by the
|
|
13375
|
+
* teardown of the element it replaced.
|
|
13376
|
+
*
|
|
13377
|
+
* The directive is purely lifecycle-driven: it has no DOM updater and never templatizes the node.
|
|
13378
|
+
*/
|
|
13379
|
+
class VRefDirective {
|
|
13380
|
+
/**
|
|
13381
|
+
* The virtual node to which this directive is applied.
|
|
13382
|
+
*/
|
|
13383
|
+
#vNode;
|
|
13384
|
+
/**
|
|
13385
|
+
* The static ref name parsed from `ref="name"`. Undefined for the dynamic `:ref` form.
|
|
13386
|
+
*/
|
|
13387
|
+
#staticName;
|
|
13388
|
+
/**
|
|
13389
|
+
* Expression evaluator for the dynamic `:ref` / `v-bind:ref` form. Undefined for static refs.
|
|
13390
|
+
*/
|
|
13391
|
+
#evaluator;
|
|
13392
|
+
/**
|
|
13393
|
+
* The name under which this element was actually registered, retained so unmount removes the
|
|
13394
|
+
* exact same slot it created (the dynamic name is evaluated once at mount and may differ from
|
|
13395
|
+
* the source expression). Undefined when nothing was registered (function ref, or empty value).
|
|
13396
|
+
*/
|
|
13397
|
+
#registeredName;
|
|
13398
|
+
/**
|
|
13399
|
+
* The function supplied by a function ref (`:ref="el => ..."`), retained so it can be invoked
|
|
13400
|
+
* with null on unmount. Undefined unless a function ref was used.
|
|
13401
|
+
*/
|
|
13402
|
+
#functionRef;
|
|
13403
|
+
/**
|
|
13404
|
+
* Whether this ref is registered in array mode (collected because it lives inside a v-for).
|
|
13405
|
+
* Resolved lazily at mount time via {@link #isInsideForLoop}.
|
|
13406
|
+
*/
|
|
13407
|
+
#asArray = false;
|
|
13408
|
+
/**
|
|
13409
|
+
* @param context The context for parsing the directive.
|
|
13410
|
+
*/
|
|
13411
|
+
constructor(context) {
|
|
13412
|
+
this.#vNode = context.vNode;
|
|
13413
|
+
const attrName = context.attribute.name;
|
|
13414
|
+
const isDynamic = attrName !== StandardDirectiveName.REF; // ":ref" or "v-bind:ref"
|
|
13415
|
+
if (isDynamic) {
|
|
13416
|
+
// Dynamic / function ref: the value is an expression.
|
|
13417
|
+
const expression = context.attribute.value;
|
|
13418
|
+
if (expression && context.vNode.bindings) {
|
|
13419
|
+
this.#evaluator = ExpressionEvaluator.create(expression, context.vNode.bindings, context.vNode.vApplication.functionDependencies);
|
|
13420
|
+
}
|
|
13421
|
+
}
|
|
13422
|
+
else {
|
|
13423
|
+
// Static ref: the value is the literal name.
|
|
13424
|
+
const name = context.attribute.value.trim();
|
|
13425
|
+
this.#staticName = name.length > 0 ? name : undefined;
|
|
13426
|
+
}
|
|
13427
|
+
// Remove the directive attribute from the element so it does not linger in the DOM.
|
|
13428
|
+
this.#vNode.node.removeAttribute(attrName);
|
|
13429
|
+
}
|
|
13430
|
+
/**
|
|
13431
|
+
* @inheritdoc
|
|
13432
|
+
*/
|
|
13433
|
+
get name() {
|
|
13434
|
+
return StandardDirectiveName.REF;
|
|
13435
|
+
}
|
|
13436
|
+
/**
|
|
13437
|
+
* @inheritdoc
|
|
13438
|
+
*/
|
|
13439
|
+
get vNode() {
|
|
13440
|
+
return this.#vNode;
|
|
13441
|
+
}
|
|
13442
|
+
/**
|
|
13443
|
+
* @inheritdoc
|
|
13444
|
+
*/
|
|
13445
|
+
get needsAnchor() {
|
|
13446
|
+
return false;
|
|
13447
|
+
}
|
|
13448
|
+
/**
|
|
13449
|
+
* @inheritdoc
|
|
13450
|
+
*/
|
|
13451
|
+
get bindingsPreparer() {
|
|
13452
|
+
return undefined;
|
|
13453
|
+
}
|
|
13454
|
+
/**
|
|
13455
|
+
* @inheritdoc
|
|
13456
|
+
*/
|
|
13457
|
+
get domUpdater() {
|
|
13458
|
+
// Refs are resolved once at mount; they are deliberately not reactive (see class docs).
|
|
13459
|
+
return undefined;
|
|
13460
|
+
}
|
|
13461
|
+
/**
|
|
13462
|
+
* @inheritdoc
|
|
13463
|
+
*/
|
|
13464
|
+
get templatize() {
|
|
13465
|
+
return false;
|
|
13466
|
+
}
|
|
13467
|
+
/**
|
|
13468
|
+
* @inheritdoc
|
|
13469
|
+
*/
|
|
13470
|
+
get dependentIdentifiers() {
|
|
13471
|
+
// This directive never drives reactive updates, so it contributes no dependencies.
|
|
13472
|
+
return [];
|
|
13473
|
+
}
|
|
13474
|
+
/**
|
|
13475
|
+
* @inheritdoc
|
|
13476
|
+
*/
|
|
13477
|
+
get onMount() {
|
|
13478
|
+
// Register during the mount phase so the reference is available before any `@mounted`
|
|
13479
|
+
// hook runs (those fire a frame later via requestAnimationFrame).
|
|
13480
|
+
return () => this.#register();
|
|
13481
|
+
}
|
|
13482
|
+
/**
|
|
13483
|
+
* @inheritdoc
|
|
13484
|
+
*/
|
|
13485
|
+
get onMounted() {
|
|
13486
|
+
return undefined;
|
|
13487
|
+
}
|
|
13488
|
+
/**
|
|
13489
|
+
* @inheritdoc
|
|
13490
|
+
*/
|
|
13491
|
+
get onUpdate() {
|
|
13492
|
+
return undefined;
|
|
13493
|
+
}
|
|
13494
|
+
/**
|
|
13495
|
+
* @inheritdoc
|
|
13496
|
+
*/
|
|
13497
|
+
get onUpdated() {
|
|
13498
|
+
return undefined;
|
|
13499
|
+
}
|
|
13500
|
+
/**
|
|
13501
|
+
* @inheritdoc
|
|
13502
|
+
*/
|
|
13503
|
+
get onUnmount() {
|
|
13504
|
+
return undefined;
|
|
13505
|
+
}
|
|
13506
|
+
/**
|
|
13507
|
+
* @inheritdoc
|
|
13508
|
+
*/
|
|
13509
|
+
get onUnmounted() {
|
|
13510
|
+
// Clear during the unmount phase. The element reference is still valid here.
|
|
13511
|
+
return () => this.#unregister();
|
|
13512
|
+
}
|
|
13513
|
+
/**
|
|
13514
|
+
* @inheritdoc
|
|
13515
|
+
*/
|
|
13516
|
+
destroy() {
|
|
13517
|
+
// No additional cleanup beyond onUnmounted; kept for interface completeness.
|
|
13518
|
+
}
|
|
13519
|
+
/**
|
|
13520
|
+
* Registers this element under its resolved ref name (or invokes its function ref).
|
|
13521
|
+
*/
|
|
13522
|
+
#register() {
|
|
13523
|
+
const element = this.#vNode.node;
|
|
13524
|
+
if (this.#evaluator) {
|
|
13525
|
+
// Dynamic / function ref.
|
|
13526
|
+
let value;
|
|
13527
|
+
try {
|
|
13528
|
+
value = this.#evaluator.evaluate();
|
|
13529
|
+
}
|
|
13530
|
+
catch {
|
|
13531
|
+
// Evaluation errors are already logged by ExpressionEvaluator; nothing to register.
|
|
13532
|
+
return;
|
|
13533
|
+
}
|
|
13534
|
+
if (typeof value === 'function') {
|
|
13535
|
+
this.#functionRef = value;
|
|
13536
|
+
this.#functionRef(element);
|
|
13537
|
+
return;
|
|
13538
|
+
}
|
|
13539
|
+
if (value === null || value === undefined || value === '') {
|
|
13540
|
+
return; // Nothing to register under an empty dynamic name.
|
|
13541
|
+
}
|
|
13542
|
+
this.#registeredName = String(value);
|
|
13543
|
+
}
|
|
13544
|
+
else if (this.#staticName) {
|
|
13545
|
+
this.#registeredName = this.#staticName;
|
|
13546
|
+
}
|
|
13547
|
+
if (!this.#registeredName) {
|
|
13548
|
+
return;
|
|
13549
|
+
}
|
|
13550
|
+
this.#asArray = this.#isInsideForLoop();
|
|
13551
|
+
this.#vNode.vApplication.registerRef(this.#registeredName, element, this.#asArray);
|
|
13552
|
+
}
|
|
13553
|
+
/**
|
|
13554
|
+
* Removes this element's reference (or invokes its function ref with null).
|
|
13555
|
+
*/
|
|
13556
|
+
#unregister() {
|
|
13557
|
+
if (this.#functionRef) {
|
|
13558
|
+
this.#functionRef(null);
|
|
13559
|
+
this.#functionRef = undefined;
|
|
13560
|
+
return;
|
|
13561
|
+
}
|
|
13562
|
+
if (this.#registeredName) {
|
|
13563
|
+
this.#vNode.vApplication.unregisterRef(this.#registeredName, this.#vNode.node);
|
|
13564
|
+
this.#registeredName = undefined;
|
|
13565
|
+
}
|
|
13566
|
+
}
|
|
13567
|
+
/**
|
|
13568
|
+
* Determines whether this element is rendered inside a `v-for` by walking up the VNode tree.
|
|
13569
|
+
* A `v-for` multiplies its template into N rows, so a static ref name on (or within) a row is
|
|
13570
|
+
* shared by N elements and must be collected into an array — matching Vue's documented
|
|
13571
|
+
* "ref inside v-for yields an array" behavior. The walk inspects ancestors only: the directive
|
|
13572
|
+
* lives on a cloned row (or a descendant of one), and the clone's parent chain leads back to the
|
|
13573
|
+
* `v-for` template node, which still carries the `v-for` directive.
|
|
13574
|
+
*/
|
|
13575
|
+
#isInsideForLoop() {
|
|
13576
|
+
for (let ancestor = this.#vNode.parentVNode; ancestor; ancestor = ancestor.parentVNode) {
|
|
13577
|
+
const hasFor = ancestor.directiveManager?.directives?.some(d => d.name === StandardDirectiveName.V_FOR);
|
|
13578
|
+
if (hasFor) {
|
|
13579
|
+
return true;
|
|
13580
|
+
}
|
|
13581
|
+
}
|
|
13582
|
+
return false;
|
|
13583
|
+
}
|
|
13584
|
+
}
|
|
13585
|
+
|
|
13350
13586
|
// Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
|
|
13351
13587
|
/**
|
|
13352
13588
|
* The directive parser for standard directives.
|
|
@@ -13391,15 +13627,33 @@
|
|
|
13391
13627
|
context.attribute.name === StandardDirectiveName.V_TEXT ||
|
|
13392
13628
|
// v-focus, v-focus.<modifier>
|
|
13393
13629
|
context.attribute.name === StandardDirectiveName.V_FOCUS ||
|
|
13394
|
-
context.attribute.name.startsWith(StandardDirectiveName.V_FOCUS + ".")
|
|
13630
|
+
context.attribute.name.startsWith(StandardDirectiveName.V_FOCUS + ".") ||
|
|
13631
|
+
// ref, :ref, v-bind:ref
|
|
13632
|
+
this.#isRefAttribute(context.attribute.name)) {
|
|
13395
13633
|
return true;
|
|
13396
13634
|
}
|
|
13397
13635
|
return false;
|
|
13398
13636
|
}
|
|
13637
|
+
/**
|
|
13638
|
+
* Determines whether the attribute is a template reference (`ref`, `:ref`, or `v-bind:ref`).
|
|
13639
|
+
* The dynamic forms also match the generic v-bind shorthand, so {@link parse} dispatches them
|
|
13640
|
+
* to VRefDirective explicitly before the v-bind branch is reached.
|
|
13641
|
+
*/
|
|
13642
|
+
#isRefAttribute(name) {
|
|
13643
|
+
return name === StandardDirectiveName.REF ||
|
|
13644
|
+
name === ":" + StandardDirectiveName.REF ||
|
|
13645
|
+
name === StandardDirectiveName.V_BIND + ":" + StandardDirectiveName.REF;
|
|
13646
|
+
}
|
|
13399
13647
|
/**
|
|
13400
13648
|
* @inheritdoc
|
|
13401
13649
|
*/
|
|
13402
13650
|
parse(context) {
|
|
13651
|
+
// ref, :ref, v-bind:ref
|
|
13652
|
+
// Checked before the v-bind branch below, since ":ref" / "v-bind:ref" also match the
|
|
13653
|
+
// generic v-bind shorthand but must be handled as template references, not attribute binds.
|
|
13654
|
+
if (this.#isRefAttribute(context.attribute.name)) {
|
|
13655
|
+
return new VRefDirective(context);
|
|
13656
|
+
}
|
|
13403
13657
|
if (context.attribute.name === StandardDirectiveName.V_IF) {
|
|
13404
13658
|
return new VIfDirective(context);
|
|
13405
13659
|
}
|
|
@@ -13836,6 +14090,17 @@
|
|
|
13836
14090
|
* The watcher manager for this application.
|
|
13837
14091
|
*/
|
|
13838
14092
|
#watcher;
|
|
14093
|
+
/**
|
|
14094
|
+
* Template references registered by the `ref` directive, exposed to data(), methods, and
|
|
14095
|
+
* expressions as `$refs` (Vue's `$refs`). Each entry is either a single Element or, for a
|
|
14096
|
+
* `ref` used inside `v-for`, an array of Elements.
|
|
14097
|
+
*
|
|
14098
|
+
* This object is intentionally marked raw (non-reactive): mutating it when an element mounts or
|
|
14099
|
+
* unmounts must never schedule a render, and it must not be used to drive reactive template
|
|
14100
|
+
* output — matching Vue's semantics for `$refs`. It is held as a direct field (in addition to
|
|
14101
|
+
* being injected as the `$refs` binding) so registration is a plain property write.
|
|
14102
|
+
*/
|
|
14103
|
+
#refs = {};
|
|
13839
14104
|
/**
|
|
13840
14105
|
* Flag to indicate if an update is already scheduled.
|
|
13841
14106
|
*/
|
|
@@ -13936,6 +14201,13 @@
|
|
|
13936
14201
|
get functionDependencies() {
|
|
13937
14202
|
return this.#functionDependencies;
|
|
13938
14203
|
}
|
|
14204
|
+
/**
|
|
14205
|
+
* Gets the template references registered via the `ref` directive (Vue's `$refs`). The same
|
|
14206
|
+
* object is exposed to data(), methods, and expressions as `$refs`. Non-reactive by design.
|
|
14207
|
+
*/
|
|
14208
|
+
get refs() {
|
|
14209
|
+
return this.#refs;
|
|
14210
|
+
}
|
|
13939
14211
|
/**
|
|
13940
14212
|
* Mounts the application.
|
|
13941
14213
|
* @param target The CSS selector string or HTMLElement to mount the application to.
|
|
@@ -14064,6 +14336,56 @@
|
|
|
14064
14336
|
}
|
|
14065
14337
|
return identifiers;
|
|
14066
14338
|
}
|
|
14339
|
+
/**
|
|
14340
|
+
* Registers a template reference under the given name. Called by the `ref` directive during the
|
|
14341
|
+
* mount phase. When {@param asArray} is true (the element lives inside a `v-for`), references
|
|
14342
|
+
* accumulate into an array in registration order; otherwise the name holds a single element.
|
|
14343
|
+
*
|
|
14344
|
+
* Mutates the raw (non-reactive) `$refs` object directly, so this never schedules a render.
|
|
14345
|
+
*
|
|
14346
|
+
* @param name The ref name.
|
|
14347
|
+
* @param element The element (or component host element) to register.
|
|
14348
|
+
* @param asArray Whether to collect into an array (refs used inside a v-for).
|
|
14349
|
+
*/
|
|
14350
|
+
registerRef(name, element, asArray) {
|
|
14351
|
+
if (asArray) {
|
|
14352
|
+
const current = this.#refs[name];
|
|
14353
|
+
if (Array.isArray(current)) {
|
|
14354
|
+
if (!current.includes(element)) {
|
|
14355
|
+
current.push(element);
|
|
14356
|
+
}
|
|
14357
|
+
}
|
|
14358
|
+
else {
|
|
14359
|
+
this.#refs[name] = [element];
|
|
14360
|
+
}
|
|
14361
|
+
return;
|
|
14362
|
+
}
|
|
14363
|
+
this.#refs[name] = element;
|
|
14364
|
+
}
|
|
14365
|
+
/**
|
|
14366
|
+
* Removes a template reference registered under the given name. Called by the `ref` directive
|
|
14367
|
+
* during the unmount phase. The removal is identity-safe: an entry is only cleared when it still
|
|
14368
|
+
* points at {@param element}, so a newly mounted element that reused the name is never clobbered
|
|
14369
|
+
* by the teardown of the element it replaced.
|
|
14370
|
+
*
|
|
14371
|
+
* @param name The ref name.
|
|
14372
|
+
* @param element The element whose registration should be removed.
|
|
14373
|
+
*/
|
|
14374
|
+
unregisterRef(name, element) {
|
|
14375
|
+
const current = this.#refs[name];
|
|
14376
|
+
if (Array.isArray(current)) {
|
|
14377
|
+
const index = current.indexOf(element);
|
|
14378
|
+
if (index !== -1) {
|
|
14379
|
+
current.splice(index, 1);
|
|
14380
|
+
}
|
|
14381
|
+
if (current.length === 0) {
|
|
14382
|
+
delete this.#refs[name];
|
|
14383
|
+
}
|
|
14384
|
+
}
|
|
14385
|
+
else if (current === element) {
|
|
14386
|
+
delete this.#refs[name];
|
|
14387
|
+
}
|
|
14388
|
+
}
|
|
14067
14389
|
/**
|
|
14068
14390
|
* Initializes bindings from data, computed properties, and methods.
|
|
14069
14391
|
* @returns The initialized bindings object.
|
|
@@ -14084,6 +14406,10 @@
|
|
|
14084
14406
|
this.#bindings.set('$nextTick', (callback) => this.#nextTick(callback));
|
|
14085
14407
|
this.#bindings.set('$markRaw', (obj) => ReactiveProxy.markRaw(obj));
|
|
14086
14408
|
this.#bindings.set('$emit', (name, detail, options) => this.#emit(name, detail, options));
|
|
14409
|
+
// Inject the (non-reactive) $refs container. Marking it raw prevents VBindings from wrapping
|
|
14410
|
+
// it in a reactive proxy, so populating it from the ref directive never schedules a render.
|
|
14411
|
+
ReactiveProxy.markRaw(this.#refs);
|
|
14412
|
+
this.#bindings.set('$refs', this.#refs);
|
|
14087
14413
|
// Add methods
|
|
14088
14414
|
if (this.#options.methods) {
|
|
14089
14415
|
for (const [key, method] of Object.entries(this.#options.methods)) {
|