File "utils.js"
Full Path: /home/fresvfqn/waterdamagerestorationandrepairsmithtown.com/wp-content/plugins/surerank/src/apps/admin-onboarding/utils.js
File size: 7.09 KB
MIME-type: text/x-java
Charset: utf-8
import { cn } from '@Functions/utils';
import { forwardRef, Fragment, useState } from '@wordpress/element';
import { Select, Input, Checkbox, Label, Text } from '@bsf/force-ui';
import { __ } from '@wordpress/i18n';
import MediaPreview from '../admin-components/media-preview';
import { createMediaFrame } from '@/global/utils/utils';
// Track which elements have already been initially focused
const initiallyFocusedElements = new WeakMap();
export const focusHelper = ( el ) => {
if (
el &&
typeof el.focus === 'function' &&
! initiallyFocusedElements.get( el )
) {
// Use setTimeout to ensure the element is fully rendered and avoid focusing during re-renders
setTimeout( () => {
el.focus();
}, 0 );
initiallyFocusedElements.set( el, true );
}
};
export const renderField = ( field, fieldValue, onChange, error, option ) => {
const {
label,
name,
type,
options: fieldOptions,
defaultValue,
width = 'full',
combobox = false,
size = 'md',
description = null,
} = field;
const handleClick = ( event ) => {
//prevent native file input from opening
event.preventDefault();
const frame = createMediaFrame( {
title: field?.label,
button: {
text: field?.label,
},
multiple: false,
} );
frame.on( 'select', () => {
const attachment = frame
.state()
.get( 'selection' )
.first()
.toJSON();
const attachmentId = attachment.id || null;
const fileName = attachment.filename || null;
const fileUrl = attachment.url || null;
const fileType = attachment?.type || null;
const fileSize = attachment?.filesizeInBytes || null;
onChange( {
attachment_id: attachmentId,
name: fileName,
size: fileSize,
url: fileUrl,
type: fileType,
} );
frame.close();
} );
frame.open();
};
let extraProps = {};
if ( option && 'initialFocus' in option && option.initialFocus ) {
extraProps = {
ref: focusHelper,
};
}
// If field contains description render in a common variable
const renderDescription = description ? (
<Text size={ 14 } weight={ 400 } color="help">
{ description }
</Text>
) : null;
let children = null;
switch ( type ) {
case 'select':
children = (
<SelectWithCustomSearch
key={ name }
id={ name }
searchFn={ field.searchFn }
size={ size }
combobox={ combobox }
label={ label }
value={ fieldValue }
defaultValue={ defaultValue }
onChange={ onChange }
options={ fieldOptions }
by={ field.by || 'value' }
{ ...extraProps }
/>
);
break;
case 'selectGroup':
children = (
<SelectWithGroup
key={ name }
id={ name }
label={ label }
value={ fieldValue }
options={ fieldOptions }
onChange={ onChange }
{ ...extraProps }
/>
);
break;
case 'checkbox':
children = (
<div className="space-y-1.5" key={ name }>
<Checkbox
size={ size }
id={ name }
name={ name }
label={ {
heading: label,
} }
checked={ fieldValue }
onChange={ onChange }
/>
{ error && (
<Label variant="error" className="ml-8">
{ __( 'This is required', 'surerank' ) }
</Label>
) }
</div>
);
break;
case 'file':
const mediaPreviewProps = {
imageUrl:
typeof fieldValue === 'string' ? fieldValue : undefined,
imageId:
typeof fieldValue === 'object'
? fieldValue?.attachment_id
: undefined,
onRemove: () => onChange( null ),
};
children = (
<div key={ name } className="space-y-1.5">
<Input
id={ name }
label={ label }
type="file"
size="md"
onClick={ handleClick }
/>
{ renderDescription }
{ fieldValue && fieldValue.attachment_id !== 0 && (
<div className="pt-0.5">
<MediaPreview { ...mediaPreviewProps } />
</div>
) }
</div>
);
break;
default:
children = (
<Fragment key={ name }>
<div className="space-y-1.5">
<Input
id={ name }
size={ size }
name={ name }
label={ label }
type={ type }
value={ fieldValue }
onChange={ onChange }
autoComplete="off"
error={ error }
{ ...extraProps }
/>
{ error && <Label variant="error">{ error }</Label> }
</div>
</Fragment>
);
}
return (
<div
className={ cn(
width !== 'full' ? 'grow w-full md:w-5/12' : 'w-full'
) }
>
{ children }
</div>
);
};
const SelectWithCustomSearch = forwardRef(
(
{
id,
searchFn,
size,
combobox,
label,
defaultValue,
value,
onChange,
options = [],
by = 'value',
},
ref
) => {
const [ selectOptions, setSelectOptions ] = useState( options );
const handleSearch = searchFn
? async ( keyword ) => {
const results = await searchFn( keyword );
setSelectOptions( results );
}
: undefined;
const findSelectedValue = selectOptions.find(
( option ) => option.value === value
);
return (
<Select
size={ size }
by={ by }
combobox={ combobox }
value={ findSelectedValue || defaultValue?.value }
onChange={ onChange }
{ ...( combobox &&
typeof searchFn === 'function' && {
searchFn: handleSearch,
} ) }
>
<Select.Button
id={ id }
label={ label }
render={ ( selectedValue ) =>
selectedValue?.label || defaultValue?.label
}
type="button"
ref={ ref }
/>
<Select.Portal id="surerank-root">
<Select.Options>
{ selectOptions.length > 0 ? (
selectOptions.map( ( option ) => (
<Select.Option
key={ option.value }
value={ option }
selected={
findSelectedValue?.value ===
option.value ||
String( defaultValue?.value ) ===
String( option.value )
}
>
{ option.label }
</Select.Option>
) )
) : (
<Select.Option disabled>
{ __( 'No options available', 'surerank' ) }
</Select.Option>
) }
</Select.Options>
</Select.Portal>
</Select>
);
}
);
const formatWithSpace = ( value ) => {
if ( ! value || typeof value !== 'string' ) {
return value;
}
return value
.replace( /([a-z])([A-Z])/g, '$1 $2' )
.replace( /([A-Z])([A-Z][a-z])/g, '$1 $2' );
};
const SelectWithGroup = forwardRef(
( { id, label, value, onChange, options = [] }, ref ) => {
return (
<Select size="md" value={ value } onChange={ onChange }>
<Select.Button
id={ id }
label={ label }
render={ ( selectedValue ) =>
formatWithSpace( selectedValue )
}
type="button"
ref={ ref }
/>
<Select.Portal id="surerank-root">
<Select.Options>
{ options.map( ( group, index ) => (
<Select.OptionGroup
key={ index }
label={ group.label }
>
{ Object.entries( group.options ).map(
( [ key, valueLabel ] ) => (
<Select.Option
key={ key }
value={ key }
>
{ formatWithSpace( valueLabel ) }
</Select.Option>
)
) }
</Select.OptionGroup>
) ) }
</Select.Options>
</Select.Portal>
</Select>
);
}
);