fix(ui): tooltip positioning issues (#6439)
This commit is contained in:
@@ -84,14 +84,14 @@ const _RichText: React.FC<
|
|||||||
width,
|
width,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<FieldLabel
|
||||||
|
CustomLabel={CustomLabel}
|
||||||
|
label={label}
|
||||||
|
required={required}
|
||||||
|
{...(labelProps || {})}
|
||||||
|
/>
|
||||||
<div className={`${baseClass}__wrap`}>
|
<div className={`${baseClass}__wrap`}>
|
||||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||||
<FieldLabel
|
|
||||||
CustomLabel={CustomLabel}
|
|
||||||
label={label}
|
|
||||||
required={required}
|
|
||||||
{...(labelProps || {})}
|
|
||||||
/>
|
|
||||||
<ErrorBoundary fallbackRender={fallbackRender} onReset={() => {}}>
|
<ErrorBoundary fallbackRender={fallbackRender} onReset={() => {}}>
|
||||||
<LexicalProvider
|
<LexicalProvider
|
||||||
editorConfig={editorConfig}
|
editorConfig={editorConfig}
|
||||||
|
|||||||
@@ -106,10 +106,6 @@
|
|||||||
line-height: unset;
|
line-height: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__error-wrap {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__rows {
|
&__rows {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
@import '../scss/styles.scss';
|
@import '../scss/styles.scss';
|
||||||
|
|
||||||
.rich-text-lexical {
|
.rich-text-lexical {
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.errorBoundary {
|
.errorBoundary {
|
||||||
pre {
|
pre {
|
||||||
text-wrap: unset;
|
text-wrap: unset;
|
||||||
|
|||||||
@@ -315,14 +315,14 @@ const RichTextField: React.FC<
|
|||||||
width,
|
width,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<FieldLabel
|
||||||
|
CustomLabel={CustomLabel}
|
||||||
|
label={label}
|
||||||
|
required={required}
|
||||||
|
{...(labelProps || {})}
|
||||||
|
/>
|
||||||
<div className={`${baseClass}__wrap`}>
|
<div className={`${baseClass}__wrap`}>
|
||||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||||
<FieldLabel
|
|
||||||
CustomLabel={CustomLabel}
|
|
||||||
label={label}
|
|
||||||
required={required}
|
|
||||||
{...(labelProps || {})}
|
|
||||||
/>
|
|
||||||
<Slate
|
<Slate
|
||||||
editor={editor}
|
editor={editor}
|
||||||
key={JSON.stringify({ initialValue, path })} // makes sure slate is completely re-rendered when initialValue changes, bypassing the slate-internal value memoization. That way, external changes to the form will update the editor
|
key={JSON.stringify({ initialValue, path })} // makes sure slate is completely re-rendered when initialValue changes, bypassing the slate-internal value memoization. That way, external changes to the form will update the editor
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
background-color: var(--theme-elevation-800);
|
background-color: var(--theme-elevation-800);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 2;
|
z-index: 3;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
padding: base(0.2) base(0.4);
|
padding: base(0.2) base(0.4);
|
||||||
color: var(--theme-elevation-0);
|
color: var(--theme-elevation-0);
|
||||||
@@ -52,8 +52,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
&--position-top {
|
&--position-top {
|
||||||
bottom: 100%;
|
top: calc(var(--base) * -0.6 - 13px);
|
||||||
transform: translate3d(-50%, calc(var(--caret-size) * -1), 0);
|
transform: translate3d(-50%, calc(var(--caret-size) * -1), 0);
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
@@ -62,8 +63,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
&--position-bottom {
|
&--position-bottom {
|
||||||
top: 100%;
|
bottom: calc(var(--base) * -0.6 - 13px);
|
||||||
transform: translate3d(-50%, var(--caret-size), 0);
|
transform: translate3d(-50%, var(--caret-size), 0);
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ export type Props = {
|
|||||||
className?: string
|
className?: string
|
||||||
delay?: number
|
delay?: number
|
||||||
show?: boolean
|
show?: boolean
|
||||||
|
/**
|
||||||
|
* If the tooltip position should not change depending on if the toolbar is outside the boundingRef. @default false
|
||||||
|
*/
|
||||||
|
staticPositioning?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Tooltip: React.FC<Props> = (props) => {
|
export const Tooltip: React.FC<Props> = (props) => {
|
||||||
@@ -21,6 +25,7 @@ export const Tooltip: React.FC<Props> = (props) => {
|
|||||||
className,
|
className,
|
||||||
delay = 350,
|
delay = 350,
|
||||||
show: showFromProps = true,
|
show: showFromProps = true,
|
||||||
|
staticPositioning = false,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const [show, setShow] = React.useState(showFromProps)
|
const [show, setShow] = React.useState(showFromProps)
|
||||||
@@ -28,11 +33,14 @@ export const Tooltip: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
const getTitleAttribute = (content) => (typeof content === 'string' ? content : '')
|
const getTitleAttribute = (content) => (typeof content === 'string' ? content : '')
|
||||||
|
|
||||||
const [ref, intersectionEntry] = useIntersect({
|
const [ref, intersectionEntry] = useIntersect(
|
||||||
root: boundingRef?.current || null,
|
{
|
||||||
rootMargin: '-145px 0px 0px 100px',
|
root: boundingRef?.current || null,
|
||||||
threshold: 0,
|
rootMargin: '-145px 0px 0px 100px',
|
||||||
})
|
threshold: 0,
|
||||||
|
},
|
||||||
|
staticPositioning,
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let timerId: NodeJS.Timeout
|
let timerId: NodeJS.Timeout
|
||||||
@@ -52,22 +60,25 @@ export const Tooltip: React.FC<Props> = (props) => {
|
|||||||
}, [showFromProps, delay])
|
}, [showFromProps, delay])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (staticPositioning) return
|
||||||
setPosition(intersectionEntry?.isIntersecting ? 'top' : 'bottom')
|
setPosition(intersectionEntry?.isIntersecting ? 'top' : 'bottom')
|
||||||
}, [intersectionEntry])
|
}, [intersectionEntry, staticPositioning])
|
||||||
|
|
||||||
|
// The first aside is always on top. The purpose of that is that it can reliably be used for the interaction observer (as it's not moving around), to calculate the position of the actual tooltip.
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<aside
|
{!staticPositioning && (
|
||||||
aria-hidden="true"
|
<aside
|
||||||
className={['tooltip', className, `tooltip--caret-${alignCaret}`, 'tooltip--position-top']
|
aria-hidden="true"
|
||||||
.filter(Boolean)
|
className={['tooltip', className, `tooltip--caret-${alignCaret}`, 'tooltip--position-top']
|
||||||
.join(' ')}
|
.filter(Boolean)
|
||||||
ref={ref}
|
.join(' ')}
|
||||||
title={getTitleAttribute(children)}
|
ref={ref}
|
||||||
>
|
style={{ opacity: '0' }}
|
||||||
<div className="tooltip-content">{children}</div>
|
>
|
||||||
</aside>
|
<div className="tooltip-content">{children}</div>
|
||||||
|
</aside>
|
||||||
|
)}
|
||||||
<aside
|
<aside
|
||||||
className={[
|
className={[
|
||||||
'tooltip',
|
'tooltip',
|
||||||
|
|||||||
@@ -54,10 +54,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__error-wrap {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__row-header {
|
&__row-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -202,11 +202,7 @@ export const _ArrayField: React.FC<ArrayFieldProps> = (props) => {
|
|||||||
.join(' ')}
|
.join(' ')}
|
||||||
id={`field-${path.replace(/\./g, '__')}`}
|
id={`field-${path.replace(/\./g, '__')}`}
|
||||||
>
|
>
|
||||||
{showError && (
|
{showError && <FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />}
|
||||||
<div className={`${baseClass}__error-wrap`}>
|
|
||||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<header className={`${baseClass}__header`}>
|
<header className={`${baseClass}__header`}>
|
||||||
<div className={`${baseClass}__header-wrap`}>
|
<div className={`${baseClass}__header-wrap`}>
|
||||||
<div className={`${baseClass}__header-content`}>
|
<div className={`${baseClass}__header-content`}>
|
||||||
|
|||||||
@@ -75,10 +75,6 @@
|
|||||||
line-height: unset;
|
line-height: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__error-wrap {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__rows {
|
&__rows {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -215,11 +215,7 @@ const _BlocksField: React.FC<BlocksFieldProps> = (props) => {
|
|||||||
.join(' ')}
|
.join(' ')}
|
||||||
id={`field-${path.replace(/\./g, '__')}`}
|
id={`field-${path.replace(/\./g, '__')}`}
|
||||||
>
|
>
|
||||||
{showError && (
|
{showError && <FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />}
|
||||||
<div className={`${baseClass}__error-wrap`}>
|
|
||||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<header className={`${baseClass}__header`}>
|
<header className={`${baseClass}__header`}>
|
||||||
<div className={`${baseClass}__header-wrap`}>
|
<div className={`${baseClass}__header-wrap`}>
|
||||||
<div className={`${baseClass}__heading-with-error`}>
|
<div className={`${baseClass}__heading-with-error`}>
|
||||||
|
|||||||
@@ -10,10 +10,6 @@
|
|||||||
margin-bottom: 0.2em;
|
margin-bottom: 0.2em;
|
||||||
max-width: fit-content;
|
max-width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__error-wrap {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-input {
|
.checkbox-input {
|
||||||
|
|||||||
@@ -97,9 +97,7 @@ const CheckboxField: React.FC<CheckboxFieldProps> = (props) => {
|
|||||||
width,
|
width,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={`${baseClass}__error-wrap`}>
|
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
|
||||||
</div>
|
|
||||||
<CheckboxInput
|
<CheckboxInput
|
||||||
AfterInput={AfterInput}
|
AfterInput={AfterInput}
|
||||||
BeforeInput={BeforeInput}
|
BeforeInput={BeforeInput}
|
||||||
|
|||||||
@@ -87,14 +87,14 @@ const CodeField: React.FC<CodeFieldProps> = (props) => {
|
|||||||
width,
|
width,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
|
||||||
<FieldLabel
|
<FieldLabel
|
||||||
CustomLabel={CustomLabel}
|
CustomLabel={CustomLabel}
|
||||||
label={label}
|
label={label}
|
||||||
required={required}
|
required={required}
|
||||||
{...(labelProps || {})}
|
{...(labelProps || {})}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div className={`${fieldBaseClass}__wrap`}>
|
||||||
|
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||||
{BeforeInput}
|
{BeforeInput}
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
defaultLanguage={prismToMonacoLanguageMap[language] || language}
|
defaultLanguage={prismToMonacoLanguageMap[language] || language}
|
||||||
|
|||||||
@@ -50,21 +50,23 @@ export const ConfirmPassword: React.FC<ConfirmPasswordFieldProps> = (props) => {
|
|||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ')}
|
.join(' ')}
|
||||||
>
|
>
|
||||||
<FieldError path={path} />
|
|
||||||
<FieldLabel
|
<FieldLabel
|
||||||
htmlFor="field-confirm-password"
|
htmlFor="field-confirm-password"
|
||||||
label={t('authentication:confirmPassword')}
|
label={t('authentication:confirmPassword')}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<input
|
<div className={`${fieldBaseClass}__wrap`}>
|
||||||
autoComplete="off"
|
<FieldError path={path} />
|
||||||
disabled={!!disabled}
|
<input
|
||||||
id="field-confirm-password"
|
autoComplete="off"
|
||||||
name="confirm-password"
|
disabled={!!disabled}
|
||||||
onChange={setValue}
|
id="field-confirm-password"
|
||||||
type="password"
|
name="confirm-password"
|
||||||
value={(value as string) || ''}
|
onChange={setValue}
|
||||||
/>
|
type="password"
|
||||||
|
value={(value as string) || ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
@import '../../scss/styles.scss';
|
@import '../../scss/styles.scss';
|
||||||
|
|
||||||
.date-time-field {
|
|
||||||
&__error-wrap {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
html[data-theme='light'] {
|
html[data-theme='light'] {
|
||||||
.date-time-field {
|
.date-time-field {
|
||||||
&--has-error {
|
&--has-error {
|
||||||
|
|||||||
@@ -90,16 +90,14 @@ const DateTimeField: React.FC<DateFieldProps> = (props) => {
|
|||||||
width,
|
width,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={`${baseClass}__error-wrap`}>
|
|
||||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
|
||||||
</div>
|
|
||||||
<FieldLabel
|
<FieldLabel
|
||||||
CustomLabel={CustomLabel}
|
CustomLabel={CustomLabel}
|
||||||
label={label}
|
label={label}
|
||||||
required={required}
|
required={required}
|
||||||
{...(labelProps || {})}
|
{...(labelProps || {})}
|
||||||
/>
|
/>
|
||||||
<div className={`${baseClass}__input-wrapper`} id={`field-${path.replace(/\./g, '__')}`}>
|
<div className={`${fieldBaseClass}__wrap`} id={`field-${path.replace(/\./g, '__')}`}>
|
||||||
|
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||||
{BeforeInput}
|
{BeforeInput}
|
||||||
<DatePickerField
|
<DatePickerField
|
||||||
{...datePickerProps}
|
{...datePickerProps}
|
||||||
|
|||||||
@@ -77,14 +77,14 @@ const EmailField: React.FC<EmailFieldProps> = (props) => {
|
|||||||
width,
|
width,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
|
||||||
<FieldLabel
|
<FieldLabel
|
||||||
CustomLabel={CustomLabel}
|
CustomLabel={CustomLabel}
|
||||||
label={label}
|
label={label}
|
||||||
required={required}
|
required={required}
|
||||||
{...(labelProps || {})}
|
{...(labelProps || {})}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div className={`${fieldBaseClass}__wrap`}>
|
||||||
|
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||||
{BeforeInput}
|
{BeforeInput}
|
||||||
<input
|
<input
|
||||||
autoComplete={autoComplete}
|
autoComplete={autoComplete}
|
||||||
|
|||||||
@@ -130,14 +130,15 @@ const JSONFieldComponent: React.FC<JSONFieldProps> = (props) => {
|
|||||||
width,
|
width,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
|
||||||
<FieldLabel
|
<FieldLabel
|
||||||
CustomLabel={CustomLabel}
|
CustomLabel={CustomLabel}
|
||||||
label={label}
|
label={label}
|
||||||
required={required}
|
required={required}
|
||||||
{...(labelProps || {})}
|
{...(labelProps || {})}
|
||||||
/>
|
/>
|
||||||
<div>
|
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||||
|
<div className={`${fieldBaseClass}__wrap`}>
|
||||||
|
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||||
{BeforeInput}
|
{BeforeInput}
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
defaultLanguage="json"
|
defaultLanguage="json"
|
||||||
|
|||||||
@@ -161,62 +161,64 @@ const NumberFieldComponent: React.FC<NumberFieldProps> = (props) => {
|
|||||||
required={required}
|
required={required}
|
||||||
{...(labelProps || {})}
|
{...(labelProps || {})}
|
||||||
/>
|
/>
|
||||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
<div className={`${fieldBaseClass}__wrap`}>
|
||||||
{hasMany ? (
|
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||||
<ReactSelect
|
{hasMany ? (
|
||||||
className={`field-${path.replace(/\./g, '__')}`}
|
<ReactSelect
|
||||||
disabled={readOnly}
|
className={`field-${path.replace(/\./g, '__')}`}
|
||||||
filterOption={(_, rawInput) => {
|
|
||||||
// eslint-disable-next-line no-restricted-globals
|
|
||||||
const isOverHasMany = Array.isArray(value) && value.length >= maxRows
|
|
||||||
return isNumber(rawInput) && !isOverHasMany
|
|
||||||
}}
|
|
||||||
isClearable
|
|
||||||
isCreatable
|
|
||||||
isMulti
|
|
||||||
isSortable
|
|
||||||
noOptionsMessage={() => {
|
|
||||||
const isOverHasMany = Array.isArray(value) && value.length >= maxRows
|
|
||||||
if (isOverHasMany) {
|
|
||||||
return t('validation:limitReached', { max: maxRows, value: value.length + 1 })
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}}
|
|
||||||
// numberOnly
|
|
||||||
onChange={handleHasManyChange}
|
|
||||||
options={[]}
|
|
||||||
placeholder={t('general:enterAValue')}
|
|
||||||
showError={showError}
|
|
||||||
value={valueToRender as Option[]}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
{BeforeInput}
|
|
||||||
<input
|
|
||||||
disabled={readOnly}
|
disabled={readOnly}
|
||||||
id={`field-${path.replace(/\./g, '__')}`}
|
filterOption={(_, rawInput) => {
|
||||||
max={max}
|
// eslint-disable-next-line no-restricted-globals
|
||||||
min={min}
|
const isOverHasMany = Array.isArray(value) && value.length >= maxRows
|
||||||
name={path}
|
return isNumber(rawInput) && !isOverHasMany
|
||||||
onChange={handleChange}
|
|
||||||
onWheel={(e) => {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-expect-error
|
|
||||||
e.target.blur()
|
|
||||||
}}
|
}}
|
||||||
placeholder={getTranslation(placeholder, i18n)}
|
isClearable
|
||||||
step={step}
|
isCreatable
|
||||||
type="number"
|
isMulti
|
||||||
value={typeof value === 'number' ? value : ''}
|
isSortable
|
||||||
|
noOptionsMessage={() => {
|
||||||
|
const isOverHasMany = Array.isArray(value) && value.length >= maxRows
|
||||||
|
if (isOverHasMany) {
|
||||||
|
return t('validation:limitReached', { max: maxRows, value: value.length + 1 })
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}}
|
||||||
|
// numberOnly
|
||||||
|
onChange={handleHasManyChange}
|
||||||
|
options={[]}
|
||||||
|
placeholder={t('general:enterAValue')}
|
||||||
|
showError={showError}
|
||||||
|
value={valueToRender as Option[]}
|
||||||
/>
|
/>
|
||||||
{AfterInput}
|
) : (
|
||||||
</div>
|
<div>
|
||||||
)}
|
{BeforeInput}
|
||||||
{CustomDescription !== undefined ? (
|
<input
|
||||||
CustomDescription
|
disabled={readOnly}
|
||||||
) : (
|
id={`field-${path.replace(/\./g, '__')}`}
|
||||||
<FieldDescription {...(descriptionProps || {})} />
|
max={max}
|
||||||
)}
|
min={min}
|
||||||
|
name={path}
|
||||||
|
onChange={handleChange}
|
||||||
|
onWheel={(e) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-expect-error
|
||||||
|
e.target.blur()
|
||||||
|
}}
|
||||||
|
placeholder={getTranslation(placeholder, i18n)}
|
||||||
|
step={step}
|
||||||
|
type="number"
|
||||||
|
value={typeof value === 'number' ? value : ''}
|
||||||
|
/>
|
||||||
|
{AfterInput}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{CustomDescription !== undefined ? (
|
||||||
|
CustomDescription
|
||||||
|
) : (
|
||||||
|
<FieldDescription {...(descriptionProps || {})} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,22 +67,25 @@ const PasswordField: React.FC<PasswordFieldProps> = (props) => {
|
|||||||
width,
|
width,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
|
||||||
<FieldLabel
|
<FieldLabel
|
||||||
CustomLabel={CustomLabel}
|
CustomLabel={CustomLabel}
|
||||||
label={label}
|
label={label}
|
||||||
required={required}
|
required={required}
|
||||||
{...(labelProps || {})}
|
{...(labelProps || {})}
|
||||||
/>
|
/>
|
||||||
<input
|
<div className={`${fieldBaseClass}__wrap`}>
|
||||||
autoComplete={autoComplete}
|
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||||
disabled={formProcessing || disabled}
|
|
||||||
id={`field-${path.replace(/\./g, '__')}`}
|
<input
|
||||||
name={path}
|
autoComplete={autoComplete}
|
||||||
onChange={setValue}
|
disabled={formProcessing || disabled}
|
||||||
type="password"
|
id={`field-${path.replace(/\./g, '__')}`}
|
||||||
value={(value as string) || ''}
|
name={path}
|
||||||
/>
|
onChange={setValue}
|
||||||
|
type="password"
|
||||||
|
value={(value as string) || ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
@import '../../scss/styles.scss';
|
@import '../../scss/styles.scss';
|
||||||
|
|
||||||
.radio-group {
|
.radio-group {
|
||||||
&__error-wrap {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--layout-horizontal {
|
&--layout-horizontal {
|
||||||
ul {
|
ul {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -99,57 +99,58 @@ const RadioGroupField: React.FC<RadioFieldProps> = (props) => {
|
|||||||
width,
|
width,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={`${baseClass}__error-wrap`}>
|
|
||||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
|
||||||
</div>
|
|
||||||
<FieldLabel
|
<FieldLabel
|
||||||
CustomLabel={CustomLabel}
|
CustomLabel={CustomLabel}
|
||||||
label={label}
|
label={label}
|
||||||
required={required}
|
required={required}
|
||||||
{...(labelProps || {})}
|
{...(labelProps || {})}
|
||||||
/>
|
/>
|
||||||
<ul className={`${baseClass}--group`} id={`field-${path.replace(/\./g, '__')}`}>
|
<div className={`${fieldBaseClass}__wrap`}>
|
||||||
{options.map((option) => {
|
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||||
let optionValue = ''
|
|
||||||
|
|
||||||
if (optionIsObject(option)) {
|
<ul className={`${baseClass}--group`} id={`field-${path.replace(/\./g, '__')}`}>
|
||||||
optionValue = option.value
|
{options.map((option) => {
|
||||||
} else {
|
let optionValue = ''
|
||||||
optionValue = option
|
|
||||||
}
|
|
||||||
|
|
||||||
const isSelected = String(optionValue) === String(value)
|
if (optionIsObject(option)) {
|
||||||
|
optionValue = option.value
|
||||||
|
} else {
|
||||||
|
optionValue = option
|
||||||
|
}
|
||||||
|
|
||||||
const id = `field-${path}-${optionValue}${uuid ? `-${uuid}` : ''}`
|
const isSelected = String(optionValue) === String(value)
|
||||||
|
|
||||||
return (
|
const id = `field-${path}-${optionValue}${uuid ? `-${uuid}` : ''}`
|
||||||
<li key={`${path} - ${optionValue}`}>
|
|
||||||
<Radio
|
|
||||||
id={id}
|
|
||||||
isSelected={isSelected}
|
|
||||||
onChange={() => {
|
|
||||||
if (typeof onChangeFromProps === 'function') {
|
|
||||||
onChangeFromProps(optionValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!readOnly) {
|
return (
|
||||||
setValue(optionValue)
|
<li key={`${path} - ${optionValue}`}>
|
||||||
}
|
<Radio
|
||||||
}}
|
id={id}
|
||||||
option={optionIsObject(option) ? option : { label: option, value: option }}
|
isSelected={isSelected}
|
||||||
path={path}
|
onChange={() => {
|
||||||
readOnly={readOnly}
|
if (typeof onChangeFromProps === 'function') {
|
||||||
uuid={uuid}
|
onChangeFromProps(optionValue)
|
||||||
/>
|
}
|
||||||
</li>
|
|
||||||
)
|
if (!readOnly) {
|
||||||
})}
|
setValue(optionValue)
|
||||||
</ul>
|
}
|
||||||
{CustomDescription !== undefined ? (
|
}}
|
||||||
CustomDescription
|
option={optionIsObject(option) ? option : { label: option, value: option }}
|
||||||
) : (
|
path={path}
|
||||||
<FieldDescription {...(descriptionProps || {})} />
|
readOnly={readOnly}
|
||||||
)}
|
uuid={uuid}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
{CustomDescription !== undefined ? (
|
||||||
|
CustomDescription
|
||||||
|
) : (
|
||||||
|
<FieldDescription {...(descriptionProps || {})} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -471,116 +471,119 @@ const RelationshipField: React.FC<RelationshipFieldProps> = (props) => {
|
|||||||
width,
|
width,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
|
||||||
<FieldLabel
|
<FieldLabel
|
||||||
CustomLabel={CustomLabel}
|
CustomLabel={CustomLabel}
|
||||||
label={label}
|
label={label}
|
||||||
required={required}
|
required={required}
|
||||||
{...(labelProps || {})}
|
{...(labelProps || {})}
|
||||||
/>
|
/>
|
||||||
{!errorLoading && (
|
<div className={`${fieldBaseClass}__wrap`}>
|
||||||
<div className={`${baseClass}__wrap`}>
|
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||||
<ReactSelect
|
|
||||||
backspaceRemovesValue={!drawerIsOpen}
|
{!errorLoading && (
|
||||||
components={{
|
<div className={`${baseClass}__wrap`}>
|
||||||
MultiValueLabel,
|
<ReactSelect
|
||||||
SingleValue,
|
backspaceRemovesValue={!drawerIsOpen}
|
||||||
}}
|
components={{
|
||||||
customProps={{
|
MultiValueLabel,
|
||||||
disableKeyDown: drawerIsOpen,
|
SingleValue,
|
||||||
disableMouseDown: drawerIsOpen,
|
}}
|
||||||
onSave,
|
customProps={{
|
||||||
setDrawerIsOpen,
|
disableKeyDown: drawerIsOpen,
|
||||||
}}
|
disableMouseDown: drawerIsOpen,
|
||||||
disabled={readOnly || formProcessing || drawerIsOpen}
|
onSave,
|
||||||
filterOption={enableWordBoundarySearch ? filterOption : undefined}
|
setDrawerIsOpen,
|
||||||
isLoading={isLoading}
|
}}
|
||||||
isMulti={hasMany}
|
disabled={readOnly || formProcessing || drawerIsOpen}
|
||||||
isSortable={isSortable}
|
filterOption={enableWordBoundarySearch ? filterOption : undefined}
|
||||||
onChange={
|
isLoading={isLoading}
|
||||||
!readOnly
|
isMulti={hasMany}
|
||||||
? (selected) => {
|
isSortable={isSortable}
|
||||||
if (selected === null) {
|
onChange={
|
||||||
setValue(hasMany ? [] : null)
|
!readOnly
|
||||||
} else if (hasMany) {
|
? (selected) => {
|
||||||
setValue(
|
if (selected === null) {
|
||||||
selected
|
setValue(hasMany ? [] : null)
|
||||||
? selected.map((option) => {
|
} else if (hasMany) {
|
||||||
if (hasMultipleRelations) {
|
setValue(
|
||||||
return {
|
selected
|
||||||
relationTo: option.relationTo,
|
? selected.map((option) => {
|
||||||
value: option.value,
|
if (hasMultipleRelations) {
|
||||||
|
return {
|
||||||
|
relationTo: option.relationTo,
|
||||||
|
value: option.value,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return option.value
|
return option.value
|
||||||
})
|
})
|
||||||
: null,
|
: null,
|
||||||
)
|
)
|
||||||
} else if (hasMultipleRelations) {
|
} else if (hasMultipleRelations) {
|
||||||
setValue({
|
setValue({
|
||||||
relationTo: selected.relationTo,
|
relationTo: selected.relationTo,
|
||||||
value: selected.value,
|
value: selected.value,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
setValue(selected.value)
|
setValue(selected.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
: undefined
|
||||||
: undefined
|
}
|
||||||
}
|
onInputChange={(newSearch) => handleInputChange(newSearch, value)}
|
||||||
onInputChange={(newSearch) => handleInputChange(newSearch, value)}
|
onMenuClose={() => {
|
||||||
onMenuClose={() => {
|
menuIsOpen.current = false
|
||||||
menuIsOpen.current = false
|
}}
|
||||||
}}
|
onMenuOpen={() => {
|
||||||
onMenuOpen={() => {
|
menuIsOpen.current = true
|
||||||
menuIsOpen.current = true
|
|
||||||
|
|
||||||
if (!hasLoadedFirstPageRef.current) {
|
if (!hasLoadedFirstPageRef.current) {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
|
void getResults({
|
||||||
|
lastLoadedPage: {},
|
||||||
|
onSuccess: () => {
|
||||||
|
hasLoadedFirstPageRef.current = true
|
||||||
|
setIsLoading(false)
|
||||||
|
},
|
||||||
|
value: initialValue,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onMenuScrollToBottom={() => {
|
||||||
void getResults({
|
void getResults({
|
||||||
lastLoadedPage: {},
|
lastFullyLoadedRelation,
|
||||||
onSuccess: () => {
|
lastLoadedPage,
|
||||||
hasLoadedFirstPageRef.current = true
|
search,
|
||||||
setIsLoading(false)
|
sort: false,
|
||||||
},
|
|
||||||
value: initialValue,
|
value: initialValue,
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}}
|
|
||||||
onMenuScrollToBottom={() => {
|
|
||||||
void getResults({
|
|
||||||
lastFullyLoadedRelation,
|
|
||||||
lastLoadedPage,
|
|
||||||
search,
|
|
||||||
sort: false,
|
|
||||||
value: initialValue,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
options={options}
|
|
||||||
showError={showError}
|
|
||||||
value={valueToRender ?? null}
|
|
||||||
/>
|
|
||||||
{!readOnly && allowCreate && (
|
|
||||||
<AddNewRelation
|
|
||||||
{...{
|
|
||||||
dispatchOptions,
|
|
||||||
hasMany,
|
|
||||||
options,
|
|
||||||
path,
|
|
||||||
relationTo,
|
|
||||||
setValue,
|
|
||||||
value,
|
|
||||||
}}
|
}}
|
||||||
|
options={options}
|
||||||
|
showError={showError}
|
||||||
|
value={valueToRender ?? null}
|
||||||
/>
|
/>
|
||||||
)}
|
{!readOnly && allowCreate && (
|
||||||
</div>
|
<AddNewRelation
|
||||||
)}
|
{...{
|
||||||
{errorLoading && <div className={`${baseClass}__error-loading`}>{errorLoading}</div>}
|
dispatchOptions,
|
||||||
{CustomDescription !== undefined ? (
|
hasMany,
|
||||||
CustomDescription
|
options,
|
||||||
) : (
|
path,
|
||||||
<FieldDescription {...(descriptionProps || {})} />
|
relationTo,
|
||||||
)}
|
setValue,
|
||||||
|
value,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{errorLoading && <div className={`${baseClass}__error-loading`}>{errorLoading}</div>}
|
||||||
|
{CustomDescription !== undefined ? (
|
||||||
|
CustomDescription
|
||||||
|
) : (
|
||||||
|
<FieldDescription {...(descriptionProps || {})} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,14 +149,15 @@ const SelectField: React.FC<SelectFieldProps> = (props) => {
|
|||||||
width,
|
width,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
|
||||||
<FieldLabel
|
<FieldLabel
|
||||||
CustomLabel={CustomLabel}
|
CustomLabel={CustomLabel}
|
||||||
label={label}
|
label={label}
|
||||||
required={required}
|
required={required}
|
||||||
{...(labelProps || {})}
|
{...(labelProps || {})}
|
||||||
/>
|
/>
|
||||||
<div>
|
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||||
|
<div className={`${fieldBaseClass}__wrap`}>
|
||||||
|
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||||
{BeforeInput}
|
{BeforeInput}
|
||||||
<ReactSelect
|
<ReactSelect
|
||||||
disabled={readOnly}
|
disabled={readOnly}
|
||||||
|
|||||||
@@ -60,61 +60,64 @@ export const TextInput: React.FC<TextInputProps> = (props) => {
|
|||||||
width,
|
width,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
|
||||||
<FieldLabel
|
<FieldLabel
|
||||||
CustomLabel={CustomLabel}
|
CustomLabel={CustomLabel}
|
||||||
label={label}
|
label={label}
|
||||||
required={required}
|
required={required}
|
||||||
{...(labelProps || {})}
|
{...(labelProps || {})}
|
||||||
/>
|
/>
|
||||||
{hasMany ? (
|
<div className={`${fieldBaseClass}__wrap`}>
|
||||||
<ReactSelect
|
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||||
className={`field-${path.replace(/\./g, '__')}`}
|
|
||||||
disabled={readOnly}
|
{hasMany ? (
|
||||||
// prevent adding additional options if maxRows is reached
|
<ReactSelect
|
||||||
filterOption={() =>
|
className={`field-${path.replace(/\./g, '__')}`}
|
||||||
!maxRows ? true : !(Array.isArray(value) && maxRows && value.length >= maxRows)
|
|
||||||
}
|
|
||||||
isClearable
|
|
||||||
isCreatable
|
|
||||||
isMulti
|
|
||||||
isSortable
|
|
||||||
noOptionsMessage={() => {
|
|
||||||
const isOverHasMany = Array.isArray(value) && value.length >= maxRows
|
|
||||||
if (isOverHasMany) {
|
|
||||||
return t('validation:limitReached', { max: maxRows, value: value.length + 1 })
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}}
|
|
||||||
onChange={onChange}
|
|
||||||
options={[]}
|
|
||||||
placeholder={t('general:enterAValue')}
|
|
||||||
showError={showError}
|
|
||||||
value={valueToRender}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
{BeforeInput}
|
|
||||||
<input
|
|
||||||
data-rtl={rtl}
|
|
||||||
disabled={readOnly}
|
disabled={readOnly}
|
||||||
id={`field-${path?.replace(/\./g, '__')}`}
|
// prevent adding additional options if maxRows is reached
|
||||||
name={path}
|
filterOption={() =>
|
||||||
|
!maxRows ? true : !(Array.isArray(value) && maxRows && value.length >= maxRows)
|
||||||
|
}
|
||||||
|
isClearable
|
||||||
|
isCreatable
|
||||||
|
isMulti
|
||||||
|
isSortable
|
||||||
|
noOptionsMessage={() => {
|
||||||
|
const isOverHasMany = Array.isArray(value) && value.length >= maxRows
|
||||||
|
if (isOverHasMany) {
|
||||||
|
return t('validation:limitReached', { max: maxRows, value: value.length + 1 })
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onKeyDown={onKeyDown}
|
options={[]}
|
||||||
placeholder={getTranslation(placeholder, i18n)}
|
placeholder={t('general:enterAValue')}
|
||||||
ref={inputRef}
|
showError={showError}
|
||||||
type="text"
|
value={valueToRender}
|
||||||
value={value || ''}
|
|
||||||
/>
|
/>
|
||||||
{AfterInput}
|
) : (
|
||||||
</div>
|
<div>
|
||||||
)}
|
{BeforeInput}
|
||||||
{CustomDescription !== undefined ? (
|
<input
|
||||||
CustomDescription
|
data-rtl={rtl}
|
||||||
) : (
|
disabled={readOnly}
|
||||||
<FieldDescription {...(descriptionProps || {})} />
|
id={`field-${path?.replace(/\./g, '__')}`}
|
||||||
)}
|
name={path}
|
||||||
|
onChange={onChange}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
placeholder={getTranslation(placeholder, i18n)}
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
value={value || ''}
|
||||||
|
/>
|
||||||
|
{AfterInput}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{CustomDescription !== undefined ? (
|
||||||
|
CustomDescription
|
||||||
|
) : (
|
||||||
|
<FieldDescription {...(descriptionProps || {})} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,36 +54,38 @@ export const TextareaInput: React.FC<TextAreaInputProps> = (props) => {
|
|||||||
width,
|
width,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
|
||||||
<FieldLabel
|
<FieldLabel
|
||||||
CustomLabel={CustomLabel}
|
CustomLabel={CustomLabel}
|
||||||
label={label}
|
label={label}
|
||||||
required={required}
|
required={required}
|
||||||
{...(labelProps || {})}
|
{...(labelProps || {})}
|
||||||
/>
|
/>
|
||||||
{BeforeInput}
|
<div className={`${fieldBaseClass}__wrap`}>
|
||||||
<label className="textarea-outer" htmlFor={`field-${path.replace(/\./g, '__')}`}>
|
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||||
<div className="textarea-inner">
|
{BeforeInput}
|
||||||
<div className="textarea-clone" data-value={value || placeholder || ''} />
|
<label className="textarea-outer" htmlFor={`field-${path.replace(/\./g, '__')}`}>
|
||||||
<textarea
|
<div className="textarea-inner">
|
||||||
className="textarea-element"
|
<div className="textarea-clone" data-value={value || placeholder || ''} />
|
||||||
data-rtl={rtl}
|
<textarea
|
||||||
disabled={readOnly}
|
className="textarea-element"
|
||||||
id={`field-${path.replace(/\./g, '__')}`}
|
data-rtl={rtl}
|
||||||
name={path}
|
disabled={readOnly}
|
||||||
onChange={onChange}
|
id={`field-${path.replace(/\./g, '__')}`}
|
||||||
placeholder={getTranslation(placeholder, i18n)}
|
name={path}
|
||||||
rows={rows}
|
onChange={onChange}
|
||||||
value={value || ''}
|
placeholder={getTranslation(placeholder, i18n)}
|
||||||
/>
|
rows={rows}
|
||||||
</div>
|
value={value || ''}
|
||||||
</label>
|
/>
|
||||||
{AfterInput}
|
</div>
|
||||||
{CustomDescription !== undefined ? (
|
</label>
|
||||||
CustomDescription
|
{AfterInput}
|
||||||
) : (
|
{CustomDescription !== undefined ? (
|
||||||
<FieldDescription {...(descriptionProps || {})} />
|
CustomDescription
|
||||||
)}
|
) : (
|
||||||
|
<FieldDescription {...(descriptionProps || {})} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,56 +133,59 @@ export const UploadInput: React.FC<UploadInputProps> = (props) => {
|
|||||||
width,
|
width,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FieldError CustomError={CustomError} {...(errorProps || {})} />
|
|
||||||
<FieldLabel
|
<FieldLabel
|
||||||
CustomLabel={CustomLabel}
|
CustomLabel={CustomLabel}
|
||||||
label={label}
|
label={label}
|
||||||
required={required}
|
required={required}
|
||||||
{...(labelProps || {})}
|
{...(labelProps || {})}
|
||||||
/>
|
/>
|
||||||
{collection?.upload && (
|
<div className={`${fieldBaseClass}__wrap`}>
|
||||||
<React.Fragment>
|
<FieldError CustomError={CustomError} {...(errorProps || {})} />
|
||||||
{fileDoc && !missingFile && (
|
|
||||||
<FileDetails
|
{collection?.upload && (
|
||||||
collectionSlug={relationTo}
|
<React.Fragment>
|
||||||
doc={fileDoc}
|
{fileDoc && !missingFile && (
|
||||||
handleRemove={
|
<FileDetails
|
||||||
readOnly
|
collectionSlug={relationTo}
|
||||||
? undefined
|
doc={fileDoc}
|
||||||
: () => {
|
handleRemove={
|
||||||
onChange(null)
|
readOnly
|
||||||
}
|
? undefined
|
||||||
}
|
: () => {
|
||||||
uploadConfig={collection.upload}
|
onChange(null)
|
||||||
/>
|
}
|
||||||
)}
|
}
|
||||||
{(!fileDoc || missingFile) && (
|
uploadConfig={collection.upload}
|
||||||
<div className={`${baseClass}__wrap`}>
|
/>
|
||||||
<div className={`${baseClass}__buttons`}>
|
)}
|
||||||
<DocumentDrawerToggler className={`${baseClass}__toggler`} disabled={readOnly}>
|
{(!fileDoc || missingFile) && (
|
||||||
<Button buttonStyle="secondary" disabled={readOnly} el="div">
|
<div className={`${baseClass}__wrap`}>
|
||||||
{t('fields:uploadNewLabel', {
|
<div className={`${baseClass}__buttons`}>
|
||||||
label: getTranslation(collection.labels.singular, i18n),
|
<DocumentDrawerToggler className={`${baseClass}__toggler`} disabled={readOnly}>
|
||||||
})}
|
<Button buttonStyle="secondary" disabled={readOnly} el="div">
|
||||||
</Button>
|
{t('fields:uploadNewLabel', {
|
||||||
</DocumentDrawerToggler>
|
label: getTranslation(collection.labels.singular, i18n),
|
||||||
<ListDrawerToggler className={`${baseClass}__toggler`} disabled={readOnly}>
|
})}
|
||||||
<Button buttonStyle="secondary" disabled={readOnly} el="div">
|
</Button>
|
||||||
{t('fields:chooseFromExisting')}
|
</DocumentDrawerToggler>
|
||||||
</Button>
|
<ListDrawerToggler className={`${baseClass}__toggler`} disabled={readOnly}>
|
||||||
</ListDrawerToggler>
|
<Button buttonStyle="secondary" disabled={readOnly} el="div">
|
||||||
|
{t('fields:chooseFromExisting')}
|
||||||
|
</Button>
|
||||||
|
</ListDrawerToggler>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
{CustomDescription !== undefined ? (
|
||||||
{CustomDescription !== undefined ? (
|
CustomDescription
|
||||||
CustomDescription
|
) : (
|
||||||
) : (
|
<FieldDescription {...(descriptionProps || {})} />
|
||||||
<FieldDescription {...(descriptionProps || {})} />
|
)}
|
||||||
)}
|
</React.Fragment>
|
||||||
</React.Fragment>
|
)}
|
||||||
)}
|
{!readOnly && <DocumentDrawer onSave={onSave} />}
|
||||||
{!readOnly && <DocumentDrawer onSave={onSave} />}
|
{!readOnly && <ListDrawer onSelect={onSelect} />}
|
||||||
{!readOnly && <ListDrawer onSelect={onSelect} />}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
.field-error.tooltip {
|
.field-error.tooltip {
|
||||||
font-family: var(--font-body);
|
font-family: var(--font-body);
|
||||||
top: 0;
|
|
||||||
bottom: auto;
|
|
||||||
left: auto;
|
left: auto;
|
||||||
max-width: 75%;
|
max-width: 75%;
|
||||||
right: calc(var(--base) * 0.5);
|
right: calc(var(--base) * 0.5);
|
||||||
@@ -12,5 +10,6 @@
|
|||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
border-top-color: var(--theme-error-500);
|
border-top-color: var(--theme-error-500);
|
||||||
|
border-bottom-color: var(--theme-error-500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ const DefaultFieldError: React.FC<ErrorProps> = (props) => {
|
|||||||
const message = messageFromProps || errorMessage
|
const message = messageFromProps || errorMessage
|
||||||
const showMessage = showErrorFromProps || (hasSubmitted && valid === false)
|
const showMessage = showErrorFromProps || (hasSubmitted && valid === false)
|
||||||
|
|
||||||
if (showMessage) {
|
if (showMessage && message?.length) {
|
||||||
return (
|
return (
|
||||||
<Tooltip alignCaret={alignCaret} className={baseClass} delay={0}>
|
<Tooltip alignCaret={alignCaret} className={baseClass} delay={0} staticPositioning>
|
||||||
{message}
|
{message}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,8 +11,13 @@
|
|||||||
--spacing-field: 0;
|
--spacing-field: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.field-type__wrap {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
& > .field-type {
|
& > .field-type {
|
||||||
margin-bottom: var(--spacing-field);
|
margin-bottom: var(--spacing-field);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
&[type='hidden'] {
|
&[type='hidden'] {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export const useIntersect = (
|
|||||||
const [node, setNode] = useState(null)
|
const [node, setNode] = useState(null)
|
||||||
|
|
||||||
const observer = useRef(
|
const observer = useRef(
|
||||||
typeof window !== 'undefined' && 'IntersectionObserver' in window
|
typeof window !== 'undefined' && 'IntersectionObserver' in window && !disable
|
||||||
? new window.IntersectionObserver(([ent]) => updateEntry(ent), {
|
? new window.IntersectionObserver(([ent]) => updateEntry(ent), {
|
||||||
root,
|
root,
|
||||||
rootMargin,
|
rootMargin,
|
||||||
|
|||||||
Reference in New Issue
Block a user