@neeloong/form 0.20.0 → 0.21.1
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 +43 -25
- package/index.full.js +640 -676
- package/index.full.min.js +8 -8
- package/index.full.min.mjs +6 -6
- package/index.min.mjs +3 -3
- package/index.mjs +640 -676
- package/package.json +1 -1
package/index.full.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @neeloong/form v0.
|
|
3
|
-
* (c) 2024-
|
|
2
|
+
* @neeloong/form v0.21.1
|
|
3
|
+
* (c) 2024-2026 Fierflame
|
|
4
4
|
* @license Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -729,7 +729,6 @@
|
|
|
729
729
|
* @param {string | number | null} [options.index]
|
|
730
730
|
* @param {boolean} [options.new]
|
|
731
731
|
* @param {(value: any, index: any, store: Store) => void} [options.onUpdate]
|
|
732
|
-
* @param {(value: any, index: any, store: Store) => void} [options.onUpdateState]
|
|
733
732
|
*/
|
|
734
733
|
function create(schema, options) {
|
|
735
734
|
const type = schema.type;
|
|
@@ -860,13 +859,14 @@
|
|
|
860
859
|
*
|
|
861
860
|
* @template {Store} T
|
|
862
861
|
* @param {T} store
|
|
863
|
-
* @param {((store: T) => any) | any} def
|
|
862
|
+
* @param {((store: T, value?: any) => any) | any} def
|
|
863
|
+
* @returns {(value?: any) => unknown}
|
|
864
864
|
*/
|
|
865
865
|
function makeDefault(store, def) {
|
|
866
866
|
if (typeof def !== 'function') {
|
|
867
867
|
return () => structuredClone(def);
|
|
868
868
|
}
|
|
869
|
-
return () => structuredClone(def(store));
|
|
869
|
+
return (value) => structuredClone(def(store, value));
|
|
870
870
|
}
|
|
871
871
|
|
|
872
872
|
/** @import { Ref } from './ref.mjs' */
|
|
@@ -879,7 +879,7 @@
|
|
|
879
879
|
*/
|
|
880
880
|
class Store {
|
|
881
881
|
/** @type {Map<string, Set<(value: any, store: any) => void | boolean | null>>} */
|
|
882
|
-
#events = new Map()
|
|
882
|
+
#events = new Map();
|
|
883
883
|
/**
|
|
884
884
|
* 触发事件并通知监听器
|
|
885
885
|
* @template {keyof Schema.Events} K
|
|
@@ -912,7 +912,7 @@
|
|
|
912
912
|
events.set(key, set);
|
|
913
913
|
}
|
|
914
914
|
set.add(fn);
|
|
915
|
-
return () => { set?.delete(fn); }
|
|
915
|
+
return () => { set?.delete(fn); };
|
|
916
916
|
|
|
917
917
|
}
|
|
918
918
|
/**
|
|
@@ -923,7 +923,7 @@
|
|
|
923
923
|
* @param {boolean} [options.new] 是否为新建环境
|
|
924
924
|
*/
|
|
925
925
|
static create(schema, options = {}) {
|
|
926
|
-
return create({type: schema}, { ...options, parent: null });
|
|
926
|
+
return create({ type: schema }, { ...options, parent: null });
|
|
927
927
|
}
|
|
928
928
|
/**
|
|
929
929
|
* 设置自定义类型的存储类
|
|
@@ -945,8 +945,7 @@
|
|
|
945
945
|
* @param {Schema.Field<M>} schema 字段的 Schema 定义
|
|
946
946
|
* @param {object} [options] 可选配置
|
|
947
947
|
* @param {Store?} [options.parent]
|
|
948
|
-
* @param {((store: Store) => any) |
|
|
949
|
-
* @param {*} [options.state]
|
|
948
|
+
* @param {((store: Store, value?: any) => any) | object | number | string | boolean | null | undefined} [options.default]
|
|
950
949
|
* @param {number | string | null} [options.index]
|
|
951
950
|
* @param {number | Signal.State<number> | Signal.Computed<number>} [options.size]
|
|
952
951
|
* @param {boolean} [options.null]
|
|
@@ -974,22 +973,19 @@
|
|
|
974
973
|
* @param {Ref?} [options.ref]
|
|
975
974
|
*
|
|
976
975
|
* @param {((value: any) => any)?} [options.setValue]
|
|
977
|
-
* @param {((value: any) => any)?} [options.
|
|
978
|
-
* @param {((value: any, state: any) => [value: any, state: any])?} [options.convert]
|
|
976
|
+
* @param {((value: any) => any)?} [options.convert]
|
|
979
977
|
*
|
|
980
978
|
* @param {((value: T?, index: any, store: Store) => void)?} [options.onUpdate]
|
|
981
|
-
* @param {((value: T?, index: any, store: Store) => void)?} [options.onUpdateState]
|
|
982
979
|
*/
|
|
983
980
|
constructor(schema, {
|
|
984
|
-
null: isNull,
|
|
985
|
-
setValue,
|
|
981
|
+
null: isNull, ref, default: defaultValue,
|
|
982
|
+
setValue, convert, onUpdate,
|
|
986
983
|
validator, validators,
|
|
987
984
|
index, size, new: isNew, parent: parentNode,
|
|
988
985
|
hidden, clearable, required, disabled, readonly, removable,
|
|
989
986
|
label, description, placeholder, min, max, step, minLength, maxLength, pattern, values: values$1
|
|
990
987
|
} = {}) {
|
|
991
988
|
this.schema = schema;
|
|
992
|
-
this.#state.set(typeof state === 'object' && state || {});
|
|
993
989
|
const parent = parentNode instanceof Store ? parentNode : null;
|
|
994
990
|
if (parent) {
|
|
995
991
|
this.#parent = parent;
|
|
@@ -1033,7 +1029,7 @@
|
|
|
1033
1029
|
const s = selfReadonly.get();
|
|
1034
1030
|
return s === null ? readonlyScript.get() : s;
|
|
1035
1031
|
};
|
|
1036
|
-
const readonlyParent =
|
|
1032
|
+
const readonlyParent = parent ? parent.#readonly : null;
|
|
1037
1033
|
this.#selfReadonly = selfReadonly;
|
|
1038
1034
|
this.#readonly = readonlyParent
|
|
1039
1035
|
? new exports.Signal.Computed(() => readonlyParent.get() || getReadonly())
|
|
@@ -1062,15 +1058,15 @@
|
|
|
1062
1058
|
|
|
1063
1059
|
const [changed, changedResult, cancelChange] = createAsyncValidator(this, schema.validators?.change, validators?.change);
|
|
1064
1060
|
const [blurred, blurredResult, cancelBlur] = createAsyncValidator(this, schema.validators?.blur, validators?.blur);
|
|
1065
|
-
this.listen('change', () => {changed();});
|
|
1066
|
-
this.listen('blur', () => {blurred();});
|
|
1061
|
+
this.listen('change', () => { changed(); });
|
|
1062
|
+
this.listen('blur', () => { blurred(); });
|
|
1067
1063
|
this.#errors = merge(validatorResult, changedResult, blurredResult);
|
|
1068
1064
|
this.#validatorResult = validatorResult;
|
|
1069
1065
|
this.#changed = changed;
|
|
1070
1066
|
this.#blurred = blurred;
|
|
1071
1067
|
this.#cancelChange = cancelChange;
|
|
1072
1068
|
this.#cancelBlur = cancelBlur;
|
|
1073
|
-
|
|
1069
|
+
|
|
1074
1070
|
if (size instanceof exports.Signal.State || size instanceof exports.Signal.Computed) {
|
|
1075
1071
|
this.#size = size;
|
|
1076
1072
|
} else {
|
|
@@ -1084,30 +1080,25 @@
|
|
|
1084
1080
|
}
|
|
1085
1081
|
this.#ref = ref || null;
|
|
1086
1082
|
this.#onUpdate = onUpdate || null;
|
|
1087
|
-
this.#onUpdateState = onUpdateState || null;
|
|
1088
1083
|
this.#setValue = typeof setValue === 'function' ? setValue : null;
|
|
1089
|
-
this.#setState = typeof setState === 'function' ? setState : null;
|
|
1090
1084
|
this.#convert = typeof convert === 'function' ? convert : null;
|
|
1091
1085
|
this.#index.set(index ?? '');
|
|
1092
|
-
|
|
1086
|
+
|
|
1093
1087
|
for (const [k, f] of Object.entries(schema.events || {})) {
|
|
1094
1088
|
if (typeof f !== 'function') { continue; }
|
|
1095
1089
|
// @ts-ignore
|
|
1096
1090
|
this.listen(k, f);
|
|
1097
1091
|
}
|
|
1098
1092
|
}
|
|
1099
|
-
#createDefault
|
|
1100
|
-
|
|
1093
|
+
#createDefault;
|
|
1094
|
+
/** @param {any} [value] @returns {any} */
|
|
1095
|
+
createDefault(value) { return this.#createDefault(value); }
|
|
1101
1096
|
/** @type {((value: any) => any)?} */
|
|
1102
|
-
#setValue = null
|
|
1097
|
+
#setValue = null;
|
|
1103
1098
|
/** @type {((value: any) => any)?} */
|
|
1104
|
-
#
|
|
1105
|
-
/** @type {((value: any, state: any) => [value: any, state: any])?} */
|
|
1106
|
-
#convert = null
|
|
1099
|
+
#convert = null;
|
|
1107
1100
|
/** @type {((value: any, index: any, store: Store) => void)?} */
|
|
1108
|
-
#onUpdate = null
|
|
1109
|
-
/** @type {((value: any, index: any, store: Store) => void)?} */
|
|
1110
|
-
#onUpdateState = null
|
|
1101
|
+
#onUpdate = null;
|
|
1111
1102
|
/** @readonly @type {Store?} */
|
|
1112
1103
|
#parent = null;
|
|
1113
1104
|
/** @readonly @type {Store} */
|
|
@@ -1119,7 +1110,7 @@
|
|
|
1119
1110
|
/** @readonly @type {any} */
|
|
1120
1111
|
#component;
|
|
1121
1112
|
/** @type {Signal.State<boolean>?} */
|
|
1122
|
-
#selfLoading = null
|
|
1113
|
+
#selfLoading = null;
|
|
1123
1114
|
/** @type {Signal.State<boolean>} */
|
|
1124
1115
|
#loading;
|
|
1125
1116
|
get loading() {
|
|
@@ -1127,7 +1118,7 @@
|
|
|
1127
1118
|
}
|
|
1128
1119
|
set loading(loading) {
|
|
1129
1120
|
const s = this.#selfLoading;
|
|
1130
|
-
if (!s) { return }
|
|
1121
|
+
if (!s) { return; }
|
|
1131
1122
|
s.set(Boolean(loading));
|
|
1132
1123
|
}
|
|
1133
1124
|
/** 存储对象自身 */
|
|
@@ -1166,9 +1157,9 @@
|
|
|
1166
1157
|
get immutable() { return this.#immutable; }
|
|
1167
1158
|
|
|
1168
1159
|
/** @readonly @type {Signal.Computed<boolean>} */
|
|
1169
|
-
#new
|
|
1160
|
+
#new;
|
|
1170
1161
|
/** @readonly @type {Signal.State<boolean>} */
|
|
1171
|
-
#selfNew
|
|
1162
|
+
#selfNew;
|
|
1172
1163
|
get selfNew() { return this.#selfNew.get(); }
|
|
1173
1164
|
set selfNew(v) { this.#selfNew.set(Boolean(v)); }
|
|
1174
1165
|
/** 是否新建项 */
|
|
@@ -1176,9 +1167,9 @@
|
|
|
1176
1167
|
set new(v) { this.#selfNew.set(Boolean(v)); }
|
|
1177
1168
|
|
|
1178
1169
|
/** @readonly @type {Signal.State<boolean?>} */
|
|
1179
|
-
#selfHidden
|
|
1170
|
+
#selfHidden;
|
|
1180
1171
|
/** @readonly @type {Signal.Computed<boolean>} */
|
|
1181
|
-
#hidden
|
|
1172
|
+
#hidden;
|
|
1182
1173
|
get selfHidden() { return this.#selfHidden.get(); }
|
|
1183
1174
|
set selfHidden(v) { this.#selfHidden.set(typeof v === 'boolean' ? v : null); }
|
|
1184
1175
|
/** 是否可隐藏 */
|
|
@@ -1186,9 +1177,9 @@
|
|
|
1186
1177
|
set hidden(v) { this.#selfHidden.set(typeof v === 'boolean' ? v : null); }
|
|
1187
1178
|
|
|
1188
1179
|
/** @readonly @type {Signal.State<boolean?>} */
|
|
1189
|
-
#selfClearable
|
|
1180
|
+
#selfClearable;
|
|
1190
1181
|
/** @readonly @type {Signal.Computed<boolean>} */
|
|
1191
|
-
#clearable
|
|
1182
|
+
#clearable;
|
|
1192
1183
|
get selfClearable() { return this.#selfClearable.get(); }
|
|
1193
1184
|
set selfClearable(v) { this.#selfClearable.set(typeof v === 'boolean' ? v : null); }
|
|
1194
1185
|
/** 是否可清除 */
|
|
@@ -1196,9 +1187,9 @@
|
|
|
1196
1187
|
set clearable(v) { this.#selfClearable.set(typeof v === 'boolean' ? v : null); }
|
|
1197
1188
|
|
|
1198
1189
|
/** @readonly @type {Signal.State<boolean?>} */
|
|
1199
|
-
#selfRequired
|
|
1190
|
+
#selfRequired;
|
|
1200
1191
|
/** @readonly @type {Signal.Computed<boolean>} */
|
|
1201
|
-
#required
|
|
1192
|
+
#required;
|
|
1202
1193
|
get selfRequired() { return this.#selfRequired.get(); }
|
|
1203
1194
|
set selfRequired(v) { this.#selfRequired.set(typeof v === 'boolean' ? v : null); }
|
|
1204
1195
|
/** 是否必填 */
|
|
@@ -1206,9 +1197,9 @@
|
|
|
1206
1197
|
set required(v) { this.#selfRequired.set(typeof v === 'boolean' ? v : null); }
|
|
1207
1198
|
|
|
1208
1199
|
/** @readonly @type {Signal.State<boolean?>} */
|
|
1209
|
-
#selfDisabled
|
|
1200
|
+
#selfDisabled;
|
|
1210
1201
|
/** @readonly @type {Signal.Computed<boolean>} */
|
|
1211
|
-
#disabled
|
|
1202
|
+
#disabled;
|
|
1212
1203
|
get selfDisabled() { return this.#selfDisabled.get(); }
|
|
1213
1204
|
set selfDisabled(v) { this.#selfDisabled.set(typeof v === 'boolean' ? v : null); }
|
|
1214
1205
|
/** 是否禁用字段 */
|
|
@@ -1216,9 +1207,9 @@
|
|
|
1216
1207
|
set disabled(v) { this.#selfDisabled.set(typeof v === 'boolean' ? v : null); }
|
|
1217
1208
|
|
|
1218
1209
|
/** @readonly @type {Signal.State<boolean?>} */
|
|
1219
|
-
#selfReadonly
|
|
1210
|
+
#selfReadonly;
|
|
1220
1211
|
/** @readonly @type {Signal.Computed<boolean>} */
|
|
1221
|
-
#readonly
|
|
1212
|
+
#readonly;
|
|
1222
1213
|
get selfReadonly() { return this.#selfReadonly.get(); }
|
|
1223
1214
|
set selfReadonly(v) { this.#selfReadonly.set(typeof v === 'boolean' ? v : null); }
|
|
1224
1215
|
/** 是否只读 */
|
|
@@ -1227,9 +1218,9 @@
|
|
|
1227
1218
|
|
|
1228
1219
|
|
|
1229
1220
|
/** @readonly @type {Signal.State<boolean?>} */
|
|
1230
|
-
#selfRemovable
|
|
1221
|
+
#selfRemovable;
|
|
1231
1222
|
/** @readonly @type {Signal.Computed<boolean>} */
|
|
1232
|
-
#removable
|
|
1223
|
+
#removable;
|
|
1233
1224
|
get selfRemovable() { return this.#selfRemovable.get(); }
|
|
1234
1225
|
set selfRemovable(v) { this.#selfRemovable.set(typeof v === 'boolean' ? v : null); }
|
|
1235
1226
|
/** 是否只读 */
|
|
@@ -1238,9 +1229,9 @@
|
|
|
1238
1229
|
|
|
1239
1230
|
|
|
1240
1231
|
/** @readonly @type {Signal.State<string?>} */
|
|
1241
|
-
#selfLabel
|
|
1232
|
+
#selfLabel;
|
|
1242
1233
|
/** @readonly @type {Signal.Computed<string?>} */
|
|
1243
|
-
#label
|
|
1234
|
+
#label;
|
|
1244
1235
|
get selfLabel() { return this.#selfLabel.get(); }
|
|
1245
1236
|
set selfLabel(v) { this.#selfLabel.set(string(v)); }
|
|
1246
1237
|
/** 字段的标签信息 */
|
|
@@ -1249,9 +1240,9 @@
|
|
|
1249
1240
|
|
|
1250
1241
|
|
|
1251
1242
|
/** @readonly @type {Signal.State<string?>} */
|
|
1252
|
-
#selfDescription
|
|
1243
|
+
#selfDescription;
|
|
1253
1244
|
/** @readonly @type {Signal.Computed<string?>} */
|
|
1254
|
-
#description
|
|
1245
|
+
#description;
|
|
1255
1246
|
get selfDescription() { return this.#selfDescription.get(); }
|
|
1256
1247
|
set selfDescription(v) { this.#selfDescription.set(string(v)); }
|
|
1257
1248
|
/** 字段的描述信息 */
|
|
@@ -1259,9 +1250,9 @@
|
|
|
1259
1250
|
set description(v) { this.#selfDescription.set(string(v)); }
|
|
1260
1251
|
|
|
1261
1252
|
/** @readonly @type {Signal.State<string?>} */
|
|
1262
|
-
#selfPlaceholder
|
|
1253
|
+
#selfPlaceholder;
|
|
1263
1254
|
/** @readonly @type {Signal.Computed<string?>} */
|
|
1264
|
-
#placeholder
|
|
1255
|
+
#placeholder;
|
|
1265
1256
|
get selfPlaceholder() { return this.#selfPlaceholder.get(); }
|
|
1266
1257
|
set selfPlaceholder(v) { this.#selfPlaceholder.set(string(v)); }
|
|
1267
1258
|
/** 字段的占位符信息 */
|
|
@@ -1270,9 +1261,9 @@
|
|
|
1270
1261
|
|
|
1271
1262
|
|
|
1272
1263
|
/** @readonly @type {Signal.State<number?>} */
|
|
1273
|
-
#selfMin
|
|
1264
|
+
#selfMin;
|
|
1274
1265
|
/** @readonly @type {Signal.Computed<number?>} */
|
|
1275
|
-
#min
|
|
1266
|
+
#min;
|
|
1276
1267
|
get selfMin() { return this.#selfMin.get(); }
|
|
1277
1268
|
set selfMin(v) { this.#selfMin.set(number(v)); }
|
|
1278
1269
|
/** 数值字段的最小值限制 */
|
|
@@ -1281,9 +1272,9 @@
|
|
|
1281
1272
|
|
|
1282
1273
|
|
|
1283
1274
|
/** @readonly @type {Signal.State<number?>} */
|
|
1284
|
-
#selfMax
|
|
1275
|
+
#selfMax;
|
|
1285
1276
|
/** @readonly @type {Signal.Computed<number?>} */
|
|
1286
|
-
#max
|
|
1277
|
+
#max;
|
|
1287
1278
|
get selfMax() { return this.#selfMax.get(); }
|
|
1288
1279
|
set selfMax(v) { this.#selfMax.set(number(v)); }
|
|
1289
1280
|
/** 数值字段的最大值限制 */
|
|
@@ -1292,9 +1283,9 @@
|
|
|
1292
1283
|
|
|
1293
1284
|
|
|
1294
1285
|
/** @readonly @type {Signal.State<number?>} */
|
|
1295
|
-
#selfStep
|
|
1286
|
+
#selfStep;
|
|
1296
1287
|
/** @readonly @type {Signal.Computed<number?>} */
|
|
1297
|
-
#step
|
|
1288
|
+
#step;
|
|
1298
1289
|
get selfStep() { return this.#selfStep.get(); }
|
|
1299
1290
|
set selfStep(v) { this.#selfStep.set(number(v)); }
|
|
1300
1291
|
/** 数值字段的步长 */
|
|
@@ -1302,9 +1293,9 @@
|
|
|
1302
1293
|
set step(v) { this.#selfStep.set(number(v)); }
|
|
1303
1294
|
|
|
1304
1295
|
/** @readonly @type {Signal.State<number?>} */
|
|
1305
|
-
#selfMinLength
|
|
1296
|
+
#selfMinLength;
|
|
1306
1297
|
/** @readonly @type {Signal.Computed<number?>} */
|
|
1307
|
-
#minLength
|
|
1298
|
+
#minLength;
|
|
1308
1299
|
get selfMinLength() { return this.#selfMinLength.get(); }
|
|
1309
1300
|
set selfMinLength(v) { this.#selfMinLength.set(number(v)); }
|
|
1310
1301
|
/** 最小长度 */
|
|
@@ -1312,9 +1303,9 @@
|
|
|
1312
1303
|
set minLength(v) { this.#selfMinLength.set(number(v)); }
|
|
1313
1304
|
|
|
1314
1305
|
/** @readonly @type {Signal.State<number?>} */
|
|
1315
|
-
#selfMaxLength
|
|
1306
|
+
#selfMaxLength;
|
|
1316
1307
|
/** @readonly @type {Signal.Computed<number?>} */
|
|
1317
|
-
#maxLength
|
|
1308
|
+
#maxLength;
|
|
1318
1309
|
get selfMaxLength() { return this.#selfMaxLength.get(); }
|
|
1319
1310
|
set selfMaxLength(v) { this.#selfMaxLength.set(number(v)); }
|
|
1320
1311
|
/** 最大长度 */
|
|
@@ -1322,9 +1313,9 @@
|
|
|
1322
1313
|
set maxLength(v) { this.#selfMaxLength.set(number(v)); }
|
|
1323
1314
|
|
|
1324
1315
|
/** @readonly @type {Signal.State<RegExp?>} */
|
|
1325
|
-
#selfPattern
|
|
1316
|
+
#selfPattern;
|
|
1326
1317
|
/** @readonly @type {Signal.Computed<RegExp?>} */
|
|
1327
|
-
#pattern
|
|
1318
|
+
#pattern;
|
|
1328
1319
|
get selfPattern() { return this.#selfPattern.get(); }
|
|
1329
1320
|
set selfPattern(v) { this.#selfPattern.set(regex(v)); }
|
|
1330
1321
|
/** 模式 */
|
|
@@ -1333,9 +1324,9 @@
|
|
|
1333
1324
|
|
|
1334
1325
|
|
|
1335
1326
|
/** @readonly @type {Signal.State<(Schema.Value.Group | Schema.Value)[] | null>} */
|
|
1336
|
-
#selfValues
|
|
1327
|
+
#selfValues;
|
|
1337
1328
|
/** @readonly @type {Signal.Computed<(Schema.Value.Group | Schema.Value)[] | null>} */
|
|
1338
|
-
#values
|
|
1329
|
+
#values;
|
|
1339
1330
|
get selfValues() { return this.#selfValues.get(); }
|
|
1340
1331
|
set selfValues(v) { this.#selfValues.set(values(v)); }
|
|
1341
1332
|
/** 可选值列表 */
|
|
@@ -1344,24 +1335,24 @@
|
|
|
1344
1335
|
|
|
1345
1336
|
|
|
1346
1337
|
/** @type {Signal.Computed<string[]>} */
|
|
1347
|
-
#errors
|
|
1338
|
+
#errors;
|
|
1348
1339
|
/** @type {Signal.Computed<string[]>} */
|
|
1349
|
-
#validatorResult
|
|
1340
|
+
#validatorResult;
|
|
1350
1341
|
/** @type {() => Promise<string[]>} */
|
|
1351
|
-
#changed
|
|
1342
|
+
#changed;
|
|
1352
1343
|
/** @type {() => Promise<string[]>} */
|
|
1353
|
-
#blurred
|
|
1344
|
+
#blurred;
|
|
1354
1345
|
/** @type {() => void} */
|
|
1355
|
-
#cancelChange
|
|
1346
|
+
#cancelChange;
|
|
1356
1347
|
/** @type {() => void} */
|
|
1357
|
-
#cancelBlur
|
|
1348
|
+
#cancelBlur;
|
|
1358
1349
|
/** 所有校验错误列表 */
|
|
1359
1350
|
get errors() { return this.#errors.get(); }
|
|
1360
1351
|
/** 字段校验错误信息 */
|
|
1361
1352
|
get error() { return this.#errors.get()[0]; }
|
|
1362
1353
|
|
|
1363
1354
|
/** @returns {IterableIterator<[key: string | number, value: Store]>} */
|
|
1364
|
-
*[Symbol.iterator]() {}
|
|
1355
|
+
*[Symbol.iterator]() { }
|
|
1365
1356
|
/**
|
|
1366
1357
|
* 获取子存储
|
|
1367
1358
|
* @param {string | number} key
|
|
@@ -1373,11 +1364,9 @@
|
|
|
1373
1364
|
#initValue = new exports.Signal.State(/** @type {T?} */(null));
|
|
1374
1365
|
#value = new exports.Signal.State(this.#initValue.get());
|
|
1375
1366
|
|
|
1376
|
-
|
|
1377
|
-
#state = new exports.Signal.State(/** @type {any} */(null));
|
|
1378
1367
|
|
|
1379
1368
|
/** 内容是否已改变 */
|
|
1380
|
-
get changed() { return this.#value.get()
|
|
1369
|
+
get changed() { return !Object.is(this.#value.get(), this.#initValue.get()); }
|
|
1381
1370
|
|
|
1382
1371
|
/** 字段当前值 */
|
|
1383
1372
|
get value() { return this.#value.get(); }
|
|
@@ -1392,34 +1381,26 @@
|
|
|
1392
1381
|
this.#requestUpdate();
|
|
1393
1382
|
}
|
|
1394
1383
|
|
|
1395
|
-
/** 字段状态 */
|
|
1396
|
-
get state() { return this.#state.get(); }
|
|
1397
|
-
set state(v) {
|
|
1398
|
-
const newState = this.#setState?.(v);
|
|
1399
|
-
const sta = newState === undefined ? v : newState;
|
|
1400
|
-
this.#state.set(sta);
|
|
1401
|
-
this.#onUpdateState?.(sta, this.#index.get(), this);
|
|
1402
|
-
this.#requestUpdate();
|
|
1403
|
-
}
|
|
1404
1384
|
#requestUpdate() {
|
|
1405
1385
|
if (this.#needUpdate) { return; }
|
|
1406
1386
|
this.#needUpdate = true;
|
|
1407
1387
|
queueMicrotask(() => {
|
|
1408
1388
|
const oldValue = this.#value.get();
|
|
1409
|
-
|
|
1410
|
-
this.#runUpdate(oldValue, oldState);
|
|
1389
|
+
this.#runUpdate(oldValue);
|
|
1411
1390
|
});
|
|
1412
1391
|
}
|
|
1413
1392
|
/** 重置数据 */
|
|
1414
|
-
reset(value = this.#initValue.get()) {
|
|
1415
|
-
this.#reset(value);
|
|
1393
|
+
reset(value = this.#set ? this.#initValue.get() : this.#createDefault(), isNew = this.#selfNew.get()) {
|
|
1394
|
+
this.#reset(value, Boolean(isNew));
|
|
1416
1395
|
}
|
|
1417
1396
|
/**
|
|
1418
1397
|
*
|
|
1419
1398
|
* @param {*} v
|
|
1399
|
+
* @param {boolean} isNew
|
|
1420
1400
|
* @returns
|
|
1421
1401
|
*/
|
|
1422
|
-
#reset(v) {
|
|
1402
|
+
#reset(v, isNew) {
|
|
1403
|
+
this.#selfNew.set(isNew);
|
|
1423
1404
|
const newValue = this.#setValue?.(v);
|
|
1424
1405
|
const value = newValue === undefined ? v : newValue;
|
|
1425
1406
|
this.#cancelChange();
|
|
@@ -1427,16 +1408,17 @@
|
|
|
1427
1408
|
this.#set = true;
|
|
1428
1409
|
if (!value || typeof value !== 'object') {
|
|
1429
1410
|
for (const [, field] of this) {
|
|
1430
|
-
field.#reset(null);
|
|
1411
|
+
field.#reset(null, false);
|
|
1431
1412
|
}
|
|
1432
1413
|
this.#value.set(value);
|
|
1433
1414
|
this.#initValue.set(value);
|
|
1415
|
+
this.#onUpdate?.(value, this.#index.get(), this);
|
|
1434
1416
|
return value;
|
|
1435
1417
|
}
|
|
1436
1418
|
/** @type {*} */
|
|
1437
|
-
const newValues = Array.isArray(value) ? [...value] : {...value};
|
|
1419
|
+
const newValues = Array.isArray(value) ? [...value] : { ...value };
|
|
1438
1420
|
for (const [key, field] of this) {
|
|
1439
|
-
newValues[key] = field.#reset(newValues[key]);
|
|
1421
|
+
newValues[key] = field.#reset(Object.hasOwn(newValues, key) ? newValues[key] : undefined, false);
|
|
1440
1422
|
}
|
|
1441
1423
|
this.#value.set(newValues);
|
|
1442
1424
|
this.#initValue.set(newValues);
|
|
@@ -1449,59 +1431,48 @@
|
|
|
1449
1431
|
/**
|
|
1450
1432
|
*
|
|
1451
1433
|
* @param {T} value
|
|
1452
|
-
* @param {*} state
|
|
1453
1434
|
* @returns
|
|
1454
1435
|
*/
|
|
1455
|
-
#toUpdate(value
|
|
1456
|
-
|
|
1457
|
-
if(
|
|
1458
|
-
this.#value.
|
|
1459
|
-
this.#
|
|
1460
|
-
return this.#runUpdate(val, sta);
|
|
1436
|
+
#toUpdate(value) {
|
|
1437
|
+
let val = this.#convert?.(value) ?? value;
|
|
1438
|
+
if (val === undefined) { val = value; }
|
|
1439
|
+
if (Object.is(this.#value.get(), val)) { return val; }
|
|
1440
|
+
return this.#runUpdate(val);
|
|
1461
1441
|
}
|
|
1462
1442
|
/**
|
|
1463
1443
|
*
|
|
1464
1444
|
* @param {*} val
|
|
1465
|
-
* @param {*} sta
|
|
1466
1445
|
* @returns {[any, any]}
|
|
1467
1446
|
*/
|
|
1468
|
-
#runUpdate(val
|
|
1447
|
+
#runUpdate(val) {
|
|
1469
1448
|
this.#needUpdate = false;
|
|
1470
1449
|
let initValue = val;
|
|
1471
1450
|
if (val && typeof val === 'object') {
|
|
1472
1451
|
/** @type {T} */
|
|
1473
1452
|
// @ts-ignore
|
|
1474
|
-
let newValues = Array.isArray(val) ? [...val] : {...val};
|
|
1475
|
-
let newStates = Array.isArray(val) ? Array.isArray(sta) ? [...sta] : [] : {...sta};
|
|
1453
|
+
let newValues = Array.isArray(val) ? [...val] : { ...val };
|
|
1476
1454
|
let updated = false;
|
|
1477
1455
|
for (const [key, field] of this) {
|
|
1478
1456
|
// @ts-ignore
|
|
1479
|
-
const data = val[key];
|
|
1480
|
-
const
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
updated = true;
|
|
1486
|
-
}
|
|
1487
|
-
if (state !== newState) {
|
|
1488
|
-
newStates[key] = newState;
|
|
1489
|
-
updated = true;
|
|
1490
|
-
}
|
|
1457
|
+
const data = Object.hasOwn(val, key) ? val[key] : undefined;
|
|
1458
|
+
const newData = field.#toUpdate(data);
|
|
1459
|
+
if (Object.is(data, newData)) { continue; }
|
|
1460
|
+
// @ts-ignore
|
|
1461
|
+
newValues[key] = newData;
|
|
1462
|
+
updated = true;
|
|
1491
1463
|
}
|
|
1492
1464
|
if (updated) {
|
|
1493
1465
|
val = newValues;
|
|
1494
|
-
sta = newStates;
|
|
1495
1466
|
initValue = val;
|
|
1496
1467
|
this.#value.set(val);
|
|
1497
|
-
this.#state.set(newStates);
|
|
1498
1468
|
}
|
|
1499
1469
|
}
|
|
1500
1470
|
if (!this.#set) {
|
|
1501
1471
|
this.#set = true;
|
|
1502
1472
|
this.#initValue.set(initValue);
|
|
1503
1473
|
}
|
|
1504
|
-
|
|
1474
|
+
this.#value.set(val);
|
|
1475
|
+
return val;
|
|
1505
1476
|
}
|
|
1506
1477
|
/**
|
|
1507
1478
|
* 异步校验
|
|
@@ -1525,18 +1496,18 @@
|
|
|
1525
1496
|
return Promise.all([this.#validatorResult.get(), this.#changed(), this.#blurred()])
|
|
1526
1497
|
.then(v => {
|
|
1527
1498
|
const errors = v.flat();
|
|
1528
|
-
return errors.length ? errors : null
|
|
1499
|
+
return errors.length ? errors : null;
|
|
1529
1500
|
});
|
|
1530
1501
|
}
|
|
1531
1502
|
const selfPath = Array.isArray(path) ? path : [];
|
|
1532
|
-
const list = [this.validate().then(errors => {
|
|
1533
|
-
if (!errors?.length) {return [];}
|
|
1534
|
-
return [{path: [...selfPath], store: /** @type {Store} */(this), errors}]
|
|
1503
|
+
const list = [this.validate(true).then(errors => {
|
|
1504
|
+
if (!errors?.length) { return []; }
|
|
1505
|
+
return [{ path: [...selfPath], store: /** @type {Store} */(this), errors }];
|
|
1535
1506
|
})];
|
|
1536
1507
|
for (const [key, field] of this) {
|
|
1537
1508
|
list.push(field.validate([...selfPath, key]));
|
|
1538
1509
|
}
|
|
1539
|
-
return Promise.all(list).then(v => v.flat())
|
|
1510
|
+
return Promise.all(list).then(v => v.flat());
|
|
1540
1511
|
}
|
|
1541
1512
|
}
|
|
1542
1513
|
|
|
@@ -1550,8 +1521,8 @@
|
|
|
1550
1521
|
class ObjectStore extends Store {
|
|
1551
1522
|
get kind() { return 'object'; }
|
|
1552
1523
|
/** @type {Record<string, Store>} */
|
|
1553
|
-
#children
|
|
1554
|
-
*[Symbol.iterator]() {yield* Object.entries(this.#children);}
|
|
1524
|
+
#children;
|
|
1525
|
+
*[Symbol.iterator]() { yield* Object.entries(this.#children); }
|
|
1555
1526
|
/**
|
|
1556
1527
|
*
|
|
1557
1528
|
* @param {string | number} key
|
|
@@ -1565,26 +1536,25 @@
|
|
|
1565
1536
|
* @param {number | string | null} [options.index]
|
|
1566
1537
|
* @param {boolean} [options.new]
|
|
1567
1538
|
* @param {((value: T?, index: any, store: Store) => void)?} [options.onUpdate]
|
|
1568
|
-
* @param {((value: T?, index: any, store: Store) => void)?} [options.onUpdateState]
|
|
1569
1539
|
*/
|
|
1570
|
-
constructor(schema,{ parent, index, new: isNew, onUpdate
|
|
1540
|
+
constructor(schema, { parent, index, new: isNew, onUpdate } = {}) {
|
|
1571
1541
|
const childrenTypes = Object.entries(schema.type);
|
|
1572
1542
|
/** @type {Record<string, Store>} */
|
|
1573
1543
|
const children = Object.create(null);
|
|
1574
1544
|
super(schema, {
|
|
1575
|
-
parent, index, new: isNew, onUpdate,
|
|
1545
|
+
parent, index, new: isNew, onUpdate,
|
|
1576
1546
|
size: childrenTypes.length,
|
|
1577
1547
|
setValue(v) { return typeof v === 'object' ? v : null; },
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
return [
|
|
1581
|
-
typeof v === 'object' ? v : {},
|
|
1582
|
-
typeof state === 'object' ? state : {},
|
|
1583
|
-
]
|
|
1548
|
+
convert(v) {
|
|
1549
|
+
return typeof v === 'object' ? v : {};
|
|
1584
1550
|
},
|
|
1585
|
-
default: schema.default ?? (() =>
|
|
1586
|
-
Object.entries(children)
|
|
1587
|
-
|
|
1551
|
+
default: schema.default ?? ((store, value) => {
|
|
1552
|
+
const list = Object.entries(children);
|
|
1553
|
+
let obj = value;
|
|
1554
|
+
if (!obj || typeof obj !== 'object') { obj = schema.default; }
|
|
1555
|
+
if (!obj || typeof obj !== 'object') { obj = {}; }
|
|
1556
|
+
return Object.fromEntries(list.map(([k, v]) => [k, v.createDefault(Object.hasOwn(obj, k) ? obj[k] : null)]));
|
|
1557
|
+
}),
|
|
1588
1558
|
});
|
|
1589
1559
|
const childCommonOptions = {
|
|
1590
1560
|
parent: this,
|
|
@@ -1592,17 +1562,12 @@
|
|
|
1592
1562
|
onUpdate: (value, index, store) => {
|
|
1593
1563
|
if (store !== this.#children[index]) { return; }
|
|
1594
1564
|
// @ts-ignore
|
|
1595
|
-
this.value = {...this.value, [index]: value};
|
|
1565
|
+
this.value = { ...this.value, [index]: value };
|
|
1596
1566
|
},
|
|
1597
|
-
/** @param {*} state @param {*} index @param {Store} store */
|
|
1598
|
-
onUpdateState: (state, index, store) => {
|
|
1599
|
-
if (store !== this.#children[index]) { return; }
|
|
1600
|
-
this.state = {...this.state, [index]: state};
|
|
1601
|
-
}
|
|
1602
1567
|
};
|
|
1603
1568
|
|
|
1604
1569
|
for (const [index, field] of childrenTypes) {
|
|
1605
|
-
children[index] = create(field, {...childCommonOptions, index});
|
|
1570
|
+
children[index] = create(field, { ...childCommonOptions, index });
|
|
1606
1571
|
}
|
|
1607
1572
|
this.#children = children;
|
|
1608
1573
|
}
|
|
@@ -1647,9 +1612,8 @@
|
|
|
1647
1612
|
* @param {boolean} [options.new]
|
|
1648
1613
|
* @param {boolean} [options.addable]
|
|
1649
1614
|
* @param {(value: any, index: any, store: Store) => void} [options.onUpdate]
|
|
1650
|
-
* @param {(value: any, index: any, store: Store) => void} [options.onUpdateState]
|
|
1651
1615
|
*/
|
|
1652
|
-
constructor(schema, { parent, onUpdate,
|
|
1616
|
+
constructor(schema, { parent, onUpdate, index, new: isNew, addable } = {}) {
|
|
1653
1617
|
const childrenState = new exports.Signal.State(/** @type {Store[]} */([]));
|
|
1654
1618
|
// @ts-ignore
|
|
1655
1619
|
const updateChildren = (list) => {
|
|
@@ -1668,22 +1632,16 @@
|
|
|
1668
1632
|
super(schema, {
|
|
1669
1633
|
index, new: isNew, parent,
|
|
1670
1634
|
size: new exports.Signal.Computed(() => childrenState.get().length),
|
|
1671
|
-
state: [],
|
|
1672
1635
|
setValue(v) { return Array.isArray(v) ? v : v == null ? null : [v]; },
|
|
1673
|
-
|
|
1674
|
-
convert(v, state) {
|
|
1636
|
+
convert(v) {
|
|
1675
1637
|
const val = Array.isArray(v) ? v : v == null ? null : [v];
|
|
1676
1638
|
updateChildren(val);
|
|
1677
|
-
return
|
|
1678
|
-
val,
|
|
1679
|
-
(Array.isArray(state) ? state : v == null ? [] : [state]),
|
|
1680
|
-
];
|
|
1639
|
+
return val;
|
|
1681
1640
|
},
|
|
1682
|
-
onUpdate: (value, index,
|
|
1641
|
+
onUpdate: (value, index, store) => {
|
|
1683
1642
|
updateChildren(value);
|
|
1684
|
-
onUpdate?.(value, index,
|
|
1643
|
+
onUpdate?.(value, index, store);
|
|
1685
1644
|
},
|
|
1686
|
-
onUpdateState,
|
|
1687
1645
|
default: schema.default ?? [],
|
|
1688
1646
|
});
|
|
1689
1647
|
|
|
@@ -1702,16 +1660,6 @@
|
|
|
1702
1660
|
val[index] = value;
|
|
1703
1661
|
this.value = val;
|
|
1704
1662
|
},
|
|
1705
|
-
/** @param {*} state @param {*} index @param {Store} store */
|
|
1706
|
-
onUpdateState: (state, index, store) => {
|
|
1707
|
-
if (childrenState.get()[index] !== store) { return; }
|
|
1708
|
-
const sta = [...this.state || []];
|
|
1709
|
-
if (sta.length < index) {
|
|
1710
|
-
sta.length = index;
|
|
1711
|
-
}
|
|
1712
|
-
sta[index] = state;
|
|
1713
|
-
this.state = sta;
|
|
1714
|
-
},
|
|
1715
1663
|
};
|
|
1716
1664
|
this.#create = (index, isNew) => {
|
|
1717
1665
|
const child = create(schema, { ...childCommonOptions, index, new: isNew });
|
|
@@ -1753,13 +1701,7 @@
|
|
|
1753
1701
|
children[i].index = i;
|
|
1754
1702
|
}
|
|
1755
1703
|
const val = [...data];
|
|
1756
|
-
val.splice(insertIndex, 0,
|
|
1757
|
-
const state = this.state;
|
|
1758
|
-
if (Array.isArray(state)) {
|
|
1759
|
-
const sta = [...state];
|
|
1760
|
-
sta.splice(insertIndex, 0, {});
|
|
1761
|
-
this.state = sta;
|
|
1762
|
-
}
|
|
1704
|
+
val.splice(insertIndex, 0, item.createDefault(value));
|
|
1763
1705
|
this.#children.set(children);
|
|
1764
1706
|
this.value = val;
|
|
1765
1707
|
return true;
|
|
@@ -1789,12 +1731,6 @@
|
|
|
1789
1731
|
}
|
|
1790
1732
|
const val = [...data];
|
|
1791
1733
|
const [value] = val.splice(removeIndex, 1);
|
|
1792
|
-
const state = this.state;
|
|
1793
|
-
if (Array.isArray(state)) {
|
|
1794
|
-
const sta = [...this.state];
|
|
1795
|
-
sta.splice(removeIndex, 1);
|
|
1796
|
-
this.state = sta;
|
|
1797
|
-
}
|
|
1798
1734
|
this.#children.set(children);
|
|
1799
1735
|
this.value = val;
|
|
1800
1736
|
return value;
|
|
@@ -1834,16 +1770,6 @@
|
|
|
1834
1770
|
while (toIndex > val.length) { val.push(null); }
|
|
1835
1771
|
val.splice(toIndex, 0, ...values);
|
|
1836
1772
|
|
|
1837
|
-
const state = this.state;
|
|
1838
|
-
if (Array.isArray(state)) {
|
|
1839
|
-
const sta = [...state];
|
|
1840
|
-
const states = sta.splice(from, len);
|
|
1841
|
-
for (let i = states.length; i < len; i++) { states.push({}); }
|
|
1842
|
-
while (toIndex > sta.length) { sta.push({}); }
|
|
1843
|
-
sta.splice(toIndex, 0, ...states);
|
|
1844
|
-
|
|
1845
|
-
this.state = sta;
|
|
1846
|
-
}
|
|
1847
1773
|
this.#children.set(children);
|
|
1848
1774
|
this.value = val;
|
|
1849
1775
|
return len;
|
|
@@ -1871,15 +1797,6 @@
|
|
|
1871
1797
|
const bValue = val[b];
|
|
1872
1798
|
val[b] = aValue;
|
|
1873
1799
|
val[a] = bValue;
|
|
1874
|
-
const state = this.state;
|
|
1875
|
-
if (Array.isArray(state)) {
|
|
1876
|
-
const sta = [...state];
|
|
1877
|
-
const aValue = sta[a];
|
|
1878
|
-
const bValue = sta[b];
|
|
1879
|
-
sta[b] = aValue;
|
|
1880
|
-
sta[a] = bValue;
|
|
1881
|
-
this.state = sta;
|
|
1882
|
-
}
|
|
1883
1800
|
this.#children.set(children);
|
|
1884
1801
|
this.value = val;
|
|
1885
1802
|
return true;
|
|
@@ -3028,7 +2945,6 @@
|
|
|
3028
2945
|
kind: true,
|
|
3029
2946
|
|
|
3030
2947
|
value: true,
|
|
3031
|
-
state: true,
|
|
3032
2948
|
|
|
3033
2949
|
store: true,
|
|
3034
2950
|
parent: true,
|
|
@@ -3086,7 +3002,8 @@
|
|
|
3086
3002
|
yield [`${key}${sign}${k}`, {get: () => val[k]}];
|
|
3087
3003
|
}
|
|
3088
3004
|
yield [`${key}${sign}value`, {get: () => val.value, set: v => val.value = v}];
|
|
3089
|
-
|
|
3005
|
+
/** @deprecated */
|
|
3006
|
+
yield [`${key}${sign}state`, {get: () => null, set: v => {}}];
|
|
3090
3007
|
yield [`${key}${sign}reset`, {exec: () => val.reset()}];
|
|
3091
3008
|
// @ts-ignore
|
|
3092
3009
|
yield [`${key}${sign}validate`, {exec: v => val.validate(v ? [] : null)}];
|
|
@@ -3374,6 +3291,8 @@
|
|
|
3374
3291
|
const res = Object.fromEntries([...bindableSet].map(v => [
|
|
3375
3292
|
`$${v}`, cb => watch(() => store[v], cb, true)
|
|
3376
3293
|
]));
|
|
3294
|
+
/** @deprecated */
|
|
3295
|
+
res.$$state = cb => watch(() => null, cb, true);
|
|
3377
3296
|
return res;
|
|
3378
3297
|
}
|
|
3379
3298
|
/**
|
|
@@ -3390,13 +3309,15 @@
|
|
|
3390
3309
|
}
|
|
3391
3310
|
/** @type {Record<string, {get(): any; set?(v: any): void}> | void} */
|
|
3392
3311
|
const res = Object.fromEntries([...bindableSet].map(v => [
|
|
3393
|
-
`$${v}`, v === 'value'
|
|
3312
|
+
`$${v}`, v === 'value' ? {
|
|
3394
3313
|
get: () => store[v],
|
|
3395
3314
|
set: (s)=>{store[v] = s;}
|
|
3396
3315
|
} : {
|
|
3397
3316
|
get: () => store[v],
|
|
3398
3317
|
}
|
|
3399
3318
|
]));
|
|
3319
|
+
/** @deprecated */
|
|
3320
|
+
res.$$state = { get: () => null, set: () => {} };
|
|
3400
3321
|
return res;
|
|
3401
3322
|
}
|
|
3402
3323
|
/**
|
|
@@ -3411,7 +3332,8 @@
|
|
|
3411
3332
|
if (!store) { return; }
|
|
3412
3333
|
switch(type) {
|
|
3413
3334
|
case 'value': return v => {store.value = v; };
|
|
3414
|
-
|
|
3335
|
+
/** @deprecated */
|
|
3336
|
+
case 'state': return v => { };
|
|
3415
3337
|
}
|
|
3416
3338
|
}
|
|
3417
3339
|
/**
|
|
@@ -3429,7 +3351,8 @@
|
|
|
3429
3351
|
}
|
|
3430
3352
|
return {
|
|
3431
3353
|
'$value': v => {store.value = v; },
|
|
3432
|
-
|
|
3354
|
+
/** @deprecated */
|
|
3355
|
+
'$state': v => { },
|
|
3433
3356
|
'$input': v => {store.emit('input', v); },
|
|
3434
3357
|
'$change': v => {store.emit('change', v); },
|
|
3435
3358
|
'$click': v => {store.emit('click', v); },
|
|
@@ -5324,431 +5247,85 @@
|
|
|
5324
5247
|
/** @import { Store } from '../Store/index.mjs' */
|
|
5325
5248
|
/** @import { StoreLayout } from '../types.mjs' */
|
|
5326
5249
|
|
|
5250
|
+
|
|
5327
5251
|
/**
|
|
5328
5252
|
*
|
|
5329
|
-
* @param {
|
|
5330
|
-
* @param {StoreLayout.Renderer} fieldRenderer
|
|
5331
|
-
* @param {StoreLayout.Field?} layout
|
|
5332
|
-
* @param {object} option
|
|
5333
|
-
* @param {(string | StoreLayout.Action[])[]} option.columns
|
|
5334
|
-
* @param {() => void} option.remove
|
|
5335
|
-
* @param {() => void} option.dragenter
|
|
5336
|
-
* @param {() => void} option.dragstart
|
|
5337
|
-
* @param {() => void} option.dragend
|
|
5338
|
-
* @param {{get(): boolean}} option.deletable
|
|
5339
|
-
* @param {StoreLayout.Options?} options
|
|
5340
|
-
|
|
5341
|
-
* @returns {[HTMLTableSectionElement, () => void]}
|
|
5253
|
+
* @param {string} field
|
|
5342
5254
|
*/
|
|
5343
|
-
function
|
|
5344
|
-
columns,
|
|
5345
|
-
remove, dragenter, dragstart, dragend, deletable
|
|
5346
|
-
}, options) {
|
|
5347
|
-
const root = document.createElement('tbody');
|
|
5348
|
-
root.addEventListener('dragenter', () => {
|
|
5349
|
-
dragenter();
|
|
5350
|
-
});
|
|
5351
|
-
root.addEventListener('dragstart', (event) => {
|
|
5352
|
-
if (event.target !== event.currentTarget) { return; }
|
|
5353
|
-
dragstart();
|
|
5354
|
-
});
|
|
5355
|
-
root.addEventListener('dragend', dragend);
|
|
5356
|
-
const head = root.appendChild(document.createElement('tr'));
|
|
5357
|
-
|
|
5358
|
-
/** @type {(() => void)[]} */
|
|
5359
|
-
const destroyList = [];
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
let trigger = () => { };
|
|
5363
|
-
/** @type {HTMLButtonElement[]} */
|
|
5364
|
-
const triggerList = [];
|
|
5365
|
-
if (columns.find(v => Array.isArray(v) && v.includes('trigger'))) {
|
|
5366
|
-
const body = root.appendChild(document.createElement('tr'));
|
|
5367
|
-
const main = body.appendChild(document.createElement('td'));
|
|
5368
|
-
main.colSpan = columns.length;
|
|
5369
|
-
|
|
5370
|
-
const [form, destroy] = Form(store, fieldRenderer, layout, options);
|
|
5371
|
-
main.appendChild(form);
|
|
5372
|
-
destroyList.push(destroy);
|
|
5373
|
-
body.hidden = true;
|
|
5374
|
-
trigger = function click() {
|
|
5375
|
-
if (body.hidden) {
|
|
5376
|
-
body.hidden = false;
|
|
5377
|
-
for (const ext of triggerList) {
|
|
5378
|
-
ext.classList.remove('NeeloongForm-table-line-open');
|
|
5379
|
-
ext.classList.add('NeeloongForm-table-line-close');
|
|
5380
|
-
}
|
|
5381
|
-
} else {
|
|
5382
|
-
body.hidden = true;
|
|
5383
|
-
for (const ext of triggerList) {
|
|
5384
|
-
ext.classList.remove('NeeloongForm-table-line-close');
|
|
5385
|
-
ext.classList.add('NeeloongForm-table-line-open');
|
|
5386
|
-
}
|
|
5387
|
-
}
|
|
5388
|
-
};
|
|
5389
|
-
|
|
5390
|
-
}
|
|
5255
|
+
function createFieldFilter(field) {
|
|
5391
5256
|
|
|
5392
5257
|
/**
|
|
5393
5258
|
*
|
|
5394
|
-
* @param {
|
|
5259
|
+
* @param {StoreLayout.Item} v
|
|
5260
|
+
* @returns {v is StoreLayout.Field}
|
|
5395
5261
|
*/
|
|
5396
|
-
function pointerdown({ pointerId }) {
|
|
5397
|
-
root.draggable = true;
|
|
5398
|
-
/** @param {PointerEvent} event */
|
|
5399
|
-
function pointerup(event) {
|
|
5400
|
-
if (event.pointerId !== pointerId) { return; }
|
|
5401
|
-
if (!root) { return; }
|
|
5402
|
-
root.draggable = false;
|
|
5403
|
-
window.removeEventListener('pointerup', pointerup, { capture: true });
|
|
5404
|
-
window.removeEventListener('pointercancel', pointerup, { capture: true });
|
|
5405
|
-
}
|
|
5406
|
-
window.addEventListener('pointerup', pointerup, { capture: true });
|
|
5407
|
-
window.addEventListener('pointercancel', pointerup, { capture: true });
|
|
5408
|
-
|
|
5409
|
-
}
|
|
5410
5262
|
|
|
5411
|
-
|
|
5412
|
-
if (
|
|
5413
|
-
|
|
5414
|
-
const child = store.child(name);
|
|
5415
|
-
if (!child) { continue; }
|
|
5416
|
-
const [el, destroy] = FormField(child, fieldRenderer, null, options, true);
|
|
5417
|
-
destroyList.push(destroy);
|
|
5418
|
-
td.appendChild(el);
|
|
5419
|
-
continue;
|
|
5420
|
-
}
|
|
5421
|
-
const handle = head.appendChild(document.createElement('th'));
|
|
5422
|
-
handle.classList.add('NeeloongForm-table-line-handle');
|
|
5423
|
-
for (const k of name) {
|
|
5424
|
-
switch (k) {
|
|
5425
|
-
case 'trigger': {
|
|
5426
|
-
const ext = handle.appendChild(document.createElement('button'));
|
|
5427
|
-
ext.classList.add('NeeloongForm-table-line-open');
|
|
5428
|
-
triggerList.push(ext);
|
|
5429
|
-
ext.addEventListener('click', trigger);
|
|
5430
|
-
continue;
|
|
5431
|
-
}
|
|
5432
|
-
case 'move': {
|
|
5433
|
-
if (!options?.editable) { continue; }
|
|
5434
|
-
const move = handle.appendChild(document.createElement('button'));
|
|
5435
|
-
move.classList.add('NeeloongForm-table-move');
|
|
5436
|
-
move.addEventListener('pointerdown', pointerdown);
|
|
5437
|
-
destroyList.push(watch(() => store.readonly || store.disabled, disabled => {
|
|
5438
|
-
move.disabled = disabled;
|
|
5439
|
-
}, true));
|
|
5440
|
-
continue;
|
|
5441
|
-
}
|
|
5442
|
-
case 'remove': {
|
|
5443
|
-
if (!options?.editable) { continue; }
|
|
5444
|
-
const del = handle.appendChild(document.createElement('button'));
|
|
5445
|
-
del.classList.add('NeeloongForm-table-remove');
|
|
5446
|
-
del.addEventListener('click', remove);
|
|
5447
|
-
destroyList.push(watch(() => !deletable.get() || store.readonly || store.disabled, disabled => {
|
|
5448
|
-
del.disabled = disabled;
|
|
5449
|
-
}, true));
|
|
5450
|
-
continue;
|
|
5451
|
-
}
|
|
5452
|
-
case 'serial': {
|
|
5453
|
-
const serial = handle.appendChild(document.createElement('span'));
|
|
5454
|
-
serial.classList.add('NeeloongForm-table-serial');
|
|
5455
|
-
continue;
|
|
5456
|
-
}
|
|
5457
|
-
}
|
|
5458
|
-
}
|
|
5459
|
-
}
|
|
5263
|
+
return function (v) {
|
|
5264
|
+
if (v.type && v.type !== 'field') { return false; }
|
|
5265
|
+
return v.field === field;
|
|
5460
5266
|
|
|
5461
|
-
|
|
5462
|
-
for (const destroy of destroyList) {
|
|
5463
|
-
destroy();
|
|
5464
|
-
}
|
|
5465
|
-
}];
|
|
5267
|
+
};
|
|
5466
5268
|
}
|
|
5467
|
-
|
|
5468
|
-
/** @import { Store, ArrayStore } from '../Store/index.mjs' */
|
|
5469
|
-
/** @import { StoreLayout } from '../types.mjs' */
|
|
5470
|
-
|
|
5471
5269
|
/**
|
|
5472
5270
|
*
|
|
5473
|
-
* @param {
|
|
5474
|
-
* @param {
|
|
5475
|
-
* @param {
|
|
5476
|
-
* @param {
|
|
5477
|
-
* @param {
|
|
5271
|
+
* @param {StoreLayout.Renderer} fieldRenderer
|
|
5272
|
+
* @param {Store} store
|
|
5273
|
+
* @param {Node} node
|
|
5274
|
+
* @param {StoreLayout.Options?} options
|
|
5275
|
+
* @param {StoreLayout?} [layout]
|
|
5276
|
+
* @param {Node} [anchor]
|
|
5277
|
+
* @param {(child?: Store<any, any> | undefined) => void} [dragenter]
|
|
5478
5278
|
*/
|
|
5479
|
-
function
|
|
5480
|
-
const tr = parent.appendChild(document.createElement('tr'));
|
|
5279
|
+
function renderHtml(store, fieldRenderer, node, options, layout, anchor, dragenter) {
|
|
5481
5280
|
/** @type {(() => void)[]} */
|
|
5482
5281
|
const destroyList = [];
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5282
|
+
if (node instanceof Element) {
|
|
5283
|
+
const tagName = node.tagName.toLowerCase();
|
|
5284
|
+
if (!node.parentNode) { return () => { }; }
|
|
5285
|
+
if (tagName === 'nl-form-field') {
|
|
5286
|
+
const field = node.getAttribute('name') || '';
|
|
5287
|
+
const mode = node.getAttribute('mode') || '';
|
|
5288
|
+
const fieldStore = field ? store.child(field) : store;
|
|
5289
|
+
const fieldLayout = field ? layout?.fields?.find(createFieldFilter(field)) || null : null;
|
|
5290
|
+
if (!fieldStore) { return () => { }; }
|
|
5291
|
+
switch (mode) {
|
|
5292
|
+
case 'grid': {
|
|
5293
|
+
const [el, destroy] = Form(store, fieldRenderer, fieldLayout, options);
|
|
5294
|
+
node.replaceWith(el);
|
|
5295
|
+
return destroy;
|
|
5296
|
+
}
|
|
5297
|
+
}
|
|
5298
|
+
const component = fieldStore.component;
|
|
5299
|
+
if (component) {
|
|
5300
|
+
const res = fieldRenderer(fieldStore, component, options);
|
|
5301
|
+
if (res) {
|
|
5302
|
+
const [el, destroy] = res;
|
|
5303
|
+
node.replaceWith(el);
|
|
5304
|
+
return destroy;
|
|
5489
5305
|
}
|
|
5490
|
-
td.innerText = label;
|
|
5491
|
-
continue;
|
|
5492
|
-
}
|
|
5493
|
-
const th = tr.appendChild(document.createElement('th'));
|
|
5494
|
-
if (!editable) { continue; }
|
|
5495
|
-
for (const it of col) {
|
|
5496
|
-
switch (it) {
|
|
5497
|
-
case 'add':
|
|
5498
|
-
const button = th.appendChild(document.createElement('button'));
|
|
5499
|
-
button.addEventListener('click', add);
|
|
5500
|
-
button.classList.add('NeeloongForm-table-add');
|
|
5501
|
-
destroyList.push(watch(() => !addable.get(), disabled => { button.disabled = disabled; }, true));
|
|
5502
|
-
continue;
|
|
5503
5306
|
}
|
|
5307
|
+
const value = node.getAttribute('placeholder') || '';
|
|
5308
|
+
node.replaceWith(document.createTextNode(value));
|
|
5309
|
+
return () => { };
|
|
5504
5310
|
}
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5311
|
+
if (tagName === 'nl-form-button') {
|
|
5312
|
+
const button = document.createElement('button');
|
|
5313
|
+
button.className = 'NeeloongForm-item-button';
|
|
5314
|
+
const click = node.getAttribute('click') || '';
|
|
5315
|
+
const call = options?.call;
|
|
5316
|
+
if (click && typeof call === 'function') {
|
|
5317
|
+
button.addEventListener('click', e => call(click, e, store, options));
|
|
5318
|
+
}
|
|
5319
|
+
for (const n of [...node.childNodes]) {
|
|
5320
|
+
button.appendChild(n);
|
|
5321
|
+
}
|
|
5322
|
+
node.replaceWith(button);
|
|
5323
|
+
return () => { };
|
|
5509
5324
|
}
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
* @param {ArrayStore} store
|
|
5515
|
-
* @param {StoreLayout.Renderer} fieldRenderer
|
|
5516
|
-
* @param {StoreLayout.Field?} layout
|
|
5517
|
-
* @param {StoreLayout.Options?} options
|
|
5518
|
-
* @returns {[HTMLTableElement, () => void]}
|
|
5519
|
-
*/
|
|
5520
|
-
function Table(store, fieldRenderer, layout, options) {
|
|
5521
|
-
const headerColumns = layout?.columns;
|
|
5522
|
-
const fieldList = Object.entries(store.type || {})
|
|
5523
|
-
.filter(([k, v]) => typeof v?.type !== 'object')
|
|
5524
|
-
.map(([field, {width, label}]) => ({field, width, label}));
|
|
5525
|
-
/** @type {({ field: string; width: any; label: any; } | StoreLayout.Action[])[]} */
|
|
5526
|
-
let columns = [];
|
|
5527
|
-
if (Array.isArray(headerColumns)) {
|
|
5528
|
-
const map = new Map(fieldList.map(v => [v.field, v]));
|
|
5529
|
-
columns = headerColumns.map(v => {
|
|
5530
|
-
if (typeof v === 'number') { return [] }
|
|
5531
|
-
if (typeof v === 'string') { return map.get(v) || [] }
|
|
5532
|
-
if (!Array.isArray(v)) { return []; }
|
|
5533
|
-
/** @type {Set<StoreLayout.Action>} */
|
|
5534
|
-
const options = new Set(['add', 'move', 'trigger', 'remove', 'serial']);
|
|
5535
|
-
return v.filter(v => options.delete(v));
|
|
5536
|
-
}).filter(v => !Array.isArray(v) || v.length);
|
|
5537
|
-
}
|
|
5538
|
-
if (!columns.length) {
|
|
5539
|
-
columns = [['add', 'trigger', 'move', 'remove', 'serial']];
|
|
5540
|
-
}
|
|
5541
|
-
if (!columns.find(v => !Array.isArray(v))) {
|
|
5542
|
-
columns.push(...fieldList.slice(0, 3));
|
|
5543
|
-
}
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
const table = document.createElement('table');
|
|
5548
|
-
table.className = 'NeeloongForm-table';
|
|
5549
|
-
const thead = table.appendChild(document.createElement('thead'));
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
const addable = new exports.Signal.Computed(() => store.addable);
|
|
5554
|
-
const deletable = { get: () => Boolean(options?.editable) };
|
|
5555
|
-
function add() {
|
|
5556
|
-
const data = {};
|
|
5557
|
-
store.add(data);
|
|
5558
|
-
|
|
5559
|
-
}
|
|
5560
|
-
/**
|
|
5561
|
-
*
|
|
5562
|
-
* @param {Store} child
|
|
5563
|
-
*/
|
|
5564
|
-
function remove(child) {
|
|
5565
|
-
store.remove(Number(child.index));
|
|
5566
|
-
}
|
|
5567
|
-
let dragRow = -1;
|
|
5568
|
-
/**
|
|
5569
|
-
*
|
|
5570
|
-
* @param {Store} [child]
|
|
5571
|
-
*/
|
|
5572
|
-
function dragenter(child) {
|
|
5573
|
-
if (dragRow < 0) { return; }
|
|
5574
|
-
const index = child ? Number(child.index) : store.children.length;
|
|
5575
|
-
if (index < 0 || dragRow < 0 || dragRow === index) { return; }
|
|
5576
|
-
if (store.move(dragRow, index)) {
|
|
5577
|
-
dragRow = index;
|
|
5578
|
-
}
|
|
5579
|
-
}
|
|
5580
|
-
/**
|
|
5581
|
-
*
|
|
5582
|
-
* @param {Store} child
|
|
5583
|
-
*/
|
|
5584
|
-
function dragstart(child) {
|
|
5585
|
-
dragRow = Number(child.index);
|
|
5586
|
-
|
|
5587
|
-
}
|
|
5588
|
-
function dragend() {
|
|
5589
|
-
dragRow = -1;
|
|
5590
|
-
|
|
5591
|
-
}
|
|
5592
|
-
/** @type {(() => void)[]} */
|
|
5593
|
-
const destroyList = [];
|
|
5594
|
-
destroyList.push(renderHead(thead, columns, add, addable, Boolean(options?.editable)));
|
|
5595
|
-
switch (layout?.tableFoot) {
|
|
5596
|
-
default:
|
|
5597
|
-
case 'header': {
|
|
5598
|
-
const tfoot = table.appendChild(document.createElement('tfoot'));
|
|
5599
|
-
tfoot.addEventListener('dragenter', () => { dragenter(); });
|
|
5600
|
-
destroyList.push(renderHead(tfoot, columns, add, addable, Boolean(options?.editable)));
|
|
5601
|
-
break;
|
|
5602
|
-
}
|
|
5603
|
-
case 'add': {
|
|
5604
|
-
const tfoot = table.appendChild(document.createElement('tfoot'));
|
|
5605
|
-
tfoot.addEventListener('dragenter', () => { dragenter(); });
|
|
5606
|
-
const tr = tfoot.appendChild(document.createElement('tr'));
|
|
5607
|
-
const th = tr.appendChild(document.createElement('th'));
|
|
5608
|
-
th.colSpan = columns.length;
|
|
5609
|
-
const button = th.appendChild(document.createElement('button'));
|
|
5610
|
-
button.addEventListener('click', add);
|
|
5611
|
-
button.classList.add('NeeloongForm-table-foot-add');
|
|
5612
|
-
destroyList.push(watch(() => !addable.get(), disabled => { button.disabled = disabled; }, true));
|
|
5613
|
-
break;
|
|
5614
|
-
}
|
|
5615
|
-
case 'none':
|
|
5616
|
-
}
|
|
5617
|
-
const start = thead;
|
|
5618
|
-
/** @type {Map<Store, [HTMLTableSectionElement, () => void]>} */
|
|
5619
|
-
let seMap = new Map();
|
|
5620
|
-
/** @param {Map<Store, [tbody: HTMLTableSectionElement, destroy: () => void]>} map */
|
|
5621
|
-
function destroyMap(map) {
|
|
5622
|
-
for (const [el, destroy] of map.values()) {
|
|
5623
|
-
destroy();
|
|
5624
|
-
el.remove();
|
|
5625
|
-
}
|
|
5626
|
-
|
|
5627
|
-
}
|
|
5628
|
-
const columnNames = columns.map((v) => Array.isArray(v) ? v : v.field);
|
|
5629
|
-
const childrenResult = watch(() => store.children, function render(children) {
|
|
5630
|
-
let nextNode = thead.nextSibling;
|
|
5631
|
-
const oldSeMap = seMap;
|
|
5632
|
-
seMap = new Map();
|
|
5633
|
-
for (let child of children) {
|
|
5634
|
-
const old = oldSeMap.get(child);
|
|
5635
|
-
if (!old) {
|
|
5636
|
-
const [el, destroy] = Line(child, fieldRenderer, layout, {
|
|
5637
|
-
columns: columnNames,
|
|
5638
|
-
remove: remove.bind(null, child),
|
|
5639
|
-
dragenter: dragenter.bind(null, child),
|
|
5640
|
-
dragstart: dragstart.bind(null, child),
|
|
5641
|
-
dragend,
|
|
5642
|
-
deletable,
|
|
5643
|
-
}, options);
|
|
5644
|
-
table.insertBefore(el, nextNode);
|
|
5645
|
-
seMap.set(child, [el, destroy]);
|
|
5646
|
-
continue;
|
|
5647
|
-
}
|
|
5648
|
-
oldSeMap.delete(child);
|
|
5649
|
-
seMap.set(child, old);
|
|
5650
|
-
if (nextNode === old[0]) {
|
|
5651
|
-
nextNode = nextNode.nextSibling;
|
|
5652
|
-
continue;
|
|
5653
|
-
}
|
|
5654
|
-
table.insertBefore(old[0], nextNode);
|
|
5655
|
-
}
|
|
5656
|
-
destroyMap(oldSeMap);
|
|
5657
|
-
}, true);
|
|
5658
|
-
|
|
5659
|
-
return [table, () => {
|
|
5660
|
-
start.remove();
|
|
5661
|
-
thead.remove();
|
|
5662
|
-
destroyMap(seMap);
|
|
5663
|
-
childrenResult();
|
|
5664
|
-
for (const destroy of destroyList) {
|
|
5665
|
-
destroy();
|
|
5666
|
-
}
|
|
5667
|
-
}];
|
|
5668
|
-
}
|
|
5669
|
-
|
|
5670
|
-
/** @import { Store } from '../Store/index.mjs' */
|
|
5671
|
-
/** @import { StoreLayout } from '../types.mjs' */
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
/**
|
|
5675
|
-
*
|
|
5676
|
-
* @param {string} field
|
|
5677
|
-
*/
|
|
5678
|
-
function createFieldFilter(field) {
|
|
5679
|
-
|
|
5680
|
-
/**
|
|
5681
|
-
*
|
|
5682
|
-
* @param {StoreLayout.Item} v
|
|
5683
|
-
* @returns {v is StoreLayout.Field}
|
|
5684
|
-
*/
|
|
5685
|
-
|
|
5686
|
-
return function (v) {
|
|
5687
|
-
if (v.type && v.type !== 'field') { return false; }
|
|
5688
|
-
return v.field === field;
|
|
5689
|
-
|
|
5690
|
-
};
|
|
5691
|
-
}
|
|
5692
|
-
/**
|
|
5693
|
-
*
|
|
5694
|
-
* @param {StoreLayout.Renderer} fieldRenderer
|
|
5695
|
-
* @param {Store} store
|
|
5696
|
-
* @param {Node} node
|
|
5697
|
-
* @param {StoreLayout.Options?} options
|
|
5698
|
-
* @param {StoreLayout?} [layout]
|
|
5699
|
-
* @param {Node} [anchor]
|
|
5700
|
-
* @param {(child?: Store<any, any> | undefined) => void} [dragenter]
|
|
5701
|
-
*/
|
|
5702
|
-
function renderHtml(store, fieldRenderer, node, options, layout, anchor, dragenter) {
|
|
5703
|
-
/** @type {(() => void)[]} */
|
|
5704
|
-
const destroyList = [];
|
|
5705
|
-
if (node instanceof Element) {
|
|
5706
|
-
const tagName = node.tagName.toLowerCase();
|
|
5707
|
-
if (!node.parentNode) { return () => { }; }
|
|
5708
|
-
if (tagName === 'nl-form-field') {
|
|
5709
|
-
const field = node.getAttribute('name') || '';
|
|
5710
|
-
const mode = node.getAttribute('mode') || '';
|
|
5711
|
-
const fieldStore = field ? store.child(field) : store;
|
|
5712
|
-
const fieldLayout = field ? layout?.fields?.find(createFieldFilter(field)) || null : null;
|
|
5713
|
-
if (!fieldStore) { return () => { }; }
|
|
5714
|
-
switch (mode) {
|
|
5715
|
-
case 'grid': {
|
|
5716
|
-
const [el, destroy] = Form(store, fieldRenderer, fieldLayout, options);
|
|
5717
|
-
node.replaceWith(el);
|
|
5718
|
-
return destroy;
|
|
5719
|
-
}
|
|
5720
|
-
}
|
|
5721
|
-
const component = fieldStore.component;
|
|
5722
|
-
if (component) {
|
|
5723
|
-
const res = fieldRenderer(fieldStore, component, options);
|
|
5724
|
-
if (res) {
|
|
5725
|
-
const [el, destroy] = res;
|
|
5726
|
-
node.replaceWith(el);
|
|
5727
|
-
return destroy;
|
|
5728
|
-
}
|
|
5729
|
-
}
|
|
5730
|
-
const value = node.getAttribute('placeholder') || '';
|
|
5731
|
-
node.replaceWith(document.createTextNode(value));
|
|
5732
|
-
return () => { };
|
|
5733
|
-
}
|
|
5734
|
-
if (tagName === 'nl-form-button') {
|
|
5735
|
-
const button = document.createElement('button');
|
|
5736
|
-
button.className = 'NeeloongForm-item-button';
|
|
5737
|
-
const click = node.getAttribute('click') || '';
|
|
5738
|
-
const call = options?.call;
|
|
5739
|
-
if (click && typeof call === 'function') {
|
|
5740
|
-
button.addEventListener('click', e => call(click, e, store, options));
|
|
5741
|
-
}
|
|
5742
|
-
for (const n of [...node.childNodes]) {
|
|
5743
|
-
button.appendChild(n);
|
|
5744
|
-
}
|
|
5745
|
-
node.replaceWith(button);
|
|
5746
|
-
return () => { };
|
|
5747
|
-
}
|
|
5748
|
-
const name = node.getAttribute('nl-form-field');
|
|
5749
|
-
if (name) {
|
|
5750
|
-
const array = name.endsWith('[]');
|
|
5751
|
-
const field = array ? name.slice(0, name.length - 2) : name;
|
|
5325
|
+
const name = node.getAttribute('nl-form-field');
|
|
5326
|
+
if (name) {
|
|
5327
|
+
const array = name.endsWith('[]');
|
|
5328
|
+
const field = array ? name.slice(0, name.length - 2) : name;
|
|
5752
5329
|
|
|
5753
5330
|
const fieldStore = field ? store.child(field) : store;
|
|
5754
5331
|
if (!fieldStore) {
|
|
@@ -5884,34 +5461,406 @@
|
|
|
5884
5461
|
}
|
|
5885
5462
|
}
|
|
5886
5463
|
}
|
|
5887
|
-
if (node instanceof Element || node instanceof DocumentFragment) {
|
|
5888
|
-
for (const n of [...node.children]) {
|
|
5889
|
-
destroyList.push(renderHtml(store, fieldRenderer, n, options, layout, anchor));
|
|
5464
|
+
if (node instanceof Element || node instanceof DocumentFragment) {
|
|
5465
|
+
for (const n of [...node.children]) {
|
|
5466
|
+
destroyList.push(renderHtml(store, fieldRenderer, n, options, layout, anchor));
|
|
5467
|
+
}
|
|
5468
|
+
}
|
|
5469
|
+
return () => {
|
|
5470
|
+
for (const destroy of destroyList) {
|
|
5471
|
+
destroy();
|
|
5472
|
+
}
|
|
5473
|
+
};
|
|
5474
|
+
|
|
5475
|
+
}
|
|
5476
|
+
|
|
5477
|
+
/**
|
|
5478
|
+
*
|
|
5479
|
+
* @param {string | ParentNode | null} [html]
|
|
5480
|
+
* @returns {ParentNode}
|
|
5481
|
+
*/
|
|
5482
|
+
function getHtmlContent(html) {
|
|
5483
|
+
if (!html) {
|
|
5484
|
+
return document.createElement('template').content;
|
|
5485
|
+
}
|
|
5486
|
+
if (typeof html !== 'string') {
|
|
5487
|
+
return /** @type {ParentNode} */(html.cloneNode(true));
|
|
5488
|
+
}
|
|
5489
|
+
const template = document.createElement('template');
|
|
5490
|
+
template.innerHTML = html;
|
|
5491
|
+
return template.content;
|
|
5492
|
+
}
|
|
5493
|
+
|
|
5494
|
+
/**
|
|
5495
|
+
*
|
|
5496
|
+
* @param {Store<any, any>} store
|
|
5497
|
+
* @param {StoreLayout.Renderer} fieldRenderer
|
|
5498
|
+
* @param {StoreLayout.Field?} layout
|
|
5499
|
+
* @param {StoreLayout.Options?} options
|
|
5500
|
+
* @returns {[ParentNode, () => void]}
|
|
5501
|
+
*/
|
|
5502
|
+
function FormFieldInline(store, fieldRenderer, layout, options) {
|
|
5503
|
+
const { component } = store;
|
|
5504
|
+
return component
|
|
5505
|
+
&& fieldRenderer(store, component, options)
|
|
5506
|
+
|| [document.createElement('div'), () => { }];
|
|
5507
|
+
}
|
|
5508
|
+
|
|
5509
|
+
/** @import { Store } from '../Store/index.mjs' */
|
|
5510
|
+
/** @import { StoreLayout } from '../types.mjs' */
|
|
5511
|
+
|
|
5512
|
+
/**
|
|
5513
|
+
*
|
|
5514
|
+
* @param {Store<any, any>} store
|
|
5515
|
+
* @param {StoreLayout.Renderer} fieldRenderer
|
|
5516
|
+
* @param {StoreLayout.Field?} layout
|
|
5517
|
+
* @param {object} option
|
|
5518
|
+
* @param {StoreLayout.Column[]} option.columns
|
|
5519
|
+
* @param {() => void} option.remove
|
|
5520
|
+
* @param {() => void} option.dragenter
|
|
5521
|
+
* @param {() => void} option.dragstart
|
|
5522
|
+
* @param {() => void} option.dragend
|
|
5523
|
+
* @param {{get(): boolean}} option.deletable
|
|
5524
|
+
* @param {StoreLayout.Options?} options
|
|
5525
|
+
|
|
5526
|
+
* @returns {[HTMLTableSectionElement, () => void]}
|
|
5527
|
+
*/
|
|
5528
|
+
function Line(store, fieldRenderer, layout, {
|
|
5529
|
+
columns,
|
|
5530
|
+
remove, dragenter, dragstart, dragend, deletable
|
|
5531
|
+
}, options) {
|
|
5532
|
+
const root = document.createElement('tbody');
|
|
5533
|
+
root.addEventListener('dragenter', () => {
|
|
5534
|
+
dragenter();
|
|
5535
|
+
});
|
|
5536
|
+
root.addEventListener('dragstart', (event) => {
|
|
5537
|
+
if (event.target !== event.currentTarget) { return; }
|
|
5538
|
+
dragstart();
|
|
5539
|
+
});
|
|
5540
|
+
root.addEventListener('dragend', dragend);
|
|
5541
|
+
const head = root.appendChild(document.createElement('tr'));
|
|
5542
|
+
|
|
5543
|
+
/** @type {(() => void)[]} */
|
|
5544
|
+
const destroyList = [];
|
|
5545
|
+
|
|
5546
|
+
|
|
5547
|
+
let trigger = () => { };
|
|
5548
|
+
/** @type {HTMLButtonElement[]} */
|
|
5549
|
+
const triggerList = [];
|
|
5550
|
+
if (columns.find(v => v.actions?.includes('trigger'))) {
|
|
5551
|
+
const body = root.appendChild(document.createElement('tr'));
|
|
5552
|
+
const main = body.appendChild(document.createElement('td'));
|
|
5553
|
+
main.colSpan = columns.length;
|
|
5554
|
+
|
|
5555
|
+
const [form, destroy] = Form(store, fieldRenderer, layout, options);
|
|
5556
|
+
main.appendChild(form);
|
|
5557
|
+
destroyList.push(destroy);
|
|
5558
|
+
body.hidden = true;
|
|
5559
|
+
trigger = function click() {
|
|
5560
|
+
if (body.hidden) {
|
|
5561
|
+
body.hidden = false;
|
|
5562
|
+
for (const ext of triggerList) {
|
|
5563
|
+
ext.classList.remove('NeeloongForm-table-line-open');
|
|
5564
|
+
ext.classList.add('NeeloongForm-table-line-close');
|
|
5565
|
+
}
|
|
5566
|
+
} else {
|
|
5567
|
+
body.hidden = true;
|
|
5568
|
+
for (const ext of triggerList) {
|
|
5569
|
+
ext.classList.remove('NeeloongForm-table-line-close');
|
|
5570
|
+
ext.classList.add('NeeloongForm-table-line-open');
|
|
5571
|
+
}
|
|
5572
|
+
}
|
|
5573
|
+
};
|
|
5574
|
+
|
|
5575
|
+
}
|
|
5576
|
+
|
|
5577
|
+
/**
|
|
5578
|
+
*
|
|
5579
|
+
* @param {PointerEvent} event
|
|
5580
|
+
*/
|
|
5581
|
+
function pointerdown({ pointerId }) {
|
|
5582
|
+
root.draggable = true;
|
|
5583
|
+
/** @param {PointerEvent} event */
|
|
5584
|
+
function pointerup(event) {
|
|
5585
|
+
if (event.pointerId !== pointerId) { return; }
|
|
5586
|
+
if (!root) { return; }
|
|
5587
|
+
root.draggable = false;
|
|
5588
|
+
window.removeEventListener('pointerup', pointerup, { capture: true });
|
|
5589
|
+
window.removeEventListener('pointercancel', pointerup, { capture: true });
|
|
5590
|
+
}
|
|
5591
|
+
window.addEventListener('pointerup', pointerup, { capture: true });
|
|
5592
|
+
window.addEventListener('pointercancel', pointerup, { capture: true });
|
|
5593
|
+
|
|
5594
|
+
}
|
|
5595
|
+
|
|
5596
|
+
for (const name of columns) {
|
|
5597
|
+
const { actions, field, pattern } = name;
|
|
5598
|
+
if (!actions?.length) {
|
|
5599
|
+
const td = head.appendChild(document.createElement('td'));
|
|
5600
|
+
const child = field && store.child(field);
|
|
5601
|
+
if (child) {
|
|
5602
|
+
const [el, destroy] = FormFieldInline(child, fieldRenderer, null, options);
|
|
5603
|
+
destroyList.push(destroy);
|
|
5604
|
+
td.appendChild(el);
|
|
5605
|
+
}
|
|
5606
|
+
continue;
|
|
5607
|
+
}
|
|
5608
|
+
const handle = head.appendChild(document.createElement('th'));
|
|
5609
|
+
handle.classList.add('NeeloongForm-table-line-handle');
|
|
5610
|
+
for (const k of actions) {
|
|
5611
|
+
switch (k) {
|
|
5612
|
+
case 'trigger': {
|
|
5613
|
+
const ext = handle.appendChild(document.createElement('button'));
|
|
5614
|
+
ext.classList.add('NeeloongForm-table-line-open');
|
|
5615
|
+
triggerList.push(ext);
|
|
5616
|
+
ext.addEventListener('click', trigger);
|
|
5617
|
+
continue;
|
|
5618
|
+
}
|
|
5619
|
+
case 'move': {
|
|
5620
|
+
if (!options?.editable) { continue; }
|
|
5621
|
+
const move = handle.appendChild(document.createElement('button'));
|
|
5622
|
+
move.classList.add('NeeloongForm-table-move');
|
|
5623
|
+
move.addEventListener('pointerdown', pointerdown);
|
|
5624
|
+
destroyList.push(watch(() => store.readonly || store.disabled, disabled => {
|
|
5625
|
+
move.disabled = disabled;
|
|
5626
|
+
}, true));
|
|
5627
|
+
continue;
|
|
5628
|
+
}
|
|
5629
|
+
case 'remove': {
|
|
5630
|
+
if (!options?.editable) { continue; }
|
|
5631
|
+
const del = handle.appendChild(document.createElement('button'));
|
|
5632
|
+
del.classList.add('NeeloongForm-table-remove');
|
|
5633
|
+
del.addEventListener('click', remove);
|
|
5634
|
+
destroyList.push(watch(() => !deletable.get() || store.readonly || store.disabled, disabled => {
|
|
5635
|
+
del.disabled = disabled;
|
|
5636
|
+
}, true));
|
|
5637
|
+
continue;
|
|
5638
|
+
}
|
|
5639
|
+
case 'serial': {
|
|
5640
|
+
const serial = handle.appendChild(document.createElement('span'));
|
|
5641
|
+
serial.classList.add('NeeloongForm-table-serial');
|
|
5642
|
+
continue;
|
|
5643
|
+
}
|
|
5644
|
+
}
|
|
5645
|
+
}
|
|
5646
|
+
}
|
|
5647
|
+
|
|
5648
|
+
return [root, () => {
|
|
5649
|
+
for (const destroy of destroyList) {
|
|
5650
|
+
destroy();
|
|
5651
|
+
}
|
|
5652
|
+
}];
|
|
5653
|
+
}
|
|
5654
|
+
|
|
5655
|
+
/** @import { Store, ArrayStore } from '../Store/index.mjs' */
|
|
5656
|
+
/** @import { StoreLayout } from '../types.mjs' */
|
|
5657
|
+
|
|
5658
|
+
/**
|
|
5659
|
+
*
|
|
5660
|
+
* @param {HTMLElement} parent
|
|
5661
|
+
* @param {StoreLayout.Column[]} columns
|
|
5662
|
+
* @param {() => any} add
|
|
5663
|
+
* @param {{get(): boolean}} addable
|
|
5664
|
+
* @param {boolean?} [editable]
|
|
5665
|
+
*/
|
|
5666
|
+
function renderHead(parent, columns, add, addable, editable) {
|
|
5667
|
+
const tr = parent.appendChild(document.createElement('tr'));
|
|
5668
|
+
/** @type {(() => void)[]} */
|
|
5669
|
+
const destroyList = [];
|
|
5670
|
+
for (const { action, actions, width, label } of columns) {
|
|
5671
|
+
const th = tr.appendChild(document.createElement('th'));
|
|
5672
|
+
if (width) { th.setAttribute('width', `${width}`); }
|
|
5673
|
+
if (![action, actions].flat().includes('add')) {
|
|
5674
|
+
th.innerText = label || '';
|
|
5675
|
+
continue;
|
|
5890
5676
|
}
|
|
5677
|
+
if (!editable) { continue; }
|
|
5678
|
+
const button = th.appendChild(document.createElement('button'));
|
|
5679
|
+
button.addEventListener('click', add);
|
|
5680
|
+
button.classList.add('NeeloongForm-table-add');
|
|
5681
|
+
destroyList.push(watch(() => !addable.get(), disabled => { button.disabled = disabled; }, true));
|
|
5891
5682
|
}
|
|
5892
5683
|
return () => {
|
|
5893
5684
|
for (const destroy of destroyList) {
|
|
5894
5685
|
destroy();
|
|
5895
5686
|
}
|
|
5896
5687
|
};
|
|
5897
|
-
|
|
5898
5688
|
}
|
|
5899
|
-
|
|
5900
5689
|
/**
|
|
5901
|
-
*
|
|
5902
|
-
* @param {
|
|
5903
|
-
* @
|
|
5690
|
+
*
|
|
5691
|
+
* @param {ArrayStore} store
|
|
5692
|
+
* @param {StoreLayout.Renderer} fieldRenderer
|
|
5693
|
+
* @param {StoreLayout.Field?} layout
|
|
5694
|
+
* @param {StoreLayout.Options?} options
|
|
5695
|
+
* @returns {[HTMLTableElement, () => void]}
|
|
5904
5696
|
*/
|
|
5905
|
-
function
|
|
5906
|
-
|
|
5907
|
-
|
|
5697
|
+
function Table(store, fieldRenderer, layout, options) {
|
|
5698
|
+
const headerColumns = layout?.columns;
|
|
5699
|
+
const fieldList = Object.entries(store.type || {})
|
|
5700
|
+
.filter(([k, v]) => typeof v?.type !== 'object')
|
|
5701
|
+
.map(([field, { width, label }]) => ({ field, width, label }));
|
|
5702
|
+
/** @type {StoreLayout.Column[]} */
|
|
5703
|
+
let columns = [];
|
|
5704
|
+
if (Array.isArray(headerColumns)) {
|
|
5705
|
+
const map = new Map(fieldList.map(v => [v.field, v]));
|
|
5706
|
+
|
|
5707
|
+
/** @type {(StoreLayout.Column | null)[]} */
|
|
5708
|
+
const allColumns = headerColumns.map(v => {
|
|
5709
|
+
if (!v) { return null; }
|
|
5710
|
+
if (typeof v === 'number') { return { placeholder: v }; }
|
|
5711
|
+
if (typeof v === 'string') { return map.get(v) || null; }
|
|
5712
|
+
if (typeof v !== 'object') { return null; }
|
|
5713
|
+
if (Array.isArray(v)) {
|
|
5714
|
+
/** @type {Set<StoreLayout.Action>} */
|
|
5715
|
+
const options = new Set(['add', 'move', 'trigger', 'remove', 'serial', 'open', 'collapse']);
|
|
5716
|
+
const actions = v.filter(v => options.delete(v));
|
|
5717
|
+
if (!actions) { return null; }
|
|
5718
|
+
return { actions };
|
|
5719
|
+
}
|
|
5720
|
+
const { action, actions, field, placeholder, pattern, width, label } = v;
|
|
5721
|
+
if (field) {
|
|
5722
|
+
const define = map.get(field);
|
|
5723
|
+
if (define) {
|
|
5724
|
+
return { field, placeholder, width, label: label || define.label };
|
|
5725
|
+
}
|
|
5726
|
+
}
|
|
5727
|
+
const options = new Set(['add', 'move', 'trigger', 'remove', 'serial']);
|
|
5728
|
+
const allActions = [action, actions].flat().filter(v => v && options.delete(v));
|
|
5729
|
+
if (allActions.length) {
|
|
5730
|
+
return { actions: /** @type {StoreLayout.Action[]} */(allActions), width, label };
|
|
5731
|
+
}
|
|
5732
|
+
// if (pattern) {
|
|
5733
|
+
// return { pattern, placeholder, width, label };
|
|
5734
|
+
// }
|
|
5735
|
+
return null;
|
|
5736
|
+
});
|
|
5737
|
+
columns = /** @type {StoreLayout.Column[]} */(allColumns.filter(Boolean));
|
|
5738
|
+
|
|
5908
5739
|
}
|
|
5909
|
-
if (
|
|
5910
|
-
|
|
5740
|
+
if (!columns.length) {
|
|
5741
|
+
columns = [
|
|
5742
|
+
{ actions: ['add', 'trigger', 'move', 'remove', 'serial'] },
|
|
5743
|
+
...fieldList.slice(0, 3),
|
|
5744
|
+
];
|
|
5911
5745
|
}
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5746
|
+
|
|
5747
|
+
const table = document.createElement('table');
|
|
5748
|
+
table.className = 'NeeloongForm-table';
|
|
5749
|
+
const thead = table.appendChild(document.createElement('thead'));
|
|
5750
|
+
|
|
5751
|
+
const addable = new exports.Signal.Computed(() => store.addable);
|
|
5752
|
+
const deletable = { get: () => Boolean(options?.editable) };
|
|
5753
|
+
function add() {
|
|
5754
|
+
const data = {};
|
|
5755
|
+
store.add(data);
|
|
5756
|
+
}
|
|
5757
|
+
/**
|
|
5758
|
+
*
|
|
5759
|
+
* @param {Store} child
|
|
5760
|
+
*/
|
|
5761
|
+
function remove(child) {
|
|
5762
|
+
store.remove(Number(child.index));
|
|
5763
|
+
}
|
|
5764
|
+
let dragRow = -1;
|
|
5765
|
+
/**
|
|
5766
|
+
*
|
|
5767
|
+
* @param {Store} [child]
|
|
5768
|
+
*/
|
|
5769
|
+
function dragenter(child) {
|
|
5770
|
+
if (dragRow < 0) { return; }
|
|
5771
|
+
const index = child ? Number(child.index) : store.children.length;
|
|
5772
|
+
if (index < 0 || dragRow < 0 || dragRow === index) { return; }
|
|
5773
|
+
if (store.move(dragRow, index)) {
|
|
5774
|
+
dragRow = index;
|
|
5775
|
+
}
|
|
5776
|
+
}
|
|
5777
|
+
/**
|
|
5778
|
+
*
|
|
5779
|
+
* @param {Store} child
|
|
5780
|
+
*/
|
|
5781
|
+
function dragstart(child) {
|
|
5782
|
+
dragRow = Number(child.index);
|
|
5783
|
+
|
|
5784
|
+
}
|
|
5785
|
+
function dragend() {
|
|
5786
|
+
dragRow = -1;
|
|
5787
|
+
|
|
5788
|
+
}
|
|
5789
|
+
/** @type {(() => void)[]} */
|
|
5790
|
+
const destroyList = [];
|
|
5791
|
+
destroyList.push(renderHead(thead, columns, add, addable, Boolean(options?.editable)));
|
|
5792
|
+
switch (layout?.tableFoot) {
|
|
5793
|
+
default:
|
|
5794
|
+
case 'header': {
|
|
5795
|
+
const tfoot = table.appendChild(document.createElement('tfoot'));
|
|
5796
|
+
tfoot.addEventListener('dragenter', () => { dragenter(); });
|
|
5797
|
+
destroyList.push(renderHead(tfoot, columns, add, addable, Boolean(options?.editable)));
|
|
5798
|
+
break;
|
|
5799
|
+
}
|
|
5800
|
+
case 'add': {
|
|
5801
|
+
const tfoot = table.appendChild(document.createElement('tfoot'));
|
|
5802
|
+
tfoot.addEventListener('dragenter', () => { dragenter(); });
|
|
5803
|
+
const tr = tfoot.appendChild(document.createElement('tr'));
|
|
5804
|
+
const th = tr.appendChild(document.createElement('th'));
|
|
5805
|
+
th.colSpan = columns.length;
|
|
5806
|
+
const button = th.appendChild(document.createElement('button'));
|
|
5807
|
+
button.addEventListener('click', add);
|
|
5808
|
+
button.classList.add('NeeloongForm-table-foot-add');
|
|
5809
|
+
destroyList.push(watch(() => !addable.get(), disabled => { button.disabled = disabled; }, true));
|
|
5810
|
+
break;
|
|
5811
|
+
}
|
|
5812
|
+
case 'none':
|
|
5813
|
+
}
|
|
5814
|
+
const start = thead;
|
|
5815
|
+
/** @type {Map<Store, [HTMLTableSectionElement, () => void]>} */
|
|
5816
|
+
let seMap = new Map();
|
|
5817
|
+
/** @param {Map<Store, [tbody: HTMLTableSectionElement, destroy: () => void]>} map */
|
|
5818
|
+
function destroyMap(map) {
|
|
5819
|
+
for (const [el, destroy] of map.values()) {
|
|
5820
|
+
destroy();
|
|
5821
|
+
el.remove();
|
|
5822
|
+
}
|
|
5823
|
+
|
|
5824
|
+
}
|
|
5825
|
+
const childrenResult = watch(() => store.children, function render(children) {
|
|
5826
|
+
let nextNode = thead.nextSibling;
|
|
5827
|
+
const oldSeMap = seMap;
|
|
5828
|
+
seMap = new Map();
|
|
5829
|
+
for (let child of children) {
|
|
5830
|
+
const old = oldSeMap.get(child);
|
|
5831
|
+
if (!old) {
|
|
5832
|
+
const [el, destroy] = Line(child, fieldRenderer, layout, {
|
|
5833
|
+
columns,
|
|
5834
|
+
remove: remove.bind(null, child),
|
|
5835
|
+
dragenter: dragenter.bind(null, child),
|
|
5836
|
+
dragstart: dragstart.bind(null, child),
|
|
5837
|
+
dragend,
|
|
5838
|
+
deletable,
|
|
5839
|
+
}, options);
|
|
5840
|
+
table.insertBefore(el, nextNode);
|
|
5841
|
+
seMap.set(child, [el, destroy]);
|
|
5842
|
+
continue;
|
|
5843
|
+
}
|
|
5844
|
+
oldSeMap.delete(child);
|
|
5845
|
+
seMap.set(child, old);
|
|
5846
|
+
if (nextNode === old[0]) {
|
|
5847
|
+
nextNode = nextNode.nextSibling;
|
|
5848
|
+
continue;
|
|
5849
|
+
}
|
|
5850
|
+
table.insertBefore(old[0], nextNode);
|
|
5851
|
+
}
|
|
5852
|
+
destroyMap(oldSeMap);
|
|
5853
|
+
}, true);
|
|
5854
|
+
|
|
5855
|
+
return [table, () => {
|
|
5856
|
+
start.remove();
|
|
5857
|
+
thead.remove();
|
|
5858
|
+
destroyMap(seMap);
|
|
5859
|
+
childrenResult();
|
|
5860
|
+
for (const destroy of destroyList) {
|
|
5861
|
+
destroy();
|
|
5862
|
+
}
|
|
5863
|
+
}];
|
|
5915
5864
|
}
|
|
5916
5865
|
|
|
5917
5866
|
/** @import { StoreLayout } from '../types.mjs' */
|
|
@@ -6157,7 +6106,7 @@
|
|
|
6157
6106
|
* @param {StoreLayout.Field?} layout
|
|
6158
6107
|
* @param {State} initState
|
|
6159
6108
|
* @param {object} option
|
|
6160
|
-
* @param {
|
|
6109
|
+
* @param {StoreLayout.Column[]} option.columns
|
|
6161
6110
|
* @param {() => void} option.remove
|
|
6162
6111
|
* @param {(el: HTMLElement) => () => void} option.dragenter
|
|
6163
6112
|
* @param {() => void} option.dragstart
|
|
@@ -6252,38 +6201,40 @@
|
|
|
6252
6201
|
dropChildren.addEventListener('dragover', (e) => e.preventDefault());
|
|
6253
6202
|
dropFront.addEventListener('drop', () => drop());
|
|
6254
6203
|
dropChildren.addEventListener('drop', () => drop(true));
|
|
6255
|
-
destroyList.push(effect(() => {
|
|
6204
|
+
destroyList.push(effect(() => {
|
|
6256
6205
|
dropFront.hidden = dropChildren.hidden = !state.get().droppable;
|
|
6257
|
-
|
|
6206
|
+
}));
|
|
6258
6207
|
|
|
6259
|
-
let dragleave = () => {};
|
|
6208
|
+
let dragleave = () => { };
|
|
6260
6209
|
dropFront.addEventListener('dragenter', () => dragleave = dragenter(dropFront));
|
|
6261
6210
|
dropChildren.addEventListener('dragenter', () => dragleave = dragenter(dropChildren));
|
|
6262
6211
|
dropFront.addEventListener('dragleave', () => dragleave());
|
|
6263
6212
|
dropChildren.addEventListener('dragleave', () => dragleave());
|
|
6264
6213
|
|
|
6265
|
-
for (const
|
|
6266
|
-
if (
|
|
6267
|
-
const td = line.appendChild(document.createElement('div'));
|
|
6268
|
-
td.classList.add('NeeloongForm-tree-placeholder');
|
|
6269
|
-
td.style.flex = `${name}`;
|
|
6270
|
-
if (click) { td.addEventListener('click', click); }
|
|
6271
|
-
if (moveStart) { td.addEventListener('pointerdown', moveStart); }
|
|
6272
|
-
continue;
|
|
6273
|
-
}
|
|
6274
|
-
if (!Array.isArray(name)) {
|
|
6214
|
+
for (const { actions, pattern, placeholder, width, field } of columns) {
|
|
6215
|
+
if (!actions?.length) {
|
|
6275
6216
|
const td = line.appendChild(document.createElement('div'));
|
|
6276
6217
|
td.classList.add('NeeloongForm-tree-cell');
|
|
6277
|
-
const child = store.child(name);
|
|
6278
|
-
if (!child) { continue; }
|
|
6279
|
-
const [el, destroy] = FormField(child, fieldRenderer, null, { ...options, editable: false }, true);
|
|
6280
|
-
destroyList.push(destroy);
|
|
6281
|
-
td.appendChild(el);
|
|
6282
6218
|
if (click) { td.addEventListener('click', click); }
|
|
6283
6219
|
if (moveStart) { td.addEventListener('pointerdown', moveStart); }
|
|
6220
|
+
if (field) {
|
|
6221
|
+
const child = store.child(field);
|
|
6222
|
+
if (!child) { continue; }
|
|
6223
|
+
const [el, destroy] = FormFieldInline(child, fieldRenderer, null, { ...options, editable: false });
|
|
6224
|
+
destroyList.push(destroy);
|
|
6225
|
+
td.appendChild(el);
|
|
6226
|
+
continue;
|
|
6227
|
+
}
|
|
6228
|
+
if (typeof placeholder === 'number') {
|
|
6229
|
+
td.style.flex = `${placeholder}`;
|
|
6230
|
+
}
|
|
6231
|
+
if (typeof width === 'number') {
|
|
6232
|
+
td.style.width = `${width}px`;
|
|
6233
|
+
}
|
|
6284
6234
|
continue;
|
|
6235
|
+
|
|
6285
6236
|
}
|
|
6286
|
-
for (const k of
|
|
6237
|
+
for (const k of actions) {
|
|
6287
6238
|
switch (k) {
|
|
6288
6239
|
case 'trigger': {
|
|
6289
6240
|
const btn = line.appendChild(document.createElement('button'));
|
|
@@ -6490,28 +6441,54 @@
|
|
|
6490
6441
|
const fieldList = Object.entries(store.type || {})
|
|
6491
6442
|
.filter(([k, v]) => typeof v?.type !== 'object')
|
|
6492
6443
|
.map(([field, { width, label }]) => ({ field, width, label }));
|
|
6493
|
-
/** @type {
|
|
6444
|
+
/** @type {StoreLayout.Column[]} */
|
|
6494
6445
|
let columns = [];
|
|
6495
6446
|
if (Array.isArray(headerColumns)) {
|
|
6496
6447
|
const map = new Map(fieldList.map(v => [v.field, v]));
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
if (
|
|
6500
|
-
if (
|
|
6501
|
-
|
|
6448
|
+
/** @type {(StoreLayout.Column | null)[]} */
|
|
6449
|
+
const allColumns = headerColumns.map(v => {
|
|
6450
|
+
if (!v) { return null; }
|
|
6451
|
+
if (typeof v === 'number') { return { placeholder: v }; }
|
|
6452
|
+
if (typeof v === 'string') { return map.get(v) || null; }
|
|
6453
|
+
if (typeof v !== 'object') { return null; }
|
|
6454
|
+
if (Array.isArray(v)) {
|
|
6455
|
+
/** @type {Set<StoreLayout.Action>} */
|
|
6456
|
+
const options = new Set(['add', 'move', 'trigger', 'remove', 'serial', 'open', 'collapse']);
|
|
6457
|
+
const actions = v.filter(v => options.delete(v));
|
|
6458
|
+
if (!actions) { return null; }
|
|
6459
|
+
return { actions };
|
|
6460
|
+
}
|
|
6461
|
+
const { action, actions, field, placeholder, pattern, width, label } = v;
|
|
6462
|
+
if (field) {
|
|
6463
|
+
const define = map.get(field);
|
|
6464
|
+
if (define) {
|
|
6465
|
+
return { field, placeholder, width, label: label || define.label };
|
|
6466
|
+
}
|
|
6467
|
+
}
|
|
6502
6468
|
const options = new Set(['add', 'move', 'trigger', 'remove', 'serial', 'open', 'collapse']);
|
|
6503
|
-
|
|
6504
|
-
|
|
6469
|
+
const allActions = [action, actions].flat().filter(v => v && options.delete(v));
|
|
6470
|
+
if (allActions.length) {
|
|
6471
|
+
return { actions: /** @type {StoreLayout.Action[]} */(allActions), width, label };
|
|
6472
|
+
}
|
|
6473
|
+
if (pattern) {
|
|
6474
|
+
return { pattern, placeholder, width, label };
|
|
6475
|
+
}
|
|
6476
|
+
if (placeholder || width) {
|
|
6477
|
+
return { placeholder, width, label };
|
|
6478
|
+
}
|
|
6479
|
+
return null;
|
|
6480
|
+
});
|
|
6481
|
+
columns = /** @type {StoreLayout.Column[]} */(allColumns.filter(Boolean));
|
|
6505
6482
|
}
|
|
6506
6483
|
if (!columns.length) {
|
|
6507
|
-
columns = [
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6484
|
+
columns = [
|
|
6485
|
+
{ actions: ['collapse', 'move'] },
|
|
6486
|
+
fieldList[0],
|
|
6487
|
+
{ actions: ['add', 'remove'] },
|
|
6488
|
+
];
|
|
6511
6489
|
}
|
|
6512
6490
|
|
|
6513
6491
|
|
|
6514
|
-
|
|
6515
6492
|
const root = document.createElement('div');
|
|
6516
6493
|
root.className = 'NeeloongForm-tree';
|
|
6517
6494
|
const main = root.appendChild(document.createElement('div'));
|
|
@@ -6781,7 +6758,6 @@
|
|
|
6781
6758
|
}
|
|
6782
6759
|
/** @type {State[]} */
|
|
6783
6760
|
const states = [];
|
|
6784
|
-
const columnNames = columns.map((v) => Array.isArray(v) || typeof v === 'number' ? v : v.field);
|
|
6785
6761
|
const childrenResult = watch(() => store.children, function render(children) {
|
|
6786
6762
|
let nextNode = start.nextSibling;
|
|
6787
6763
|
const oldSeMap = seMap;
|
|
@@ -6798,7 +6774,7 @@
|
|
|
6798
6774
|
const old = oldSeMap.get(child);
|
|
6799
6775
|
if (!old) {
|
|
6800
6776
|
const [el, destroy, setState] = TreeLine(child, detailsStore, fieldRenderer, layout, state, {
|
|
6801
|
-
columns
|
|
6777
|
+
columns,
|
|
6802
6778
|
remove: remove.bind(null, child),
|
|
6803
6779
|
dragenter,
|
|
6804
6780
|
dragstart: dragstart.bind(null, child),
|
|
@@ -6869,22 +6845,10 @@
|
|
|
6869
6845
|
* @param {StoreLayout.Renderer} fieldRenderer
|
|
6870
6846
|
* @param {StoreLayout.Field?} layout
|
|
6871
6847
|
* @param {StoreLayout.Options?} options
|
|
6872
|
-
* @param {boolean} [inline]
|
|
6873
6848
|
* @returns {[ParentNode, () => void]}
|
|
6874
6849
|
*/
|
|
6875
|
-
function FormField(store, fieldRenderer, layout, options
|
|
6850
|
+
function FormField(store, fieldRenderer, layout, options) {
|
|
6876
6851
|
const { type, component } = store;
|
|
6877
|
-
if (inline) {
|
|
6878
|
-
const html = layout?.inlineHtml;
|
|
6879
|
-
if (html) {
|
|
6880
|
-
const content = getHtmlContent(html);
|
|
6881
|
-
const destroy = renderHtml(store, fieldRenderer, content, options, layout);
|
|
6882
|
-
return [content, destroy];
|
|
6883
|
-
}
|
|
6884
|
-
return component
|
|
6885
|
-
&& fieldRenderer(store, component, options)
|
|
6886
|
-
|| [document.createElement('div'), () => { }];
|
|
6887
|
-
}
|
|
6888
6852
|
const isObject = type && typeof type === 'object';
|
|
6889
6853
|
const html = layout?.html;
|
|
6890
6854
|
/** @type {StoreLayout.Grid['cell']} */
|