@neeloong/form 0.4.2 → 0.4.3

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/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @neeloong/form v0.4.2
2
+ * @neeloong/form v0.4.3
3
3
  * (c) 2024-2025 Fierflame
4
4
  * @license Apache-2.0
5
5
  */
@@ -79,7 +79,7 @@ declare namespace index_d {
79
79
 
80
80
  type VerifyError = any;
81
81
  type Component = {
82
- tag: string | ((ctx: any) => Element | [Element, (Element | null)?]);
82
+ tag: string | ((ctx: Component.Context) => Element | [Element, (Element | null)?]);
83
83
  is?: string | undefined;
84
84
  attrs?: Record<string, Component.Attr> | undefined;
85
85
  events?: Record<string, Component.Event> | undefined;
@@ -100,7 +100,7 @@ declare namespace Component {
100
100
  addEvent: (event: string, listener: EventListener) => void;
101
101
  destroy: () => void;
102
102
  tag: any;
103
- init: () => void;
103
+ mount: () => void;
104
104
  };
105
105
  type Context = {
106
106
  props?: Set<string> | null | undefined;
@@ -128,8 +128,8 @@ declare namespace Component {
128
128
  namespace Event {
129
129
  type Filter = ($event: any, param: string[], env: any) => boolean | null | void;
130
130
  }
131
+ type Getter = (path: string[], next?: ((path: string[]) => Component | null) | undefined) => Component | null;
131
132
  }
132
- type ComponentGetter = (path: string[], next?: ((path: string[]) => Component | null) | undefined) => Component | null;
133
133
  type Schema = Record<string, Schema.Field>;
134
134
  declare namespace Schema {
135
135
  type Field = (Schema.Object | Schema.Type) & Schema.Attr;
@@ -162,7 +162,7 @@ declare namespace Schema {
162
162
  props?: null | undefined;
163
163
  array?: boolean | undefined;
164
164
  };
165
- type Event = {
165
+ type Events = {
166
166
  input: InputEvent;
167
167
  change: InputEvent;
168
168
  click: Event;
@@ -286,19 +286,19 @@ declare class Store<T = any> {
286
286
  });
287
287
  /**
288
288
  *
289
- * @template {keyof Schema.Event} K
289
+ * @template {keyof Schema.Events} K
290
290
  * @param {K} event
291
- * @param {Schema.Event[K]} value
291
+ * @param {Schema.Events[K]} value
292
292
  */
293
- emit<K extends keyof Schema.Event>(event: K, value: Schema.Event[K]): boolean;
293
+ emit<K extends keyof Schema.Events>(event: K, value: Schema.Events[K]): boolean;
294
294
  /**
295
295
  *
296
- * @template {keyof Schema.Event} K
296
+ * @template {keyof Schema.Events} K
297
297
  * @param {K} event
298
- * @param {(this: this, p: Schema.Event[K], store: this) => void | boolean | null} listener
298
+ * @param {(this: this, p: Schema.Events[K], store: this) => void | boolean | null} listener
299
299
  * @returns {() => void}
300
300
  */
301
- listen<K extends keyof Schema.Event>(event: K, listener: (this: this, p: Schema.Event[K], store: this) => void | boolean | null): () => void;
301
+ listen<K extends keyof Schema.Events>(event: K, listener: (this: this, p: Schema.Events[K], store: this) => void | boolean | null): () => void;
302
302
  get null(): boolean;
303
303
  get kind(): string;
304
304
  schema: Schema.Field;
@@ -426,9 +426,9 @@ declare function _default(store: Store, layouts: (Node | string)[], parent: Elem
426
426
  * @param {Store} store
427
427
  * @param {(Layout.Node | string)[]} layouts
428
428
  * @param {Element} parent
429
- * @param {((path: string[]) => Component?)?} [components]
429
+ * @param {Component.Getter?} [components]
430
430
  * @returns {() => void}
431
431
  */
432
- declare function _default(store: Store, layouts: (Node | string)[], parent: Element, components?: ((path: string[]) => Component | null) | null | undefined): () => void;
432
+ declare function _default(store: Store, layouts: (Node | string)[], parent: Element, components?: Component.Getter | null | undefined): () => void;
433
433
 
434
- export { Component, type ComponentGetter, index_d as Layout, Schema, Store, type VerifyError, _default as render };
434
+ export { Component, index_d as Layout, Schema, Store, type VerifyError, _default as render };
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @neeloong/form v0.4.2
2
+ * @neeloong/form v0.4.3
3
3
  * (c) 2024-2025 Fierflame
4
4
  * @license Apache-2.0
5
5
  */
@@ -697,9 +697,9 @@
697
697
  #events = new Map()
698
698
  /**
699
699
  *
700
- * @template {keyof Schema.Event} K
700
+ * @template {keyof Schema.Events} K
701
701
  * @param {K} event
702
- * @param {Schema.Event[K]} value
702
+ * @param {Schema.Events[K]} value
703
703
  */
704
704
  emit(event, value) {
705
705
  const key = typeof event === 'number' ? String(event) : event;
@@ -712,9 +712,9 @@
712
712
  }
713
713
  /**
714
714
  *
715
- * @template {keyof Schema.Event} K
715
+ * @template {keyof Schema.Events} K
716
716
  * @param {K} event
717
- * @param {(this: this, p: Schema.Event[K], store: this) => void | boolean | null} listener
717
+ * @param {(this: this, p: Schema.Events[K], store: this) => void | boolean | null} listener
718
718
  * @returns {() => void}
719
719
  */
720
720
  listen(event, listener) {
@@ -2611,12 +2611,12 @@
2611
2611
  }
2612
2612
 
2613
2613
  /**
2614
- * @param {string} name
2614
+ * @param {string | true} name
2615
2615
  * @param {string} type
2616
2616
  * @param {(value: any) => void} cb
2617
2617
  */
2618
2618
  bind(name, type, cb) {
2619
- const item = this.#items[name];
2619
+ const item = this.#items[name === true ? '' : name];
2620
2620
  if (!item?.get) { return; }
2621
2621
  const { store } = item;
2622
2622
  if (!store) {
@@ -2632,11 +2632,11 @@
2632
2632
  }
2633
2633
  }
2634
2634
  /**
2635
- * @param {string} name
2635
+ * @param {string | true} name
2636
2636
  * @returns {Record<string, ((cb: (value: any) => void) => () => void) | void> | void}
2637
2637
  */
2638
2638
  bindAll(name) {
2639
- const item = this.#items[name];
2639
+ const item = this.#items[name === true ? '' : name];
2640
2640
  if (!item?.get) { return; }
2641
2641
  const { store } = item;
2642
2642
  if (!store) {
@@ -2651,12 +2651,12 @@
2651
2651
  return res;
2652
2652
  }
2653
2653
  /**
2654
- * @param {string} name
2654
+ * @param {string | true} name
2655
2655
  * @param {string} type
2656
2656
  * @returns {((value: any) => void) | void}
2657
2657
  */
2658
2658
  bindSet(name, type) {
2659
- const item = this.#items[name];
2659
+ const item = this.#items[name === true ? '' : name];
2660
2660
  if (!item?.get) { return; }
2661
2661
  const { store } = item;
2662
2662
  if (!store) { return; }
@@ -2666,11 +2666,11 @@
2666
2666
  }
2667
2667
  }
2668
2668
  /**
2669
- * @param {string} name
2669
+ * @param {string | true} name
2670
2670
  * @returns {Record<string, (value: any) => void> | void}
2671
2671
  */
2672
2672
  bindEvents(name) {
2673
- const item = this.#items[name];
2673
+ const item = this.#items[name === true ? '' : name];
2674
2674
  if (!item?.get) { return; }
2675
2675
  const { store } = item;
2676
2676
  if (!store) {
@@ -3036,7 +3036,7 @@
3036
3036
  continue;
3037
3037
  }
3038
3038
  const bind = attr.bind;
3039
- if (!bindValue || !bind || typeof bindValue === 'boolean') {
3039
+ if (!bindValue || !bind) {
3040
3040
  handler.set(name, attr.default);
3041
3041
  continue;
3042
3042
  }
@@ -3058,8 +3058,9 @@
3058
3058
  continue;
3059
3059
  }
3060
3060
  if (!isState) {
3061
- bk.add(envs.watch(bindValue, v => handler.set(name, v)));
3062
- handler.addEvent(event, (...args) => { envs.all[bindValue] = set(...args);});
3061
+ const bindKey = bindValue === true ? '' : bindValue;
3062
+ bk.add(envs.watch(bindKey, v => handler.set(name, v)));
3063
+ handler.addEvent(event, (...args) => { envs.all[bindKey] = set(...args);});
3063
3064
  continue;
3064
3065
  }
3065
3066
  const r = envs.bind(bindValue, 'state', v => handler.set(name, v));
@@ -3296,7 +3297,6 @@
3296
3297
 
3297
3298
  /** @import { Component } from '../types.mjs' */
3298
3299
  /** @import Environment from './Environment/index.mjs' */
3299
- /** @import Store from '../Store/index.mjs' */
3300
3300
 
3301
3301
 
3302
3302
  /** @type {Record<string, Component.Event.Filter>} */
@@ -3345,7 +3345,7 @@
3345
3345
  if (evt instanceof KeyboardEvent) {
3346
3346
  const key = evt.code.toLowerCase().replace(/-/g, '');
3347
3347
  for (const k of param) {
3348
- if (key === k.toLowerCase().replace(/-/g, '')) { return true }
3348
+ if (key === k.toLowerCase().replace(/-/g, '')) { return true; }
3349
3349
  }
3350
3350
  return false;
3351
3351
  }
@@ -3384,34 +3384,34 @@
3384
3384
  if (evt instanceof PointerEvent) {
3385
3385
  const pointerType = evt.pointerType.toLowerCase().replace(/-/g, '');
3386
3386
  for (const k of param) {
3387
- if (pointerType === k.toLowerCase().replace(/-/g, '')) { return true }
3387
+ if (pointerType === k.toLowerCase().replace(/-/g, '')) { return true; }
3388
3388
  }
3389
3389
  return false;
3390
3390
  }
3391
3391
  },
3392
3392
 
3393
3393
  ctrl(evt) {
3394
- if (evt instanceof MouseEvent|| evt instanceof KeyboardEvent || evt instanceof TouchEvent) {
3394
+ if (evt instanceof MouseEvent || evt instanceof KeyboardEvent || evt instanceof TouchEvent) {
3395
3395
  return evt.ctrlKey;
3396
3396
  }
3397
3397
  },
3398
3398
  alt(evt) {
3399
- if (evt instanceof MouseEvent|| evt instanceof KeyboardEvent || evt instanceof TouchEvent) {
3399
+ if (evt instanceof MouseEvent || evt instanceof KeyboardEvent || evt instanceof TouchEvent) {
3400
3400
  return evt.altKey;
3401
3401
  }
3402
3402
  },
3403
3403
  shift(evt) {
3404
- if (evt instanceof MouseEvent|| evt instanceof KeyboardEvent || evt instanceof TouchEvent) {
3404
+ if (evt instanceof MouseEvent || evt instanceof KeyboardEvent || evt instanceof TouchEvent) {
3405
3405
  return evt.shiftKey;
3406
3406
  }
3407
3407
  },
3408
3408
  meta(evt) {
3409
- if (evt instanceof MouseEvent|| evt instanceof KeyboardEvent || evt instanceof TouchEvent) {
3409
+ if (evt instanceof MouseEvent || evt instanceof KeyboardEvent || evt instanceof TouchEvent) {
3410
3410
  return evt.metaKey;
3411
3411
  }
3412
3412
  },
3413
3413
  cmd(evt) {
3414
- if (evt instanceof MouseEvent|| evt instanceof KeyboardEvent || evt instanceof TouchEvent) {
3414
+ if (evt instanceof MouseEvent || evt instanceof KeyboardEvent || evt instanceof TouchEvent) {
3415
3415
  return evt.ctrlKey || evt.metaKey;
3416
3416
  }
3417
3417
  },
@@ -3424,25 +3424,45 @@
3424
3424
  */
3425
3425
  function createContext(component, env) {
3426
3426
  const tag = typeof component === 'string' ? component : component.tag;
3427
- const { attrs, events } = typeof component !== 'string' && component || {attrs: null, events: null };
3427
+ const { attrs, events } = typeof component !== 'string' && component || { attrs: null, events: null };
3428
3428
 
3429
3429
  let destroyed = false;
3430
- let init = false;
3430
+ const destroyedState = new exports.Signal.State(false);
3431
+ let mounted = false;
3432
+ const mountedState = new exports.Signal.State(false);
3433
+ /** @type {Record<string, Signal.State<any> | void>} */
3434
+ const attrStates = Object.create(null);
3431
3435
  const tagAttrs = Object.create(null);
3432
3436
 
3437
+ const attrWatchers = new Set();
3438
+
3433
3439
  /** @type {[string, ($event: any) => void, AddEventListenerOptions][]} */
3434
3440
  const allEvents = [];
3435
3441
  const stateEmitter = new EventEmitter();
3436
- /** @type {EventEmitter<Record<string, [any, any, string]>>} */
3437
- const attrEmitter = new EventEmitter();
3438
3442
  /** @type {Component.Context} */
3439
3443
  const context = {
3440
3444
  events: allEvents,
3441
- props: attrs ? new Set(Object.entries(attrs).filter(([,a]) => a.isProp).map(([e]) => e)) : null,
3445
+ props: attrs ? new Set(Object.entries(attrs).filter(([, a]) => a.isProp).map(([e]) => e)) : null,
3442
3446
  tagAttrs,
3443
- watchAttr(name, fn) { return attrEmitter.listen(name, fn); },
3444
- get destroyed() { return destroyed},
3445
- get init() { return init},
3447
+ watchAttr(name, fn) {
3448
+ if (destroyed) { return () => { }; }
3449
+ const state = attrStates[name];
3450
+ if (!state) { return () => { }; }
3451
+ let old = state.get();
3452
+ const w = watch(() => state.get(), v => {
3453
+ const o = old;
3454
+ old = v;
3455
+ fn(v, o, name);
3456
+ });
3457
+ attrWatchers.add(w);
3458
+
3459
+ return () => {
3460
+ attrWatchers.delete(w);
3461
+ w();
3462
+ };
3463
+ },
3464
+ get destroyed() { return destroyedState.get(); },
3465
+ get init() { return mountedState.get(); },
3446
3466
  listen(name, listener) { return stateEmitter.listen(name, listener); },
3447
3467
  };
3448
3468
  /** @type {Component.Handler} */
@@ -3450,11 +3470,18 @@
3450
3470
  tag,
3451
3471
  set(name, value) {
3452
3472
  if (attrs && !(name in attrs)) { return; }
3453
- if (!(name in tagAttrs)) { tagAttrs[name] = void 0; }
3454
- const old = tagAttrs[name];
3455
- if (old === value) { return; }
3456
- tagAttrs[name] = value;
3457
- attrEmitter.emit(name, value, old, name);
3473
+ let state = attrStates[name];
3474
+ if (state) {
3475
+ state.set(value);
3476
+ return;
3477
+ }
3478
+ const s = new exports.Signal.State(value);
3479
+ attrStates[name] = s;
3480
+ Object.defineProperty(tagAttrs, name, {
3481
+ configurable: true,
3482
+ enumerable: true,
3483
+ get: s.get.bind(s),
3484
+ });
3458
3485
  },
3459
3486
  addEvent(name, fn) {
3460
3487
  if (typeof fn !== 'function') { return; }
@@ -3465,14 +3492,14 @@
3465
3492
  const options = {};
3466
3493
  /** @type {[Component.Event.Filter, string[], boolean][]} */
3467
3494
  const filterFns = [];
3468
- if (filters) for (let f = fs.shift();f;f = fs.shift()) {
3495
+ if (filters) for (let f = fs.shift(); f; f = fs.shift()) {
3469
3496
  const paramIndex = f.indexOf(':');
3470
3497
  const noParamName = paramIndex >= 0 ? f.slice(0, paramIndex) : f;
3471
3498
  const param = paramIndex >= 0 ? f.slice(paramIndex + 1).split(':') : [];
3472
3499
  const filterName = noParamName.replace(/^-+/, '');
3473
3500
  const sub = (noParamName.length - filterName.length) % 2 === 1;
3474
3501
  let filter = filters[filterName] || filterName;
3475
- switch(filter) {
3502
+ switch (filter) {
3476
3503
  case 'once':
3477
3504
  options.once = !sub;
3478
3505
  break;
@@ -3493,20 +3520,25 @@
3493
3520
  allEvents.push([e, $event => {
3494
3521
  const global = env.all;
3495
3522
  for (const [filter, param, sub] of filterFns) {
3496
- if (filter($event, param, global) === sub) { return}
3523
+ if (filter($event, param, global) === sub) { return; }
3497
3524
  }
3498
3525
  fn($event, global);
3499
3526
  }, options]);
3500
3527
  },
3501
3528
  destroy() {
3502
- if (destroyed) { return }
3529
+ if (destroyed) { return; }
3503
3530
  destroyed = true;
3531
+ destroyedState.set(true);
3532
+ for (const w of attrWatchers) {
3533
+ w();
3534
+ }
3504
3535
  stateEmitter.emit('destroy');
3505
3536
  },
3506
- init() {
3507
- if (init) { return }
3508
- init = true;
3509
- stateEmitter.emit('init', {events: allEvents});
3537
+ mount() {
3538
+ if (mounted) { return; }
3539
+ mounted = true;
3540
+ mountedState.set(true);
3541
+ stateEmitter.emit('init', { events: allEvents });
3510
3542
  },
3511
3543
 
3512
3544
  };
@@ -4119,7 +4151,7 @@
4119
4151
  * @param {string | boolean | null} [bind]
4120
4152
  */
4121
4153
  function bindBase(handler, env, bind) {
4122
- if (!bind || typeof bind === 'boolean') { return () => {}; }
4154
+ if (!bind) { return () => {}; }
4123
4155
  let bk = new Set();
4124
4156
  for (const [key, effect] of Object.entries(env.bindAll(bind) || {})) {
4125
4157
  if (typeof effect !== 'function') { continue; }
@@ -4147,7 +4179,7 @@
4147
4179
  * @param {Environment} env
4148
4180
  * @param {Record<string, [Layout.Node, Environment]>} templates
4149
4181
  * @param {string[]} componentPath
4150
- * @param {((path: string[]) => Component?)?} [getComponent]
4182
+ * @param {Component.Getter?} [getComponent]
4151
4183
  */
4152
4184
  function renderItem(layout, parent, next, env, templates, componentPath, getComponent) {
4153
4185
  env = env.set(layout.aliases, layout.vars);
@@ -4203,7 +4235,7 @@
4203
4235
  bindClasses(root, layout.classes, env);
4204
4236
  bindStyles(root, layout.styles, env);
4205
4237
 
4206
- handler.init();
4238
+ handler.mount();
4207
4239
 
4208
4240
  return () => {
4209
4241
  root.remove();
@@ -4221,7 +4253,7 @@
4221
4253
  * @param {Environment} env
4222
4254
  * @param {Record<string, [Layout.Node, Environment]>} templates
4223
4255
  * @param {string[]} componentPath
4224
- * @param {((path: string[]) => Component?)?} [getComponent]
4256
+ * @param {Component.Getter?} [getComponent]
4225
4257
  * @returns {() => void}
4226
4258
  */
4227
4259
  function render(layout, parent, next, env, templates, componentPath, getComponent) {
@@ -4260,15 +4292,15 @@
4260
4292
  * @param {Store} store
4261
4293
  * @param {(Layout.Node | string)[]} layouts
4262
4294
  * @param {Element} parent
4263
- * @param {((path: string[]) => Component?)?} [components]
4295
+ * @param {Component.Getter?} [components]
4264
4296
  * @returns {() => void}
4265
4297
  */
4266
4298
  /**
4267
4299
  * @param {Store} store
4268
4300
  * @param {(Layout.Node | string)[]} layouts
4269
4301
  * @param {Element} parent
4270
- * @param {((path: string[]) => Component?) | Record<string, Store | {get?(): any; set?(v: any): void; exec?(...p: any[]): any; calc?(...p: any[]): any }> | null} [opt1]
4271
- * @param {((path: string[]) => Component?) | Record<string, Store | {get?(): any; set?(v: any): void; exec?(...p: any[]): any; calc?(...p: any[]): any }> | null} [opt2]
4302
+ * @param {Component.Getter | Record<string, Store | {get?(): any; set?(v: any): void; exec?(...p: any[]): any; calc?(...p: any[]): any }> | null} [opt1]
4303
+ * @param {Component.Getter | Record<string, Store | {get?(): any; set?(v: any): void; exec?(...p: any[]): any; calc?(...p: any[]): any }> | null} [opt2]
4272
4304
  */
4273
4305
  function index (store, layouts, parent, opt1, opt2) {
4274
4306
  const options = [opt1, opt2];