@alepha/ui 0.11.6 → 0.11.9

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,11 @@
1
1
  import type { TObject } from "@alepha/core";
2
+ import { parseQueryString } from "@alepha/postgres";
3
+ import { useEvents } from "@alepha/react";
2
4
  import {
3
5
  ActionIcon,
4
6
  Badge,
5
- Code,
6
7
  Divider,
8
+ Flex,
7
9
  Group,
8
10
  Popover,
9
11
  Stack,
@@ -11,13 +13,15 @@ import {
11
13
  TextInput,
12
14
  type TextInputProps,
13
15
  } from "@mantine/core";
14
- import { IconFilter, IconX } from "@tabler/icons-react";
16
+ import { IconFilter, IconInfoTriangle, IconX } from "@tabler/icons-react";
15
17
  import { useRef, useState } from "react";
18
+ import { ui } from "../../constants/ui.ts";
16
19
  import {
17
20
  extractSchemaFields,
18
21
  OPERATOR_INFO,
19
22
  type SchemaField,
20
23
  } from "../../utils/extractSchemaFields.ts";
24
+ import ActionButton from "../buttons/ActionButton.tsx";
21
25
 
22
26
  export interface ControlQueryBuilderProps
23
27
  extends Omit<TextInputProps, "value" | "onChange"> {
@@ -35,34 +39,67 @@ const ControlQueryBuilder = ({
35
39
  schema,
36
40
  value = "",
37
41
  onChange,
38
- placeholder = "Enter query or click help for assistance...",
42
+ placeholder = "Enter query or click for assistance...",
39
43
  ...textInputProps
40
44
  }: ControlQueryBuilderProps) => {
41
45
  const [helpOpened, setHelpOpened] = useState(false);
42
46
  const [textValue, setTextValue] = useState(value);
43
47
  const inputRef = useRef<HTMLInputElement>(null);
44
48
  const fields = schema ? extractSchemaFields(schema) : [];
49
+ const [error, setError] = useState<string | null>(null);
50
+
51
+ const isValid = (value: string) => {
52
+ try {
53
+ parseQueryString(value.trim());
54
+ } catch (e) {
55
+ setError((e as Error).message);
56
+ return false;
57
+ }
58
+ setError(null);
59
+ return true;
60
+ };
45
61
 
46
62
  const handleTextChange = (newValue: string) => {
47
63
  setTextValue(newValue);
48
- onChange?.(newValue);
64
+ if (isValid(newValue)) {
65
+ onChange?.(newValue);
66
+ }
49
67
  };
50
68
 
51
69
  const handleClear = () => {
52
70
  setTextValue("");
53
71
  onChange?.("");
72
+ isValid("");
54
73
  };
55
74
 
56
75
  const handleInsert = (text: string) => {
57
76
  const newValue = textValue ? `${textValue}${text} ` : `${text} `;
58
77
  setTextValue(newValue);
59
- onChange?.(newValue);
78
+ if (isValid(newValue)) {
79
+ onChange?.(newValue);
80
+ }
60
81
  // Refocus the input after inserting
61
82
  setTimeout(() => {
62
83
  inputRef.current?.focus();
84
+ // set cursor to end
85
+ const length = inputRef.current?.value.length || 0;
86
+ inputRef.current?.setSelectionRange(length, length);
63
87
  }, 0);
64
88
  };
65
89
 
90
+ useEvents(
91
+ {
92
+ "form:change": (event) => {
93
+ if (event.id === inputRef.current?.form?.id) {
94
+ if (event.path === (textInputProps as any)["data-path"]) {
95
+ setTextValue(event.value ?? "");
96
+ }
97
+ }
98
+ },
99
+ },
100
+ [],
101
+ );
102
+
66
103
  return (
67
104
  <Popover
68
105
  width={800}
@@ -72,10 +109,8 @@ const ControlQueryBuilder = ({
72
109
  onChange={setHelpOpened}
73
110
  closeOnClickOutside
74
111
  closeOnEscape
75
- withArrow
76
- arrowSize={14}
77
112
  transitionProps={{
78
- transition: "fade-down",
113
+ transition: "fade-up",
79
114
  duration: 200,
80
115
  timingFunction: "ease",
81
116
  }}
@@ -87,7 +122,9 @@ const ControlQueryBuilder = ({
87
122
  value={textValue}
88
123
  onChange={(e) => handleTextChange(e.currentTarget.value)}
89
124
  onFocus={() => setHelpOpened(true)}
90
- leftSection={<IconFilter size={16} />}
125
+ leftSection={
126
+ error ? <IconInfoTriangle size={16} /> : <IconFilter size={16} />
127
+ }
91
128
  rightSection={
92
129
  textValue && (
93
130
  <ActionIcon
@@ -103,7 +140,14 @@ const ControlQueryBuilder = ({
103
140
  {...textInputProps}
104
141
  />
105
142
  </Popover.Target>
106
- <Popover.Dropdown>
143
+ <Popover.Dropdown
144
+ bg={"transparent"}
145
+ p={"xs"}
146
+ bd={`1px solid ${ui.colors.border}`}
147
+ style={{
148
+ backdropFilter: "blur(20px)",
149
+ }}
150
+ >
107
151
  <QueryHelp fields={fields} onInsert={handleInsert} />
108
152
  </Popover.Dropdown>
109
153
  </Popover>
@@ -121,7 +165,14 @@ interface QueryHelpProps {
121
165
 
122
166
  function QueryHelp({ fields, onInsert }: QueryHelpProps) {
123
167
  return (
124
- <Group gap="md" align="flex-start" wrap="nowrap">
168
+ <Group
169
+ gap="md"
170
+ align="flex-start"
171
+ wrap="nowrap"
172
+ bg={ui.colors.surface}
173
+ p={"sm"}
174
+ bdrs={"sm"}
175
+ >
125
176
  {/* Left Column: Operators */}
126
177
  <Stack gap="md" style={{ flex: 1 }}>
127
178
  {/* Available Operators */}
@@ -132,16 +183,17 @@ function QueryHelp({ fields, onInsert }: QueryHelpProps) {
132
183
  <Stack gap={4}>
133
184
  {Object.entries(OPERATOR_INFO).map(([key, info]) => (
134
185
  <Group key={key} gap="xs" wrap="nowrap">
135
- <Code
136
- style={{
137
- minWidth: 35,
138
- textAlign: "center",
139
- cursor: "pointer",
140
- }}
186
+ <ActionButton
187
+ px={"xs"}
188
+ size={"xs"}
189
+ h={24}
190
+ variant={"default"}
191
+ justify={"center"}
192
+ miw={48}
141
193
  onClick={() => onInsert(info.symbol)}
142
194
  >
143
195
  {info.symbol}
144
- </Code>
196
+ </ActionButton>
145
197
  <Text size="xs" c="dimmed" style={{ flex: 1 }}>
146
198
  {info.label}
147
199
  </Text>
@@ -159,31 +211,33 @@ function QueryHelp({ fields, onInsert }: QueryHelpProps) {
159
211
  </Text>
160
212
  <Stack gap={4}>
161
213
  <Group gap="xs" wrap="nowrap">
162
- <Code
163
- style={{
164
- minWidth: 35,
165
- textAlign: "center",
166
- cursor: "pointer",
167
- }}
214
+ <ActionButton
215
+ px={"xs"}
216
+ size={"xs"}
217
+ h={24}
218
+ variant={"default"}
219
+ justify={"center"}
220
+ miw={48}
168
221
  onClick={() => onInsert("&")}
169
222
  >
170
223
  &
171
- </Code>
224
+ </ActionButton>
172
225
  <Text size="xs" c="dimmed">
173
226
  AND
174
227
  </Text>
175
228
  </Group>
176
229
  <Group gap="xs" wrap="nowrap">
177
- <Code
178
- style={{
179
- minWidth: 35,
180
- textAlign: "center",
181
- cursor: "pointer",
182
- }}
230
+ <ActionButton
231
+ px={"xs"}
232
+ size={"xs"}
233
+ h={24}
234
+ variant={"default"}
235
+ justify={"center"}
236
+ miw={48}
183
237
  onClick={() => onInsert("|")}
184
238
  >
185
239
  |
186
- </Code>
240
+ </ActionButton>
187
241
  <Text size="xs" c="dimmed">
188
242
  OR
189
243
  </Text>
@@ -197,49 +251,60 @@ function QueryHelp({ fields, onInsert }: QueryHelpProps) {
197
251
 
198
252
  {/* Right Column: Fields */}
199
253
  {fields.length > 0 && (
200
- <Stack gap="xs" style={{ flex: 2 }}>
254
+ <Flex direction={"column"} gap="xs" style={{ flex: 2 }}>
201
255
  <Text size="sm" fw={600}>
202
256
  Fields
203
257
  </Text>
204
- <Stack gap={4} style={{ maxHeight: 300, overflowY: "auto" }}>
258
+ <Flex
259
+ direction={"column"}
260
+ gap={4}
261
+ style={{ maxHeight: 300, overflowY: "auto" }}
262
+ >
205
263
  {fields.map((field) => (
206
- <Group key={field.path} gap="xs" wrap="nowrap" align="flex-start">
207
- <Code
208
- style={{ minWidth: 120, cursor: "pointer" }}
264
+ <Flex key={field.path} gap="xs" wrap="nowrap" align="flex-start">
265
+ <ActionButton
266
+ px={"xs"}
267
+ size={"xs"}
268
+ h={24}
269
+ variant={"default"}
270
+ justify={"end"}
271
+ miw={120}
209
272
  onClick={() => onInsert(field.path)}
210
273
  >
211
274
  {field.path}
212
- </Code>
213
- <Stack gap={2} style={{ flex: 1, minWidth: 0 }}>
275
+ </ActionButton>
276
+ <Flex
277
+ mt={3}
278
+ direction={"column"}
279
+ gap={2}
280
+ style={{ flex: 1, minWidth: 0 }}
281
+ >
214
282
  <Text size="xs" c="dimmed" lineClamp={1}>
215
283
  {field.description || field.type}
216
284
  </Text>
217
285
  {field.enum && (
218
- <Group gap={4} wrap="wrap">
286
+ <Group gap={0} wrap="wrap">
219
287
  {field.enum.map((enumValue) => (
220
- <Code
288
+ <ActionButton
289
+ px={"xs"}
290
+ size={"xs"}
291
+ h={24}
221
292
  key={enumValue}
222
- style={{
223
- cursor: "pointer",
224
- fontStyle: "italic",
225
- fontSize: "0.75rem",
226
- }}
227
- c="blue"
228
293
  onClick={() => onInsert(enumValue)}
229
294
  >
230
295
  {enumValue}
231
- </Code>
296
+ </ActionButton>
232
297
  ))}
233
298
  </Group>
234
299
  )}
235
- </Stack>
300
+ </Flex>
236
301
  <Badge size="xs" variant="light" style={{ flexShrink: 0 }}>
237
302
  {field.type}
238
303
  </Badge>
239
- </Group>
304
+ </Flex>
240
305
  ))}
241
- </Stack>
242
- </Stack>
306
+ </Flex>
307
+ </Flex>
243
308
  )}
244
309
  </Group>
245
310
  );
@@ -97,7 +97,7 @@ const ControlSelect = (props: ControlSelectProps) => {
97
97
 
98
98
  return (
99
99
  <Input.Wrapper {...inputProps}>
100
- <Flex mt={"calc(var(--mantine-spacing-xs) / 2)"}>
100
+ <Flex>
101
101
  <SegmentedControl
102
102
  disabled={inputProps.disabled}
103
103
  defaultValue={String(props.input.props.defaultValue)}
@@ -1,17 +1,18 @@
1
1
  import {
2
+ Alepha,
2
3
  type Async,
3
4
  type Page,
4
5
  type PageMetadata,
6
+ type Static,
5
7
  type TObject,
6
8
  t,
7
9
  } from "@alepha/core";
8
10
  import { DateTimeProvider, type DurationLike } from "@alepha/datetime";
9
11
  import { useInject } from "@alepha/react";
10
- import { useForm } from "@alepha/react-form";
12
+ import { type FormModel, useForm } from "@alepha/react-form";
11
13
  import {
12
14
  Flex,
13
15
  Pagination,
14
- Paper,
15
16
  Select,
16
17
  Table,
17
18
  type TableProps,
@@ -20,40 +21,60 @@ import {
20
21
  import { useDebouncedCallback } from "@mantine/hooks";
21
22
  import { type ReactNode, useEffect, useState } from "react";
22
23
  import ActionButton from "../buttons/ActionButton.tsx";
23
- import TypeForm from "../form/TypeForm.tsx";
24
+ import TypeForm, { type TypeFormProps } from "../form/TypeForm.tsx";
24
25
 
25
- export interface DataTableColumn<T extends object> {
26
+ export interface DataTableColumnContext<Filters extends TObject> {
27
+ index: number;
28
+ form: FormModel<Filters>;
29
+ alepha: Alepha;
30
+ }
31
+
32
+ export interface DataTableColumn<T extends object, Filters extends TObject> {
26
33
  label: string;
27
- value: (item: T, index: number) => ReactNode;
34
+ value: (item: T, ctx: DataTableColumnContext<Filters>) => ReactNode;
35
+ fit?: boolean;
28
36
  }
29
37
 
30
38
  export type MaybePage<T> = Omit<Page<T>, "page"> & {
31
39
  page?: Partial<PageMetadata>;
32
40
  };
33
41
 
34
- export interface DataTableProps<T extends object> {
42
+ export interface DataTableSubmitContext<T extends object> {
43
+ items: T[];
44
+ }
45
+
46
+ export interface DataTableProps<T extends object, Filters extends TObject> {
35
47
  /**
36
48
  * The items to display in the table. Can be a static page of items or a function that returns a promise resolving to a page of items.
37
49
  */
38
50
  items:
39
51
  | MaybePage<T>
40
52
  | ((
41
- filters: Record<string, string> & {
53
+ filters: Static<Filters> & {
42
54
  page: number;
43
55
  size: number;
44
56
  sort?: string;
45
57
  },
58
+ ctx: DataTableSubmitContext<T>,
46
59
  ) => Async<MaybePage<T>>);
47
60
 
48
61
  /**
49
62
  * The columns to display in the table. Each column is defined by a key and a DataTableColumn object.
50
63
  */
51
64
  columns: {
52
- [key: string]: DataTableColumn<T>;
65
+ [key: string]: DataTableColumn<T, Filters>;
53
66
  };
54
67
 
55
68
  defaultSize?: number;
56
69
 
70
+ typeFormProps?: Partial<Omit<TypeFormProps<Filters>, "form">>;
71
+
72
+ onFilterChange?: (
73
+ key: string,
74
+ value: unknown,
75
+ form: FormModel<Filters>,
76
+ ) => void;
77
+
57
78
  /**
58
79
  * Optional filters to apply to the data.
59
80
  */
@@ -89,7 +110,9 @@ export interface DataTableProps<T extends object> {
89
110
  tableTrProps?: (item: T) => TableTrProps;
90
111
  }
91
112
 
92
- const DataTable = <T extends object>(props: DataTableProps<T>) => {
113
+ const DataTable = <T extends object, Filters extends TObject>(
114
+ props: DataTableProps<T, Filters>,
115
+ ) => {
93
116
  const [items, setItems] = useState<MaybePage<T>>(
94
117
  typeof props.items === "function"
95
118
  ? {
@@ -98,11 +121,11 @@ const DataTable = <T extends object>(props: DataTableProps<T>) => {
98
121
  : props.items,
99
122
  );
100
123
 
101
- const defaultSize = props.infinityScroll ? 50 : props.defaultSize || 10;
124
+ const defaultSize = props.infinityScroll ? 100 : props.defaultSize || 10;
102
125
  const [page, setPage] = useState(1);
103
126
  const [size, setSize] = useState(String(defaultSize));
104
- const [isLoadingMore, setIsLoadingMore] = useState(false);
105
127
  const [currentPage, setCurrentPage] = useState(0);
128
+ const alepha = useInject(Alepha);
106
129
 
107
130
  const form = useForm(
108
131
  {
@@ -115,11 +138,14 @@ const DataTable = <T extends object>(props: DataTableProps<T>) => {
115
138
  handler: async (values, args) => {
116
139
  if (typeof props.items === "function") {
117
140
  const response = await props.items(
118
- values as Record<string, string> & {
141
+ values as Static<Filters> & {
119
142
  page: number;
120
143
  size: number;
121
144
  sort?: string;
122
145
  },
146
+ {
147
+ items: items.content,
148
+ },
123
149
  );
124
150
 
125
151
  if (props.infinityScroll && values.page > 0) {
@@ -131,8 +157,8 @@ const DataTable = <T extends object>(props: DataTableProps<T>) => {
131
157
  } else {
132
158
  setItems(response);
133
159
  }
160
+
134
161
  setCurrentPage(values.page);
135
- setIsLoadingMore(false);
136
162
  }
137
163
  },
138
164
  onReset: async () => {
@@ -153,21 +179,20 @@ const DataTable = <T extends object>(props: DataTableProps<T>) => {
153
179
  return;
154
180
  }
155
181
 
156
- //submitDebounce();
182
+ props.onFilterChange?.(key, value, form as any);
157
183
  },
158
184
  },
159
- [],
185
+ [items],
160
186
  );
161
187
 
162
188
  const submitDebounce = useDebouncedCallback(() => form.submit(), {
163
- delay: 1000,
189
+ delay: 800,
164
190
  });
165
191
 
166
192
  const dt = useInject(DateTimeProvider);
167
193
 
168
194
  useEffect(() => {
169
195
  if (props.submitOnInit) {
170
- console.log("submitting");
171
196
  form.submit();
172
197
  }
173
198
  if (props.submitEvery) {
@@ -189,19 +214,18 @@ const DataTable = <T extends object>(props: DataTableProps<T>) => {
189
214
  if (!props.infinityScroll || typeof props.items !== "function") return;
190
215
 
191
216
  const handleScroll = () => {
192
- if (isLoadingMore) return;
217
+ if (form.submitting) return;
193
218
 
194
219
  const scrollTop = window.scrollY;
195
220
  const windowHeight = window.innerHeight;
196
221
  const docHeight = document.documentElement.scrollHeight;
197
222
 
198
- const isNearBottom = scrollTop + windowHeight >= docHeight - 200;
223
+ const isNearBottom = scrollTop + windowHeight >= docHeight - 300;
199
224
 
200
225
  if (isNearBottom) {
201
226
  const totalPages = items.page?.totalPages ?? 1;
202
227
 
203
228
  if (currentPage + 1 < totalPages) {
204
- setIsLoadingMore(true);
205
229
  form.input.page.set(currentPage + 1);
206
230
  }
207
231
  }
@@ -211,14 +235,24 @@ const DataTable = <T extends object>(props: DataTableProps<T>) => {
211
235
  return () => window.removeEventListener("scroll", handleScroll);
212
236
  }, [
213
237
  props.infinityScroll,
214
- isLoadingMore,
238
+ form.submitting,
215
239
  items.page?.totalPages,
216
240
  currentPage,
217
241
  form,
218
242
  ]);
219
243
 
220
244
  const head = Object.entries(props.columns).map(([key, col]) => (
221
- <Table.Th key={key}>
245
+ <Table.Th
246
+ key={key}
247
+ style={{
248
+ ...(col.fit
249
+ ? {
250
+ width: "1%",
251
+ whiteSpace: "nowrap",
252
+ }
253
+ : {}),
254
+ }}
255
+ >
222
256
  <ActionButton justify={"space-between"} radius={0} fullWidth size={"xs"}>
223
257
  {col.label}
224
258
  </ActionButton>
@@ -232,7 +266,13 @@ const DataTable = <T extends object>(props: DataTableProps<T>) => {
232
266
  return (
233
267
  <Table.Tr key={JSON.stringify(item)} {...trProps}>
234
268
  {Object.entries(props.columns).map(([key, col]) => (
235
- <Table.Td key={key}>{col.value(item as T, index)}</Table.Td>
269
+ <Table.Td key={key}>
270
+ {col.value(item as T, {
271
+ index,
272
+ form: form as unknown as FormModel<Filters>,
273
+ alepha,
274
+ })}
275
+ </Table.Td>
236
276
  ))}
237
277
  </Table.Tr>
238
278
  );
@@ -242,15 +282,29 @@ const DataTable = <T extends object>(props: DataTableProps<T>) => {
242
282
 
243
283
  return (
244
284
  <Flex direction={"column"} gap={"sm"} flex={1}>
245
- <Paper withBorder p={"sm"}>
246
- {props.filters ? <TypeForm form={form} schema={schema} /> : null}
247
- </Paper>
248
- <Table striped stripedColor={""} {...props.tableProps}>
249
- <Table.Thead>
250
- <Table.Tr>{head}</Table.Tr>
251
- </Table.Thead>
252
- <Table.Tbody>{rows}</Table.Tbody>
253
- </Table>
285
+ {props.filters ? (
286
+ <TypeForm
287
+ {...props.typeFormProps}
288
+ form={form as unknown as FormModel<Filters>}
289
+ schema={schema}
290
+ />
291
+ ) : null}
292
+
293
+ <Flex flex={1} className={"overflow-auto"}>
294
+ <Table
295
+ striped
296
+ withRowBorders
297
+ withColumnBorders
298
+ withTableBorder
299
+ stripedColor={""}
300
+ {...props.tableProps}
301
+ >
302
+ <Table.Thead>
303
+ <Table.Tr>{head}</Table.Tr>
304
+ </Table.Thead>
305
+ <Table.Tbody>{rows}</Table.Tbody>
306
+ </Table>
307
+ </Flex>
254
308
 
255
309
  {!props.infinityScroll && (
256
310
  <Flex justify={"space-between"} align={"center"}>
package/src/index.ts CHANGED
@@ -23,6 +23,7 @@ export type {
23
23
  export { default as ActionButton } from "./components/buttons/ActionButton.tsx";
24
24
  export { default as DarkModeButton } from "./components/buttons/DarkModeButton.tsx";
25
25
  export { default as OmnibarButton } from "./components/buttons/OmnibarButton.tsx";
26
+ export { default as JsonViewer } from "./components/data/JsonViewer.tsx";
26
27
  export { default as AlertDialog } from "./components/dialogs/AlertDialog.tsx";
27
28
  export { default as ConfirmDialog } from "./components/dialogs/ConfirmDialog.tsx";
28
29
  export { default as PromptDialog } from "./components/dialogs/PromptDialog.tsx";
@@ -1,9 +1,11 @@
1
- import type { ModalProps } from "@mantine/core";
1
+ import { Flex, type ModalProps } from "@mantine/core";
2
2
  import { modals } from "@mantine/modals";
3
3
  import type { ReactNode } from "react";
4
+ import JsonViewer from "../components/data/JsonViewer.tsx";
4
5
  import AlertDialog from "../components/dialogs/AlertDialog";
5
6
  import ConfirmDialog from "../components/dialogs/ConfirmDialog";
6
7
  import PromptDialog from "../components/dialogs/PromptDialog";
8
+ import { ui } from "../constants/ui.ts";
7
9
 
8
10
  // Base interfaces
9
11
  export interface BaseDialogOptions extends Partial<ModalProps> {
@@ -161,7 +163,16 @@ export class DialogService {
161
163
  * Show a JSON editor/viewer dialog
162
164
  */
163
165
  public json(data?: any, options?: BaseDialogOptions): void {
164
- // Implementation to be added
166
+ this.open({
167
+ size: "lg",
168
+ title: options?.title || "Json Viewer",
169
+ ...options,
170
+ content: (
171
+ <Flex bdrs={"md"} w={"100%"} flex={1} p={"sm"} bg={ui.colors.surface}>
172
+ <JsonViewer size={"xs"} data={data} />
173
+ </Flex>
174
+ ),
175
+ });
165
176
  }
166
177
 
167
178
  /**
@@ -51,7 +51,7 @@ export function extractSchemaFields(
51
51
  // Determine the display type - use format for datetime-related fields
52
52
  const format = "format" in fieldSchema ? fieldSchema.format : undefined;
53
53
  const baseType =
54
- "type" in fieldSchema ? (fieldSchema.type as string) : "unknown";
54
+ "type" in fieldSchema ? (fieldSchema.type as string) : "object";
55
55
 
56
56
  let displayType = baseType;
57
57
  if (format === "date-time") {
@@ -76,6 +76,7 @@ export function extractSchemaFields(
76
76
  // Handle enum
77
77
  if ("enum" in fieldSchema && fieldSchema.enum) {
78
78
  field.enum = fieldSchema.enum;
79
+ field.type = "enum";
79
80
  }
80
81
 
81
82
  // Handle nested objects
@@ -1,3 +0,0 @@
1
- import { t as AlephaMantineProvider_default } from "./AlephaMantineProvider-Be0DAazb.js";
2
-
3
- export { AlephaMantineProvider_default as default };