@vertesia/ui 0.79.3 → 0.79.4
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/MenuList.js +2 -5
- package/lib/esm/core/components/MenuList.js.map +1 -1
- package/lib/esm/core/components/shadcn/dialog.js +16 -2
- package/lib/esm/core/components/shadcn/dialog.js.map +1 -1
- package/lib/esm/core/components/shadcn/filters/filter/SelectFilter.js +6 -9
- package/lib/esm/core/components/shadcn/filters/filter/SelectFilter.js.map +1 -1
- package/lib/esm/core/components/shadcn/filters/filterBar.js +1 -1
- package/lib/esm/core/components/shadcn/filters/filterBar.js.map +1 -1
- package/lib/esm/core/components/shadcn/selectBox.js +1 -1
- package/lib/esm/core/components/shadcn/selectBox.js.map +1 -1
- package/lib/esm/env/index.js +4 -1
- package/lib/esm/env/index.js.map +1 -1
- package/lib/esm/features/facets/CollectionsFacetsNav.js +5 -1
- package/lib/esm/features/facets/CollectionsFacetsNav.js.map +1 -1
- package/lib/esm/features/layout/GenericPageNavHeader.js +5 -2
- package/lib/esm/features/layout/GenericPageNavHeader.js.map +1 -1
- package/lib/esm/features/store/collections/EditCollectionView.js +1 -1
- package/lib/esm/features/store/collections/EditCollectionView.js.map +1 -1
- package/lib/esm/features/store/objects/DocumentSearchResults.js +2 -1
- package/lib/esm/features/store/objects/DocumentSearchResults.js.map +1 -1
- package/lib/esm/router/HistoryNavigator.js +22 -2
- package/lib/esm/router/HistoryNavigator.js.map +1 -1
- package/lib/esm/shell/login/UserInfo.js +2 -1
- package/lib/esm/shell/login/UserInfo.js.map +1 -1
- package/lib/esm/shell/login/UserSessionMenu.js +7 -1
- package/lib/esm/shell/login/UserSessionMenu.js.map +1 -1
- package/lib/esm/widgets/form/Form.js +5 -1
- package/lib/esm/widgets/form/Form.js.map +1 -1
- package/lib/esm/widgets/schema-editor/ManagedSchema.js +0 -3
- package/lib/esm/widgets/schema-editor/ManagedSchema.js.map +1 -1
- package/lib/esm/widgets/schema-editor/json-schema4-utils.js +1 -1
- package/lib/esm/widgets/schema-editor/json-schema4-utils.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types/core/components/shadcn/dialog.d.ts +2 -1
- package/lib/types/core/components/shadcn/dialog.d.ts.map +1 -1
- package/lib/types/core/components/shadcn/filters/filterBar.d.ts.map +1 -1
- package/lib/types/core/components/shadcn/selectBox.d.ts.map +1 -1
- package/lib/types/env/index.d.ts +3 -1
- package/lib/types/env/index.d.ts.map +1 -1
- package/lib/types/features/facets/CollectionsFacetsNav.d.ts.map +1 -1
- package/lib/types/features/layout/GenericPageNavHeader.d.ts.map +1 -1
- package/lib/types/features/store/objects/DocumentSearchResults.d.ts.map +1 -1
- package/lib/types/router/HistoryNavigator.d.ts +3 -0
- package/lib/types/router/HistoryNavigator.d.ts.map +1 -1
- package/lib/types/shell/login/UserInfo.d.ts.map +1 -1
- package/lib/types/shell/login/UserSessionMenu.d.ts.map +1 -1
- package/lib/types/widgets/form/Form.d.ts.map +1 -1
- package/lib/types/widgets/schema-editor/ManagedSchema.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-env.js +1 -1
- package/lib/vertesia-ui-env.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-router.js +1 -1
- package/lib/vertesia-ui-router.js.map +1 -1
- package/lib/vertesia-ui-shell.js +1 -1
- package/lib/vertesia-ui-shell.js.map +1 -1
- package/lib/vertesia-ui-widgets.js +1 -1
- package/lib/vertesia-ui-widgets.js.map +1 -1
- package/package.json +7 -7
- package/src/core/components/MenuList.tsx +3 -6
- package/src/core/components/shadcn/dialog.tsx +19 -1
- package/src/core/components/shadcn/filters/filter/SelectFilter.tsx +31 -31
- package/src/core/components/shadcn/filters/filterBar.tsx +1 -0
- package/src/core/components/shadcn/selectBox.tsx +1 -0
- package/src/env/index.ts +7 -2
- package/src/features/facets/CollectionsFacetsNav.tsx +5 -1
- package/src/features/layout/GenericPageNavHeader.tsx +5 -2
- package/src/features/store/collections/EditCollectionView.tsx +2 -2
- package/src/features/store/objects/DocumentSearchResults.tsx +2 -1
- package/src/router/HistoryNavigator.ts +30 -2
- package/src/shell/login/UserInfo.tsx +2 -0
- package/src/shell/login/UserSessionMenu.tsx +12 -1
- package/src/widgets/form/Form.tsx +6 -1
- package/src/widgets/schema-editor/ManagedSchema.ts +0 -3
- package/src/widgets/schema-editor/json-schema4-utils.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertesia/ui",
|
|
3
|
-
"version": "0.79.
|
|
3
|
+
"version": "0.79.4",
|
|
4
4
|
"description": "Vertesia UI components and and hooks",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
"lucide-react": "^0.511.0",
|
|
51
51
|
"monaco-editor": "^0.52.2",
|
|
52
52
|
"motion": "^12.12.1",
|
|
53
|
-
"react": "^19.1.
|
|
53
|
+
"react": "^19.1.2",
|
|
54
54
|
"react-calendar": "^6.0.0",
|
|
55
55
|
"react-date-picker": "^11.0.0",
|
|
56
|
-
"react-dom": "^19.1.
|
|
56
|
+
"react-dom": "^19.1.2",
|
|
57
57
|
"react-error-boundary": "^6.0.0",
|
|
58
58
|
"react-markdown": "^10.1.0",
|
|
59
59
|
"react-resizable-panels": "^3.0.6",
|
|
@@ -61,9 +61,9 @@
|
|
|
61
61
|
"tailwind-merge": "^3.3.0",
|
|
62
62
|
"ts-md5": "^1.3.1",
|
|
63
63
|
"unist-util-visit": "^5.0.0",
|
|
64
|
-
"@vertesia/
|
|
65
|
-
"@vertesia/json": "0.79.
|
|
66
|
-
"@vertesia/
|
|
64
|
+
"@vertesia/common": "0.79.4",
|
|
65
|
+
"@vertesia/json": "0.79.4",
|
|
66
|
+
"@vertesia/client": "0.79.4"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@eslint/js": "^9.27.0",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"@types/json-schema": "^7.0.15",
|
|
74
74
|
"@types/lodash-es": "^4.17.12",
|
|
75
75
|
"@types/node": "^22.15.21",
|
|
76
|
-
"@types/react": "^19.1.
|
|
76
|
+
"@types/react": "^19.1.2",
|
|
77
77
|
"@types/react-dom": "^19.1.1",
|
|
78
78
|
"eslint": "^9.27.0",
|
|
79
79
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
@@ -7,7 +7,7 @@ interface MenuListProps {
|
|
|
7
7
|
}
|
|
8
8
|
export function MenuList({ className, children }: MenuListProps) {
|
|
9
9
|
return (
|
|
10
|
-
<ul className={`${className} space-y-1 flex flex-col items-start
|
|
10
|
+
<ul className={`${className} space-y-1 flex flex-col items-start`}>
|
|
11
11
|
{children}
|
|
12
12
|
</ul>
|
|
13
13
|
)
|
|
@@ -21,11 +21,8 @@ interface MenuListItemProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
|
21
21
|
const MenuListItem = forwardRef<HTMLAnchorElement, MenuListItemProps>(function _MenuListItem(props, ref) {
|
|
22
22
|
const { current, children, className, href = '#', onClick, ...others } = props;
|
|
23
23
|
return (
|
|
24
|
-
<li className={clsx(className, current ?
|
|
25
|
-
'
|
|
26
|
-
:
|
|
27
|
-
'text-gray-700 dark:dark:text-slate-300 hover:text-indigo-600 hover:bg-gray-50 dark:hover:bg-slate-800 dark:hover:text-slate-50 dark:border dark:border-transparent dark:hover:border-slate-50',
|
|
28
|
-
'w-full rounded-md p-2 pl-3 text-sm leading-6 font-semibold')}>
|
|
24
|
+
<li className={clsx(className, current ? 'bg-muted' : '',
|
|
25
|
+
'w-full p-2 pl-3 text-sm leading-6 font-semibold hover:bg-muted')}>
|
|
29
26
|
<a ref={ref} href={href} onClick={(e) => {
|
|
30
27
|
if (onClick) {
|
|
31
28
|
e.preventDefault();
|
|
@@ -16,6 +16,7 @@ interface ModalProps {
|
|
|
16
16
|
className?: string;
|
|
17
17
|
allowOverflow?: boolean;
|
|
18
18
|
disableCloseOnClickOutside?: boolean;
|
|
19
|
+
size?: "sm" | "md" | "lg" | "xl";
|
|
19
20
|
}
|
|
20
21
|
const ModalContext = createContext<boolean>(false)
|
|
21
22
|
export function useIsInModal() {
|
|
@@ -34,12 +35,27 @@ export function VModal({
|
|
|
34
35
|
noCloseButton = false,
|
|
35
36
|
allowOverflow = false,
|
|
36
37
|
disableCloseOnClickOutside = false,
|
|
38
|
+
size = "md",
|
|
37
39
|
}: ModalProps) {
|
|
38
40
|
const handleOpenChange = (open: boolean) => {
|
|
39
41
|
if (!open) {
|
|
40
42
|
onClose();
|
|
41
43
|
}
|
|
42
44
|
};
|
|
45
|
+
function getSizeClasses() {
|
|
46
|
+
switch (size) {
|
|
47
|
+
case "sm":
|
|
48
|
+
return "max-w-[20vw]";
|
|
49
|
+
case "md":
|
|
50
|
+
return "max-w-[40vw]";
|
|
51
|
+
case "lg":
|
|
52
|
+
return "max-w-[60vw]";
|
|
53
|
+
case "xl":
|
|
54
|
+
return "max-w-[80vw]";
|
|
55
|
+
default:
|
|
56
|
+
return "max-w-[40vw]";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
43
59
|
|
|
44
60
|
return (
|
|
45
61
|
<Dialog
|
|
@@ -49,6 +65,7 @@ export function VModal({
|
|
|
49
65
|
handleOpenChange(open);
|
|
50
66
|
}
|
|
51
67
|
}}
|
|
68
|
+
|
|
52
69
|
>
|
|
53
70
|
{allowOverflow && <DialogOverlay className="z-50 fixed inset-0 bg-black/80" />}
|
|
54
71
|
<VisuallyHidden>
|
|
@@ -57,7 +74,8 @@ export function VModal({
|
|
|
57
74
|
<DialogContent
|
|
58
75
|
className={cn(
|
|
59
76
|
"min-h-20 p-4",
|
|
60
|
-
"fixed left-[50%] top-[50%] z-50 grid w-full
|
|
77
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full translate-x-[-50%] translate-y-[-50%] border bg-background shadow-lg duration-200 sm:rounded-lg",
|
|
78
|
+
getSizeClasses(),
|
|
61
79
|
className
|
|
62
80
|
)}
|
|
63
81
|
>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { CommandItem
|
|
2
|
+
import { CommandItem } from "../../command";
|
|
3
3
|
import { Button } from "../../button";
|
|
4
4
|
import { Filter, FilterGroup, FilterGroupOption, FilterOption } from "../types";
|
|
5
5
|
import { DynamicLabel } from "../DynamicLabel";
|
|
@@ -52,12 +52,8 @@ export default function SelectFilter({
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
const options = getFilteredOptions(selectedView);
|
|
55
|
-
const selectedGroup = filterGroups.find(g => g.name === selectedView);
|
|
56
|
-
|
|
57
|
-
if (options.length === 0) {
|
|
58
|
-
return <CommandEmpty>No matching options</CommandEmpty>;
|
|
59
|
-
}
|
|
60
55
|
|
|
56
|
+
const selectedGroup = filterGroups.find(g => g.name === selectedView);
|
|
61
57
|
const groupTitle = selectedGroup?.placeholder || selectedGroup?.name;
|
|
62
58
|
|
|
63
59
|
const handleApply = () => {
|
|
@@ -117,35 +113,39 @@ export default function SelectFilter({
|
|
|
117
113
|
};
|
|
118
114
|
|
|
119
115
|
return (
|
|
120
|
-
|
|
121
|
-
<div className="flex items-center p-1.5 text-xs text-muted">
|
|
116
|
+
<div className="flex flex-col h-full">
|
|
117
|
+
<div className="flex items-center p-1.5 text-xs text-muted shrink-0">
|
|
122
118
|
<span>{groupTitle}</span>
|
|
123
119
|
</div>
|
|
124
|
-
<div className="
|
|
125
|
-
|
|
126
|
-
|
|
120
|
+
<div className="flex-1 overflow-hidden min-h-0">
|
|
121
|
+
<div className="max-h-[200px] overflow-y-auto">
|
|
122
|
+
{options.length > 0 && (
|
|
123
|
+
options.map((option: FilterGroupOption) => {
|
|
124
|
+
const isSelected = selectedOptions.some(opt => opt.value === option.value);
|
|
127
125
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
126
|
+
return (
|
|
127
|
+
<CommandItem
|
|
128
|
+
key={option.value || `option-${Math.random()}`}
|
|
129
|
+
className={`group flex gap-2 items-center w-full hover:bg-muted ${selectedGroup?.multiple && isSelected ? 'bg-muted' : ''
|
|
130
|
+
}`}
|
|
131
|
+
onSelect={() => handleOptionToggle(option)}
|
|
132
|
+
>
|
|
133
|
+
<DynamicLabel
|
|
134
|
+
value={option.value || ''}
|
|
135
|
+
labelRenderer={option.labelRenderer || selectedGroup?.labelRenderer}
|
|
136
|
+
fallbackLabel={option.label}
|
|
137
|
+
/>
|
|
138
|
+
{selectedGroup?.multiple && isSelected && (
|
|
139
|
+
<span className="ml-auto text-xs text-success">✓</span>
|
|
140
|
+
)}
|
|
141
|
+
</CommandItem>
|
|
142
|
+
);
|
|
143
|
+
})
|
|
144
|
+
)}
|
|
145
|
+
</div>
|
|
146
146
|
</div>
|
|
147
147
|
{selectedGroup?.multiple && (
|
|
148
|
-
<div className="p-2 border-t">
|
|
148
|
+
<div className="p-2 border-t shrink-0">
|
|
149
149
|
<div className="flex gap-2 justify-end">
|
|
150
150
|
<Button variant="ghost" size="sm" onClick={handleClose}>
|
|
151
151
|
Cancel
|
|
@@ -156,6 +156,6 @@ export default function SelectFilter({
|
|
|
156
156
|
</div>
|
|
157
157
|
</div>
|
|
158
158
|
)}
|
|
159
|
-
|
|
159
|
+
</div>
|
|
160
160
|
);
|
|
161
161
|
}
|
|
@@ -330,6 +330,7 @@ const FilterBtn = ({ className }: { className?: string }) => {
|
|
|
330
330
|
)
|
|
331
331
|
}
|
|
332
332
|
<CommandList>
|
|
333
|
+
<CommandEmpty>No matching filters</CommandEmpty>
|
|
333
334
|
<CommandGroup>
|
|
334
335
|
{!selectedView ? getAvailableFilterGroups() : renderFilterOptions()}
|
|
335
336
|
</CommandGroup>
|
|
@@ -275,6 +275,7 @@ export function VSelectBox<T = any>({ options, optionLabel, value, onChange, add
|
|
|
275
275
|
<div className="flex items-center gap-1 group">
|
|
276
276
|
{isClearable && value && (Array.isArray(value) ? value.length > 0 : true) && (
|
|
277
277
|
<Button variant={"link"} size={"icon"}
|
|
278
|
+
disabled={disabled}
|
|
278
279
|
alt="Clear selection"
|
|
279
280
|
onClick={(e) => {
|
|
280
281
|
e.stopPropagation();
|
package/src/env/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { AuthTokenPayload } from "@vertesia/common";
|
|
|
6
6
|
export interface EnvProps {
|
|
7
7
|
name: string; // the app name
|
|
8
8
|
version: string,
|
|
9
|
+
sdkVersion?: string, // the @vertesia/ui package version
|
|
9
10
|
isLocalDev: boolean,
|
|
10
11
|
isDocker: boolean,
|
|
11
12
|
type: "production" | "staging" | "preview" | "development" | string,
|
|
@@ -20,7 +21,7 @@ export interface EnvProps {
|
|
|
20
21
|
appId?: string,
|
|
21
22
|
providerType?: string,
|
|
22
23
|
},
|
|
23
|
-
datadog
|
|
24
|
+
datadog?: boolean,
|
|
24
25
|
logger?: {
|
|
25
26
|
info: (msg: string, ...args: any) => void,
|
|
26
27
|
warn: (msg: string, ...args: any) => void,
|
|
@@ -52,6 +53,10 @@ export class VertesiaEnvironment implements Readonly<EnvProps> {
|
|
|
52
53
|
return this.prop("version");
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
get sdkVersion() {
|
|
57
|
+
return this._props?.sdkVersion;
|
|
58
|
+
}
|
|
59
|
+
|
|
55
60
|
get name() {
|
|
56
61
|
return this.prop("name");
|
|
57
62
|
}
|
|
@@ -93,7 +98,7 @@ export class VertesiaEnvironment implements Readonly<EnvProps> {
|
|
|
93
98
|
}
|
|
94
99
|
|
|
95
100
|
get datadog() {
|
|
96
|
-
return this.
|
|
101
|
+
return this._props?.datadog ?? false;
|
|
97
102
|
}
|
|
98
103
|
|
|
99
104
|
get logger() {
|
|
@@ -40,7 +40,11 @@ export function useCollectionsFilterGroups(facets: CollectionsFacetsNavProps['fa
|
|
|
40
40
|
placeholder: 'Type',
|
|
41
41
|
type: 'select' as const,
|
|
42
42
|
multiple: true,
|
|
43
|
-
options: typeOptions
|
|
43
|
+
options: typeOptions,
|
|
44
|
+
filterBy: (value: string, searchText: string) => {
|
|
45
|
+
const option = typeOptions.find(opt => opt.value === value);
|
|
46
|
+
return option?.label?.toLowerCase().includes(searchText.toLowerCase()) ?? false;
|
|
47
|
+
}
|
|
44
48
|
};
|
|
45
49
|
customFilterGroups.push(typeFilterGroup);
|
|
46
50
|
}
|
|
@@ -28,8 +28,11 @@ export function GenericPageNavHeader({ className, children, title, description,
|
|
|
28
28
|
const pathSegments: string[] = (cleanHref as string).split('/').filter((segment: string) => segment.length > 0);
|
|
29
29
|
|
|
30
30
|
if (pathSegments.length === 3) {
|
|
31
|
+
if (entry.title !== document.title && entry.title) {
|
|
32
|
+
return entry.title;
|
|
33
|
+
}
|
|
31
34
|
const secondSegment = pathSegments[1];
|
|
32
|
-
return `${capitalize(secondSegment)} Detail`;
|
|
35
|
+
return `${capitalize(secondSegment)} Detail (...${pathSegments[2].slice(-6)})`;
|
|
33
36
|
} else if (pathSegments.length >= 2) {
|
|
34
37
|
return capitalize(pathSegments[pathSegments.length - 1]);
|
|
35
38
|
} else if (pathSegments.length === 1) {
|
|
@@ -51,7 +54,7 @@ export function GenericPageNavHeader({ className, children, title, description,
|
|
|
51
54
|
items.push({
|
|
52
55
|
label: buildBreadcrumbLabel(entry),
|
|
53
56
|
href: entry.href,
|
|
54
|
-
onClick: () =>
|
|
57
|
+
onClick: () => navigate(entry.href, { stepsBack: stepsBack })
|
|
55
58
|
});
|
|
56
59
|
});
|
|
57
60
|
}
|
|
@@ -265,11 +265,11 @@ function PropertiesEditor({ typeId, collection }: PropertiesEditorProps) {
|
|
|
265
265
|
|
|
266
266
|
return (
|
|
267
267
|
<Panel title="Properties" action={
|
|
268
|
-
<Button size="lg" isLoading={isUpdating} type="submit">
|
|
268
|
+
<Button size="lg" isLoading={isUpdating} type="submit" onClick={() => _onSave(object?.value)}>
|
|
269
269
|
Save
|
|
270
270
|
</Button>}
|
|
271
271
|
>
|
|
272
|
-
<GeneratedForm object={object}
|
|
272
|
+
<GeneratedForm object={object} />
|
|
273
273
|
</Panel>
|
|
274
274
|
);
|
|
275
275
|
}
|
|
@@ -27,7 +27,8 @@ const defaultLayout: ColumnLayout[] = [
|
|
|
27
27
|
|
|
28
28
|
function getTableLayout(registry: TypeRegistry, type: string | undefined): ColumnLayout[] {
|
|
29
29
|
const layout = type ? registry.getTypeLayout(type) : defaultLayout;
|
|
30
|
-
|
|
30
|
+
const result = layout ?? defaultLayout;
|
|
31
|
+
return result;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
interface DocumentSearchResultsWithDropZoneProps {
|
|
@@ -66,6 +66,10 @@ export interface NavigateOptions {
|
|
|
66
66
|
* if defined, indicate whether the basePath will be used as a top-level base path or a nested base path.
|
|
67
67
|
*/
|
|
68
68
|
isBasePathNested?: boolean;
|
|
69
|
+
// Number of steps to go back in history, which will pop the history stack instead of pushing a new entry
|
|
70
|
+
stepsBack?: number;
|
|
71
|
+
// the title to set for the new history entry
|
|
72
|
+
title?: string;
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
function getElementHrefAsUrl(elem: HTMLElement) {
|
|
@@ -113,6 +117,11 @@ export class HistoryNavigator {
|
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
navigate(to: string, options: NavigateOptions = {}) {
|
|
120
|
+
if (options.stepsBack && options.stepsBack > 0) {
|
|
121
|
+
this.stepBack(options.stepsBack, options);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
116
125
|
if (options.basePath) {
|
|
117
126
|
let basePath = options.basePath;
|
|
118
127
|
if (!basePath.startsWith('/')) {
|
|
@@ -124,6 +133,24 @@ export class HistoryNavigator {
|
|
|
124
133
|
this._navigate(new URL(to, window.location.href), 'navigate', options);
|
|
125
134
|
}
|
|
126
135
|
|
|
136
|
+
stepBack(steps: number, options: NavigateOptions = {}) {
|
|
137
|
+
const historyChain = window.history.state.historyChain || [];
|
|
138
|
+
const to = historyChain.length >= steps
|
|
139
|
+
? new URL(historyChain[historyChain.length - steps].href, window.location.href)
|
|
140
|
+
: new URL(window.location.origin, window.location.href);
|
|
141
|
+
this._navigate(to, 'popState', options);
|
|
142
|
+
|
|
143
|
+
const stateToStore = {
|
|
144
|
+
from: window.location.href,
|
|
145
|
+
historyChain: historyChain.slice(0, -steps),
|
|
146
|
+
data: options.state || undefined,
|
|
147
|
+
title: options.title || document.title
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
window.history['replaceState'](stateToStore, '', to.href);
|
|
151
|
+
this.fireLocationChange(new AfterLocationChangeEvent('popState', to, options.state));
|
|
152
|
+
}
|
|
153
|
+
|
|
127
154
|
_navigate(to: URL, type: LocationChangeType, options: NavigateOptions) {
|
|
128
155
|
const beforeEvent = new BeforeLocationChangeEvent(type, to, options.state);
|
|
129
156
|
this.fireLocationChange(beforeEvent);
|
|
@@ -133,7 +160,7 @@ export class HistoryNavigator {
|
|
|
133
160
|
|
|
134
161
|
// Build navigation chain by preserving previous history
|
|
135
162
|
const currentState = window.history.state;
|
|
136
|
-
const currentTitle = document.title;
|
|
163
|
+
const currentTitle = options.title || document.title;
|
|
137
164
|
|
|
138
165
|
// Create new history chain entry
|
|
139
166
|
const newChainEntry = {
|
|
@@ -160,7 +187,8 @@ export class HistoryNavigator {
|
|
|
160
187
|
const stateToStore = {
|
|
161
188
|
from: window.location.href,
|
|
162
189
|
historyChain: historyChain,
|
|
163
|
-
data: options.state || undefined
|
|
190
|
+
data: options.state || undefined,
|
|
191
|
+
title: options.title || document.title
|
|
164
192
|
};
|
|
165
193
|
|
|
166
194
|
window.history[options.replace ? 'replaceState' : 'pushState'](stateToStore, '', to.href);
|
|
@@ -2,6 +2,7 @@ import { getTenantIdFromProject } from "@vertesia/common";
|
|
|
2
2
|
import { VTabs, VTabsBar, VTabsPanel, VTooltip } from "@vertesia/ui/core";
|
|
3
3
|
import { Env } from "@vertesia/ui/env";
|
|
4
4
|
import { useUserSession } from "@vertesia/ui/session";
|
|
5
|
+
// Package version is now passed as prop from the consuming application
|
|
5
6
|
import { Check, CopyIcon } from "lucide-react";
|
|
6
7
|
import { useState } from "react";
|
|
7
8
|
|
|
@@ -61,6 +62,7 @@ export default function InfoList() {
|
|
|
61
62
|
<InfoItems title="Server" value={server} />
|
|
62
63
|
<InfoItems title="Store" value={store} />
|
|
63
64
|
<InfoItems title="App Version" value={Env.version} />
|
|
65
|
+
<InfoItems title="SDK Version" value={Env.sdkVersion || 'unknown'} />
|
|
64
66
|
</div>
|
|
65
67
|
}
|
|
66
68
|
];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthTokenPayload } from "@vertesia/common";
|
|
1
|
+
import { AuthTokenPayload, Permission } from "@vertesia/common";
|
|
2
2
|
import { Avatar, Button, MenuList, ModeToggle, Spinner } from "@vertesia/ui/core";
|
|
3
3
|
import { useUserSession } from "@vertesia/ui/session";
|
|
4
4
|
import { Popover } from "@vertesia/ui/widgets";
|
|
@@ -6,6 +6,8 @@ import clsx from "clsx";
|
|
|
6
6
|
import { useState } from "react";
|
|
7
7
|
import SignInModal from "./SignInModal";
|
|
8
8
|
import InfoList from "./UserInfo";
|
|
9
|
+
import { useNavigate } from "@vertesia/ui/router";
|
|
10
|
+
import { useUserPermissions } from "@vertesia/ui/features";
|
|
9
11
|
interface UserSessionMenuProps {
|
|
10
12
|
name?: string
|
|
11
13
|
picture?: string;
|
|
@@ -38,9 +40,13 @@ interface UserSessionPopupProps {
|
|
|
38
40
|
}
|
|
39
41
|
function UserSessionPopup({ className, asMenuTrigger = false }: UserSessionPopupProps) {
|
|
40
42
|
const session = useUserSession();
|
|
43
|
+
const navigate = useNavigate();
|
|
44
|
+
const perms = useUserPermissions();
|
|
41
45
|
const { user } = session;
|
|
42
46
|
if (!session || !user) return null;
|
|
43
47
|
|
|
48
|
+
const isProjectManager = perms.hasPermission(Permission.project_admin);
|
|
49
|
+
|
|
44
50
|
return (
|
|
45
51
|
<Popover strategy='fixed' placement='bottom-start' zIndex={100}>
|
|
46
52
|
<Popover.Trigger click>
|
|
@@ -68,6 +74,11 @@ function UserSessionPopup({ className, asMenuTrigger = false }: UserSessionPopup
|
|
|
68
74
|
</div>
|
|
69
75
|
<div className='py-2'>
|
|
70
76
|
<MenuList>
|
|
77
|
+
{isProjectManager && (
|
|
78
|
+
<MenuList.Item className='px-2' onClick={() => navigate('/settings', { replace: true })}>
|
|
79
|
+
Settings
|
|
80
|
+
</MenuList.Item>
|
|
81
|
+
)}
|
|
71
82
|
<MenuList.Item className='px-2' onClick={() => session.logout()}>
|
|
72
83
|
Sign out
|
|
73
84
|
</MenuList.Item>
|
|
@@ -94,10 +94,15 @@ export function ScalarField({ object, editor, inline = false }: ScalarFieldProps
|
|
|
94
94
|
object.value = object.schema.isNumber ? parseFloat(value) : value
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
if (object.isListItem) {
|
|
98
|
+
// List items don't need the FormItem wrapper (no label, description, etc.)
|
|
99
|
+
return <Component object={object} type={inputType} onChange={handleOnChange} />;
|
|
100
|
+
}
|
|
101
|
+
|
|
97
102
|
return (
|
|
98
103
|
<FormItem label={object.title} required={object.schema.isRequired} description={object.schema.description}
|
|
99
104
|
className={clsx('flex', inline ? 'flex-row items-center' : 'flex-col')}>
|
|
100
|
-
|
|
105
|
+
<Component object={object} type={inputType} onChange={handleOnChange} />
|
|
101
106
|
</FormItem>
|
|
102
107
|
)
|
|
103
108
|
}
|
|
@@ -279,16 +279,13 @@ export class SchemaNode {
|
|
|
279
279
|
if (this.schema.editor && data.editor === null) {
|
|
280
280
|
// explicitly set to null => delete current editor
|
|
281
281
|
this.schema.editor = undefined;
|
|
282
|
-
this.schema.format = undefined;
|
|
283
282
|
updated = true;
|
|
284
283
|
} else if (data.editor) { // a new editor is set
|
|
285
284
|
this.schema.editor = data.editor;
|
|
286
|
-
this.schema.format = data.editor;
|
|
287
285
|
updated = true;
|
|
288
286
|
} else if (typeChanged) {
|
|
289
287
|
// preserve editor only if the type didn't change
|
|
290
288
|
this.schema.editor = undefined;
|
|
291
|
-
this.schema.format = undefined;
|
|
292
289
|
updated = true;
|
|
293
290
|
}
|
|
294
291
|
if (data.description !== this.description) {
|
|
@@ -132,7 +132,7 @@ export function getTypeSignature(schema: JSONSchema): TypeSignature {
|
|
|
132
132
|
typeName = getItemTypeName(schema.items);
|
|
133
133
|
}
|
|
134
134
|
let displayTypeName: string = typeName;
|
|
135
|
-
switch (schema.editor
|
|
135
|
+
switch (schema.editor) {
|
|
136
136
|
case 'textarea': {
|
|
137
137
|
displayTypeName = 'text'; break;
|
|
138
138
|
}
|