@neeloong/form 0.2.0 → 0.3.0
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 +2 -1
- package/index.d.mts +4 -2
- package/index.js +122 -41
- package/index.min.js +5 -5
- package/index.min.mjs +6 -6
- package/index.mjs +122 -41
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -160,12 +160,13 @@ render(store, layouts, app);
|
|
|
160
160
|
|
|
161
161
|
优先级从高到低:
|
|
162
162
|
|
|
163
|
-
1.
|
|
163
|
+
1. 模板定义: `!template`
|
|
164
164
|
1. 条件: `!if` `!else`
|
|
165
165
|
1. 子属性: `!value`
|
|
166
166
|
1. 枚举: `!enum`
|
|
167
167
|
1. 别名与计算名: `*别名` `*计算名`
|
|
168
168
|
1. 显式变量: `+变量`
|
|
169
|
+
1. 片段与模板调用: `!fragment`
|
|
169
170
|
1. 属性与事件: `:绑定属性` `@事件` `普通属性` `!bind`
|
|
170
171
|
1. 子内容: `!text` `!html`
|
|
171
172
|
1. 注释: `!comment`
|
package/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @neeloong/form v0.
|
|
2
|
+
* @neeloong/form v0.3.0
|
|
3
3
|
* (c) 2024-2025 Fierflame
|
|
4
4
|
* @license Apache-2.0
|
|
5
5
|
*/
|
|
@@ -325,7 +325,8 @@ declare function parse(source: string, { creteCalc, creteEvent, simpleTag, }?: O
|
|
|
325
325
|
declare function toString(value: Node | (Node | string)[], formable?: boolean): string;
|
|
326
326
|
|
|
327
327
|
type Directives = {
|
|
328
|
-
|
|
328
|
+
template?: string | undefined;
|
|
329
|
+
fragment?: string | boolean | undefined;
|
|
329
330
|
if?: string | Function | undefined;
|
|
330
331
|
else?: boolean | undefined;
|
|
331
332
|
/**
|
|
@@ -356,6 +357,7 @@ type Node = {
|
|
|
356
357
|
attrs: Record<string, string | {
|
|
357
358
|
name: string;
|
|
358
359
|
} | ((global: any) => void)>;
|
|
360
|
+
params: Record<string, string | ((global: any) => void)>;
|
|
359
361
|
classes: Record<string, string | boolean | ((global: any) => void)>;
|
|
360
362
|
styles: Record<string, string | ((global: any) => void)>;
|
|
361
363
|
events: Record<string, string | (($event: any, global: any) => void)>;
|
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @neeloong/form v0.
|
|
2
|
+
* @neeloong/form v0.3.0
|
|
3
3
|
* (c) 2024-2025 Fierflame
|
|
4
4
|
* @license Apache-2.0
|
|
5
5
|
*/
|
|
@@ -1478,7 +1478,7 @@
|
|
|
1478
1478
|
|
|
1479
1479
|
/** @import * as Layout from './index.mjs' */
|
|
1480
1480
|
|
|
1481
|
-
const attrPattern = /^(?<decorator>[
|
|
1481
|
+
const attrPattern = /^(?<decorator>[:@!+*\.?]|style:|样式:)?(?<name>-?[\w\p{Unified_Ideograph}_][-\w\p{Unified_Ideograph}_:\d\.]*)$/u;
|
|
1482
1482
|
const nameRegex = /^(?<name>[a-zA-Z$\p{Unified_Ideograph}_][\da-zA-Z$\p{Unified_Ideograph}_]*)?$/u;
|
|
1483
1483
|
/**
|
|
1484
1484
|
* @param {Layout.Node} node
|
|
@@ -1486,7 +1486,7 @@
|
|
|
1486
1486
|
* @param {Exclude<Layout.Options['creteEvent'], undefined>} creteEvent
|
|
1487
1487
|
*/
|
|
1488
1488
|
function createAttributeAdder(node, creteCalc, creteEvent) {
|
|
1489
|
-
const { attrs, directives, events, classes, styles, vars, aliases } = node;
|
|
1489
|
+
const { attrs, directives, events, classes, styles, vars, aliases, params } = node;
|
|
1490
1490
|
/**
|
|
1491
1491
|
* @param {string} qName
|
|
1492
1492
|
* @param {string} value
|
|
@@ -1517,13 +1517,13 @@
|
|
|
1517
1517
|
vars[name] = !value ? '' : nameRegex.test(value) ? value : creteCalc(value);
|
|
1518
1518
|
} else if (decorator === '*') {
|
|
1519
1519
|
aliases[name] = nameRegex.test(value) ? value : creteCalc(value);
|
|
1520
|
+
} else if (decorator === '?') {
|
|
1521
|
+
params[name] = nameRegex.test(value) ? value : creteCalc(value);
|
|
1520
1522
|
} else if (decorator === '!') {
|
|
1521
1523
|
const key = name.toString();
|
|
1522
1524
|
switch (key) {
|
|
1523
|
-
case 'fragment':
|
|
1524
|
-
case 'else':
|
|
1525
|
-
directives[key] = true;
|
|
1526
|
-
break;
|
|
1525
|
+
case 'fragment': directives.fragment = value || true; break;
|
|
1526
|
+
case 'else': directives.else = true; break;
|
|
1527
1527
|
case 'enum':
|
|
1528
1528
|
directives.enum = value ? nameRegex.test(value) ? value : creteCalc(value) : true;
|
|
1529
1529
|
break;
|
|
@@ -1532,6 +1532,7 @@
|
|
|
1532
1532
|
case 'html':
|
|
1533
1533
|
directives[key] = nameRegex.test(value) ? value : creteCalc(value);
|
|
1534
1534
|
break;
|
|
1535
|
+
case 'template':
|
|
1535
1536
|
case 'bind':
|
|
1536
1537
|
case 'value':
|
|
1537
1538
|
case 'comment':
|
|
@@ -1563,6 +1564,7 @@
|
|
|
1563
1564
|
styles: Object.create(null),
|
|
1564
1565
|
vars: Object.create(null),
|
|
1565
1566
|
aliases: Object.create(null),
|
|
1567
|
+
params: Object.create(null),
|
|
1566
1568
|
};
|
|
1567
1569
|
}
|
|
1568
1570
|
|
|
@@ -2209,7 +2211,9 @@
|
|
|
2209
2211
|
/**
|
|
2210
2212
|
* @typedef {object} Directives
|
|
2211
2213
|
*
|
|
2212
|
-
* @property {
|
|
2214
|
+
* @property {string} [template]
|
|
2215
|
+
*
|
|
2216
|
+
* @property {boolean | string} [fragment]
|
|
2213
2217
|
*
|
|
2214
2218
|
* @property {string | Function} [if]
|
|
2215
2219
|
* @property {boolean} [else]
|
|
@@ -2238,6 +2242,7 @@
|
|
|
2238
2242
|
* @property {string?} [is]
|
|
2239
2243
|
* @property {string} [id]
|
|
2240
2244
|
* @property {Record<string, string | {name: string} | ((global: any) => void)>} attrs
|
|
2245
|
+
* @property {Record<string, string | ((global: any) => void)>} params
|
|
2241
2246
|
* @property {Record<string, string | boolean | ((global: any) => void)>} classes
|
|
2242
2247
|
* @property {Record<string, string | ((global: any) => void)>} styles
|
|
2243
2248
|
* @property {Record<string, string | (($event: any, global: any) => void)>} events
|
|
@@ -2298,6 +2303,8 @@
|
|
|
2298
2303
|
return () => { w.unwatch(computed); };
|
|
2299
2304
|
}
|
|
2300
2305
|
|
|
2306
|
+
/** @import * as Layout from '../Layout/index.mjs' */
|
|
2307
|
+
|
|
2301
2308
|
const bindable = {
|
|
2302
2309
|
new: true,
|
|
2303
2310
|
readonly: true,
|
|
@@ -2621,6 +2628,67 @@
|
|
|
2621
2628
|
}
|
|
2622
2629
|
return cloned;
|
|
2623
2630
|
}
|
|
2631
|
+
/**
|
|
2632
|
+
*
|
|
2633
|
+
* @param {Layout.Node} template
|
|
2634
|
+
* @param {Layout.Node} source
|
|
2635
|
+
* @param {Environment} sourceEnv
|
|
2636
|
+
*/
|
|
2637
|
+
params({params}, {attrs}, sourceEnv) {
|
|
2638
|
+
if (Object.keys(params).length === 0) { return this; }
|
|
2639
|
+
const cloned = new Environment(this);
|
|
2640
|
+
cloned.#store = this.#store;
|
|
2641
|
+
cloned.#parent = this.#parent;
|
|
2642
|
+
cloned.#object = this.#object;
|
|
2643
|
+
const explicit = cloned.#explicit;
|
|
2644
|
+
const items = cloned.#items;
|
|
2645
|
+
for (const [key, param] of Object.entries(params)) {
|
|
2646
|
+
const attr = key in attrs ? attrs[key] : null;
|
|
2647
|
+
if (typeof attr === 'string') {
|
|
2648
|
+
explicit[key] = items[key] = {get: () => attr};
|
|
2649
|
+
} else if (attr && typeof attr === 'object') {
|
|
2650
|
+
const item = sourceEnv.#items[attr.name];
|
|
2651
|
+
if (!item?.get) { continue; }
|
|
2652
|
+
if (!item.store) {
|
|
2653
|
+
explicit[key] = items[key] = item;
|
|
2654
|
+
continue;
|
|
2655
|
+
}
|
|
2656
|
+
for (const [k, it] of toItem(item.store, key)) {
|
|
2657
|
+
explicit[k] = items[k] = it;
|
|
2658
|
+
}
|
|
2659
|
+
continue;
|
|
2660
|
+
|
|
2661
|
+
} else if (typeof attr === 'function') {
|
|
2662
|
+
const val = new exports.Signal.Computed(() => attr(sourceEnv.getters));
|
|
2663
|
+
explicit[key] = items[key] = {
|
|
2664
|
+
get: () => { return val.get(); },
|
|
2665
|
+
};
|
|
2666
|
+
|
|
2667
|
+
continue;
|
|
2668
|
+
} else if (typeof param === 'function') {
|
|
2669
|
+
const getters = cloned.getters;
|
|
2670
|
+
cloned.#getters = null;
|
|
2671
|
+
const val = new exports.Signal.Computed(() => param(getters));
|
|
2672
|
+
explicit[key] = items[key] = {
|
|
2673
|
+
get: () => { return val.get(); },
|
|
2674
|
+
};
|
|
2675
|
+
continue;
|
|
2676
|
+
|
|
2677
|
+
} else {
|
|
2678
|
+
const item = items[param];
|
|
2679
|
+
if (!item?.get) { continue; }
|
|
2680
|
+
if (!item.store) {
|
|
2681
|
+
explicit[key] = items[key] = item;
|
|
2682
|
+
continue;
|
|
2683
|
+
}
|
|
2684
|
+
for (const [k, it] of toItem(item.store, key)) {
|
|
2685
|
+
explicit[k] = items[k] = it;
|
|
2686
|
+
}
|
|
2687
|
+
continue;
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
return cloned;
|
|
2691
|
+
}
|
|
2624
2692
|
/** @param {Record<string, any>} object */
|
|
2625
2693
|
setObject(object) {
|
|
2626
2694
|
const cloned = new Environment(this);
|
|
@@ -3670,15 +3738,24 @@
|
|
|
3670
3738
|
* @param {Element} parent
|
|
3671
3739
|
* @param {Node?} next
|
|
3672
3740
|
* @param {Environment} envs
|
|
3673
|
-
* @param {
|
|
3741
|
+
* @param {Record<string, [Layout.Node, Environment]>} templates
|
|
3742
|
+
* @param {(layout: Layout.Node, templates: Record<string, any>) => () => void} renderItem
|
|
3674
3743
|
* @returns {() => void}
|
|
3675
3744
|
*/
|
|
3676
|
-
function renderList(layouts, parent, next, envs, renderItem) {
|
|
3745
|
+
function renderList(layouts, parent, next, envs, templates, renderItem) {
|
|
3677
3746
|
|
|
3678
3747
|
/** @type {Set<() => void>?} */
|
|
3679
3748
|
let bkList = new Set();
|
|
3680
3749
|
/** @type {[string | Function | null, Layout.Node][]} */
|
|
3681
3750
|
let ifList = [];
|
|
3751
|
+
/** @type {Record<string, [Layout.Node, Environment]>} */
|
|
3752
|
+
let currentTemplates = Object.create(templates);
|
|
3753
|
+
for (const layout of layouts) {
|
|
3754
|
+
if (typeof layout === 'string') { continue; }
|
|
3755
|
+
const name = layout.directives.template;
|
|
3756
|
+
if (!name) { continue; }
|
|
3757
|
+
currentTemplates[name] = [layout, envs];
|
|
3758
|
+
}
|
|
3682
3759
|
|
|
3683
3760
|
/** @param {[string | Function | null, Layout.Node][]} list */
|
|
3684
3761
|
function renderIf(list) {
|
|
@@ -3696,7 +3773,7 @@
|
|
|
3696
3773
|
function renderIndex(index) {
|
|
3697
3774
|
const layout = list[index]?.[1];
|
|
3698
3775
|
if (!layout) { return; }
|
|
3699
|
-
destroy = renderItem(layout);
|
|
3776
|
+
destroy = renderItem(layout, currentTemplates);
|
|
3700
3777
|
}
|
|
3701
3778
|
bkList.add(() => {
|
|
3702
3779
|
destroy();
|
|
@@ -3723,6 +3800,11 @@
|
|
|
3723
3800
|
bkList.add(() => node.remove());
|
|
3724
3801
|
continue;
|
|
3725
3802
|
}
|
|
3803
|
+
if (layout.directives.template) {
|
|
3804
|
+
renderIf(ifList);
|
|
3805
|
+
ifList = [];
|
|
3806
|
+
continue;
|
|
3807
|
+
}
|
|
3726
3808
|
if (ifList.length && layout.directives.else) {
|
|
3727
3809
|
const ifv = layout.directives.if || null;
|
|
3728
3810
|
ifList.push([ifv, layout]);
|
|
@@ -3740,7 +3822,7 @@
|
|
|
3740
3822
|
continue;
|
|
3741
3823
|
}
|
|
3742
3824
|
bkList.add(
|
|
3743
|
-
renderItem(layout)
|
|
3825
|
+
renderItem(layout, currentTemplates)
|
|
3744
3826
|
);
|
|
3745
3827
|
}
|
|
3746
3828
|
|
|
@@ -3870,34 +3952,31 @@
|
|
|
3870
3952
|
|
|
3871
3953
|
/** @import Store from '../Store/index.mjs' */
|
|
3872
3954
|
|
|
3873
|
-
/**
|
|
3874
|
-
* @param {Layout.Node} layout
|
|
3875
|
-
* @param {Element} parent
|
|
3876
|
-
* @param {Node?} next
|
|
3877
|
-
* @param {Environment} env
|
|
3878
|
-
* @param {(layout: Layout.Node) => () => void} renderItem
|
|
3879
|
-
* @returns {() => void}
|
|
3880
|
-
*/
|
|
3881
|
-
function renderFragment(layout, parent, next, env, renderItem) {
|
|
3882
|
-
return renderFillDirectives(parent, next, env, layout.directives) ||
|
|
3883
|
-
renderList(layout.children || [], parent, next, env, renderItem);
|
|
3884
|
-
|
|
3885
|
-
}
|
|
3886
3955
|
/**
|
|
3887
3956
|
* @param {Layout.Node} layout
|
|
3888
3957
|
* @param {Element} parent
|
|
3889
3958
|
* @param {Node?} next
|
|
3890
3959
|
* @param {Store} store
|
|
3891
3960
|
* @param {Environment} env
|
|
3961
|
+
* @param {Record<string, [Layout.Node, Environment]>} templates
|
|
3892
3962
|
* @param {string[]} componentPath
|
|
3893
3963
|
* @param {((path: string[]) => Component?)?} [getComponent]
|
|
3894
3964
|
*/
|
|
3895
|
-
function renderItem(layout, parent, next, store, env, componentPath, getComponent) {
|
|
3965
|
+
function renderItem(layout, parent, next, store, env, templates, componentPath, getComponent) {
|
|
3896
3966
|
env = env.set(layout.aliases, layout.vars);
|
|
3967
|
+
const fragment = layout.directives.fragment;
|
|
3968
|
+
if (fragment && typeof fragment === 'string') {
|
|
3969
|
+
const template = templates[fragment];
|
|
3970
|
+
if (!template) { return () => {}; }
|
|
3971
|
+
const [templateLayout, templateEnv] = template;
|
|
3972
|
+
const newEnv = templateEnv.params(templateLayout, layout, env);
|
|
3973
|
+
return render(templateLayout, parent, next, store, newEnv, templates, componentPath, getComponent);
|
|
3974
|
+
}
|
|
3897
3975
|
if (!layout.name || layout.directives.fragment) {
|
|
3898
|
-
return
|
|
3899
|
-
|
|
3900
|
-
|
|
3976
|
+
return renderFillDirectives(parent, next, env, layout.directives) ||
|
|
3977
|
+
renderList(layout.children || [], parent, next, env, templates, (layout, templates) => {
|
|
3978
|
+
return render(layout, parent, next, store, env, templates, componentPath, getComponent);
|
|
3979
|
+
});
|
|
3901
3980
|
}
|
|
3902
3981
|
const path = [...componentPath, layout.name];
|
|
3903
3982
|
const component = getComponent?.(path);
|
|
@@ -3921,13 +4000,13 @@
|
|
|
3921
4000
|
: createTagComponent(context, component.tag, component.is)
|
|
3922
4001
|
: createTagComponent(context, layout.name, layout.is);
|
|
3923
4002
|
const root = Array.isArray(r) ? r[0] : r;
|
|
3924
|
-
const slot = Array.isArray(r)
|
|
4003
|
+
const slot = Array.isArray(r) ? r[1] : root;
|
|
3925
4004
|
parent.insertBefore(root, next);
|
|
3926
|
-
const children =
|
|
4005
|
+
const children = slot ?
|
|
3927
4006
|
renderFillDirectives(slot, null, env, layout.directives)
|
|
3928
|
-
|| renderList(layout.children || [], slot, null, env,
|
|
3929
|
-
return render(
|
|
3930
|
-
});
|
|
4007
|
+
|| renderList(layout.children || [], slot, null, env, templates, (layout, templates) => {
|
|
4008
|
+
return render(layout, slot, null, store, env, templates, componentPath, getComponent);
|
|
4009
|
+
}) : () => {};
|
|
3931
4010
|
|
|
3932
4011
|
|
|
3933
4012
|
bindClasses(root, layout.classes, env);
|
|
@@ -3949,11 +4028,12 @@
|
|
|
3949
4028
|
* @param {Node?} next
|
|
3950
4029
|
* @param {Store} store
|
|
3951
4030
|
* @param {Environment} env
|
|
4031
|
+
* @param {Record<string, [Layout.Node, Environment]>} templates
|
|
3952
4032
|
* @param {string[]} componentPath
|
|
3953
4033
|
* @param {((path: string[]) => Component?)?} [getComponent]
|
|
3954
4034
|
* @returns {() => void}
|
|
3955
4035
|
*/
|
|
3956
|
-
function render(layout, parent, next, store, env, componentPath, getComponent) {
|
|
4036
|
+
function render(layout, parent, next, store, env, templates, componentPath, getComponent) {
|
|
3957
4037
|
const { directives } = layout;
|
|
3958
4038
|
const { value } = directives;
|
|
3959
4039
|
if (value) {
|
|
@@ -3964,22 +4044,22 @@
|
|
|
3964
4044
|
}
|
|
3965
4045
|
const enumValue = directives.enum;
|
|
3966
4046
|
if (!enumValue) {
|
|
3967
|
-
return renderItem(layout, parent, next, store, env, componentPath, getComponent);
|
|
4047
|
+
return renderItem(layout, parent, next, store, env, templates, componentPath, getComponent);
|
|
3968
4048
|
}
|
|
3969
4049
|
const newStore = enumValue === true ? store : env.enum(enumValue);
|
|
3970
4050
|
if (newStore instanceof ArrayStore) {
|
|
3971
4051
|
return renderArray(layout, parent, next, newStore, env, (a, b, c, store, env) => {
|
|
3972
|
-
return renderItem(a, b, c, store, env, componentPath, getComponent);
|
|
4052
|
+
return renderItem(a, b, c, store, env, templates, componentPath, getComponent);
|
|
3973
4053
|
});
|
|
3974
4054
|
}
|
|
3975
4055
|
if (newStore instanceof ObjectStore) {
|
|
3976
4056
|
return renderObject(layout, parent, next, newStore, env, (a, b, c, store, env) => {
|
|
3977
|
-
return renderItem(a, b, c, store, env, componentPath, getComponent);
|
|
4057
|
+
return renderItem(a, b, c, store, env, templates, componentPath, getComponent);
|
|
3978
4058
|
});
|
|
3979
4059
|
}
|
|
3980
4060
|
if (typeof newStore === 'function') {
|
|
3981
4061
|
return renderEnum(layout, parent, next, store, newStore, env, (a, b, c, store, env) => {
|
|
3982
|
-
return renderItem(a, b, c, store, env, componentPath, getComponent);
|
|
4062
|
+
return renderItem(a, b, c, store, env, templates, componentPath, getComponent);
|
|
3983
4063
|
});
|
|
3984
4064
|
}
|
|
3985
4065
|
return () => { };
|
|
@@ -4014,8 +4094,9 @@
|
|
|
4014
4094
|
const components = options.find(v => typeof v === 'function');
|
|
4015
4095
|
const global = options.find(v => typeof v === 'object');
|
|
4016
4096
|
const env = new Environment(global).setStore(store);
|
|
4017
|
-
|
|
4018
|
-
|
|
4097
|
+
const templates = Object.create(null);
|
|
4098
|
+
return renderList(layouts, parent, null, env, templates, (layout, templates) => {
|
|
4099
|
+
return render(layout, parent, null, store, env, templates, [], components);
|
|
4019
4100
|
});
|
|
4020
4101
|
}
|
|
4021
4102
|
|