Skip to content

Commit

Permalink
JNG-5994 typeahead enum combo
Browse files Browse the repository at this point in the history
  • Loading branch information
noherczeg committed Oct 31, 2024
1 parent 7f821a6 commit 9fc96f1
Show file tree
Hide file tree
Showing 4 changed files with 325 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@
<source>src/containers/View/Galaxy/View/ViewGalaxyView.tsx</source>
<source>src/containers/View/Galaxy/View/ViewGalaxyViewDialogContainer.tsx</source>
<source>src/containers/View/Galaxy/View/ViewGalaxyViewPageContainer.tsx</source>
<source>src/containers/View/Matter/Form/ViewMatterForm.tsx</source>
<source>src/containers/Planet/View/PlanetView.tsx</source>
<source>src/containers/Planet/View/PlanetViewPageContainer.tsx</source>
<source>src/pages/God/God/Earth/AccessViewPage/index.tsx</source>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
//////////////////////////////////////////////////////////////////////////////
// G E N E R A T E D S O U R C E
// --------------------------------
// Factory expression: #application.pageContainers
// Path expression: 'src/containers/'+#containerPath(#self)+'/'+#containerComponentName(#self)+'.tsx'
// Template name: actor/src/containers/container.tsx
// Template file: actor/src/containers/container.tsx.hbs

import LoadingButton from '@mui/lab/LoadingButton';
import Autocomplete from '@mui/material/Autocomplete';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import ButtonGroup from '@mui/material/ButtonGroup';
import Container from '@mui/material/Container';
import Grid from '@mui/material/Grid';
import IconButton from '@mui/material/IconButton';
import InputAdornment from '@mui/material/InputAdornment';
import TextField from '@mui/material/TextField';
import Typography from '@mui/material/Typography';
import { OBJECTCLASS } from '@pandino/pandino-api';
import { useTrackService } from '@pandino/react-hooks';
import { clsx } from 'clsx';
import type { Dispatch, FC, SetStateAction } from 'react';
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { DropdownButton, MdiIcon, useJudoNavigation } from '~/components';
import { useConfirmDialog } from '~/components/dialog';
import { useL10N } from '~/l10n/l10n-context';
import type { EnumOption } from '~/utilities';
import { isErrorOperationFault, useErrorHandler } from '~/utilities';

import {} from '@mui/x-date-pickers';
import type {} from '@mui/x-date-pickers';
import { NumericInput } from '~/components/widgets';
import { Tags } from '~/components/widgets';
import { autoFocusRefDelay } from '~/config';
import { useConfirmationBeforeChange } from '~/hooks';
import type { MatterType } from '~/services/data-api/model/MatterType';
import type { ViewMatter, ViewMatterStored } from '~/services/data-api/model/ViewMatter';
import { VIEW_MATTER_FORM_CONTAINER_ACTIONS_HOOK_INTERFACE_KEY } from './customization';
import type { ViewMatterFormContainerHook } from './customization';
import type { ViewMatterFormActionDefinitions, ViewMatterFormProps } from './types';

// XMIID: God/(esm/_D0xk0Oq_EeuMzos2n42msw)/TransferObjectFormPageContainer
// Name: View::Matter::Form
export default function ViewMatterForm(props: ViewMatterFormProps) {
// Container props
const {
refreshCounter,
isLoading,
isDraft,
dataPath,
actions: pageActions,
data,
isFormUpdateable,
isFormDeleteable,
storeDiff,
editMode,
validation,
setValidation,
submit,
} = props;

// Container hooks
const { t } = useTranslation();
const { navigate, back } = useJudoNavigation();
const { locale: l10nLocale } = useL10N();
const { openConfirmDialog } = useConfirmDialog();

useConfirmationBeforeChange(
editMode,
t('judo.form.navigation.confirmation', {
defaultValue: 'You have potential unsaved changes in your form, are you sure you would like to navigate away?',
}),
);
// Pandino Container Action overrides
const { service: customContainerHook } = useTrackService<ViewMatterFormContainerHook>(
`(${OBJECTCLASS}=${VIEW_MATTER_FORM_CONTAINER_ACTIONS_HOOK_INTERFACE_KEY})`,
);
const containerActions: ViewMatterFormActionDefinitions = customContainerHook?.(data, editMode, storeDiff) || {};
const actions = useMemo(() => ({ ...pageActions, ...containerActions }), [pageActions, containerActions]);
const typeOptions = useMemo<EnumOption<keyof typeof MatterType>[]>(
() => [
{
id: 'God/(esm/_zK1VMORqEeuSU8xLq1yYbw)/EnumerationTypeMember',
value: 'dark',
i18nKey: 'enumerations.MatterType.dark',
i18nDefaultValue: 'dark',
},
{
id: 'God/(esm/_0PAvAORqEeuSU8xLq1yYbw)/EnumerationTypeMember',
value: 'interstellarMedium',
i18nKey: 'enumerations.MatterType.interstellarMedium',
i18nDefaultValue: 'interstellarMedium',
},
{
id: 'God/(esm/_17vWkORqEeuSU8xLq1yYbw)/EnumerationTypeMember',
value: 'intergalacticDust',
i18nKey: 'enumerations.MatterType.intergalacticDust',
i18nDefaultValue: 'intergalacticDust',
},
],
[],
);
const effectiveTypeOptions = useMemo<EnumOption<keyof typeof MatterType>[]>(() => {
if (actions.filterTypeOptions) {
return actions.filterTypeOptions(data, typeOptions);
}
return typeOptions;
}, [actions.filterTypeOptions]);
const autoFocusInputRef = useRef<any>(null);

useEffect(() => {
const timeout = setTimeout(() => {
if (typeof autoFocusInputRef?.current?.focus === 'function') {
autoFocusInputRef.current.focus();
}
}, autoFocusRefDelay);

return () => clearTimeout(timeout);
}, []);

return (
<Grid container data-container-id="God/(esm/_D0xk0Oq_EeuMzos2n42msw)/TransferObjectFormPageContainer">
<Grid item data-name="Form" xs={12} sm={12} md={36.0}>
<Grid container direction="row" spacing={2}>
<Grid item xs={12} sm={12}>
<Grid
id="God/(esm/_D0xk0Oq_EeuMzos2n42msw)/TransferObjectFormVisualElement"
data-name="Form"
container
direction="column"
alignItems="stretch"
justifyContent="flex-start"
spacing={2}
>
<Grid item xs={12} sm={12}>
<Autocomplete
id="God/(esm/_T4Q2kOq_EeuMzos2n42msw)/EnumerationTypeCombo"
autoHighlight
value={data.type || null}
disabled={actions?.isTypeDisabled ? actions.isTypeDisabled(data, editMode, isLoading) : isLoading}
readOnly={
(actions?.isTypeReadonly ? actions.isTypeReadonly(data, editMode, isLoading) : false) ||
!isFormUpdateable()
}
options={[
...effectiveTypeOptions.map((o) => ({
label: t(o.i18nKey, { defaultValue: o.i18nDefaultValue }),
value: o.value,
key: o.id,
})),
]}
isOptionEqualToValue={(option) => option.value === data.type}
getOptionKey={(option: any) => option.key}
getOptionLabel={(option: any) => {
if (typeof option === 'string') {
const o = effectiveTypeOptions.find((o) => o.value === option)!;
return t(o.i18nKey, { defaultValue: o.i18nDefaultValue });
}
return option.label;
}}
onChange={(_: any, newValue: { label: string; value: string | null; key: string } | null) => {
storeDiff('type', newValue?.value || null);
}}
disableClearable={actions?.isTypeRequired ? actions.isTypeRequired(data, editMode) : false}
renderInput={(params) => (
<TextField
{...params}
name={'type'}
required={actions?.isTypeRequired ? actions.isTypeRequired(data, editMode) : true}
label={t('View.Matter.Form.type', { defaultValue: 'Type' }) as string}
inputRef={autoFocusInputRef}
error={!!validation.get('type')}
helperText={validation.get('type')}
InputLabelProps={{ shrink: true }}
InputProps={{
...params.InputProps,
startAdornment: (
<InputAdornment position="start">
<MdiIcon path="chart-bubble" sx={{ mt: -2 }} />
</InputAdornment>
),
}}
className={clsx({
'JUDO-viewMode': !editMode,
'JUDO-required': actions?.isTypeRequired ? actions.isTypeRequired(data, editMode) : true,
})}
/>
)}
/>
</Grid>

<Grid item xs={12} sm={12}>
<NumericInput
required={actions?.isMassRequired ? actions.isMassRequired(data, editMode) : false}
name="mass"
id="God/(esm/_T4dq4Oq_EeuMzos2n42msw)/MeasuredTypeInput"
label={t('View.Matter.Form.mass', { defaultValue: 'Mass' }) as string}
customInput={TextField}
value={data.mass ?? ''}
formatValue={true}
decimalScale={2}
className={clsx({
'JUDO-viewMode': !editMode,
'JUDO-required': false,
})}
disabled={actions?.isMassDisabled ? actions.isMassDisabled(data, editMode, isLoading) : isLoading}
error={!!validation.get('mass')}
helperText={validation.get('mass')}
onValueChange={(values, sourceInfo) => {
const newValue = values.floatValue === undefined ? null : values.floatValue;
if (data.mass !== newValue) {
storeDiff('mass', newValue);
}
}}
InputLabelProps={{ shrink: true }}
InputProps={{
readOnly: actions?.isMassReadonly
? actions.isMassReadonly(data, editMode, isLoading)
: false || !isFormUpdateable(),
startAdornment: (
<InputAdornment position="start">
<MdiIcon path="blur" />
</InputAdornment>
),
}}
/>
</Grid>

<Grid item xs={12} sm={12}>
<TextField
required={actions?.isShortNoteRequired ? actions.isShortNoteRequired(data, editMode) : false}
name="shortNote"
id="God/(esm/_uN0FQFENEeyWtMpj61RngA)/StringTypeTextInput"
label={t('View.Matter.Form.shortNote', { defaultValue: 'Short Note' }) as string}
value={data.shortNote ?? ''}
className={clsx({
'JUDO-viewMode': !editMode,
'JUDO-required': false,
})}
disabled={
actions?.isShortNoteDisabled ? actions.isShortNoteDisabled(data, editMode, isLoading) : isLoading
}
error={!!validation.get('shortNote')}
helperText={validation.get('shortNote')}
onChange={(event) => {
const realValue = event.target.value?.length === 0 ? null : event.target.value;
storeDiff('shortNote', realValue);
}}
InputLabelProps={{ shrink: true }}
InputProps={{
readOnly: actions?.isShortNoteReadonly
? actions.isShortNoteReadonly(data, editMode, isLoading)
: false || !isFormUpdateable(),
startAdornment: (
<InputAdornment position="start">
<MdiIcon path="scatter-plot-outline" />
</InputAdornment>
),
}}
inputProps={{
maxLength: 127,
}}
/>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class UiImportHelper {
Map.entry("dateinput", Set.of("InputAdornment")),
Map.entry("datetimeinput", Set.of("InputAdornment")),
Map.entry("divider", Set.of("Divider")),
Map.entry("enumerationcombo", Set.of("TextField", "MenuItem", "InputAdornment")),
Map.entry("enumerationcombo", Set.of("Autocomplete", "TextField", "InputAdornment")),
Map.entry("enumerationradio", Set.of("RadioGroup", "FormControlLabel", "Radio", "FormControl", "FormHelperText", "InputLabel")),
Map.entry("formatted", Set.of("Typography")),
Map.entry("label", Set.of("Typography")),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,56 +11,66 @@
actions={actions}
>
{{/ if }}
<TextField
<Autocomplete
id="{{ getXMIID child }}"
autoHighlight
value={data.{{ child.attributeType.name }} || null}
disabled={actions?.is{{ firstToUpper child.attributeType.name }}Disabled ? actions.is{{ firstToUpper child.attributeType.name }}Disabled(data, editMode, isLoading) : isLoading}
readOnly={(actions?.is{{ firstToUpper child.attributeType.name }}Readonly ? actions.is{{ firstToUpper child.attributeType.name }}Readonly(data, editMode, isLoading) : false) || !isFormUpdateable()}
options={[
...effective{{ firstToUpper child.attributeType.name }}Options.map((o) => ({
label: t(o.i18nKey, { defaultValue: o.i18nDefaultValue }),
value: o.value,
key: o.id,
}))
]}
isOptionEqualToValue={ (option) => option.value === data.{{ child.attributeType.name }} }
getOptionKey={(option: any) => option.key }
getOptionLabel={(option: any) => {
if (typeof option === 'string') {
const o = effective{{ firstToUpper child.attributeType.name }}Options.find(o => o.value === option)!;
return t(o.i18nKey, { defaultValue: o.i18nDefaultValue })
}
return option.label;
} }
onChange={(_: any, newValue: { label: string, value: string | null, key: string } | null) => {
storeDiff('{{ child.attributeType.name }}', newValue?.value || null);
}}
disableClearable={actions?.is{{ firstToUpper child.attributeType.name }}Required ? actions.is{{ firstToUpper child.attributeType.name }}Required(data, editMode) : false}
renderInput={(params) => <TextField
{...params}
name={'{{ child.attributeType.name }}'}
required={actions?.is{{ firstToUpper child.attributeType.name }}Required ? actions.is{{ firstToUpper child.attributeType.name }}Required(data, editMode) : ({{# if child.requiredBy }}data.{{ child.requiredBy.name }} ||{{/ if }} {{ boolValue child.attributeType.isRequired }})}
name="{{ child.attributeType.name }}"
id="{{ getXMIID child }}"
label={ t('{{ getTranslationKeyForVisualElement child }}', { defaultValue: '{{ child.label }}' }) as string }
{{# if (shouldElementHaveAutoFocus child) }}
inputRef={autoFocusInputRef}
inputRef={autoFocusInputRef}
{{/ if }}
label={ t('{{ getTranslationKeyForVisualElement child }}', { defaultValue: '{{ child.label }}' }) as string }
value={ data.{{ child.attributeType.name }} || '' }
className={ clsx({
'JUDO-viewMode': !editMode,
'JUDO-required': {{ boolValue child.attributeType.isRequired }},
}) }
disabled={actions?.is{{ firstToUpper child.attributeType.name }}Disabled ? actions.is{{ firstToUpper child.attributeType.name }}Disabled(data, editMode, isLoading) : ({{# if child.enabledBy }}!data.{{ child.enabledBy.name }} ||{{/ if }} isLoading)}
error={ !!validation.get('{{ child.attributeType.name }}') }
helperText={ validation.get('{{ child.attributeType.name }}') }
{{# if child.onBlur }}
onBlur={ () => {
onBlur={ () => {
if (actions?.on{{ firstToUpper child.attributeType.name }}BlurAction) {
actions.on{{ firstToUpper child.attributeType.name }}BlurAction(data, storeDiff, editMode, submit);
actions.on{{ firstToUpper child.attributeType.name }}BlurAction(data, storeDiff, editMode, submit);
}
} }
} }
{{/ if }}
onChange={ (event) => {
storeDiff('{{ child.attributeType.name }}', event.target.value);
} }
error={ !!validation.get('{{ child.attributeType.name }}') }
helperText={ validation.get('{{ child.attributeType.name }}') }
InputLabelProps={ { shrink: true } }
InputProps={ {
readOnly: actions?.is{{ firstToUpper child.attributeType.name }}Readonly ? actions.is{{ firstToUpper child.attributeType.name }}Readonly(data, editMode, isLoading) : ({{ boolValue child.attributeType.isReadOnly }} || !isFormUpdateable()),
{{# if child.icon }}
startAdornment: (
<InputAdornment position="start">
<MdiIcon path="{{ child.icon.iconName }}" />
</InputAdornment>
),
{{/ if }}
...params.InputProps,
{{# if child.icon }}
startAdornment: (
<InputAdornment position="start">
<MdiIcon path="{{ child.icon.iconName }}" sx={ { mt: -2 } } />
</InputAdornment>
),
{{/ if }}
} }
select
>
{!(actions?.is{{ firstToUpper child.attributeType.name }}Required ? actions.is{{ firstToUpper child.attributeType.name }}Required(data, editMode) : ({{# if child.requiredBy }}data.{{ child.requiredBy.name }} ||{{/ if }} {{ boolValue child.attributeType.isRequired }})) && (
<MenuItem id="{{ getXMIID child }}-null" value={null as any}>
<em>{t('enumerations.{{ restParamName child.attributeType.dataType }}._null', { defaultValue: 'None' })}</em>
</MenuItem>
)}
{effective{{ firstToUpper child.attributeType.name }}Options.map((o) => (
<MenuItem key={o.id} id={o.id} value={o.value as any}>
{t(o.i18nKey, { defaultValue: o.i18nDefaultValue })}
</MenuItem>
))}
</TextField>
className={clsx({
'JUDO-viewMode': !editMode,
'JUDO-required': actions?.is{{ firstToUpper child.attributeType.name }}Required ? actions.is{{ firstToUpper child.attributeType.name }}Required(data, editMode) : {{ boolValue child.attributeType.isRequired }},
})}
/>}
/>
{{# if child.customImplementation }}
</ComponentProxy>
{{/ if }}
Expand Down

0 comments on commit 9fc96f1

Please sign in to comment.