@mongoosejs/studio 0.2.5 → 0.2.6

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.
@@ -1,9 +1,10 @@
1
- <div class="border border-gray-200 bg-white rounded-lg mb-2">
1
+ <div class="border border-gray-200 bg-white rounded-lg mb-2" style="overflow: visible;">
2
2
  <!-- Collapsible Header -->
3
3
  <div
4
4
  @click="toggleCollapse"
5
5
  class="p-1 cursor-pointer flex items-center justify-between border-b border-gray-200 transition-colors duration-200 ease-in-out"
6
6
  :class="{ 'bg-amber-100 hover:bg-amber-200': highlight, 'bg-slate-100 hover:bg-gray-100': !highlight }"
7
+ style="overflow: visible; position: relative;"
7
8
  >
8
9
  <div class="flex items-center" >
9
10
  <svg
@@ -17,6 +18,56 @@
17
18
  </svg>
18
19
  <span class="font-medium text-gray-900">{{path.path}}</span>
19
20
  <span class="ml-2 text-sm text-gray-500">({{(path.instance || 'unknown').toLowerCase()}})</span>
21
+ <div v-if="isGeoJsonGeometry" class="ml-3 inline-flex items-center gap-2">
22
+ <div class="inline-flex items-center rounded-full bg-gray-200 p-0.5 text-xs font-semibold">
23
+ <button
24
+ type="button"
25
+ class="rounded-full px-2.5 py-0.5 transition"
26
+ :class="detailViewMode === 'text' ? 'bg-blue-600 text-white shadow' : 'text-gray-700 hover:text-gray-900'"
27
+ :style="detailViewMode === 'text' ? 'color: white !important; background-color: #2563eb !important;' : ''"
28
+ @click.stop="setDetailViewMode('text')">
29
+ Text
30
+ </button>
31
+ <button
32
+ type="button"
33
+ class="rounded-full px-2.5 py-0.5 transition"
34
+ :class="detailViewMode === 'map' ? 'bg-blue-600 text-white shadow' : 'text-gray-700 hover:text-gray-900'"
35
+ :style="detailViewMode === 'map' ? 'color: white !important; background-color: #2563eb !important;' : ''"
36
+ @click.stop="setDetailViewMode('map')">
37
+ Map
38
+ </button>
39
+ </div>
40
+ <!-- Info icon with tooltip -->
41
+ <div v-if="editting" class="relative inline-block" style="z-index: 10002;" @mouseenter="showTooltip = true" @mouseleave="showTooltip = false" @click.stop>
42
+ <svg
43
+ ref="infoIcon"
44
+ class="w-6 h-6 text-gray-400 hover:text-gray-600 cursor-help"
45
+ fill="none"
46
+ stroke="currentColor"
47
+ viewBox="0 0 24 24"
48
+ >
49
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
50
+ </svg>
51
+ <div
52
+ v-show="showTooltip"
53
+ ref="tooltip"
54
+ class="absolute left-full top-0 ml-2 w-64 p-3 text-white text-xs rounded-lg shadow-xl"
55
+ style="z-index: 99999; pointer-events: none; white-space: normal; position: fixed; background-color: #111827;"
56
+ :style="getTooltipStyle()"
57
+ >
58
+ <div class="font-semibold mb-2">Map Controls:</div>
59
+ <div v-if="isGeoJsonPoint" class="space-y-1">
60
+ <div>• Drag pin to move location</div>
61
+ </div>
62
+ <div v-else-if="isGeoJsonPolygon" class="space-y-1">
63
+ <div>• Drag vertices to reshape polygon</div>
64
+ <div v-if="isMultiPolygon">• Right-click edge to add new vertex</div>
65
+ <div>• Right-click vertex to delete</div>
66
+ </div>
67
+ <div class="absolute top-2 -left-1 w-0 h-0 border-t-4 border-b-4 border-r-4 border-transparent border-r-gray-900"></div>
68
+ </div>
69
+ </div>
70
+ </div>
20
71
  </div>
21
72
  <div class="flex items-center gap-2">
22
73
  <button
@@ -69,7 +120,18 @@
69
120
 
70
121
  <!-- Field Content -->
71
122
  <div v-if="editting && path.path !== '_id'">
123
+ <!-- Use detail-default with map editing for GeoJSON geometries -->
124
+ <component
125
+ v-if="isGeoJsonGeometry"
126
+ :is="getComponentForPath(path)"
127
+ :value="getEditValueForPath(path)"
128
+ :view-mode="detailViewMode"
129
+ :on-change="handleInputChange"
130
+ >
131
+ </component>
132
+ <!-- Use standard edit components for other types -->
72
133
  <component
134
+ v-else
73
135
  :is="getEditComponentForPath(path)"
74
136
  :value="getEditValueForPath(path)"
75
137
  :format="dateType"
@@ -131,7 +193,10 @@
131
193
  </div>
132
194
  <!-- Expanded view -->
133
195
  <div v-else-if="needsTruncation && isValueExpanded" class="relative">
134
- <component :is="getComponentForPath(path)" :value="getValueForPath(path.path)"></component>
196
+ <component
197
+ :is="getComponentForPath(path)"
198
+ :value="getValueForPath(path.path)"
199
+ :view-mode="detailViewMode"></component>
135
200
  <button
136
201
  @click="toggleValueExpansion"
137
202
  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"
@@ -144,7 +209,10 @@
144
209
  </div>
145
210
  <!-- Full view (no truncation needed) -->
146
211
  <div v-else>
147
- <component :is="getComponentForPath(path)" :value="getValueForPath(path.path)"></component>
212
+ <component
213
+ :is="getComponentForPath(path)"
214
+ :value="getValueForPath(path.path)"
215
+ :view-mode="detailViewMode"></component>
148
216
  </div>
149
217
  </div>
150
218
  </div>
@@ -17,8 +17,10 @@ module.exports = app => app.component('document-property', {
17
17
  dateType: 'picker', // picker, iso
18
18
  isCollapsed: false, // Start uncollapsed by default
19
19
  isValueExpanded: false, // Track if the value is expanded
20
+ detailViewMode: 'text',
20
21
  copyButtonLabel: 'Copy',
21
- copyResetTimeoutId: null
22
+ copyResetTimeoutId: null,
23
+ showTooltip: false
22
24
  };
23
25
  },
24
26
  beforeDestroy() {
@@ -92,9 +94,58 @@ module.exports = app => app.component('document-property', {
92
94
  return this.arrayValue.length - 2;
93
95
  }
94
96
  return 0;
97
+ },
98
+ isGeoJsonGeometry() {
99
+ const value = this.getValueForPath(this.path.path);
100
+ return value != null
101
+ && typeof value === 'object'
102
+ && !Array.isArray(value)
103
+ && Object.prototype.hasOwnProperty.call(value, 'type')
104
+ && Object.prototype.hasOwnProperty.call(value, 'coordinates');
105
+ },
106
+ isGeoJsonPoint() {
107
+ const value = this.getValueForPath(this.path.path);
108
+ return this.isGeoJsonGeometry && value.type === 'Point';
109
+ },
110
+ isGeoJsonPolygon() {
111
+ const value = this.getValueForPath(this.path.path);
112
+ return this.isGeoJsonGeometry && (value.type === 'Polygon' || value.type === 'MultiPolygon');
113
+ },
114
+ isMultiPolygon() {
115
+ const value = this.getValueForPath(this.path.path);
116
+ return this.isGeoJsonGeometry && value.type === 'MultiPolygon';
117
+ }
118
+ },
119
+ watch: {
120
+ isGeoJsonGeometry(newValue) {
121
+ if (!newValue) {
122
+ this.detailViewMode = 'text';
123
+ } else if (this.editting) {
124
+ // Default to map view when editing GeoJSON
125
+ this.detailViewMode = 'map';
126
+ }
127
+ },
128
+ editting(newValue) {
129
+ // When entering edit mode for GeoJSON, default to map view
130
+ if (newValue && this.isGeoJsonGeometry) {
131
+ this.detailViewMode = 'map';
132
+ }
95
133
  }
96
134
  },
97
135
  methods: {
136
+ setDetailViewMode(mode) {
137
+ this.detailViewMode = mode;
138
+
139
+ // When switching to map view, expand the container and value so the map is visible
140
+ if (mode === 'map' && this.isGeoJsonGeometry) {
141
+ if (this.isCollapsed) {
142
+ this.isCollapsed = false;
143
+ }
144
+ if (this.needsTruncation && !this.isValueExpanded) {
145
+ this.isValueExpanded = true;
146
+ }
147
+ }
148
+ },
98
149
  handleInputChange(newValue) {
99
150
  const currentValue = this.getValueForPath(this.path.path);
100
151
 
@@ -165,6 +216,11 @@ module.exports = app => app.component('document-property', {
165
216
  if (!this.document) {
166
217
  return;
167
218
  }
219
+ // If there are unsaved changes for this path, use the changed value
220
+ if (Object.prototype.hasOwnProperty.call(this.changes, path)) {
221
+ return this.changes[path];
222
+ }
223
+ // Otherwise, use the document value
168
224
  const documentValue = mpath.get(path, this.document);
169
225
  return documentValue;
170
226
  },
@@ -184,6 +240,16 @@ module.exports = app => app.component('document-property', {
184
240
  this.copyResetTimeoutId = null;
185
241
  }, 5000);
186
242
  },
243
+ getTooltipStyle() {
244
+ if (!this.$refs.infoIcon || !this.showTooltip) {
245
+ return {};
246
+ }
247
+ const rect = this.$refs.infoIcon.getBoundingClientRect();
248
+ return {
249
+ left: (rect.right + 8) + 'px',
250
+ top: rect.top + 'px'
251
+ };
252
+ },
187
253
  copyPropertyValue() {
188
254
  const textToCopy = this.valueAsString;
189
255
  if (textToCopy == null) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mongoosejs/studio",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "A Mongoose-native MongoDB UI with schema-aware autocomplete, AI-assisted queries, and dashboards that understand your models - not just your data.",
5
5
  "homepage": "https://mongoosestudio.app/",
6
6
  "repository": {