@abpjs/theme-basic 2.2.0 → 2.7.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.
@@ -4,4 +4,7 @@ export * from './layout-account';
4
4
  export * from './layout-empty';
5
5
  export * from './change-password';
6
6
  export * from './profile';
7
+ export * from './logo';
8
+ export * from './nav-items';
9
+ export * from './routes';
7
10
  export * from './blocks/sidebars/sidebar-with-collapsible';
@@ -56,5 +56,8 @@ export interface LayoutApplicationProps {
56
56
  export declare function LayoutApplication({ showLanguageSelector, showCurrentUser, showSearch, showHelpCenter, helpCenterUrl, showSettings, settingsUrl, defaultIcon, headerContent, footerContent, children, }: LayoutApplicationProps): React.ReactElement;
57
57
  export declare namespace LayoutApplication {
58
58
  var type: eLayoutType;
59
+ var logoComponentKey: "Theme.LogoComponent";
60
+ var routesComponentKey: "Theme.RoutesComponent";
61
+ var navItemsComponentKey: "Theme.NavItemsComponent";
59
62
  }
60
63
  export default LayoutApplication;
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ /**
3
+ * Props for the LogoComponent.
4
+ * @since 2.7.0
5
+ */
6
+ export interface LogoComponentProps {
7
+ /** Custom styles for the logo container */
8
+ style?: React.CSSProperties;
9
+ /** Custom link path (overrides branding logoLink if provided) */
10
+ linkTo?: string;
11
+ }
12
+ /**
13
+ * Public API Logo component that displays the application logo.
14
+ * Uses the branding configuration from BrandingProvider.
15
+ *
16
+ * This component is part of the theme-basic public API and can be
17
+ * replaced using the component replacement system with eThemeBasicComponents.Logo.
18
+ *
19
+ * @since 2.7.0 - Added as public API component
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * // Basic usage
24
+ * <LogoComponent />
25
+ *
26
+ * // With custom link
27
+ * <LogoComponent linkTo="/dashboard" />
28
+ * ```
29
+ */
30
+ export declare function LogoComponent({ style, linkTo }: LogoComponentProps): React.ReactElement;
31
+ export default LogoComponent;
@@ -0,0 +1,2 @@
1
+ export { LogoComponent, type LogoComponentProps } from './LogoComponent';
2
+ export { LogoComponent as default } from './LogoComponent';
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import { UserProfileProps } from '../blocks/sidebars/sidebar-with-collapsible/user-profile';
3
+ /**
4
+ * Props for the NavItemsComponent.
5
+ * @since 2.7.0
6
+ */
7
+ export interface NavItemsComponentProps {
8
+ /** Whether the screen is small (mobile) */
9
+ smallScreen?: boolean;
10
+ /** User profile action handlers */
11
+ userProfileProps?: UserProfileProps;
12
+ /** Whether to show language selector */
13
+ showLanguageSelector?: boolean;
14
+ /** Whether to show current user */
15
+ showCurrentUser?: boolean;
16
+ }
17
+ /**
18
+ * Public API component for navigation items (language selector, user menu, custom elements).
19
+ * Handles the registration of built-in navigation elements and renders custom elements.
20
+ *
21
+ * This component is part of the theme-basic public API and can be
22
+ * replaced using the component replacement system with eThemeBasicComponents.NavItems.
23
+ *
24
+ * @since 2.7.0 - Added as public API component
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * <NavItemsComponent
29
+ * showLanguageSelector={true}
30
+ * showCurrentUser={true}
31
+ * userProfileProps={{
32
+ * onLogout: () => logout(),
33
+ * onChangePassword: () => openChangePassword(),
34
+ * }}
35
+ * />
36
+ * ```
37
+ */
38
+ export declare function NavItemsComponent({ smallScreen, userProfileProps, showLanguageSelector, showCurrentUser, }: NavItemsComponentProps): React.ReactElement;
39
+ export default NavItemsComponent;
@@ -0,0 +1,2 @@
1
+ export { NavItemsComponent, type NavItemsComponentProps } from './NavItemsComponent';
2
+ export { NavItemsComponent as default } from './NavItemsComponent';
@@ -0,0 +1,40 @@
1
+ import React, { ReactNode } from 'react';
2
+ import { ABP } from '@abpjs/core';
3
+ /**
4
+ * Props for the RoutesComponent.
5
+ * @since 2.7.0
6
+ */
7
+ export interface RoutesComponentProps {
8
+ /** Whether the screen is small (mobile) */
9
+ smallScreen?: boolean;
10
+ /** Whether to use dynamic dropdown positioning for children */
11
+ isDropdownChildDynamic?: boolean;
12
+ /** Default icon for routes without specific icons */
13
+ defaultIcon?: ReactNode;
14
+ /** Custom routes to display (overrides config routes) */
15
+ routes?: ABP.FullRoute[];
16
+ }
17
+ /**
18
+ * Public API component for rendering navigation routes.
19
+ * Displays the visible routes from the config with support for
20
+ * collapsible child routes.
21
+ *
22
+ * This component is part of the theme-basic public API and can be
23
+ * replaced using the component replacement system with eThemeBasicComponents.Routes.
24
+ *
25
+ * @since 2.7.0 - Added as public API component
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * // Basic usage - uses routes from config
30
+ * <RoutesComponent />
31
+ *
32
+ * // With custom default icon
33
+ * <RoutesComponent defaultIcon={<LuHome />} />
34
+ *
35
+ * // With custom routes
36
+ * <RoutesComponent routes={customRoutes} />
37
+ * ```
38
+ */
39
+ export declare function RoutesComponent({ smallScreen, isDropdownChildDynamic, defaultIcon, routes: customRoutes, }: RoutesComponentProps): React.ReactElement;
40
+ export default RoutesComponent;
@@ -0,0 +1,2 @@
1
+ export { RoutesComponent, type RoutesComponentProps } from './RoutesComponent';
2
+ export { RoutesComponent as default } from './RoutesComponent';
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Theme basic component key constants.
3
+ * Used for component replacement/customization in the theme system.
4
+ *
5
+ * @since 2.7.0
6
+ */
7
+ export declare const eThemeBasicComponents: {
8
+ readonly ApplicationLayout: "Theme.ApplicationLayoutComponent";
9
+ readonly AccountLayout: "Theme.AccountLayoutComponent";
10
+ readonly EmptyLayout: "Theme.EmptyLayoutComponent";
11
+ readonly Logo: "Theme.LogoComponent";
12
+ readonly Routes: "Theme.RoutesComponent";
13
+ readonly NavItems: "Theme.NavItemsComponent";
14
+ };
15
+ /**
16
+ * Type for eThemeBasicComponents values
17
+ */
18
+ export type eThemeBasicComponents = (typeof eThemeBasicComponents)[keyof typeof eThemeBasicComponents];
@@ -0,0 +1,2 @@
1
+ export * from './components';
2
+ export * from './navigation-element-names';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Navigation element name constants.
3
+ * Used for identifying built-in navigation elements in the layout.
4
+ *
5
+ * @since 2.7.0
6
+ */
7
+ export declare const eNavigationElementNames: {
8
+ readonly Language: "LanguageRef";
9
+ readonly User: "CurrentUserRef";
10
+ };
11
+ /**
12
+ * Type for eNavigationElementNames values
13
+ */
14
+ export type eNavigationElementNames = (typeof eNavigationElementNames)[keyof typeof eNavigationElementNames];
package/dist/index.d.ts CHANGED
@@ -2,29 +2,48 @@
2
2
  * @abpjs/theme-basic
3
3
  *
4
4
  * ABP Framework Theme Basic components for React.
5
- * Translated from @abp/ng.theme.basic v2.2.0
5
+ * Translated from @abp/ng.theme.basic v2.7.0
6
6
  *
7
7
  * This package provides the basic theme layout components for ABP React applications.
8
8
  *
9
+ * Changes in v2.7.0:
10
+ * - Added eThemeBasicComponents enum for component replacement keys
11
+ * - Added eNavigationElementNames enum for built-in navigation element names
12
+ * - Added LogoComponent public API component
13
+ * - Added NavItemsComponent public API component
14
+ * - Added RoutesComponent public API component
15
+ * - Added LayoutStateService (useLayoutStateService hook)
16
+ * - ApplicationLayoutComponent now exposes component keys (logoComponentKey, routesComponentKey, navItemsComponentKey)
17
+ * - Dependency update to @abp/ng.theme.shared v2.7.0
18
+ *
19
+ * Changes in v2.4.0:
20
+ * - Angular: InitialService now uses DomInsertionService instead of LazyLoadService
21
+ * - Angular: appendStyle() now returns void instead of Observable<void>
22
+ * - Dependency update to @abp/ng.theme.shared v2.4.0
23
+ * - No impact on React translation (styles handled via Chakra UI)
24
+ *
9
25
  * Changes in v2.2.0:
10
26
  * - Angular: Added .ui-table .pagination-wrapper styles (handled by Chakra UI in React)
11
27
  * - Dependency update to @abp/ng.theme.shared v2.2.0
12
28
  * - No functional code changes
13
29
  *
14
- * @version 2.2.0
30
+ * @version 2.7.0
15
31
  * @since 2.0.0 - Removed legacy .abp-confirm styles (no impact on React - we use Chakra UI)
16
32
  * @since 2.1.0 - Angular: OAuthService replaced with AuthService (already using useAuth in React)
17
33
  * - Angular: Added styles for loading, modal-backdrop, confirmation (handled by Chakra UI)
18
34
  */
19
35
  export * from './models';
36
+ export * from './enums';
37
+ export * from './services';
20
38
  export { LayoutProvider, useLayoutContext, useLayoutService, useNavigationElements, BrandingProvider, useBranding, useLogo, } from './contexts';
21
39
  export type { LayoutService, LayoutContextValue, LayoutProviderProps, BrandingConfig, BrandingContextValue, BrandingProviderProps, } from './contexts';
22
40
  export * from './hooks';
23
41
  export * from './components';
24
42
  export * from './providers';
25
43
  import { LayoutApplication } from './components/layout-application';
44
+ import { LayoutAccount } from './components/layout-account';
26
45
  /**
27
46
  * Array of layout components for use with ABP's dynamic layout system.
28
47
  * Matches the Angular LAYOUTS constant.
29
48
  */
30
- export declare const LAYOUTS: (typeof LayoutApplication)[];
49
+ export declare const LAYOUTS: (typeof LayoutApplication | typeof LayoutAccount)[];
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -32,11 +42,14 @@ __export(index_exports, {
32
42
  LayoutEmpty: () => LayoutEmpty,
33
43
  LayoutProvider: () => LayoutProvider,
34
44
  Logo: () => Logo,
45
+ LogoComponent: () => LogoComponent,
35
46
  LogoIcon: () => LogoIcon,
36
47
  LogoText: () => LogoText,
48
+ NavItemsComponent: () => NavItemsComponent,
37
49
  NavLinks: () => NavLinks,
38
50
  Navbar: () => Navbar,
39
51
  Profile: () => import_theme_shared2.Profile,
52
+ RoutesComponent: () => RoutesComponent,
40
53
  SIDEBAR_Z_INDEX: () => SIDEBAR_Z_INDEX,
41
54
  SearchField: () => SearchField,
42
55
  SearchProvider: () => SearchProvider,
@@ -46,15 +59,34 @@ __export(index_exports, {
46
59
  UserProfile: () => UserProfile,
47
60
  defaultThemeBasicConfig: () => defaultThemeBasicConfig,
48
61
  defineConfig: () => import_theme_shared3.defineConfig,
62
+ eNavigationElementNames: () => eNavigationElementNames,
63
+ eThemeBasicComponents: () => eThemeBasicComponents,
49
64
  useBranding: () => useBranding,
50
65
  useLayoutContext: () => useLayoutContext,
51
66
  useLayoutService: () => useLayoutService,
67
+ useLayoutStateService: () => useLayoutStateService,
52
68
  useLogo: () => useLogo,
53
69
  useNavigationElements: () => useNavigationElements,
54
70
  useSearch: () => useSearch
55
71
  });
56
72
  module.exports = __toCommonJS(index_exports);
57
73
 
74
+ // src/enums/components.ts
75
+ var eThemeBasicComponents = {
76
+ ApplicationLayout: "Theme.ApplicationLayoutComponent",
77
+ AccountLayout: "Theme.AccountLayoutComponent",
78
+ EmptyLayout: "Theme.EmptyLayoutComponent",
79
+ Logo: "Theme.LogoComponent",
80
+ Routes: "Theme.RoutesComponent",
81
+ NavItems: "Theme.NavItemsComponent"
82
+ };
83
+
84
+ // src/enums/navigation-element-names.ts
85
+ var eNavigationElementNames = {
86
+ Language: "LanguageRef",
87
+ User: "CurrentUserRef"
88
+ };
89
+
58
90
  // src/contexts/layout.context.tsx
59
91
  var import_react = require("react");
60
92
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -123,6 +155,16 @@ function useNavigationElements() {
123
155
  return useLayoutContext().state.navigationElements;
124
156
  }
125
157
 
158
+ // src/services/layout-state.service.ts
159
+ function useLayoutStateService() {
160
+ const { state, service } = useLayoutContext();
161
+ return {
162
+ getNavigationElements: () => state.navigationElements,
163
+ dispatchAddNavigationElement: (elements) => service.addNavigationElement(elements),
164
+ dispatchRemoveNavigationElementByName: (name) => service.removeNavigationElement(name)
165
+ };
166
+ }
167
+
126
168
  // src/contexts/branding.context.tsx
127
169
  var import_react2 = require("react");
128
170
  var import_jsx_runtime2 = require("react/jsx-runtime");
@@ -792,6 +834,9 @@ function LayoutApplication({
792
834
  const navigate = (0, import_react_router_dom6.useNavigate)();
793
835
  const { logout } = (0, import_core6.useAuth)();
794
836
  const { direction, isRtl } = (0, import_core6.useDirection)();
837
+ const logoComponentKey = eThemeBasicComponents.Logo;
838
+ const routesComponentKey = eThemeBasicComponents.Routes;
839
+ const navItemsComponentKey = eThemeBasicComponents.NavItems;
795
840
  const [isChangePasswordOpen, setIsChangePasswordOpen] = (0, import_react16.useState)(false);
796
841
  const [isProfileOpen, setIsProfileOpen] = (0, import_react16.useState)(false);
797
842
  const handleLogout = (0, import_react16.useCallback)(() => {
@@ -874,6 +919,9 @@ function LayoutApplication({
874
919
  ] });
875
920
  }
876
921
  LayoutApplication.type = import_core6.eLayoutType.application;
922
+ LayoutApplication.logoComponentKey = eThemeBasicComponents.Logo;
923
+ LayoutApplication.routesComponentKey = eThemeBasicComponents.Routes;
924
+ LayoutApplication.navItemsComponentKey = eThemeBasicComponents.NavItems;
877
925
 
878
926
  // src/components/layout-account/LayoutAccount.tsx
879
927
  var import_react18 = require("@chakra-ui/react");
@@ -961,19 +1009,124 @@ function LayoutEmpty({ children }) {
961
1009
  }
962
1010
  LayoutEmpty.type = import_core8.eLayoutType.empty;
963
1011
 
1012
+ // src/components/logo/LogoComponent.tsx
1013
+ var import_react20 = require("@chakra-ui/react");
1014
+ var import_react_router_dom9 = require("react-router-dom");
1015
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1016
+ function LogoComponent({ style, linkTo }) {
1017
+ const { logo, appName, logoLink } = useBranding();
1018
+ const logoContent = logo || (appName ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react20.Text, { fontWeight: "bold", fontSize: "lg", children: appName }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DefaultLogo, { style }));
1019
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react20.Box, { asChild: true, style, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_router_dom9.Link, { to: linkTo || logoLink || "/", children: logoContent }) });
1020
+ }
1021
+
1022
+ // src/components/nav-items/NavItemsComponent.tsx
1023
+ var import_react21 = __toESM(require("react"));
1024
+ var import_react22 = require("@chakra-ui/react");
1025
+ var import_core9 = require("@abpjs/core");
1026
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1027
+ function NavItemsComponent({
1028
+ smallScreen = false,
1029
+ userProfileProps,
1030
+ showLanguageSelector = true,
1031
+ showCurrentUser = true
1032
+ }) {
1033
+ const { currentUser, localization } = (0, import_core9.useConfig)();
1034
+ const { isAuthenticated } = (0, import_core9.useAuth)();
1035
+ const { language, setLanguage } = (0, import_core9.useSession)();
1036
+ const { state, service } = useLayoutContext();
1037
+ const defaultLanguage = (0, import_react21.useMemo)(() => {
1038
+ const lang = localization?.languages?.find((l) => l.cultureName === language);
1039
+ return lang?.displayName || language || "";
1040
+ }, [localization, language]);
1041
+ const dropdownLanguages = (0, import_react21.useMemo)(() => {
1042
+ return localization?.languages?.filter((l) => l.cultureName !== language) || [];
1043
+ }, [localization, language]);
1044
+ const onChangeLang = (0, import_react21.useCallback)(
1045
+ (cultureName) => {
1046
+ setLanguage(cultureName);
1047
+ window.location.reload();
1048
+ },
1049
+ [setLanguage]
1050
+ );
1051
+ const logout = (0, import_react21.useCallback)(() => {
1052
+ userProfileProps?.onLogout?.();
1053
+ }, [userProfileProps]);
1054
+ (0, import_react21.useEffect)(() => {
1055
+ if (showLanguageSelector && localization?.languages && localization.languages.length > 1) {
1056
+ service.addNavigationElement({
1057
+ name: eNavigationElementNames.Language,
1058
+ element: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(LanguageSelector, {}, "language-selector"),
1059
+ order: 2
1060
+ });
1061
+ return () => {
1062
+ service.removeNavigationElement(eNavigationElementNames.Language);
1063
+ };
1064
+ }
1065
+ }, [service, showLanguageSelector, localization?.languages]);
1066
+ (0, import_react21.useEffect)(() => {
1067
+ if (showCurrentUser) {
1068
+ service.addNavigationElement({
1069
+ name: eNavigationElementNames.User,
1070
+ element: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(UserProfile, { ...userProfileProps }, "user-profile"),
1071
+ order: 3
1072
+ });
1073
+ return () => {
1074
+ service.removeNavigationElement(eNavigationElementNames.User);
1075
+ };
1076
+ }
1077
+ }, [service, showCurrentUser, userProfileProps]);
1078
+ const navElements = state.navigationElements;
1079
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react22.Stack, { gap: 2, width: "full", children: navElements.map((element) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react21.default.Fragment, { children: element.element }, element.name)) });
1080
+ }
1081
+
1082
+ // src/components/routes/RoutesComponent.tsx
1083
+ var import_react23 = require("react");
1084
+ var import_react24 = require("@chakra-ui/react");
1085
+ var import_core10 = require("@abpjs/core");
1086
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1087
+ function getVisibleRoutes2(routes) {
1088
+ return routes.reduce((acc, val) => {
1089
+ if (val.invisible) {
1090
+ return acc;
1091
+ }
1092
+ const route = { ...val };
1093
+ if (route.children && route.children.length) {
1094
+ route.children = getVisibleRoutes2(route.children);
1095
+ }
1096
+ return [...acc, route];
1097
+ }, []);
1098
+ }
1099
+ function RoutesComponent({
1100
+ smallScreen = false,
1101
+ isDropdownChildDynamic = false,
1102
+ defaultIcon,
1103
+ routes: customRoutes
1104
+ }) {
1105
+ const { routes: configRoutes } = (0, import_core10.useConfig)();
1106
+ const { direction } = (0, import_core10.useDirection)();
1107
+ const routes = customRoutes || configRoutes || [];
1108
+ const visibleRoutes = (0, import_react23.useMemo)(() => {
1109
+ return getVisibleRoutes2(routes);
1110
+ }, [routes]);
1111
+ const trackByFn = (index, item) => {
1112
+ return item.name || item.path || index;
1113
+ };
1114
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react24.Stack, { gap: "1", dir: direction, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(NavLinks, { defaultIcon }) });
1115
+ }
1116
+
964
1117
  // src/providers/ThemeBasicProvider.tsx
965
- var import_react20 = require("react");
1118
+ var import_react25 = require("react");
966
1119
  var import_theme_shared3 = require("@abpjs/theme-shared");
967
- var import_core9 = require("@abpjs/core");
968
- var import_jsx_runtime17 = require("react/jsx-runtime");
1120
+ var import_core11 = require("@abpjs/core");
1121
+ var import_jsx_runtime20 = require("react/jsx-runtime");
969
1122
  function LocaleSync({ children }) {
970
- const { language } = (0, import_core9.useSession)();
971
- const { direction } = (0, import_core9.useDirection)();
972
- (0, import_react20.useEffect)(() => {
1123
+ const { language } = (0, import_core11.useSession)();
1124
+ const { direction } = (0, import_core11.useDirection)();
1125
+ (0, import_react25.useEffect)(() => {
973
1126
  document.documentElement.dir = direction;
974
1127
  document.documentElement.lang = language || "en";
975
1128
  }, [direction, language]);
976
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_jsx_runtime17.Fragment, { children });
1129
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_jsx_runtime20.Fragment, { children });
977
1130
  }
978
1131
  function ThemeBasicInner({
979
1132
  children,
@@ -988,9 +1141,9 @@ function ThemeBasicInner({
988
1141
  appName,
989
1142
  logoLink
990
1143
  }) {
991
- const { language } = (0, import_core9.useSession)();
1144
+ const { language } = (0, import_core11.useSession)();
992
1145
  const locale = language || "en-US";
993
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1146
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
994
1147
  import_theme_shared3.ThemeSharedProvider,
995
1148
  {
996
1149
  renderToasts,
@@ -1000,14 +1153,14 @@ function ThemeBasicInner({
1000
1153
  enableColorMode,
1001
1154
  defaultColorMode,
1002
1155
  locale,
1003
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(LocaleSync, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1156
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LocaleSync, { children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1004
1157
  BrandingProvider,
1005
1158
  {
1006
1159
  logo,
1007
1160
  logoIcon,
1008
1161
  appName,
1009
1162
  logoLink,
1010
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(LayoutProvider, { children })
1163
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LayoutProvider, { children })
1011
1164
  }
1012
1165
  ) })
1013
1166
  }
@@ -1081,7 +1234,7 @@ function ThemeBasicProvider({
1081
1234
  logoLink
1082
1235
  }) {
1083
1236
  const mergedThemeOverrides = themeOverrides || defaultThemeBasicConfig;
1084
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1237
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1085
1238
  ThemeBasicInner,
1086
1239
  {
1087
1240
  renderToasts,
@@ -1115,11 +1268,14 @@ var LAYOUTS = [LayoutApplication, LayoutAccount, LayoutEmpty];
1115
1268
  LayoutEmpty,
1116
1269
  LayoutProvider,
1117
1270
  Logo,
1271
+ LogoComponent,
1118
1272
  LogoIcon,
1119
1273
  LogoText,
1274
+ NavItemsComponent,
1120
1275
  NavLinks,
1121
1276
  Navbar,
1122
1277
  Profile,
1278
+ RoutesComponent,
1123
1279
  SIDEBAR_Z_INDEX,
1124
1280
  SearchField,
1125
1281
  SearchProvider,
@@ -1129,9 +1285,12 @@ var LAYOUTS = [LayoutApplication, LayoutAccount, LayoutEmpty];
1129
1285
  UserProfile,
1130
1286
  defaultThemeBasicConfig,
1131
1287
  defineConfig,
1288
+ eNavigationElementNames,
1289
+ eThemeBasicComponents,
1132
1290
  useBranding,
1133
1291
  useLayoutContext,
1134
1292
  useLayoutService,
1293
+ useLayoutStateService,
1135
1294
  useLogo,
1136
1295
  useNavigationElements,
1137
1296
  useSearch
package/dist/index.mjs CHANGED
@@ -1,3 +1,19 @@
1
+ // src/enums/components.ts
2
+ var eThemeBasicComponents = {
3
+ ApplicationLayout: "Theme.ApplicationLayoutComponent",
4
+ AccountLayout: "Theme.AccountLayoutComponent",
5
+ EmptyLayout: "Theme.EmptyLayoutComponent",
6
+ Logo: "Theme.LogoComponent",
7
+ Routes: "Theme.RoutesComponent",
8
+ NavItems: "Theme.NavItemsComponent"
9
+ };
10
+
11
+ // src/enums/navigation-element-names.ts
12
+ var eNavigationElementNames = {
13
+ Language: "LanguageRef",
14
+ User: "CurrentUserRef"
15
+ };
16
+
1
17
  // src/contexts/layout.context.tsx
2
18
  import {
3
19
  createContext,
@@ -72,6 +88,16 @@ function useNavigationElements() {
72
88
  return useLayoutContext().state.navigationElements;
73
89
  }
74
90
 
91
+ // src/services/layout-state.service.ts
92
+ function useLayoutStateService() {
93
+ const { state, service } = useLayoutContext();
94
+ return {
95
+ getNavigationElements: () => state.navigationElements,
96
+ dispatchAddNavigationElement: (elements) => service.addNavigationElement(elements),
97
+ dispatchRemoveNavigationElementByName: (name) => service.removeNavigationElement(name)
98
+ };
99
+ }
100
+
75
101
  // src/contexts/branding.context.tsx
76
102
  import {
77
103
  createContext as createContext2,
@@ -751,6 +777,9 @@ function LayoutApplication({
751
777
  const navigate = useNavigate();
752
778
  const { logout } = useAuth2();
753
779
  const { direction, isRtl } = useDirection4();
780
+ const logoComponentKey = eThemeBasicComponents.Logo;
781
+ const routesComponentKey = eThemeBasicComponents.Routes;
782
+ const navItemsComponentKey = eThemeBasicComponents.NavItems;
754
783
  const [isChangePasswordOpen, setIsChangePasswordOpen] = useState5(false);
755
784
  const [isProfileOpen, setIsProfileOpen] = useState5(false);
756
785
  const handleLogout = useCallback3(() => {
@@ -833,6 +862,9 @@ function LayoutApplication({
833
862
  ] });
834
863
  }
835
864
  LayoutApplication.type = eLayoutType.application;
865
+ LayoutApplication.logoComponentKey = eThemeBasicComponents.Logo;
866
+ LayoutApplication.routesComponentKey = eThemeBasicComponents.Routes;
867
+ LayoutApplication.navItemsComponentKey = eThemeBasicComponents.NavItems;
836
868
 
837
869
  // src/components/layout-account/LayoutAccount.tsx
838
870
  import { Box as Box6, Flex as Flex3 } from "@chakra-ui/react";
@@ -920,19 +952,124 @@ function LayoutEmpty({ children }) {
920
952
  }
921
953
  LayoutEmpty.type = eLayoutType3.empty;
922
954
 
955
+ // src/components/logo/LogoComponent.tsx
956
+ import { Box as Box8, Text as Text4 } from "@chakra-ui/react";
957
+ import { Link as RouterLink4 } from "react-router-dom";
958
+ import { jsx as jsx17 } from "react/jsx-runtime";
959
+ function LogoComponent({ style, linkTo }) {
960
+ const { logo, appName, logoLink } = useBranding();
961
+ const logoContent = logo || (appName ? /* @__PURE__ */ jsx17(Text4, { fontWeight: "bold", fontSize: "lg", children: appName }) : /* @__PURE__ */ jsx17(DefaultLogo, { style }));
962
+ return /* @__PURE__ */ jsx17(Box8, { asChild: true, style, children: /* @__PURE__ */ jsx17(RouterLink4, { to: linkTo || logoLink || "/", children: logoContent }) });
963
+ }
964
+
965
+ // src/components/nav-items/NavItemsComponent.tsx
966
+ import React5, { useEffect, useCallback as useCallback4, useMemo as useMemo6 } from "react";
967
+ import { Stack as Stack3 } from "@chakra-ui/react";
968
+ import { useConfig as useConfig4, useAuth as useAuth3, useSession as useSession2 } from "@abpjs/core";
969
+ import { jsx as jsx18 } from "react/jsx-runtime";
970
+ function NavItemsComponent({
971
+ smallScreen = false,
972
+ userProfileProps,
973
+ showLanguageSelector = true,
974
+ showCurrentUser = true
975
+ }) {
976
+ const { currentUser, localization } = useConfig4();
977
+ const { isAuthenticated } = useAuth3();
978
+ const { language, setLanguage } = useSession2();
979
+ const { state, service } = useLayoutContext();
980
+ const defaultLanguage = useMemo6(() => {
981
+ const lang = localization?.languages?.find((l) => l.cultureName === language);
982
+ return lang?.displayName || language || "";
983
+ }, [localization, language]);
984
+ const dropdownLanguages = useMemo6(() => {
985
+ return localization?.languages?.filter((l) => l.cultureName !== language) || [];
986
+ }, [localization, language]);
987
+ const onChangeLang = useCallback4(
988
+ (cultureName) => {
989
+ setLanguage(cultureName);
990
+ window.location.reload();
991
+ },
992
+ [setLanguage]
993
+ );
994
+ const logout = useCallback4(() => {
995
+ userProfileProps?.onLogout?.();
996
+ }, [userProfileProps]);
997
+ useEffect(() => {
998
+ if (showLanguageSelector && localization?.languages && localization.languages.length > 1) {
999
+ service.addNavigationElement({
1000
+ name: eNavigationElementNames.Language,
1001
+ element: /* @__PURE__ */ jsx18(LanguageSelector, {}, "language-selector"),
1002
+ order: 2
1003
+ });
1004
+ return () => {
1005
+ service.removeNavigationElement(eNavigationElementNames.Language);
1006
+ };
1007
+ }
1008
+ }, [service, showLanguageSelector, localization?.languages]);
1009
+ useEffect(() => {
1010
+ if (showCurrentUser) {
1011
+ service.addNavigationElement({
1012
+ name: eNavigationElementNames.User,
1013
+ element: /* @__PURE__ */ jsx18(UserProfile, { ...userProfileProps }, "user-profile"),
1014
+ order: 3
1015
+ });
1016
+ return () => {
1017
+ service.removeNavigationElement(eNavigationElementNames.User);
1018
+ };
1019
+ }
1020
+ }, [service, showCurrentUser, userProfileProps]);
1021
+ const navElements = state.navigationElements;
1022
+ return /* @__PURE__ */ jsx18(Stack3, { gap: 2, width: "full", children: navElements.map((element) => /* @__PURE__ */ jsx18(React5.Fragment, { children: element.element }, element.name)) });
1023
+ }
1024
+
1025
+ // src/components/routes/RoutesComponent.tsx
1026
+ import { useMemo as useMemo7 } from "react";
1027
+ import { Stack as Stack4 } from "@chakra-ui/react";
1028
+ import { useConfig as useConfig5, useDirection as useDirection6 } from "@abpjs/core";
1029
+ import { jsx as jsx19 } from "react/jsx-runtime";
1030
+ function getVisibleRoutes2(routes) {
1031
+ return routes.reduce((acc, val) => {
1032
+ if (val.invisible) {
1033
+ return acc;
1034
+ }
1035
+ const route = { ...val };
1036
+ if (route.children && route.children.length) {
1037
+ route.children = getVisibleRoutes2(route.children);
1038
+ }
1039
+ return [...acc, route];
1040
+ }, []);
1041
+ }
1042
+ function RoutesComponent({
1043
+ smallScreen = false,
1044
+ isDropdownChildDynamic = false,
1045
+ defaultIcon,
1046
+ routes: customRoutes
1047
+ }) {
1048
+ const { routes: configRoutes } = useConfig5();
1049
+ const { direction } = useDirection6();
1050
+ const routes = customRoutes || configRoutes || [];
1051
+ const visibleRoutes = useMemo7(() => {
1052
+ return getVisibleRoutes2(routes);
1053
+ }, [routes]);
1054
+ const trackByFn = (index, item) => {
1055
+ return item.name || item.path || index;
1056
+ };
1057
+ return /* @__PURE__ */ jsx19(Stack4, { gap: "1", dir: direction, children: /* @__PURE__ */ jsx19(NavLinks, { defaultIcon }) });
1058
+ }
1059
+
923
1060
  // src/providers/ThemeBasicProvider.tsx
924
- import { useEffect } from "react";
1061
+ import { useEffect as useEffect2 } from "react";
925
1062
  import { ThemeSharedProvider, defineConfig } from "@abpjs/theme-shared";
926
- import { useSession as useSession2, useDirection as useDirection6 } from "@abpjs/core";
927
- import { Fragment as Fragment4, jsx as jsx17 } from "react/jsx-runtime";
1063
+ import { useSession as useSession3, useDirection as useDirection7 } from "@abpjs/core";
1064
+ import { Fragment as Fragment4, jsx as jsx20 } from "react/jsx-runtime";
928
1065
  function LocaleSync({ children }) {
929
- const { language } = useSession2();
930
- const { direction } = useDirection6();
931
- useEffect(() => {
1066
+ const { language } = useSession3();
1067
+ const { direction } = useDirection7();
1068
+ useEffect2(() => {
932
1069
  document.documentElement.dir = direction;
933
1070
  document.documentElement.lang = language || "en";
934
1071
  }, [direction, language]);
935
- return /* @__PURE__ */ jsx17(Fragment4, { children });
1072
+ return /* @__PURE__ */ jsx20(Fragment4, { children });
936
1073
  }
937
1074
  function ThemeBasicInner({
938
1075
  children,
@@ -947,9 +1084,9 @@ function ThemeBasicInner({
947
1084
  appName,
948
1085
  logoLink
949
1086
  }) {
950
- const { language } = useSession2();
1087
+ const { language } = useSession3();
951
1088
  const locale = language || "en-US";
952
- return /* @__PURE__ */ jsx17(
1089
+ return /* @__PURE__ */ jsx20(
953
1090
  ThemeSharedProvider,
954
1091
  {
955
1092
  renderToasts,
@@ -959,14 +1096,14 @@ function ThemeBasicInner({
959
1096
  enableColorMode,
960
1097
  defaultColorMode,
961
1098
  locale,
962
- children: /* @__PURE__ */ jsx17(LocaleSync, { children: /* @__PURE__ */ jsx17(
1099
+ children: /* @__PURE__ */ jsx20(LocaleSync, { children: /* @__PURE__ */ jsx20(
963
1100
  BrandingProvider,
964
1101
  {
965
1102
  logo,
966
1103
  logoIcon,
967
1104
  appName,
968
1105
  logoLink,
969
- children: /* @__PURE__ */ jsx17(LayoutProvider, { children })
1106
+ children: /* @__PURE__ */ jsx20(LayoutProvider, { children })
970
1107
  }
971
1108
  ) })
972
1109
  }
@@ -1040,7 +1177,7 @@ function ThemeBasicProvider({
1040
1177
  logoLink
1041
1178
  }) {
1042
1179
  const mergedThemeOverrides = themeOverrides || defaultThemeBasicConfig;
1043
- return /* @__PURE__ */ jsx17(
1180
+ return /* @__PURE__ */ jsx20(
1044
1181
  ThemeBasicInner,
1045
1182
  {
1046
1183
  renderToasts,
@@ -1073,11 +1210,14 @@ export {
1073
1210
  LayoutEmpty,
1074
1211
  LayoutProvider,
1075
1212
  Logo,
1213
+ LogoComponent,
1076
1214
  LogoIcon,
1077
1215
  LogoText,
1216
+ NavItemsComponent,
1078
1217
  NavLinks,
1079
1218
  Navbar,
1080
1219
  Profile,
1220
+ RoutesComponent,
1081
1221
  SIDEBAR_Z_INDEX,
1082
1222
  SearchField,
1083
1223
  SearchProvider,
@@ -1087,9 +1227,12 @@ export {
1087
1227
  UserProfile,
1088
1228
  defaultThemeBasicConfig,
1089
1229
  defineConfig,
1230
+ eNavigationElementNames,
1231
+ eThemeBasicComponents,
1090
1232
  useBranding,
1091
1233
  useLayoutContext,
1092
1234
  useLayoutService,
1235
+ useLayoutStateService,
1093
1236
  useLogo,
1094
1237
  useNavigationElements,
1095
1238
  useSearch
@@ -0,0 +1 @@
1
+ export * from './layout-state.service';
@@ -0,0 +1,55 @@
1
+ import { Layout } from '../models';
2
+ /**
3
+ * Interface for the LayoutStateService.
4
+ * Provides methods to access and modify layout state.
5
+ *
6
+ * @since 2.7.0
7
+ */
8
+ export interface LayoutStateService {
9
+ /**
10
+ * Get all navigation elements from the layout state.
11
+ * @returns Array of navigation elements
12
+ */
13
+ getNavigationElements(): Layout.NavigationElement[];
14
+ /**
15
+ * Dispatch action to add navigation elements.
16
+ * Wraps the layout service's addNavigationElement method.
17
+ *
18
+ * @param elements - Single element or array of elements to add
19
+ */
20
+ dispatchAddNavigationElement(elements: Layout.NavigationElement | Layout.NavigationElement[]): void;
21
+ /**
22
+ * Dispatch action to remove a navigation element by name.
23
+ * Wraps the layout service's removeNavigationElement method.
24
+ *
25
+ * @param name - Name of the element to remove
26
+ */
27
+ dispatchRemoveNavigationElementByName(name: string): void;
28
+ }
29
+ /**
30
+ * Hook to get the LayoutStateService.
31
+ * Provides a service-like interface matching the Angular LayoutStateService.
32
+ *
33
+ * @since 2.7.0
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * function MyComponent() {
38
+ * const layoutStateService = useLayoutStateService();
39
+ *
40
+ * // Get current navigation elements
41
+ * const elements = layoutStateService.getNavigationElements();
42
+ *
43
+ * // Add a new element
44
+ * layoutStateService.dispatchAddNavigationElement({
45
+ * name: 'MyElement',
46
+ * element: <MyNavItem />,
47
+ * order: 1,
48
+ * });
49
+ *
50
+ * // Remove an element
51
+ * layoutStateService.dispatchRemoveNavigationElementByName('MyElement');
52
+ * }
53
+ * ```
54
+ */
55
+ export declare function useLayoutStateService(): LayoutStateService;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abpjs/theme-basic",
3
- "version": "2.2.0",
3
+ "version": "2.7.0",
4
4
  "description": "ABP Framework Theme Basic components for React - translated from @abp/ng.theme.basic",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -27,11 +27,11 @@
27
27
  "lucide-react": "^0.400.0",
28
28
  "react-hook-form": "^7.48.0",
29
29
  "react-icons": "^5.0.0",
30
- "@abpjs/core": "2.2.0",
31
- "@abpjs/theme-shared": "2.2.0"
30
+ "@abpjs/core": "2.7.0",
31
+ "@abpjs/theme-shared": "2.7.0"
32
32
  },
33
33
  "devDependencies": {
34
- "@abp/ng.theme.basic": "2.2.0",
34
+ "@abp/ng.theme.basic": "2.7.0",
35
35
  "@testing-library/jest-dom": "^6.6.3",
36
36
  "@testing-library/react": "^16.1.0",
37
37
  "@vitest/coverage-v8": "^3.2.0",