@picked/svelte 0.0.47-next.1 → 0.0.47-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -35,10 +35,62 @@
|
|
|
35
35
|
_referenceTo?: string
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
function isRecord(val: unknown): val is Record<string, unknown> {
|
|
39
|
+
return val !== null && typeof val === 'object' && !Array.isArray(val)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function formatLogicRule(rule: unknown): string {
|
|
43
|
+
if (rule === null || rule === undefined) return '?'
|
|
44
|
+
if (typeof rule === 'string') return rule
|
|
45
|
+
if (typeof rule === 'number' || typeof rule === 'boolean') return String(rule)
|
|
46
|
+
if (Array.isArray(rule)) {
|
|
47
|
+
return rule.map(formatLogicRule).join(', ')
|
|
48
|
+
}
|
|
49
|
+
if (isRecord(rule)) {
|
|
50
|
+
const keys = Object.keys(rule)
|
|
51
|
+
if (keys.length === 0) return 'Condition'
|
|
52
|
+
const op = keys[0]
|
|
53
|
+
const args = rule[op]
|
|
54
|
+
|
|
55
|
+
if (op === 'var') {
|
|
56
|
+
return String(Array.isArray(args) ? args[0] : args)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const compOps: Record<string, string> = {
|
|
60
|
+
'==': '=',
|
|
61
|
+
'===': '=',
|
|
62
|
+
'!=': '≠',
|
|
63
|
+
'!==': '≠',
|
|
64
|
+
'>': '>',
|
|
65
|
+
'<': '<',
|
|
66
|
+
'>=': '≥',
|
|
67
|
+
'<=': '≤',
|
|
68
|
+
}
|
|
69
|
+
if (op in compOps && Array.isArray(args) && args.length >= 2) {
|
|
70
|
+
return `${formatLogicRule(args[0])} ${compOps[op]} ${formatLogicRule(args[1])}`
|
|
71
|
+
}
|
|
72
|
+
if (op === 'and' && Array.isArray(args)) {
|
|
73
|
+
return args.map(formatLogicRule).join(' & ')
|
|
74
|
+
}
|
|
75
|
+
if (op === 'or' && Array.isArray(args)) {
|
|
76
|
+
return args.map(formatLogicRule).join(' | ')
|
|
77
|
+
}
|
|
78
|
+
if ((op === '!' || op === 'not') && Array.isArray(args)) {
|
|
79
|
+
return `!${formatLogicRule(args[0])}`
|
|
80
|
+
}
|
|
81
|
+
if (op === 'in' && Array.isArray(args) && args.length >= 2) {
|
|
82
|
+
return `${formatLogicRule(args[0])} in ${formatLogicRule(args[1])}`
|
|
83
|
+
}
|
|
84
|
+
return `${op}(?)`
|
|
85
|
+
}
|
|
86
|
+
return '?'
|
|
87
|
+
}
|
|
88
|
+
|
|
38
89
|
function renderGraph() {
|
|
39
90
|
d3.select(svgEl).selectAll('*').remove()
|
|
40
91
|
|
|
41
92
|
const stepsMap = new Map(steps.map((s) => [s.id, s]))
|
|
93
|
+
const conditionalsMap = new Map(conditionals.map((c) => [c.id, c]))
|
|
42
94
|
const outgoingEdges = new Map<string, ExperienceEdge[]>()
|
|
43
95
|
edges.forEach((e) => {
|
|
44
96
|
if (!outgoingEdges.has(e.sourceNodeId)) outgoingEdges.set(e.sourceNodeId, [])
|
|
@@ -49,7 +101,12 @@
|
|
|
49
101
|
if (node.type === 'origin') return 'Start'
|
|
50
102
|
if (node.type === 'step' && node.stepId)
|
|
51
103
|
return stepsMap.get(node.stepId)?.label ?? 'Unknown Step'
|
|
52
|
-
if (node.type === 'conditional' && node.conditionalId)
|
|
104
|
+
if (node.type === 'conditional' && node.conditionalId) {
|
|
105
|
+
const conditional = conditionalsMap.get(node.conditionalId)
|
|
106
|
+
if (conditional?.label) return conditional.label
|
|
107
|
+
if (conditional?.condition) return formatLogicRule(conditional.condition)
|
|
108
|
+
return 'Condition'
|
|
109
|
+
}
|
|
53
110
|
return 'Unknown Node'
|
|
54
111
|
}
|
|
55
112
|
|
|
@@ -81,15 +138,16 @@
|
|
|
81
138
|
}
|
|
82
139
|
|
|
83
140
|
if (rootNode) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
141
|
+
// Use a global visited set so join nodes (multiple incoming branches converging
|
|
142
|
+
// to the same node) only appear once in the tree, with reference nodes on
|
|
143
|
+
// subsequent visits to indicate the convergence point.
|
|
144
|
+
const globalVisited = new Set<string>()
|
|
145
|
+
const convertGraphToTree = (node: ProcessedNode): ProcessedNode => {
|
|
146
|
+
if (globalVisited.has(node.id)) {
|
|
89
147
|
return {
|
|
90
148
|
...node,
|
|
91
149
|
id: `${node.id}_ref`,
|
|
92
|
-
name:
|
|
150
|
+
name: `↩ ${node.name}`,
|
|
93
151
|
type: `${node.type}_ref`,
|
|
94
152
|
children: [],
|
|
95
153
|
_isReference: true,
|
|
@@ -97,9 +155,8 @@
|
|
|
97
155
|
}
|
|
98
156
|
}
|
|
99
157
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return { ...node, children: node.children.map((c) => convertGraphToTree(c, newVisited)) }
|
|
158
|
+
globalVisited.add(node.id)
|
|
159
|
+
return { ...node, children: node.children.map((c) => convertGraphToTree(c)) }
|
|
103
160
|
}
|
|
104
161
|
|
|
105
162
|
const treeData = convertGraphToTree(rootNode)
|
|
@@ -128,31 +185,75 @@
|
|
|
128
185
|
|
|
129
186
|
const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`)
|
|
130
187
|
|
|
188
|
+
const linkGenerator = d3
|
|
189
|
+
.linkHorizontal<d3.HierarchyPointLink<ProcessedNode>, ProcessedNode>()
|
|
190
|
+
.x((d) => d.y)
|
|
191
|
+
.y((d) => d.x)
|
|
192
|
+
|
|
131
193
|
g.selectAll('.link')
|
|
132
194
|
.data(treeRoot.links())
|
|
133
195
|
.enter()
|
|
134
196
|
.append('path')
|
|
135
197
|
.attr('class', 'link')
|
|
136
198
|
.attr('fill', 'none')
|
|
137
|
-
.attr('stroke',
|
|
199
|
+
.attr('stroke', (d: any) => {
|
|
200
|
+
if (d.source.data.type !== 'conditional') return '#94a3b8'
|
|
201
|
+
const targetId = d.target.data._isReference
|
|
202
|
+
? d.target.data._referenceTo
|
|
203
|
+
: d.target.data.id
|
|
204
|
+
const edge = d.source.data._originalEdges.find(
|
|
205
|
+
(e: ExperienceEdge) => e.destinationNodeId === targetId,
|
|
206
|
+
)
|
|
207
|
+
if (edge?.sourceNodeHandle === 'true') return '#10b981'
|
|
208
|
+
if (edge?.sourceNodeHandle === 'false') return '#ef4444'
|
|
209
|
+
return '#94a3b8'
|
|
210
|
+
})
|
|
138
211
|
.attr('stroke-width', 2)
|
|
139
|
-
.attr(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
)
|
|
212
|
+
.attr('d', linkGenerator)
|
|
213
|
+
|
|
214
|
+
// Label true/false branches from conditional nodes
|
|
215
|
+
g.selectAll('.edge-label')
|
|
216
|
+
.data(treeRoot.links().filter((d: any) => d.source.data.type === 'conditional'))
|
|
217
|
+
.enter()
|
|
218
|
+
.append('text')
|
|
219
|
+
.attr('class', 'edge-label')
|
|
220
|
+
.attr('text-anchor', 'middle')
|
|
221
|
+
.attr('font-size', '10px')
|
|
222
|
+
.attr('font-weight', 'bold')
|
|
223
|
+
.attr('x', (d: any) => (d.source.y + d.target.y) / 2)
|
|
224
|
+
.attr('y', (d: any) => (d.source.x + d.target.x) / 2 - 6)
|
|
225
|
+
.attr('fill', (d: any) => {
|
|
226
|
+
const targetId = d.target.data._isReference
|
|
227
|
+
? d.target.data._referenceTo
|
|
228
|
+
: d.target.data.id
|
|
229
|
+
const edge = d.source.data._originalEdges.find(
|
|
230
|
+
(e: ExperienceEdge) => e.destinationNodeId === targetId,
|
|
231
|
+
)
|
|
232
|
+
if (edge?.sourceNodeHandle === 'true') return '#10b981'
|
|
233
|
+
if (edge?.sourceNodeHandle === 'false') return '#ef4444'
|
|
234
|
+
return '#64748b'
|
|
235
|
+
})
|
|
236
|
+
.text((d: any) => {
|
|
237
|
+
const targetId = d.target.data._isReference
|
|
238
|
+
? d.target.data._referenceTo
|
|
239
|
+
: d.target.data.id
|
|
240
|
+
const edge = d.source.data._originalEdges.find(
|
|
241
|
+
(e: ExperienceEdge) => e.destinationNodeId === targetId,
|
|
242
|
+
)
|
|
243
|
+
if (edge?.sourceNodeHandle === 'true') return 'Yes'
|
|
244
|
+
if (edge?.sourceNodeHandle === 'false') return 'No'
|
|
245
|
+
return ''
|
|
246
|
+
})
|
|
146
247
|
|
|
147
|
-
const
|
|
248
|
+
const nodesSelection = g
|
|
148
249
|
.selectAll('.node')
|
|
149
250
|
.data(treeRoot.descendants())
|
|
150
251
|
.enter()
|
|
151
252
|
.append('g')
|
|
152
253
|
.attr('class', 'node')
|
|
153
|
-
.attr('transform', (d: any) => `translate(${d.y},${d.x})`)
|
|
254
|
+
.attr('transform', (d: any) => `translate(${d.y},${d.x})`)
|
|
154
255
|
|
|
155
|
-
|
|
256
|
+
nodesSelection
|
|
156
257
|
.append('circle')
|
|
157
258
|
.attr('r', (d: any) => (d.data.type === 'origin' ? 15 : 10))
|
|
158
259
|
.attr('fill', (d: any) => {
|
|
@@ -164,14 +265,14 @@
|
|
|
164
265
|
.attr('stroke', '#fff')
|
|
165
266
|
.attr('stroke-width', 2)
|
|
166
267
|
|
|
167
|
-
|
|
268
|
+
nodesSelection
|
|
168
269
|
.append('text')
|
|
169
270
|
.attr('dy', (d: any) => (d.children ? -15 : 25))
|
|
170
271
|
.attr('text-anchor', 'middle')
|
|
171
272
|
.attr('font-size', '12px')
|
|
172
273
|
.attr('fill', '#1e293b')
|
|
173
274
|
.text((d: any) =>
|
|
174
|
-
d.data.name.length >
|
|
275
|
+
d.data.name.length > 30 ? `${d.data.name.slice(0, 27)}...` : d.data.name,
|
|
175
276
|
)
|
|
176
277
|
|
|
177
278
|
svg.on('dblclick.zoom', null) // Remove default double click zoom
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@picked/svelte",
|
|
3
|
-
"version": "0.0.47-next.
|
|
3
|
+
"version": "0.0.47-next.3",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"d3": "^7.9.0"
|
|
6
6
|
},
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
},
|
|
87
87
|
"module": "./dist/index.js",
|
|
88
88
|
"peerDependencies": {
|
|
89
|
-
"@picked/client": "0.0.47-next.
|
|
89
|
+
"@picked/client": "0.0.47-next.3",
|
|
90
90
|
"effector": "^23.3.0",
|
|
91
91
|
"svelte": "^5.34.3"
|
|
92
92
|
},
|