goobs-frontend 0.7.58 → 0.7.60

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": "goobs-frontend",
3
- "version": "0.7.58",
3
+ "version": "0.7.60",
4
4
  "description": "A comprehensive React-based UI library built on Material-UI, offering a wide range of customizable components including grids, typography, buttons, cards, forms, navigation, pricing tables, steppers, tooltips, accordions, and more. Designed for building responsive and consistent user interfaces with advanced features like form validation, theming, and code syntax highlighting.",
5
5
  "license": "MIT",
6
6
  "main": "./src/index.ts",
@@ -25,29 +25,29 @@
25
25
  "@emotion/cache": "^11.11.0",
26
26
  "@emotion/react": "^11.11.4",
27
27
  "@emotion/styled": "^11.11.5",
28
- "@mui/icons-material": "^5.15.20",
29
- "@mui/material": "^5.15.20",
30
- "@types/lodash": "^4.17.5",
31
- "goobs-cache": "^1.2.1",
32
- "highlight.js": "^11.9.0",
28
+ "@mui/icons-material": "^5.16.0",
29
+ "@mui/material": "^5.16.0",
30
+ "@types/lodash": "^4.17.6",
31
+ "goobs-cache": "^1.3.2",
32
+ "highlight.js": "^11.10.0",
33
33
  "lodash": "^4.17.21",
34
- "next": "14.2.4"
34
+ "next": "14.2.5"
35
35
  },
36
36
  "devDependencies": {
37
- "@next/eslint-plugin-next": "^14.2.4",
38
- "@types/node": "^20.14.9",
39
- "@types/react": "18.3.0",
37
+ "@next/eslint-plugin-next": "^14.2.5",
38
+ "@types/node": "^20.14.10",
39
+ "@types/react": "18.3.3",
40
40
  "@types/react-dom": "^18.3.0",
41
- "@typescript-eslint/eslint-plugin": "^7.15.0",
42
- "@typescript-eslint/parser": "^7.15.0",
41
+ "@typescript-eslint/eslint-plugin": "^7.16.0",
42
+ "@typescript-eslint/parser": "^7.16.0",
43
43
  "eslint": "^8.57.0",
44
- "eslint-config-next": "^14.2.4",
44
+ "eslint-config-next": "^14.2.5",
45
45
  "eslint-config-prettier": "^9.1.0",
46
46
  "eslint-plugin-prettier": "^5.1.3",
47
47
  "prettier": "^3.3.2",
48
48
  "react": "^18.3.1",
49
49
  "react-dom": "^18.3.1",
50
- "typescript": "^5.5.2"
50
+ "typescript": "^5.5.3"
51
51
  },
52
52
  "files": [
53
53
  "src"
@@ -7,46 +7,24 @@ import Typography from '../Typography'
7
7
  import { get, JSONValue } from 'goobs-cache'
8
8
  import { red } from '../../styles/palette'
9
9
 
10
- /**
11
- * Defines the possible alignment options for button content.
12
- */
13
10
  export type ButtonAlignment = 'left' | 'center' | 'right'
14
11
 
15
- /**
16
- * Defines the structure of helper footer messages used for form validation.
17
- */
18
12
  export interface HelperFooterMessage {
19
- /** Indicates whether the message represents an error or success state */
20
13
  status: 'error' | 'success'
21
- /** The message to display in the status area */
22
14
  statusMessage: string
23
- /** The message to spread across multiple components */
24
15
  spreadMessage: string
25
- /** Priority of the spread message for determining which message to show */
26
16
  spreadMessagePriority: number
27
- /** The name of the form this message is associated with */
28
17
  formname: string
29
- /** Indicates if the field associated with this message is required */
30
18
  required: boolean
31
19
  }
32
20
 
33
- /**
34
- * Props for the CustomButton component.
35
- * Extends ButtonProps from Material-UI, omitting 'color' and 'variant'.
36
- */
37
21
  export interface CustomButtonProps
38
22
  extends Omit<ButtonProps, 'color' | 'variant'> {
39
- /** Text to display on the button */
40
23
  text?: string
41
- /** Background color of the button */
42
24
  backgroundcolor?: string
43
- /** Color of the button's outline */
44
25
  outlinecolor?: string
45
- /** Color of the button's text */
46
26
  fontcolor?: string
47
- /** Alignment of the button's text */
48
27
  fontlocation?: ButtonAlignment
49
- /** Typography variant for the button's text */
50
28
  fontvariant?:
51
29
  | 'arapeyh1'
52
30
  | 'arapeyh2'
@@ -75,32 +53,18 @@ export interface CustomButtonProps
75
53
  | 'merriparagraph'
76
54
  | 'merrihelperheader'
77
55
  | 'merrihelperfooter'
78
- /** Icon to display on the button. Set to false to hide the icon */
79
56
  icon?: React.ReactNode | false
80
- /** Color of the icon */
81
57
  iconcolor?: string
82
- /** Size of the icon */
83
58
  iconsize?: string
84
- /** Position of the icon relative to the text */
85
59
  iconlocation?: 'left' | 'top' | 'right'
86
- /** Style variant of the button */
87
60
  variant?: 'text' | 'outlined' | 'contained'
88
- /** Function to call when the button is clicked */
89
61
  onClick?: () => void
90
- /** Helper footer message for form validation */
91
62
  helperfooter?: HelperFooterMessage
92
- /** Width of the button */
93
63
  width?: string
94
- /** Name of the form this button is associated with */
95
64
  formname?: string
96
- /** Name attribute for the button element */
97
65
  name?: string
98
66
  }
99
67
 
100
- /**
101
- * CustomButton component renders a customizable button with integrated form validation.
102
- * It displays error messages based on helper footers and form validation status.
103
- */
104
68
  const CustomButton: React.FC<CustomButtonProps> = props => {
105
69
  const {
106
70
  text,
@@ -121,24 +85,16 @@ const CustomButton: React.FC<CustomButtonProps> = props => {
121
85
  width,
122
86
  } = props
123
87
 
124
- /** State for storing the current error message */
125
88
  const [errorMessage, setErrorMessage] = useState<string | undefined>(
126
89
  undefined
127
90
  )
128
- /** State for tracking whether the associated form is valid */
129
91
  const [isFormValid, setIsFormValid] = useState<boolean>(true)
130
- /** State for storing helper footer messages */
131
92
  const [helperFooterValue, setHelperFooterValue] = useState<
132
93
  Record<string, HelperFooterMessage>
133
94
  >({})
134
95
 
135
- /**
136
- * Updates the form validation status and error message based on helper footers.
137
- * This function filters relevant footers, checks for errors and empty required fields,
138
- * and updates the error message and form validity accordingly.
139
- */
140
96
  const updateFormValidation = useCallback(() => {
141
- if (formname) {
97
+ if (formname && helperFooterValue) {
142
98
  const relevantFooters = Object.values(helperFooterValue).filter(
143
99
  footer => footer?.formname === formname
144
100
  )
@@ -168,13 +124,12 @@ const CustomButton: React.FC<CustomButtonProps> = props => {
168
124
  setErrorMessage(undefined)
169
125
  setIsFormValid(true)
170
126
  }
127
+ } else {
128
+ setErrorMessage(undefined)
129
+ setIsFormValid(true)
171
130
  }
172
131
  }, [formname, helperFooterValue])
173
132
 
174
- /**
175
- * Fetches helper footer data from the cache when formname changes.
176
- * This effect runs whenever the formname prop changes.
177
- */
178
133
  useEffect(() => {
179
134
  const fetchHelperFooter = async () => {
180
135
  const helperFooterResult = await get('helperFooter', 'client')
@@ -189,28 +144,18 @@ const CustomButton: React.FC<CustomButtonProps> = props => {
189
144
  HelperFooterMessage
190
145
  >
191
146
  )
147
+ } else {
148
+ setHelperFooterValue({})
192
149
  }
193
150
  }
194
151
 
195
152
  fetchHelperFooter()
196
153
  }, [formname])
197
154
 
198
- /**
199
- * Triggers form validation whenever helperFooterValue changes.
200
- * This effect ensures that the form validation is updated whenever
201
- * the helper footer messages change.
202
- */
203
155
  useEffect(() => {
204
156
  updateFormValidation()
205
157
  }, [updateFormValidation])
206
158
 
207
- /**
208
- * Renders the icon element based on the provided icon prop.
209
- * If the icon prop is false, it returns null.
210
- * If the icon is a valid React element, it clones it with the specified size.
211
- * Otherwise, it renders a default StarIcon.
212
- * @returns The rendered icon element or null.
213
- */
214
159
  const renderIcon = () => {
215
160
  if (icon === false) {
216
161
  return null
@@ -223,10 +168,6 @@ const CustomButton: React.FC<CustomButtonProps> = props => {
223
168
  return <StarIcon style={{ fontSize: iconsize }} />
224
169
  }
225
170
 
226
- /**
227
- * Handles the button click event.
228
- * If the form is valid and an onClick handler is provided, it calls the handler.
229
- */
230
171
  const handleButtonClick = async () => {
231
172
  if (!isFormValid) {
232
173
  return
@@ -82,7 +82,7 @@ const ConfirmationCodeInputs: React.FC<ConfirmationCodeInputsProps> = ({
82
82
  'verificationCode',
83
83
  combinedCode,
84
84
  new Date(Date.now() + 3600000),
85
- 'memory'
85
+ 'client'
86
86
  )
87
87
  }
88
88
  }, [combinedCode, isValid])
@@ -55,11 +55,12 @@ const useLink = (grid: {
55
55
  ...cellconfig,
56
56
  },
57
57
  component: (
58
- <Link key={`link-${index}`} href={link} passHref {...restProps}>
58
+ <Link key={`link-${index}`} href={link} passHref>
59
59
  <Typography
60
60
  text={text}
61
61
  fontvariant={fontvariant}
62
62
  fontcolor={fontcolor}
63
+ {...restProps}
63
64
  />
64
65
  </Link>
65
66
  ),
@@ -1,172 +1,187 @@
1
- // File: src/components/PopupForm/index.tsx
2
1
  'use client'
3
- import React, { forwardRef, useImperativeHandle, useRef } from 'react'
2
+
3
+ import React, {
4
+ forwardRef,
5
+ useImperativeHandle,
6
+ useRef,
7
+ useMemo,
8
+ useCallback,
9
+ } from 'react'
4
10
  import { Close } from '@mui/icons-material'
5
11
  import { Dialog, IconButton, Box } from '@mui/material'
6
12
  import ContentSection, { ContentSectionProps } from '../../Content'
7
13
  import { formContainerStyle } from './../../../styles/Form'
8
14
  import { ExtendedTypographyProps } from '../../Content/Structure/typography/useGridTypography'
9
- import Typography from '../../Typography'
10
15
 
11
16
  /**
12
17
  * Props for the PopupForm component.
18
+ * @interface PopupFormProps
13
19
  */
14
20
  export interface PopupFormProps {
15
- /** Title of the popup form */
21
+ /** The title of the popup form */
16
22
  title?: string
17
- /** Description of the popup form */
23
+ /** The description of the popup form */
18
24
  description?: string
19
- /** Boolean to control the open state of the dialog */
20
- open: boolean
21
- /** Function to call when closing the dialog */
22
- onClose: () => void
23
- /** ContentSectionProps to render the form content */
25
+ /** The grid configuration for the form content */
24
26
  grids?: ContentSectionProps['grids']
25
- /** Optional function to handle form submission */
26
- onSubmit?: () => void
27
- /** Optional direct content to render */
27
+ /** Callback function to handle form submission */
28
+ onSubmit?: (event: React.FormEvent<HTMLFormElement>) => void
29
+ /** Custom content to render inside the form */
28
30
  content?: React.ReactNode
31
+ /** The type of popup to render ('dialog' or 'modal') */
32
+ popupType: 'dialog' | 'modal'
33
+ /** Whether the popup is open (only applicable for 'dialog' type) */
34
+ open?: boolean
35
+ /** Callback function to handle closing the popup (only applicable for 'dialog' type) */
36
+ onClose?: () => void
29
37
  }
30
38
 
31
39
  /**
32
- * PopupForm component renders a popup form with a title, description, and content sections.
33
- * It uses the ContentSection component to render the form content within a Material-UI Dialog.
34
- * Handles form submission and displays submitted data internally.
40
+ * PopupForm Component
41
+ *
42
+ * A flexible popup form component that can be rendered as either a dialog or a modal.
43
+ * It supports custom content, grids, and header configuration.
35
44
  *
36
- * @param props The props for the PopupForm component.
37
- * @param ref Ref forwarded to the form element.
38
- * @returns The rendered popup form.
45
+ * @component
46
+ * @example
47
+ * <PopupForm
48
+ * title="Login"
49
+ * description="Please enter your credentials"
50
+ * popupType="dialog"
51
+ * open={isOpen}
52
+ * onClose={handleClose}
53
+ * onSubmit={handleSubmit}
54
+ * content={<LoginForm />}
55
+ * />
39
56
  */
40
57
  const PopupForm = forwardRef<HTMLFormElement, PopupFormProps>(
41
- ({ title, description, open, onClose, grids, onSubmit, content }, ref) => {
42
- const [submittedData, setSubmittedData] = React.useState<
43
- Record<string, string>
44
- >({})
58
+ (
59
+ { title, description, grids, onSubmit, content, popupType, open, onClose },
60
+ ref
61
+ ) => {
45
62
  const internalFormRef = useRef<HTMLFormElement>(null)
46
63
 
64
+ // Expose the internal form ref to the parent component
47
65
  useImperativeHandle(ref, () => internalFormRef.current as HTMLFormElement)
48
66
 
49
67
  /**
50
- * headerGrid contains the grid configuration for the form header.
51
- * It includes the title and description as typography items.
68
+ * Memoized header grid configuration
52
69
  */
53
- const headerGrid: ContentSectionProps['grids'][0] = {
54
- grid: {
55
- gridconfig: {
56
- gridname: 'formHeader',
57
- marginbottom: 1,
58
- gridwidth: '100%',
59
- },
60
- },
61
- typography: [
62
- {
63
- text: title,
64
- fontvariant: 'merrih5',
65
- fontcolor: 'black',
66
- columnconfig: {
67
- column: 1,
70
+ const headerGrid: ContentSectionProps['grids'][0] = useMemo(
71
+ () => ({
72
+ grid: {
73
+ gridconfig: {
68
74
  gridname: 'formHeader',
69
- columnwidth: '100%',
70
- alignment: 'left',
71
- marginbottom: 0.5,
75
+ marginbottom: 1,
76
+ gridwidth: '100%',
72
77
  },
73
78
  },
74
- {
75
- text: description,
76
- fontvariant: 'merriparagraph',
77
- fontcolor: 'black',
78
- columnconfig: {
79
- column: 1,
80
- gridname: 'formHeader',
81
- columnwidth: '100%',
79
+ typography: [
80
+ {
81
+ text: title,
82
+ fontvariant: 'merrih5',
83
+ fontcolor: 'black',
84
+ columnconfig: {
85
+ row: 1,
86
+ column: 1,
87
+ gridname: 'formHeader',
88
+ columnwidth: '100%',
89
+ alignment: 'left',
90
+ marginbottom: 1.5,
91
+ },
82
92
  },
83
- },
84
- ] as ExtendedTypographyProps[],
85
- }
86
-
87
- /** Combine the header grid with the provided content grids */
88
- const contentSectionGrids: ContentSectionProps['grids'] = [
89
- headerGrid,
90
- ...(grids || []),
91
- ]
93
+ {
94
+ text: description,
95
+ fontvariant: 'merriparagraph',
96
+ fontcolor: 'black',
97
+ columnconfig: {
98
+ row: 2,
99
+ column: 1,
100
+ alignment: 'left',
101
+ gridname: 'formHeader',
102
+ columnwidth: '100%',
103
+ },
104
+ },
105
+ ] as ExtendedTypographyProps[],
106
+ }),
107
+ [title, description]
108
+ )
92
109
 
93
110
  /**
94
- * Handles form submission and processes form data internally.
95
- * @param event - The form submission event
111
+ * Handle form submission
112
+ * @param {React.FormEvent<HTMLFormElement>} event - The form submission event
96
113
  */
97
- const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
98
- event.preventDefault()
99
- const formData = new FormData(event.currentTarget)
100
- const data: Record<string, string> = {}
101
-
102
- formData.forEach((value, key) => {
103
- data[key] = value.toString()
104
- })
105
-
106
- setSubmittedData(data)
107
-
108
- if (onSubmit) {
109
- onSubmit()
110
- }
111
- }
114
+ const handleSubmit = useCallback(
115
+ (event: React.FormEvent<HTMLFormElement>) => {
116
+ event.preventDefault()
117
+ if (onSubmit) {
118
+ onSubmit(event)
119
+ }
120
+ },
121
+ [onSubmit]
122
+ )
112
123
 
113
124
  /**
114
- * Renders the header typography elements
125
+ * Memoized header render function
115
126
  */
116
- const renderHeaderTypography = () => {
117
- if (Array.isArray(headerGrid.typography)) {
118
- return headerGrid.typography.map(
119
- (typo: ExtendedTypographyProps, index: number) => (
120
- <Typography key={index} {...typo} />
121
- )
122
- )
123
- } else if (headerGrid.typography) {
124
- return <Typography {...headerGrid.typography} />
125
- }
126
- return null
127
- }
127
+ const renderHeader = useMemo(
128
+ () => <ContentSection grids={[headerGrid]} />,
129
+ [headerGrid]
130
+ )
128
131
 
129
- return (
130
- <Dialog open={open} onClose={onClose} fullWidth maxWidth="sm">
131
- <IconButton
132
- size="small"
133
- onClick={onClose}
134
- sx={{
135
- position: 'absolute',
136
- right: 8,
137
- top: 8,
138
- color: theme => theme.palette.grey[500],
139
- }}
140
- >
141
- <Close />
142
- </IconButton>
132
+ /**
133
+ * Memoized content render function
134
+ */
135
+ const renderContent = useMemo(
136
+ () => (
143
137
  <Box
144
138
  // @ts-ignore
145
139
  sx={formContainerStyle}
146
140
  >
141
+ <Box mb={0}>{renderHeader}</Box>
147
142
  <form onSubmit={handleSubmit} ref={internalFormRef}>
148
- {content ? (
149
- <>
150
- {renderHeaderTypography()}
151
- {content}
152
- </>
153
- ) : (
154
- <ContentSection grids={contentSectionGrids} />
155
- )}
143
+ {React.isValidElement(content)
144
+ ? React.cloneElement(content as React.ReactElement, {
145
+ onSubmit: handleSubmit,
146
+ })
147
+ : content || (grids && <ContentSection grids={grids} />)}
156
148
  </form>
157
- {Object.keys(submittedData).length > 0 && (
158
- <Box mt={2}>
159
- <Typography fontvariant="merrih6" text="Submitted Data:" />
160
- {Object.entries(submittedData).map(([key, value]) => (
161
- <Typography
162
- key={key}
163
- fontvariant="merriparagraph"
164
- text={`${key}: ${value}`}
165
- />
166
- ))}
167
- </Box>
168
- )}
169
149
  </Box>
150
+ ),
151
+ [renderHeader, handleSubmit, content, grids]
152
+ )
153
+
154
+ if (popupType === 'modal') {
155
+ return (
156
+ <Dialog
157
+ open={true}
158
+ fullWidth
159
+ maxWidth="sm"
160
+ disableEscapeKeyDown
161
+ hideBackdrop
162
+ >
163
+ {renderContent}
164
+ </Dialog>
165
+ )
166
+ }
167
+
168
+ return (
169
+ <Dialog open={open || false} onClose={onClose} fullWidth maxWidth="sm">
170
+ {onClose && (
171
+ <IconButton
172
+ size="small"
173
+ onClick={onClose}
174
+ sx={{
175
+ position: 'absolute',
176
+ right: 8,
177
+ top: 8,
178
+ color: theme => theme.palette.grey[500],
179
+ }}
180
+ >
181
+ <Close />
182
+ </IconButton>
183
+ )}
184
+ {renderContent}
170
185
  </Dialog>
171
186
  )
172
187
  }