@neeloong/form 0.4.0 → 0.4.2

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 CHANGED
@@ -197,6 +197,10 @@ render(store, layouts, app);
197
197
  - `$max` 只读属性
198
198
  - `$step` 只读属性
199
199
  - `$values` 只读属性
200
+ - `$type` 只读属性
201
+ - `$meta` 只读属性
202
+ - `$component` 只读属性
203
+ - `$kind` 只读属性
200
204
  1. 数组字段扩展隐式函数(只在事件中可用)
201
205
  - `$insert(index, value)`
202
206
  - `$add(value)`
package/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @neeloong/form v0.4.0
2
+ * @neeloong/form v0.4.2
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);
82
+ tag: string | ((ctx: any) => 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;
@@ -156,25 +156,22 @@ declare namespace Schema {
156
156
  type?: null | undefined;
157
157
  props?: Record<string, Schema.Field> | undefined;
158
158
  array?: boolean | undefined;
159
- meta?: any;
160
159
  };
161
160
  type Type = {
162
161
  type: string;
163
162
  props?: null | undefined;
164
163
  array?: boolean | undefined;
165
- meta?: any;
166
164
  };
167
165
  type Event = {
168
- input?: Function | null | undefined;
169
- change?: Function | null | undefined;
170
- click?: Function | null | undefined;
171
- focus?: Function | null | undefined;
172
- blur?: Function | null | undefined;
173
- add?: Function | null | undefined;
174
- remove?: Function | null | undefined;
175
- move?: Function | null | undefined;
166
+ input: InputEvent;
167
+ change: InputEvent;
168
+ click: Event;
169
+ focus: Event;
170
+ blur: Event;
176
171
  };
177
172
  type Attr = {
173
+ meta?: any;
174
+ component?: any;
178
175
  immutable?: boolean | undefined;
179
176
  creatable?: boolean | undefined;
180
177
  hidden?: boolean | ((store: Store, root: Store) => boolean) | null | undefined;
@@ -210,6 +207,13 @@ declare namespace Schema {
210
207
  * 可选值
211
208
  */
212
209
  values?: (Schema.Value.Define | Schema.Value.Group.Define)[] | undefined;
210
+ events?: {
211
+ input?: ((this: Store, value: InputEvent, store: Store) => void | boolean | null) | null | undefined;
212
+ change?: ((this: Store, value: InputEvent, store: Store) => void | boolean | null) | null | undefined;
213
+ click?: ((this: Store, value: Event, store: Store) => void | boolean | null) | null | undefined;
214
+ focus?: ((this: Store, value: Event, store: Store) => void | boolean | null) | null | undefined;
215
+ blur?: ((this: Store, value: Event, store: Store) => void | boolean | null) | null | undefined;
216
+ } | undefined;
213
217
  };
214
218
  }
215
219
 
@@ -280,10 +284,30 @@ declare class Store<T = any> {
280
284
  onUpdate?: ((value: T | null, index: any) => void) | null | undefined;
281
285
  onUpdateState?: ((value: T | null, index: any) => void) | null | undefined;
282
286
  });
287
+ /**
288
+ *
289
+ * @template {keyof Schema.Event} K
290
+ * @param {K} event
291
+ * @param {Schema.Event[K]} value
292
+ */
293
+ emit<K extends keyof Schema.Event>(event: K, value: Schema.Event[K]): boolean;
294
+ /**
295
+ *
296
+ * @template {keyof Schema.Event} K
297
+ * @param {K} event
298
+ * @param {(this: this, p: Schema.Event[K], store: this) => void | boolean | null} listener
299
+ * @returns {() => void}
300
+ */
301
+ listen<K extends keyof Schema.Event>(event: K, listener: (this: this, p: Schema.Event[K], store: this) => void | boolean | null): () => void;
283
302
  get null(): boolean;
303
+ get kind(): string;
284
304
  schema: Schema.Field;
305
+ get store(): this;
285
306
  get parent(): Store<any> | null;
286
307
  get root(): Store<any>;
308
+ get type(): any;
309
+ get meta(): any;
310
+ get component(): any;
287
311
  set length(v: number);
288
312
  get length(): number;
289
313
  set index(v: string | number);
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @neeloong/form v0.4.0
2
+ * @neeloong/form v0.4.2
3
3
  * (c) 2024-2025 Fierflame
4
4
  * @license Apache-2.0
5
5
  */
@@ -693,6 +693,43 @@
693
693
  * @template [T=any]
694
694
  */
695
695
  class Store {
696
+ /** @type {Map<string, Set<(value: any, store: any) => void | boolean | null>>} */
697
+ #events = new Map()
698
+ /**
699
+ *
700
+ * @template {keyof Schema.Event} K
701
+ * @param {K} event
702
+ * @param {Schema.Event[K]} value
703
+ */
704
+ emit(event, value) {
705
+ const key = typeof event === 'number' ? String(event) : event;
706
+ const events = this.#events;
707
+ let canceled = false;
708
+ for (const d of [...events.get(key) || []]) {
709
+ canceled = d(value, this) === false || canceled;
710
+ }
711
+ return !canceled;
712
+ }
713
+ /**
714
+ *
715
+ * @template {keyof Schema.Event} K
716
+ * @param {K} event
717
+ * @param {(this: this, p: Schema.Event[K], store: this) => void | boolean | null} listener
718
+ * @returns {() => void}
719
+ */
720
+ listen(event, listener) {
721
+ const fn = listener.bind(this);
722
+ const events = this.#events;
723
+ const key = typeof event === 'number' ? String(event) : event;
724
+ let set = events.get(key);
725
+ if (!set) {
726
+ set = new Set();
727
+ events.set(key, set);
728
+ }
729
+ set.add(fn);
730
+ return () => { set?.delete(fn); }
731
+
732
+ }
696
733
  /**
697
734
  * @param {Schema} schema
698
735
  * @param {object} [options]
@@ -703,6 +740,7 @@
703
740
  }
704
741
  #null = false;
705
742
  get null() { return this.#null; }
743
+ get kind() { return ''; }
706
744
  /**
707
745
  * @param {Schema.Field} schema
708
746
  * @param {object} options
@@ -747,6 +785,9 @@
747
785
  this.#root = parent.#root;
748
786
  // TODO: 事件向上冒泡
749
787
  }
788
+ this.#type = schema.type;
789
+ this.#meta = schema.meta;
790
+ this.#component = schema.component;
750
791
 
751
792
  const selfNewState = new exports.Signal.State(Boolean(isNew));
752
793
  this.#selfNew = selfNewState;
@@ -807,6 +848,13 @@
807
848
  this.#convert = typeof convert === 'function' ? convert : null;
808
849
  this.#length.set(length || 0);
809
850
  this.#index.set(index ?? '');
851
+
852
+ for (const [k, f] of Object.entries(schema.events || {})) {
853
+ if (typeof f !== 'function') { continue; }
854
+ // @ts-ignore
855
+ this.listen(k, f);
856
+ }
857
+
810
858
 
811
859
  }
812
860
  #destroyed = false;
@@ -824,8 +872,18 @@
824
872
  #parent = null;
825
873
  /** @readonly @type {Store} */
826
874
  #root = this;
875
+ /** @readonly @type {any} */
876
+ #type;
877
+ /** @readonly @type {any} */
878
+ #meta;
879
+ /** @readonly @type {any} */
880
+ #component;
881
+ get store() { return this; }
827
882
  get parent() { return this.#parent; }
828
883
  get root() { return this.#root; }
884
+ get type() { return this.#type; }
885
+ get meta() { return this.#meta; }
886
+ get component() { return this.#component; }
829
887
 
830
888
  #length = new exports.Signal.State(0);
831
889
  get length() { return this.#length.get(); }
@@ -1110,6 +1168,7 @@
1110
1168
 
1111
1169
 
1112
1170
  class ObjectStore extends Store {
1171
+ get kind() { return 'object'; }
1113
1172
  /** @type {Record<string, Store>} */
1114
1173
  #children
1115
1174
  *[Symbol.iterator]() {yield* Object.entries(this.#children);}
@@ -1200,6 +1259,7 @@
1200
1259
  }
1201
1260
  return children[Number(key)] || null;
1202
1261
  }
1262
+ get kind() { return 'array'; }
1203
1263
  /**
1204
1264
  * @param {Schema.Field} schema
1205
1265
  * @param {object} [options]
@@ -2324,8 +2384,24 @@
2324
2384
  }
2325
2385
 
2326
2386
  const bindable = {
2387
+ type: true,
2388
+ meta: true,
2389
+ component: true,
2390
+ kind: true,
2391
+
2392
+ value: true,
2393
+ state: true,
2394
+
2395
+ store: true,
2396
+ parent: true,
2397
+ root: true,
2398
+
2399
+ schema: true,
2400
+
2327
2401
  new: true,
2328
2402
  readonly: true,
2403
+ creatable: true,
2404
+ immutable: true,
2329
2405
 
2330
2406
  required: true,
2331
2407
  clearable: true,
@@ -2339,6 +2415,11 @@
2339
2415
  max: true,
2340
2416
  step: true,
2341
2417
  values: true,
2418
+
2419
+ null: true,
2420
+ index: true,
2421
+ no: true,
2422
+ length: true,
2342
2423
  };
2343
2424
  /** @type {Set<keyof typeof bindable>} */
2344
2425
  // @ts-ignore
@@ -2353,21 +2434,12 @@
2353
2434
  */
2354
2435
  function *toItem(val, key = '', sign = '$') {
2355
2436
  yield [`${key}`, {get: () => val.value, set: v => val.value = v, store: val}];
2356
- yield [`${key}${sign}value`, {get: () => val.value, set: v => val.value = v}];
2357
- yield [`${key}${sign}state`, {get: () => val.state, set: v => val.state = v}];
2358
- yield [`${key}${sign}store`, {get: () => val}];
2359
- yield [`${key}${sign}schema`, {get: () => val.schema}];
2360
- yield [`${key}${sign}null`, {get: () => val.null}];
2361
- yield [`${key}${sign}index`, {get: () => val.index}];
2362
- yield [`${key}${sign}no`, {get: () => val.no}];
2363
- yield [`${key}${sign}length`, {get: () => val.length}];
2364
- yield [`${key}${sign}creatable`, {get: () => val.creatable}];
2365
- yield [`${key}${sign}immutable`, {get: () => val.immutable}];
2366
-
2367
2437
 
2368
2438
  for (const k of bindableSet) {
2369
2439
  yield [`${key}${sign}${k}`, {get: () => val[k]}];
2370
2440
  }
2441
+ yield [`${key}${sign}value`, {get: () => val.value, set: v => val.value = v}];
2442
+ yield [`${key}${sign}state`, {get: () => val.state, set: v => val.state = v}];
2371
2443
  if (!(val instanceof ArrayStore)) { return; }
2372
2444
  yield [`${key}${sign}insert`, {exec: (index, value) => val.insert(index, value)}];
2373
2445
  yield [`${key}${sign}add`, {exec: (v) => val.add(v)}];
@@ -2547,10 +2619,11 @@
2547
2619
  const item = this.#items[name];
2548
2620
  if (!item?.get) { return; }
2549
2621
  const { store } = item;
2550
- if (!store) { return; }
2551
- switch(type) {
2552
- case 'value': return watch(() => store.value, cb);
2553
- case 'state': return watch(() => store.state, cb);
2622
+ if (!store) {
2623
+ switch(type) {
2624
+ case 'value': return watch(() => item.get(), cb);
2625
+ }
2626
+ return;
2554
2627
  }
2555
2628
  // @ts-ignore
2556
2629
  if (bindableSet.has(type)) {
@@ -2575,8 +2648,6 @@
2575
2648
  const res = Object.fromEntries([...bindableSet].map(v => [
2576
2649
  `$${v}`, cb => watch(() => store[v], cb)
2577
2650
  ]));
2578
- res.$value = cb => watch(() => store.value, cb);
2579
- res.$state = cb => watch(() => store.state, cb);
2580
2651
  return res;
2581
2652
  }
2582
2653
  /**
@@ -2596,9 +2667,9 @@
2596
2667
  }
2597
2668
  /**
2598
2669
  * @param {string} name
2599
- * @returns {Record<string, ((value: any) => void) | void> | void}
2670
+ * @returns {Record<string, (value: any) => void> | void}
2600
2671
  */
2601
- bindStateAllSet(name) {
2672
+ bindEvents(name) {
2602
2673
  const item = this.#items[name];
2603
2674
  if (!item?.get) { return; }
2604
2675
  const { store } = item;
@@ -2606,10 +2677,15 @@
2606
2677
  const set = item.set;
2607
2678
  if (typeof set !== 'function') { return; }
2608
2679
  return { '$value': set }
2609
- }
2680
+ }
2610
2681
  return {
2611
2682
  '$value': v => {store.value = v; },
2612
2683
  '$state': v => {store.state = v; },
2684
+ '$input': v => {store.emit('input', v); },
2685
+ '$change': v => {store.emit('change', v); },
2686
+ '$click': v => {store.emit('click', v); },
2687
+ '$focus': v => {store.emit('focus', v); },
2688
+ '$blur': v => {store.emit('blur', v); },
2613
2689
  }
2614
2690
  }
2615
2691
 
@@ -3011,9 +3087,8 @@
3011
3087
  * @param {Component.Handler} handler
3012
3088
  * @param {Environment} envs
3013
3089
  * @param {Record<string, string | {name: string} | Layout.Calc>} attrs
3014
- * @param {string | boolean | null} [bindValue]
3015
3090
  */
3016
- function bindBaseAttrs(handler, envs, attrs, bindValue) {
3091
+ function bindBaseAttrs(handler, envs, attrs) {
3017
3092
  const tag = handler.tag;
3018
3093
  let bk = new Set();
3019
3094
  for (const [name, attr] of Object.entries(attrs)) {
@@ -3029,16 +3104,6 @@
3029
3104
  }
3030
3105
  bk.add(envs.watch(attrSchema, val => handler.set(name, val)));
3031
3106
  }
3032
- if (bindValue && typeof bindValue !== 'boolean') {
3033
- for (const [key, effect] of Object.entries(envs.bindAll(bindValue) || {})) {
3034
- if (typeof effect !== 'function') { continue; }
3035
- bk.add(effect(val => handler.set(key, val)));
3036
- }
3037
- for (const [key, setter] of Object.entries(envs.bindStateAllSet(bindValue) || {})) {
3038
- if (typeof setter !== 'function') { continue; }
3039
- handler.addEvent(key, $event => setter($event));
3040
- }
3041
- }
3042
3107
 
3043
3108
  return ()=> {
3044
3109
  const list = bk;
@@ -4045,6 +4110,34 @@
4045
4110
  };
4046
4111
  }
4047
4112
 
4113
+ /** @import { Component } from '../types.mjs' */
4114
+
4115
+
4116
+ /**
4117
+ * @param {Component.Handler} handler
4118
+ * @param {Environment} env
4119
+ * @param {string | boolean | null} [bind]
4120
+ */
4121
+ function bindBase(handler, env, bind) {
4122
+ if (!bind || typeof bind === 'boolean') { return () => {}; }
4123
+ let bk = new Set();
4124
+ for (const [key, effect] of Object.entries(env.bindAll(bind) || {})) {
4125
+ if (typeof effect !== 'function') { continue; }
4126
+ bk.add(effect(val => handler.set(key, val)));
4127
+ }
4128
+ for (const [key, setter] of Object.entries(env.bindEvents(bind) || {})) {
4129
+ handler.addEvent(key, $event => setter($event));
4130
+ }
4131
+
4132
+ return ()=> {
4133
+ const list = bk;
4134
+ bk = new Set();
4135
+ for (const s of list) {
4136
+ s();
4137
+ }
4138
+ }
4139
+ }
4140
+
4048
4141
  /** @import Store from '../Store/index.mjs' */
4049
4142
 
4050
4143
  /**
@@ -4058,12 +4151,13 @@
4058
4151
  */
4059
4152
  function renderItem(layout, parent, next, env, templates, componentPath, getComponent) {
4060
4153
  env = env.set(layout.aliases, layout.vars);
4154
+ const bind = layout.directives.bind;
4061
4155
  const fragment = layout.directives.fragment;
4062
4156
  if (fragment && typeof fragment === 'string') {
4063
4157
  const template = templates[fragment];
4064
4158
  if (!template) { return () => {}; }
4065
4159
  const [templateLayout, templateEnv] = template;
4066
- const newEnv = templateEnv.params(templateLayout, layout, env, layout.directives.bind);
4160
+ const newEnv = templateEnv.params(templateLayout, layout, env, bind);
4067
4161
  return render(templateLayout, parent, next, newEnv, templates, componentPath, getComponent);
4068
4162
  }
4069
4163
  if (!layout.name || layout.directives.fragment) {
@@ -4080,14 +4174,17 @@
4080
4174
 
4081
4175
  const componentAttrs = component?.attrs;
4082
4176
  const attrs = componentAttrs
4083
- ? bindAttrs(handler, env, layout.attrs, componentAttrs, layout.directives.bind)
4084
- : bindBaseAttrs(handler, env, layout.attrs, layout.directives.bind);
4177
+ ? bindAttrs(handler, env, layout.attrs, componentAttrs, bind)
4178
+ : bindBaseAttrs(handler, env, layout.attrs);
4085
4179
 
4086
4180
  for (const [name, event] of Object.entries(layout.events)) {
4087
4181
  const fn = env.getEvent(event);
4088
4182
  if (fn) { handler.addEvent(name, fn); }
4089
4183
  }
4090
4184
 
4185
+ const base = bindBase(handler, env, bind);
4186
+
4187
+
4091
4188
  const r = component ?
4092
4189
  typeof component.tag === 'function'
4093
4190
  ? component.tag(context)
@@ -4113,6 +4210,7 @@
4113
4210
  handler.destroy();
4114
4211
  attrs();
4115
4212
  children();
4213
+ base();
4116
4214
  };
4117
4215
  }
4118
4216
  /**