Not possible to use on repeater fields
dnlmzw opened this issue · 10 comments
Thanks for taking the time to write this plugin.
I don't see any way to use this on repeater fields, as the reference provided is only on a "document"-level.
Did I miss anything?
+1
attempting to use this within an nested object
{
title: "Select video type",
name: "selectVideoType",
type: "string",
options: {
list: [
{
title: "Url",
value: "url",
// icon: AiOutlineBgColors,
},
{
title: "File",
value: "file",
// icon: BsCardImage,
},
],
layout: "dropdown",
},
},
{
title: "Video Url",
name: "videoUrl",
type: "string",
inputComponent: ConditionalField,
options: {
condition: object => object.selectVideoType === 'url'
}
},
simply hides the field even if the select field is changed
Tested with boolean and does not work :'(
@jamesryan-dev I'm using the "Hull"-starter for Sanity and found out that it comes with conditional fields built-in, which takes into consideration the context of the field:
https://github.com/ndimatteo/HULL/blob/main/studio/components/conditional-field.js
You can see how it's applied here:
https://github.com/ndimatteo/HULL/blob/main/studio/schemas/modules/hero.js
Thank you so much for sharing - I'll look into this and see if i can emulate within my current Sanity ecosystem
Also Hull looks so cool will deff be using this for future projects =}
I found that the hotspot
-field on image
type fields was breaking with the script from HULL, so I ended up using this script and copy pasting in the getContext
-part from HULL. It now does exactly what I need. It looks as follows:
import React from 'react'
import {
withDocument,
withValuePath,
FormBuilderInput
} from 'part:@sanity/form-builder'
class ConditionalField extends React.PureComponent {
fieldRef = React.createRef()
focus() {
if (this.fieldRef?.current) {
this.fieldRef.current.focus()
}
}
getContext(level = 1) {
// gets value path from withValuePath HOC, and applies path to document
// we remove the last 𝑥 elements from the valuePath
const valuePath = this.props.getValuePath()
const removeItems = -Math.abs(level)
return valuePath.length + removeItems <= 0
? this.props.document
: valuePath.slice(0, removeItems).reduce((context, current) => {
// basic string path
if (typeof current === 'string') {
return context[current] || {}
}
// object path with key used on arrays
if (
typeof current === 'object' &&
Array.isArray(context) &&
current._key
) {
return (
context.filter(
item => item._key && item._key === current._key
)[0] || {}
)
}
}, this.props.document)
}
render() {
const {
document,
type,
value,
level,
focusPath,
onFocus,
onBlur,
onChange,
getValuePath,
markers = [],
presence = [],
compareValue
} = this.props
const shouldRenderField = type?.options?.condition
const renderField = shouldRenderField
? shouldRenderField(document, this.getContext.bind(this))
: true
if (!renderField) {
return <div style={{ marginBottom: '-32px' }} />
}
const { type: _unusedType, inputComponent, ...usableType } = type
return (
<FormBuilderInput
level={level}
type={usableType}
value={value}
onChange={patchEvent => onChange(patchEvent)}
path={getValuePath()}
focusPath={focusPath}
onFocus={onFocus}
onBlur={onBlur}
ref={this.fieldRef}
markers={markers}
presence={presence}
compareValue={compareValue}
/>
)
}
}
export default withValuePath(withDocument(ConditionalField))
This totally breaks on nested fields - I did some black magic to get it to work with one level of nesting.
i.e.
conditional field 1
conditional field 2
Hey everyone! Thanks for chipping in and pointing to possible solutions :)
I have a clear view of how this API could look like and even how to implement it, but I stopped working on this plugin as the Sanity team has voiced they're currently working on a native solution.
Don't want to compete with native, so I'll archive this repository and package as soon as that lands
Hey everyone
I had 2 issues while using this:
- validation markers not working as expected
- values for conditional fields polluting the resulting document data even when the field is not displayed
I haven't had time to put up a PR (also probably doesn't make sense if this is not going to be maintained), but I think the following snippet fixes those 2 issues, so leaving it here in case it's useful to anyone:
import React from 'react';
import { withDocument, withValuePath, FormBuilderInput } from 'part:@sanity/form-builder';
import { PatchEvent, unset } from 'part:@sanity/form-builder/patch-event';
class ConditionalField extends React.PureComponent<any> {
fieldRef: any = React.createRef();
focus() {
if (this.fieldRef?.current) {
this.fieldRef.current.focus();
}
}
getContext(level = 1) {
// gets value path from withValuePath HOC, and applies path to document
// we remove the last 𝑥 elements from the valuePath
const valuePath = this.props.getValuePath();
const removeItems = -Math.abs(level);
return valuePath.length + removeItems <= 0
? this.props.document
: valuePath.slice(0, removeItems).reduce((context: any, current: any) => {
// basic string path
if (typeof current === 'string') {
return context[current] || {};
}
// object path with key used on arrays
if (typeof current === 'object' && Array.isArray(context) && current._key) {
return context.filter((item) => item._key && item._key === current._key)[0] || {};
}
}, this.props.document);
}
render() {
const { type, document, value, onChange } = this.props;
const shouldRenderField = type?.options?.condition;
const renderField = shouldRenderField
? shouldRenderField(document, this.getContext.bind(this))
: true;
if (!renderField) {
// Clear the value when hiding the input
if (value) onChange(PatchEvent.from(unset()));
return null;
}
const {
level,
focusPath,
onFocus,
onBlur,
getValuePath,
markers = [],
presence = [],
compareValue,
} = this.props;
const { type: _unusedType, inputComponent, ...usableType } = type;
return (
<FormBuilderInput
level={level}
type={usableType}
value={value}
onChange={(patchEvent: any) => onChange(patchEvent)}
path={getValuePath()}
focusPath={focusPath}
onFocus={onFocus}
onBlur={onBlur}
ref={this.fieldRef}
markers={markers.map((marker: any) => ({
...marker,
// Pass the right path for validation markers
path: getValuePath(),
}))}
presence={presence}
compareValue={compareValue}
/>
);
}
}
export default withValuePath(withDocument(ConditionalField));
¡Hola @juannorris! Thanks a ton for this :)
Given so many people are using this library, I decided to push a v1 with these features you outlined, which also solves this issue. I'm not removing values by default as you suggested because it could be destructive. Instead, use the clearOnHidden value to do that
I strongly recommend upgrading to v1 (take a look at the docs) and benefiting from a rounder API and smoother UX for your editors
Awesome, thanks @hdoro!