@picked/svelte 0.0.54-next.3 → 0.0.54-next.4

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.
@@ -85,10 +85,8 @@
85
85
  fields.push({ label: 'Step Key', value: step.key })
86
86
  }
87
87
 
88
- // eslint-disable-next-line sonarjs/deprecation -- backwards-compat fallback for label
89
- const stepName = ('name' in step && step.name) || ('label' in step && step.label)
90
- if (stepName) {
91
- fields.push({ label: 'Step Name', value: stepName })
88
+ if ('name' in step && step.name) {
89
+ fields.push({ label: 'Step Name', value: step.name })
92
90
  }
93
91
 
94
92
  return fields
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  /** @file D3-based tree visualization of an experience's node graph in the devtools panel. */
3
+ import { onDestroy } from 'svelte'
3
4
  import * as d3 from 'd3'
4
5
  import type { ExperienceEdge, ExperienceNode, ExperienceStore } from '@picked/client'
5
6
  import { Badge, Button, Spinner, Stack, Text, VisualizationContainer } from '../../ui'
@@ -16,6 +17,12 @@
16
17
  const loading = false
17
18
 
18
19
  let treeRenderingDone = false
20
+ let tooltipEl: d3.Selection<HTMLDivElement, unknown, null, undefined> | null = null
21
+
22
+ onDestroy(() => {
23
+ tooltipEl?.remove()
24
+ tooltipEl = null
25
+ })
19
26
 
20
27
  const tree = experience.treeStore
21
28
 
@@ -23,11 +30,13 @@
23
30
  $: edges = $tree.edges
24
31
  $: conditions = $tree.conditions
25
32
 
33
+
26
34
  let zoomBehavior: d3.ZoomBehavior<SVGSVGElement, unknown>
27
35
 
28
36
  interface ProcessedNode {
29
37
  id: string
30
38
  name: string
39
+ tooltip?: string
31
40
  type: string
32
41
  children: ProcessedNode[]
33
42
  _originalNode: ExperienceNode
@@ -137,25 +146,32 @@
137
146
 
138
147
  const getNodeLabel = (node: ExperienceNode): string => {
139
148
  if (node.type === 'origin') return 'Start'
140
- if (node.type === 'step')
141
- return node.name ?? 'Unknown Step'
149
+ if (node.type === 'step') return node.name ?? 'Unknown Step'
150
+
142
151
  if (node.type === 'condition') {
143
- const conditionId = node.conditionId
144
- const conditionItem = conditionId ? conditionsMap.get(conditionId) : undefined
145
- if (conditionItem?.name) return conditionItem.name
146
- if (conditionItem?.logic) return formatLogicRule(conditionItem.logic)
147
- return 'Condition'
152
+ const conditionItem = node.conditionId ? conditionsMap.get(node.conditionId) : undefined
153
+ return conditionItem?.name ?? 'Condition'
148
154
  }
149
155
 
156
+ if (node.type === 'action') return node.name ?? node.actionKey ?? 'Action'
157
+
150
158
  return 'Unknown Node'
151
159
  }
152
160
 
161
+ const getNodeTooltip = (node: ExperienceNode): string | undefined => {
162
+ if (node.type !== 'condition') return undefined
163
+ const conditionItem = node.conditionId ? conditionsMap.get(node.conditionId) : undefined
164
+ if (conditionItem?.logic) return formatLogicRule(conditionItem.logic)
165
+ return undefined
166
+ }
167
+
153
168
  // eslint-disable-next-line svelte/prefer-svelte-reactivity -- D3 visualization runs imperatively outside Svelte reactivity
154
169
  const processedNodes = new Map<string, ProcessedNode>()
155
170
  nodes.forEach((n) => {
156
171
  processedNodes.set(n.id, {
157
172
  id: n.id,
158
173
  name: getNodeLabel(n),
174
+ tooltip: getNodeTooltip(n),
159
175
  type: n.type,
160
176
  children: [],
161
177
  _originalNode: n,
@@ -302,20 +318,58 @@
302
318
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- D3 callback types are untyped
303
319
  .attr('transform', (d: any) => `translate(${d.y},${d.x})`)
304
320
 
305
- nodesSelection
321
+ const circles = nodesSelection
306
322
  .append('circle')
307
323
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- D3 callback types are untyped
308
324
  .attr('r', (d: any) => (d.data.type === 'origin' ? 15 : 10))
309
325
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- D3 callback types are untyped
310
326
  .attr('fill', (d: any) => {
311
327
  if (d.data._isReference) return '#38bdf8'
312
- if (d.data.type === 'origin') return '#00cd90'
328
+ if (d.data.type === 'origin') return '#6b7280'
313
329
  if (d.data.type === 'condition') return '#f59e0b'
314
- return '#1748ff'
330
+ if (d.data.type === 'action') return '#22c55e'
331
+ return '#3b82f6'
315
332
  })
316
333
  .attr('stroke', '#fff')
317
334
  .attr('stroke-width', 2)
318
335
 
336
+ tooltipEl?.remove()
337
+ tooltipEl = d3
338
+ .select(svgEl.parentElement)
339
+ .append('div')
340
+
341
+ const tooltip = tooltipEl
342
+ .style('position', 'absolute')
343
+ .style('pointer-events', 'none')
344
+ .style('background', '#1e293b')
345
+ .style('color', '#f8fafc')
346
+ .style('padding', '6px 10px')
347
+ .style('border-radius', '6px')
348
+ .style('font-size', '11px')
349
+ .style('max-width', '260px')
350
+ .style('white-space', 'pre-wrap')
351
+ .style('opacity', '0')
352
+ .style('z-index', '10')
353
+
354
+ circles
355
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- D3 callback types are untyped
356
+ .filter((d: any) => d.data.tooltip)
357
+ .style('cursor', 'default')
358
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- D3 callback types are untyped
359
+ .on('mouseenter', function showTooltip(event: MouseEvent, d: any) {
360
+ const container = svgEl.parentElement
361
+ if (!container || !d.data.tooltip) return
362
+ const rect = container.getBoundingClientRect()
363
+ tooltip
364
+ .text(d.data.tooltip)
365
+ .style('left', `${event.clientX - rect.left + 12}px`)
366
+ .style('top', `${event.clientY - rect.top - 8}px`)
367
+ .style('opacity', '1')
368
+ })
369
+ .on('mouseleave', function hideTooltip() {
370
+ tooltip.style('opacity', '0')
371
+ })
372
+
319
373
  nodesSelection
320
374
  .append('text')
321
375
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- D3 callback types are untyped
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@picked/svelte",
3
- "version": "0.0.54-next.3",
3
+ "version": "0.0.54-next.4",
4
4
  "dependencies": {
5
5
  "d3": "^7.9.0"
6
6
  },
@@ -88,7 +88,7 @@
88
88
  },
89
89
  "module": "./dist/index.js",
90
90
  "peerDependencies": {
91
- "@picked/client": "0.0.54-next.3",
91
+ "@picked/client": "0.0.54-next.4",
92
92
  "effector": "^23.3.0",
93
93
  "svelte": "^5.46.4"
94
94
  },