@vertesia/ui 0.76.0 → 0.78.0
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/lib/esm/core/components/SidePanel.js +1 -1
- package/lib/esm/core/components/SidePanel.js.map +1 -1
- package/lib/esm/core/hooks/PortalContainerProvider.js +42 -0
- package/lib/esm/core/hooks/PortalContainerProvider.js.map +1 -0
- package/lib/esm/core/hooks/index.js +1 -0
- package/lib/esm/core/hooks/index.js.map +1 -1
- package/lib/esm/features/store/objects/DocumentSearchResults.js +15 -1
- package/lib/esm/features/store/objects/DocumentSearchResults.js.map +1 -1
- package/lib/esm/features/store/objects/components/DocumentIcon.js +1 -1
- package/lib/esm/features/store/objects/components/DocumentIcon.js.map +1 -1
- package/lib/esm/features/store/objects/search/DocumentSearchContext.js +6 -3
- package/lib/esm/features/store/objects/search/DocumentSearchContext.js.map +1 -1
- package/lib/esm/shell/apps/AppProjectSelector.js +17 -6
- package/lib/esm/shell/apps/AppProjectSelector.js.map +1 -1
- package/lib/esm/shell/apps/index.js +1 -1
- package/lib/esm/shell/apps/index.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types/core/hooks/PortalContainerProvider.d.ts +7 -0
- package/lib/types/core/hooks/PortalContainerProvider.d.ts.map +1 -0
- package/lib/types/core/hooks/index.d.ts +1 -0
- package/lib/types/core/hooks/index.d.ts.map +1 -1
- package/lib/types/features/store/objects/DocumentSearchResults.d.ts.map +1 -1
- package/lib/types/features/store/objects/components/DocumentIcon.d.ts.map +1 -1
- package/lib/types/features/store/objects/search/DocumentSearchContext.d.ts.map +1 -1
- package/lib/types/shell/apps/AppProjectSelector.d.ts +1 -1
- package/lib/types/shell/apps/AppProjectSelector.d.ts.map +1 -1
- package/lib/types/shell/apps/index.d.ts +1 -1
- package/lib/types/shell/apps/index.d.ts.map +1 -1
- package/lib/vertesia-ui-core.js +1 -1
- package/lib/vertesia-ui-core.js.map +1 -1
- package/lib/vertesia-ui-features.js +1 -1
- package/lib/vertesia-ui-features.js.map +1 -1
- package/lib/vertesia-ui-shell.js +1 -1
- package/lib/vertesia-ui-shell.js.map +1 -1
- package/package.json +4 -4
- package/src/core/components/SidePanel.tsx +2 -2
- package/src/core/hooks/PortalContainerProvider.tsx +56 -0
- package/src/core/hooks/index.ts +1 -0
- package/src/features/store/objects/DocumentSearchResults.tsx +19 -2
- package/src/features/store/objects/components/DocumentIcon.tsx +7 -2
- package/src/features/store/objects/search/DocumentSearchContext.ts +8 -3
- package/src/shell/apps/AppProjectSelector.tsx +19 -8
- package/src/shell/apps/index.ts +1 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
const PortalContainerContext = React.createContext<HTMLElement | undefined>(undefined);
|
|
4
|
+
|
|
5
|
+
function findOrCreatePortalContainer(root: ShadowRoot | Document, id = "plugin-portal-container") {
|
|
6
|
+
// look only at direct children
|
|
7
|
+
for (const child of Array.from(root.children)) {
|
|
8
|
+
if (child instanceof HTMLElement && child.id === id) {
|
|
9
|
+
return child;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
// not found → create
|
|
13
|
+
const container = document.createElement("div");
|
|
14
|
+
container.id = id;
|
|
15
|
+
root.appendChild(container);
|
|
16
|
+
return container;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function PortalContainerProvider({
|
|
20
|
+
children,
|
|
21
|
+
id = "plugin-portal-container",
|
|
22
|
+
}: {
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
id?: string;
|
|
25
|
+
}) {
|
|
26
|
+
const ref = React.useRef<HTMLDivElement>(null);
|
|
27
|
+
const [container, setContainer] = React.useState<HTMLElement | null | undefined>(undefined);
|
|
28
|
+
|
|
29
|
+
React.useEffect(() => {
|
|
30
|
+
if (ref.current) {
|
|
31
|
+
const root = ref.current.getRootNode();
|
|
32
|
+
if (root instanceof ShadowRoot || root instanceof Document) {
|
|
33
|
+
const container = findOrCreatePortalContainer(root, id);
|
|
34
|
+
setContainer(container);
|
|
35
|
+
} else {
|
|
36
|
+
setContainer(null);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}, [id]);
|
|
40
|
+
|
|
41
|
+
// If container not discovered yet → render hidden marker only
|
|
42
|
+
if (container === undefined) {
|
|
43
|
+
return <div ref={ref} style={{ display: "none" }} />;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Once container is resolved (null or HTMLElement) → provide it
|
|
47
|
+
return (
|
|
48
|
+
<PortalContainerContext.Provider value={container || undefined}>
|
|
49
|
+
{children}
|
|
50
|
+
</PortalContainerContext.Provider>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function usePortalContainer() {
|
|
55
|
+
return React.useContext(PortalContainerContext);
|
|
56
|
+
}
|
package/src/core/hooks/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useRef, useState } from "react";
|
|
1
|
+
import { useRef, useState, useEffect } from "react";
|
|
2
2
|
import { ColumnLayout, ContentObject, ContentObjectItem, ComplexSearchQuery } from '@vertesia/common';
|
|
3
3
|
import {
|
|
4
4
|
|
|
@@ -12,6 +12,7 @@ import { useDocumentFilterGroups, useDocumentFilterHandler } from "../../facets/
|
|
|
12
12
|
import { VectorSearchWidget } from './components/VectorSearchWidget';
|
|
13
13
|
import { ContentDispositionButton } from './components/ContentDispositionButton';
|
|
14
14
|
import { DocumentTable } from './DocumentTable';
|
|
15
|
+
import { ExtendedColumnLayout } from './layout/DocumentTableColumn';
|
|
15
16
|
import { useDocumentSearch, useWatchDocumentSearchFacets, useWatchDocumentSearchResult } from './search/DocumentSearchContext';
|
|
16
17
|
import { useDocumentUploadHandler } from './upload/useUploadHandler';
|
|
17
18
|
import { ContentOverview } from './components/ContentOverview';
|
|
@@ -101,6 +102,21 @@ export function DocumentSearchResults({ layout, onUpload, allowFilter = true, al
|
|
|
101
102
|
const [filters, setFilters] = useState<BaseFilter[]>([]);
|
|
102
103
|
|
|
103
104
|
const loadMoreRef = useRef<HTMLDivElement>(null);
|
|
105
|
+
|
|
106
|
+
// Trigger initial search when component mounts
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (!isReady && objects.length === 0) {
|
|
109
|
+
// Manually set loading state to show spinner during initial load
|
|
110
|
+
search._updateRunningState(true);
|
|
111
|
+
search.search().then(() => {
|
|
112
|
+
setIsReady(true);
|
|
113
|
+
}).catch(err => {
|
|
114
|
+
console.error('Initial search failed:', err);
|
|
115
|
+
search._updateRunningState(false);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}, []);
|
|
119
|
+
|
|
104
120
|
useIntersectionObserver(loadMoreRef, () => {
|
|
105
121
|
if (isReady && objects.length > 0 && objects.length != loaded) {
|
|
106
122
|
setIsReady(false);
|
|
@@ -126,7 +142,8 @@ export function DocumentSearchResults({ layout, onUpload, allowFilter = true, al
|
|
|
126
142
|
{
|
|
127
143
|
name: "Search Score",
|
|
128
144
|
field: "score",
|
|
129
|
-
|
|
145
|
+
render: (item) => (item as any).score?.toFixed(4) || "0.0000"
|
|
146
|
+
} satisfies ExtendedColumnLayout,
|
|
130
147
|
];
|
|
131
148
|
setActualLayout(layout);
|
|
132
149
|
}
|
|
@@ -77,8 +77,8 @@ export function DocumentIcon({ selection, document, onSelectionChange, onRowClic
|
|
|
77
77
|
)
|
|
78
78
|
}
|
|
79
79
|
<Separator className='bg-gray-200 h-[2px]' />
|
|
80
|
-
<CardContent className="p-2 flex flex-col
|
|
81
|
-
<div className="flex flex-col">
|
|
80
|
+
<CardContent className="p-2 flex flex-col">
|
|
81
|
+
<div className="flex flex-col overflow-hidden">
|
|
82
82
|
<VTooltip
|
|
83
83
|
placement='top'
|
|
84
84
|
description={document.properties?.title ?? document.name}>
|
|
@@ -94,6 +94,11 @@ export function DocumentIcon({ selection, document, onSelectionChange, onRowClic
|
|
|
94
94
|
) : <p className="text-xs text-muted">{"\u2002"}</p>
|
|
95
95
|
}
|
|
96
96
|
</div>
|
|
97
|
+
{document.score && (
|
|
98
|
+
<div className="text-xs text-muted w-full flex justify-end">
|
|
99
|
+
Score: {(document.score).toFixed(4) ?? "-"}
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
97
102
|
</CardContent>
|
|
98
103
|
</Card>
|
|
99
104
|
)
|
|
@@ -107,9 +107,11 @@ export class DocumentSearch implements SearchInterface {
|
|
|
107
107
|
facets: includeFacets ? this.facetSpecs : undefined
|
|
108
108
|
};
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
const request = this.collectionId ?
|
|
111
111
|
this.client.collections.searchMembers(this.collectionId, payload)
|
|
112
112
|
: this.client.objects.search(payload);
|
|
113
|
+
|
|
114
|
+
return request;
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
_facetsRequest() {
|
|
@@ -126,7 +128,7 @@ export class DocumentSearch implements SearchInterface {
|
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
_search(loadMore = false, noFacets = false) {
|
|
129
|
-
if (this.isRunning) { // avoid searching when a search is pending
|
|
131
|
+
if (this.isRunning && loadMore) { // avoid searching when a search is pending, but allow initial search
|
|
130
132
|
return Promise.resolve(false);
|
|
131
133
|
}
|
|
132
134
|
this.result.value = {
|
|
@@ -165,7 +167,10 @@ export class DocumentSearch implements SearchInterface {
|
|
|
165
167
|
}
|
|
166
168
|
|
|
167
169
|
search(noFacets = false) {
|
|
168
|
-
if (
|
|
170
|
+
// Allow initial search even if isLoading is true (for initial page load)
|
|
171
|
+
if (this.isRunning && this.objects.length > 0) {
|
|
172
|
+
return Promise.resolve(false);
|
|
173
|
+
}
|
|
169
174
|
return this._search(false, noFacets);
|
|
170
175
|
}
|
|
171
176
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ProjectRef, RequireAtLeastOne } from "@vertesia/common";
|
|
2
2
|
import { SelectBox, useFetch } from "@vertesia/ui/core";
|
|
3
|
-
import { useUserSession } from "@vertesia/ui/session";
|
|
3
|
+
import { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY, useUserSession } from "@vertesia/ui/session";
|
|
4
4
|
import { useState } from "react";
|
|
5
5
|
|
|
6
6
|
interface AppProjectSelectorProps {
|
|
7
7
|
app: RequireAtLeastOne<{ id?: string, name?: string }, 'id' | 'name'>;
|
|
8
|
-
onChange
|
|
8
|
+
onChange?: (value: ProjectRef) => void | boolean;
|
|
9
9
|
placeholder?: string;
|
|
10
10
|
}
|
|
11
11
|
export function AppProjectSelector({ app, onChange, placeholder }: AppProjectSelectorProps) {
|
|
@@ -14,11 +14,23 @@ export function AppProjectSelector({ app, onChange, placeholder }: AppProjectSel
|
|
|
14
14
|
return client.apps.getAppInstallationProjects(app);
|
|
15
15
|
}, [app.id, app.name])
|
|
16
16
|
|
|
17
|
+
const _onChange = (project: ProjectRef) => {
|
|
18
|
+
if (onChange) {
|
|
19
|
+
if (!onChange(project)) {
|
|
20
|
+
// if onChange returns true then the defualt on change is called
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// default on change
|
|
25
|
+
localStorage.setItem(LastSelectedAccountId_KEY, project.account);
|
|
26
|
+
localStorage.setItem(LastSelectedProjectId_KEY + '-' + project.account, project.id);
|
|
27
|
+
window.location.reload();
|
|
28
|
+
}
|
|
29
|
+
|
|
17
30
|
if (error) {
|
|
18
31
|
return <span className='text-red-600'>Error: failed to fetch projects: {error.message}</span>
|
|
19
32
|
}
|
|
20
|
-
|
|
21
|
-
return <SelectProject placeholder={placeholder} initialValue={project?.id} projects={projects || []} onChange={onChange} />
|
|
33
|
+
return <SelectProject placeholder={placeholder} initialValue={project?.id} projects={projects || []} onChange={_onChange} />
|
|
22
34
|
}
|
|
23
35
|
|
|
24
36
|
interface SelectProjectProps {
|
|
@@ -28,17 +40,16 @@ interface SelectProjectProps {
|
|
|
28
40
|
placeholder?: string;
|
|
29
41
|
}
|
|
30
42
|
function SelectProject({ initialValue, projects, onChange, placeholder = "Select Project" }: Readonly<SelectProjectProps>) {
|
|
31
|
-
const [value, setValue] = useState<ProjectRef | undefined>(
|
|
32
|
-
return initialValue ? projects.find(p => p.id === initialValue) : undefined
|
|
33
|
-
});
|
|
43
|
+
const [value, setValue] = useState<ProjectRef | undefined>();
|
|
34
44
|
const _onChange = (value: ProjectRef) => {
|
|
35
45
|
setValue(value)
|
|
36
46
|
onChange(value)
|
|
37
47
|
}
|
|
48
|
+
let actualValue = !value && initialValue ? projects.find(p => p.id === initialValue) : value;
|
|
38
49
|
return (
|
|
39
50
|
<SelectBox
|
|
40
51
|
by="id"
|
|
41
|
-
value={
|
|
52
|
+
value={actualValue}
|
|
42
53
|
options={projects}
|
|
43
54
|
optionLabel={(option) => option.name}
|
|
44
55
|
placeholder={placeholder}
|
package/src/shell/apps/index.ts
CHANGED