nexus-shared 1.1.18 → 1.1.20
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 +1 -1
- package/src/api-services/authentication-service.tsx +23 -0
- package/src/api-services/system-service.tsx +28 -315
- package/src/client.ts +2 -2
- package/src/components/documents/button.tsx +17 -11
- package/src/components/documents/icon-box.tsx +1 -1
- package/src/components/documents/tab-button.tsx +2 -2
- package/src/components/documents/tag.tsx +30 -0
- package/src/components/inputs/input-box.tsx +4 -2
- package/src/components/inputs/input-element.tsx +1 -1
- package/src/components/inputs/input-form.tsx +126 -14
- package/src/components/layouts/global-dialogbox.tsx +5 -9
- package/src/components/layouts/global-layout.tsx +3 -0
- package/src/components/layouts/layout-helpers.tsx +1 -4
- package/src/components/layouts/panels/user-panel.tsx +112 -0
- package/src/components/layouts/utility-menu.tsx +9 -29
- package/src/components/panels/theme-panel.tsx +2 -2
- package/src/helpers/browser-helpers.tsx +88 -3
- package/src/helpers/input-helpers.tsx +34 -4
- package/src/helpers/validation-helpers.tsx +302 -0
- package/src/index.ts +2 -18
- package/src/interface.ts +5 -1
- package/src/interfaces/auth-token-interfaces.tsx +6 -0
- package/src/interfaces/browser-interfaces.tsx +13 -0
- package/src/interfaces/button-interfaces.tsx +2 -2
- package/src/interfaces/http-interfaces.tsx +86 -8
- package/src/interfaces/icon-interfaces.tsx +3 -0
- package/src/interfaces/input-interfaces.tsx +61 -11
- package/src/interfaces/menu-interfaces.tsx +13 -0
- package/src/interfaces/message-interfaces.tsx +32 -0
- package/src/interfaces/storage-interfaces.tsx +2 -0
- package/src/interfaces/theme-interfaces.tsx +43 -129
- package/src/interfaces/type-interfaces.tsx +8 -2
- package/src/interfaces/user-interfaces.tsx +47 -0
- package/src/nexus.environments.tsx +41 -24
- package/src/proxy-api/proxy-backend.tsx +60 -0
- package/src/proxy-api/proxy-constants.tsx +28 -0
- package/src/proxy-api/proxy-helpers.tsx +91 -0
- package/src/proxy-api-client.tsx +1 -0
- package/src/proxy-api-server.tsx +2 -0
- package/src/services/http-client-services.tsx +26 -11
- package/src/services/http-services.tsx +42 -27
- package/src/services/loader-service.tsx +34 -17
- package/src/services/localstorage-service.tsx +81 -5
- package/src/services/message-services.tsx +383 -0
- package/src/services/theme-service.tsx +30 -16
- package/src/services/user-services.tsx +10 -0
- package/src/sso-config/auth-token-validation.ts +16 -0
- package/src/sso-config/callback-route.tsx +35 -0
- package/src/sso-config/cookie-encryption.ts +51 -0
- package/src/sso-config/cookie-helpers.tsx +102 -0
- package/src/sso-config/forgot-password-route.tsx +36 -0
- package/src/sso-config/oauth-callback-state.ts +61 -0
- package/src/sso-config/pkce-helpers.ts +9 -0
- package/src/sso-config/provider-complete-route.tsx +18 -0
- package/src/sso-config/redirect-context-actions.tsx +45 -0
- package/src/sso-config/redirect-context-helpers.tsx +159 -0
- package/src/sso-config/redirect-context-persist.tsx +18 -0
- package/src/sso-config/redirect-context-route.tsx +45 -0
- package/src/sso-config/refresh-route.tsx +23 -0
- package/src/sso-config/sign-in-route.tsx +74 -0
- package/src/sso-config/sign-out-route.tsx +57 -0
- package/src/sso-config/sign-up-route.tsx +57 -0
- package/src/sso-config/sso-backend-flow.ts +38 -0
- package/src/sso-config/sso-complete-route.tsx +50 -0
- package/src/sso-config/sso-config.ts +59 -0
- package/src/sso-config/sso-interfaces.tsx +99 -0
- package/src/sso-config/sso-response-helpers.tsx +20 -0
- package/src/sso-server.tsx +21 -1
- package/src/styles/nexus.dialog.css +3 -0
- package/src/styles/nexus.icon.css +1 -0
- package/src/styles/nexus.logic.css +34 -5
- package/src/interfaces/sso-interfaces.tsx +0 -52
- package/src/load-env.ts +0 -2
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
2
|
|
|
3
3
|
import React, { useEffect } from "react";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { HideLoader, ShowLoader, SubmitApi, WarningMessage } from "../../client";
|
|
5
|
+
import { GetInputId, SetResponseErrorMessage } from "../../helpers/input-helpers";
|
|
6
|
+
import { IsValidInputs } from "../../helpers/validation-helpers";
|
|
7
|
+
import { EActions, EAnimationClasses, EBackgrounds, EButtonTypes, EFlexPositions, EIcons, EIconTypes, ESizes, GetApiInformations, GetButton, UN, VoidCallbackHandler } from "../../interface";
|
|
8
|
+
import { DEFAULT_ROW_GAP, EInputTypes, GetForms, IApiForm, IForm, IFormCreateResults, IInputCreateResults, ISubmitForm, ISubmitFormCreateResults, OnFormChangeHandler, OnFormSubmitHandler } from "../../interfaces/input-interfaces";
|
|
9
|
+
import { GetFormButtonPosition } from "../../services/localstorage-service";
|
|
10
|
+
import { CreateButton } from "../documents/button";
|
|
6
11
|
import { CreateInputs, DisposeInputs } from "./input";
|
|
7
12
|
|
|
8
13
|
const forceChangeDefaultValue = (inputResults: IInputCreateResults[], newDefaultValue: any): boolean => {
|
|
@@ -16,37 +21,144 @@ const forceChangeDefaultValue = (inputResults: IInputCreateResults[], newDefault
|
|
|
16
21
|
}
|
|
17
22
|
return isChanged;
|
|
18
23
|
};
|
|
19
|
-
export const CreateForm = (param: IForm, rowGap?: number): IFormCreateResults | null => {
|
|
20
|
-
const { inputs, onChanged, index, isTabularForm,
|
|
24
|
+
export const CreateForm = (param: IForm, parentIdentifier: HTMLElement | string | UN, rowGap?: number, onFormRendered?: VoidCallbackHandler): IFormCreateResults | null => {
|
|
25
|
+
const { inputs, onChanged, index, isTabularForm, defaultValue } = param;
|
|
21
26
|
const parentElement = typeof parentIdentifier === "string" ? document.getElementById(parentIdentifier) : parentIdentifier;
|
|
22
27
|
|
|
23
28
|
if (!parentElement || inputs.length === 0 || !onChanged) {
|
|
24
29
|
//TODO: WarningMessage("Invalid Inputs", "Please provide valid inputs, onChanged function and parent element.");
|
|
25
30
|
return null;
|
|
26
31
|
}
|
|
32
|
+
const rowElement = document.createElement("div");
|
|
33
|
+
if (parentElement) parentElement.appendChild(rowElement);
|
|
27
34
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
parent.style.paddingTop = "var(--IEM)";
|
|
31
|
-
if (parentElement) parentElement.appendChild(parent);
|
|
35
|
+
rowElement.className = `row g${rowGap ?? DEFAULT_ROW_GAP} sb fg`;
|
|
36
|
+
rowElement.style.paddingTop = "var(--IEM)";
|
|
32
37
|
|
|
33
|
-
const inputResults = CreateInputs(inputs, onChanged, index, isTabularForm,
|
|
38
|
+
const inputResults = CreateInputs(inputs, onChanged, index, isTabularForm, rowElement, defaultValue);
|
|
39
|
+
|
|
40
|
+
onFormRendered?.(inputResults);
|
|
34
41
|
|
|
35
42
|
return {
|
|
36
43
|
inputResults: inputResults,
|
|
37
|
-
formElement:
|
|
44
|
+
formElement: rowElement,
|
|
38
45
|
dispose: (invokeOnChanged?: boolean) => {
|
|
39
46
|
DisposeInputs(inputResults, invokeOnChanged);
|
|
40
|
-
|
|
47
|
+
rowElement.remove();
|
|
41
48
|
},
|
|
42
49
|
forceChangeDefaultValue: (newDefaultValue: any) => forceChangeDefaultValue(inputResults, newDefaultValue),
|
|
43
50
|
} as IFormCreateResults;
|
|
44
51
|
};
|
|
45
52
|
|
|
46
|
-
export const Form = ({ param, rowGap }: { param: IForm; rowGap?: number }): React.ReactNode => {
|
|
53
|
+
export const Form = ({ param, parentIdentifier, rowGap, onFormRendered }: { param: IForm; parentIdentifier: HTMLElement | string | UN; rowGap?: number; onFormRendered?: VoidCallbackHandler }): React.ReactNode => {
|
|
47
54
|
useEffect(() => {
|
|
48
|
-
const formResults = CreateForm(param, rowGap);
|
|
55
|
+
const formResults = CreateForm(param, parentIdentifier, rowGap, onFormRendered);
|
|
49
56
|
return () => formResults?.dispose();
|
|
50
57
|
}, [param, rowGap]);
|
|
51
58
|
return <React.Fragment></React.Fragment>;
|
|
52
59
|
};
|
|
60
|
+
|
|
61
|
+
export const CreateSubmitForm = (param: ISubmitForm, parentIdentifier: HTMLElement | string | UN, rowGap?: number, buttonPosition?: EFlexPositions, formAction?: EActions | UN, onFormRendered?: VoidCallbackHandler): ISubmitFormCreateResults | null => {
|
|
62
|
+
const parentElement = typeof parentIdentifier === "string" ? document.getElementById(parentIdentifier) : parentIdentifier;
|
|
63
|
+
buttonPosition = buttonPosition ?? GetFormButtonPosition();
|
|
64
|
+
|
|
65
|
+
if (!parentElement || !param.inputs || param.inputs.length === 0 || !param.onSubmit) return null;
|
|
66
|
+
|
|
67
|
+
let formValue: any | UN;
|
|
68
|
+
let defaultValue: any | UN = GetApiInformations.IS_API(param.defaultValue) ? null : param.defaultValue;
|
|
69
|
+
const onChangedHandler: OnFormChangeHandler = (currentValue, isInitilizing, isForcedChanged, isDisposed, currentKey, index, type, dataSource) => {
|
|
70
|
+
formValue = currentValue;
|
|
71
|
+
if (param.onChanged) param.onChanged(currentValue, isInitilizing, isForcedChanged, isDisposed, currentKey, index, type, dataSource);
|
|
72
|
+
};
|
|
73
|
+
const onFormSubmit = async (event: Event) => {
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
const isValidForm = IsValidInputs(formResults?.inputResults, formValue, true, true);
|
|
76
|
+
const isValid = param.overrideValidation ? await param.overrideValidation(formValue, isValidForm, formResults, formAction) : isValidForm;
|
|
77
|
+
|
|
78
|
+
param.onSubmit({ ...formValue }, isValidForm, event, formResults!, defaultValue);
|
|
79
|
+
if (!isValid) WarningMessage("Invalid Inputs", "Please fill all required fields correctly.");
|
|
80
|
+
};
|
|
81
|
+
const formParameters = GetForms.FORM(param.inputs, onChangedHandler, defaultValue);
|
|
82
|
+
const formElement = document.createElement("form");
|
|
83
|
+
parentElement.appendChild(formElement);
|
|
84
|
+
const formResults = CreateForm(formParameters, formElement, rowGap, onFormRendered);
|
|
85
|
+
if (!formResults) return null;
|
|
86
|
+
formResults.formElement.classList.add("h100", "fg");
|
|
87
|
+
|
|
88
|
+
const submitButton = param.inputs.find((input) => input.inputType === EInputTypes.SubmitButton);
|
|
89
|
+
if (!submitButton) {
|
|
90
|
+
const buttonStyle: React.CSSProperties = {
|
|
91
|
+
width: "fit-content",
|
|
92
|
+
};
|
|
93
|
+
const DEFAULT_BUTTON_CLASS = "col- jc r";
|
|
94
|
+
rowGap = rowGap ?? DEFAULT_ROW_GAP;
|
|
95
|
+
const PADDING = rowGap * 2;
|
|
96
|
+
|
|
97
|
+
const buttonParam = GetButton("Submit", null, null, EIcons.SAVE, null, null, EBackgrounds.PrimaryColor, DEFAULT_BUTTON_CLASS, null, EIconTypes.OUTLINED, null, 0, null, EButtonTypes.SUBMIT);
|
|
98
|
+
const buttonContainer = document.createElement("div");
|
|
99
|
+
buttonContainer.className = `df ac pt1 pb5 ${buttonPosition} bb pr${PADDING} pl${PADDING}`;
|
|
100
|
+
buttonContainer.style.minHeight = "calc(var(--IH) * 1.5)";
|
|
101
|
+
|
|
102
|
+
formElement.appendChild(buttonContainer);
|
|
103
|
+
CreateButton(buttonParam, buttonContainer, buttonStyle, false);
|
|
104
|
+
|
|
105
|
+
if (param.onCancelClick) {
|
|
106
|
+
const buttonParam = GetButton("Cancel", null, null, EIcons.CLOSE, param.onCancelClick, null, EBackgrounds.ErrorColor, DEFAULT_BUTTON_CLASS + ` ml${PADDING}`, null, EIconTypes.OUTLINED, null, 0, null, EButtonTypes.BUTTON);
|
|
107
|
+
CreateButton(buttonParam, buttonContainer, buttonStyle, false);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
formElement.className = `df ddc w100 h100 oh fg pr`;
|
|
112
|
+
formElement.addEventListener("submit", onFormSubmit);
|
|
113
|
+
|
|
114
|
+
const onDispose = () => {
|
|
115
|
+
formResults?.dispose();
|
|
116
|
+
formElement.remove();
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
formElement: formElement,
|
|
121
|
+
dispose: () => onDispose(),
|
|
122
|
+
formObject: formResults,
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export const SubmitForm = ({ param, parentIdentifier, rowGap, buttonPosition, formAction, onFormRendered }: { param: ISubmitForm; parentIdentifier: HTMLElement | string | UN; rowGap?: number; buttonPosition?: EFlexPositions; formAction?: EActions | UN; onFormRendered?: VoidCallbackHandler }): React.ReactNode => {
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
const submitForm = CreateSubmitForm(param, parentIdentifier, rowGap, buttonPosition, formAction, onFormRendered);
|
|
129
|
+
return () => submitForm?.dispose();
|
|
130
|
+
}, [param, parentIdentifier, rowGap, buttonPosition, formAction]);
|
|
131
|
+
return <React.Fragment></React.Fragment>;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const CreateApiForm = (param: IApiForm, parentIdentifier: HTMLElement | string | UN, rowGap?: number, buttonPosition?: EFlexPositions, formAction?: EActions | UN, overrideLoaderParend?: HTMLElement | string | UN, successMessage?: boolean | UN, errorMessage?: boolean | UN, onFormRendered?: VoidCallbackHandler): ISubmitFormCreateResults | null => {
|
|
135
|
+
let isLoading = false;
|
|
136
|
+
const onFormSubmit: OnFormSubmitHandler = async (currentValue, isValidForm, event, form, defaultValue) => {
|
|
137
|
+
if (isLoading) return;
|
|
138
|
+
isLoading = true;
|
|
139
|
+
|
|
140
|
+
if (param.onSubmit) param.onSubmit(currentValue, isValidForm, event, form, defaultValue);
|
|
141
|
+
if (!isValidForm) return;
|
|
142
|
+
|
|
143
|
+
const loaderParent = overrideLoaderParend ?? submitForm?.formElement ?? parentIdentifier;
|
|
144
|
+
ShowLoader(loaderParent, EAnimationClasses.SPIN, true, ESizes.Normal, null, null, null, "r");
|
|
145
|
+
const apiResponse = await SubmitApi(param.api, currentValue, successMessage, errorMessage);
|
|
146
|
+
if (!apiResponse.isSuccess) SetResponseErrorMessage(apiResponse, submitForm?.formObject.inputResults);
|
|
147
|
+
HideLoader(loaderParent);
|
|
148
|
+
param.onApiSubmit(currentValue, apiResponse, submitForm!, defaultValue);
|
|
149
|
+
isLoading = false;
|
|
150
|
+
};
|
|
151
|
+
const submitFormParam = GetForms.SUBMITFORM(param?.inputs, onFormSubmit, param?.defaultValue, param?.overrideValidation, param?.onChanged, param?.onCancelClick);
|
|
152
|
+
const submitForm = CreateSubmitForm(submitFormParam, parentIdentifier, rowGap, buttonPosition, formAction, onFormRendered);
|
|
153
|
+
if (!submitForm) return null;
|
|
154
|
+
|
|
155
|
+
return submitForm;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export const ApiForm = ({ param, parentIdentifier, rowGap, buttonPosition, formAction, overrideLoaderParend, successMessage, errorMessage, onFormRendered }: { param: IApiForm; parentIdentifier: HTMLElement | string | UN; rowGap?: number; buttonPosition?: EFlexPositions; formAction?: EActions | UN; overrideLoaderParend?: HTMLElement | string | UN; successMessage?: boolean | UN; errorMessage?: boolean | UN; onFormRendered?: VoidCallbackHandler }): React.ReactNode => {
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
const apiForm = CreateApiForm(param, parentIdentifier, rowGap, buttonPosition, formAction, overrideLoaderParend, successMessage, errorMessage, onFormRendered);
|
|
161
|
+
return () => apiForm?.dispose();
|
|
162
|
+
}, [param, parentIdentifier, rowGap, buttonPosition, formAction, overrideLoaderParend, successMessage, errorMessage]);
|
|
163
|
+
return <React.Fragment></React.Fragment>;
|
|
164
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
2
|
|
|
3
3
|
import React, { useEffect } from "react";
|
|
4
4
|
import { ConvertRemToPixels, IsKeyPressed } from "../../helpers/browser-helpers";
|
|
@@ -17,7 +17,7 @@ export const DEFAULT_DIALOGBOX_HOVER_EFFECT_DELAY = 500;
|
|
|
17
17
|
interface IDialogRegistryEntry {
|
|
18
18
|
triggerElement: HTMLElement;
|
|
19
19
|
affectedElement: HTMLElement;
|
|
20
|
-
iggnoreChildClick?: boolean;
|
|
20
|
+
iggnoreChildClick?: boolean | UN;
|
|
21
21
|
close: (isInitial: boolean) => void;
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -32,12 +32,7 @@ let globalDocumentClickTimeout: ReturnType<typeof setTimeout>;
|
|
|
32
32
|
const shouldKeepDialogOpen = (entry: IDialogRegistryEntry, path: EventTarget[], target: HTMLElement) => {
|
|
33
33
|
if (entry.iggnoreChildClick === false) return target === entry.triggerElement;
|
|
34
34
|
|
|
35
|
-
return (
|
|
36
|
-
path.includes(entry.triggerElement) ||
|
|
37
|
-
path.includes(entry.affectedElement) ||
|
|
38
|
-
isClickInsideOpenNestedDialog(entry.triggerElement, path) ||
|
|
39
|
-
entry.triggerElement.contains(target)
|
|
40
|
-
);
|
|
35
|
+
return path.includes(entry.triggerElement) || path.includes(entry.affectedElement) || isClickInsideOpenNestedDialog(entry.triggerElement, path) || entry.triggerElement.contains(target);
|
|
41
36
|
};
|
|
42
37
|
|
|
43
38
|
const isClickInsideOpenNestedDialog = (parentTrigger: HTMLElement, path: EventTarget[]) => {
|
|
@@ -169,7 +164,7 @@ export const GlobalDialogbox = ({ children, hoverEffectDelay, position, buttonId
|
|
|
169
164
|
return <ul id={GetDialogId(buttonIdentity)}>{isVisible && children}</ul>;
|
|
170
165
|
};
|
|
171
166
|
|
|
172
|
-
export const InitializeDialogbox = (triggerElement: HTMLElement, affectedElement: HTMLElement, hoverEffectDelay?: number | boolean, defaultHeight?: string, defaultPadding?: number, defaultWidth?: number, background?: EBackgrounds, onVisibilityChanged?: (isVisible: boolean) => void, onDisposed?: () => void, iggnoreChildClick?: boolean, position?: EPositions, defaultClass?: string, preferTopWhenEnoughSpace?: boolean, targetInput?: HTMLTextAreaElement | HTMLInputElement | UN, isAutoHideInInitialization?: boolean): IDialogboxResults | null => {
|
|
167
|
+
export const InitializeDialogbox = (triggerElement: HTMLElement, affectedElement: HTMLElement, hoverEffectDelay?: number | boolean | UN, defaultHeight?: string | UN, defaultPadding?: number | UN, defaultWidth?: number | UN, background?: EBackgrounds | UN, onVisibilityChanged?: (isVisible: boolean) => void | UN, onDisposed?: () => void | UN, iggnoreChildClick?: boolean | UN, position?: EPositions | UN, defaultClass?: string | UN, preferTopWhenEnoughSpace?: boolean | UN, targetInput?: HTMLTextAreaElement | HTMLInputElement | UN, isAutoHideInInitialization?: boolean | UN): IDialogboxResults | null => {
|
|
173
168
|
if (!triggerElement || !affectedElement) return null;
|
|
174
169
|
defaultHeight = defaultHeight ?? "auto";
|
|
175
170
|
defaultPadding = defaultPadding ?? 5;
|
|
@@ -331,6 +326,7 @@ export const InitializeDialogbox = (triggerElement: HTMLElement, affectedElement
|
|
|
331
326
|
unregisterDialog(triggerElement);
|
|
332
327
|
onDisposed?.();
|
|
333
328
|
};
|
|
329
|
+
// @ts-ignore
|
|
334
330
|
let visibilityTimeout: NodeJS.Timeout;
|
|
335
331
|
const visibility = (visible: boolean, isInitial: boolean) => {
|
|
336
332
|
const isVisible = visible;
|
|
@@ -6,6 +6,7 @@ import { IGlobalLayout, ROOT_LOADER_IDENTITY } from "../../interfaces/layout-int
|
|
|
6
6
|
import { FILES } from "../../nexus.environments";
|
|
7
7
|
import { LayoutHelpers } from "./layout-helpers";
|
|
8
8
|
|
|
9
|
+
import { GetSystemThemesAsync } from "../../api-services/system-service";
|
|
9
10
|
import "./../../styles/nexus.animation.css";
|
|
10
11
|
import "./../../styles/nexus.core.css";
|
|
11
12
|
import "./../../styles/nexus.dialog.css";
|
|
@@ -18,6 +19,8 @@ import "./../../styles/nexus.utility.css";
|
|
|
18
19
|
const inter = Inter({ weight: ["300", "400", "500", "600", "700", "800"], subsets: ["latin"] });
|
|
19
20
|
|
|
20
21
|
export const GlobalLayout = async ({ children, globalLayout }: Readonly<{ children: React.ReactNode; globalLayout: IGlobalLayout }>) => {
|
|
22
|
+
const systemThemes = await GetSystemThemesAsync();
|
|
23
|
+
globalLayout.systemThemes = systemThemes;
|
|
21
24
|
return (
|
|
22
25
|
<html lang="en">
|
|
23
26
|
<head>
|
|
@@ -3,15 +3,12 @@
|
|
|
3
3
|
import { useEffect } from "react";
|
|
4
4
|
import { RemoveElement } from "../../helpers/browser-helpers";
|
|
5
5
|
import { IGlobalLayout, ROOT_LOADER_IDENTITY } from "../../interfaces/layout-interfaces";
|
|
6
|
-
import { DEFAULT_THEME_INFORMATIONS } from "../../interfaces/theme-interfaces";
|
|
7
6
|
import { O } from "../../nexus-client";
|
|
8
7
|
import { InitializeStyles } from "../../services/theme-service";
|
|
9
8
|
|
|
10
9
|
export const LayoutHelpers = ({ globalLayout }: Readonly<{ globalLayout: IGlobalLayout }>) => {
|
|
11
10
|
useEffect(() => {
|
|
12
|
-
|
|
13
|
-
O.systemThemes = DEFAULT_THEME_INFORMATIONS;
|
|
14
|
-
|
|
11
|
+
O.systemThemes = globalLayout.systemThemes;
|
|
15
12
|
const timeout = setTimeout(() => {
|
|
16
13
|
clearTimeout(timeout);
|
|
17
14
|
RemoveElement(ROOT_LOADER_IDENTITY, 500);
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import Image from "next/image";
|
|
2
|
+
import React, { JSX, useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { GetProfileDetails } from "../../../api-services/authentication-service";
|
|
4
|
+
import { BuildImageUrl, GetAuthPageURL, GetSignOutPageURL } from "../../../helpers/browser-helpers";
|
|
5
|
+
import { BUTTON_IDENTITY, ClickHandler, EBackgrounds, EButtonTypes, EFileSizes, EIcons, EMenuVisibility, EOpacity, EPositions, ESizes, GetButton, GetUtilityMenuCode, IButton, ICurrentUser, IGlobalLayout, IPanelMenu, IsAdmin_SuperAdmin, UN } from "../../../interface";
|
|
6
|
+
import { FILES } from "../../../nexus.environments";
|
|
7
|
+
import { InputLoader, ShowLoader } from "../../../services/loader-service";
|
|
8
|
+
import { SetCurrentUser } from "../../../services/user-services";
|
|
9
|
+
import { Buttons } from "../../documents/button";
|
|
10
|
+
import { Iconbox } from "../../documents/icon-box";
|
|
11
|
+
import { GetUserRoleTag } from "../../documents/tag";
|
|
12
|
+
import { GlobalDialogbox } from "../global-dialogbox";
|
|
13
|
+
|
|
14
|
+
const USER_PROFILE_ID = GetUtilityMenuCode("user-profile");
|
|
15
|
+
export const DEFAULT_USER_PROFILE_PICTURE_CLASS = "profile-user";
|
|
16
|
+
const USER_PROFILE_BUTTON_ID = "user-profile-menu-tab";
|
|
17
|
+
|
|
18
|
+
export const ProfilePanel = ({ globalLayout, userPanels, boxBackground, hasAccessToken }: Readonly<{ globalLayout: IGlobalLayout; userPanels: IPanelMenu[]; boxBackground?: EBackgrounds; hasAccessToken?: boolean | UN }>): JSX.Element => {
|
|
19
|
+
const [isLoading, setIsLoading] = useState(hasAccessToken === true);
|
|
20
|
+
const [currentUser, setCurrentUser] = useState<ICurrentUser | null>(null);
|
|
21
|
+
const groupPanels = useMemo(() => {
|
|
22
|
+
// TODO: Implement menu visibility logic
|
|
23
|
+
const isAuthenticated = currentUser && currentUser.username && hasAccessToken === true;
|
|
24
|
+
const visibilityFlag = isAuthenticated ? EMenuVisibility.AuthenticatedOnly : EMenuVisibility.GuestOnly;
|
|
25
|
+
const visibleMenus = userPanels.filter((x) => (x.menuVisibilities & visibilityFlag) !== 0);
|
|
26
|
+
return groupByGroupOrder(visibleMenus);
|
|
27
|
+
}, [userPanels, currentUser]);
|
|
28
|
+
const fetchProfileDetails = async () => {
|
|
29
|
+
setIsLoading(true);
|
|
30
|
+
InputLoader(USER_PROFILE_ID, true);
|
|
31
|
+
const response = await GetProfileDetails(true);
|
|
32
|
+
setCurrentUser(response);
|
|
33
|
+
SetCurrentUser(response);
|
|
34
|
+
setIsLoading(false);
|
|
35
|
+
InputLoader(USER_PROFILE_ID, false);
|
|
36
|
+
};
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (hasAccessToken) fetchProfileDetails();
|
|
39
|
+
}, [hasAccessToken]);
|
|
40
|
+
return (
|
|
41
|
+
<div id={USER_PROFILE_ID} className={`pr ${ESizes.Small} ib ${boxBackground} h p2 r`}>
|
|
42
|
+
<Image src={FILES.ICONS.USER} alt="logo" width={74} height={74} className="img" />
|
|
43
|
+
{!isLoading && (
|
|
44
|
+
<GlobalDialogbox hoverEffectDelay={false} defaultPadding={0} position={EPositions.Left} buttonIdentity={USER_PROFILE_ID} iggnoreChildClick={false} defaultWidth={25}>
|
|
45
|
+
<React.Fragment>
|
|
46
|
+
<div className="bb h100 oh df ddc pl5">
|
|
47
|
+
<div style={{ display: currentUser ? "flex" : "none" }} className="df ac pr5 pt5">
|
|
48
|
+
<div className="ib xl oh item pr">
|
|
49
|
+
{currentUser && <Iconbox onClicked={() => onEditProfileClicked(currentUser)} iconOpacity={EOpacity.NONE} iconDefaultClass="xl" defaultClass="xl pa ho1 bg950 rr" background={EBackgrounds.None} icon={EIcons.CAMERA} />}
|
|
50
|
+
<img src={BuildImageUrl(currentUser?.profilePicture, EFileSizes.SIZE_128, FILES.ICONS.USER)} alt="profile-picture" className={`${DEFAULT_USER_PROFILE_PICTURE_CLASS} img ba2 rr`} />
|
|
51
|
+
</div>
|
|
52
|
+
<div className="ml5 w100">
|
|
53
|
+
<div className="df ac">
|
|
54
|
+
<h5 className="fw500 bg1 o09">{currentUser?.fullname}</h5>
|
|
55
|
+
</div>
|
|
56
|
+
<div className="df ac mt2">
|
|
57
|
+
<p className="bg1 mr5 o06">{currentUser?.emailAddress ?? `@${currentUser?.username}`}</p>
|
|
58
|
+
{IsAdmin_SuperAdmin(currentUser?.roleName) && GetUserRoleTag(currentUser?.roleName, true, "")}
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div className="sb sm h100 mt5 mb5 pr5">
|
|
64
|
+
{groupPanels.map((group, index) => (
|
|
65
|
+
<Buttons key={index} padding={0} pt={index !== 0 ? 5 : 0} buttons={group} />
|
|
66
|
+
))}
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</React.Fragment>
|
|
70
|
+
</GlobalDialogbox>
|
|
71
|
+
)}
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const onEditProfileClicked = (currentUser: ICurrentUser) => {
|
|
77
|
+
// TODO: Implement edit profile functionality
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
function groupByGroupOrder(menuArray: IPanelMenu[]): IButton[][] {
|
|
81
|
+
const grouped: Record<number, IButton[]> = {};
|
|
82
|
+
menuArray.forEach((menu) => {
|
|
83
|
+
if (!grouped[menu.groupOrder]) {
|
|
84
|
+
grouped[menu.groupOrder] = [];
|
|
85
|
+
}
|
|
86
|
+
grouped[menu.groupOrder].push(GetButton(menu.menuName, menu.menuCode, menu.menuUrl, menu.menuIcon, onClickedPanelMenu, menu, null, null, menu.displayOrder, null, null, 12, 0, EButtonTypes.BUTTON));
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const groupedArray = Object.keys(grouped)
|
|
90
|
+
.sort((a, b) => Number(a) - Number(b)) // Sort groups by groupOrder
|
|
91
|
+
.map((groupKey) => grouped[Number(groupKey)].sort((a, b) => (a.displayOrder ?? 0) - (b.displayOrder ?? 0)));
|
|
92
|
+
|
|
93
|
+
return groupedArray;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const onClickedPanelMenu: ClickHandler = async (event, element, data: IPanelMenu) => {
|
|
97
|
+
switch (data.menuCode) {
|
|
98
|
+
case BUTTON_IDENTITY(false).SIGN_IN_BUTTON: {
|
|
99
|
+
ShowLoader();
|
|
100
|
+
const url = GetAuthPageURL();
|
|
101
|
+
return (window.location.href = url);
|
|
102
|
+
}
|
|
103
|
+
case BUTTON_IDENTITY(false).SIGN_OUT_BUTTON: {
|
|
104
|
+
ShowLoader();
|
|
105
|
+
SetCurrentUser(null);
|
|
106
|
+
return (window.location.href = GetSignOutPageURL());
|
|
107
|
+
}
|
|
108
|
+
default: {
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
@@ -1,56 +1,36 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { UN } from "nexus-shared";
|
|
2
2
|
import { EIconTypes } from "../../interfaces/icon-interfaces";
|
|
3
|
-
import { EPositions, ESizes, IGlobalLayout } from "../../interfaces/layout-interfaces";
|
|
4
|
-
import { GetUtilityMenuCode, IPanelMenu, IUtilityMenu
|
|
3
|
+
import { EOpacity, EPositions, ESizes, IGlobalLayout } from "../../interfaces/layout-interfaces";
|
|
4
|
+
import { BUTTON_IDENTITY, GetUtilityMenuCode, IPanelMenu, IUtilityMenu } from "../../interfaces/menu-interfaces";
|
|
5
5
|
import { EBackgrounds } from "../../interfaces/theme-interfaces";
|
|
6
6
|
import { Iconbox } from "../documents/icon-box";
|
|
7
7
|
import { PageTitle } from "../documents/page-title";
|
|
8
8
|
import { ThemePanel } from "../panels/theme-panel";
|
|
9
9
|
import { GlobalDialogbox } from "./global-dialogbox";
|
|
10
|
+
import { ProfilePanel } from "./panels/user-panel";
|
|
10
11
|
|
|
11
|
-
export const
|
|
12
|
-
const _ = isneedPrefix ? UTILITIES_BUTTON_PREFIX : "";
|
|
13
|
-
return {
|
|
14
|
-
PROFILE_BOX: `${_}profile`,
|
|
15
|
-
SEARCH_BOX: `${_}root-search`,
|
|
16
|
-
FAVORITE_BOX: `${_}favorite-menu`,
|
|
17
|
-
CARD_BOX: `${_}cart-menu`,
|
|
18
|
-
THEME_BOX: `${_}switch-theme`,
|
|
19
|
-
MEGA_BOX: `${_}mega-menu`,
|
|
20
|
-
SIGN_IN_BUTTON: `${_}sign-in`,
|
|
21
|
-
SIGN_OUT_BUTTON: `${_}sign-out`,
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
export const UtilityMenu = ({ utilityMenus, userPanels, column, paddingRight, globalLayout, iconBoxBackground }: Readonly<{ utilityMenus: IUtilityMenu[]; userPanels: IPanelMenu[]; column?: number; paddingRight?: number; globalLayout: IGlobalLayout; iconBoxBackground: EBackgrounds }>) => {
|
|
12
|
+
export const UtilityMenu = ({ utilityMenus, userPanels, column, paddingRight, globalLayout, iconBoxBackground, hasAccessToken }: Readonly<{ utilityMenus: IUtilityMenu[]; userPanels: IPanelMenu[]; column?: number; paddingRight?: number; globalLayout: IGlobalLayout; iconBoxBackground: EBackgrounds; hasAccessToken?: boolean | UN }>) => {
|
|
25
13
|
return (
|
|
26
|
-
<div style={{ paddingRight: paddingRight ?? 0
|
|
14
|
+
<div style={{ paddingRight: paddingRight ?? 0 }} className={`utilities df fr ac jr h100p col-${column ?? 12}`}>
|
|
27
15
|
{utilityMenus.map((utilityMenu, index) => (
|
|
28
16
|
<UtilityIcon key={index} utilityMenu={utilityMenu} boxBackground={iconBoxBackground} globalLayout={globalLayout} />
|
|
29
17
|
))}
|
|
30
|
-
<ProfilePanel globalLayout={globalLayout} boxBackground={iconBoxBackground} userPanels={userPanels} />
|
|
18
|
+
<ProfilePanel globalLayout={globalLayout} boxBackground={iconBoxBackground} userPanels={userPanels} hasAccessToken={hasAccessToken} />
|
|
31
19
|
</div>
|
|
32
20
|
);
|
|
33
21
|
};
|
|
34
22
|
|
|
35
23
|
const UtilityIcon = ({ utilityMenu, globalLayout, boxBackground }: Readonly<{ utilityMenu: IUtilityMenu; globalLayout: IGlobalLayout; boxBackground?: EBackgrounds }>) => {
|
|
36
24
|
return (
|
|
37
|
-
<Iconbox id={GetUtilityMenuCode(utilityMenu.menuCode)} icon={utilityMenu.menuIcon} iconType={EIconTypes.FILLED} iconSize={ESizes.Small} boxSize={ESizes.Small} background={boxBackground} defaultClass="
|
|
25
|
+
<Iconbox id={GetUtilityMenuCode(utilityMenu.menuCode)} icon={utilityMenu.menuIcon} iconType={EIconTypes.FILLED} iconSize={ESizes.Small} boxSize={ESizes.Small} background={boxBackground} iconOpacity={EOpacity.O07} defaultClass="ml2">
|
|
38
26
|
<UtilityDialogBox utilityMenu={utilityMenu} globalLayout={globalLayout} />
|
|
39
27
|
</Iconbox>
|
|
40
28
|
);
|
|
41
29
|
};
|
|
42
30
|
|
|
43
|
-
export const ProfilePanel = ({ globalLayout, userPanels, boxBackground }: Readonly<{ globalLayout: IGlobalLayout; userPanels: IPanelMenu[]; boxBackground?: EBackgrounds }>): JSX.Element => {
|
|
44
|
-
return (
|
|
45
|
-
<div className={`pr ${ESizes.Small} ib ${boxBackground} h p2 r`}>
|
|
46
|
-
<h6>BA</h6>
|
|
47
|
-
</div>
|
|
48
|
-
);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
31
|
const UtilityDialogBox = ({ utilityMenu, globalLayout }: Readonly<{ utilityMenu: IUtilityMenu; globalLayout: IGlobalLayout }>) => {
|
|
52
32
|
return (
|
|
53
|
-
<GlobalDialogbox hoverEffectDelay={
|
|
33
|
+
<GlobalDialogbox hoverEffectDelay={false} defaultPadding={5} position={EPositions.Left} buttonIdentity={GetUtilityMenuCode(utilityMenu.menuCode)} defaultWidth={30}>
|
|
54
34
|
<div>
|
|
55
35
|
<PageTitle marginBottom={5} borderBottom={1} title={utilityMenu.menuName} />
|
|
56
36
|
<UtilityPanel utilityMenu={utilityMenu} globalLayout={globalLayout} />
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
2
|
|
|
3
3
|
import React, { useEffect } from "react";
|
|
4
4
|
import { GetButton, IButton } from "../../interfaces/button-interfaces";
|
|
@@ -30,7 +30,7 @@ const RenderSwitchTheme = (baseElement: HTMLElement) => {
|
|
|
30
30
|
baseElement.appendChild(p);
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
|
-
const themeButtons: IButton[] = themes.map((theme, index) => GetButton(theme.displayName, theme.systemName, null, theme.
|
|
33
|
+
const themeButtons: IButton[] = themes.map((theme, index) => GetButton(theme.displayName, theme.systemName, null, theme.iconName, onClicked, theme, null, null, theme.displayOrder, EIconTypes.OUTLINED, null));
|
|
34
34
|
const activeButton = themeButtons.find((button) => button.id === currentTheme);
|
|
35
35
|
CreateTabButtons(themeButtons, baseElement, activeButton, onClicked);
|
|
36
36
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { BuildProxyApiUrl, EModules, ESigninRequestMode, UN } from "../interface";
|
|
1
2
|
import { EAnimationClasses } from "../interfaces/layout-interfaces";
|
|
2
|
-
import { NEXUS_INFO } from "../nexus.environments";
|
|
3
|
-
import { EKeyModifier, EKeys } from "./../interfaces/browser-interfaces";
|
|
3
|
+
import { FILES, NEXUS_INFO, URLS } from "../nexus.environments";
|
|
4
|
+
import { EFileSizes, EKeyModifier, EKeys } from "./../interfaces/browser-interfaces";
|
|
4
5
|
import { HasFlag } from "./bitwise-helpers";
|
|
5
6
|
import { Debounce } from "./utility-helpers";
|
|
6
7
|
|
|
7
8
|
export const IsBrowser = (): boolean => typeof window !== "undefined" && typeof document !== "undefined";
|
|
8
9
|
|
|
9
|
-
export const RemoveElement = async (elementIdentity: string | HTMLElement, timeout?: number, animation?: EAnimationClasses): Promise<boolean> => {
|
|
10
|
+
export const RemoveElement = async (elementIdentity: string | HTMLElement, timeout?: number | UN, animation?: EAnimationClasses | UN): Promise<boolean> => {
|
|
10
11
|
return new Promise(async (resolve) => {
|
|
11
12
|
if (!IsBrowser() || !elementIdentity) return resolve(false);
|
|
12
13
|
else if (typeof elementIdentity === "string") {
|
|
@@ -69,3 +70,87 @@ export const FindChildElements = (parent: HTMLElement, childSelector: string, sh
|
|
|
69
70
|
return children.length > 0 ? [children[0]] : [];
|
|
70
71
|
}
|
|
71
72
|
};
|
|
73
|
+
export const PxToRem = (px: number): number => {
|
|
74
|
+
const fontSize = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
|
|
75
|
+
return Math.round((px / fontSize) * 1000) / 1000;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const ConvertColorToRGBA = (color: string, alpha: number = 0.1): string => {
|
|
79
|
+
if (!color) return "rgba(0, 0, 0, 0.1)";
|
|
80
|
+
|
|
81
|
+
let finalColor = color.trim();
|
|
82
|
+
if (finalColor.startsWith("var(")) finalColor = finalColor.replace(/^var\(\s*/, "").replace(/\s*\)$/, "");
|
|
83
|
+
// CSS Variable Support
|
|
84
|
+
if (finalColor.startsWith("--")) finalColor = getComputedStyle(document.documentElement).getPropertyValue(finalColor).trim();
|
|
85
|
+
|
|
86
|
+
// Create temporary element for browser auto parsing
|
|
87
|
+
const temp = document.createElement("div");
|
|
88
|
+
temp.style.color = finalColor;
|
|
89
|
+
|
|
90
|
+
document.body.appendChild(temp);
|
|
91
|
+
|
|
92
|
+
const computed = getComputedStyle(temp).color;
|
|
93
|
+
|
|
94
|
+
document.body.removeChild(temp);
|
|
95
|
+
|
|
96
|
+
// computed => rgb(...) or rgba(...)
|
|
97
|
+
const match = computed.match(/rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)(?:[,\s\/]+([\d.]+))?\s*\)/i);
|
|
98
|
+
|
|
99
|
+
if (!match) return "rgba(0, 0, 0, 1)";
|
|
100
|
+
|
|
101
|
+
const r = parseInt(match[1], 10);
|
|
102
|
+
const g = parseInt(match[2], 10);
|
|
103
|
+
const b = parseInt(match[3], 10);
|
|
104
|
+
|
|
105
|
+
let a = match[4] !== undefined ? parseFloat(match[4]) : 1;
|
|
106
|
+
|
|
107
|
+
// Override alpha if provided
|
|
108
|
+
if (alpha) a = alpha;
|
|
109
|
+
|
|
110
|
+
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
111
|
+
};
|
|
112
|
+
export function FindParentElement<ELEMENT extends Element>(element: Element | null | undefined, selector: string): ELEMENT | null {
|
|
113
|
+
if (!element || !selector || !element.matches) return null;
|
|
114
|
+
|
|
115
|
+
if (element.matches(selector)) return element as ELEMENT;
|
|
116
|
+
|
|
117
|
+
return FindParentElement<ELEMENT>(element.parentElement, selector);
|
|
118
|
+
}
|
|
119
|
+
export const GetCurrentFullURL = () => window?.location?.href;
|
|
120
|
+
export const GetCurrentBaseURL = () => window?.location?.origin;
|
|
121
|
+
export const BuildImageUrl = (viewToken?: string | UN, size?: EFileSizes | UN, fallback?: string | UN, isPublic: boolean = false) => {
|
|
122
|
+
if (!viewToken) return fallback ?? FILES.ICONS.NO_IMAGE;
|
|
123
|
+
if (!size) size = EFileSizes.SIZE_256;
|
|
124
|
+
if (isPublic) return BuildProxyApiUrl(EModules.Drive, `Public/Image/${size}/${viewToken}`);
|
|
125
|
+
else return BuildProxyApiUrl(EModules.Drive, `Private/Image/${size}/${viewToken}`);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const resolveReturnPath = (redirectURL?: string | UN): string => {
|
|
129
|
+
if (redirectURL) {
|
|
130
|
+
try {
|
|
131
|
+
const url = new URL(redirectURL, GetCurrentBaseURL());
|
|
132
|
+
return url.pathname + url.search || "/";
|
|
133
|
+
} catch {
|
|
134
|
+
/* fall through to current location */
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (!IsBrowser()) return "/";
|
|
138
|
+
return window.location.pathname + window.location.search || "/";
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const GetAuthPageURL = (redirectURL?: string | UN, signinMode?: ESigninRequestMode | UN) => {
|
|
142
|
+
const setupUrl = new URL(URLS.GLOBAL_SIGNIN);
|
|
143
|
+
setupUrl.searchParams.set("redirect_uri", `${GetCurrentBaseURL()}/api/auth/callback`);
|
|
144
|
+
setupUrl.searchParams.set("state", resolveReturnPath(redirectURL));
|
|
145
|
+
setupUrl.searchParams.set("signin_mode", signinMode ?? ESigninRequestMode.DEFAULT);
|
|
146
|
+
return setupUrl.toString();
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/** Clears the session via /api/auth/sign-out; non-admin apps return to the current page (with query params). */
|
|
150
|
+
export const GetSignOutPageURL = (returnPath?: string | UN) => {
|
|
151
|
+
const signOutUrl = new URL(`${GetCurrentBaseURL()}/api/auth/sign-out`);
|
|
152
|
+
if (!NEXUS_INFO.IS_ADMIN_MODULE) {
|
|
153
|
+
signOutUrl.searchParams.set("returnUrl", resolveReturnPath(returnPath));
|
|
154
|
+
}
|
|
155
|
+
return signOutUrl.toString();
|
|
156
|
+
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { DEFAULT_ICON_ELEMENT_NAME } from "../interface";
|
|
1
|
+
import { DEFAULT_ICON_ELEMENT_NAME, IErrorRespose } from "../interface";
|
|
2
2
|
import { EValueChangeTypes, IInputCreateResults, IInputRootParameters } from "../interfaces/input-interfaces";
|
|
3
3
|
import { ESizes } from "../interfaces/layout-interfaces";
|
|
4
4
|
import { UN } from "../interfaces/type-interfaces";
|
|
5
|
+
import { AddRemoveErrorFromInput } from "./validation-helpers";
|
|
5
6
|
|
|
7
|
+
export const INPUT_ICON_BOX_CLASS = "input-icon-box";
|
|
6
8
|
export const GetParentKey = (parentKey?: string | null) => {
|
|
7
9
|
if (parentKey) return parentKey.split("|")[0];
|
|
8
10
|
return undefined;
|
|
@@ -23,22 +25,50 @@ export const ForceSetValue = (input?: IInputCreateResults | null, value?: any |
|
|
|
23
25
|
clearTimeout(timeoutValue);
|
|
24
26
|
}, timeout ?? 0);
|
|
25
27
|
};
|
|
28
|
+
|
|
29
|
+
export const ApplyPasswordInputs = (inputResults: IInputCreateResults[] | UN, selectors: string[] | UN) => {
|
|
30
|
+
if (!inputResults || inputResults.length === 0 || !selectors || selectors.length === 0) return;
|
|
31
|
+
for (const selector of selectors) {
|
|
32
|
+
const input = inputResults.find((x) => x.input.id === selector);
|
|
33
|
+
if (input) ApplyPasswordInput(input.rootElement.querySelector(`div.ib.${INPUT_ICON_BOX_CLASS}`), input.inputElement);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
26
37
|
export const ApplyPasswordInput = (iconbox: HTMLElement | null, inputElement: HTMLElement | null) => {
|
|
27
38
|
const input = inputElement as HTMLInputElement | undefined;
|
|
28
39
|
if (!input || !iconbox) return;
|
|
40
|
+
|
|
41
|
+
if (!iconbox.classList.contains("h")) iconbox.classList.add("h");
|
|
42
|
+
|
|
29
43
|
const icon = iconbox.querySelector(DEFAULT_ICON_ELEMENT_NAME) as HTMLElement | null;
|
|
30
|
-
if (icon)
|
|
44
|
+
if (!icon) return;
|
|
31
45
|
const onMouseLeave = () => {
|
|
32
46
|
iconbox.removeEventListener("mouseleave", onMouseLeave);
|
|
33
47
|
iconbox.removeEventListener("click", onClicked);
|
|
34
48
|
input.type = "password";
|
|
35
49
|
if (icon) icon.setAttribute("name", "key");
|
|
36
50
|
};
|
|
51
|
+
const onMouseEnter = () => {
|
|
52
|
+
icon.setAttribute("name", "eye");
|
|
53
|
+
iconbox.addEventListener("mouseleave", onMouseLeave);
|
|
54
|
+
iconbox.addEventListener("click", onClicked);
|
|
55
|
+
};
|
|
37
56
|
const onClicked = () => {
|
|
38
57
|
const isPassword = input.type === "password";
|
|
39
58
|
input.type = isPassword ? "text" : "password";
|
|
40
59
|
if (icon) icon.setAttribute("name", isPassword ? "eye-off" : "eye");
|
|
41
60
|
};
|
|
42
|
-
iconbox.addEventListener("
|
|
43
|
-
|
|
61
|
+
iconbox.addEventListener("mouseenter", onMouseEnter);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const SetResponseErrorMessage = (apiResponse: IErrorRespose, inputResults: IInputCreateResults[] | UN) => {
|
|
65
|
+
if (!apiResponse || !inputResults || inputResults.length === 0 || !apiResponse.inputName) return;
|
|
66
|
+
|
|
67
|
+
const inputKeys = apiResponse.inputName.toLowerCase().split("|");
|
|
68
|
+
let isFirstElement = true;
|
|
69
|
+
for (const inputKey of inputKeys) {
|
|
70
|
+
const input = inputResults.find((x) => GetInputId(x.input).toLowerCase() === inputKey);
|
|
71
|
+
if (input) AddRemoveErrorFromInput(input.input, input.inputElement, true, apiResponse.message, isFirstElement);
|
|
72
|
+
isFirstElement = false;
|
|
73
|
+
}
|
|
44
74
|
};
|