@nocobase/client-v2 2.1.3 → 2.1.5

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.
@@ -0,0 +1,170 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ import type { FlowContext } from '@nocobase/flow-engine';
11
+ import { namePathToPathKey, parsePathString, pathKeyToNamePath } from '../models/blocks/form/value-runtime/path';
12
+
13
+ export type NamePath = Array<string | number>;
14
+
15
+ export type FieldIndexEntry = {
16
+ name: string;
17
+ index: number;
18
+ };
19
+
20
+ export function isSameNamePath(a: NamePath, b: NamePath) {
21
+ return a.length === b.length && a.every((seg, index) => seg === b[index]);
22
+ }
23
+
24
+ export function isNamePathPrefix(prefix: NamePath, path: NamePath) {
25
+ if (prefix.length > path.length) return false;
26
+ return prefix.every((seg, index) => seg === path[index]);
27
+ }
28
+
29
+ export function dedupeNamePaths(paths: NamePath[]) {
30
+ const byKey = new Map<string, NamePath>();
31
+ for (const path of paths) {
32
+ if (!path?.length) continue;
33
+ byKey.set(namePathToPathKey(path), path);
34
+ }
35
+ return Array.from(byKey.values());
36
+ }
37
+
38
+ export function minimizeNamePaths(paths: NamePath[]) {
39
+ const deduped = dedupeNamePaths(paths);
40
+ return deduped.filter((path, index) => {
41
+ return !deduped.some((other, otherIndex) => otherIndex !== index && isNamePathPrefix(path, other));
42
+ });
43
+ }
44
+
45
+ export function parseFieldIndexEntries(fieldIndex: unknown): FieldIndexEntry[] {
46
+ const arr = Array.isArray(fieldIndex) ? fieldIndex : [];
47
+ const entries: FieldIndexEntry[] = [];
48
+ for (const item of arr) {
49
+ if (typeof item !== 'string') continue;
50
+ const [name, indexStr] = item.split(':');
51
+ const index = Number(indexStr);
52
+ if (!name || Number.isNaN(index)) continue;
53
+ entries.push({ name, index });
54
+ }
55
+ return entries;
56
+ }
57
+
58
+ export function getFieldIndexEntriesFromContext(ctx: FlowContext | { model?: unknown; fieldIndex?: unknown }) {
59
+ const model = ctx?.model as { context?: { fieldIndex?: unknown } } | undefined;
60
+ return parseFieldIndexEntries(model?.context?.fieldIndex ?? (ctx as { fieldIndex?: unknown })?.fieldIndex);
61
+ }
62
+
63
+ export function buildItemRowPath(entries: FieldIndexEntry[], parentDepth: number): NamePath | null {
64
+ const targetIndex = entries.length - 1 - parentDepth;
65
+ if (targetIndex < 0) return null;
66
+
67
+ const out: NamePath = [];
68
+ for (let index = 0; index <= targetIndex; index++) {
69
+ out.push(entries[index].name, entries[index].index);
70
+ }
71
+ return out;
72
+ }
73
+
74
+ export function buildItemListRootPath(entries: FieldIndexEntry[], parentDepth: number): NamePath | null {
75
+ const rowPath = buildItemRowPath(entries, parentDepth);
76
+ if (!rowPath?.length) return null;
77
+ return rowPath.slice(0, -1);
78
+ }
79
+
80
+ export function parseDependencyPath(subPath: string): NamePath {
81
+ return parsePathString(subPath).filter((seg) => typeof seg !== 'object') as NamePath;
82
+ }
83
+
84
+ export function parsePathKey(pathKey: string): NamePath {
85
+ return pathKeyToNamePath(pathKey) as NamePath;
86
+ }
87
+
88
+ export function getChangedPathsFromPayload(
89
+ payload: unknown,
90
+ options: { includeArrayChangedValues?: boolean } = {},
91
+ ): NamePath[] {
92
+ const payloadObj = payload as
93
+ | {
94
+ changedPaths?: unknown;
95
+ changedValues?: unknown;
96
+ }
97
+ | undefined;
98
+ const rawChangedPaths = Array.isArray(payloadObj?.changedPaths) ? payloadObj.changedPaths : [];
99
+ const out: NamePath[] = [];
100
+
101
+ for (const path of rawChangedPaths) {
102
+ if (Array.isArray(path)) {
103
+ if (path.length === 1 && typeof path[0] === 'string') {
104
+ const namePath = pathKeyToNamePath(path[0]);
105
+ if (namePath.length) out.push(namePath as NamePath);
106
+ continue;
107
+ }
108
+ const segs = path.filter((seg) => typeof seg === 'string' || typeof seg === 'number') as NamePath;
109
+ if (segs.length) out.push(segs);
110
+ continue;
111
+ }
112
+ if (typeof path === 'string' && path) {
113
+ out.push(pathKeyToNamePath(path) as NamePath);
114
+ }
115
+ }
116
+
117
+ if (out.length) {
118
+ return out;
119
+ }
120
+
121
+ const changedValues = payloadObj?.changedValues;
122
+ const canReadChangedValues =
123
+ changedValues &&
124
+ typeof changedValues === 'object' &&
125
+ (options.includeArrayChangedValues || !Array.isArray(changedValues));
126
+ if (canReadChangedValues) {
127
+ for (const key of Object.keys(changedValues)) {
128
+ const namePath = pathKeyToNamePath(key);
129
+ if (namePath.length) out.push(namePath as NamePath);
130
+ }
131
+ }
132
+
133
+ return out;
134
+ }
135
+
136
+ export function isFormValueChangeSource(model: unknown) {
137
+ const candidate = model as
138
+ | {
139
+ emitter?: { on?: unknown; off?: unknown };
140
+ formValueRuntime?: unknown;
141
+ context?: { form?: unknown; setFormValues?: unknown };
142
+ }
143
+ | undefined;
144
+ if (!candidate || typeof candidate !== 'object') return false;
145
+ if (!candidate.emitter || typeof candidate.emitter.on !== 'function' || typeof candidate.emitter.off !== 'function') {
146
+ return false;
147
+ }
148
+ return (
149
+ !!candidate.formValueRuntime || !!candidate.context?.form || typeof candidate.context?.setFormValues === 'function'
150
+ );
151
+ }
152
+
153
+ export function findFormValueChangeSource(ctx: FlowContext): unknown | null {
154
+ const candidates: unknown[] = [];
155
+ const push = (model: unknown) => {
156
+ if (model && !candidates.includes(model)) candidates.push(model);
157
+ };
158
+
159
+ const model = ctx.model as { context?: { blockModel?: unknown }; parent?: unknown } | undefined;
160
+ push(model?.context?.blockModel);
161
+ push(ctx.model);
162
+
163
+ let cursor = model?.parent as { parent?: unknown } | undefined;
164
+ while (cursor) {
165
+ push(cursor);
166
+ cursor = cursor?.parent as { parent?: unknown } | undefined;
167
+ }
168
+
169
+ return candidates.find(isFormValueChangeSource) || null;
170
+ }