nexus-public 1.0.10 → 1.0.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexus-public",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "Nexus public React components for Next.js",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -20,7 +20,7 @@
20
20
  "next": "16.2.6"
21
21
  },
22
22
  "dependencies": {
23
- "nexus-shared": "^1.1.19"
23
+ "nexus-shared": "^1.1.21"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/react": "^19.2.15",
@@ -0,0 +1,13 @@
1
+ "use server";
2
+
3
+ import { APIS, EMenuTargets, HttpGet, IClientMenu, NEXUS_CONFIG, NEXUS_INFO } from "nexus-shared";
4
+
5
+ // @ts-ignore
6
+ const processEnv = process.env;
7
+ const TOKEN_GLOBAL_CLIENT_MENU = processEnv.TOKEN_GLOBAL_CLIENT_MENU as string;
8
+
9
+ export const GetClientMenusAsync = async (): Promise<IClientMenu[]> => {
10
+ const response = await HttpGet(`${APIS.CONFIGURATION}/Resources/GlobalMenus/${NEXUS_INFO.CURRENT_MODULE}/${NEXUS_INFO.IS_ADMIN_MODULE ? EMenuTargets.AdminModule : EMenuTargets.ClientModule}/${TOKEN_GLOBAL_CLIENT_MENU}`, null, NEXUS_CONFIG.DEFAULT_REVALIDATE_TIME_ONE_DAY);
11
+ if (!response.isSuccess) return [];
12
+ return response.result as IClientMenu[];
13
+ };
@@ -1,24 +1,26 @@
1
1
  "use server";
2
2
 
3
- import { EBackgrounds, GetClientMenus, GetUserPanels, GetUtilityMenus, GlobalLayout, IGlobalLayout, NEXUS_CONFIG } from "nexus-shared";
3
+ import { EBackgrounds, GetUserPanelsAsync, GetUtilityMenusAsync, GlobalLayout, HasAccessToken, IGlobalLayout, NEXUS_CONFIG } from "nexus-shared";
4
4
  import { UtilityMenu } from "nexus-shared/client";
5
- import { HasAuthSession } from "nexus-shared/sso-server";
6
5
  import React from "react";
6
+ import { GetClientMenusAsync } from "../../api-services/system-service";
7
7
  import { ClientMenu } from "./client-menu";
8
8
 
9
+ import "./../../styles/nexus.layout.css";
10
+
9
11
  export const ClientLayout = async ({ children, globalLayout, menuBackground }: Readonly<{ children: React.ReactNode; globalLayout: IGlobalLayout; menuBackground: EBackgrounds }>) => {
10
- const authCookies = await HasAuthSession();
11
- const utilityMenus = await GetUtilityMenus();
12
- const userPanels = await GetUserPanels();
13
- const clientMenus = await GetClientMenus();
12
+ const hasAccessToken = await HasAccessToken(false);
13
+ const utilityMenus = await GetUtilityMenusAsync();
14
+ const userPanels = await GetUserPanelsAsync();
15
+ const clientMenus = await GetClientMenusAsync();
14
16
 
15
17
  return (
16
18
  <GlobalLayout globalLayout={globalLayout}>
17
19
  <div style={{ height: "100vh", width: "100vw" }} className="oh df ddc">
18
- <div style={{ zIndex: "2" }} className={`bb1 ${menuBackground}`}>
19
- <div style={{ height: "fit-content", zIndex: "2" }} className="row mw ma df ac client-menu">
20
+ <div style={{ zIndex: "1" }} className={`bb1 ${menuBackground}`}>
21
+ <div style={{ height: "fit-content" }} className="row mw ma df ac client-menu">
20
22
  <ClientMenu column={8} clientMenus={clientMenus} boxBackground={menuBackground} />
21
- <UtilityMenu column={4} utilityMenus={utilityMenus} userPanels={userPanels} globalLayout={globalLayout} iconBoxBackground={menuBackground} />
23
+ <UtilityMenu column={4} utilityMenus={utilityMenus} userPanels={userPanels} globalLayout={globalLayout} iconBoxBackground={menuBackground} hasAccessToken={hasAccessToken} />
22
24
  </div>
23
25
  </div>
24
26
  <div style={{ zIndex: "0" }} className={`body-context h100 fg sb ${NEXUS_CONFIG.DEFAULT_BACKGROUND}`}>
@@ -1,29 +1,177 @@
1
1
  "use client";
2
-
3
2
  import Image from "next/image";
4
3
  import Link from "next/link";
5
- import { DEFAULT_SMALL_ICON_BOX_SIZE, EBackgrounds, EIconTypes, EOpacity, ESizes, FILES, IClientMenu } from "nexus-shared";
6
- import { Icon } from "nexus-shared/client";
4
+ import { DEFAULT_ANIMATION_DURATION, DEFAULT_BUTTON_CLASS, DEFAULT_ICON_ELEMENT_NAME, DEFAULT_MOBILE_VIEW_WIDTH, EBackgroundColors, EBackgrounds, EColorTypes, EIcons, EIconTypes, EOpacity, EPositions, ESizes, FILES, FindActiveMenu, GetButton, GetVisibleAndHiddenItems, IClientMenu, IDialogboxResults, RenderChildren, SetSiblingMenuLinkActive, UN } from "nexus-shared";
5
+ import { CreateButtons, CreateToggleButton, FindParentElement, GetDynamicColor, HideLoader, Icon, Iconbox, InitializeDialogbox, SET_BUTTON_ACTIVE_CLASS, SetDocumentTitleDebounce, ShowLoader } from "nexus-shared/client";
6
+ import React, { useEffect, useRef } from "react";
7
7
 
8
8
  const TOGGLE_BUTTON_ID = "client-menu-toggle-button";
9
9
  const LINK_LOGO_BUTTON_ID = "client-menu-logo-button";
10
-
11
10
  export const ClientMenu = ({ clientMenus, column, boxBackground }: Readonly<{ clientMenus: IClientMenu[]; column: number; boxBackground: EBackgrounds }>) => {
11
+ const menuContainerRef = useRef<HTMLDivElement>(null);
12
+ const onDocumentClicked = (e: Event) => {
13
+ const parent = FindParentElement(e.target as Element, ".client-main-menu");
14
+ if (parent) return;
15
+ e.preventDefault();
16
+ toggleClientMenu(false);
17
+ };
18
+ const toggleClientMenu = (isShow: boolean) => {
19
+ const menuContainer = menuContainerRef.current;
20
+ if (isShow) {
21
+ document.addEventListener("touchstart", onDocumentClicked);
22
+ document.addEventListener("click", onDocumentClicked);
23
+ } else {
24
+ document.removeEventListener("touchstart", onDocumentClicked);
25
+ document.removeEventListener("click", onDocumentClicked);
26
+ }
27
+ if (!menuContainer) return;
28
+ menuContainer.style.left = isShow ? "0" : "-100%";
29
+ };
30
+
31
+ useEffect(() => {
32
+ const innerWidth = window.innerWidth;
33
+ const isMobileView = innerWidth < DEFAULT_MOBILE_VIEW_WIDTH;
34
+ const toggleButton = document.getElementById(TOGGLE_BUTTON_ID);
35
+ const linkLogoButton = document.getElementById(LINK_LOGO_BUTTON_ID);
36
+ if (isMobileView) {
37
+ if (toggleButton) toggleButton.classList.remove("dn", "o0");
38
+ if (linkLogoButton) linkLogoButton.style.display = "none";
39
+ const activeMenu = FindActiveMenu(clientMenus);
40
+ if (activeMenu) {
41
+ const activeLink = document.getElementById(`cml-${activeMenu.menuCode}`);
42
+ if (activeLink) onMainLinkClicked(activeLink, activeMenu, toggleClientMenu, boxBackground, false);
43
+ }
44
+ const menuContainer = menuContainerRef.current?.parentElement;
45
+ if (menuContainer) menuContainer.style.maxWidth = "4rem";
46
+ } else {
47
+ if (toggleButton) toggleButton.style.display = "none";
48
+
49
+ const maxVisible = innerWidth > 1200 ? 4 : innerWidth > 800 ? 3 : 2;
50
+
51
+ renderClientMenu(toggleClientMenu, clientMenus, maxVisible, menuContainerRef.current?.parentElement, boxBackground);
52
+ }
53
+ }, []);
54
+
12
55
  return (
13
- <div style={{ zIndex: 4 }} className={`col-${column ?? 12} df ac`}>
14
- <Link id={LINK_LOGO_BUTTON_ID} href={"/home"} style={{ padding: "0.5rem 0.6rem", height: `3.0rem` }} className="bg52 h bb mr5">
15
- <Image src={FILES.LOGO.FULL} alt="logo" width={74} height={74} className="h100 w100" />
16
- </Link>
17
- <ul className="df ac">
18
- {clientMenus.slice(0, 4).map((m) => (
19
- <li key={m.menuCode}>
20
- <Link href={m.menuUrl ?? "/"} className={`${boxBackground} h bb mr5 df ac pl4 pr4 r o07 ho1`} style={{ minHeight: `calc(var(--IH) * ${DEFAULT_SMALL_ICON_BOX_SIZE})` }}>
21
- <Icon size={ESizes.Tiny} type={EIconTypes.FILLED} opacity={EOpacity.O08} defaultClass="mr2" icon={m.menuIcon} />
22
- <span className="fw400">{m.menuName ?? "Unknown"}</span>
23
- </Link>
24
- </li>
25
- ))}
26
- </ul>
56
+ <div style={{ zIndex: 1 }} className={`col-${column ?? 12} df ac`}>
57
+ <Iconbox id={TOGGLE_BUTTON_ID} icon={EIcons.MENU} defaultClass="ml5" onClicked={() => toggleClientMenu(true)} iconDefaultClass="lg" iconType={EIconTypes.OUTLINED} background={boxBackground} />
58
+ <div ref={menuContainerRef} className={`client-main-menu ${boxBackground} bs3 h100 anim pf top`} style={{ maxWidth: "90vw", width: "30rem", borderTopRightRadius: "var(--BR1)", borderBottomRightRadius: "var(--BR1)", left: "-100%", zIndex: 4 }}>
59
+ <div className={`df ac bg52 p5`}>
60
+ <div style={{ height: "calc(var(--IW) / 1.0)" }}>
61
+ <Image src={FILES.LOGO.FULL} alt="logo" width={74} height={74} className="img" />
62
+ </div>
63
+ <h3 className="ml5 fg o0">NEXUS</h3>
64
+ <Iconbox onClicked={() => toggleClientMenu(false)} icon={EIcons.CLOSE} defaultClass={`bg52 lg h`} iconSize={ESizes.ExtraLarge} iconOpacity={EOpacity.FULL} iconType={EIconTypes.OUTLINED} boxStyle={{ borderColor: "var(--C21)" }} />
65
+ </div>
66
+ <ul className="sb sm h100" style={{ maxHeight: "80vh" }}>
67
+ {clientMenus.map((m, i) => (
68
+ <li key={i} className={`df ac`}>
69
+ <Link onClick={(event: React.MouseEvent) => onMainLinkClicked(event, m, toggleClientMenu, boxBackground)} id={`cml-${m.menuCode}`} data-label={m.menuName} href={m.menuUrl ?? "/"} className={DEFAULT_BUTTON_CLASS(12, 6, 0, null, boxBackground) + " client-menu-link"}>
70
+ <Icon size={ESizes.Small} type={EIconTypes.FILLED} defaultClass="mr6 ml2" icon={m.menuIcon} />
71
+ <span className="fw400 fs-11 o1">{m.menuName ?? "Unknown"}</span>
72
+ </Link>
73
+ </li>
74
+ ))}
75
+ </ul>
76
+ </div>
27
77
  </div>
28
78
  );
29
79
  };
80
+ const onMainLinkClicked = (e: React.MouseEvent | HTMLElement, menu: IClientMenu | null, toggleClientMenu: (isShow: boolean) => void, boxBackground: EBackgrounds, showLoader: boolean = true) => {
81
+ if (showLoader) {
82
+ ShowLoader();
83
+ HideLoader(null, null, DEFAULT_ANIMATION_DURATION);
84
+ }
85
+ toggleClientMenu(false);
86
+ const link = e instanceof HTMLElement ? e : (e.currentTarget as HTMLElement);
87
+ SetSiblingMenuLinkActive(link, link.closest("ul"), "a.client-menu-link", boxBackground);
88
+ document.body.click();
89
+ };
90
+
91
+ const renderClientMenu = (closeMenuHandler: (isShow: boolean) => void, clientMenus: IClientMenu[], maxVisible: number, parent: HTMLElement | UN, boxBackground: EBackgrounds) => {
92
+ if (!parent) return;
93
+ const menuContainer = document.createElement("ul");
94
+ menuContainer.className = "client-menu df ac";
95
+ menuContainer.style.height = "100%";
96
+ parent.appendChild(menuContainer);
97
+
98
+ let activeItem: IClientMenu | undefined = FindActiveMenu(clientMenus);
99
+
100
+ const visibleItems: IClientMenu[] = [];
101
+ const hiddenItems: IClientMenu[] = [];
102
+ GetVisibleAndHiddenItems(clientMenus, visibleItems, hiddenItems, activeItem, maxVisible);
103
+
104
+ const elementList: HTMLElement[] = [];
105
+ const dataKey = "menuCode";
106
+ const dataAttributeKey = "data-menu-code";
107
+
108
+ let dialogBoxObject: IDialogboxResults | null = null;
109
+
110
+ const toggleButton = CreateToggleButton(null, null, null, ESizes.Small, null, null, null, boxBackground);
111
+ const onMenuLinkClick = (e: Event, menu: IClientMenu) => {
112
+ e.preventDefault();
113
+ ShowLoader();
114
+ HideLoader(undefined, undefined, DEFAULT_ANIMATION_DURATION);
115
+ closeMenuHandler(false);
116
+ if (menu === activeItem) return;
117
+ if (dialogBoxObject) dialogBoxObject.visibility(false);
118
+ SetDocumentTitleDebounce(menu?.documentTitle ?? menu?.menuName);
119
+ activeItem = menu;
120
+
121
+ const isHiddenItemClicked = hiddenItems.some((item) => item === menu);
122
+ GetVisibleAndHiddenItems(clientMenus, visibleItems, hiddenItems, activeItem, maxVisible);
123
+
124
+ const linkButton = document.getElementById(`cml-${menu.menuCode}`);
125
+ if (linkButton) linkButton.click();
126
+ RenderChildren(menuContainer, elementList, toggleButton, activeItem, visibleItems, hiddenItems, createMenuItem, refreshElementClass, false, isHiddenItemClicked, dataKey, dataAttributeKey);
127
+ };
128
+ const createMenuItem = (menu: IClientMenu) => {
129
+ const isActive = activeItem?.menuCode === menu.menuCode;
130
+ const menuItem = document.createElement("li");
131
+ menuItem.setAttribute(dataAttributeKey, menu.menuCode);
132
+ //menuItem.className = "df ac li-item";
133
+ const link = document.createElement("a");
134
+ link.setAttribute(dataAttributeKey, menu.menuCode);
135
+ link.href = menu.menuUrl ?? "#";
136
+ link.innerHTML = `</${DEFAULT_ICON_ELEMENT_NAME}><span class="fw400 en nw">${menu.menuName ?? "Unknown"}</span>`;
137
+ link.onclick = (event: Event) => onMenuLinkClick(event, menu);
138
+ link.className = _getLinkClass(isActive, boxBackground);
139
+ link.style.minHeight = `var(--TOP_HEIGHT)`;
140
+ SET_BUTTON_ACTIVE_CLASS(isActive, link, GetDynamicColor(boxBackground, EColorTypes.Hover) as EBackgroundColors, null, GetDynamicColor(boxBackground, EColorTypes.Text) as EBackgroundColors, "active");
141
+ menuItem.appendChild(link);
142
+ return menuItem;
143
+ };
144
+ const refreshElementClass = (element: HTMLElement, activeItem: IClientMenu | UN) => {
145
+ const link = element.querySelector("a");
146
+ const menuCode = link?.getAttribute(dataAttributeKey);
147
+ const isActive = activeItem?.menuCode === menuCode;
148
+
149
+ if (link) {
150
+ link.className = _getLinkClass(isActive, boxBackground);
151
+ SET_BUTTON_ACTIVE_CLASS(isActive, link, GetDynamicColor(boxBackground, EColorTypes.Hover) as EBackgroundColors, null, GetDynamicColor(boxBackground, EColorTypes.Text) as EBackgroundColors, "active");
152
+ }
153
+ };
154
+ RenderChildren(menuContainer, elementList, toggleButton, activeItem, visibleItems, hiddenItems, createMenuItem, refreshElementClass, true, false, dataKey, dataAttributeKey);
155
+ if (hiddenItems.length > 0) {
156
+ const dialogBox = document.createElement("div");
157
+ toggleButton.appendChild(dialogBox);
158
+
159
+ dialogBox.style.maxHeight = "80vh";
160
+ dialogBox.classList.add("sb", "dn", "sm");
161
+
162
+ let linkButtons: HTMLDivElement | undefined;
163
+ const onVisibilityChanged = (isVisible: boolean) => {
164
+ if (isVisible) {
165
+ const menuButtons = hiddenItems.map((menu, index) => GetButton(menu.menuName, menu.menuCode, menu.menuUrl, menu.menuIcon, (event) => onMenuLinkClick(event as Event, menu), menu));
166
+ if (linkButtons) linkButtons.remove();
167
+ linkButtons = CreateButtons(menuButtons, dialogBox, null, 6, 0, null, null, "pr5 pl5 mt5 mb5");
168
+
169
+ if (linkButtons) dialogBox.appendChild(linkButtons);
170
+ } else if (linkButtons) linkButtons.remove();
171
+ };
172
+
173
+ dialogBoxObject = InitializeDialogbox(toggleButton, dialogBox, null, null, 0, null, null, onVisibilityChanged, undefined, false, EPositions.Right);
174
+ }
175
+ };
176
+
177
+ const _getLinkClass = (isActive: boolean, background?: EBackgrounds | null) => `${background} pr db btn h100 h bb df ac pl5 pr5 r ${isActive ? "active" : "h fw400"}`;
@@ -0,0 +1,24 @@
1
+ :root {
2
+ --MW: 90vw;
3
+ }
4
+ .mw {
5
+ width: var(--MW);
6
+ }
7
+
8
+ @media (min-width: 2500px) {
9
+ :root {
10
+ --MW: 2000px;
11
+ }
12
+ }
13
+
14
+ @media (max-width: 1150px) {
15
+ :root {
16
+ --MW: 96vw;
17
+ }
18
+ }
19
+
20
+ @media (max-width: 750px) {
21
+ .client-menu .utilities {
22
+ width: 100% !important;
23
+ }
24
+ }