nexus-shared 1.1.5 → 1.1.6

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.
Files changed (70) hide show
  1. package/package.json +8 -4
  2. package/src/api-services/preference-service.tsx +5 -0
  3. package/src/api-services/system-service.tsx +322 -0
  4. package/src/components/documents/button.tsx +136 -0
  5. package/src/components/documents/icon-box.tsx +92 -0
  6. package/src/components/documents/page-title.tsx +7 -0
  7. package/src/components/documents/tab-button.tsx +169 -0
  8. package/src/components/index.js +0 -0
  9. package/src/components/inputs/checkbox-input.tsx +66 -0
  10. package/src/components/inputs/input-box.tsx +45 -0
  11. package/src/components/inputs/input-element.tsx +65 -0
  12. package/src/components/inputs/input-form.tsx +50 -0
  13. package/src/components/inputs/input.tsx +181 -0
  14. package/src/components/inputs/number-input.tsx +108 -0
  15. package/src/components/inputs/radiobox-input.tsx +53 -0
  16. package/src/components/inputs/textarea-input.tsx +47 -0
  17. package/src/components/inputs/textbox-input.tsx +45 -0
  18. package/src/components/layouts/global-dialogbox.tsx +433 -0
  19. package/src/components/layouts/global-layout.tsx +63 -0
  20. package/src/components/layouts/layout-helpers.tsx +23 -0
  21. package/src/components/layouts/utility-menu.tsx +71 -0
  22. package/src/components/panels/theme-panel.tsx +44 -0
  23. package/src/helpers/bitwise-helpers.tsx +11 -0
  24. package/src/helpers/browser-helpers.tsx +73 -0
  25. package/src/helpers/datasource-helpers.tsx +99 -0
  26. package/src/helpers/element-helpers.tsx +57 -0
  27. package/src/helpers/input-helpers.tsx +24 -0
  28. package/src/helpers/string-helpers.tsx +28 -0
  29. package/src/helpers/utility-helpers.tsx +44 -0
  30. package/src/index.ts +0 -11
  31. package/src/interfaces/browser-interfaces.tsx +23 -0
  32. package/src/interfaces/button-interfaces.tsx +63 -0
  33. package/src/interfaces/datasource-interfaces.tsx +22 -0
  34. package/src/interfaces/datatable-interfaces.tsx +25 -0
  35. package/src/interfaces/dialogbox-interfaces.tsx +5 -0
  36. package/src/interfaces/http-interfaces.tsx +15 -0
  37. package/src/interfaces/icon-interfaces.tsx +126 -0
  38. package/src/interfaces/input-interfaces.tsx +360 -0
  39. package/src/interfaces/layout-interfaces.tsx +191 -0
  40. package/src/interfaces/menu-interfaces.tsx +36 -0
  41. package/src/interfaces/permission-interfaces.tsx +9 -0
  42. package/src/interfaces/storage-interfaces.tsx +3 -0
  43. package/src/interfaces/system-interfaces.tsx +22 -0
  44. package/src/interfaces/theme-interfaces.tsx +209 -0
  45. package/src/interfaces/type-interfaces.tsx +22 -0
  46. package/src/nexus-client.tsx +23 -0
  47. package/src/nexus.environments.tsx +34 -0
  48. package/src/services/loader-service.tsx +168 -0
  49. package/src/services/localstorage-service.tsx +45 -0
  50. package/src/services/theme-service.tsx +149 -0
  51. package/src/styles/nexus.animation.css +269 -0
  52. package/src/styles/nexus.core.css +119 -0
  53. package/src/styles/nexus.dialog.css +141 -0
  54. package/src/styles/nexus.icon.css +50 -0
  55. package/src/styles/nexus.input.css +207 -0
  56. package/src/styles/nexus.loader.css +11 -0
  57. package/src/styles/nexus.logic.css +18 -0
  58. package/src/styles/nexus.utility.css +347 -0
  59. package/src/client/index.ts +0 -1
  60. package/src/client/nexus-selectable-list.css +0 -131
  61. package/src/client/nexus-selectable-list.tsx +0 -111
  62. package/src/client.ts +0 -7
  63. package/src/interface.ts +0 -5
  64. package/src/interfaces/index.ts +0 -6
  65. package/src/interfaces/nexus-base.ts +0 -5
  66. package/src/interfaces/nexus-list.ts +0 -24
  67. package/src/server/index.ts +0 -1
  68. package/src/server/nexus-stat-list.css +0 -92
  69. package/src/server/nexus-stat-list.tsx +0 -46
  70. package/src/server.ts +0 -7
@@ -0,0 +1,169 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { CreateClone, DynamicSorting, GetVisibleAndHiddenItems } from "../../helpers/datasource-helpers";
3
+ import { RenderChildren } from "../../helpers/element-helpers";
4
+ import { IButton } from "../../interfaces/button-interfaces";
5
+ import { IDialogboxResults } from "../../interfaces/dialogbox-interfaces";
6
+ import { DEFAULT_ICON_ELEMENT_NAME, EIcons, EIconTypes } from "../../interfaces/icon-interfaces";
7
+ import { EPositions } from "../../interfaces/layout-interfaces";
8
+ import { ClickHandler, UN } from "../../interfaces/type-interfaces";
9
+ import { GlobalDialogbox, InitializeDialogbox } from "./../layouts/global-dialogbox";
10
+ import { Button, Buttons, CreateButton, CreateButtons, SET_BUTTON_ACTIVE_CLASS } from "./button";
11
+ import { CreateToggleButton, Iconbox } from "./icon-box";
12
+
13
+ export const THEME_MENU_ELIPSE_BTN_ID = "theme-menu-ellipse-btn";
14
+
15
+ const GetMaxVisibleButtons = (parentWidth: number) => (parentWidth > 1400 ? 8 : parentWidth > 1200 ? 7 : parentWidth > 1000 ? 6 : parentWidth > 950 ? 5 : parentWidth > 750 ? 4 : parentWidth > 600 ? 3 : 2);
16
+
17
+ export function CreateTabButtons<T>(buttons: IButton[], parent: HTMLElement | null, activeButton?: IButton | null, onClicked?: ClickHandler): HTMLElement | null {
18
+ if (!buttons || buttons.length === 0) return null;
19
+ const elementList: HTMLElement[] = [];
20
+ const toggleButton = CreateToggleButton();
21
+ const themeContainer = document.createElement("div");
22
+ const visibleItems: IButton[] = [];
23
+ const hiddenItems: IButton[] = [];
24
+ const dataKey = "code";
25
+ const dataAttributeKey = "data-menu-code";
26
+ let dialogBoxObject: IDialogboxResults | null = null;
27
+ let maxVisible = 6;
28
+ let activeItem = activeButton ?? buttons[0];
29
+ let linkButtons: HTMLElement | undefined = undefined;
30
+
31
+ toggleButton.appendChild(themeContainer);
32
+
33
+ const buttonContainer = document.createElement("div");
34
+ buttonContainer.className = "df ac jc r";
35
+ if (parent) parent.appendChild(buttonContainer);
36
+
37
+ const onButtonClicked: ClickHandler = (event, element, button, index) => {
38
+ if (button === activeItem) return;
39
+ activeItem = button;
40
+ if (onClicked) onClicked(event, element, button, index);
41
+
42
+ const isHiddenItemClicked = hiddenItems.some((item) => item === button);
43
+ GetVisibleAndHiddenItems(buttons, visibleItems, hiddenItems, activeItem, maxVisible);
44
+ RenderChildren(buttonContainer, elementList, toggleButton, activeItem, visibleItems, hiddenItems, createButton, refreshElementClass, false, isHiddenItemClicked, dataKey, dataAttributeKey);
45
+ };
46
+ for (const button of buttons) button.onClick = (event, element, data, index) => onButtonClicked(event as Event, element, button);
47
+
48
+ const createButton = (button: IButton) => {
49
+ const btn = CreateButton(button, buttonContainer, null, false);
50
+ if (button.id) {
51
+ btn.setAttribute(dataAttributeKey, `btn-code-${button.id}`);
52
+ btn.id = button.id;
53
+ }
54
+
55
+ btn.classList.add("jc");
56
+ btn.style.height = "var(--IW)";
57
+
58
+ refreshElementClass(btn, activeItem);
59
+ return btn;
60
+ };
61
+
62
+ const refreshElementClass = (element: HTMLElement, activeItem: IButton | UN) => {
63
+ const menuCode = element?.getAttribute(dataAttributeKey);
64
+ const isActive = activeItem?.id && `btn-code-${activeItem?.id}` === menuCode;
65
+ SET_BUTTON_ACTIVE_CLASS(isActive as boolean, element);
66
+ const icon = element.querySelector(DEFAULT_ICON_ELEMENT_NAME);
67
+ if (isActive) {
68
+ element.classList.add("active");
69
+ if (document.activeElement) (document.activeElement as HTMLElement).blur();
70
+ if (icon) icon.setAttribute("name", activeItem.icon ?? "");
71
+ } else {
72
+ element.classList.remove("active");
73
+ if (activeItem)
74
+ if (icon) {
75
+ let iconName = icon.getAttribute("name");
76
+ if (iconName && !iconName.endsWith("-outline")) iconName += "-outline";
77
+ icon.setAttribute("name", iconName ?? "");
78
+ }
79
+ }
80
+ };
81
+ const onVisibility = (isVisible: boolean) => {
82
+ if (linkButtons) linkButtons.remove();
83
+ linkButtons = CreateButtons(hiddenItems, themeContainer, null, 6, 0);
84
+ };
85
+ const parentBound = parent?.getBoundingClientRect();
86
+ const parentWidth = parentBound ? parentBound.width : 1000;
87
+ maxVisible = GetMaxVisibleButtons(parentWidth);
88
+ GetVisibleAndHiddenItems(buttons, visibleItems, hiddenItems, activeItem, maxVisible);
89
+ RenderChildren(buttonContainer, elementList, toggleButton, activeItem, visibleItems, hiddenItems, createButton, refreshElementClass, true, false, dataKey, dataAttributeKey);
90
+
91
+ if (hiddenItems.length > 0) {
92
+ linkButtons = CreateButtons(hiddenItems, themeContainer, null, 6, 0);
93
+ dialogBoxObject = InitializeDialogbox(toggleButton, themeContainer, true, undefined, undefined, 40, undefined, onVisibility, undefined, false, EPositions.Right, undefined, undefined, undefined, true);
94
+ dialogBoxObject?.visibility(false, true);
95
+ }
96
+
97
+ return buttonContainer;
98
+ }
99
+ export const TabButtons = ({ buttons, activeButton, onClicked }: { buttons: IButton[]; activeButton?: string | null | Function; onClicked?: ClickHandler }) => {
100
+ const [tabButtons, setTabButtons] = useState<ITab>({ visibleButtons: null, hiddenButtons: null });
101
+ const [activeItem, setActiveItem] = useState<string | null>(null);
102
+ const tabContainerRef = React.useRef<HTMLDivElement>(null);
103
+
104
+ useEffect(() => {
105
+ const timeout = setTimeout(() => {
106
+ clearTimeout(timeout);
107
+
108
+ const tabContainer = tabContainerRef.current;
109
+ if (!tabContainer || !buttons || buttons.length === 0) return;
110
+
111
+ const sortingButtons = DynamicSorting<IButton>(buttons, "displayOrder", true);
112
+ const newButtons = CreateClone(sortingButtons) as IButton[];
113
+ const parentBound = tabContainer.getBoundingClientRect();
114
+ const parentWidth = parentBound.width;
115
+ const maxVisible = GetMaxVisibleButtons(parentWidth);
116
+ const visibleItems: IButton[] = [];
117
+ const hiddenItems: IButton[] = [];
118
+ const active = typeof activeButton === "function" ? (newButtons.find((btn) => btn.id === activeButton()) ?? newButtons[0]) : (newButtons.find((btn) => btn.id === (activeItem ?? activeButton)) ?? newButtons[0]);
119
+ GetVisibleAndHiddenItems(newButtons, visibleItems, hiddenItems, active, maxVisible);
120
+
121
+ for (const btn of visibleItems) {
122
+ if (active && active.id && btn.id === active.id) {
123
+ btn.iconType = EIconTypes.FILLED;
124
+ btn.defaultClass = "btn active jc r";
125
+ } else btn.defaultClass = "bg1 jc r";
126
+ }
127
+
128
+ for (const btn of hiddenItems) btn.defaultClass = "bg1";
129
+
130
+ for (const btn of newButtons) {
131
+ btn.onClick = (event, element, data, index) => {
132
+ if (btn.id === activeItem) return;
133
+ if (onClicked) onClicked(event, element, btn, index);
134
+ setActiveItem(btn.id ?? null);
135
+ };
136
+ }
137
+
138
+ setTabButtons({ visibleButtons: visibleItems, hiddenButtons: hiddenItems.length > 0 ? hiddenItems : null });
139
+ }, 0);
140
+ return () => clearTimeout(timeout);
141
+ }, [buttons, activeItem, onClicked]);
142
+
143
+ /* ---------------- render ---------------- */
144
+ return (
145
+ <div ref={tabContainerRef} className="df ac jc r">
146
+ {tabButtons.visibleButtons?.map((btn) => (
147
+ <Button
148
+ styles={{
149
+ height: "var(--IW)",
150
+ }}
151
+ key={btn.id}
152
+ button={btn}
153
+ />
154
+ ))}
155
+
156
+ {tabButtons.hiddenButtons && (
157
+ <Iconbox defaultClass="ml5" icon={EIcons.ELLIPSIS_HORIZONTAL} iconType={EIconTypes.FILLED} iconDefaultClass="r90" id={THEME_MENU_ELIPSE_BTN_ID}>
158
+ <GlobalDialogbox defaultPadding={0} iggnoreChildClick={false} buttonIdentity={THEME_MENU_ELIPSE_BTN_ID} position={EPositions.Right}>
159
+ <Buttons buttons={tabButtons.hiddenButtons} />
160
+ </GlobalDialogbox>
161
+ </Iconbox>
162
+ )}
163
+ </div>
164
+ );
165
+ };
166
+ interface ITab {
167
+ visibleButtons: IButton[] | null;
168
+ hiddenButtons: IButton[] | null;
169
+ }
File without changes
@@ -0,0 +1,66 @@
1
+ import { GetInputId, SetHaveValueClass } from "../../helpers/input-helpers";
2
+ import { EValueChangeTypes, ICheckbox, IInputCreateResults } from "../../interfaces/input-interfaces";
3
+ import { CreateInput } from "./input";
4
+ import { CreateInputBox, CreateInputWrapper } from "./input-box";
5
+ import { CreateRadiobox } from "./radiobox-input";
6
+
7
+ export const CreateCheckbox = (param: ICheckbox, parentElement: HTMLElement | null, isTabular: boolean) => {
8
+ if (param.groupId) return CreateRadiobox(param, parentElement, isTabular, true);
9
+
10
+ const input = CreateInput(param) as HTMLInputElement;
11
+ const ELEMENT_ID = GetInputId(param);
12
+
13
+ input.classList.add("pr");
14
+ input.classList.remove("ipt");
15
+ input.defaultChecked = param.defaultValue ?? false;
16
+
17
+ const box = CreateInputBox(param, input, null, isTabular);
18
+ const label = box.querySelector("label") as HTMLLabelElement;
19
+ if (label) label.className = "p2 ilb hc o08";
20
+ const wrapper = isTabular ? box : CreateInputWrapper(param, box, parentElement);
21
+
22
+ box.classList.add("df", "ac", "pl3", "ipt-bxh");
23
+ box.classList.remove("ipt-bx");
24
+
25
+ function notifyChanged(val: boolean, isInit: boolean, isForced: boolean, isDisposed: boolean) {
26
+ if (!param.checkedValue) param.onChanged?.(val, isInit, isForced, isDisposed, ELEMENT_ID, undefined);
27
+ else param.onChanged?.(val ? param.checkedValue : null, isInit, isForced, isDisposed, ELEMENT_ID, undefined);
28
+ }
29
+
30
+ function handleChanged(val: boolean, isInit: boolean, isForced: boolean, isDisposed: boolean, type?: EValueChangeTypes, invokeOnChanged?: boolean) {
31
+ SetHaveValueClass(input, val);
32
+ if (invokeOnChanged === false) return;
33
+ // Group members share one form key; only the checked member should report on init.
34
+ if (isInit && param.groupId && !val) return;
35
+ notifyChanged(val, isInit, isForced, isDisposed);
36
+ }
37
+
38
+ function forceChangeDefaultValue(defaultValue: boolean, type?: EValueChangeTypes, invokeOnChanged?: boolean): boolean {
39
+ input.checked = defaultValue;
40
+ handleChanged(defaultValue, false, true, false, type, invokeOnChanged);
41
+ return true;
42
+ }
43
+
44
+ function onInputChanged() {
45
+ handleChanged(input.checked, false, false, false);
46
+ }
47
+
48
+ input.addEventListener("input", onInputChanged);
49
+
50
+ input.checked = param.defaultValue ?? false;
51
+ handleChanged(input.checked, true, false, false);
52
+
53
+ return {
54
+ input: param,
55
+ inputElement: input,
56
+ rootElement: wrapper,
57
+ forceChangeDefaultValue: forceChangeDefaultValue,
58
+ dispose: function (invokeOnChanged?: boolean) {
59
+ if (invokeOnChanged !== false) handleChanged(param.defaultValue ?? false, false, true, true);
60
+ input.removeEventListener("input", onInputChanged);
61
+ input.remove();
62
+ box.remove();
63
+ wrapper.remove();
64
+ },
65
+ } as IInputCreateResults;
66
+ };
@@ -0,0 +1,45 @@
1
+ import { DEFAULT_INPUT_BORDER_SIZE, IInputRootParameters, REQUIRED_TEXT1 } from "../../interfaces/input-interfaces";
2
+ import { EBackgrounds } from "../../interfaces/theme-interfaces";
3
+ import { CreateIconBox } from "../documents/icon-box";
4
+
5
+ export const CreateInputBox = (param: IInputRootParameters<any>, child: HTMLElement | null, parent: HTMLElement | null, isTabular: boolean) => {
6
+ const bg = param.background ?? "bg1";
7
+
8
+ const BORDER_SIZE = param.borderSize ?? DEFAULT_INPUT_BORDER_SIZE;
9
+
10
+ const border = BORDER_SIZE > 0 ? `b${BORDER_SIZE}` : "bat1";
11
+ const box = document.createElement("div");
12
+ box.className = `ipt-bx ${param.inputSize ?? ""} db bb anim ${border} pr r ${bg}`;
13
+ if (child) box.appendChild(child);
14
+
15
+ if (param.label && !isTabular) {
16
+ const label = document.createElement("label");
17
+ label.className = "pa t50 ty50 left ilb en anim ml2";
18
+ label.innerHTML = param.label + ((param as any).isRequired ? `<span class="tx21 fs-08 ml1">${REQUIRED_TEXT1}</span>` : "");
19
+ label.htmlFor = param.id;
20
+ box.appendChild(label);
21
+ }
22
+
23
+ if (param.icon) {
24
+ box.classList.add("df", "ac");
25
+ const I = param.icon;
26
+ CreateIconBox(I.icon, box, null, null, I.type, I.background ?? param.background ?? EBackgrounds.None, I.boxSize, I.iconSize, null, null, null, null, I.opacity, null, null, null, null, I.onClicked, I.onMouseEnter, (I.onClicked || I.onMouseEnter) !== undefined);
27
+ }
28
+
29
+ if (parent) parent.appendChild(box);
30
+ return box;
31
+ };
32
+ export const CreateInputWrapper = (param: IInputRootParameters<any>, child: HTMLElement | null, parent: HTMLElement | null) => {
33
+ const col = document.createElement("div");
34
+ const wp = document.createElement("div");
35
+ const smb = document.createElement("div");
36
+ col.className = !param.columnSize || param.columnSize === 12 ? "col-12" : `col-${param.columnSize}`;
37
+ if (param.autoAdjustColumn !== false) col.classList.add("col-sm-12");
38
+ wp.className = "ipt-wp";
39
+ smb.className = "smb";
40
+ if (child) wp.appendChild(child);
41
+ wp.appendChild(smb);
42
+ col.appendChild(wp);
43
+ if (parent) parent.appendChild(col);
44
+ return wp;
45
+ };
@@ -0,0 +1,65 @@
1
+ import { GetInputId } from "../../helpers/input-helpers";
2
+ import { EButtonTypes } from "../../interfaces/button-interfaces";
3
+ import { EIconTypes } from "../../interfaces/icon-interfaces";
4
+ import { IElement, IInputCreateResults, ILinkbutton, ISubmitbutton } from "../../interfaces/input-interfaces";
5
+ import { EBackgrounds } from "../../interfaces/theme-interfaces";
6
+ import { CreateLinkButton } from "../documents/button";
7
+ import { CreateIconBox } from "../documents/icon-box";
8
+ import { CreateInputBox, CreateInputWrapper } from "./input-box";
9
+
10
+ export const CreateElement = (param: IElement, parentElement: HTMLElement | null, isTabular: boolean): IInputCreateResults => {
11
+ const ELEMENT_ID = GetInputId(param);
12
+ const box = CreateInputBox(param, null, null, isTabular);
13
+ const label = box.querySelector("label") as HTMLLabelElement;
14
+ if (label) label.remove();
15
+ const wrapper = isTabular ? box : CreateInputWrapper(param, box, parentElement);
16
+
17
+ return {
18
+ input: param,
19
+ inputElement: box as HTMLInputElement,
20
+ rootElement: wrapper,
21
+ forceChangeDefaultValue: null,
22
+ dispose: function (invokeOnChanged?: boolean) {
23
+ if (invokeOnChanged !== false) param.onLoad?.(null, null, false, false, true, ELEMENT_ID, 0);
24
+ box.remove();
25
+ wrapper.remove();
26
+ },
27
+ } as IInputCreateResults;
28
+ };
29
+
30
+ export const CreateInputLink = (param: ILinkbutton, parentElement: HTMLElement | null, isTabular: boolean): IInputCreateResults => {
31
+ const elementObject = CreateElement(param, parentElement, isTabular);
32
+ const box = elementObject.inputElement as HTMLDivElement;
33
+ box.classList.add("bat1", "df", "ac", "ipt-bxh");
34
+ box.classList.remove("ipt-bx");
35
+ const link = CreateLinkButton(box, param.href, param.label ?? "Nexus Link", param.onClicked, param.defaultClass, null, param.borderBottom, param.background, param.target, param.position);
36
+
37
+ link.classList.add("w100");
38
+
39
+ box.appendChild(link);
40
+ return elementObject;
41
+ };
42
+ export const CreateInputButton = (param: ISubmitbutton, parentElement: HTMLElement | null, isTabular: boolean): IInputCreateResults => {
43
+ const iconParam = param.icon;
44
+ const button = document.createElement("button");
45
+ param.icon = undefined;
46
+ const elementObject = CreateElement(param, parentElement, isTabular);
47
+ const box = elementObject.inputElement as HTMLDivElement;
48
+ box.appendChild(button);
49
+ box.classList.add("h");
50
+ button.type = param.type ?? EButtonTypes.SUBMIT;
51
+
52
+ button.classList.add("h", "df", "ac", "jc", "ipt", "h100", "sn", "w100", "r", param.background ?? EBackgrounds.PrimaryColor);
53
+ const value = param.placeholder ?? param.label ?? "Submit";
54
+
55
+ if (iconParam) {
56
+ const span = document.createElement("span");
57
+ span.textContent = value;
58
+ span.classList.add("nw", "toh", "fs-10", "fw-600");
59
+ const icon = CreateIconBox(iconParam.icon, button, null, null, iconParam.type ?? EIconTypes.FILLED, iconParam.background ?? param.background ?? EBackgrounds.None, iconParam.boxSize, iconParam.iconSize, null, null, null, null, iconParam.opacity, null, null, null, null, iconParam.onClicked, iconParam.onMouseEnter);
60
+ icon.classList.add("mr3");
61
+ button.appendChild(span);
62
+ } else button.innerText = value;
63
+
64
+ return elementObject;
65
+ };
@@ -0,0 +1,50 @@
1
+ import React, { useEffect } from "react";
2
+ import { GetInputId } from "../../helpers/input-helpers";
3
+ import { IForm, IFormCreateResults, IInputCreateResults } from "../../interfaces/input-interfaces";
4
+ import { CreateInputs, DisposeInputs } from "./input";
5
+
6
+ const forceChangeDefaultValue = (inputResults: IInputCreateResults[], newDefaultValue: any): boolean => {
7
+ let isChanged = true;
8
+ for (const inputResult of inputResults) {
9
+ if (inputResult.forceChangeDefaultValue) {
10
+ const value = newDefaultValue ? (newDefaultValue[GetInputId(inputResult.input)] ?? null) : undefined;
11
+ const success = inputResult.forceChangeDefaultValue(value);
12
+ if (isChanged) isChanged = success;
13
+ }
14
+ }
15
+ return isChanged;
16
+ };
17
+ export const CreateForm = (param: IForm, rowGap?: number): IFormCreateResults | null => {
18
+ const { inputs, onChanged, index, isTabularForm, parentIdentifier } = param;
19
+ const parentElement = typeof parentIdentifier === "string" ? document.getElementById(parentIdentifier) : parentIdentifier;
20
+
21
+ if (!parentElement || inputs.length === 0 || !onChanged) {
22
+ //TODO: WarningMessage("Invalid Inputs", "Please provide valid inputs, onChanged function and parent element.");
23
+ return null;
24
+ }
25
+
26
+ const parent = document.createElement("div");
27
+ parent.className = `row g${rowGap ?? 3} sb fg`;
28
+ parent.style.paddingTop = "var(--IEM)";
29
+ if (parentElement) parentElement.appendChild(parent);
30
+
31
+ const inputResults = CreateInputs(inputs, onChanged, index, isTabularForm, parent, param.defaultValue);
32
+
33
+ return {
34
+ inputResults: inputResults,
35
+ formElement: parent,
36
+ dispose: (invokeOnChanged?: boolean) => {
37
+ DisposeInputs(inputResults, invokeOnChanged);
38
+ parent.remove();
39
+ },
40
+ forceChangeDefaultValue: (newDefaultValue: any) => forceChangeDefaultValue(inputResults, newDefaultValue),
41
+ } as IFormCreateResults;
42
+ };
43
+
44
+ export const Form = ({ param, rowGap }: { param: IForm; rowGap?: number }): React.ReactNode => {
45
+ useEffect(() => {
46
+ const formResults = CreateForm(param, rowGap);
47
+ return () => formResults?.dispose();
48
+ }, [param, rowGap]);
49
+ return <React.Fragment></React.Fragment>;
50
+ };
@@ -0,0 +1,181 @@
1
+ import { CreateClone } from "../../helpers/datasource-helpers";
2
+ import { GetInputClasses, GetInputId, GetParentKey } from "../../helpers/input-helpers";
3
+ import { IsNullOrEmpty } from "../../helpers/string-helpers";
4
+ import { Debounce } from "../../helpers/utility-helpers";
5
+ import { DEFAULT_TEXTAREA_ROWS, EInputTypes, ICheckbox, IElement, IInputCreateResults, IInputRootParameters, ILinkbutton, INumber, IRadiobox, ISlider, ISubmitbutton, ITextarea, ITextbox, MAX_INPUT_TYPE, OnFormChangeHandler, REQUIRED_TEXT1 } from "../../interfaces/input-interfaces";
6
+ import { UN } from "../../interfaces/type-interfaces";
7
+ import { CreateCheckbox } from "./checkbox-input";
8
+ import { CreateElement, CreateInputButton, CreateInputLink } from "./input-element";
9
+ import { CreateNumber } from "./number-input";
10
+ import { CreateRadiobox } from "./radiobox-input";
11
+ import { CreateTextarea } from "./textarea-input";
12
+ import { CreateTextbox } from "./textbox-input";
13
+
14
+ const _getInputType = (inputType: EInputTypes) => {
15
+ switch (inputType) {
16
+ case EInputTypes.Checkbox:
17
+ return "checkbox";
18
+ case EInputTypes.Textarea:
19
+ return "textarea";
20
+ case EInputTypes.Number:
21
+ return "number";
22
+ case EInputTypes.Slider:
23
+ case EInputTypes.Range:
24
+ return "range";
25
+ default:
26
+ return "text";
27
+ }
28
+ };
29
+
30
+ export function CreateInput(param: ITextbox | ISlider | ICheckbox | ITextarea | INumber) {
31
+ const isTextarea = param.inputType === EInputTypes.Textarea;
32
+ const isNumber = param.inputType === EInputTypes.Number;
33
+ const isSlider = param.inputType === EInputTypes.Slider || param.inputType === EInputTypes.Range;
34
+ const isCheckbox = param.inputType === EInputTypes.Checkbox;
35
+ const isRadiobox = param.inputType === EInputTypes.Radiobox;
36
+
37
+ const inputType = _getInputType(param.inputType);
38
+ const input = document.createElement(isTextarea ? "textarea" : "input");
39
+ if (!isTextarea) (input as HTMLInputElement).type = inputType;
40
+ if (isCheckbox || isRadiobox) {
41
+ if ((param as ICheckbox).groupId) (input as HTMLInputElement).name = (param as ICheckbox).groupId!;
42
+ else if (isRadiobox) (input as HTMLInputElement).name = GetInputId(param as IRadiobox);
43
+
44
+ // No need to set type
45
+ } else if (isTextarea) (input as HTMLTextAreaElement).rows = (param as ITextarea).rows ?? DEFAULT_TEXTAREA_ROWS;
46
+ else if (isNumber) {
47
+ const numberParam = param as INumber;
48
+ const numberInput = input as HTMLInputElement;
49
+ const decimalPlaces = numberParam.floatingPoint ?? 0;
50
+ numberInput.type = "number";
51
+
52
+ if (numberParam.step) numberInput.step = numberParam.step.toString();
53
+ else numberInput.step = decimalPlaces > 0 ? (1 / Math.pow(10, decimalPlaces)).toString() : "1";
54
+
55
+ if (numberParam.minLength !== undefined && numberParam.minLength !== null) numberInput.min = numberParam.minLength.toString();
56
+ if (numberParam.maxLength !== undefined && numberParam.maxLength !== null) numberInput.max = numberParam.maxLength.toString();
57
+ } else if (isSlider) {
58
+ (input as HTMLInputElement).type = "range";
59
+ (input as HTMLInputElement).step = (param as ISlider).step?.toString() ?? "1";
60
+ (input as HTMLInputElement).min = (param as ISlider).minLength?.toString() ?? "0";
61
+ (input as HTMLInputElement).max = (param as ISlider).maxLength?.toString() ?? "100";
62
+ } else (input as HTMLInputElement).type = (param as ITextbox).isPassword ? "password" : "text";
63
+ if (!isSlider && !isNumber) {
64
+ if ((param as ITextbox).minLength) (input as HTMLInputElement).minLength = (param as ITextbox).minLength ?? 0;
65
+ if ((param as ITextbox).maxLength) (input as HTMLInputElement).maxLength = (param as ITextbox).maxLength ?? 0;
66
+ }
67
+
68
+ input.className = `${GetInputClasses(param.inputSize, param.defaultClass)}${isTextarea ? " sb" : ""}`;
69
+ input.autocomplete = param.autoComplete ?? "on";
70
+ input.required = !!param.isRequired;
71
+ input.readOnly = !!param.isReadonly;
72
+ input.disabled = !!param.isReadonly;
73
+ input.id = param.id;
74
+ input.placeholder = param.placeholder ?? "";
75
+ input.value = param.defaultValue?.toString() ?? "";
76
+
77
+ return input;
78
+ }
79
+ export const CreateInputs = (inputs: IInputRootParameters<any>[], onChanged: OnFormChangeHandler, index: number, isTabularForm: boolean, parentElement: HTMLElement | null, defaultValue?: any): IInputCreateResults[] => {
80
+ if (inputs.length === 0 || !onChanged) {
81
+ //TODO: WarningMessage("Invalid Inputs", "Please provide valid inputs, onChanged function and parent element.");
82
+ return [];
83
+ }
84
+ const inputValues: any = {};
85
+ const onChangedDebounced = Debounce(onChanged, 10);
86
+
87
+ const results: IInputCreateResults[] = [];
88
+ const pushResult = (result: IInputCreateResults) => results.push(result);
89
+ const handleInputChange = (currentValue: any, isInitilizing: boolean, isForcedChanged: boolean, isDisposed: boolean, currentKey: string, dataSource?: any[]) => {
90
+ if (inputValues[currentKey] === currentValue && !isForcedChanged && !isDisposed) return;
91
+
92
+ inputValues[currentKey] = currentValue;
93
+ (isInitilizing && !isTabularForm ? onChangedDebounced : onChanged)(inputValues, isInitilizing, isForcedChanged, isDisposed, currentKey, index, undefined, dataSource);
94
+ const result = results.find((x) => GetInputId(x.input) === currentKey);
95
+ if (result) {
96
+ const timeout = setTimeout(() => {
97
+ clearTimeout(timeout);
98
+ const children = results.filter((x) => GetParentKey(x.input.parentKey) === currentKey);
99
+ for (const child of children) {
100
+ child.input.defaultValue = inputValues[child.input.id];
101
+ child.onParentChanged?.(currentValue, isInitilizing, isForcedChanged, isDisposed, currentKey, index, undefined, dataSource);
102
+ }
103
+ }, 1);
104
+ }
105
+ };
106
+ const udpatedInputs = (CreateClone(inputs) as IInputRootParameters<any>[]).sort((a, b) => a.displayOrder - b.displayOrder);
107
+ for (const ipt of udpatedInputs) {
108
+ const ELEMENT_ID = GetInputId(ipt);
109
+ ipt.defaultValue = defaultValue?.[ELEMENT_ID] ?? ipt.defaultValue;
110
+ if (ipt.inputType < MAX_INPUT_TYPE) {
111
+ ipt.onChanged = (currentValue: any, isInitilizing: boolean, isForcedChanged: boolean, isDisposed: boolean, currentKey: string, dataSource?: any | any[]) => handleInputChange(currentValue, isInitilizing, isForcedChanged, isDisposed, currentKey, dataSource);
112
+ if ((ipt as any).isRequired && !IsNullOrEmpty(ipt.placeholder)) ipt.placeholder += ` ${REQUIRED_TEXT1}`;
113
+ }
114
+
115
+ switch (ipt.inputType) {
116
+ case EInputTypes.Textbox: {
117
+ pushResult(CreateTextbox(ipt as ITextbox, parentElement, isTabularForm));
118
+ break;
119
+ }
120
+ case EInputTypes.Number: {
121
+ pushResult(CreateNumber(ipt as INumber, parentElement, isTabularForm));
122
+ break;
123
+ }
124
+ case EInputTypes.Textarea: {
125
+ pushResult(CreateTextarea(ipt as ITextarea, parentElement, isTabularForm));
126
+ break;
127
+ }
128
+ case EInputTypes.Checkbox: {
129
+ pushResult(CreateCheckbox(ipt as ICheckbox, parentElement, isTabularForm));
130
+ break;
131
+ }
132
+ case EInputTypes.Radiobox: {
133
+ pushResult(CreateRadiobox(ipt as IRadiobox, parentElement, isTabularForm));
134
+ break;
135
+ }
136
+ case EInputTypes.LinkButton: {
137
+ pushResult(CreateInputLink(ipt as ILinkbutton, parentElement, isTabularForm));
138
+ break;
139
+ }
140
+ case EInputTypes.HtmlElement: {
141
+ pushResult(CreateElement(ipt as IElement, parentElement, isTabularForm));
142
+ break;
143
+ }
144
+ case EInputTypes.SubmitButton: {
145
+ pushResult(CreateInputButton(ipt as ISubmitbutton, parentElement, isTabularForm));
146
+ break;
147
+ }
148
+ /*
149
+ case EInputTypes.MultiSelection:
150
+ case EInputTypes.SingleSelection: {
151
+ pushResult(CreateSelection(ipt as ISelection, parentElement, isTabularForm));
152
+ break;
153
+ }
154
+
155
+ case EInputTypes.TabularForm: {
156
+ pushResult(CreateTabular(ipt as ITablularForm, parentElement));
157
+ break;
158
+ }
159
+
160
+ case EInputTypes.Datepicker: {
161
+ pushResult(CreateDatepicker(ipt as IDate, parentElement, isTabularForm));
162
+ break;
163
+ }
164
+ case EInputTypes.Slider:
165
+ case EInputTypes.Range: {
166
+ pushResult(CreateSlider(ipt as ISlider, parentElement, isTabularForm));
167
+ break;
168
+ }
169
+
170
+
171
+ */
172
+ default:
173
+ break;
174
+ }
175
+ }
176
+ return results;
177
+ };
178
+ export const DisposeInputs = (inputs: IInputCreateResults[] | UN, invokeOnChanged?: boolean) => {
179
+ if (!inputs || inputs.length === 0) return;
180
+ for (const input of inputs) input.dispose(invokeOnChanged);
181
+ };