@visns-studio/visns-components 5.14.8 → 5.14.9
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/README.md +59 -0
- package/package.json +1 -1
- package/src/components/DataGrid.jsx +24 -47
- package/src/components/generic/GenericDashboard.jsx +42 -56
- package/src/components/generic/GenericDetail.jsx +21 -43
- package/src/components/generic/GenericIndex.jsx +14 -36
- package/src/components/generic/StandardModal.jsx +119 -0
- package/src/index.js +2 -0
package/README.md
CHANGED
|
@@ -50,6 +50,7 @@ VISNS Components is a React-based UI component library that provides a set of re
|
|
|
50
50
|
- **Download** - File download functionality
|
|
51
51
|
- **Loader** - Loading indicators
|
|
52
52
|
- **Notification** - Toast notifications
|
|
53
|
+
- **StandardModal** - Reusable modal component with consistent styling
|
|
53
54
|
- **Call Popup** - Modal dialogs
|
|
54
55
|
- **QR Code** - QR code generation and printing
|
|
55
56
|
|
|
@@ -2312,6 +2313,64 @@ The QrCode component fetches QR code data from the specified URL and displays it
|
|
|
2312
2313
|
}
|
|
2313
2314
|
```
|
|
2314
2315
|
|
|
2316
|
+
#### StandardModal
|
|
2317
|
+
|
|
2318
|
+
A reusable modal component that standardizes the look and feel across all components. Uses the same styling as GenericIndex, GenericDetail, and DataGrid modals, providing consistent modal behavior throughout the application.
|
|
2319
|
+
|
|
2320
|
+
```jsx
|
|
2321
|
+
import { StandardModal } from '@visns-studio/visns-components';
|
|
2322
|
+
|
|
2323
|
+
// Basic usage (for custom content with header)
|
|
2324
|
+
<StandardModal
|
|
2325
|
+
isOpen={showModal}
|
|
2326
|
+
onClose={() => setShowModal(false)}
|
|
2327
|
+
title="Modal Title"
|
|
2328
|
+
showHeader={true}
|
|
2329
|
+
>
|
|
2330
|
+
<div>Your custom content here</div>
|
|
2331
|
+
</StandardModal>
|
|
2332
|
+
|
|
2333
|
+
// Usage with Form components (no header - Form provides its own)
|
|
2334
|
+
<StandardModal
|
|
2335
|
+
isOpen={modalShow}
|
|
2336
|
+
onClose={modalClose}
|
|
2337
|
+
closeOnDocumentClick={false}
|
|
2338
|
+
verticalAlign="top"
|
|
2339
|
+
>
|
|
2340
|
+
<Form
|
|
2341
|
+
closeModal={modalClose}
|
|
2342
|
+
formSettings={formData}
|
|
2343
|
+
// ... other Form props
|
|
2344
|
+
/>
|
|
2345
|
+
</StandardModal>
|
|
2346
|
+
```
|
|
2347
|
+
|
|
2348
|
+
**Props:**
|
|
2349
|
+
|
|
2350
|
+
- `isOpen` (boolean): Whether the modal is open
|
|
2351
|
+
- `onClose` (function): Function to call when modal should close
|
|
2352
|
+
- `title` (string): Modal title (only used if showHeader is true)
|
|
2353
|
+
- `children` (ReactNode): Modal content
|
|
2354
|
+
- `size` (string): Modal size - 'small' (400px), 'medium' (90vw, default), 'large' (95vw)
|
|
2355
|
+
- `closeOnDocumentClick` (boolean): Whether to close on outside click (default: true)
|
|
2356
|
+
- `closeOnEscape` (boolean): Whether to close on escape key (default: true)
|
|
2357
|
+
- `verticalAlign` (string): Vertical alignment - 'center' (default) or 'top'
|
|
2358
|
+
- `showHeader` (boolean): Whether to show modal header (default: false)
|
|
2359
|
+
- `customStyles` (object): Custom styles to override defaults
|
|
2360
|
+
|
|
2361
|
+
**Size Options:**
|
|
2362
|
+
|
|
2363
|
+
- **Small**: 400px width, ideal for simple dialogs and confirmations
|
|
2364
|
+
- **Medium** (default): 90vw width with 600px min and 1400px max, matches original modal sizing
|
|
2365
|
+
- **Large**: 95vw width with 800px min and 1600px max, for very large content
|
|
2366
|
+
|
|
2367
|
+
**Important Notes:**
|
|
2368
|
+
|
|
2369
|
+
- When wrapping Form components, use `showHeader={false}` (default) since Form components render their own headers
|
|
2370
|
+
- When creating custom modals with simple content, use `showHeader={true}` to display the title and close button
|
|
2371
|
+
- The component automatically handles responsive sizing and vertical alignment
|
|
2372
|
+
- Uses the same CSS classes as existing modals (modalwrap, modal, modal__header, modal__content)
|
|
2373
|
+
|
|
2315
2374
|
### Enhanced OCR Template Component
|
|
2316
2375
|
|
|
2317
2376
|
#### OcrTemplateEnhanced (v5.14.0+)
|
package/package.json
CHANGED
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
89
89
|
},
|
|
90
90
|
"name": "@visns-studio/visns-components",
|
|
91
|
-
"version": "5.14.
|
|
91
|
+
"version": "5.14.9",
|
|
92
92
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
93
93
|
"main": "src/index.js",
|
|
94
94
|
"files": [
|
|
@@ -30,7 +30,6 @@ import React, {
|
|
|
30
30
|
import { useNavigate } from 'react-router-dom';
|
|
31
31
|
import debounce from 'lodash.debounce';
|
|
32
32
|
import moment from 'moment';
|
|
33
|
-
import Popup from 'reactjs-popup';
|
|
34
33
|
import 'reactjs-popup/dist/index.css';
|
|
35
34
|
import parse from 'html-react-parser';
|
|
36
35
|
import { saveAs } from 'file-saver';
|
|
@@ -80,6 +79,7 @@ import CustomFetch from './Fetch';
|
|
|
80
79
|
import Download from './Download';
|
|
81
80
|
import Form from './Form';
|
|
82
81
|
import CellWithTooltip from './cells/CellWithTooltip';
|
|
82
|
+
import StandardModal from './generic/StandardModal';
|
|
83
83
|
import DataGridSearch from './controls/DataGridSearch';
|
|
84
84
|
import AutoRefreshControls from './controls/AutoRefreshControls';
|
|
85
85
|
import AudioPlayer from './controls/AudioPlayer';
|
|
@@ -3714,55 +3714,32 @@ const DataGrid = forwardRef(
|
|
|
3714
3714
|
)}
|
|
3715
3715
|
|
|
3716
3716
|
<AudioPlayer audioSource={audioSource} />
|
|
3717
|
-
<
|
|
3718
|
-
|
|
3717
|
+
<StandardModal
|
|
3718
|
+
isOpen={modalShow}
|
|
3719
3719
|
onClose={modalClose}
|
|
3720
|
+
title={formData.title || 'Edit Data'}
|
|
3720
3721
|
closeOnDocumentClick={false}
|
|
3721
|
-
|
|
3722
|
-
width: windowWidth < 768 ? '95vw' : '90vw',
|
|
3723
|
-
minWidth: windowWidth < 768 ? '95vw' : '600px',
|
|
3724
|
-
maxWidth: '1400px',
|
|
3725
|
-
...(formData.verticalAlign === 'top' && {
|
|
3726
|
-
position: 'fixed',
|
|
3727
|
-
top: '5vh',
|
|
3728
|
-
left: '50%',
|
|
3729
|
-
transform: 'translateX(-50%)',
|
|
3730
|
-
margin: '0',
|
|
3731
|
-
}),
|
|
3732
|
-
}}
|
|
3733
|
-
nested
|
|
3722
|
+
verticalAlign={formData.verticalAlign || 'center'}
|
|
3734
3723
|
>
|
|
3735
|
-
<
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
}
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
mapbox={mapbox}
|
|
3755
|
-
modalOpen={modalOpen}
|
|
3756
|
-
paramValue={paramValue}
|
|
3757
|
-
style={style}
|
|
3758
|
-
updateForm={setFormData}
|
|
3759
|
-
userProfile={userProfile}
|
|
3760
|
-
bulkEditMode={bulkEditMode}
|
|
3761
|
-
bulkEditGroupData={bulkEditGroupData}
|
|
3762
|
-
/>
|
|
3763
|
-
</div>
|
|
3764
|
-
</div>
|
|
3765
|
-
</Popup>
|
|
3724
|
+
<Form
|
|
3725
|
+
ajaxSetting={ajaxSetting}
|
|
3726
|
+
api={api}
|
|
3727
|
+
closeModal={modalClose}
|
|
3728
|
+
columnId={formId}
|
|
3729
|
+
fetchTable={handleReload}
|
|
3730
|
+
formSettings={formData}
|
|
3731
|
+
formType={formType}
|
|
3732
|
+
gridRef={gridRef}
|
|
3733
|
+
mapbox={mapbox}
|
|
3734
|
+
modalOpen={modalOpen}
|
|
3735
|
+
paramValue={paramValue}
|
|
3736
|
+
style={style}
|
|
3737
|
+
updateForm={setFormData}
|
|
3738
|
+
userProfile={userProfile}
|
|
3739
|
+
bulkEditMode={bulkEditMode}
|
|
3740
|
+
bulkEditGroupData={bulkEditGroupData}
|
|
3741
|
+
/>
|
|
3742
|
+
</StandardModal>
|
|
3766
3743
|
<GalleryModal
|
|
3767
3744
|
modalGalleryShow={modalGalleryShow}
|
|
3768
3745
|
modalGalleryClose={modalGalleryClose}
|
|
@@ -4,15 +4,15 @@ import { Link, useNavigate } from 'react-router-dom';
|
|
|
4
4
|
import { ResponsiveBar } from '@nivo/bar';
|
|
5
5
|
import { ResponsiveLine } from '@nivo/line';
|
|
6
6
|
import { ResponsivePie } from '@nivo/pie';
|
|
7
|
-
import Popup from 'reactjs-popup';
|
|
8
7
|
import dayjs from 'dayjs';
|
|
9
8
|
import parse from 'html-react-parser';
|
|
10
9
|
import { createSwapy } from 'swapy';
|
|
11
|
-
import {
|
|
10
|
+
import { Minus, Plus, Star, Trash2, Eye, RotateCcw, Clock, Database, BarChart3, AlertCircle } from 'lucide-react';
|
|
12
11
|
import { confirmDialog } from '../utils/ConfirmDialog';
|
|
13
12
|
|
|
14
13
|
import CustomFetch from '../Fetch';
|
|
15
14
|
import MultiSelect from '../MultiSelect';
|
|
15
|
+
import StandardModal from './StandardModal';
|
|
16
16
|
|
|
17
17
|
import styles from '../styles/GenericDashboard.module.scss';
|
|
18
18
|
|
|
@@ -1961,63 +1961,49 @@ function GenericDashboard({ setting, userProfile, dynamicDashboard = false }) {
|
|
|
1961
1961
|
)}
|
|
1962
1962
|
<div className={styles.grid} ref={container}>
|
|
1963
1963
|
{renderWidgets()}
|
|
1964
|
-
<
|
|
1965
|
-
|
|
1964
|
+
<StandardModal
|
|
1965
|
+
isOpen={showAddWidgetModal}
|
|
1966
1966
|
onClose={() => setShowAddWidgetModal(false)}
|
|
1967
|
-
|
|
1967
|
+
title="Add a Widget"
|
|
1968
|
+
showHeader={true}
|
|
1968
1969
|
>
|
|
1969
|
-
|
|
1970
|
-
<div
|
|
1971
|
-
<
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
>
|
|
2000
|
-
{widget.title}
|
|
2001
|
-
</option>
|
|
2002
|
-
))}
|
|
2003
|
-
</select>
|
|
2004
|
-
<button
|
|
2005
|
-
className={styles.addWidgetButton}
|
|
2006
|
-
onClick={handleAddWidget}
|
|
2007
|
-
disabled={!selectedAddWidgetId}
|
|
2008
|
-
>
|
|
2009
|
-
Add
|
|
2010
|
-
</button>
|
|
2011
|
-
</div>
|
|
2012
|
-
) : (
|
|
2013
|
-
<div className={styles.noWidgetsMessage}>
|
|
2014
|
-
No more widgets available
|
|
2015
|
-
</div>
|
|
2016
|
-
)}
|
|
2017
|
-
</div>
|
|
1970
|
+
{availableWidgets.length > 0 ? (
|
|
1971
|
+
<div>
|
|
1972
|
+
<select
|
|
1973
|
+
value={selectedAddWidgetId}
|
|
1974
|
+
onChange={(e) =>
|
|
1975
|
+
setSelectedAddWidgetId(
|
|
1976
|
+
e.target.value
|
|
1977
|
+
)
|
|
1978
|
+
}
|
|
1979
|
+
className={styles.addWidgetSelect}
|
|
1980
|
+
>
|
|
1981
|
+
<option value="">
|
|
1982
|
+
-- Select a widget --
|
|
1983
|
+
</option>
|
|
1984
|
+
{availableWidgets.map((widget) => (
|
|
1985
|
+
<option
|
|
1986
|
+
key={widget.id}
|
|
1987
|
+
value={widget.id}
|
|
1988
|
+
>
|
|
1989
|
+
{widget.title}
|
|
1990
|
+
</option>
|
|
1991
|
+
))}
|
|
1992
|
+
</select>
|
|
1993
|
+
<button
|
|
1994
|
+
className={styles.addWidgetButton}
|
|
1995
|
+
onClick={handleAddWidget}
|
|
1996
|
+
disabled={!selectedAddWidgetId}
|
|
1997
|
+
>
|
|
1998
|
+
ADD
|
|
1999
|
+
</button>
|
|
2018
2000
|
</div>
|
|
2019
|
-
|
|
2020
|
-
|
|
2001
|
+
) : (
|
|
2002
|
+
<div className={styles.noWidgetsMessage}>
|
|
2003
|
+
No more widgets available
|
|
2004
|
+
</div>
|
|
2005
|
+
)}
|
|
2006
|
+
</StandardModal>
|
|
2021
2007
|
</div>
|
|
2022
2008
|
</>
|
|
2023
2009
|
);
|
|
@@ -39,7 +39,6 @@ import { Calendar, momentLocalizer } from 'react-big-calendar';
|
|
|
39
39
|
import withDragAndDrop from 'react-big-calendar/lib/addons/dragAndDrop';
|
|
40
40
|
import moment from 'moment';
|
|
41
41
|
import parse from 'html-react-parser';
|
|
42
|
-
import Popup from 'reactjs-popup';
|
|
43
42
|
import { motion } from 'framer-motion';
|
|
44
43
|
import Dropzone from 'react-dropzone';
|
|
45
44
|
import truncate from 'truncate';
|
|
@@ -79,6 +78,7 @@ import MergeEntity from '../MergeEntity';
|
|
|
79
78
|
import MultiSelect from '../MultiSelect';
|
|
80
79
|
import QrCode from '../QrCode';
|
|
81
80
|
import StagePopupModal from './StagePopupModal';
|
|
81
|
+
import StandardModal from './StandardModal';
|
|
82
82
|
import Table from '../DataGrid';
|
|
83
83
|
import TableFilter from '../TableFilter';
|
|
84
84
|
import CategorizedDropZone from '../CategorizedDropZone';
|
|
@@ -3831,51 +3831,29 @@ function GenericDetail({
|
|
|
3831
3831
|
</div>
|
|
3832
3832
|
</div>
|
|
3833
3833
|
|
|
3834
|
-
<
|
|
3835
|
-
|
|
3834
|
+
<StandardModal
|
|
3835
|
+
isOpen={modalShow}
|
|
3836
3836
|
onClose={modalClose}
|
|
3837
|
+
title={formData.title || 'Edit Details'}
|
|
3837
3838
|
closeOnDocumentClick={false}
|
|
3838
|
-
|
|
3839
|
-
width: windowWidth < 768 ? '95vw' : '90vw',
|
|
3840
|
-
minWidth: windowWidth < 768 ? '95vw' : '600px',
|
|
3841
|
-
maxWidth: '1400px',
|
|
3842
|
-
...(formData.verticalAlign === 'top' && {
|
|
3843
|
-
position: 'fixed',
|
|
3844
|
-
top: '5vh',
|
|
3845
|
-
left: '50%',
|
|
3846
|
-
transform: 'translateX(-50%)',
|
|
3847
|
-
margin: '0',
|
|
3848
|
-
}),
|
|
3849
|
-
}}
|
|
3839
|
+
verticalAlign={formData.verticalAlign || 'center'}
|
|
3850
3840
|
>
|
|
3851
|
-
<
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
}
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
style={userProfile?.settings?.style || {}}
|
|
3868
|
-
updateForm={setFormData}
|
|
3869
|
-
userProfile={userProfile}
|
|
3870
|
-
paramValue={
|
|
3871
|
-
routeParams[urlParam]
|
|
3872
|
-
? routeParams[urlParam]
|
|
3873
|
-
: 0
|
|
3874
|
-
}
|
|
3875
|
-
/>
|
|
3876
|
-
</div>
|
|
3877
|
-
</div>
|
|
3878
|
-
</Popup>
|
|
3841
|
+
<Form
|
|
3842
|
+
closeModal={modalClose}
|
|
3843
|
+
columnId={formId}
|
|
3844
|
+
fetchTable={handleReload}
|
|
3845
|
+
formSettings={formData}
|
|
3846
|
+
formType={formType}
|
|
3847
|
+
style={userProfile?.settings?.style || {}}
|
|
3848
|
+
updateForm={setFormData}
|
|
3849
|
+
userProfile={userProfile}
|
|
3850
|
+
paramValue={
|
|
3851
|
+
routeParams[urlParam]
|
|
3852
|
+
? routeParams[urlParam]
|
|
3853
|
+
: 0
|
|
3854
|
+
}
|
|
3855
|
+
/>
|
|
3856
|
+
</StandardModal>
|
|
3879
3857
|
|
|
3880
3858
|
<StagePopupModal
|
|
3881
3859
|
show={showStagePopup}
|
|
@@ -7,7 +7,6 @@ import { saveAs } from 'file-saver';
|
|
|
7
7
|
import _ from 'lodash';
|
|
8
8
|
import parse from 'html-react-parser';
|
|
9
9
|
import moment from 'moment';
|
|
10
|
-
import Popup from 'reactjs-popup';
|
|
11
10
|
|
|
12
11
|
import CustomFetch from '../Fetch';
|
|
13
12
|
import Download from '../Download';
|
|
@@ -16,6 +15,7 @@ import Table from '../DataGrid';
|
|
|
16
15
|
import TableFilter from '../TableFilter';
|
|
17
16
|
import GenericGrid from './GenericGrid';
|
|
18
17
|
import GenericReportForm from './GenericReportForm';
|
|
18
|
+
import StandardModal from './StandardModal';
|
|
19
19
|
|
|
20
20
|
import styles from '../styles/GenericIndex.module.scss';
|
|
21
21
|
|
|
@@ -1006,44 +1006,22 @@ function GenericIndex({
|
|
|
1006
1006
|
</div>
|
|
1007
1007
|
|
|
1008
1008
|
{/* Custom Action Modal */}
|
|
1009
|
-
<
|
|
1010
|
-
|
|
1009
|
+
<StandardModal
|
|
1010
|
+
isOpen={customActionModalShow}
|
|
1011
1011
|
onClose={closeCustomActionModal}
|
|
1012
|
+
title={customActionFormData.title || 'Custom Action'}
|
|
1012
1013
|
closeOnDocumentClick={false}
|
|
1013
|
-
|
|
1014
|
-
width: windowWidth < 768 ? '95vw' : '90vw',
|
|
1015
|
-
minWidth: windowWidth < 768 ? '95vw' : '600px',
|
|
1016
|
-
maxWidth: '1400px',
|
|
1017
|
-
...(customActionFormData.verticalAlign === 'top' && {
|
|
1018
|
-
position: 'fixed',
|
|
1019
|
-
top: '5vh',
|
|
1020
|
-
left: '50%',
|
|
1021
|
-
transform: 'translateX(-50%)',
|
|
1022
|
-
margin: '0',
|
|
1023
|
-
}),
|
|
1024
|
-
}}
|
|
1014
|
+
verticalAlign={customActionFormData.verticalAlign || 'center'}
|
|
1025
1015
|
>
|
|
1026
|
-
<
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
<div className={styles.modal}>
|
|
1036
|
-
<Form
|
|
1037
|
-
closeModal={closeCustomActionModal}
|
|
1038
|
-
formSettings={customActionFormData}
|
|
1039
|
-
formType="create"
|
|
1040
|
-
fetchTable={handleCustomActionSubmit}
|
|
1041
|
-
style={userProfile?.settings?.style || {}}
|
|
1042
|
-
userProfile={userProfile}
|
|
1043
|
-
/>
|
|
1044
|
-
</div>
|
|
1045
|
-
</div>
|
|
1046
|
-
</Popup>
|
|
1016
|
+
<Form
|
|
1017
|
+
closeModal={closeCustomActionModal}
|
|
1018
|
+
formSettings={customActionFormData}
|
|
1019
|
+
formType="create"
|
|
1020
|
+
fetchTable={handleCustomActionSubmit}
|
|
1021
|
+
style={userProfile?.settings?.style || {}}
|
|
1022
|
+
userProfile={userProfile}
|
|
1023
|
+
/>
|
|
1024
|
+
</StandardModal>
|
|
1047
1025
|
</>
|
|
1048
1026
|
);
|
|
1049
1027
|
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Popup from 'reactjs-popup';
|
|
3
|
+
import { X } from 'lucide-react';
|
|
4
|
+
import formStyles from '../styles/Form.module.scss';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* StandardModal Component
|
|
8
|
+
*
|
|
9
|
+
* A reusable modal component that standardizes the look and feel across all components.
|
|
10
|
+
* Uses the same styles as GenericIndex, GenericDetail, and DataGrid via Form.module.scss.
|
|
11
|
+
*
|
|
12
|
+
* @param {Object} props
|
|
13
|
+
* @param {boolean} props.isOpen - Whether the modal is open
|
|
14
|
+
* @param {Function} props.onClose - Function to call when modal should close
|
|
15
|
+
* @param {string} props.title - Modal title (only used if showHeader is true)
|
|
16
|
+
* @param {React.ReactNode} props.children - Modal content
|
|
17
|
+
* @param {string} props.size - Modal size: 'small', 'medium', 'large' (default: 'medium')
|
|
18
|
+
* @param {boolean} props.closeOnDocumentClick - Whether to close on outside click (default: true)
|
|
19
|
+
* @param {boolean} props.closeOnEscape - Whether to close on escape key (default: true)
|
|
20
|
+
* @param {string} props.verticalAlign - Vertical alignment: 'center', 'top' (default: 'center')
|
|
21
|
+
* @param {boolean} props.showHeader - Whether to show modal header (default: false, because Form components have their own headers)
|
|
22
|
+
* @param {Object} props.customStyles - Custom styles to override defaults
|
|
23
|
+
*/
|
|
24
|
+
const StandardModal = ({
|
|
25
|
+
isOpen,
|
|
26
|
+
onClose,
|
|
27
|
+
title,
|
|
28
|
+
children,
|
|
29
|
+
size = 'medium',
|
|
30
|
+
closeOnDocumentClick = true,
|
|
31
|
+
closeOnEscape = true,
|
|
32
|
+
verticalAlign = 'center',
|
|
33
|
+
showHeader = false,
|
|
34
|
+
customStyles = {}
|
|
35
|
+
}) => {
|
|
36
|
+
// Apply vertical alignment class if needed
|
|
37
|
+
const getModalClasses = () => {
|
|
38
|
+
let classes = formStyles.modal;
|
|
39
|
+
if (verticalAlign === 'top') {
|
|
40
|
+
classes += ' modal-top-aligned';
|
|
41
|
+
}
|
|
42
|
+
return classes;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Get size-based styles for the popup content
|
|
46
|
+
const getContentStyle = () => {
|
|
47
|
+
const baseStyle = {
|
|
48
|
+
...customStyles.modal
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Apply size-based styles
|
|
52
|
+
switch (size) {
|
|
53
|
+
case 'small':
|
|
54
|
+
return {
|
|
55
|
+
...baseStyle,
|
|
56
|
+
width: '400px',
|
|
57
|
+
minWidth: '400px',
|
|
58
|
+
maxWidth: '600px'
|
|
59
|
+
};
|
|
60
|
+
case 'large':
|
|
61
|
+
return {
|
|
62
|
+
...baseStyle,
|
|
63
|
+
width: '95vw',
|
|
64
|
+
minWidth: '800px',
|
|
65
|
+
maxWidth: '1600px'
|
|
66
|
+
};
|
|
67
|
+
case 'medium':
|
|
68
|
+
default:
|
|
69
|
+
// Use the same dimensions as modalWide class (original modal sizing)
|
|
70
|
+
return {
|
|
71
|
+
...baseStyle,
|
|
72
|
+
width: '90vw',
|
|
73
|
+
minWidth: '600px',
|
|
74
|
+
maxWidth: '1400px'
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<Popup
|
|
81
|
+
open={isOpen}
|
|
82
|
+
onClose={onClose}
|
|
83
|
+
modal
|
|
84
|
+
closeOnDocumentClick={closeOnDocumentClick}
|
|
85
|
+
closeOnEscape={closeOnEscape}
|
|
86
|
+
contentStyle={getContentStyle()}
|
|
87
|
+
>
|
|
88
|
+
<div className={formStyles.modalwrap}>
|
|
89
|
+
<div
|
|
90
|
+
className={getModalClasses()}
|
|
91
|
+
>
|
|
92
|
+
{showHeader && (
|
|
93
|
+
<div className={formStyles.modal__header} style={customStyles.header}>
|
|
94
|
+
<h1 style={customStyles.title}>{title}</h1>
|
|
95
|
+
<button
|
|
96
|
+
className={formStyles.modal__close}
|
|
97
|
+
onClick={onClose}
|
|
98
|
+
style={customStyles.closeButton}
|
|
99
|
+
type="button"
|
|
100
|
+
aria-label="Close modal"
|
|
101
|
+
>
|
|
102
|
+
<X strokeWidth={1} size={24} />
|
|
103
|
+
</button>
|
|
104
|
+
</div>
|
|
105
|
+
)}
|
|
106
|
+
{showHeader ? (
|
|
107
|
+
<div className={formStyles.modal__content} style={customStyles.content}>
|
|
108
|
+
{children}
|
|
109
|
+
</div>
|
|
110
|
+
) : (
|
|
111
|
+
children
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
</Popup>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export default StandardModal;
|
package/src/index.js
CHANGED
|
@@ -72,6 +72,7 @@ import GenericSort from './components/generic/GenericSort';
|
|
|
72
72
|
import NotificationList from './components/generic/NotificationList';
|
|
73
73
|
import OcrTemplateEnhanced from './components/generic/OcrTemplateEnhanced';
|
|
74
74
|
import StagePopupModal from './components/generic/StagePopupModal';
|
|
75
|
+
import StandardModal from './components/generic/StandardModal';
|
|
75
76
|
|
|
76
77
|
/** Proposal Components */
|
|
77
78
|
import ProposalTemplateSectionManager from './components/proposal/ProposalTemplateSectionManager';
|
|
@@ -145,6 +146,7 @@ export {
|
|
|
145
146
|
showConfirmDialog,
|
|
146
147
|
SortableList,
|
|
147
148
|
StagePopupModal,
|
|
149
|
+
StandardModal,
|
|
148
150
|
Table,
|
|
149
151
|
TableFilter,
|
|
150
152
|
VariableInserter,
|