@mintjamsinc/ichigojs 0.1.7 → 0.1.8

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.
@@ -60,6 +60,7 @@
60
60
  StandardDirectiveName["V_BIND"] = "v-bind";
61
61
  StandardDirectiveName["V_MODEL"] = "v-model";
62
62
  StandardDirectiveName["V_RESIZE"] = "v-resize";
63
+ StandardDirectiveName["V_INTERSECTION"] = "v-intersection";
63
64
  })(StandardDirectiveName || (StandardDirectiveName = {}));
64
65
 
65
66
  // This file was generated. Do not modify manually!
@@ -6874,6 +6875,19 @@
6874
6875
  get isKey() {
6875
6876
  return (this.#attributeName === 'key');
6876
6877
  }
6878
+ /**
6879
+ * Indicates if this directive is binding the "options" attribute or any of its sub-properties (e.g., "options.intersection").
6880
+ * The "options" attribute is special and is used for passing options to certain directives like VIntersectionDirective.
6881
+ */
6882
+ get isOptions() {
6883
+ return (this.#attributeName === 'options' || this.#attributeName?.startsWith('options.') === true);
6884
+ }
6885
+ /**
6886
+ * Gets the name of the attribute being bound (e.g., "src", "class", "options", "options.intersection").
6887
+ */
6888
+ get attributeName() {
6889
+ return this.#attributeName;
6890
+ }
6877
6891
  /**
6878
6892
  * Gets the original expression string from the directive.
6879
6893
  */
@@ -6926,8 +6940,8 @@
6926
6940
  * Renders the bound attribute by evaluating the expression and updating the DOM element.
6927
6941
  */
6928
6942
  #render() {
6929
- // If this directive is binding the "key" attribute, do nothing
6930
- if (this.isKey) {
6943
+ // Do nothing for special attributes
6944
+ if (this.isKey || this.isOptions) {
6931
6945
  return;
6932
6946
  }
6933
6947
  const element = this.#vNode.node;
@@ -7371,16 +7385,47 @@
7371
7385
  }
7372
7386
 
7373
7387
  // Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
7388
+ /**
7389
+ * Manages directives associated with a virtual node (VNode).
7390
+ * This class is responsible for parsing, storing, and managing the lifecycle of directives.
7391
+ * It also provides access to bindings preparers and DOM updaters from the associated directives.
7392
+ */
7374
7393
  class VDirectiveManager {
7375
7394
  /**
7376
7395
  * The virtual node to which this directive handler is associated.
7377
7396
  */
7378
7397
  #vNode;
7398
+ /**
7399
+ * The list of directives associated with the virtual node.
7400
+ * This may be undefined if there are no directives.
7401
+ */
7379
7402
  #directives;
7403
+ /**
7404
+ * The anchor comment node used for certain directives (e.g., v-if, v-for).
7405
+ * This may be undefined if no directive requires an anchor.
7406
+ */
7380
7407
  #anchorNode;
7408
+ /**
7409
+ * The list of bindings preparers from the associated directives.
7410
+ * This may be undefined if no directive provides a bindings preparer.
7411
+ */
7381
7412
  #bindingsPreparers;
7413
+ /**
7414
+ * The list of DOM updaters from the associated directives.
7415
+ * This may be undefined if no directive provides a DOM updater.
7416
+ */
7382
7417
  #domUpdaters;
7418
+ /**
7419
+ * The directive that binds the ":key" or "v-bind:key" attribute, if any.
7420
+ * This directive is special and is used for optimizing rendering of lists.
7421
+ * If no such directive exists, this is undefined.
7422
+ */
7383
7423
  #keyDirective;
7424
+ /**
7425
+ * A cache of VBindDirectives for options specific to certain directives.
7426
+ * The keys are directive names (e.g., 'options', 'options.intersection').
7427
+ */
7428
+ #optionsDirectives = {};
7384
7429
  constructor(vNode) {
7385
7430
  // Directives can only be associated with element nodes
7386
7431
  if (vNode.nodeType !== Node.ELEMENT_NODE) {
@@ -7438,6 +7483,35 @@
7438
7483
  get keyDirective() {
7439
7484
  return this.#keyDirective;
7440
7485
  }
7486
+ /**
7487
+ * Gets a record of VBindDirectives for options specific to certain directives.
7488
+ * The keys are directive names (e.g., 'options', 'options.intersection').
7489
+ */
7490
+ get optionsDirectives() {
7491
+ return this.#optionsDirectives;
7492
+ }
7493
+ /**
7494
+ * Gets the VBindDirective for options specific to the given directive name.
7495
+ * Searches in order: `:options.{directive}` -> `:options`
7496
+ *
7497
+ * @param directive The directive name (e.g., 'intersection', 'resize')
7498
+ * @returns The VBindDirective instance or undefined
7499
+ */
7500
+ optionsDirective(directive) {
7501
+ if (!this.#directives || this.#directives.length === 0) {
7502
+ return undefined;
7503
+ }
7504
+ // Search for `:options.{directive}` or `v-bind:options.{directive}` first
7505
+ const specificAttrName = `options.${directive}`;
7506
+ if (this.#optionsDirectives[specificAttrName]) {
7507
+ return this.#optionsDirectives[specificAttrName];
7508
+ }
7509
+ // Fallback: search for `:options` or `v-bind:options`
7510
+ if (this.#optionsDirectives['options']) {
7511
+ return this.#optionsDirectives['options'];
7512
+ }
7513
+ return undefined;
7514
+ }
7441
7515
  /**
7442
7516
  * Cleans up any resources used by the directive handler.
7443
7517
  */
@@ -7497,6 +7571,10 @@
7497
7571
  if (directive.name === StandardDirectiveName.V_BIND && directive.isKey) {
7498
7572
  this.#keyDirective = directive;
7499
7573
  }
7574
+ // If this is an options binding directive, cache it
7575
+ if (directive.name === StandardDirectiveName.V_BIND && directive.isOptions) {
7576
+ this.#optionsDirectives[directive.name] = directive;
7577
+ }
7500
7578
  }
7501
7579
  }
7502
7580
  // Sort directives by priority: v-for > v-if > v-else-if > v-else > v-show > others
@@ -8984,6 +9062,209 @@
8984
9062
  }
8985
9063
  }
8986
9064
 
9065
+ // Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
9066
+ /**
9067
+ * Directive for observing element intersection with viewport or ancestor elements using IntersectionObserver.
9068
+ * The `v-intersection` directive allows you to respond to changes in an element's visibility.
9069
+ *
9070
+ * Example usage:
9071
+ * <div v-intersection="handleIntersection">Observable content</div>
9072
+ * <div v-intersection="handleIntersection" :options.intersection="{threshold: 0.5}">Observable content</div>
9073
+ *
9074
+ * The handler receives IntersectionObserverEntry array as the first argument and $ctx as the second:
9075
+ * handleIntersection(entries, $ctx) {
9076
+ * const entry = entries[0];
9077
+ * if (entry.isIntersecting) {
9078
+ * console.log('Element is visible!');
9079
+ * }
9080
+ * }
9081
+ *
9082
+ * Options can be provided via :options or :options.intersection attribute:
9083
+ * :options="{root: null, threshold: 0.5, rootMargin: '0px'}"
9084
+ * :options.intersection="{root: null, threshold: 0.5, rootMargin: '0px'}"
9085
+ *
9086
+ * This directive is useful for lazy-loading, infinite scrolling, animation triggers,
9087
+ * and other features that depend on element visibility.
9088
+ */
9089
+ class VIntersectionDirective {
9090
+ /**
9091
+ * The virtual node to which this directive is applied.
9092
+ */
9093
+ #vNode;
9094
+ /**
9095
+ * A list of variable and function names used in the directive's expression.
9096
+ */
9097
+ #dependentIdentifiers;
9098
+ /**
9099
+ * The intersection handler wrapper function.
9100
+ */
9101
+ #handlerWrapper;
9102
+ /**
9103
+ * The IntersectionObserver instance.
9104
+ */
9105
+ #intersectionObserver;
9106
+ /**
9107
+ * @param context The context for parsing the directive.
9108
+ */
9109
+ constructor(context) {
9110
+ this.#vNode = context.vNode;
9111
+ // Parse the expression to extract identifiers and create the handler wrapper
9112
+ const expression = context.attribute.value;
9113
+ if (expression) {
9114
+ this.#dependentIdentifiers = ExpressionUtils.extractIdentifiers(expression, context.vNode.vApplication.functionDependencies);
9115
+ this.#handlerWrapper = this.#createIntersectionHandlerWrapper(expression);
9116
+ }
9117
+ // Remove the directive attribute from the element
9118
+ this.#vNode.node.removeAttribute(context.attribute.name);
9119
+ }
9120
+ /**
9121
+ * @inheritdoc
9122
+ */
9123
+ get name() {
9124
+ return StandardDirectiveName.V_INTERSECTION;
9125
+ }
9126
+ /**
9127
+ * @inheritdoc
9128
+ */
9129
+ get vNode() {
9130
+ return this.#vNode;
9131
+ }
9132
+ /**
9133
+ * @inheritdoc
9134
+ */
9135
+ get needsAnchor() {
9136
+ return false;
9137
+ }
9138
+ /**
9139
+ * @inheritdoc
9140
+ */
9141
+ get bindingsPreparer() {
9142
+ return undefined;
9143
+ }
9144
+ /**
9145
+ * @inheritdoc
9146
+ */
9147
+ get domUpdater() {
9148
+ return undefined;
9149
+ }
9150
+ /**
9151
+ * @inheritdoc
9152
+ */
9153
+ get templatize() {
9154
+ return false;
9155
+ }
9156
+ /**
9157
+ * @inheritdoc
9158
+ */
9159
+ get dependentIdentifiers() {
9160
+ return this.#dependentIdentifiers ?? [];
9161
+ }
9162
+ /**
9163
+ * @inheritdoc
9164
+ */
9165
+ get onMount() {
9166
+ return undefined;
9167
+ }
9168
+ /**
9169
+ * @inheritdoc
9170
+ */
9171
+ get onMounted() {
9172
+ if (!this.#handlerWrapper) {
9173
+ return undefined;
9174
+ }
9175
+ const element = this.#vNode.node;
9176
+ const handler = this.#handlerWrapper;
9177
+ return () => {
9178
+ // Get options from :options.intersection or :options directive
9179
+ let optionsDirective = this.#vNode.directiveManager?.optionsDirective('intersection');
9180
+ // Evaluate the options expression
9181
+ let options;
9182
+ if (optionsDirective && optionsDirective.expression) {
9183
+ // Evaluate the options expression
9184
+ const identifiers = optionsDirective.dependentIdentifiers;
9185
+ const values = identifiers.map(id => this.#vNode.bindings?.get(id));
9186
+ const args = identifiers.join(", ");
9187
+ const funcBody = `return (${optionsDirective.expression});`;
9188
+ const func = new Function(args, funcBody);
9189
+ options = func(...values);
9190
+ }
9191
+ // Create IntersectionObserver and start observing
9192
+ this.#intersectionObserver = new IntersectionObserver((entries) => {
9193
+ handler(entries);
9194
+ }, options);
9195
+ this.#intersectionObserver.observe(element);
9196
+ };
9197
+ }
9198
+ /**
9199
+ * @inheritdoc
9200
+ */
9201
+ get onUpdate() {
9202
+ return undefined;
9203
+ }
9204
+ /**
9205
+ * @inheritdoc
9206
+ */
9207
+ get onUpdated() {
9208
+ return undefined;
9209
+ }
9210
+ /**
9211
+ * @inheritdoc
9212
+ */
9213
+ get onUnmount() {
9214
+ return undefined;
9215
+ }
9216
+ /**
9217
+ * @inheritdoc
9218
+ */
9219
+ get onUnmounted() {
9220
+ return undefined;
9221
+ }
9222
+ /**
9223
+ * @inheritdoc
9224
+ */
9225
+ destroy() {
9226
+ // Disconnect the IntersectionObserver when the directive is destroyed
9227
+ if (this.#intersectionObserver) {
9228
+ this.#intersectionObserver.disconnect();
9229
+ this.#intersectionObserver = undefined;
9230
+ }
9231
+ }
9232
+ /**
9233
+ * Creates a wrapper function for intersection handlers.
9234
+ * @param expression The expression string to evaluate.
9235
+ * @returns A function that handles the intersection event.
9236
+ */
9237
+ #createIntersectionHandlerWrapper(expression) {
9238
+ const identifiers = this.#dependentIdentifiers ?? [];
9239
+ const vNode = this.#vNode;
9240
+ // Return a function that handles the intersection event with proper scope
9241
+ return (entries) => {
9242
+ const bindings = vNode.bindings;
9243
+ const $ctx = {
9244
+ element: vNode.node,
9245
+ vnode: vNode,
9246
+ userData: vNode.userData
9247
+ };
9248
+ // If the expression is just a method name, call it with bindings as 'this'
9249
+ const trimmedExpr = expression.trim();
9250
+ if (identifiers.includes(trimmedExpr) && typeof bindings?.get(trimmedExpr) === 'function') {
9251
+ const methodName = trimmedExpr;
9252
+ const originalMethod = bindings?.get(methodName);
9253
+ // Call the method with bindings as 'this' context
9254
+ // Pass entries as first argument and $ctx as second argument
9255
+ return originalMethod(entries, $ctx);
9256
+ }
9257
+ // For inline expressions, evaluate normally
9258
+ // Note: inline expressions receive entries and $ctx as parameters
9259
+ const values = identifiers.map(id => vNode.bindings?.get(id));
9260
+ const args = [...identifiers, 'entries', '$ctx'].join(", ");
9261
+ const funcBody = `return (${expression});`;
9262
+ const func = new Function(args, funcBody);
9263
+ return func.call(bindings?.raw, ...values, entries, $ctx);
9264
+ };
9265
+ }
9266
+ }
9267
+
8987
9268
  // Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
8988
9269
  /**
8989
9270
  * Directive for two-way data binding on form input elements.
@@ -9309,7 +9590,7 @@
9309
9590
  * @mounted="onMounted" - Called after the element is inserted into the DOM
9310
9591
  * @update="onUpdate" - Called before the element is updated
9311
9592
  * @updated="onUpdated" - Called after the element is updated
9312
- * @unmount="onUnmount" - Called before the element is removed from the DOM
9593
+ * @unmount="onUnmount" - Called before VNode cleanup begins
9313
9594
  * @unmounted="onUnmounted" - Called after VNode cleanup is complete (element reference still available)
9314
9595
  *
9315
9596
  * This directive is essential for handling user interactions and lifecycle events in your application.
@@ -10031,7 +10312,9 @@
10031
10312
  context.attribute.name === StandardDirectiveName.V_MODEL ||
10032
10313
  context.attribute.name.startsWith(StandardDirectiveName.V_MODEL + ".") ||
10033
10314
  // v-resize
10034
- context.attribute.name === StandardDirectiveName.V_RESIZE) {
10315
+ context.attribute.name === StandardDirectiveName.V_RESIZE ||
10316
+ // v-intersection
10317
+ context.attribute.name === StandardDirectiveName.V_INTERSECTION) {
10035
10318
  return true;
10036
10319
  }
10037
10320
  return false;
@@ -10074,6 +10357,10 @@
10074
10357
  if (context.attribute.name === StandardDirectiveName.V_RESIZE) {
10075
10358
  return new VResizeDirective(context);
10076
10359
  }
10360
+ // v-intersection
10361
+ if (context.attribute.name === StandardDirectiveName.V_INTERSECTION) {
10362
+ return new VIntersectionDirective(context);
10363
+ }
10077
10364
  throw new Error(`The attribute "${context.attribute.name}" cannot be parsed by ${this.name}.`);
10078
10365
  }
10079
10366
  }