@onehat/ui 0.3.35 → 0.3.37
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/Components/Form/Field/Combo/Combo.js +8 -3
- package/src/Components/Form/Form.js +196 -173
- package/src/Components/Grid/Grid.js +40 -21
- package/src/Components/Hoc/withData.js +25 -4
- package/src/Components/Hoc/withEditor.js +5 -1
- package/src/Components/Hoc/withPdfButton.js +1 -0
- package/src/Components/Hoc/withSelection.js +1 -1
- package/src/Components/Hoc/withSideEditor.js +4 -0
- package/src/Components/Hoc/withWindowedEditor.js +4 -0
- package/src/Components/Icons/Excel.js +14 -0
- package/src/Components/Panel/Panel.js +1 -1
- package/src/Components/Report/Report.js +124 -0
- package/src/Components/Viewer/Viewer.js +88 -83
- package/src/Functions/buildAdditionalButtons.js +43 -0
package/package.json
CHANGED
|
@@ -38,6 +38,7 @@ export function ComboComponent(props) {
|
|
|
38
38
|
hideMenuOnSelection = true,
|
|
39
39
|
_input = {},
|
|
40
40
|
isEditor = false,
|
|
41
|
+
isDisabled = false,
|
|
41
42
|
|
|
42
43
|
// withValue
|
|
43
44
|
value,
|
|
@@ -258,6 +259,10 @@ export function ComboComponent(props) {
|
|
|
258
259
|
},
|
|
259
260
|
searchForMatches = async (value) => {
|
|
260
261
|
|
|
262
|
+
if (_.isEmpty(value)) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
261
266
|
let found;
|
|
262
267
|
if (Repository) {
|
|
263
268
|
|
|
@@ -275,9 +280,7 @@ export function ComboComponent(props) {
|
|
|
275
280
|
searchField = displayFieldName + ' LIKE';
|
|
276
281
|
}
|
|
277
282
|
|
|
278
|
-
|
|
279
|
-
value += '%';
|
|
280
|
-
}
|
|
283
|
+
value += '%';
|
|
281
284
|
|
|
282
285
|
await Repository.filter(searchField, value);
|
|
283
286
|
if (!this.isAutoLoad) {
|
|
@@ -408,6 +411,7 @@ export function ComboComponent(props) {
|
|
|
408
411
|
ref={inputRef}
|
|
409
412
|
value={textValue}
|
|
410
413
|
autoSubmit={true}
|
|
414
|
+
isDisabled={isDisabled}
|
|
411
415
|
onChangeValue={onInputChangeText}
|
|
412
416
|
onKeyPress={onInputKeyPress}
|
|
413
417
|
onBlur={onInputBlur}
|
|
@@ -453,6 +457,7 @@ export function ComboComponent(props) {
|
|
|
453
457
|
color: 'primary.800',
|
|
454
458
|
size: 'sm',
|
|
455
459
|
}}
|
|
460
|
+
isDisabled={isDisabled}
|
|
456
461
|
onPress={onTriggerPress}
|
|
457
462
|
onBlur={onTriggerBlur}
|
|
458
463
|
h="100%"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useEffect, useState, useRef, isValidElement, } from 'react';
|
|
2
|
+
import { View, } from 'react-native';
|
|
2
3
|
import {
|
|
3
4
|
Box,
|
|
4
5
|
Column,
|
|
@@ -28,6 +29,7 @@ import withEditor from '../Hoc/withEditor.js';
|
|
|
28
29
|
import withPdfButton from '../Hoc/withPdfButton.js';
|
|
29
30
|
import inArray from '../../Functions/inArray.js';
|
|
30
31
|
import getComponentFromType from '../../Functions/getComponentFromType.js';
|
|
32
|
+
import buildAdditionalButtons from '../../Functions/buildAdditionalButtons.js';
|
|
31
33
|
import Button from '../Buttons/Button.js';
|
|
32
34
|
import IconButton from '../Buttons/IconButton.js';
|
|
33
35
|
import AngleLeft from '../Icons/AngleLeft.js';
|
|
@@ -38,6 +40,8 @@ import Footer from '../Layout/Footer.js';
|
|
|
38
40
|
import Label from '../Form/Label.js';
|
|
39
41
|
import _ from 'lodash';
|
|
40
42
|
|
|
43
|
+
const CONTAINER_THRESHOLD = 900;
|
|
44
|
+
|
|
41
45
|
// TODO: memoize field Components
|
|
42
46
|
|
|
43
47
|
// Modes:
|
|
@@ -73,6 +77,7 @@ function Form(props) {
|
|
|
73
77
|
submitBtnLabel,
|
|
74
78
|
onSubmit,
|
|
75
79
|
additionalEditButtons,
|
|
80
|
+
additionalFooterButtons,
|
|
76
81
|
|
|
77
82
|
// sizing of outer container
|
|
78
83
|
h,
|
|
@@ -113,6 +118,7 @@ function Form(props) {
|
|
|
113
118
|
isSingle = !isMultiple, // for convenience
|
|
114
119
|
forceUpdate = useForceUpdate(),
|
|
115
120
|
[previousRecord, setPreviousRecord] = useState(record),
|
|
121
|
+
[containerWidth, setContainerWidth] = useState(),
|
|
116
122
|
initialValues = _.merge(startingValues, (record && !record.isDestroyed ? record.submitValues : {})),
|
|
117
123
|
defaultValues = isMultiple ? getNullFieldValues(initialValues, Repository) : initialValues, // when multiple entities, set all default values to null
|
|
118
124
|
{
|
|
@@ -267,9 +273,9 @@ function Form(props) {
|
|
|
267
273
|
return <Row>{elements}</Row>;
|
|
268
274
|
},
|
|
269
275
|
buildFromItems = () => {
|
|
270
|
-
return _.map(items, (item, ix) =>
|
|
276
|
+
return _.map(items, (item, ix) => buildFromItem(item, ix, columnDefaults));
|
|
271
277
|
},
|
|
272
|
-
|
|
278
|
+
buildFromItem = (item, ix, defaults) => {
|
|
273
279
|
let {
|
|
274
280
|
type,
|
|
275
281
|
title,
|
|
@@ -307,8 +313,12 @@ function Form(props) {
|
|
|
307
313
|
type = 'Text';
|
|
308
314
|
}
|
|
309
315
|
}
|
|
310
|
-
if (
|
|
311
|
-
editorTypeProps.autoLoad =
|
|
316
|
+
if (item.hasOwnProperty('autoLoad')) {
|
|
317
|
+
editorTypeProps.autoLoad = item.autoLoad;
|
|
318
|
+
} else {
|
|
319
|
+
if (type?.match && type.match(/Combo$/) && Repository?.isRemote && !Repository?.isLoaded) {
|
|
320
|
+
editorTypeProps.autoLoad = true;
|
|
321
|
+
}
|
|
312
322
|
}
|
|
313
323
|
const Element = getComponentFromType(type);
|
|
314
324
|
let children;
|
|
@@ -317,9 +327,26 @@ function Form(props) {
|
|
|
317
327
|
if (_.isEmpty(items)) {
|
|
318
328
|
return null;
|
|
319
329
|
}
|
|
330
|
+
if (type === 'Column') {
|
|
331
|
+
if (containerWidth < CONTAINER_THRESHOLD) {
|
|
332
|
+
// everything is in one column
|
|
333
|
+
if (propsToPass.hasOwnProperty('flex')) {
|
|
334
|
+
delete propsToPass.flex;
|
|
335
|
+
}
|
|
336
|
+
if (propsToPass.hasOwnProperty('width')) {
|
|
337
|
+
delete propsToPass.width;
|
|
338
|
+
}
|
|
339
|
+
if (propsToPass.hasOwnProperty('w')) {
|
|
340
|
+
delete propsToPass.w;
|
|
341
|
+
}
|
|
342
|
+
propsToPass.w = '100%';
|
|
343
|
+
propsToPass.mb = 1;
|
|
344
|
+
}
|
|
345
|
+
propsToPass.pl = 3;
|
|
346
|
+
}
|
|
320
347
|
const itemDefaults = item.defaults;
|
|
321
348
|
children = _.map(items, (item, ix) => {
|
|
322
|
-
return
|
|
349
|
+
return buildFromItem(item, ix, itemDefaults);
|
|
323
350
|
});
|
|
324
351
|
return <Element key={ix} title={title} {...itemDefaults} {...propsToPass} {...editorTypeProps}>{children}</Element>;
|
|
325
352
|
}
|
|
@@ -428,6 +455,14 @@ function Form(props) {
|
|
|
428
455
|
|
|
429
456
|
}
|
|
430
457
|
}
|
|
458
|
+
|
|
459
|
+
if (item.additionalEditButtons) {
|
|
460
|
+
element = <Row flex={1} flexWrap="wrap">
|
|
461
|
+
{element}
|
|
462
|
+
{buildAdditionalButtons(item.additionalEditButtons, self, { fieldState, formSetValue, formGetValues, formState })}
|
|
463
|
+
</Row>;
|
|
464
|
+
}
|
|
465
|
+
|
|
431
466
|
if (label && editorType !== EDITOR_TYPE__INLINE) {
|
|
432
467
|
const labelProps = {};
|
|
433
468
|
if (defaults?.labelWidth) {
|
|
@@ -481,43 +516,6 @@ function Form(props) {
|
|
|
481
516
|
}
|
|
482
517
|
return components;
|
|
483
518
|
},
|
|
484
|
-
buildAdditionalButtons = (configs) => {
|
|
485
|
-
const additionalButtons = [];
|
|
486
|
-
_.each(additionalEditButtons, (config) => {
|
|
487
|
-
const {
|
|
488
|
-
key,
|
|
489
|
-
text,
|
|
490
|
-
handler,
|
|
491
|
-
icon,
|
|
492
|
-
isDisabled,
|
|
493
|
-
color = '#fff',
|
|
494
|
-
} = config,
|
|
495
|
-
buttonProps = {};
|
|
496
|
-
if (key) {
|
|
497
|
-
buttonProps.key = key;
|
|
498
|
-
buttonProps.reference = key;
|
|
499
|
-
}
|
|
500
|
-
if (handler) {
|
|
501
|
-
buttonProps.onPress = handler;
|
|
502
|
-
}
|
|
503
|
-
if (icon) {
|
|
504
|
-
buttonProps.leftIcon = <Icon as={icon} color="#fff" size="sm" />;
|
|
505
|
-
}
|
|
506
|
-
if (isDisabled) {
|
|
507
|
-
buttonProps.isDisabled = isDisabled;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
const button = <Button
|
|
511
|
-
color={color}
|
|
512
|
-
ml={2}
|
|
513
|
-
parent={self}
|
|
514
|
-
reference={key}
|
|
515
|
-
{...buttonProps}
|
|
516
|
-
>{text}</Button>;
|
|
517
|
-
additionalButtons.push(button);
|
|
518
|
-
});
|
|
519
|
-
return additionalButtons;
|
|
520
|
-
},
|
|
521
519
|
onSubmitError = (errors, e) => {
|
|
522
520
|
debugger;
|
|
523
521
|
if (editorType === EDITOR_TYPE__INLINE) {
|
|
@@ -538,6 +536,13 @@ function Form(props) {
|
|
|
538
536
|
const values = record.submitValues;
|
|
539
537
|
reset(values);
|
|
540
538
|
}
|
|
539
|
+
},
|
|
540
|
+
onLayoutDecorated = (e) => {
|
|
541
|
+
if (onLayout) {
|
|
542
|
+
onLayout(e);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
setContainerWidth(e.nativeEvent.layout.width);
|
|
541
546
|
};
|
|
542
547
|
|
|
543
548
|
useEffect(() => {
|
|
@@ -592,148 +597,166 @@ function Form(props) {
|
|
|
592
597
|
sizeProps.maxHeight = maxHeight;
|
|
593
598
|
}
|
|
594
599
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
+
let formComponents,
|
|
601
|
+
editor,
|
|
602
|
+
additionalButtons,
|
|
603
|
+
isSaveDisabled = false,
|
|
604
|
+
isSubmitDisabled = false,
|
|
605
|
+
savingProps = {};
|
|
600
606
|
|
|
607
|
+
if (containerWidth) { // we need to render this component twice in order to get the container width. Skip this on first render
|
|
608
|
+
|
|
609
|
+
if (isSaving) {
|
|
610
|
+
savingProps.borderTopWidth = 2;
|
|
611
|
+
savingProps.borderTopColor = '#f00';
|
|
612
|
+
}
|
|
601
613
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
}
|
|
614
|
+
if (editorType === EDITOR_TYPE__INLINE) {
|
|
615
|
+
formComponents = buildFromColumnsConfig();
|
|
616
|
+
editor = <ScrollView
|
|
617
|
+
horizontal={true}
|
|
618
|
+
flex={1}
|
|
619
|
+
bg="#fff"
|
|
620
|
+
py={1}
|
|
621
|
+
borderTopWidth={3}
|
|
622
|
+
borderBottomWidth={5}
|
|
623
|
+
borderTopColor="primary.100"
|
|
624
|
+
borderBottomColor="primary.100"
|
|
625
|
+
>{formComponents}</ScrollView>;
|
|
626
|
+
// } else if (editorType === EDITOR_TYPE__PLAIN) {
|
|
627
|
+
// formComponents = buildFromItems();
|
|
628
|
+
// const formAncillaryComponents = buildAncillary();
|
|
629
|
+
// editor = <>
|
|
630
|
+
// <Column p={4}>{formComponents}</Column>
|
|
631
|
+
// <Column pt={4}>{formAncillaryComponents}</Column>
|
|
632
|
+
// </>;
|
|
633
|
+
} else {
|
|
634
|
+
formComponents = buildFromItems();
|
|
635
|
+
const formAncillaryComponents = buildAncillary();
|
|
636
|
+
editor = <ScrollView _web={{ minHeight, }} width="100%" pb={1}>
|
|
637
|
+
{containerWidth >= CONTAINER_THRESHOLD ? <Row p={4} pl={0}>{formComponents}</Row> : null}
|
|
638
|
+
{containerWidth < CONTAINER_THRESHOLD ? <Column p={4}>{formComponents}</Column> : null}
|
|
639
|
+
<Column m={2} pt={4}>{formAncillaryComponents}</Column>
|
|
640
|
+
</ScrollView>;
|
|
641
|
+
}
|
|
631
642
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
643
|
+
let editorModeF;
|
|
644
|
+
switch(editorMode) {
|
|
645
|
+
case EDITOR_MODE__VIEW:
|
|
646
|
+
editorModeF = 'View';
|
|
647
|
+
break;
|
|
648
|
+
case EDITOR_MODE__ADD:
|
|
649
|
+
editorModeF = 'Add';
|
|
650
|
+
break;
|
|
651
|
+
case EDITOR_MODE__EDIT:
|
|
652
|
+
editorModeF = isMultiple ? 'Edit Multiple' : 'Edit';
|
|
653
|
+
break;
|
|
654
|
+
}
|
|
644
655
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
isSaveDisabled = true;
|
|
653
|
-
}
|
|
656
|
+
if (!_.isEmpty(formState.errors)) {
|
|
657
|
+
isSaveDisabled = true;
|
|
658
|
+
isSubmitDisabled = true;
|
|
659
|
+
}
|
|
660
|
+
if (_.isEmpty(formState.dirtyFields) && !record?.isRemotePhantom) {
|
|
661
|
+
isSaveDisabled = true;
|
|
662
|
+
}
|
|
654
663
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
664
|
+
if (editorType === EDITOR_TYPE__INLINE) {
|
|
665
|
+
buttonGroupProps.position = 'fixed';
|
|
666
|
+
buttonGroupProps.left = 10; // TODO: I would prefer to have this be centered, but it's a lot more complex than just making it stick to the left
|
|
667
|
+
footerProps.alignItems = 'flex-start';
|
|
668
|
+
}
|
|
660
669
|
|
|
661
|
-
|
|
670
|
+
additionalButtons = buildAdditionalButtons(additionalEditButtons);
|
|
671
|
+
}
|
|
662
672
|
|
|
663
|
-
return <Column {...sizeProps} onLayout={
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
<Button
|
|
668
|
-
key="backBtn"
|
|
669
|
-
onPress={onBack}
|
|
670
|
-
leftIcon={<Icon as={AngleLeft} color="#fff" size="sm" />}
|
|
671
|
-
color="#fff"
|
|
672
|
-
>Back</Button>}
|
|
673
|
-
{isSingle && editorMode === EDITOR_MODE__EDIT && onViewMode &&
|
|
674
|
-
<Button
|
|
675
|
-
key="viewBtn"
|
|
676
|
-
onPress={onViewMode}
|
|
677
|
-
leftIcon={<Icon as={Eye} color="#fff" size="sm" />}
|
|
678
|
-
color="#fff"
|
|
679
|
-
>To View</Button>}
|
|
680
|
-
</Row>
|
|
681
|
-
{editorMode === EDITOR_MODE__EDIT && !_.isEmpty(additionalButtons) &&
|
|
682
|
-
<Row p={4} alignItems="center" justifyContent="flex-end">
|
|
683
|
-
{additionalButtons}
|
|
684
|
-
</Row>}
|
|
685
|
-
|
|
686
|
-
{editor}
|
|
687
|
-
|
|
688
|
-
<Footer justifyContent="flex-end" {...footerProps} {...savingProps}>
|
|
689
|
-
{onDelete && editorMode === EDITOR_MODE__EDIT && isSingle &&
|
|
690
|
-
<Row flex={1} justifyContent="flex-start">
|
|
673
|
+
return <Column {...sizeProps} onLayout={onLayoutDecorated} ref={formRef}>
|
|
674
|
+
{containerWidth && <>
|
|
675
|
+
<Row px={4} pt={4} alignItems="center" justifyContent="flex-end">
|
|
676
|
+
{isSingle && editorMode === EDITOR_MODE__EDIT && onBack &&
|
|
691
677
|
<Button
|
|
692
|
-
key="
|
|
693
|
-
onPress={
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
678
|
+
key="backBtn"
|
|
679
|
+
onPress={onBack}
|
|
680
|
+
leftIcon={<Icon as={AngleLeft} color="#fff" size="sm" />}
|
|
681
|
+
color="#fff"
|
|
682
|
+
>Back</Button>}
|
|
683
|
+
{isSingle && editorMode === EDITOR_MODE__EDIT && onViewMode &&
|
|
684
|
+
<Button
|
|
685
|
+
key="viewBtn"
|
|
686
|
+
onPress={onViewMode}
|
|
687
|
+
leftIcon={<Icon as={Eye} color="#fff" size="sm" />}
|
|
698
688
|
color="#fff"
|
|
699
|
-
>
|
|
689
|
+
>To View</Button>}
|
|
690
|
+
</Row>
|
|
691
|
+
{editorMode === EDITOR_MODE__EDIT && !_.isEmpty(additionalButtons) &&
|
|
692
|
+
<Row p={4} alignItems="center" justifyContent="flex-end" flexWrap="wrap">
|
|
693
|
+
{additionalButtons}
|
|
700
694
|
</Row>}
|
|
701
|
-
|
|
702
|
-
{
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
variant="ghost"
|
|
715
|
-
onPress={onCancel}
|
|
716
|
-
color="#fff"
|
|
717
|
-
>Cancel</Button>}
|
|
718
|
-
{!isEditorViewOnly && onSave && <Button
|
|
719
|
-
key="saveBtn"
|
|
720
|
-
onPress={(e) => handleSubmit(onSaveDecorated, onSubmitError)(e)}
|
|
721
|
-
isDisabled={isSaveDisabled}
|
|
722
|
-
color="#fff"
|
|
723
|
-
>{editorMode === EDITOR_MODE__ADD ? 'Add' : 'Save'}</Button>}
|
|
724
|
-
{onSubmit && <Button
|
|
725
|
-
key="submitBtn"
|
|
726
|
-
onPress={(e) => handleSubmit(onSubmitDecorated, onSubmitError)(e)}
|
|
727
|
-
isDisabled={isSubmitDisabled}
|
|
695
|
+
|
|
696
|
+
{editor}
|
|
697
|
+
|
|
698
|
+
<Footer justifyContent="flex-end" {...footerProps} {...savingProps}>
|
|
699
|
+
{onDelete && editorMode === EDITOR_MODE__EDIT && isSingle &&
|
|
700
|
+
<Row flex={1} justifyContent="flex-start">
|
|
701
|
+
<Button
|
|
702
|
+
key="deleteBtn"
|
|
703
|
+
onPress={onDelete}
|
|
704
|
+
bg="warning"
|
|
705
|
+
_hover={{
|
|
706
|
+
bg: 'warningHover',
|
|
707
|
+
}}
|
|
728
708
|
color="#fff"
|
|
729
|
-
>
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
709
|
+
>Delete</Button>
|
|
710
|
+
</Row>}
|
|
711
|
+
|
|
712
|
+
{!isEditorViewOnly &&
|
|
713
|
+
<IconButton
|
|
714
|
+
key="resetBtn"
|
|
715
|
+
onPress={() => {
|
|
716
|
+
if (onReset) {
|
|
717
|
+
onReset();
|
|
718
|
+
}
|
|
719
|
+
reset();
|
|
720
|
+
}}
|
|
721
|
+
icon={<Rotate color="#fff" />}
|
|
722
|
+
/>}
|
|
723
|
+
{!isEditorViewOnly && isSingle && onCancel &&
|
|
724
|
+
<Button
|
|
725
|
+
key="cancelBtn"
|
|
726
|
+
variant="ghost"
|
|
727
|
+
onPress={onCancel}
|
|
728
|
+
color="#fff"
|
|
729
|
+
>Cancel</Button>}
|
|
730
|
+
{!isEditorViewOnly && onSave &&
|
|
731
|
+
<Button
|
|
732
|
+
key="saveBtn"
|
|
733
|
+
onPress={(e) => handleSubmit(onSaveDecorated, onSubmitError)(e)}
|
|
734
|
+
isDisabled={isSaveDisabled}
|
|
735
|
+
color="#fff"
|
|
736
|
+
>{editorMode === EDITOR_MODE__ADD ? 'Add' : 'Save'}</Button>}
|
|
737
|
+
{onSubmit &&
|
|
738
|
+
<Button
|
|
739
|
+
key="submitBtn"
|
|
740
|
+
onPress={(e) => handleSubmit(onSubmitDecorated, onSubmitError)(e)}
|
|
741
|
+
isDisabled={isSubmitDisabled}
|
|
742
|
+
color="#fff"
|
|
743
|
+
>{submitBtnLabel || 'Submit'}</Button>}
|
|
744
|
+
|
|
745
|
+
{isEditorViewOnly && onClose && editorType !== EDITOR_TYPE__SIDE &&
|
|
746
|
+
<Button
|
|
747
|
+
key="closeBtn"
|
|
748
|
+
onPress={onClose}
|
|
749
|
+
color="#fff"
|
|
750
|
+
>Close</Button>}
|
|
751
|
+
|
|
752
|
+
{additionalFooterButtons && _.map(additionalFooterButtons, (props) => {
|
|
753
|
+
return <Button
|
|
754
|
+
{...props}
|
|
755
|
+
onPress={(e) => handleSubmit(props.onPress, onSubmitError)(e)}
|
|
756
|
+
>{props.text}</Button>;
|
|
757
|
+
})}
|
|
758
|
+
</Footer>
|
|
759
|
+
</>}
|
|
737
760
|
</Column>;
|
|
738
761
|
}
|
|
739
762
|
|
|
@@ -73,6 +73,7 @@ function GridComponent(props) {
|
|
|
73
73
|
},
|
|
74
74
|
flatListProps = {},
|
|
75
75
|
// enableEditors = false,
|
|
76
|
+
loadOnRender = true,
|
|
76
77
|
pullToRefresh = true,
|
|
77
78
|
hideNavColumn = true,
|
|
78
79
|
noneFoundText,
|
|
@@ -147,7 +148,9 @@ function GridComponent(props) {
|
|
|
147
148
|
forceUpdate = useForceUpdate(),
|
|
148
149
|
containerRef = useRef(),
|
|
149
150
|
gridRef = useRef(),
|
|
151
|
+
gridContainerRef = useRef(),
|
|
150
152
|
isAddingRef = useRef(),
|
|
153
|
+
[isRendered, setIsRendered] = useState(false),
|
|
151
154
|
[isReady, setIsReady] = useState(false),
|
|
152
155
|
[isLoading, setIsLoading] = useState(false),
|
|
153
156
|
[localColumnsConfig, setLocalColumnsConfigRaw] = useState([]),
|
|
@@ -620,30 +623,46 @@ function GridComponent(props) {
|
|
|
620
623
|
setDragRowSlot(null);
|
|
621
624
|
},
|
|
622
625
|
onLayout = (e) => {
|
|
623
|
-
if (disableAdjustingPageSizeToHeight || !Repository || CURRENT_MODE !== UI_MODE_WEB || !gridRef.current || isAddingRef.current) {
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
const
|
|
628
|
-
gr = gridRef.current,
|
|
629
|
-
scrollableNode = gr.getScrollableNode(),
|
|
630
|
-
scrollableNodeBoundingBox = scrollableNode.getBoundingClientRect(),
|
|
631
|
-
scrollableNodeHeight = scrollableNodeBoundingBox.height,
|
|
632
|
-
firstRow = scrollableNode.children[0].children[showHeaders ? 1: 0];
|
|
633
626
|
|
|
634
|
-
|
|
635
|
-
|
|
627
|
+
let doLoad = false;
|
|
628
|
+
if (!isRendered) {
|
|
629
|
+
setIsRendered(true);
|
|
630
|
+
if (loadOnRender && Repository && !Repository.isLoaded && !Repository.isLoading && !Repository.isAutoLoad) {
|
|
631
|
+
doLoad = true; // first time in onLayout only!
|
|
632
|
+
}
|
|
636
633
|
}
|
|
637
634
|
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
635
|
+
const adjustPageSizeToHeight = !!(disableAdjustingPageSizeToHeight || !Repository || CURRENT_MODE !== UI_MODE_WEB || !gridRef.current || isAddingRef.current);
|
|
636
|
+
if (adjustPageSizeToHeight) {
|
|
637
|
+
// this currently only works on web
|
|
638
|
+
const
|
|
639
|
+
gr = gridContainerRef.current,
|
|
640
|
+
// scrollableNode = gr.getScrollableNode(),
|
|
641
|
+
// scrollableNodeBoundingBox = scrollableNode.getBoundingClientRect(),
|
|
642
|
+
// scrollableNodeHeight = scrollableNodeBoundingBox.height,
|
|
643
|
+
// firstRow = scrollableNode.children[0].children[showHeaders ? 1: 0];
|
|
644
|
+
height = gr.getBoundingClientRect().height;
|
|
645
|
+
// IDEALLY, we want the grid to load right away with appropriate limits.
|
|
646
|
+
// Currently, it's been loading once, then doing layout then loading again with correct limit.
|
|
647
|
+
// How do we get the right limit before it renders??
|
|
648
|
+
// Estimate based on (container height -header -footer) / avg height? This won't work for rows that exceed the avg height.
|
|
649
|
+
// Maybe we do that avg at first, and if it exceeds, then we do another query to lose the later ones, which are hidden anyway.
|
|
650
|
+
// It'll only do that once. Better to hide the offscreen ones, than to show gap at first, and later fill it
|
|
651
|
+
// if (firstRow) { // TODO: this assumes there is a row there already, which is wrong!
|
|
652
|
+
// const
|
|
653
|
+
// rowHeight = firstRow.getBoundingClientRect().height,
|
|
654
|
+
// rowsPerContainer = Math.floor(scrollableNodeHeight / rowHeight);
|
|
655
|
+
// let pageSize = rowsPerContainer;
|
|
656
|
+
// if (showHeaders) {
|
|
657
|
+
// pageSize--;
|
|
658
|
+
// }
|
|
659
|
+
// if (pageSize !== Repository.pageSize) {
|
|
660
|
+
// Repository.setPageSize(pageSize);
|
|
661
|
+
// }
|
|
662
|
+
// }
|
|
644
663
|
}
|
|
645
|
-
if (
|
|
646
|
-
Repository.
|
|
664
|
+
if (doLoad) {
|
|
665
|
+
Repository.load();
|
|
647
666
|
}
|
|
648
667
|
},
|
|
649
668
|
debouncedOnLayout = useCallback(_.debounce(onLayout, 500), []);
|
|
@@ -827,7 +846,7 @@ function GridComponent(props) {
|
|
|
827
846
|
>
|
|
828
847
|
{topToolbar}
|
|
829
848
|
|
|
830
|
-
<Column w="100%" flex={1} minHeight={40} borderTopWidth={isLoading ? 2 : 1} borderTopColor={isLoading ? '#f00' : 'trueGray.300'} onClick={() => {
|
|
849
|
+
<Column ref={gridContainerRef} w="100%" flex={1} minHeight={40} borderTopWidth={isLoading ? 2 : 1} borderTopColor={isLoading ? '#f00' : 'trueGray.300'} onClick={() => {
|
|
831
850
|
if (!isDragMode && !isInlineEditorShown) {
|
|
832
851
|
deselectAll();
|
|
833
852
|
}
|
|
@@ -18,8 +18,9 @@ export default function withData(WrappedComponent) {
|
|
|
18
18
|
setRepository,
|
|
19
19
|
uniqueRepository = false,
|
|
20
20
|
model,
|
|
21
|
-
autoLoad
|
|
21
|
+
autoLoad, // bool
|
|
22
22
|
pageSize,
|
|
23
|
+
baseParams,
|
|
23
24
|
|
|
24
25
|
// For plain JS data
|
|
25
26
|
data,
|
|
@@ -46,11 +47,14 @@ export default function withData(WrappedComponent) {
|
|
|
46
47
|
return () => {};
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
let repositoryId;
|
|
51
|
+
|
|
49
52
|
(async () => {
|
|
50
53
|
let Repository;
|
|
51
54
|
if (uniqueRepository) {
|
|
52
55
|
const schema = oneHatData.getSchema(model);
|
|
53
56
|
Repository = await oneHatData.createRepository({ schema });
|
|
57
|
+
repositoryId = Repository.id;
|
|
54
58
|
} else {
|
|
55
59
|
Repository = oneHatData.getRepository(model);
|
|
56
60
|
}
|
|
@@ -59,8 +63,19 @@ export default function withData(WrappedComponent) {
|
|
|
59
63
|
Repository.setPageSize(pageSize);
|
|
60
64
|
}
|
|
61
65
|
|
|
62
|
-
if (
|
|
63
|
-
|
|
66
|
+
if (baseParams) {
|
|
67
|
+
Repository.setBaseParams(baseParams);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if (Repository && !Repository.isLoaded && Repository.isRemote && !Repository.isAutoLoad && !Repository.isLoading) {
|
|
72
|
+
let doAutoLoad = Repository.autoLoad;
|
|
73
|
+
if (!_.isNil(autoLoad)) { // prop can override schema setting for autoLoad
|
|
74
|
+
doAutoLoad = autoLoad;
|
|
75
|
+
}
|
|
76
|
+
if (doAutoLoad) {
|
|
77
|
+
await Repository.load();
|
|
78
|
+
}
|
|
64
79
|
}
|
|
65
80
|
|
|
66
81
|
setLocalRepository(Repository);
|
|
@@ -73,7 +88,13 @@ export default function withData(WrappedComponent) {
|
|
|
73
88
|
setIsReady(true);
|
|
74
89
|
})();
|
|
75
90
|
|
|
76
|
-
|
|
91
|
+
return () => {
|
|
92
|
+
if (repositoryId) {
|
|
93
|
+
oneHatData.deleteRepository(repositoryId);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
}, []);
|
|
77
98
|
|
|
78
99
|
if (!isReady) {
|
|
79
100
|
return null;
|
|
@@ -138,7 +138,11 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
138
138
|
setEditorMode(EDITOR_MODE__EDIT);
|
|
139
139
|
setIsEditorShown(true);
|
|
140
140
|
},
|
|
141
|
-
onDelete = async (
|
|
141
|
+
onDelete = async (args) => {
|
|
142
|
+
let cb = null;
|
|
143
|
+
if (_.isFunction(args)) {
|
|
144
|
+
cb = args;
|
|
145
|
+
}
|
|
142
146
|
if (_.isEmpty(selection) || (_.isArray(selection) && (selection.length > 1 || selection[0]?.isDestroyed))) {
|
|
143
147
|
return;
|
|
144
148
|
}
|
|
@@ -281,7 +281,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
281
281
|
(async () => {
|
|
282
282
|
|
|
283
283
|
if (usesWithValue && Repository?.isRemote
|
|
284
|
-
&& !Repository.isAutoLoad && !Repository.isLoaded && !Repository.isLoading) {
|
|
284
|
+
&& !Repository.isAutoLoad && !Repository.isLoaded && !Repository.isLoading && (!_.isNil(value) || !_.isEmpty(selection)) || autoSelectFirstItem) {
|
|
285
285
|
// on initialization, we can't conformSelectionToValue if the repository is not yet loaded,
|
|
286
286
|
// so first load repo, then conform to value
|
|
287
287
|
await Repository.load();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc.
|
|
2
|
+
import * as React from "react"
|
|
3
|
+
import Svg, { Path } from "react-native-svg"
|
|
4
|
+
import { Icon } from 'native-base';
|
|
5
|
+
|
|
6
|
+
function SvgComponent(props) {
|
|
7
|
+
return (
|
|
8
|
+
<Icon xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" {...props}>
|
|
9
|
+
<Path d="M64 0C28.7 0 0 28.7 0 64v384c0 35.3 28.7 64 64 64h256c35.3 0 64-28.7 64-64V160H256c-17.7 0-32-14.3-32-32V0H64zm192 0v128h128L256 0zM155.7 250.2l36.3 51.9 36.3-51.9c7.6-10.9 22.6-13.5 33.4-5.9s13.5 22.6 5.9 33.4L221.3 344l46.4 66.2c7.6 10.9 5 25.8-5.9 33.4s-25.8 5-33.4-5.9L192 385.8l-36.3 51.9c-7.6 10.9-22.6 13.5-33.4 5.9s-13.5-22.6-5.9-33.4l46.3-66.2-46.4-66.2c-7.6-10.9-5-25.8 5.9-33.4s25.8-5 33.4 5.9z" />
|
|
10
|
+
</Icon>
|
|
11
|
+
)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default SvgComponent
|
|
@@ -35,7 +35,7 @@ function Panel(props) {
|
|
|
35
35
|
onLayout = null,
|
|
36
36
|
|
|
37
37
|
// Header
|
|
38
|
-
title = UiGlobals.customInflect(Inflector.camel2words(Inflector.underscore(props.model))),
|
|
38
|
+
title = props.model ? UiGlobals.customInflect(Inflector.camel2words(Inflector.underscore(props.model))) : '',
|
|
39
39
|
showHeader = true,
|
|
40
40
|
header = null,
|
|
41
41
|
isClosable = false,
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Column,
|
|
4
|
+
Icon,
|
|
5
|
+
Row,
|
|
6
|
+
Text,
|
|
7
|
+
} from 'native-base';
|
|
8
|
+
import { EDITOR_TYPE__PLAIN } from '../../Constants/Editor';
|
|
9
|
+
import {
|
|
10
|
+
UI_MODE_WEB,
|
|
11
|
+
CURRENT_MODE,
|
|
12
|
+
} from '../../Constants/UiModes.js';
|
|
13
|
+
import Form from '../Form/Form.js';
|
|
14
|
+
import withComponent from '../Hoc/withComponent.js';
|
|
15
|
+
import ChartLine from '../Icons/ChartLine.js';
|
|
16
|
+
import Pdf from '../Icons/Pdf.js';
|
|
17
|
+
import Excel from '../Icons/Excel.js';
|
|
18
|
+
import UiGlobals from '../../UiGlobals.js';
|
|
19
|
+
import _ from 'lodash';
|
|
20
|
+
|
|
21
|
+
const
|
|
22
|
+
PDF = 'PDF',
|
|
23
|
+
EXCEL = 'PhpOffice';
|
|
24
|
+
|
|
25
|
+
function Report(props) {
|
|
26
|
+
if (CURRENT_MODE !== UI_MODE_WEB) {
|
|
27
|
+
return <Text>Reports are web only!</Text>;
|
|
28
|
+
}
|
|
29
|
+
const {
|
|
30
|
+
title,
|
|
31
|
+
description,
|
|
32
|
+
reportId,
|
|
33
|
+
// icon,
|
|
34
|
+
disablePdf = false,
|
|
35
|
+
disableExcel = false,
|
|
36
|
+
includePresets = false,
|
|
37
|
+
showReportHeaders = true,
|
|
38
|
+
h = '300px',
|
|
39
|
+
} = props,
|
|
40
|
+
styles = UiGlobals.styles,
|
|
41
|
+
url = UiGlobals.baseURL + 'Reports/getReport',
|
|
42
|
+
buttons = [],
|
|
43
|
+
downloadWithFetch = (data) => {
|
|
44
|
+
const options = {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: {
|
|
47
|
+
'Content-Type': 'application/json',
|
|
48
|
+
},
|
|
49
|
+
body: JSON.stringify(data),
|
|
50
|
+
};
|
|
51
|
+
fetch(url, options)
|
|
52
|
+
.then( res => res.blob() )
|
|
53
|
+
.then( blob => {
|
|
54
|
+
const
|
|
55
|
+
winName = 'ReportWindow',
|
|
56
|
+
opts = 'resizable=yes,height=600,width=800,location=0,menubar=0,scrollbars=1',
|
|
57
|
+
externalWindow = window.open('', winName, opts),
|
|
58
|
+
file = externalWindow.URL.createObjectURL(blob);
|
|
59
|
+
externalWindow.location.assign(file);
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
getReport = (reportType, data) => {
|
|
63
|
+
const params = {
|
|
64
|
+
report_id: reportId,
|
|
65
|
+
outputFileType: reportType,
|
|
66
|
+
showReportHeaders,
|
|
67
|
+
// download_token, // not sure this is needed
|
|
68
|
+
...data,
|
|
69
|
+
},
|
|
70
|
+
closeWindow = reportType === EXCEL;
|
|
71
|
+
|
|
72
|
+
downloadWithFetch(params, closeWindow);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const propsIcon = props._icon || {};
|
|
76
|
+
propsIcon.size = 60;
|
|
77
|
+
propsIcon.px = 5;
|
|
78
|
+
let icon = props.icon;
|
|
79
|
+
if (_.isEmpty(icon)) {
|
|
80
|
+
icon = ChartLine;
|
|
81
|
+
}
|
|
82
|
+
if (React.isValidElement(icon)) {
|
|
83
|
+
if (!_.isEmpty(propsIcon)) {
|
|
84
|
+
icon = React.cloneElement(icon, {...propsIcon});
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
icon = <Icon as={icon} {...propsIcon} />;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!disableExcel) {
|
|
91
|
+
buttons.push({
|
|
92
|
+
key: 'ExcelBtn',
|
|
93
|
+
text: 'Download Excel',
|
|
94
|
+
leftIcon: <Icon as={Excel} size="md" color="#fff" />,
|
|
95
|
+
onPress: (data) => getReport(EXCEL, data),
|
|
96
|
+
ml: 1,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (!disablePdf) {
|
|
100
|
+
buttons.push({
|
|
101
|
+
key: 'pdfBtn',
|
|
102
|
+
text: 'Download PDF',
|
|
103
|
+
leftIcon: <Icon as={Pdf} size="md" color="#fff" />,
|
|
104
|
+
onPress: (data) => getReport(PDF, data),
|
|
105
|
+
ml: 1,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return <Column w="100%" borderWidth={1} borderColor="primary.300" pt={4} mb={3}>
|
|
109
|
+
<Row>
|
|
110
|
+
{icon && <Column>{icon}</Column>}
|
|
111
|
+
<Column>
|
|
112
|
+
<Text fontSize="2xl">{title}</Text>
|
|
113
|
+
<Text fontSize="sm">{description}</Text>
|
|
114
|
+
</Column>
|
|
115
|
+
</Row>
|
|
116
|
+
<Form
|
|
117
|
+
type={EDITOR_TYPE__PLAIN}
|
|
118
|
+
additionalFooterButtons={buttons}
|
|
119
|
+
{...props._form}
|
|
120
|
+
/>
|
|
121
|
+
</Column>;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export default withComponent(Report);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useRef, } from 'react';
|
|
1
|
+
import { useRef, useState, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Column,
|
|
4
4
|
Icon,
|
|
@@ -14,12 +14,15 @@ import withComponent from '../Hoc/withComponent.js';
|
|
|
14
14
|
import withPdfButton from '../Hoc/withPdfButton.js';
|
|
15
15
|
import inArray from '../../Functions/inArray.js';
|
|
16
16
|
import getComponentFromType from '../../Functions/getComponentFromType.js';
|
|
17
|
+
import buildAdditionalButtons from '../../Functions/buildAdditionalButtons.js';
|
|
17
18
|
import Button from '../Buttons/Button.js';
|
|
18
19
|
import Label from '../Form/Label.js';
|
|
19
20
|
import Pencil from '../Icons/Pencil.js';
|
|
20
21
|
import Footer from '../Layout/Footer.js';
|
|
21
22
|
import _ from 'lodash';
|
|
22
23
|
|
|
24
|
+
const CONTAINER_THRESHOLD = 900;
|
|
25
|
+
|
|
23
26
|
function Viewer(props) {
|
|
24
27
|
const {
|
|
25
28
|
viewerCanDelete = false,
|
|
@@ -48,13 +51,14 @@ function Viewer(props) {
|
|
|
48
51
|
} = props,
|
|
49
52
|
scrollViewRef = useRef(),
|
|
50
53
|
isMultiple = _.isArray(record),
|
|
54
|
+
[containerWidth, setContainerWidth] = useState(),
|
|
51
55
|
isSideEditor = editorType === EDITOR_TYPE__SIDE,
|
|
52
56
|
styles = UiGlobals.styles,
|
|
53
57
|
flex = props.flex || 1,
|
|
54
58
|
buildFromItems = () => {
|
|
55
|
-
return _.map(items, (item, ix) =>
|
|
59
|
+
return _.map(items, (item, ix) => buildFromItem(item, ix, columnDefaults));
|
|
56
60
|
},
|
|
57
|
-
|
|
61
|
+
buildFromItem = (item, ix, defaults) => {
|
|
58
62
|
let {
|
|
59
63
|
type,
|
|
60
64
|
title,
|
|
@@ -90,9 +94,26 @@ function Viewer(props) {
|
|
|
90
94
|
if (_.isEmpty(items)) {
|
|
91
95
|
return null;
|
|
92
96
|
}
|
|
97
|
+
if (type === 'Column') {
|
|
98
|
+
if (containerWidth < CONTAINER_THRESHOLD) {
|
|
99
|
+
// everything is in one column
|
|
100
|
+
if (propsToPass.hasOwnProperty('flex')) {
|
|
101
|
+
delete propsToPass.flex;
|
|
102
|
+
}
|
|
103
|
+
if (propsToPass.hasOwnProperty('width')) {
|
|
104
|
+
delete propsToPass.width;
|
|
105
|
+
}
|
|
106
|
+
if (propsToPass.hasOwnProperty('w')) {
|
|
107
|
+
delete propsToPass.w;
|
|
108
|
+
}
|
|
109
|
+
propsToPass.w = '100%';
|
|
110
|
+
propsToPass.mb = 1;
|
|
111
|
+
}
|
|
112
|
+
propsToPass.pl = 3;
|
|
113
|
+
}
|
|
93
114
|
const defaults = item.defaults;
|
|
94
115
|
children = _.map(items, (item, ix) => {
|
|
95
|
-
return
|
|
116
|
+
return buildFromItem(item, ix, defaults);
|
|
96
117
|
});
|
|
97
118
|
return <Element key={ix} title={title} {...defaults} {...propsToPass} {...editorTypeProps}>{children}</Element>;
|
|
98
119
|
}
|
|
@@ -121,6 +142,14 @@ function Viewer(props) {
|
|
|
121
142
|
{...propsToPass}
|
|
122
143
|
{...editorTypeProps}
|
|
123
144
|
/>;
|
|
145
|
+
|
|
146
|
+
if (item.additionalViewButtons) {
|
|
147
|
+
element = <Row flexWrap="wrap">
|
|
148
|
+
{element}
|
|
149
|
+
{buildAdditionalButtons(item.additionalViewButtons, self)}
|
|
150
|
+
</Row>;
|
|
151
|
+
}
|
|
152
|
+
|
|
124
153
|
if (label) {
|
|
125
154
|
const labelProps = {};
|
|
126
155
|
if (defaults?.labelWidth) {
|
|
@@ -169,42 +198,8 @@ function Viewer(props) {
|
|
|
169
198
|
}
|
|
170
199
|
return components;
|
|
171
200
|
},
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
_.each(additionalViewButtons, (config) => {
|
|
175
|
-
const {
|
|
176
|
-
key,
|
|
177
|
-
text,
|
|
178
|
-
handler,
|
|
179
|
-
icon,
|
|
180
|
-
isDisabled,
|
|
181
|
-
color = '#fff',
|
|
182
|
-
} = config,
|
|
183
|
-
buttonProps = {};
|
|
184
|
-
if (key) {
|
|
185
|
-
buttonProps.key = key;
|
|
186
|
-
buttonProps.reference = key;
|
|
187
|
-
}
|
|
188
|
-
if (handler) {
|
|
189
|
-
buttonProps.onPress = handler;
|
|
190
|
-
}
|
|
191
|
-
if (icon) {
|
|
192
|
-
buttonProps.leftIcon = <Icon as={icon} color="#fff" size="sm" />;
|
|
193
|
-
}
|
|
194
|
-
if (isDisabled) {
|
|
195
|
-
buttonProps.isDisabled = isDisabled;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const button = <Button
|
|
199
|
-
color={color}
|
|
200
|
-
ml={2}
|
|
201
|
-
parent={self}
|
|
202
|
-
reference={key}
|
|
203
|
-
{...buttonProps}
|
|
204
|
-
>{text}</Button>;
|
|
205
|
-
additionalButtons.push(button);
|
|
206
|
-
});
|
|
207
|
-
return additionalButtons;
|
|
201
|
+
onLayout = (e) => {
|
|
202
|
+
setContainerWidth(e.nativeEvent.layout.width);
|
|
208
203
|
};
|
|
209
204
|
|
|
210
205
|
if (self) {
|
|
@@ -213,52 +208,62 @@ function Viewer(props) {
|
|
|
213
208
|
|
|
214
209
|
const
|
|
215
210
|
showDeleteBtn = onDelete && viewerCanDelete,
|
|
216
|
-
showCloseBtn = !isSideEditor
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
color="#fff"
|
|
228
|
-
>To Edit</Button>
|
|
229
|
-
</Row>}
|
|
230
|
-
|
|
231
|
-
{!_.isEmpty(additionalButtons) &&
|
|
232
|
-
<Row p={2} alignItems="center" justifyContent="flex-end">
|
|
233
|
-
{additionalButtons}
|
|
234
|
-
</Row>}
|
|
211
|
+
showCloseBtn = !isSideEditor;
|
|
212
|
+
let additionalButtons = null,
|
|
213
|
+
viewerComponents = null,
|
|
214
|
+
ancillaryComponents = null;
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
if (containerWidth) { // we need to render this component twice in order to get the container width. Skip this on first render
|
|
218
|
+
additionalButtons = buildAdditionalButtons(additionalViewButtons);
|
|
219
|
+
viewerComponents = buildFromItems();
|
|
220
|
+
ancillaryComponents = buildAncillary();
|
|
221
|
+
}
|
|
235
222
|
|
|
236
|
-
|
|
223
|
+
return <Column flex={flex} {...props} onLayout={onLayout}>
|
|
224
|
+
{containerWidth && <>
|
|
225
|
+
{onEditMode && <Row px={4} pt={4} alignItems="center" justifyContent="flex-end">
|
|
226
|
+
<Button
|
|
227
|
+
key="editBtn"
|
|
228
|
+
onPress={onEditMode}
|
|
229
|
+
leftIcon={<Icon as={Pencil} color="#fff" size="sm" />}
|
|
230
|
+
color="#fff"
|
|
231
|
+
>To Edit</Button>
|
|
232
|
+
</Row>}
|
|
233
|
+
{!_.isEmpty(additionalButtons) &&
|
|
234
|
+
<Row p={4} alignItems="center" justifyContent="flex-end" flexWrap="wrap">
|
|
235
|
+
{additionalButtons}
|
|
236
|
+
</Row>}
|
|
237
237
|
|
|
238
|
-
|
|
238
|
+
<ScrollView _web={{ height: 1 }} width="100%" pb={1} ref={scrollViewRef}>
|
|
239
|
+
<Column>
|
|
240
|
+
{containerWidth >= CONTAINER_THRESHOLD ? <Row p={4} pl={0}>{viewerComponents}</Row> : null}
|
|
241
|
+
{containerWidth < CONTAINER_THRESHOLD ? <Column p={4}>{viewerComponents}</Column> : null}
|
|
242
|
+
<Column m={2} pt={4}>{ancillaryComponents}</Column>
|
|
243
|
+
</Column>
|
|
244
|
+
</ScrollView>
|
|
245
|
+
{(showDeleteBtn || showCloseBtn) &&
|
|
246
|
+
<Footer justifyContent="flex-end">
|
|
247
|
+
{showDeleteBtn &&
|
|
248
|
+
<Row flex={1} justifyContent="flex-start">
|
|
249
|
+
<Button
|
|
250
|
+
key="deleteBtn"
|
|
251
|
+
onPress={onDelete}
|
|
252
|
+
bg="warning"
|
|
253
|
+
_hover={{
|
|
254
|
+
bg: 'warningHover',
|
|
255
|
+
}}
|
|
256
|
+
color="#fff"
|
|
257
|
+
>Delete</Button>
|
|
258
|
+
</Row>}
|
|
259
|
+
{showCloseBtn && <Button
|
|
260
|
+
key="closeBtn"
|
|
261
|
+
onPress={onClose}
|
|
262
|
+
color="#fff"
|
|
263
|
+
>Close</Button>}
|
|
264
|
+
</Footer>}
|
|
239
265
|
|
|
240
|
-
|
|
241
|
-
</ScrollView>
|
|
242
|
-
{(showDeleteBtn || showCloseBtn) &&
|
|
243
|
-
<Footer justifyContent="flex-end">
|
|
244
|
-
{showDeleteBtn &&
|
|
245
|
-
<Row flex={1} justifyContent="flex-start">
|
|
246
|
-
<Button
|
|
247
|
-
key="deleteBtn"
|
|
248
|
-
onPress={onDelete}
|
|
249
|
-
bg="warning"
|
|
250
|
-
_hover={{
|
|
251
|
-
bg: 'warningHover',
|
|
252
|
-
}}
|
|
253
|
-
color="#fff"
|
|
254
|
-
>Delete</Button>
|
|
255
|
-
</Row>}
|
|
256
|
-
{showCloseBtn && <Button
|
|
257
|
-
key="closeBtn"
|
|
258
|
-
onPress={onClose}
|
|
259
|
-
color="#fff"
|
|
260
|
-
>Close</Button>}
|
|
261
|
-
</Footer>}
|
|
266
|
+
</>}
|
|
262
267
|
</Column>;
|
|
263
268
|
}
|
|
264
269
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Icon,
|
|
3
|
+
} from 'native-base';
|
|
4
|
+
import Button from '../Components/Buttons/Button.js';
|
|
5
|
+
import _ from 'lodash';
|
|
6
|
+
|
|
7
|
+
export default function buildAdditionalButtons(configs, self, handlerArgs = {}) {
|
|
8
|
+
const additionalButtons = [];
|
|
9
|
+
_.each(configs, (config) => {
|
|
10
|
+
const {
|
|
11
|
+
key,
|
|
12
|
+
text,
|
|
13
|
+
handler,
|
|
14
|
+
icon,
|
|
15
|
+
isDisabled,
|
|
16
|
+
color = '#fff',
|
|
17
|
+
} = config,
|
|
18
|
+
buttonProps = {
|
|
19
|
+
key,
|
|
20
|
+
reference: key,
|
|
21
|
+
};
|
|
22
|
+
if (handler) {
|
|
23
|
+
buttonProps.onPress = () => handler(handlerArgs);
|
|
24
|
+
}
|
|
25
|
+
if (icon) {
|
|
26
|
+
buttonProps.leftIcon = <Icon as={icon} color="#fff" size="sm" />;
|
|
27
|
+
}
|
|
28
|
+
if (isDisabled) {
|
|
29
|
+
buttonProps.isDisabled = isDisabled;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const button = <Button
|
|
33
|
+
color={color}
|
|
34
|
+
ml={2}
|
|
35
|
+
mb={2}
|
|
36
|
+
parent={self}
|
|
37
|
+
reference={key}
|
|
38
|
+
{...buttonProps}
|
|
39
|
+
>{text}</Button>;
|
|
40
|
+
additionalButtons.push(button);
|
|
41
|
+
});
|
|
42
|
+
return additionalButtons;
|
|
43
|
+
}
|