@mongoosejs/studio 0.1.16 → 0.1.17
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/backend/actions/Model/updateDocument.js +3 -0
- package/backend/actions/Model/updateDocuments.js +3 -0
- package/frontend/public/app.js +684 -447
- package/frontend/public/tw.css +99 -19
- package/frontend/src/array-utils.js +66 -0
- package/frontend/src/dashboard/dashboard.js +1 -1
- package/frontend/src/detail-array/detail-array.html +25 -3
- package/frontend/src/detail-array/detail-array.js +22 -6
- package/frontend/src/document-details/document-details.html +61 -12
- package/frontend/src/document-details/document-details.js +28 -0
- package/frontend/src/document-details/document-property/document-property.html +41 -3
- package/frontend/src/document-details/document-property/document-property.js +47 -2
- package/frontend/src/edit-array/edit-array.html +5 -2
- package/frontend/src/edit-array/edit-array.js +79 -23
- package/frontend/src/index.js +2 -1
- package/frontend/src/list-array/list-array.html +1 -1
- package/frontend/src/list-array/list-array.js +0 -2
- package/frontend/src/models/models.html +6 -1
- package/frontend/src/models/models.js +32 -0
- package/package.json +1 -1
- package/frontend/src/edit-array/edit-array.css +0 -3
- package/frontend/src/list-array/list-array.css +0 -8
package/frontend/public/tw.css
CHANGED
|
@@ -631,6 +631,14 @@ video {
|
|
|
631
631
|
bottom: 0px;
|
|
632
632
|
}
|
|
633
633
|
|
|
634
|
+
.-left-2 {
|
|
635
|
+
left: -0.5rem;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
.-left-3 {
|
|
639
|
+
left: -0.75rem;
|
|
640
|
+
}
|
|
641
|
+
|
|
634
642
|
.left-0 {
|
|
635
643
|
left: 0px;
|
|
636
644
|
}
|
|
@@ -659,6 +667,10 @@ video {
|
|
|
659
667
|
top: 0.25rem;
|
|
660
668
|
}
|
|
661
669
|
|
|
670
|
+
.top-1\/2 {
|
|
671
|
+
top: 50%;
|
|
672
|
+
}
|
|
673
|
+
|
|
662
674
|
.top-2 {
|
|
663
675
|
top: 0.5rem;
|
|
664
676
|
}
|
|
@@ -769,6 +781,10 @@ video {
|
|
|
769
781
|
margin-bottom: 0.25rem;
|
|
770
782
|
}
|
|
771
783
|
|
|
784
|
+
.mb-1\.5 {
|
|
785
|
+
margin-bottom: 0.375rem;
|
|
786
|
+
}
|
|
787
|
+
|
|
772
788
|
.mb-2 {
|
|
773
789
|
margin-bottom: 0.5rem;
|
|
774
790
|
}
|
|
@@ -984,6 +1000,10 @@ video {
|
|
|
984
1000
|
max-height: 50vh;
|
|
985
1001
|
}
|
|
986
1002
|
|
|
1003
|
+
.max-h-\[6\.5em\] {
|
|
1004
|
+
max-height: 6.5em;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
987
1007
|
.min-h-0 {
|
|
988
1008
|
min-height: 0px;
|
|
989
1009
|
}
|
|
@@ -1072,6 +1092,10 @@ video {
|
|
|
1072
1092
|
min-width: 140px;
|
|
1073
1093
|
}
|
|
1074
1094
|
|
|
1095
|
+
.min-w-\[80px\] {
|
|
1096
|
+
min-width: 80px;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1075
1099
|
.min-w-full {
|
|
1076
1100
|
min-width: 100%;
|
|
1077
1101
|
}
|
|
@@ -1080,6 +1104,10 @@ video {
|
|
|
1080
1104
|
max-width: 64rem;
|
|
1081
1105
|
}
|
|
1082
1106
|
|
|
1107
|
+
.max-w-\[30em\] {
|
|
1108
|
+
max-width: 30em;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1083
1111
|
.max-w-\[calc\(100vw-3rem\)\] {
|
|
1084
1112
|
max-width: calc(100vw - 3rem);
|
|
1085
1113
|
}
|
|
@@ -1128,6 +1156,11 @@ video {
|
|
|
1128
1156
|
transform-origin: top right;
|
|
1129
1157
|
}
|
|
1130
1158
|
|
|
1159
|
+
.-translate-y-1\/2 {
|
|
1160
|
+
--tw-translate-y: -50%;
|
|
1161
|
+
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1131
1164
|
.translate-x-0 {
|
|
1132
1165
|
--tw-translate-x: 0px;
|
|
1133
1166
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
@@ -1454,6 +1487,10 @@ video {
|
|
|
1454
1487
|
border-bottom-width: 2px;
|
|
1455
1488
|
}
|
|
1456
1489
|
|
|
1490
|
+
.border-l-\[3px\] {
|
|
1491
|
+
border-left-width: 3px;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1457
1494
|
.border-r {
|
|
1458
1495
|
border-right-width: 1px;
|
|
1459
1496
|
}
|
|
@@ -1470,11 +1507,6 @@ video {
|
|
|
1470
1507
|
border-style: none;
|
|
1471
1508
|
}
|
|
1472
1509
|
|
|
1473
|
-
.border-amber-400 {
|
|
1474
|
-
--tw-border-opacity: 1;
|
|
1475
|
-
border-color: rgb(251 191 36 / var(--tw-border-opacity));
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
1510
|
.border-blue-300 {
|
|
1479
1511
|
--tw-border-opacity: 1;
|
|
1480
1512
|
border-color: rgb(147 197 253 / var(--tw-border-opacity));
|
|
@@ -1514,11 +1546,21 @@ video {
|
|
|
1514
1546
|
border-color: rgb(63 83 255 / var(--tw-border-opacity));
|
|
1515
1547
|
}
|
|
1516
1548
|
|
|
1549
|
+
.border-l-blue-500 {
|
|
1550
|
+
--tw-border-opacity: 1;
|
|
1551
|
+
border-left-color: rgb(59 130 246 / var(--tw-border-opacity));
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1517
1554
|
.bg-amber-100 {
|
|
1518
1555
|
--tw-bg-opacity: 1;
|
|
1519
1556
|
background-color: rgb(254 243 199 / var(--tw-bg-opacity));
|
|
1520
1557
|
}
|
|
1521
1558
|
|
|
1559
|
+
.bg-amber-200 {
|
|
1560
|
+
--tw-bg-opacity: 1;
|
|
1561
|
+
background-color: rgb(253 230 138 / var(--tw-bg-opacity));
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1522
1564
|
.bg-black {
|
|
1523
1565
|
--tw-bg-opacity: 1;
|
|
1524
1566
|
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
|
@@ -1644,11 +1686,6 @@ video {
|
|
|
1644
1686
|
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
|
|
1645
1687
|
}
|
|
1646
1688
|
|
|
1647
|
-
.bg-slate-50 {
|
|
1648
|
-
--tw-bg-opacity: 1;
|
|
1649
|
-
background-color: rgb(248 250 252 / var(--tw-bg-opacity));
|
|
1650
|
-
}
|
|
1651
|
-
|
|
1652
1689
|
.bg-slate-500 {
|
|
1653
1690
|
--tw-bg-opacity: 1;
|
|
1654
1691
|
background-color: rgb(100 116 139 / var(--tw-bg-opacity));
|
|
@@ -1776,6 +1813,11 @@ video {
|
|
|
1776
1813
|
padding-bottom: 0.5rem;
|
|
1777
1814
|
}
|
|
1778
1815
|
|
|
1816
|
+
.py-2\.5 {
|
|
1817
|
+
padding-top: 0.625rem;
|
|
1818
|
+
padding-bottom: 0.625rem;
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1779
1821
|
.py-3 {
|
|
1780
1822
|
padding-top: 0.75rem;
|
|
1781
1823
|
padding-bottom: 0.75rem;
|
|
@@ -1890,6 +1932,10 @@ video {
|
|
|
1890
1932
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
1891
1933
|
}
|
|
1892
1934
|
|
|
1935
|
+
.text-\[10px\] {
|
|
1936
|
+
font-size: 10px;
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1893
1939
|
.text-base {
|
|
1894
1940
|
font-size: 1rem;
|
|
1895
1941
|
line-height: 1.5rem;
|
|
@@ -2143,6 +2189,10 @@ video {
|
|
|
2143
2189
|
opacity: 0.5;
|
|
2144
2190
|
}
|
|
2145
2191
|
|
|
2192
|
+
.opacity-70 {
|
|
2193
|
+
opacity: 0.7;
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2146
2196
|
.opacity-75 {
|
|
2147
2197
|
opacity: 0.75;
|
|
2148
2198
|
}
|
|
@@ -2206,11 +2256,6 @@ video {
|
|
|
2206
2256
|
--tw-ring-inset: inset;
|
|
2207
2257
|
}
|
|
2208
2258
|
|
|
2209
|
-
.ring-amber-200 {
|
|
2210
|
-
--tw-ring-opacity: 1;
|
|
2211
|
-
--tw-ring-color: rgb(253 230 138 / var(--tw-ring-opacity));
|
|
2212
|
-
}
|
|
2213
|
-
|
|
2214
2259
|
.ring-black {
|
|
2215
2260
|
--tw-ring-opacity: 1;
|
|
2216
2261
|
--tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity));
|
|
@@ -2241,10 +2286,6 @@ video {
|
|
|
2241
2286
|
--tw-ring-opacity: 0.05;
|
|
2242
2287
|
}
|
|
2243
2288
|
|
|
2244
|
-
.ring-offset-1 {
|
|
2245
|
-
--tw-ring-offset-width: 1px;
|
|
2246
|
-
}
|
|
2247
|
-
|
|
2248
2289
|
.filter {
|
|
2249
2290
|
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
|
2250
2291
|
}
|
|
@@ -2307,6 +2348,10 @@ video {
|
|
|
2307
2348
|
color: rgb(156 163 175 / var(--tw-text-opacity));
|
|
2308
2349
|
}
|
|
2309
2350
|
|
|
2351
|
+
.last\:mb-0:last-child {
|
|
2352
|
+
margin-bottom: 0px;
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2310
2355
|
.focus-within\:ring-2:focus-within {
|
|
2311
2356
|
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
|
2312
2357
|
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
|
@@ -2322,6 +2367,16 @@ video {
|
|
|
2322
2367
|
--tw-ring-color: rgb(0 168 165 / var(--tw-ring-opacity));
|
|
2323
2368
|
}
|
|
2324
2369
|
|
|
2370
|
+
.hover\:-translate-y-0:hover {
|
|
2371
|
+
--tw-translate-y: -0px;
|
|
2372
|
+
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
.hover\:-translate-y-0\.5:hover {
|
|
2376
|
+
--tw-translate-y: -0.125rem;
|
|
2377
|
+
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2325
2380
|
.hover\:translate-x-0:hover {
|
|
2326
2381
|
--tw-translate-x: 0px;
|
|
2327
2382
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
@@ -2337,6 +2392,16 @@ video {
|
|
|
2337
2392
|
border-color: rgb(209 213 219 / var(--tw-border-opacity));
|
|
2338
2393
|
}
|
|
2339
2394
|
|
|
2395
|
+
.hover\:border-l-blue-600:hover {
|
|
2396
|
+
--tw-border-opacity: 1;
|
|
2397
|
+
border-left-color: rgb(37 99 235 / var(--tw-border-opacity));
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
.hover\:bg-amber-200:hover {
|
|
2401
|
+
--tw-bg-opacity: 1;
|
|
2402
|
+
background-color: rgb(253 230 138 / var(--tw-bg-opacity));
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2340
2405
|
.hover\:bg-blue-600:hover {
|
|
2341
2406
|
--tw-bg-opacity: 1;
|
|
2342
2407
|
background-color: rgb(37 99 235 / var(--tw-bg-opacity));
|
|
@@ -2442,6 +2507,11 @@ video {
|
|
|
2442
2507
|
background-color: rgb(148 163 184 / var(--tw-bg-opacity));
|
|
2443
2508
|
}
|
|
2444
2509
|
|
|
2510
|
+
.hover\:bg-slate-50:hover {
|
|
2511
|
+
--tw-bg-opacity: 1;
|
|
2512
|
+
background-color: rgb(248 250 252 / var(--tw-bg-opacity));
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2445
2515
|
.hover\:bg-slate-500:hover {
|
|
2446
2516
|
--tw-bg-opacity: 1;
|
|
2447
2517
|
background-color: rgb(100 116 139 / var(--tw-bg-opacity));
|
|
@@ -2502,6 +2572,16 @@ video {
|
|
|
2502
2572
|
color: rgb(6 14 172 / var(--tw-text-opacity));
|
|
2503
2573
|
}
|
|
2504
2574
|
|
|
2575
|
+
.hover\:opacity-100:hover {
|
|
2576
|
+
opacity: 1;
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
.hover\:shadow-lg:hover {
|
|
2580
|
+
--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
|
2581
|
+
--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
|
|
2582
|
+
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2505
2585
|
.focus\:z-10:focus {
|
|
2506
2586
|
z-index: 10;
|
|
2507
2587
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { inspect } = require('node-inspect-extracted');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Format a value for display in array views
|
|
7
|
+
* @param {*} item - The item to format
|
|
8
|
+
* @returns {string} - Formatted string representation
|
|
9
|
+
*/
|
|
10
|
+
function formatValue(item) {
|
|
11
|
+
if (item == null) {
|
|
12
|
+
return 'null';
|
|
13
|
+
}
|
|
14
|
+
if (typeof item === 'object') {
|
|
15
|
+
return inspect(item, { maxArrayLength: 50 });
|
|
16
|
+
}
|
|
17
|
+
return String(item);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check if an item is a plain object (not array, not null)
|
|
22
|
+
* @param {*} item - The item to check
|
|
23
|
+
* @returns {boolean} - True if item is a plain object
|
|
24
|
+
*/
|
|
25
|
+
function isObjectItem(item) {
|
|
26
|
+
return item != null && typeof item === 'object' && !Array.isArray(item) && item.constructor === Object;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get the keys of an object item
|
|
31
|
+
* @param {*} item - The item to get keys from
|
|
32
|
+
* @returns {string[]} - Array of keys, or empty array if not an object
|
|
33
|
+
*/
|
|
34
|
+
function getItemKeys(item) {
|
|
35
|
+
if (!isObjectItem(item)) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
return Object.keys(item);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Format a specific value from an object item by key
|
|
43
|
+
* @param {*} item - The object item
|
|
44
|
+
* @param {string} key - The key to get the value for
|
|
45
|
+
* @returns {string} - Formatted string representation of the value
|
|
46
|
+
*/
|
|
47
|
+
function formatItemValue(item, key) {
|
|
48
|
+
const value = item[key];
|
|
49
|
+
if (value === null) {
|
|
50
|
+
return 'null';
|
|
51
|
+
}
|
|
52
|
+
if (value === undefined) {
|
|
53
|
+
return 'undefined';
|
|
54
|
+
}
|
|
55
|
+
if (typeof value === 'object') {
|
|
56
|
+
return inspect(value, { maxArrayLength: 50 });
|
|
57
|
+
}
|
|
58
|
+
return String(value);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = {
|
|
62
|
+
formatValue,
|
|
63
|
+
isObjectItem,
|
|
64
|
+
getItemKeys,
|
|
65
|
+
formatItemValue
|
|
66
|
+
};
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
-
<div class="
|
|
2
|
-
<
|
|
3
|
-
|
|
1
|
+
<div class="w-full">
|
|
2
|
+
<div v-if="!arrayValue || arrayValue.length === 0" class="text-gray-400 text-xs py-2 text-center">
|
|
3
|
+
Empty array
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<div v-else class="mt-2">
|
|
7
|
+
<div
|
|
8
|
+
v-for="(item, index) in arrayValue"
|
|
9
|
+
:key="index"
|
|
10
|
+
:title="'Index: ' + index"
|
|
11
|
+
class="mb-1.5 last:mb-0 py-2.5 px-3 pl-4 bg-transparent border-l-[3px] border-l-blue-500 rounded-none transition-all duration-200 cursor-pointer relative hover:bg-slate-50 hover:border-l-blue-600 hover:shadow-lg hover:-translate-y-0.5">
|
|
12
|
+
<div class="absolute -left-3 top-1/2 -translate-y-1/2 w-5 h-5 bg-blue-500 text-white rounded-full flex items-center justify-center text-[10px] font-semibold font-mono z-10 hover:bg-blue-600">{{ index >= 1000 ? '1k+' : index }}</div>
|
|
13
|
+
<div v-if="arrayUtils.isObjectItem(item)" class="flex flex-col gap-1 mt-1 px-2">
|
|
14
|
+
<div
|
|
15
|
+
v-for="key in arrayUtils.getItemKeys(item)"
|
|
16
|
+
:key="key"
|
|
17
|
+
class="flex items-start gap-2 text-xs font-mono">
|
|
18
|
+
<span class="font-semibold text-gray-600 flex-shrink-0 min-w-[80px]">{{ key }}:</span>
|
|
19
|
+
<span class="text-gray-800 break-words whitespace-pre-wrap flex-1">{{ arrayUtils.formatItemValue(item, key) }}</span>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
<div v-else class="text-xs py-1.5 px-2 font-mono text-gray-800 break-words whitespace-pre-wrap mt-1">{{ arrayUtils.formatValue(item) }}</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
@@ -1,20 +1,36 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const template = require('./detail-array.html');
|
|
4
|
-
const { inspect } = require('node-inspect-extracted');
|
|
5
4
|
|
|
6
5
|
module.exports = app => app.component('detail-array', {
|
|
7
6
|
template: template,
|
|
8
7
|
props: ['value'],
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
data() {
|
|
9
|
+
return {
|
|
10
|
+
arrayValue: []
|
|
11
|
+
};
|
|
12
|
+
},
|
|
13
|
+
methods: {
|
|
14
|
+
initializeArray() {
|
|
11
15
|
if (this.value == null) {
|
|
12
|
-
|
|
16
|
+
this.arrayValue = [];
|
|
17
|
+
} else if (Array.isArray(this.value)) {
|
|
18
|
+
this.arrayValue = this.value;
|
|
19
|
+
} else {
|
|
20
|
+
this.arrayValue = [];
|
|
13
21
|
}
|
|
14
|
-
return inspect(this.value, { maxArrayLength: 50 });
|
|
15
22
|
}
|
|
16
23
|
},
|
|
17
24
|
mounted() {
|
|
18
|
-
|
|
25
|
+
this.initializeArray();
|
|
26
|
+
},
|
|
27
|
+
watch: {
|
|
28
|
+
value: {
|
|
29
|
+
handler(newValue) {
|
|
30
|
+
this.initializeArray();
|
|
31
|
+
},
|
|
32
|
+
deep: true,
|
|
33
|
+
immediate: true
|
|
34
|
+
}
|
|
19
35
|
}
|
|
20
36
|
});
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
|
|
53
53
|
<!-- Fields View -->
|
|
54
54
|
<div v-if="viewMode === 'fields'">
|
|
55
|
-
<!-- Schema Paths -->
|
|
55
|
+
<!-- Matched Schema Paths (shown first when searching) -->
|
|
56
56
|
<div
|
|
57
|
-
v-for="path in
|
|
57
|
+
v-for="path in matchedSchemaPaths"
|
|
58
58
|
:key="path.path || path"
|
|
59
59
|
class="value"
|
|
60
60
|
>
|
|
@@ -64,25 +64,74 @@
|
|
|
64
64
|
:schemaPaths="schemaPaths"
|
|
65
65
|
:editting="editting"
|
|
66
66
|
:changes="changes"
|
|
67
|
-
:highlight="
|
|
67
|
+
:highlight="true"
|
|
68
68
|
:invalid="invalid"></document-property>
|
|
69
69
|
</div>
|
|
70
70
|
|
|
71
|
-
<!-- Virtual Fields -->
|
|
71
|
+
<!-- Matched Virtual Fields (shown after matched schema paths when searching) -->
|
|
72
72
|
<div
|
|
73
|
-
v-for="path in
|
|
73
|
+
v-for="path in matchedVirtuals"
|
|
74
74
|
:key="path.name"
|
|
75
|
-
class="border rounded-lg mb-2 transition-all duration-200 ease-in-out"
|
|
76
|
-
:class="[
|
|
77
|
-
isVirtualMatched(path)
|
|
78
|
-
? 'border-amber-400 ring-2 ring-amber-200 ring-offset-1'
|
|
79
|
-
: 'border-gray-200'
|
|
80
|
-
]"
|
|
75
|
+
class="border border-gray-200 rounded-lg mb-2 transition-all duration-200 ease-in-out mt-4"
|
|
81
76
|
>
|
|
82
77
|
<!-- Virtual Field Header (Always Visible) -->
|
|
83
78
|
<div
|
|
84
79
|
@click="toggleVirtualField(path.name)"
|
|
85
|
-
class="p-3 bg-
|
|
80
|
+
class="p-3 bg-amber-100 hover:bg-amber-200 cursor-pointer flex items-center justify-between border-b border-gray-200 transition-colors duration-200 ease-in-out"
|
|
81
|
+
>
|
|
82
|
+
<div class="flex items-center">
|
|
83
|
+
<svg
|
|
84
|
+
:class="isVirtualFieldCollapsed(path.name) ? 'rotate-0' : 'rotate-90'"
|
|
85
|
+
class="w-4 h-4 text-gray-500 mr-2 transition-transform duration-200"
|
|
86
|
+
fill="none"
|
|
87
|
+
stroke="currentColor"
|
|
88
|
+
viewBox="0 0 24 24"
|
|
89
|
+
>
|
|
90
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
91
|
+
</svg>
|
|
92
|
+
<span class="font-medium text-gray-900">{{path.name}}</span>
|
|
93
|
+
<span v-if="path.isVirtual" class="ml-2 text-sm text-purple-600">(virtual - {{getVirtualFieldType(path)}})</span>
|
|
94
|
+
<span v-else class="ml-2 text-sm text-blue-600">(user-added - {{getVirtualFieldType(path)}})</span>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<!-- Virtual Field Content (Collapsible) -->
|
|
99
|
+
<div v-if="!isVirtualFieldCollapsed(path.name)" class="p-3">
|
|
100
|
+
<div v-if="path.value == null" class="text-sky-800">
|
|
101
|
+
{{'' + path.value}}
|
|
102
|
+
</div>
|
|
103
|
+
<div v-else>
|
|
104
|
+
{{path.value}}
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<!-- Unmatched Schema Paths (shown after matched items when searching, or all when not searching) -->
|
|
110
|
+
<div
|
|
111
|
+
v-for="path in unmatchedSchemaPaths"
|
|
112
|
+
:key="path.path || path"
|
|
113
|
+
class="value"
|
|
114
|
+
>
|
|
115
|
+
<document-property
|
|
116
|
+
:path="path"
|
|
117
|
+
:document="document"
|
|
118
|
+
:schemaPaths="schemaPaths"
|
|
119
|
+
:editting="editting"
|
|
120
|
+
:changes="changes"
|
|
121
|
+
:highlight="false"
|
|
122
|
+
:invalid="invalid"></document-property>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<!-- Unmatched Virtual Fields (shown last) -->
|
|
126
|
+
<div
|
|
127
|
+
v-for="path in unmatchedVirtuals"
|
|
128
|
+
:key="path.name"
|
|
129
|
+
class="border border-gray-200 rounded-lg mb-2 transition-all duration-200 ease-in-out"
|
|
130
|
+
>
|
|
131
|
+
<!-- Virtual Field Header (Always Visible) -->
|
|
132
|
+
<div
|
|
133
|
+
@click="toggleVirtualField(path.name)"
|
|
134
|
+
class="p-3 bg-gray-50 hover:bg-gray-100 cursor-pointer flex items-center justify-between border-b border-gray-200 transition-colors duration-200 ease-in-out"
|
|
86
135
|
>
|
|
87
136
|
<div class="flex items-center">
|
|
88
137
|
<svg
|
|
@@ -145,6 +145,20 @@ module.exports = app => app.component('document-details', {
|
|
|
145
145
|
|
|
146
146
|
return matches.concat(nonMatches);
|
|
147
147
|
},
|
|
148
|
+
matchedSchemaPaths() {
|
|
149
|
+
if (!this.searchQuery.trim()) {
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
const query = this.searchQuery.toLowerCase();
|
|
153
|
+
return this.typeFilteredSchemaPaths.filter(path => path.path.toLowerCase().includes(query));
|
|
154
|
+
},
|
|
155
|
+
unmatchedSchemaPaths() {
|
|
156
|
+
if (!this.searchQuery.trim()) {
|
|
157
|
+
return this.typeFilteredSchemaPaths;
|
|
158
|
+
}
|
|
159
|
+
const query = this.searchQuery.toLowerCase();
|
|
160
|
+
return this.typeFilteredSchemaPaths.filter(path => !path.path.toLowerCase().includes(query));
|
|
161
|
+
},
|
|
148
162
|
typeFilteredVirtuals() {
|
|
149
163
|
let virtuals = this.virtuals;
|
|
150
164
|
|
|
@@ -178,6 +192,20 @@ module.exports = app => app.component('document-details', {
|
|
|
178
192
|
|
|
179
193
|
return matches.concat(nonMatches);
|
|
180
194
|
},
|
|
195
|
+
matchedVirtuals() {
|
|
196
|
+
if (!this.searchQuery.trim()) {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
const query = this.searchQuery.toLowerCase();
|
|
200
|
+
return this.typeFilteredVirtuals.filter(virtual => virtual.name.toLowerCase().includes(query));
|
|
201
|
+
},
|
|
202
|
+
unmatchedVirtuals() {
|
|
203
|
+
if (!this.searchQuery.trim()) {
|
|
204
|
+
return this.typeFilteredVirtuals;
|
|
205
|
+
}
|
|
206
|
+
const query = this.searchQuery.toLowerCase();
|
|
207
|
+
return this.typeFilteredVirtuals.filter(virtual => !virtual.name.toLowerCase().includes(query));
|
|
208
|
+
},
|
|
181
209
|
schemaSearchMatchSet() {
|
|
182
210
|
if (!this.searchQuery.trim()) {
|
|
183
211
|
return new Set();
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
<!-- Collapsible Header -->
|
|
3
3
|
<div
|
|
4
4
|
@click="toggleCollapse"
|
|
5
|
-
class="p-3
|
|
6
|
-
:class="{ 'bg-amber-100': highlight, 'bg-gray-50': !highlight }"
|
|
5
|
+
class="p-3 cursor-pointer flex items-center justify-between border-b border-gray-200 transition-colors duration-200 ease-in-out"
|
|
6
|
+
:class="{ 'bg-amber-100 hover:bg-amber-200': highlight, 'bg-gray-50 hover:bg-gray-100': !highlight }"
|
|
7
7
|
>
|
|
8
8
|
<div class="flex items-center" >
|
|
9
9
|
<svg
|
|
@@ -81,7 +81,43 @@
|
|
|
81
81
|
</div>
|
|
82
82
|
<div v-else>
|
|
83
83
|
<!-- Show truncated or full value based on needsTruncation and isValueExpanded -->
|
|
84
|
-
|
|
84
|
+
<!-- Special handling for truncated arrays -->
|
|
85
|
+
<div v-if="isArray && shouldShowTruncated" class="w-full">
|
|
86
|
+
<div class="mt-2">
|
|
87
|
+
<div
|
|
88
|
+
v-for="(item, index) in truncatedArrayItems"
|
|
89
|
+
:key="index"
|
|
90
|
+
class="mb-1.5 py-2.5 px-3 pl-4 bg-transparent border-l-[3px] border-l-blue-500 rounded-none transition-all duration-200 cursor-pointer relative hover:bg-slate-50 hover:border-l-blue-600">
|
|
91
|
+
<div class="absolute -left-2 top-1/2 -translate-y-1/2 w-5 h-5 bg-blue-500 text-white rounded-full flex items-center justify-center text-[10px] font-semibold font-mono z-10 hover:bg-blue-600">{{ index }}</div>
|
|
92
|
+
<div v-if="arrayUtils.isObjectItem(item)" class="flex flex-col gap-1 mt-1 px-2">
|
|
93
|
+
<div
|
|
94
|
+
v-for="key in arrayUtils.getItemKeys(item)"
|
|
95
|
+
:key="key"
|
|
96
|
+
class="flex items-start gap-2 text-xs font-mono">
|
|
97
|
+
<span class="font-semibold text-gray-600 flex-shrink-0 min-w-[80px]">{{ key }}:</span>
|
|
98
|
+
<span class="text-gray-800 break-words whitespace-pre-wrap flex-1">{{ arrayUtils.formatItemValue(item, key) }}</span>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
<div v-else class="text-xs py-1.5 px-2 font-mono text-gray-800 break-words whitespace-pre-wrap mt-1">{{ arrayUtils.formatValue(item) }}</div>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="mb-1.5 py-2.5 px-3 pl-4 bg-transparent border-none border-l-[3px] border-l-blue-500 rounded-none transition-all duration-200 cursor-pointer relative opacity-70 hover:opacity-100">
|
|
104
|
+
<div class="text-xs py-1.5 px-2 font-mono text-gray-500 italic break-words whitespace-pre-wrap mt-1">
|
|
105
|
+
... and {{ remainingArrayCount }} more item{{ remainingArrayCount !== 1 ? 's' : '' }}
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
<button
|
|
110
|
+
@click="toggleValueExpansion"
|
|
111
|
+
class="mt-2 text-blue-600 hover:text-blue-800 text-sm font-medium flex items-center gap-1 transform transition-all duration-200 ease-in-out hover:translate-x-0.5"
|
|
112
|
+
>
|
|
113
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
114
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
|
115
|
+
</svg>
|
|
116
|
+
Show all {{ arrayValue.length }} items
|
|
117
|
+
</button>
|
|
118
|
+
</div>
|
|
119
|
+
<!-- Non-array truncated view -->
|
|
120
|
+
<div v-else-if="shouldShowTruncated && !isArray" class="relative">
|
|
85
121
|
<div class="text-gray-700 whitespace-pre-wrap break-words font-mono text-sm">{{truncatedString}}</div>
|
|
86
122
|
<button
|
|
87
123
|
@click="toggleValueExpansion"
|
|
@@ -93,6 +129,7 @@
|
|
|
93
129
|
Show more ({{valueAsString.length}} characters)
|
|
94
130
|
</button>
|
|
95
131
|
</div>
|
|
132
|
+
<!-- Expanded view -->
|
|
96
133
|
<div v-else-if="needsTruncation && isValueExpanded" class="relative">
|
|
97
134
|
<component :is="getComponentForPath(path)" :value="getValueForPath(path.path)"></component>
|
|
98
135
|
<button
|
|
@@ -105,6 +142,7 @@
|
|
|
105
142
|
Show less
|
|
106
143
|
</button>
|
|
107
144
|
</div>
|
|
145
|
+
<!-- Full view (no truncation needed) -->
|
|
108
146
|
<div v-else>
|
|
109
147
|
<component :is="getComponentForPath(path)" :value="getValueForPath(path.path)"></component>
|
|
110
148
|
</div>
|