|
1 | 1 | <script> |
2 | 2 | import ValueEditor from "./ValueEditor.svelte" |
| 3 | + import NestedValue from "./NestedValue.svelte" |
3 | 4 | import CopyButton from "$components/CopyButton.svelte" |
4 | 5 | import { updateDataAttribute } from "../../browser_panel/messaging" |
5 | 6 |
|
6 | 7 | let { valueObject, selected, dataAttribute } = $props() |
7 | 8 |
|
8 | 9 | let editingStates = $state({}) |
9 | 10 |
|
10 | | - const isObject = typeof valueObject.value === "object" && valueObject.value !== null |
| 11 | + const isComplex = (val) => typeof val === "object" && val !== null |
| 12 | + const isArray = (val) => Array.isArray(val) |
11 | 13 |
|
12 | | - const handleSave = (key, newValue) => { |
13 | | - if (isObject) { |
14 | | - const updatedObject = { ...valueObject.value, [key]: newValue } |
15 | | - updateDataAttribute(`[data-hotwire-dev-tools-uuid="${selected.uuid}"]`, dataAttribute, JSON.stringify(updatedObject)) |
| 14 | + // Deep set value in nested object/array using path array |
| 15 | + const setNestedValue = (obj, path, value) => { |
| 16 | + if (path.length === 0) return value |
| 17 | +
|
| 18 | + const newObj = JSON.parse(JSON.stringify(obj)) |
| 19 | + let current = newObj |
| 20 | +
|
| 21 | + for (let i = 0; i < path.length - 1; i++) { |
| 22 | + current = current[path[i]] |
| 23 | + } |
| 24 | +
|
| 25 | + // Try to parse as number, boolean, or keep as string |
| 26 | + let parsedValue = value |
| 27 | + if (value === "true") parsedValue = true |
| 28 | + else if (value === "false") parsedValue = false |
| 29 | + else if (value === "null") parsedValue = null |
| 30 | + else if (!isNaN(value) && value !== "") parsedValue = Number(value) |
| 31 | +
|
| 32 | + current[path[path.length - 1]] = parsedValue |
| 33 | +
|
| 34 | + return newObj |
| 35 | + } |
| 36 | +
|
| 37 | + const serializeValue = (value, type) => { |
| 38 | + // For Array and Object types, Stimulus expects JSON |
| 39 | + if (type === "array" || type === "object") { |
| 40 | + return JSON.stringify(value) |
| 41 | + } |
| 42 | +
|
| 43 | + // For other types, convert to string |
| 44 | + return String(value) |
| 45 | + } |
| 46 | +
|
| 47 | + const handleSave = (path, newValue) => { |
| 48 | + let updatedValue |
| 49 | +
|
| 50 | + if (isComplex(valueObject.value)) { |
| 51 | + updatedValue = setNestedValue(valueObject.value, path, newValue) |
16 | 52 | } else { |
17 | | - updateDataAttribute(`[data-hotwire-dev-tools-uuid="${selected.uuid}"]`, dataAttribute, newValue) |
| 53 | + // For primitive values at root level |
| 54 | + if (valueObject.type === "Number") { |
| 55 | + updatedValue = Number(newValue) |
| 56 | + } else if (valueObject.type === "Boolean") { |
| 57 | + updatedValue = newValue === "true" || newValue === true |
| 58 | + } else { |
| 59 | + updatedValue = newValue |
| 60 | + } |
18 | 61 | } |
19 | | - editingStates[key || "main"] = false |
| 62 | +
|
| 63 | + const serializedValue = serializeValue(updatedValue, valueObject.type) |
| 64 | +
|
| 65 | + updateDataAttribute(`[data-hotwire-dev-tools-uuid="${selected.uuid}"]`, dataAttribute, serializedValue) |
| 66 | +
|
| 67 | + editingStates[path.join(".")] = false |
20 | 68 | } |
21 | 69 |
|
22 | | - const handleEdit = (key) => { |
23 | | - editingStates[key || "main"] = true |
| 70 | + const handleEdit = (path) => { |
| 71 | + editingStates[path.join(".")] = true |
24 | 72 | } |
25 | 73 |
|
26 | | - const handleCancel = (key) => { |
27 | | - editingStates[key || "main"] = false |
| 74 | + const handleCancel = (path) => { |
| 75 | + editingStates[path.join(".")] = false |
28 | 76 | } |
29 | 77 | </script> |
30 | 78 |
|
31 | 79 | <div class="d-flex gap-2 mb-2"> |
32 | 80 | <wa-tree> |
33 | 81 | <wa-tree-item expanded> |
34 | | - {#if isObject} |
| 82 | + {#if isComplex(valueObject.value)} |
35 | 83 | {valueObject.name} |
36 | | - {#each Object.entries(valueObject.value) as [key, value]} |
37 | | - <wa-tree-item> |
38 | | - <div class="d-flex code-value"> |
39 | | - <span class="code-key">{key}:</span> |
40 | | - <ValueEditor {value} valueType={"string"} isEditing={editingStates[key] || false} onEdit={() => handleEdit(key)} onSave={(newVal) => handleSave(key, newVal)} onCancel={() => handleCancel(key)} /> |
41 | | - </div> |
42 | | - </wa-tree-item> |
43 | | - {/each} |
| 84 | + {#if isArray(valueObject.value)} |
| 85 | + <span class="text-muted ms-1">Array ({valueObject.value.length})</span> |
| 86 | + {/if} |
| 87 | + <NestedValue data={valueObject.value} {editingStates} onEdit={handleEdit} onSave={handleSave} onCancel={handleCancel} /> |
44 | 88 | {:else} |
45 | 89 | <span class="code-key">{valueObject.name}:</span> |
46 | 90 | <div class="d-flex code-value"> |
47 | 91 | <ValueEditor |
48 | | - value={valueObject.value} |
| 92 | + value={String(valueObject.value)} |
49 | 93 | valueType={valueObject.type} |
50 | | - isEditing={editingStates["main"] || false} |
51 | | - onEdit={() => handleEdit(null)} |
52 | | - onSave={(newVal) => handleSave(null, newVal)} |
53 | | - onCancel={() => handleCancel(null)} |
| 94 | + isEditing={editingStates["root"] || false} |
| 95 | + onEdit={() => handleEdit(["root"])} |
| 96 | + onSave={(newVal) => handleSave(["root"], newVal)} |
| 97 | + onCancel={() => handleCancel(["root"])} |
54 | 98 | /> |
55 | 99 | </div> |
56 | 100 | {/if} |
|
69 | 113 | <CopyButton value={dataAttribute} /> |
70 | 114 | </div> |
71 | 115 | <div class="d-flex justify-content-between align-items-center"> |
72 | | - <span>{`this.${valueObject.name}`}</span> |
73 | | - <CopyButton value={`this.${valueObject.name}`} /> |
74 | | - </div> |
75 | | - <div class="d-flex justify-content-between align-items-center"> |
76 | | - <span>{`this.${valueObject.name}s`}</span> |
77 | | - <CopyButton value={`this.${valueObject.name}s`} /> |
| 116 | + <span>{`this.${valueObject.name}Value`}</span> |
| 117 | + <CopyButton value={`this.${valueObject.name}Value`} /> |
78 | 118 | </div> |
79 | 119 | <div class="d-flex justify-content-between align-items-center"> |
80 | | - <span>{`this.has${valueObject.name[0].toUpperCase() + valueObject.name.slice(1)}`}</span> |
81 | | - <CopyButton value={`this.has${valueObject.name[0].toUpperCase() + valueObject.name.slice(1)}`} /> |
| 120 | + <span>{`this.has${valueObject.name[0].toUpperCase() + valueObject.name.slice(1)}Value`}</span> |
| 121 | + <CopyButton value={`this.has${valueObject.name[0].toUpperCase() + valueObject.name.slice(1)}Value`} /> |
82 | 122 | </div> |
83 | 123 | </div> |
84 | 124 | </wa-tooltip> |
|
0 commit comments