import { composePaths, computeLabel, getFirstPrimitiveProp, isDescriptionHidden, JsonFormsSubStates, Resolve, } from '@jsonforms/core'; import cloneDeep from 'lodash/cloneDeep'; import debounce from 'lodash/debounce'; import merge from 'lodash/merge'; import get from 'lodash/get'; import isPlainObject from 'lodash/isPlainObject'; import { useStyles } from '../styles'; import { computed, ComputedRef, inject, ref } from '../vue'; import Ajv from 'ajv'; import { provide } from '@vue/composition-api'; const useControlAppliedOptions = (input: I) => { return computed(() => merge( {}, cloneDeep(input.control.value.config), cloneDeep(input.control.value.uischema.options) ) ); }; const useComputedLabel = ( input: I, appliedOptions: ComputedRef ) => { return computed((): string => { return computeLabel( input.control.value.label, input.control.value.required, !!appliedOptions.value?.hideRequiredAsterisk ); }); }; /** * Adds styles, isFocused, appliedOptions and onChange */ export const useVuetifyControl = < I extends { control: any; handleChange: any } >( input: I, adaptValue: (target: any) => any = (v) => v, debounceWait?: number ) => { const changeEmitter = typeof debounceWait === 'number' ? debounce(input.handleChange, debounceWait) : input.handleChange; const onChange = (value: any) => { changeEmitter(input.control.value.path, adaptValue(value)); }; const appliedOptions = useControlAppliedOptions(input); const isFocused = ref(false); const persistentHint = (): boolean => { return !isDescriptionHidden( input.control.value.visible, input.control.value.description, isFocused.value, !!appliedOptions.value?.showUnfocusedDescription ); }; const computedLabel = useComputedLabel(input, appliedOptions); const controlWrapper = computed(() => { const { id, description, errors, label, visible, required } = input.control.value; return { id, description, errors, label, visible, required }; }); const styles = useStyles(input.control.value.uischema); const vuetifyProps = (path: string) => { const props = get(appliedOptions.value?.vuetify, path); return props && isPlainObject(props) ? props : {}; }; return { ...input, styles, isFocused, appliedOptions, controlWrapper, onChange, vuetifyProps, persistentHint, computedLabel, }; }; export const useTranslator = () => { const jsonforms = inject('jsonforms'); if (!jsonforms) { throw new Error( "'jsonforms couldn't be injected. Are you within JSON Forms?" ); } if (!jsonforms.i18n || !jsonforms.i18n.translate) { throw new Error( "'jsonforms i18n couldn't be injected. Are you within JSON Forms?" ); } const translate = computed(() => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return jsonforms.i18n!.translate!; }); return translate; }; /** * Adds styles and appliedOptions */ export const useVuetifyLayout = (input: I) => { const appliedOptions = computed(() => { return merge( {}, cloneDeep(input.layout.value.config), cloneDeep(input.layout.value.uischema.options) ); }); const vuetifyProps = (path: string) => { const props = get(appliedOptions.value?.vuetify, path); return props && isPlainObject(props) ? props : {}; }; return { ...input, styles: useStyles(input.layout.value.uischema), appliedOptions, vuetifyProps, }; }; /** * Adds styles, appliedOptions and childUiSchema */ export const useVuetifyArrayControl = ( input: I ) => { const appliedOptions = useControlAppliedOptions(input); const computedLabel = useComputedLabel(input, appliedOptions); const childLabelForIndex = (index: number) => { const childLabelProp = input.control.value.uischema.options?.childLabelProp ?? getFirstPrimitiveProp(input.control.value.schema); if (!childLabelProp) { return `${index}`; } const labelValue = Resolve.data( input.control.value.data, composePaths(`${index}`, childLabelProp) ); if ( labelValue === undefined || labelValue === null || Number.isNaN(labelValue) ) { return ''; } return `${labelValue}`; }; return { ...input, styles: useStyles(input.control.value.uischema), appliedOptions, childLabelForIndex, computedLabel, }; }; /** * Adds styles and appliedOptions */ export const useVuetifyBasicControl = ( input: I ) => { const appliedOptions = useControlAppliedOptions(input); const vuetifyProps = (path: string) => { const props = get(appliedOptions.value?.vuetify, path); return props && isPlainObject(props) ? props : {}; }; return { ...input, styles: useStyles(input.control.value.uischema), appliedOptions, vuetifyProps, }; }; /** * Extracts Ajv from JSON Forms */ export const useAjv = () => { const jsonforms = inject('jsonforms'); if (!jsonforms) { throw new Error( "'jsonforms' couldn't be injected. Are you within JSON Forms?" ); } // should always exist return jsonforms.core?.ajv as Ajv; }; export interface NestedInfo { level: number; parentElement?: 'array' | 'object'; } export const useNested = (element: false | 'array' | 'object'): NestedInfo => { const nestedInfo = inject('jsonforms.nestedInfo', { level: 0 }); if (element) { provide('jsonforms.nestedInfo', { level: nestedInfo.level + 1, parentElement: element, }); } return nestedInfo; };