ding-react-admin 2.0.8 → 2.1.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/README.md +1 -1
- package/dist/index.css +1 -1
- package/dist/index.js +1604 -1119
- package/dist/src/components/AppHub.d.ts +8 -0
- package/dist/src/components/AppHub.d.ts.map +1 -0
- package/dist/src/components/AppLauncherButton.d.ts +7 -0
- package/dist/src/components/AppLauncherButton.d.ts.map +1 -0
- package/dist/src/components/index.d.ts +2 -0
- package/dist/src/components/index.d.ts.map +1 -1
- package/dist/src/crud/InlineFormSet.d.ts +6 -2
- package/dist/src/crud/InlineFormSet.d.ts.map +1 -1
- package/dist/src/crud/ResourceList.d.ts +6 -1
- package/dist/src/crud/ResourceList.d.ts.map +1 -1
- package/dist/src/crud/context/FilterContext.d.ts +4 -1
- package/dist/src/crud/context/FilterContext.d.ts.map +1 -1
- package/dist/src/crud/fields/BooleanField.d.ts +2 -1
- package/dist/src/crud/fields/BooleanField.d.ts.map +1 -1
- package/dist/src/crud/fields/DateField.d.ts.map +1 -1
- package/dist/src/crud/fields/DateTimeField.d.ts +5 -0
- package/dist/src/crud/fields/DateTimeField.d.ts.map +1 -0
- package/dist/src/crud/fields/ReferenceField.d.ts +3 -1
- package/dist/src/crud/fields/ReferenceField.d.ts.map +1 -1
- package/dist/src/crud/fields/ReferenceManyField.d.ts +4 -1
- package/dist/src/crud/fields/ReferenceManyField.d.ts.map +1 -1
- package/dist/src/crud/fields/TextAreaField.d.ts +19 -0
- package/dist/src/crud/fields/TextAreaField.d.ts.map +1 -0
- package/dist/src/crud/fields/TimeField.d.ts +11 -0
- package/dist/src/crud/fields/TimeField.d.ts.map +1 -0
- package/dist/src/crud/filters/ReferenceFilter.d.ts +1 -1
- package/dist/src/crud/filters/ReferenceFilter.d.ts.map +1 -1
- package/dist/src/crud/filters/TextFilter.d.ts +9 -1
- package/dist/src/crud/filters/TextFilter.d.ts.map +1 -1
- package/dist/src/crud/index.d.ts +6 -1
- package/dist/src/crud/index.d.ts.map +1 -1
- package/dist/src/crud/types.d.ts +9 -0
- package/dist/src/crud/types.d.ts.map +1 -1
- package/dist/src/crud/utils/parseDayjsValue.d.ts +3 -0
- package/dist/src/crud/utils/parseDayjsValue.d.ts.map +1 -0
- package/dist/src/crud/utils/referenceSelectDropdownProps.d.ts +11 -0
- package/dist/src/crud/utils/referenceSelectDropdownProps.d.ts.map +1 -0
- package/dist/src/crud/utils/useDebouncedValue.d.ts +3 -0
- package/dist/src/crud/utils/useDebouncedValue.d.ts.map +1 -0
- package/dist/src/index.d.ts +4 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/layouts/AdminLayout.d.ts +1 -1
- package/dist/src/layouts/AdminLayout.d.ts.map +1 -1
- package/dist/src/layouts/navMenuItems.d.ts +3 -0
- package/dist/src/layouts/navMenuItems.d.ts.map +1 -1
- package/dist/src/router/createAdminRouter.d.ts.map +1 -1
- package/dist/src/types.d.ts +23 -0
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/AppHub.tsx +47 -0
- package/src/components/AppLauncherButton.tsx +33 -0
- package/src/components/NavMenuLabel.tsx +1 -1
- package/src/components/index.ts +2 -0
- package/src/components/navMenu.css +107 -0
- package/src/context/AppThemeProvider.tsx +1 -1
- package/src/crud/InlineFormSet.tsx +32 -14
- package/src/crud/ResourceList.tsx +211 -35
- package/src/crud/context/FilterContext.tsx +7 -1
- package/src/crud/fields/BooleanField.tsx +3 -1
- package/src/crud/fields/DateField.tsx +6 -4
- package/src/crud/fields/DateTimeField.tsx +9 -0
- package/src/crud/fields/ReferenceField.tsx +10 -0
- package/src/crud/fields/ReferenceManyField.tsx +13 -1
- package/src/crud/fields/TextAreaField.tsx +58 -0
- package/src/crud/fields/TimeField.tsx +48 -0
- package/src/crud/filters/ReferenceFilter.tsx +10 -0
- package/src/crud/filters/TextFilter.tsx +80 -9
- package/src/crud/index.ts +9 -1
- package/src/crud/resourceListTable.css +113 -0
- package/src/crud/types.ts +9 -0
- package/src/crud/utils/parseDayjsValue.ts +14 -0
- package/src/crud/utils/referenceSelectDropdownProps.ts +30 -0
- package/src/crud/utils/useDebouncedValue.ts +17 -0
- package/src/index.ts +11 -0
- package/src/layouts/AdminLayout.tsx +47 -9
- package/src/layouts/navMenuItems.tsx +11 -2
- package/src/router/createAdminRouter.tsx +3 -1
- package/src/types.ts +24 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { SelectProps } from "antd";
|
|
2
|
+
|
|
3
|
+
export const REFERENCE_SELECT_POPUP_MIN_WIDTH = 360;
|
|
4
|
+
|
|
5
|
+
export type ReferenceSelectDropdownOptions = {
|
|
6
|
+
/** When false (default), the dropdown may be wider than the input. */
|
|
7
|
+
popupMatchSelectWidth?: boolean | number;
|
|
8
|
+
/** Minimum popup width in px when `popupMatchSelectWidth` is false. */
|
|
9
|
+
popupMinWidth?: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/** Ant Design Select props so reference dropdowns are not clipped to input width. */
|
|
13
|
+
export function referenceSelectDropdownProps(
|
|
14
|
+
options: ReferenceSelectDropdownOptions = {},
|
|
15
|
+
): Pick<SelectProps, "popupMatchSelectWidth" | "styles"> {
|
|
16
|
+
const popupMatchSelectWidth = options.popupMatchSelectWidth ?? false;
|
|
17
|
+
if (popupMatchSelectWidth !== false) {
|
|
18
|
+
return { popupMatchSelectWidth };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const minWidth = options.popupMinWidth ?? REFERENCE_SELECT_POPUP_MIN_WIDTH;
|
|
22
|
+
return {
|
|
23
|
+
popupMatchSelectWidth: false,
|
|
24
|
+
styles: {
|
|
25
|
+
popup: {
|
|
26
|
+
root: { minWidth },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
/** Returns `value` after it stays unchanged for `delayMs`. When `delayMs <= 0`, returns `value` immediately. */
|
|
4
|
+
export function useDebouncedValue<T>(value: T, delayMs: number): T {
|
|
5
|
+
const [debounced, setDebounced] = useState(value);
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (delayMs <= 0) {
|
|
9
|
+
setDebounced(value);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const timer = window.setTimeout(() => setDebounced(value), delayMs);
|
|
13
|
+
return () => window.clearTimeout(timer);
|
|
14
|
+
}, [value, delayMs]);
|
|
15
|
+
|
|
16
|
+
return delayMs <= 0 ? value : debounced;
|
|
17
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -89,6 +89,8 @@ export { LoginPage } from "./pages/LoginPage";
|
|
|
89
89
|
export { PlaceholderPage } from "./pages/PlaceholderPage";
|
|
90
90
|
export { AuthPageLayout } from "./layouts/AuthPageLayout";
|
|
91
91
|
export {
|
|
92
|
+
AppHub,
|
|
93
|
+
AppLauncherButton,
|
|
92
94
|
AuthAlternateLink,
|
|
93
95
|
DensitySwitch,
|
|
94
96
|
ThemeSwitch,
|
|
@@ -110,10 +112,13 @@ export type {
|
|
|
110
112
|
LoginCredentials,
|
|
111
113
|
AuthRedirects,
|
|
112
114
|
CreateAdminRouterOptions,
|
|
115
|
+
AppHubProps,
|
|
116
|
+
AppLauncherButtonProps,
|
|
113
117
|
AuthAlternateLinkProps,
|
|
114
118
|
AuthPageLayoutProps,
|
|
115
119
|
LoginPageProps,
|
|
116
120
|
NavItem,
|
|
121
|
+
NavMenuItemDivider,
|
|
117
122
|
RouteAccess,
|
|
118
123
|
ThemeDensity,
|
|
119
124
|
ThemeMode,
|
|
@@ -130,9 +135,12 @@ export {
|
|
|
130
135
|
FormSteps,
|
|
131
136
|
FormStep,
|
|
132
137
|
TextField,
|
|
138
|
+
TextAreaField,
|
|
133
139
|
NumberField,
|
|
134
140
|
BooleanField,
|
|
135
141
|
DateField,
|
|
142
|
+
DateTimeField,
|
|
143
|
+
TimeField,
|
|
136
144
|
SelectField,
|
|
137
145
|
PasswordField,
|
|
138
146
|
ReferenceField,
|
|
@@ -149,6 +157,7 @@ export {
|
|
|
149
157
|
ImageColumn,
|
|
150
158
|
CustomColumn,
|
|
151
159
|
TextFilter,
|
|
160
|
+
DEFAULT_TEXT_FILTER_DEBOUNCE_MS,
|
|
152
161
|
NumberFilter,
|
|
153
162
|
BooleanFilter,
|
|
154
163
|
DateFilter,
|
|
@@ -182,6 +191,7 @@ export type {
|
|
|
182
191
|
FormStepProps,
|
|
183
192
|
InlineFormSetLayout,
|
|
184
193
|
ResourceListProps,
|
|
194
|
+
TextFilterProps,
|
|
185
195
|
BaseSourceProps,
|
|
186
196
|
ChoiceOption,
|
|
187
197
|
ChoicesLoader,
|
|
@@ -196,6 +206,7 @@ export type {
|
|
|
196
206
|
InlineFormSetBaseProps,
|
|
197
207
|
InlineRowContext,
|
|
198
208
|
InlineFieldRegistration,
|
|
209
|
+
TextAreaFieldProps,
|
|
199
210
|
FieldWrapperProps,
|
|
200
211
|
ImageFieldProps,
|
|
201
212
|
FileFieldProps,
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
} from "./navFilter";
|
|
30
30
|
import { navItemsToAntdItems } from "./navMenuItems";
|
|
31
31
|
import { filterNavByPermission } from "../permissions/resourcePermissions";
|
|
32
|
-
import type { AdminLayoutProps, NavItem } from "../types";
|
|
32
|
+
import type { AdminLayoutProps, NavItem, NavMenuItemDivider } from "../types";
|
|
33
33
|
import "../components/navMenu.css";
|
|
34
34
|
|
|
35
35
|
const LIGHT_SIDEBAR_BG = "#001529";
|
|
@@ -87,15 +87,36 @@ type NavMenuProps = {
|
|
|
87
87
|
openKeys?: string[];
|
|
88
88
|
onOpenChange?: (keys: string[]) => void;
|
|
89
89
|
onNavigate: (key: string) => void;
|
|
90
|
+
wrapLabels?: boolean;
|
|
91
|
+
itemDivider?: NavMenuItemDivider;
|
|
90
92
|
};
|
|
91
93
|
|
|
92
|
-
|
|
94
|
+
function navMenuClassName({
|
|
95
|
+
wrapLabels,
|
|
96
|
+
itemDivider = "none",
|
|
97
|
+
}: {
|
|
98
|
+
wrapLabels?: boolean;
|
|
99
|
+
itemDivider?: NavMenuItemDivider;
|
|
100
|
+
}): string {
|
|
101
|
+
const classes = ["ding-admin-nav-menu"];
|
|
102
|
+
if (wrapLabels) classes.push("ding-admin-nav-menu--wrap-labels");
|
|
103
|
+
if (itemDivider === "full") {
|
|
104
|
+
classes.push("ding-admin-nav-menu--item-divider-full");
|
|
105
|
+
} else if (itemDivider === "inset") {
|
|
106
|
+
classes.push("ding-admin-nav-menu--item-divider-inset");
|
|
107
|
+
}
|
|
108
|
+
return classes.join(" ");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
type SiderNavPanelProps = Omit<NavMenuProps, "wrapLabels" | "itemDivider"> & {
|
|
93
112
|
navQuery: string;
|
|
94
113
|
onNavQueryChange: (value: string) => void;
|
|
95
114
|
showNavSearch: boolean;
|
|
96
115
|
navSearchPlaceholder?: string;
|
|
97
116
|
scrollVariant: "default" | "on-dark";
|
|
98
117
|
searchVariant: "on-dark" | "app";
|
|
118
|
+
wrapLabels?: boolean;
|
|
119
|
+
itemDivider?: NavMenuItemDivider;
|
|
99
120
|
};
|
|
100
121
|
|
|
101
122
|
function SiderNavPanel({
|
|
@@ -111,6 +132,8 @@ function SiderNavPanel({
|
|
|
111
132
|
navSearchPlaceholder,
|
|
112
133
|
scrollVariant,
|
|
113
134
|
searchVariant,
|
|
135
|
+
wrapLabels,
|
|
136
|
+
itemDivider,
|
|
114
137
|
}: SiderNavPanelProps) {
|
|
115
138
|
return (
|
|
116
139
|
<>
|
|
@@ -138,6 +161,8 @@ function SiderNavPanel({
|
|
|
138
161
|
openKeys={openKeys}
|
|
139
162
|
onOpenChange={onOpenChange}
|
|
140
163
|
onNavigate={onNavigate}
|
|
164
|
+
wrapLabels={wrapLabels}
|
|
165
|
+
itemDivider={itemDivider}
|
|
141
166
|
/>
|
|
142
167
|
</ScrollableArea>
|
|
143
168
|
</>
|
|
@@ -151,15 +176,17 @@ function NavMenu({
|
|
|
151
176
|
openKeys,
|
|
152
177
|
onOpenChange,
|
|
153
178
|
onNavigate,
|
|
179
|
+
wrapLabels,
|
|
180
|
+
itemDivider,
|
|
154
181
|
}: NavMenuProps) {
|
|
155
182
|
return (
|
|
156
183
|
<Menu
|
|
157
|
-
className=
|
|
184
|
+
className={navMenuClassName({ wrapLabels, itemDivider })}
|
|
158
185
|
mode="inline"
|
|
159
186
|
theme="dark"
|
|
160
187
|
inlineCollapsed={inlineCollapsed}
|
|
161
188
|
selectedKeys={selectedKeys}
|
|
162
|
-
tooltip={{ placement: "right", mouseEnterDelay:
|
|
189
|
+
tooltip={{ placement: "right", mouseEnterDelay: 0 }}
|
|
163
190
|
{...(!inlineCollapsed && openKeys !== undefined && onOpenChange
|
|
164
191
|
? { openKeys, onOpenChange }
|
|
165
192
|
: {})}
|
|
@@ -181,6 +208,8 @@ export function AdminLayout({
|
|
|
181
208
|
loginPath = "/login",
|
|
182
209
|
siderCollapsedStorageKey = DEFAULT_SIDER_KEY,
|
|
183
210
|
navSearch = true,
|
|
211
|
+
navMenu,
|
|
212
|
+
hideSider = false,
|
|
184
213
|
}: AdminLayoutProps) {
|
|
185
214
|
const navigate = useNavigate();
|
|
186
215
|
const location = useLocation();
|
|
@@ -200,6 +229,9 @@ export function AdminLayout({
|
|
|
200
229
|
const navSearchEnabled = navSearch !== false;
|
|
201
230
|
const navSearchPlaceholder =
|
|
202
231
|
typeof navSearch === "object" ? navSearch.placeholder : undefined;
|
|
232
|
+
const navMenuWrapLabels = navMenu?.wrapLabels !== false;
|
|
233
|
+
const navMenuItemDivider: NavMenuItemDivider =
|
|
234
|
+
navMenu?.itemDivider ?? "inset";
|
|
203
235
|
|
|
204
236
|
const drawerTitle = mobileDrawerTitle ?? brand;
|
|
205
237
|
|
|
@@ -260,9 +292,11 @@ export function AdminLayout({
|
|
|
260
292
|
const menuItems: MenuProps["items"] = useMemo(
|
|
261
293
|
() =>
|
|
262
294
|
navItemsToAntdItems(displayNavItems, {
|
|
263
|
-
showLabelTooltip: !collapsed,
|
|
295
|
+
showLabelTooltip: !collapsed && !navMenuWrapLabels,
|
|
296
|
+
wrapLabels: navMenuWrapLabels && !collapsed,
|
|
297
|
+
collapsed,
|
|
264
298
|
}),
|
|
265
|
-
[displayNavItems, collapsed],
|
|
299
|
+
[displayNavItems, collapsed, navMenuWrapLabels],
|
|
266
300
|
);
|
|
267
301
|
|
|
268
302
|
const filteredOpenKeys = useMemo(
|
|
@@ -335,7 +369,7 @@ export function AdminLayout({
|
|
|
335
369
|
background: token.colorBgLayout,
|
|
336
370
|
}}
|
|
337
371
|
>
|
|
338
|
-
{!isMobile && (
|
|
372
|
+
{!hideSider && !isMobile && (
|
|
339
373
|
<Layout.Sider
|
|
340
374
|
collapsible
|
|
341
375
|
collapsed={collapsed}
|
|
@@ -388,11 +422,13 @@ export function AdminLayout({
|
|
|
388
422
|
navSearchPlaceholder={navSearchPlaceholder}
|
|
389
423
|
scrollVariant={siderScrollVariant}
|
|
390
424
|
searchVariant={siderSearchVariant}
|
|
425
|
+
wrapLabels={navMenuWrapLabels}
|
|
426
|
+
itemDivider={navMenuItemDivider}
|
|
391
427
|
/>
|
|
392
428
|
</div>
|
|
393
429
|
</Layout.Sider>
|
|
394
430
|
)}
|
|
395
|
-
{isMobile && (
|
|
431
|
+
{!hideSider && isMobile && (
|
|
396
432
|
<Drawer
|
|
397
433
|
title={
|
|
398
434
|
<Typography.Text
|
|
@@ -436,6 +472,8 @@ export function AdminLayout({
|
|
|
436
472
|
navSearchPlaceholder={navSearchPlaceholder}
|
|
437
473
|
scrollVariant={siderScrollVariant}
|
|
438
474
|
searchVariant={siderSearchVariant}
|
|
475
|
+
wrapLabels={navMenuWrapLabels}
|
|
476
|
+
itemDivider={navMenuItemDivider}
|
|
439
477
|
/>
|
|
440
478
|
</div>
|
|
441
479
|
</Drawer>
|
|
@@ -461,7 +499,7 @@ export function AdminLayout({
|
|
|
461
499
|
flexShrink: 0,
|
|
462
500
|
}}
|
|
463
501
|
>
|
|
464
|
-
{isMobile && (
|
|
502
|
+
{!hideSider && isMobile && (
|
|
465
503
|
<Button
|
|
466
504
|
type="text"
|
|
467
505
|
icon={<MenuOutlined />}
|
|
@@ -5,9 +5,16 @@ import { getNavItemLabel } from "./navFilter";
|
|
|
5
5
|
|
|
6
6
|
export function navItemsToAntdItems(
|
|
7
7
|
items: NavItem[],
|
|
8
|
-
options?: {
|
|
8
|
+
options?: {
|
|
9
|
+
showLabelTooltip?: boolean;
|
|
10
|
+
wrapLabels?: boolean;
|
|
11
|
+
/** Collapsed sider: native `title` for icon tooltips only. */
|
|
12
|
+
collapsed?: boolean;
|
|
13
|
+
},
|
|
9
14
|
): NonNullable<MenuProps["items"]> {
|
|
10
15
|
const showLabelTooltip = options?.showLabelTooltip !== false;
|
|
16
|
+
const wrapLabels = options?.wrapLabels === true;
|
|
17
|
+
const collapsed = options?.collapsed === true;
|
|
11
18
|
|
|
12
19
|
return items.map((item) => {
|
|
13
20
|
const IconComp = item.Icon;
|
|
@@ -16,6 +23,8 @@ export function navItemsToAntdItems(
|
|
|
16
23
|
const label =
|
|
17
24
|
title && showLabelTooltip ? (
|
|
18
25
|
<NavMenuLabel label={item.label} title={title} />
|
|
26
|
+
) : wrapLabels ? (
|
|
27
|
+
<NavMenuLabel label={item.label} title="" />
|
|
19
28
|
) : (
|
|
20
29
|
item.label
|
|
21
30
|
);
|
|
@@ -23,7 +32,7 @@ export function navItemsToAntdItems(
|
|
|
23
32
|
// Expanded: NavMenuLabel tooltip only (omit `title` to avoid native DOM tooltip).
|
|
24
33
|
// Collapsed: string title for the sider icon tooltip.
|
|
25
34
|
const itemTitle: { title?: string } =
|
|
26
|
-
|
|
35
|
+
collapsed && title ? { title } : {};
|
|
27
36
|
|
|
28
37
|
if (item.children?.length) {
|
|
29
38
|
return {
|
|
@@ -60,5 +60,7 @@ export function createAdminRouter({
|
|
|
60
60
|
element: <Navigate to={homePath} replace />,
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
return createBrowserRouter(routerRoutes
|
|
63
|
+
return createBrowserRouter(routerRoutes, {
|
|
64
|
+
basename: import.meta.env.BASE_URL.replace(/\/$/, "") || undefined,
|
|
65
|
+
});
|
|
64
66
|
}
|
package/src/types.ts
CHANGED
|
@@ -66,6 +66,8 @@ export type AppThemeProviderProps = {
|
|
|
66
66
|
densityStorageKey?: string;
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
+
export type NavMenuItemDivider = "none" | "full" | "inset";
|
|
70
|
+
|
|
69
71
|
export type AdminLayoutProps = {
|
|
70
72
|
navItems: NavItem[];
|
|
71
73
|
/** Shown in sider header when expanded; drawer title on mobile. */
|
|
@@ -87,9 +89,31 @@ export type AdminLayoutProps = {
|
|
|
87
89
|
* Use an object to customize the placeholder.
|
|
88
90
|
*/
|
|
89
91
|
navSearch?: boolean | { placeholder?: string };
|
|
92
|
+
/**
|
|
93
|
+
* Sidebar menu presentation.
|
|
94
|
+
*/
|
|
95
|
+
navMenu?: {
|
|
96
|
+
/**
|
|
97
|
+
* Wrap long labels onto multiple lines instead of truncating with ellipsis.
|
|
98
|
+
* Default **`true`**. Pass **`false`** for ellipsis + Ant Design hover tooltip.
|
|
99
|
+
*/
|
|
100
|
+
wrapLabels?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Divider between sibling rows at each submenu level.
|
|
103
|
+
* Default **`"inset"`**.
|
|
104
|
+
*/
|
|
105
|
+
itemDivider?: NavMenuItemDivider;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Hide the sidebar and mobile nav drawer (e.g. on an Odoo-style app hub home).
|
|
109
|
+
* Default **`false`**.
|
|
110
|
+
*/
|
|
111
|
+
hideSider?: boolean;
|
|
90
112
|
};
|
|
91
113
|
|
|
92
114
|
export type { AuthPageLayoutProps } from "./layouts/AuthPageLayout";
|
|
115
|
+
export type { AppHubProps } from "./components/AppHub";
|
|
116
|
+
export type { AppLauncherButtonProps } from "./components/AppLauncherButton";
|
|
93
117
|
export type { AuthAlternateLinkProps } from "./components/AuthAlternateLink";
|
|
94
118
|
|
|
95
119
|
export type LoginPageProps = {
|