@mongoosejs/studio 0.1.5 → 0.1.7

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.
@@ -2041,6 +2041,11 @@ video {
2041
2041
  color: rgb(2 132 199 / var(--tw-text-opacity));
2042
2042
  }
2043
2043
 
2044
+ .text-sky-700 {
2045
+ --tw-text-opacity: 1;
2046
+ color: rgb(3 105 161 / var(--tw-text-opacity));
2047
+ }
2048
+
2044
2049
  .text-sky-800 {
2045
2050
  --tw-text-opacity: 1;
2046
2051
  color: rgb(7 89 133 / var(--tw-text-opacity));
@@ -2086,6 +2091,18 @@ video {
2086
2091
  color: rgb(255 255 255 / var(--tw-text-opacity));
2087
2092
  }
2088
2093
 
2094
+ .underline {
2095
+ text-decoration-line: underline;
2096
+ }
2097
+
2098
+ .decoration-dotted {
2099
+ text-decoration-style: dotted;
2100
+ }
2101
+
2102
+ .underline-offset-2 {
2103
+ text-underline-offset: 2px;
2104
+ }
2105
+
2089
2106
  .accent-sky-600 {
2090
2107
  accent-color: #0284c7;
2091
2108
  }
@@ -2469,6 +2486,10 @@ video {
2469
2486
  border-color: transparent;
2470
2487
  }
2471
2488
 
2489
+ .focus\:opacity-100:focus {
2490
+ opacity: 1;
2491
+ }
2492
+
2472
2493
  .focus\:outline-none:focus {
2473
2494
  outline: 2px solid transparent;
2474
2495
  outline-offset: 2px;
@@ -44,6 +44,25 @@ module.exports = app => app.component('dashboard', {
44
44
  } finally {
45
45
  this.status = 'loaded';
46
46
  }
47
+ },
48
+ shouldEvaluateDashboard() {
49
+ if (this.dashboardResults.length === 0) {
50
+ return true;
51
+ }
52
+
53
+ const finishedEvaluatingAt = this.dashboardResults[0].finishedEvaluatingAt;
54
+ if (!finishedEvaluatingAt) {
55
+ return true;
56
+ }
57
+
58
+ const sixHoursAgo = Date.now() - 6 * 60 * 60 * 1000;
59
+ const finishedAt = new Date(finishedEvaluatingAt).getTime();
60
+
61
+ if (Number.isNaN(finishedAt)) {
62
+ return true;
63
+ }
64
+
65
+ return finishedAt < sixHoursAgo;
47
66
  }
48
67
  },
49
68
  computed: {
@@ -62,6 +81,10 @@ module.exports = app => app.component('dashboard', {
62
81
  this.title = this.dashboard.title;
63
82
  this.description = this.dashboard.description ?? '';
64
83
  this.dashboardResults = dashboardResults;
84
+ if (this.shouldEvaluateDashboard()) {
85
+ await this.evaluateDashboard();
86
+ return;
87
+ }
65
88
  this.status = 'loaded';
66
89
  }
67
90
  });
@@ -0,0 +1,118 @@
1
+ <div>
2
+ <div class="flex items-baseline whitespace-pre" :style="indentStyle">
3
+ <button
4
+ v-if="showToggle"
5
+ type="button"
6
+ class="w-4 h-4 mr-1 inline-flex items-center justify-center leading-none text-gray-500 hover:text-gray-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-slate-400 cursor-pointer"
7
+ @click.stop="handleToggle"
8
+ >
9
+ {{ isCollapsedNode ? '+' : '-' }}
10
+ </button>
11
+ <span v-else class="w-4 h-4 mr-1 inline-flex items-center justify-center invisible flex-shrink-0"></span>
12
+ <template v-if="hasKey">
13
+ <span class="text-blue-600">"{{ nodeKey }}"</span><span>: </span>
14
+ </template>
15
+ <template v-if="isComplex">
16
+ <template v-if="hasChildren">
17
+ <span>{{ openingBracket }}</span>
18
+ <span v-if="isCollapsedNode" class="mx-1">…</span>
19
+ <span v-if="isCollapsedNode">{{ closingBracket }}{{ comma }}</span>
20
+ </template>
21
+ <template v-else>
22
+ <span>{{ openingBracket }}{{ closingBracket }}{{ comma }}</span>
23
+ </template>
24
+ </template>
25
+ <template v-else>
26
+ <!--
27
+ If value is a string and overflows its container (i.e. goes over one line), show an ellipsis.
28
+ This is done via CSS ellipsis strategy.
29
+ -->
30
+ <span
31
+ v-if="shouldShowReferenceLink"
32
+ class="inline-flex items-baseline group"
33
+ >
34
+ <span
35
+ :class="[...valueClasses, 'underline', 'decoration-dotted', 'underline-offset-2']"
36
+ :style="typeof value === 'string'
37
+ ? {
38
+ display: 'inline-block',
39
+ maxWidth: '100%',
40
+ overflow: 'hidden',
41
+ textOverflow: 'ellipsis',
42
+ whiteSpace: 'nowrap',
43
+ verticalAlign: 'bottom'
44
+ }
45
+ : {}"
46
+ :title="typeof value === 'string' && $el && $el.scrollWidth > $el.clientWidth ? value : undefined"
47
+ >
48
+ {{ formattedValue }}
49
+ </span>
50
+ <span>
51
+ {{ comma }}
52
+ </span>
53
+ <a
54
+ href="#"
55
+ class="ml-1 text-sm text-sky-700 opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity"
56
+ @click.stop.prevent="goToReference(value)"
57
+ >
58
+ View Document
59
+ </a>
60
+ </span>
61
+ <span
62
+ v-else
63
+ :class="valueClasses"
64
+ :style="typeof value === 'string'
65
+ ? {
66
+ display: 'inline-block',
67
+ maxWidth: '100%',
68
+ overflow: 'hidden',
69
+ textOverflow: 'ellipsis',
70
+ whiteSpace: 'nowrap',
71
+ verticalAlign: 'bottom'
72
+ }
73
+ : {}"
74
+ :title="typeof value === 'string' && $el && $el.scrollWidth > $el.clientWidth ? value : undefined"
75
+ >
76
+ {{ formattedValue }}{{ comma }}
77
+ </span>
78
+ </template>
79
+ </div>
80
+ <template v-if="isComplex && hasChildren && !isCollapsedNode">
81
+ <json-node
82
+ v-for="child in children"
83
+ :key="child.path"
84
+ :node-key="child.displayKey"
85
+ :value="child.value"
86
+ :level="level + 1"
87
+ :is-last="child.isLast"
88
+ :path="child.path"
89
+ :toggle-collapse="toggleCollapse"
90
+ :is-collapsed="isCollapsed"
91
+ :create-child-path="createChildPath"
92
+ :indent-size="indentSize"
93
+ :max-top-level-fields="maxTopLevelFields"
94
+ :top-level-expanded="topLevelExpanded"
95
+ :expand-top-level="expandTopLevel"
96
+ :references="references"
97
+ ></json-node>
98
+ <div
99
+ v-if="hasHiddenRootChildren"
100
+ class="flex items-baseline whitespace-pre"
101
+ :style="indentStyle"
102
+ >
103
+ <span class="w-4 h-4 mr-1 inline-flex items-center justify-center invisible"></span>
104
+ <button
105
+ type="button"
106
+ class="text-xs inline-flex items-center gap-1 ml-4 text-slate-500 hover:text-slate-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-slate-400"
107
+ :title="hiddenChildrenTooltip"
108
+ @click.stop="handleExpandTopLevel"
109
+ >
110
+ <span aria-hidden="true">{{hiddenChildrenLabel}}…</span>
111
+ </button>
112
+ </div>
113
+ <div class="flex items-baseline whitespace-pre" :style="indentStyle">
114
+ <span class="w-4 h-4 mr-1 inline-flex items-center justify-center invisible"></span>
115
+ <span>{{ closingBracket }}{{ comma }}</span>
116
+ </div>
117
+ </template>
118
+ </div>
@@ -13,6 +13,7 @@
13
13
  :max-top-level-fields="maxTopLevelFields"
14
14
  :top-level-expanded="topLevelExpanded"
15
15
  :expand-top-level="expandTopLevel"
16
+ :references="references"
16
17
  ></json-node>
17
18
  </div>
18
19
  </div>
@@ -2,97 +2,19 @@
2
2
 
3
3
  const template = require('./list-json.html');
4
4
 
5
- const JsonNodeTemplate = `
6
- <div>
7
- <div class="flex items-baseline whitespace-pre" :style="indentStyle">
8
- <button
9
- v-if="showToggle"
10
- type="button"
11
- class="w-4 h-4 mr-1 inline-flex items-center justify-center leading-none text-gray-500 hover:text-gray-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-slate-400 cursor-pointer"
12
- @click.stop="handleToggle"
13
- >
14
- {{ isCollapsedNode ? '+' : '-' }}
15
- </button>
16
- <span v-else class="w-4 h-4 mr-1 inline-flex items-center justify-center invisible flex-shrink-0"></span>
17
- <template v-if="hasKey">
18
- <span class="text-blue-600">"{{ nodeKey }}"</span><span>: </span>
19
- </template>
20
- <template v-if="isComplex">
21
- <template v-if="hasChildren">
22
- <span>{{ openingBracket }}</span>
23
- <span v-if="isCollapsedNode" class="mx-1">…</span>
24
- <span v-if="isCollapsedNode">{{ closingBracket }}{{ comma }}</span>
25
- </template>
26
- <template v-else>
27
- <span>{{ openingBracket }}{{ closingBracket }}{{ comma }}</span>
28
- </template>
29
- </template>
30
- <template v-else>
31
- <!--
32
- If value is a string and overflows its container (i.e. goes over one line), show an ellipsis.
33
- This is done via CSS ellipsis strategy.
34
- -->
35
- <span
36
- :class="valueClasses"
37
- :style="typeof value === 'string'
38
- ? {
39
- display: 'inline-block',
40
- maxWidth: '100%',
41
- overflow: 'hidden',
42
- textOverflow: 'ellipsis',
43
- whiteSpace: 'nowrap',
44
- verticalAlign: 'bottom'
45
- }
46
- : {}"
47
- :title="typeof value === 'string' && $el && $el.scrollWidth > $el.clientWidth ? value : undefined"
48
- >
49
- {{ formattedValue }}{{ comma }}
50
- </span>
51
- </template>
52
- </div>
53
- <template v-if="isComplex && hasChildren && !isCollapsedNode">
54
- <json-node
55
- v-for="child in children"
56
- :key="child.path"
57
- :node-key="child.displayKey"
58
- :value="child.value"
59
- :level="level + 1"
60
- :is-last="child.isLast"
61
- :path="child.path"
62
- :toggle-collapse="toggleCollapse"
63
- :is-collapsed="isCollapsed"
64
- :create-child-path="createChildPath"
65
- :indent-size="indentSize"
66
- :max-top-level-fields="maxTopLevelFields"
67
- :top-level-expanded="topLevelExpanded"
68
- :expand-top-level="expandTopLevel"
69
- ></json-node>
70
- <div
71
- v-if="hasHiddenRootChildren"
72
- class="flex items-baseline whitespace-pre"
73
- :style="indentStyle"
74
- >
75
- <span class="w-4 h-4 mr-1 inline-flex items-center justify-center invisible"></span>
76
- <button
77
- type="button"
78
- class="text-xs inline-flex items-center gap-1 ml-4 text-slate-500 hover:text-slate-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-slate-400"
79
- :title="hiddenChildrenTooltip"
80
- @click.stop="handleExpandTopLevel"
81
- >
82
- <span aria-hidden="true">{{hiddenChildrenLabel}}…</span>
83
- </button>
84
- </div>
85
- <div class="flex items-baseline whitespace-pre" :style="indentStyle">
86
- <span class="w-4 h-4 mr-1 inline-flex items-center justify-center invisible"></span>
87
- <span>{{ closingBracket }}{{ comma }}</span>
88
- </div>
89
- </template>
90
- </div>
91
- `;
5
+ const JsonNodeTemplate = require('./json-node.html');
92
6
 
93
7
  module.exports = app => app.component('list-json', {
94
8
  template: template,
95
- props: ['value'],
9
+ props: {
10
+ value: {
11
+ required: true
12
+ },
13
+ references: {
14
+ type: Object,
15
+ default: () => ({})
16
+ }
17
+ },
96
18
  data() {
97
19
  return {
98
20
  collapsedMap: {},
@@ -196,6 +118,10 @@ module.exports = app => app.component('list-json', {
196
118
  expandTopLevel: {
197
119
  type: Function,
198
120
  default: null
121
+ },
122
+ references: {
123
+ type: Object,
124
+ default: () => ({})
199
125
  }
200
126
  },
201
127
  computed: {
@@ -334,6 +260,24 @@ module.exports = app => app.component('list-json', {
334
260
  },
335
261
  hiddenChildrenTooltip() {
336
262
  return this.hiddenChildrenLabel;
263
+ },
264
+ normalizedPath() {
265
+ if (typeof this.path !== 'string') {
266
+ return '';
267
+ }
268
+ return this.path
269
+ .replace(/^root\.?/, '')
270
+ .replace(/\[\d+\]/g, '')
271
+ .replace(/^\./, '');
272
+ },
273
+ referenceModel() {
274
+ if (!this.normalizedPath || !this.references) {
275
+ return null;
276
+ }
277
+ return this.references[this.normalizedPath] || null;
278
+ },
279
+ shouldShowReferenceLink() {
280
+ return Boolean(this.referenceModel) && typeof this.value === 'string';
337
281
  }
338
282
  },
339
283
  methods: {
@@ -358,6 +302,12 @@ module.exports = app => app.component('list-json', {
358
302
  if (this.isRoot && typeof this.expandTopLevel === 'function') {
359
303
  this.expandTopLevel();
360
304
  }
305
+ },
306
+ goToReference(id) {
307
+ if (!this.referenceModel) {
308
+ return;
309
+ }
310
+ this.$router.push({ path: `/model/${this.referenceModel}/document/${id}` });
361
311
  }
362
312
  }
363
313
  }
@@ -0,0 +1,23 @@
1
+ <form @submit.prevent="emitSearch" class="relative flex-grow m-0">
2
+ <input
3
+ ref="searchInput"
4
+ class="w-full font-mono rounded-md p-1 border border-gray-300 outline-gray-300 text-lg focus:ring-1 focus:ring-ultramarine-200 focus:ring-offset-0 focus:outline-none"
5
+ type="text"
6
+ placeholder="Filter"
7
+ v-model="searchText"
8
+ @click="initFilter"
9
+ @input="updateAutocomplete"
10
+ @keydown="handleKeyDown"
11
+ />
12
+ <ul v-if="autocompleteSuggestions.length" class="absolute z-[9999] bg-white border border-gray-300 rounded mt-1 w-full max-h-40 overflow-y-auto shadow">
13
+ <li
14
+ v-for="(suggestion, index) in autocompleteSuggestions"
15
+ :key="suggestion"
16
+ class="px-2 py-1 cursor-pointer"
17
+ :class="{ 'bg-ultramarine-100': index === autocompleteIndex }"
18
+ @mousedown.prevent="applySuggestion(index)"
19
+ >
20
+ {{ suggestion }}
21
+ </li>
22
+ </ul>
23
+ </form>
@@ -0,0 +1,227 @@
1
+ 'use strict';
2
+
3
+ const template = require('./document-search.html');
4
+ const { Trie } = require('../trie');
5
+
6
+ const QUERY_SELECTORS = [
7
+ '$eq',
8
+ '$ne',
9
+ '$gt',
10
+ '$gte',
11
+ '$lt',
12
+ '$lte',
13
+ '$in',
14
+ '$nin',
15
+ '$exists',
16
+ '$regex',
17
+ '$options',
18
+ '$text',
19
+ '$search',
20
+ '$and',
21
+ '$or',
22
+ '$nor',
23
+ '$not',
24
+ '$elemMatch',
25
+ '$size',
26
+ '$all',
27
+ '$type',
28
+ '$expr',
29
+ '$jsonSchema',
30
+ '$mod'
31
+ ];
32
+
33
+ module.exports = app => app.component('document-search', {
34
+ template,
35
+ props: {
36
+ value: {
37
+ type: String,
38
+ default: ''
39
+ },
40
+ schemaPaths: {
41
+ type: Array,
42
+ default: () => []
43
+ }
44
+ },
45
+ data() {
46
+ return {
47
+ autocompleteSuggestions: [],
48
+ autocompleteIndex: 0,
49
+ autocompleteTrie: null,
50
+ searchText: this.value || ''
51
+ };
52
+ },
53
+ watch: {
54
+ value(val) {
55
+ this.searchText = val || '';
56
+ },
57
+ schemaPaths: {
58
+ handler() {
59
+ this.buildAutocompleteTrie();
60
+ },
61
+ deep: true
62
+ }
63
+ },
64
+ created() {
65
+ this.buildAutocompleteTrie();
66
+ },
67
+ methods: {
68
+ emitSearch() {
69
+ this.$emit('input', this.searchText);
70
+ this.$emit('search', this.searchText);
71
+ },
72
+ buildAutocompleteTrie() {
73
+ this.autocompleteTrie = new Trie();
74
+ this.autocompleteTrie.bulkInsert(QUERY_SELECTORS, 5, 'operator');
75
+ if (Array.isArray(this.schemaPaths) && this.schemaPaths.length > 0) {
76
+ const paths = this.schemaPaths
77
+ .map(path => path?.path)
78
+ .filter(path => typeof path === 'string' && path.length > 0);
79
+ for (const path of this.schemaPaths) {
80
+ if (path.schema) {
81
+ paths.push(...Object.keys(path.schema).map(subpath => `${path.path}.${subpath}`));
82
+ }
83
+ }
84
+ this.autocompleteTrie.bulkInsert(paths, 10, 'fieldName');
85
+ }
86
+ },
87
+ initFilter(ev) {
88
+ if (!this.searchText) {
89
+ this.searchText = '{}';
90
+ this.$nextTick(() => {
91
+ ev.target.setSelectionRange(1, 1);
92
+ });
93
+ }
94
+ },
95
+ updateAutocomplete() {
96
+ const input = this.$refs.searchInput;
97
+ const cursorPos = input ? input.selectionStart : 0;
98
+ const before = this.searchText.slice(0, cursorPos);
99
+ const match = before.match(/(?:\{|,)\s*([^:\s]*)$/);
100
+ if (match && match[1]) {
101
+ const token = match[1];
102
+ const leadingQuoteMatch = token.match(/^["']/);
103
+ const trailingQuoteMatch = token.length > 1 && /["']$/.test(token)
104
+ ? token[token.length - 1]
105
+ : '';
106
+ const term = token
107
+ .replace(/^["']/, '')
108
+ .replace(trailingQuoteMatch ? new RegExp(`[${trailingQuoteMatch}]$`) : '', '')
109
+ .trim();
110
+ if (!term) {
111
+ this.autocompleteSuggestions = [];
112
+ return;
113
+ }
114
+
115
+ const colonMatch = before.match(/:\s*([^,\}\]]*)$/);
116
+ const role = colonMatch ? 'operator' : 'fieldName';
117
+
118
+ if (this.autocompleteTrie) {
119
+ const primarySuggestions = this.autocompleteTrie.getSuggestions(term, 10, role);
120
+ const suggestionsSet = new Set(primarySuggestions);
121
+ if (Array.isArray(this.schemaPaths) && this.schemaPaths.length > 0) {
122
+ for (const schemaPath of this.schemaPaths) {
123
+ const path = schemaPath?.path;
124
+ if (
125
+ typeof path === 'string' &&
126
+ path.startsWith(`${term}.`) &&
127
+ !suggestionsSet.has(path)
128
+ ) {
129
+ suggestionsSet.add(path);
130
+ if (suggestionsSet.size >= 10) {
131
+ break;
132
+ }
133
+ }
134
+ }
135
+ }
136
+ let suggestions = Array.from(suggestionsSet);
137
+ if (leadingQuoteMatch) {
138
+ const leadingQuote = leadingQuoteMatch[0];
139
+ suggestions = suggestions.map(suggestion => `${leadingQuote}${suggestion}`);
140
+ }
141
+ if (trailingQuoteMatch) {
142
+ suggestions = suggestions.map(suggestion =>
143
+ suggestion.endsWith(trailingQuoteMatch) ? suggestion : `${suggestion}${trailingQuoteMatch}`
144
+ );
145
+ }
146
+ this.autocompleteSuggestions = suggestions;
147
+ this.autocompleteIndex = 0;
148
+ return;
149
+ }
150
+ }
151
+ this.autocompleteSuggestions = [];
152
+ },
153
+ handleKeyDown(ev) {
154
+ if (this.autocompleteSuggestions.length === 0) {
155
+ return;
156
+ }
157
+ if (ev.key === 'Tab' || ev.key === 'Enter') {
158
+ ev.preventDefault();
159
+ this.applySuggestion(this.autocompleteIndex);
160
+ } else if (ev.key === 'ArrowDown') {
161
+ ev.preventDefault();
162
+ this.autocompleteIndex = (this.autocompleteIndex + 1) % this.autocompleteSuggestions.length;
163
+ } else if (ev.key === 'ArrowUp') {
164
+ ev.preventDefault();
165
+ this.autocompleteIndex = (this.autocompleteIndex + this.autocompleteSuggestions.length - 1) % this.autocompleteSuggestions.length;
166
+ }
167
+ },
168
+ applySuggestion(index) {
169
+ const suggestion = this.autocompleteSuggestions[index];
170
+ if (!suggestion) {
171
+ return;
172
+ }
173
+ const input = this.$refs.searchInput;
174
+ const cursorPos = input.selectionStart;
175
+ const before = this.searchText.slice(0, cursorPos);
176
+ const after = this.searchText.slice(cursorPos);
177
+ const match = before.match(/(?:\{|,)\s*([^:\s]*)$/);
178
+ const colonNeeded = !/^\s*:/.test(after);
179
+ if (!match) {
180
+ return;
181
+ }
182
+ const token = match[1];
183
+ const start = cursorPos - token.length;
184
+ let replacement = suggestion;
185
+ const leadingQuote = token.startsWith('"') || token.startsWith('\'') ? token[0] : '';
186
+ const trailingQuote = token.length > 1 && (token.endsWith('"') || token.endsWith('\'')) ? token[token.length - 1] : '';
187
+ if (leadingQuote && !replacement.startsWith(leadingQuote)) {
188
+ replacement = `${leadingQuote}${replacement}`;
189
+ }
190
+ if (trailingQuote && !replacement.endsWith(trailingQuote)) {
191
+ replacement = `${replacement}${trailingQuote}`;
192
+ }
193
+ // Only insert : if we know the user isn't entering in a nested path
194
+ if (colonNeeded && (!leadingQuote || trailingQuote)) {
195
+ replacement = `${replacement}:`;
196
+ }
197
+ this.searchText = this.searchText.slice(0, start) + replacement + after;
198
+ this.$nextTick(() => {
199
+ const pos = start + replacement.length;
200
+ input.setSelectionRange(pos, pos);
201
+ });
202
+ this.autocompleteSuggestions = [];
203
+ },
204
+ addPathFilter(path) {
205
+ if (this.searchText) {
206
+ if (this.searchText.endsWith('}')) {
207
+ this.searchText = this.searchText.slice(0, -1) + `, ${path}: }`;
208
+ } else {
209
+ this.searchText += `, ${path}: }`;
210
+ }
211
+
212
+ } else {
213
+ // If this.searchText is empty or undefined, initialize it with a new object
214
+ this.searchText = `{ ${path}: }`;
215
+ }
216
+
217
+
218
+ this.$nextTick(() => {
219
+ const input = this.$refs.searchInput;
220
+ const cursorIndex = this.searchText.lastIndexOf(':') + 2; // Move cursor after ": "
221
+
222
+ input.focus();
223
+ input.setSelectionRange(cursorIndex, cursorIndex);
224
+ });
225
+ }
226
+ }
227
+ });
@@ -37,12 +37,13 @@
37
37
  <div class="relative h-[42px] z-10">
38
38
  <div class="documents-menu">
39
39
  <div class="flex flex-row items-center w-full gap-2">
40
- <form @submit.prevent="search" class="relative flex-grow m-0">
41
- <input ref="searchInput" class="w-full font-mono rounded-md p-1 border border-gray-300 outline-gray-300 text-lg focus:ring-1 focus:ring-ultramarine-200 focus:ring-offset-0 focus:outline-none" type="text" placeholder="Filter" v-model="searchText" @click="initFilter" @input="updateAutocomplete" @keydown="handleKeyDown" />
42
- <ul v-if="autocompleteSuggestions.length" class="absolute z-[9999] bg-white border border-gray-300 rounded mt-1 w-full max-h-40 overflow-y-auto shadow">
43
- <li v-for="(suggestion, index) in autocompleteSuggestions" :key="suggestion" class="px-2 py-1 cursor-pointer" :class="{ 'bg-ultramarine-100': index === autocompleteIndex }" @mousedown.prevent="applySuggestion(index)">{{ suggestion }}</li>
44
- </ul>
45
- </form>
40
+ <document-search
41
+ ref="documentSearch"
42
+ :value="searchText"
43
+ :schema-paths="schemaPaths"
44
+ @search="search"
45
+ >
46
+ </document-search>
46
47
  <div>
47
48
  <span v-if="numDocuments == null">Loading ...</span>
48
49
  <span v-else-if="typeof numDocuments === 'number'">{{numDocuments === 1 ? numDocuments+ ' document' : numDocuments + ' documents'}}</span>
@@ -127,7 +128,7 @@
127
128
  </div>
128
129
  <table v-else-if="outputType === 'table'">
129
130
  <thead>
130
- <th v-for="path in filteredPaths" @click="clickFilter(path.path)" class="cursor-pointer">
131
+ <th v-for="path in filteredPaths" @click="addPathFilter(path.path)" class="cursor-pointer">
131
132
  {{path.path}}
132
133
  <span class="path-type">
133
134
  ({{(path.instance || 'unknown')}})
@@ -165,7 +166,7 @@
165
166
  >
166
167
  Open this Document
167
168
  </button>
168
- <list-json :value="filterDocument(document)">
169
+ <list-json :value="filterDocument(document)" :references="referenceMap">
169
170
  </list-json>
170
171
  </div>
171
172
  </div>