Skip to content

Commit

Permalink
Add OneOfControlRenderer to vue-vanilla
Browse files Browse the repository at this point in the history
  • Loading branch information
butzist committed Jun 16, 2023
1 parent df94269 commit f5a4dd1
Show file tree
Hide file tree
Showing 6 changed files with 317 additions and 1 deletion.
203 changes: 203 additions & 0 deletions packages/vue/vue-vanilla/src/complex/OneOfControlRenderer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
<template>
<div v-if="control.visible">
<combinator-properties
:schema="control.schema"
combinator-keyword="oneOf"
:path="path"
/>

<control-wrapper
v-bind="controlWrapper"
:styles="styles"
:is-focused="isFocused"
:applied-options="appliedOptions"
>
<select
:id="control.id + '-input'"
:class="styles.control.select"
:value="selectIndex"
:disabled="!control.enabled"
:autofocus="appliedOptions.focus"
@change="handleSelectChange"
@focus="isFocused = true"
@blur="isFocused = false"
>
<option
v-for="optionElement in indexedOneOfRenderInfos"
:key="optionElement.index"
:value="optionElement.index"
:label="optionElement.label"
:class="styles.control.option"
></option>
</select>
</control-wrapper>

<dispatch-renderer
v-if="selectedIndex !== undefined && selectedIndex !== null"
:schema="indexedOneOfRenderInfos[selectedIndex].schema"
:uischema="indexedOneOfRenderInfos[selectedIndex].uischema"
:path="control.path"
:renderers="control.renderers"
:cells="control.cells"
:enabled="control.enabled"
/>

<dialog ref="dialog" :class="styles.dialog.root">
<h1 :class="styles.dialog.title">
{{ t('form.clear.title', 'Clear form?') }}
</h1>

<p :class="styles.dialog.body">
{{
t(
'form.clear.text',
'Your data will be cleared. Do you want to proceed?'
)
}}
</p>

<div :class="styles.dialog.actions">
<button :onclick="onCancel" :class="styles.dialog.buttonSecondary">
{{ t('form.clear.cancel', 'No') }}
</button>
<button
ref="confirm"
:onclick="onConfirm"
:class="styles.dialog.buttonPrimary"
>
{{ t('form.clear.confirm', 'Yes') }}
</button>
</div>
</dialog>
</div>
</template>

<script lang="ts">
import {
CombinatorSubSchemaRenderInfo,
ControlElement,
createCombinatorRenderInfos,
createDefaultValue,
isOneOfControl,
JsonFormsRendererRegistryEntry,
rankWith,
} from '@jsonforms/core';
import {
DispatchRenderer,
rendererProps,
RendererProps,
useJsonFormsOneOfControl,
} from '@jsonforms/vue';
import isEmpty from 'lodash/isEmpty';
import { defineComponent, nextTick, ref } from 'vue';
import { useTranslator, useVanillaControl } from '../util';
import { ControlWrapper } from '../controls';
//import { CombinatorProperties } from './components';
const controlRenderer = defineComponent({
name: 'OneOfSelectRenderer',
components: {
ControlWrapper,
DispatchRenderer,
//CombinatorProperties,
},
props: {
...rendererProps<ControlElement>(),
},
setup(props: RendererProps<ControlElement>) {
const input = useJsonFormsOneOfControl(props);
const control = input.control.value;
const selectedIndex = ref(control.indexOfFittingSchema);
const selectIndex = ref(selectedIndex.value);
const newSelectedIndex = ref(0);
const t = useTranslator();
const dialog = ref<HTMLDialogElement>(null);
const confirm = ref<HTMLElement>(null);
return {
...useVanillaControl(input),
selectedIndex,
selectIndex,
newSelectedIndex,
dialog,
confirm,
t,
};
},
computed: {
indexedOneOfRenderInfos(): (CombinatorSubSchemaRenderInfo & {
index: number;
})[] {
const result = createCombinatorRenderInfos(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.control.schema.oneOf!,
this.control.rootSchema,
'oneOf',
this.control.uischema,
this.control.path,
this.control.uischemas
);
return result
.filter((info) => info.uischema)
.map((info, index) => ({ ...info, index: index }));
},
},
methods: {
handleSelectChange(event: Event): void {
const target = event.target as any;
this.selectIndex = target.value;
if (this.control.enabled && !isEmpty(this.control.data)) {
this.showDialog();
nextTick(() => {
this.newSelectedIndex = this.selectIndex;
// revert the selection while the dialog is open
this.selectIndex = this.selectedIndex;
this.confirm.focus();
});
} else {
nextTick(() => {
this.selectedIndex = this.selectIndex;
});
}
},
showDialog(): void {
this.dialog.showModal();
},
closeDialog(): void {
this.dialog.close();
},
onConfirm(): void {
this.newSelection();
this.closeDialog();
},
onCancel(): void {
this.newSelectedIndex = this.selectedIndex;
this.closeDialog();
},
newSelection(): void {
this.handleChange(
this.path,
this.newSelectedIndex !== undefined && this.newSelectedIndex !== null
? createDefaultValue(
this.indexedOneOfRenderInfos[this.newSelectedIndex].schema
)
: {}
);
this.selectIndex = this.newSelectedIndex;
this.selectedIndex = this.newSelectedIndex;
},
},
});
export default controlRenderer;
export const entry: JsonFormsRendererRegistryEntry = {
renderer: controlRenderer,
tester: rankWith(3, isOneOfControl),
};
</script>

Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<template>
<div v-if="isLayoutWithElements">
<dispatch-renderer
:schema="otherProps"
:path="path"
:uischema="foundUISchema"
/>
</div>
</template>

<script lang="ts">
import { Generate, JsonSchema, Layout, UISchemaElement } from '@jsonforms/core';
import omit from 'lodash/omit';
import { PropType, defineComponent } from 'vue';
import { DispatchRenderer } from '@jsonforms/vue';
interface CombinatorProps {
schema: JsonSchema;
combinatorKeyword: 'oneOf' | 'anyOf' | 'allOf';
path: string;
}
export default defineComponent({
name: 'CombinatorProperties',
components: {
DispatchRenderer,
},
props: {
schema: {
type: Object as PropType<JsonSchema>,
required: true,
},
combinatorKeyword: {
type: String as PropType<'oneOf' | 'anyOf' | 'allOf'>,
required: true,
},
path: {
type: String,
required: true,
},
},
setup(props: CombinatorProps) {
const otherProps: JsonSchema = omit(
props.schema,
props.combinatorKeyword
) as JsonSchema;
const foundUISchema: UISchemaElement = Generate.uiSchema(
otherProps,
'VerticalLayout'
);
const isLayout = (uischema: UISchemaElement): uischema is Layout =>
Object.prototype.hasOwnProperty.call(uischema, 'elements');
let isLayoutWithElements = false;
if (foundUISchema !== null && isLayout(foundUISchema)) {
isLayoutWithElements = foundUISchema.elements.length > 0;
}
return {
otherProps,
foundUISchema,
isLayoutWithElements,
};
},
});
</script>
7 changes: 6 additions & 1 deletion packages/vue/vue-vanilla/src/complex/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
export { default as ObjectControlRenderer } from './ObjectControlRenderer.vue';
export { default as OneOfControlRenderer } from './OneOfControlRenderer.vue';

import { entry as objectControlRendererEntry } from './ObjectControlRenderer.vue';
import { entry as oneOfControlRendererEntry } from './OneOfControlRenderer.vue';

export const complexRenderers = [objectControlRendererEntry];
export const complexRenderers = [
objectControlRendererEntry,
oneOfControlRendererEntry,
];
8 changes: 8 additions & 0 deletions packages/vue/vue-vanilla/src/styles/defaultStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,12 @@ export const defaultStyles: Styles = {
label: {
root: 'label-element',
},
dialog: {
root: 'dialog-root',
title: 'dialog-title',
body: 'dialog-body',
actions: 'dialog-actions',
buttonPrimary: 'dialog-button-primary',
buttonSecondary: 'dialog-button-secondary',
},
};
9 changes: 9 additions & 0 deletions packages/vue/vue-vanilla/src/styles/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const createEmptyStyles = (): Styles => ({
group: {},
arrayList: {},
label: {},
dialog: {},
});

export interface Styles {
Expand All @@ -24,6 +25,14 @@ export interface Styles {
select?: string;
option?: string;
};
dialog: {
root?: string;
title?: string;
body?: string;
actions?: string;
buttonPrimary?: string;
buttonSecondary?: string;
};
verticalLayout: {
root?: string;
item?: string;
Expand Down
24 changes: 24 additions & 0 deletions packages/vue/vue-vanilla/src/util/composition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
composePaths,
findUISchema,
getFirstPrimitiveProp,
JsonFormsSubStates,
Resolve,
} from '@jsonforms/core';

Expand Down Expand Up @@ -47,6 +48,29 @@ export const useVanillaControl = <
};
};

export const useTranslator = () => {
const jsonforms = inject<JsonFormsSubStates>('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
*/
Expand Down

0 comments on commit f5a4dd1

Please sign in to comment.