Last active
June 10, 2025 14:55
-
-
Save ambrinchaudhary/0bbc40c78ed0e991372c523f0594bf83 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * SPDX-FileCopyrightText: (c) 2000 Liferay, Inc. https://liferay.com | |
| * SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06 | |
| */ | |
| import ClayButton, {ClayButtonWithIcon} from '@clayui/button'; | |
| import ClayForm from '@clayui/form'; | |
| import ClayIcon from '@clayui/icon'; | |
| import ClayLabel from '@clayui/label'; | |
| import ClayPopover from '@clayui/popover'; | |
| import classNames from 'classnames'; | |
| import { | |
| EVENT_TYPES as CORE_EVENT_TYPES, | |
| Layout, | |
| PagesVisitor, | |
| useConfig, | |
| useForm, | |
| useFormState, | |
| } from 'data-engine-js-components-web'; | |
| import {FieldFeedback} from 'frontend-js-components-web'; | |
| import {sub} from 'frontend-js-web'; | |
| import React, {useCallback, useEffect, useMemo, useState} from 'react'; | |
| import './FieldBase.scss'; | |
| export function updateFieldNameLocale(editingLanguageId, locale, name) { | |
| return name.replace(new RegExp(`${editingLanguageId}$`), locale); | |
| } | |
| export function normalizeInputValue(fieldType, value) { | |
| if (!value) { | |
| return ''; | |
| } | |
| if ( | |
| fieldType === 'document_library' || | |
| fieldType === 'geolocation' || | |
| fieldType === 'grid' || | |
| fieldType === 'image' || | |
| fieldType === 'select' | |
| ) { | |
| return !Object.keys(value).length ? '' : JSON.stringify(value); | |
| } | |
| return value; | |
| } | |
| const getFieldDetails = ({ | |
| errorMessage, | |
| hasError, | |
| label, | |
| required, | |
| text, | |
| tip, | |
| warningMessage, | |
| }) => { | |
| const fieldDetails = []; | |
| if (label) { | |
| fieldDetails.push(Liferay.Util.escape(label)); | |
| } | |
| if (tip) { | |
| fieldDetails.push(Liferay.Util.escape(tip)); | |
| } | |
| if (text) { | |
| fieldDetails.push(Liferay.Util.escape(text)); | |
| } | |
| if (hasError) { | |
| fieldDetails.push(Liferay.Util.escape(errorMessage)); | |
| } | |
| else { | |
| if (warningMessage) { | |
| fieldDetails.push(Liferay.Util.escape(warningMessage)); | |
| } | |
| if (required) { | |
| fieldDetails.push(Liferay.Language.get('required')); | |
| } | |
| } | |
| return fieldDetails.length ? fieldDetails.join('<br>') : false; | |
| }; | |
| const HideFieldProperty = () => { | |
| return ( | |
| <ClayLabel className="ml-1" displayType="secondary"> | |
| {Liferay.Language.get('hidden')} | |
| </ClayLabel> | |
| ); | |
| }; | |
| const LabelProperty = ({hideField, label}) => { | |
| return hideField ? <span className="text-secondary">{label}</span> : label; | |
| }; | |
| const RequiredProperty = () => { | |
| return ( | |
| <span className="ddm-label-required reference-mark"> | |
| <ClayIcon symbol="asterisk" /> | |
| </span> | |
| ); | |
| }; | |
| const FieldInformation = ({popover, tooltip}) => { | |
| return popover ? ( | |
| <Popover {...popover} /> | |
| ) : ( | |
| <span | |
| className="c-ml-2 ddm-field-information text-4 text-secondary" | |
| data-testid="tooltip" | |
| tabIndex={0} | |
| title={tooltip} | |
| > | |
| <ClayIcon symbol="question-circle-full" /> | |
| </span> | |
| ); | |
| }; | |
| const Popover = ({alignPosition, content, header, image}) => { | |
| const [isPopoverVisible, setIsPopoverVisible] = useState(false); | |
| const POPOVER_MAX_WIDTH = 256; | |
| return ( | |
| <ClayPopover | |
| alignPosition={alignPosition} | |
| closeOnClickOutside | |
| data-testid="clayPopover" | |
| disableScroll | |
| header={header} | |
| onShowChange={setIsPopoverVisible} | |
| show={isPopoverVisible} | |
| style={{maxWidth: POPOVER_MAX_WIDTH}} | |
| trigger={ | |
| <ClayButtonWithIcon | |
| aria-label={Liferay.Language.get('more-information')} | |
| className="c-ml-2 text-secondary" | |
| data-testid="tooltip" | |
| displayType="unstyled" | |
| monospaced={false} | |
| size="sm" | |
| symbol="question-circle-full" | |
| /> | |
| } | |
| > | |
| <p | |
| className="mb-4" | |
| dangerouslySetInnerHTML={{ | |
| __html: content, | |
| }} | |
| /> | |
| {image && ( | |
| <img | |
| alt={image.alt} | |
| height={image.height} | |
| src={image.src} | |
| width={image.width} | |
| /> | |
| )} | |
| </ClayPopover> | |
| ); | |
| }; | |
| const FIELDSET_REGEX = /Fieldset\d+/g; | |
| const FIELDSET_REPEAT_INDEX_REGEX = /\$(\d+)(?:#|\$|$)/g; | |
| export default function FieldBase({ | |
| accessible = true, | |
| children, | |
| displayErrors, | |
| editOnlyInDefaultLanguage, | |
| errorMessage, | |
| fieldName, | |
| fieldReference, | |
| hideField, | |
| hideEditedFlag, | |
| id, | |
| instanceId, | |
| isLocalizationSupported, | |
| itemPath, | |
| label, | |
| localizedValue = {}, | |
| localizedValueEdited, | |
| name, | |
| nestedFields, | |
| onClick, | |
| overMaximumRepetitionsLimit, | |
| popover, | |
| readOnly, | |
| repeatable, | |
| required, | |
| showLabel = true, | |
| style, | |
| text, | |
| tip, | |
| tooltip, | |
| type, | |
| valid, | |
| visible, | |
| warningMessage, | |
| }) { | |
| const {disableFieldRepetition} = useConfig(); | |
| const {editingLanguageId, pages} = useFormState(); | |
| const [disabledRepeatableButton, setDisabledRepeatableButton] = | |
| useState(false); | |
| const dispatch = useForm(); | |
| const hasError = displayErrors && errorMessage && !valid; | |
| const fieldDetails = getFieldDetails({ | |
| errorMessage, | |
| hasError, | |
| label, | |
| required, | |
| text, | |
| tip, | |
| warningMessage, | |
| }); | |
| const fieldDetailsId = `${id ?? name}_fieldDetails`; | |
| const fieldLabelId = `${id ?? name}_fieldLabel`; | |
| const hiddenTranslations = useMemo(() => { | |
| if (!localizedValue) { | |
| return; | |
| } | |
| return Object.entries(localizedValue).map(([locale, value]) => { | |
| return ( | |
| <input | |
| data-field-name={`${fieldName}${instanceId}`} | |
| data-languageid={locale} | |
| data-translated={ | |
| !!localizedValueEdited?.[editingLanguageId] | |
| } | |
| key={locale} | |
| type="hidden" | |
| value={normalizeInputValue(type, value)} | |
| {...(locale !== editingLanguageId && { | |
| name: updateFieldNameLocale( | |
| editingLanguageId, | |
| locale, | |
| name | |
| ), | |
| })} | |
| /> | |
| ); | |
| }); | |
| }, [ | |
| localizedValue, | |
| localizedValueEdited, | |
| editingLanguageId, | |
| fieldName, | |
| instanceId, | |
| name, | |
| type, | |
| ]); | |
| const nonLocalizableFieldMessage = | |
| isLocalizationSupported === undefined | |
| ? Liferay.Language.get('this-field-cannot-be-localized') | |
| : isLocalizationSupported | |
| ? Liferay.Language.get('translation-is-disabled-for-this-field') | |
| : Liferay.Language.get( | |
| 'this-field-does-not-support-translations' | |
| ); | |
| const renderLabel = | |
| (label && showLabel) || hideField || repeatable || required || tooltip; | |
| const showDisabledFieldIcon = | |
| editOnlyInDefaultLanguage && showLabel && readOnly; | |
| const showGroup = | |
| type === 'checkbox_multiple' || | |
| type === 'grid' || | |
| type === 'paragraph' || | |
| type === 'radio'; | |
| const popoverOrTooltip = !!popover || !!tooltip; | |
| const showFor = | |
| type === 'date' || | |
| type === 'date_time' || | |
| type === 'document_library' || | |
| type === 'image' || | |
| type === 'numeric' || | |
| type === 'rich_text' || | |
| type === 'search_location' || | |
| type === 'text'; | |
| const readFieldDetails = !showFor; | |
| const hasFieldDetails = | |
| accessible && fieldDetails && readFieldDetails && type !== 'select'; | |
| const accessiblePropsGroup = { | |
| ...(!renderLabel && | |
| hasFieldDetails && {'aria-labelledby': fieldDetailsId}), | |
| ...(type === 'fieldset' && {role: 'group'}), | |
| }; | |
| const accessiblePropsFields = { | |
| ...(hasFieldDetails && {'aria-labelledby': fieldDetailsId}), | |
| ...(showFor && {htmlFor: id ?? name}), | |
| ...readFieldDetails, | |
| }; | |
| const defaultRows = nestedFields?.map((field) => ({ | |
| columns: [{fields: [field], size: 12}], | |
| })); | |
| const checkRepetitions = useMemo(() => { | |
| if (repeatable && name) { | |
| const currentFieldFieldsets = name.match(FIELDSET_REGEX); | |
| const currentFieldsetRepeatIndexes = name.match( | |
| FIELDSET_REPEAT_INDEX_REGEX | |
| ); | |
| if (currentFieldsetRepeatIndexes) { | |
| currentFieldsetRepeatIndexes.pop(); | |
| } | |
| const visitor = new PagesVisitor(pages); | |
| const repeatableFields = []; | |
| visitor.visitFields((field) => { | |
| const fieldName = field.name ?? field.fieldName; | |
| const fieldFieldsets = fieldName.match(FIELDSET_REGEX); | |
| const fieldsetRepeatIndexes = fieldName.match( | |
| FIELDSET_REPEAT_INDEX_REGEX | |
| ); | |
| if (fieldsetRepeatIndexes) { | |
| fieldsetRepeatIndexes.pop(); | |
| } | |
| const isSameFieldset = | |
| currentFieldFieldsets && | |
| fieldFieldsets && | |
| currentFieldsetRepeatIndexes && | |
| fieldsetRepeatIndexes && | |
| currentFieldFieldsets.every( | |
| (fieldFieldset, index) => | |
| fieldFieldset === fieldFieldsets[index] | |
| ) && | |
| currentFieldsetRepeatIndexes.every( | |
| (fieldFieldset, index) => | |
| fieldFieldset === fieldsetRepeatIndexes[index] | |
| ); | |
| if (fieldReference === field.fieldReference && isSameFieldset) { | |
| repeatableFields.push(field); | |
| } | |
| if ( | |
| !currentFieldFieldsets && | |
| fieldReference === field.fieldReference | |
| ) { | |
| repeatableFields.push(field); | |
| } | |
| }); | |
| return repeatableFields.length; | |
| } | |
| }, [fieldReference, name, pages, repeatable]); | |
| const disableRepeatableButton = () => { | |
| setDisabledRepeatableButton(true); | |
| setTimeout(() => { | |
| setDisabledRepeatableButton(false); | |
| }, 1000); | |
| }; | |
| const translationFilterChange = useCallback( | |
| (event) => { | |
| const updateFieldVisibilityRecursive = (fieldsArray) => { | |
| let anyChildVisible = false; | |
| fieldsArray.forEach((field) => { | |
| let isCurrentFieldVisible = true; | |
| if (!field.localizable) { | |
| isCurrentFieldVisible = false; | |
| } else { | |
| if (field.type !== 'fieldset') { | |
| switch (event.option) { | |
| case 'translated': | |
| if (!field.localizedValueEdited?.[editingLanguageId]) { | |
| isCurrentFieldVisible = false; | |
| } | |
| break; | |
| case 'untranslated': | |
| if (field.localizedValueEdited?.[editingLanguageId]) { | |
| isCurrentFieldVisible = false; | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| } | |
| if (field.type === 'fieldset' && field.nestedFields) { | |
| const {anyChildVisible: nestedChildrenVisible} = updateFieldVisibilityRecursive(field.nestedFields); | |
| field.visible = nestedChildrenVisible; | |
| field.hidden = !nestedChildrenVisible; | |
| field.disabled = !nestedChildrenVisible; | |
| if (field.visible) { | |
| ... |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment