@lobb-js/studio 0.47.0 → 0.49.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.
Files changed (72) hide show
  1. package/dist/actions.d.ts +2 -28
  2. package/dist/actions.js +4 -60
  3. package/dist/components/detailView/detailView.svelte +12 -62
  4. package/dist/components/detailView/detailView.svelte.d.ts +1 -1
  5. package/dist/components/detailView/fieldInput.svelte +161 -288
  6. package/dist/components/detailView/fieldInput.svelte.d.ts +1 -1
  7. package/dist/components/detailView/fields/BoolField.svelte +42 -0
  8. package/dist/components/detailView/fields/BoolField.svelte.d.ts +13 -0
  9. package/dist/components/detailView/fields/CodeField.svelte +30 -0
  10. package/dist/components/detailView/fields/CodeField.svelte.d.ts +13 -0
  11. package/dist/components/detailView/fields/CustomInputField.svelte +50 -0
  12. package/dist/components/detailView/fields/CustomInputField.svelte.d.ts +18 -0
  13. package/dist/components/detailView/fields/DateField.svelte +47 -0
  14. package/dist/components/detailView/fields/DateField.svelte.d.ts +14 -0
  15. package/dist/components/detailView/fields/EmbeddedField.svelte +139 -0
  16. package/dist/components/detailView/fields/EmbeddedField.svelte.d.ts +13 -0
  17. package/dist/components/detailView/fields/EmbeddedPolymorphicField.svelte +197 -0
  18. package/dist/components/detailView/fields/EmbeddedPolymorphicField.svelte.d.ts +13 -0
  19. package/dist/components/detailView/fields/EnumField.svelte +70 -0
  20. package/dist/components/detailView/fields/EnumField.svelte.d.ts +17 -0
  21. package/dist/components/detailView/fields/FieldWrapper.svelte +68 -0
  22. package/dist/components/detailView/fields/FieldWrapper.svelte.d.ts +18 -0
  23. package/dist/components/detailView/fields/ForeignKeyField.svelte +78 -0
  24. package/dist/components/detailView/fields/ForeignKeyField.svelte.d.ts +17 -0
  25. package/dist/components/detailView/fields/IdField.svelte +21 -0
  26. package/dist/components/detailView/fields/IdField.svelte.d.ts +12 -0
  27. package/dist/components/detailView/fields/NumberField.svelte +38 -0
  28. package/dist/components/detailView/fields/NumberField.svelte.d.ts +16 -0
  29. package/dist/components/detailView/fields/PasswordField.svelte +29 -0
  30. package/dist/components/detailView/fields/PasswordField.svelte.d.ts +12 -0
  31. package/dist/components/detailView/fields/PolymorphicField.svelte +51 -0
  32. package/dist/components/detailView/fields/PolymorphicField.svelte.d.ts +16 -0
  33. package/dist/components/detailView/fields/RichTextField.svelte +30 -0
  34. package/dist/components/detailView/fields/RichTextField.svelte.d.ts +13 -0
  35. package/dist/components/detailView/fields/StringField.svelte +35 -0
  36. package/dist/components/detailView/fields/StringField.svelte.d.ts +14 -0
  37. package/dist/components/detailView/fields/TextField.svelte +35 -0
  38. package/dist/components/detailView/fields/TextField.svelte.d.ts +14 -0
  39. package/dist/components/foreingKeyInput.svelte +1 -1
  40. package/dist/components/polymorphicInput.svelte +1 -1
  41. package/dist/components/popup/popup.svelte +23 -4
  42. package/dist/components/popup/popup.svelte.d.ts +2 -0
  43. package/dist/extensions/extension.types.d.ts +5 -2
  44. package/dist/extensions/extensionUtils.js +4 -1
  45. package/dist/popup.d.ts +31 -0
  46. package/dist/popup.js +104 -0
  47. package/package.json +7 -6
  48. package/src/lib/actions.ts +5 -95
  49. package/src/lib/components/detailView/detailView.svelte +12 -62
  50. package/src/lib/components/detailView/fieldInput.svelte +161 -288
  51. package/src/lib/components/detailView/fields/BoolField.svelte +42 -0
  52. package/src/lib/components/detailView/fields/CodeField.svelte +30 -0
  53. package/src/lib/components/detailView/fields/CustomInputField.svelte +50 -0
  54. package/src/lib/components/detailView/fields/DateField.svelte +47 -0
  55. package/src/lib/components/detailView/fields/EmbeddedField.svelte +139 -0
  56. package/src/lib/components/detailView/fields/EmbeddedPolymorphicField.svelte +197 -0
  57. package/src/lib/components/detailView/fields/EnumField.svelte +70 -0
  58. package/src/lib/components/detailView/fields/FieldWrapper.svelte +68 -0
  59. package/src/lib/components/detailView/fields/ForeignKeyField.svelte +78 -0
  60. package/src/lib/components/detailView/fields/IdField.svelte +21 -0
  61. package/src/lib/components/detailView/fields/NumberField.svelte +38 -0
  62. package/src/lib/components/detailView/fields/PasswordField.svelte +29 -0
  63. package/src/lib/components/detailView/fields/PolymorphicField.svelte +51 -0
  64. package/src/lib/components/detailView/fields/RichTextField.svelte +30 -0
  65. package/src/lib/components/detailView/fields/StringField.svelte +35 -0
  66. package/src/lib/components/detailView/fields/TextField.svelte +35 -0
  67. package/src/lib/components/foreingKeyInput.svelte +1 -1
  68. package/src/lib/components/polymorphicInput.svelte +1 -1
  69. package/src/lib/components/popup/popup.svelte +23 -4
  70. package/src/lib/extensions/extension.types.ts +4 -2
  71. package/src/lib/extensions/extensionUtils.ts +4 -1
  72. package/src/lib/popup.ts +147 -0
@@ -0,0 +1,51 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import PolymorphicInput from "../../polymorphicInput.svelte";
4
+ import FieldWrapper from "./FieldWrapper.svelte";
5
+
6
+ interface Props {
7
+ collectionField: string;
8
+ idField: string;
9
+ virtualField: string;
10
+ targetCollections: string[];
11
+ entry: Record<string, any>;
12
+ destructive?: boolean;
13
+ isDisabled?: boolean;
14
+ errorMessages?: any;
15
+ header?: any;
16
+ clearBtn?: Snippet<[]>;
17
+ }
18
+
19
+ let {
20
+ collectionField,
21
+ idField,
22
+ virtualField,
23
+ targetCollections,
24
+ entry = $bindable(),
25
+ destructive = false,
26
+ isDisabled = false,
27
+ errorMessages = [],
28
+ header,
29
+ clearBtn,
30
+ }: Props = $props();
31
+ </script>
32
+
33
+ <FieldWrapper span={2} {isDisabled} {errorMessages} {header}>
34
+ {#snippet children()}
35
+ <div class="relative">
36
+ {#if clearBtn}
37
+ <div class="absolute right-1 inset-y-0 z-10 flex items-center">
38
+ {@render clearBtn()}
39
+ </div>
40
+ {/if}
41
+ <PolymorphicInput
42
+ {collectionField}
43
+ {idField}
44
+ {virtualField}
45
+ {targetCollections}
46
+ bind:entry
47
+ {destructive}
48
+ />
49
+ </div>
50
+ {/snippet}
51
+ </FieldWrapper>
@@ -0,0 +1,30 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import RichTextEditor from "../../richTextEditor.svelte";
4
+ import FieldWrapper from "./FieldWrapper.svelte";
5
+
6
+ interface Props {
7
+ value?: any;
8
+ field: any;
9
+ destructive?: boolean;
10
+ isDisabled?: boolean;
11
+ errorMessages?: any;
12
+ header?: any;
13
+ clearBtn?: Snippet<[]>;
14
+ }
15
+
16
+ let { value = $bindable(), field, destructive = false, isDisabled = false, errorMessages = [], header, clearBtn }: Props = $props();
17
+ </script>
18
+
19
+ <FieldWrapper span={2} {isDisabled} {errorMessages} {header}>
20
+ {#snippet children()}
21
+ <div class="relative {destructive ? 'rounded-md ring-1 ring-destructive' : ''}">
22
+ {#if clearBtn}
23
+ <div class="absolute right-1 top-1 z-10">
24
+ {@render clearBtn()}
25
+ </div>
26
+ {/if}
27
+ <RichTextEditor name={field.key} bind:value />
28
+ </div>
29
+ {/snippet}
30
+ </FieldWrapper>
@@ -0,0 +1,35 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import Input from "../../ui/input/input.svelte";
4
+ import FieldWrapper from "./FieldWrapper.svelte";
5
+
6
+ interface Props {
7
+ value?: any;
8
+ placeholder?: string;
9
+ destructive?: boolean;
10
+ changedClass?: string;
11
+ isDisabled?: boolean;
12
+ errorMessages?: any;
13
+ header?: any;
14
+ clearBtn?: Snippet<[]>;
15
+ }
16
+ let { value = $bindable(), placeholder = "NULL", destructive = false, changedClass = '', isDisabled = false, errorMessages = [], header, clearBtn }: Props = $props();
17
+ </script>
18
+
19
+ <FieldWrapper span={1} {isDisabled} {errorMessages} {header}>
20
+ {#snippet children()}
21
+ <div class="relative">
22
+ {#if clearBtn}
23
+ <div class="absolute right-1 inset-y-0 z-10 flex items-center">
24
+ {@render clearBtn()}
25
+ </div>
26
+ {/if}
27
+ <Input
28
+ {placeholder}
29
+ type="text"
30
+ class="bg-muted text-xs pr-9 {changedClass} {destructive ? 'border-destructive' : ''}"
31
+ bind:value
32
+ />
33
+ </div>
34
+ {/snippet}
35
+ </FieldWrapper>
@@ -0,0 +1,35 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import Textarea from "../../ui/textarea/textarea.svelte";
4
+ import FieldWrapper from "./FieldWrapper.svelte";
5
+
6
+ interface Props {
7
+ value?: any;
8
+ placeholder?: string;
9
+ destructive?: boolean;
10
+ changedClass?: string;
11
+ isDisabled?: boolean;
12
+ errorMessages?: any;
13
+ header?: any;
14
+ clearBtn?: Snippet<[]>;
15
+ }
16
+ let { value = $bindable(), placeholder = "NULL", destructive = false, changedClass = '', isDisabled = false, errorMessages = [], header, clearBtn }: Props = $props();
17
+ </script>
18
+
19
+ <FieldWrapper span={2} {isDisabled} {errorMessages} {header}>
20
+ {#snippet children()}
21
+ <div class="relative">
22
+ {#if clearBtn}
23
+ <div class="absolute right-1 top-1 z-10">
24
+ {@render clearBtn()}
25
+ </div>
26
+ {/if}
27
+ <Textarea
28
+ {placeholder}
29
+ rows={5}
30
+ class="bg-muted text-xs pr-9 {changedClass} {destructive ? 'border-destructive' : ''}"
31
+ bind:value
32
+ />
33
+ </div>
34
+ {/snippet}
35
+ </FieldWrapper>
@@ -80,7 +80,7 @@
80
80
  <Input
81
81
  placeholder={isPendingCreate ? "AUTO GENERATED" : isEmpty ? "NULL" : ""}
82
82
  type="number"
83
- class="bg-muted text-xs {bgClass} {destructive ? '!bg-destructive/10 border-destructive' : ''}"
83
+ class="bg-muted text-xs {bgClass} {destructive ? 'border-destructive' : ''}"
84
84
  bind:value={
85
85
  () => hasRealId ? value : "",
86
86
  (v) => { value = (v === "" || v == null) ? null : Number(v); }
@@ -80,7 +80,7 @@
80
80
  }
81
81
  </script>
82
82
 
83
- <div class="flex h-9 w-full items-center gap-1.5 rounded-md border pl-1.5 pr-9 text-xs bg-muted {bgClass} {destructive ? '!bg-destructive/10 border-destructive' : ''}">
83
+ <div class="flex h-9 w-full items-center gap-1.5 rounded-md border pl-1.5 pr-9 text-xs bg-muted {bgClass} {destructive ? 'border-destructive' : ''}">
84
84
  <Popover.Root bind:open={collectionPopoverOpen}>
85
85
  <Popover.Trigger>
86
86
  {#snippet child({ props })}
@@ -6,7 +6,7 @@
6
6
  // `openPopup` mounts this; specialised popup helpers like
7
7
  // `openDataTablePopup` are thin wrappers that supply the body
8
8
  // component + props.
9
- import { X } from "lucide-svelte";
9
+ import { ArrowLeft, X } from "lucide-svelte";
10
10
  import { fade, scale } from "svelte/transition";
11
11
  import { cubicOut } from "svelte/easing";
12
12
  import Portal from "svelte-portal";
@@ -28,6 +28,12 @@
28
28
  // Use when the body brings its own header.
29
29
  hideHeader?: boolean;
30
30
  onClose?: () => void;
31
+ // Set by the action layer when there's a popup-history entry to
32
+ // pop. The chrome renders a back arrow in the title bar; bodies
33
+ // that `hideHeader: true` get `canGoBack` + `onBack` forwarded
34
+ // so they can render their own back affordance.
35
+ canGoBack?: boolean;
36
+ onBack?: () => void;
31
37
  }
32
38
 
33
39
  let {
@@ -37,6 +43,8 @@
37
43
  size = "default",
38
44
  hideHeader = false,
39
45
  onClose,
46
+ canGoBack = false,
47
+ onBack,
40
48
  }: Props = $props();
41
49
  </script>
42
50
 
@@ -54,8 +62,19 @@
54
62
  {size === 'sm' ? 'max-h-[calc(100vh-6rem)] max-w-2xl' : 'h-[85vh] max-w-7xl'}"
55
63
  >
56
64
  {#if !hideHeader}
57
- <div class="flex h-12 shrink-0 items-center justify-between gap-4 border-b px-4">
58
- <div class="text-sm font-medium">{title ?? ""}</div>
65
+ <div class="flex h-12 shrink-0 items-center justify-between gap-2 border-b px-4">
66
+ <div class="flex min-w-0 items-center gap-2">
67
+ {#if canGoBack}
68
+ <Button
69
+ variant="ghost"
70
+ size="icon"
71
+ onclick={() => onBack?.()}
72
+ class="h-8 w-8 rounded-full text-muted-foreground"
73
+ Icon={ArrowLeft}
74
+ />
75
+ {/if}
76
+ <div class="truncate text-sm font-medium">{title ?? ""}</div>
77
+ </div>
59
78
  <Button
60
79
  variant="ghost"
61
80
  size="icon"
@@ -73,7 +92,7 @@
73
92
  percentages, which don't resolve when the popup uses
74
93
  `max-h` (size="sm") instead of a fixed `h`. -->
75
94
  <div class="flex min-h-0 flex-1 flex-col overflow-hidden">
76
- <Body {...componentProps} {onClose} />
95
+ <Body {...componentProps} {onClose} {canGoBack} {onBack} />
77
96
  </div>
78
97
  </div>
79
98
  </Portal>
@@ -17,13 +17,15 @@ import type RangeCalendarButton from "../components/rangeCalendarButton.svelte";
17
17
  import type DataTable from "../components/dataTable/dataTable.svelte";
18
18
  import type Drawer from "../components/drawer.svelte";
19
19
  import type SelectRecord from "../components/selectRecord.svelte";
20
+ import type FieldWrapper from "../components/detailView/fields/FieldWrapper.svelte";
20
21
  import * as Popover from "../components/ui/popover";
21
22
  import * as intlDate from "@internationalized/date";
22
23
  import * as Icons from "lucide-svelte"
23
24
  import { ContextMenu } from "bits-ui";
24
25
  import * as Tooltip from "../components/ui/tooltip";
25
26
  import * as Breadcrumb from "../components/ui/breadcrumb";
26
- import { showDialog, type OpenDataTableDrawerProps, type OpenDataTablePopupProps, type OpenPopupProps } from "../actions";
27
+ import { showDialog, type OpenDataTableDrawerProps } from "../actions";
28
+ import type { OpenDataTablePopupProps, OpenPopupProps } from "../popup";
27
29
  import { toast } from "svelte-sonner";
28
30
  import type Drawer from "../components/drawer.svelte";
29
31
  import { Switch } from "../components/ui/switch";
@@ -50,6 +52,7 @@ export interface Components {
50
52
  Drawer: typeof Drawer;
51
53
  SelectRecord: typeof SelectRecord,
52
54
  Switch: typeof Switch,
55
+ FieldWrapper: typeof FieldWrapper,
53
56
  }
54
57
 
55
58
  export interface ExtensionUtils {
@@ -136,7 +139,6 @@ export type ExtensionComponentKey =
136
139
  | `pages.${string}`
137
140
  | `publicPages.${string}`
138
141
  | "studio.listView"
139
- | `dvFields.topRight.${string}.${string}`
140
142
  | `detailView.update.subRecords.${string}`
141
143
  | `detailView.create.subRecords.${string}`
142
144
  | `detailView.fields.topRight.${string}.${string}`
@@ -7,7 +7,8 @@ import type {
7
7
  import type { LobbClient } from "@lobb-js/sdk";
8
8
  import type { CTX } from "../store.types";
9
9
  import { toast } from "svelte-sonner";
10
- import { showDialog, openDataTableDrawer, openDataTablePopup, openPopup } from "../actions";
10
+ import { showDialog, openDataTableDrawer } from "../actions";
11
+ import { openDataTablePopup, openPopup } from "../popup";
11
12
  import { emitEvent } from "../eventSystem";
12
13
  import { Button } from "../components/ui/button";
13
14
  import { Input } from "../components/ui/input";
@@ -32,6 +33,7 @@ import RangeCalendarButton from "../components/rangeCalendarButton.svelte";
32
33
  import DataTable from "../components/dataTable/dataTable.svelte";
33
34
  import Drawer from "../components/drawer.svelte";
34
35
  import SelectRecord from "../components/selectRecord.svelte";
36
+ import FieldWrapper from "../components/detailView/fields/FieldWrapper.svelte";
35
37
  import { Switch } from "../components/ui/switch";
36
38
 
37
39
  export function getComponents(): Components {
@@ -56,6 +58,7 @@ export function getComponents(): Components {
56
58
  Drawer: Drawer,
57
59
  SelectRecord: SelectRecord,
58
60
  Switch: Switch,
61
+ FieldWrapper: FieldWrapper,
59
62
  };
60
63
  }
61
64
 
@@ -0,0 +1,147 @@
1
+ import { mount, unmount } from "svelte";
2
+ import DataTable from "./components/dataTable/dataTable.svelte";
3
+ import Popup from "./components/popup/popup.svelte";
4
+ import { createStudioContextMap, type StudioContext } from "./context";
5
+ import type { CollectionTab } from "./store.types";
6
+
7
+ export interface OpenPopupProps {
8
+ // Component rendered inside the popup. It receives `componentProps`
9
+ // spread onto it plus `onClose` so it can close the popup itself.
10
+ component: any;
11
+ componentProps?: Record<string, any>;
12
+ title?: string;
13
+ // `default` is the wide data-table popup (max-w-7xl, fixed 85vh).
14
+ // `sm` is the narrow detail-view variant (max-w-2xl, content-height).
15
+ size?: "default" | "sm";
16
+ // When true, the popup renders no chrome at all (no title bar, no
17
+ // close button). The body uses the `onClose` prop the popup passes
18
+ // it to render its own close affordance.
19
+ hideHeader?: boolean;
20
+ // Close the previously-opened popup before mounting this one —
21
+ // drill-down semantics. Only popups participate; drawers don't.
22
+ replaceLast?: boolean;
23
+ }
24
+
25
+ export interface OpenDataTablePopupProps {
26
+ collectionName: string;
27
+ filter?: Record<string, any>;
28
+ sort?: Record<string, "asc" | "desc">;
29
+ limit?: number;
30
+ title?: string;
31
+ showHeader?: boolean;
32
+ showFooter?: boolean;
33
+ showFilter?: boolean;
34
+ tabs?: CollectionTab[];
35
+ view?: { id: string; [key: string]: any };
36
+ replaceLast?: boolean;
37
+ size?: "default" | "sm";
38
+ hideHeader?: boolean;
39
+ }
40
+
41
+ let activePopup: any = null;
42
+ // Props that produced the currently-mounted popup. Tracked so we can
43
+ // push them onto `popupHistory` when this popup is swapped out by a
44
+ // `replaceLast: true` opener, enabling back navigation.
45
+ let activePopupProps: OpenPopupProps | null = null;
46
+ // Stack of popup-props that were swapped out via `replaceLast: true`.
47
+ // The back arrow re-opens the top of the stack; closing via X or the
48
+ // backdrop wipes the whole stack (a deliberate full-reset, not back).
49
+ const popupHistory: OpenPopupProps[] = [];
50
+
51
+ // Internal mount path used by both `openPopup` (user-initiated) and
52
+ // `goBackPopup` (history-driven). The `fromBack` flag suppresses
53
+ // pushing the outgoing popup onto the history stack — when the user
54
+ // hits Back, the popup they're leaving shouldn't reappear if they hit
55
+ // Back again.
56
+ function mountPopup(
57
+ studioContext: StudioContext,
58
+ props: OpenPopupProps,
59
+ opts: { fromBack?: boolean } = {},
60
+ ) {
61
+ const targetElement = document.querySelector("main");
62
+ if (!targetElement) throw new Error("main html element doesn't exist");
63
+
64
+ if (props.replaceLast && activePopup) {
65
+ // Swap: archive the outgoing popup's props (unless this open is
66
+ // itself a back-navigation) and unmount it. Outro plays in
67
+ // parallel with the new popup's intro.
68
+ if (!opts.fromBack && activePopupProps) {
69
+ popupHistory.push(activePopupProps);
70
+ }
71
+ const toUnmount = activePopup;
72
+ activePopup = null;
73
+ activePopupProps = null;
74
+ unmount(toUnmount, { outro: true });
75
+ } else if (!props.replaceLast) {
76
+ // A fresh, non-stacking popup starts a new context — drop any
77
+ // accumulated back history from the previous chain.
78
+ popupHistory.length = 0;
79
+ }
80
+
81
+ const canGoBack = popupHistory.length > 0;
82
+
83
+ const mounted = mount(Popup, {
84
+ target: targetElement,
85
+ context: createStudioContextMap(studioContext),
86
+ props: {
87
+ component: props.component,
88
+ componentProps: props.componentProps,
89
+ title: props.title,
90
+ size: props.size,
91
+ hideHeader: props.hideHeader,
92
+ canGoBack,
93
+ onBack: () => goBackPopup(studioContext),
94
+ onClose: async () => {
95
+ if (activePopup === mounted) {
96
+ activePopup = null;
97
+ activePopupProps = null;
98
+ // X / backdrop is a full reset — nuke the back
99
+ // stack so reopening anything starts fresh.
100
+ popupHistory.length = 0;
101
+ }
102
+ await unmount(mounted, { outro: true });
103
+ },
104
+ },
105
+ });
106
+ activePopup = mounted;
107
+ activePopupProps = props;
108
+ }
109
+
110
+ export function openPopup(studioContext: StudioContext, props: OpenPopupProps) {
111
+ mountPopup(studioContext, props);
112
+ }
113
+
114
+ export function goBackPopup(studioContext: StudioContext) {
115
+ const prev = popupHistory.pop();
116
+ if (!prev) return;
117
+ // Force `replaceLast: true` so the swap+outro path runs, and tell
118
+ // `mountPopup` not to push the current popup onto history.
119
+ mountPopup(studioContext, { ...prev, replaceLast: true }, { fromBack: true });
120
+ }
121
+
122
+ export function openDataTablePopup(studioContext: StudioContext, props: OpenDataTablePopupProps) {
123
+ // DataTable reads `searchParams` once during its initial $state setup,
124
+ // so sort/limit need to be folded into the searchParams object before
125
+ // we mount. Built here in plain TS — no Svelte reactivity to escape.
126
+ const searchParams: Record<string, any> = {};
127
+ if (props.sort) searchParams.sort = props.sort;
128
+ if (props.limit != null) searchParams.limit = String(props.limit);
129
+
130
+ openPopup(studioContext, {
131
+ component: DataTable,
132
+ componentProps: {
133
+ collectionName: props.collectionName,
134
+ filter: props.filter,
135
+ searchParams,
136
+ showHeader: props.showHeader,
137
+ showFooter: props.showFooter,
138
+ showFilter: props.showFilter ?? false,
139
+ tabs: props.tabs,
140
+ view: props.view,
141
+ },
142
+ title: props.title ?? props.collectionName,
143
+ size: props.size,
144
+ hideHeader: props.hideHeader,
145
+ replaceLast: props.replaceLast,
146
+ });
147
+ }