Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dynamic-layout): add no-field case spec to Dynamic Layout(item-type) #5243

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ So, `options` is fixed, and `type-options` can be changed by the user's behavior
|----------------|------|--------------|-------|-----------|--------------------|-----|----------|------|-------|------|
| root_path | O | O | O | O | O | O | O | O | O | O |
| translation_id | O | O | O | O | O | O | O | O | O | O |
| fields | X | O | O | X | O | X | X | X | X | X |
| fields | O | O | O | X | O | X | X | X | X | X |
| markdown | X | X | X | X | X | X | O | X | X | X |
| layouts | X | X | X | X | X | X | X | X | X | O |
| search | X | X | X | X | O | X | X | X | X | X |
Expand Down Expand Up @@ -246,4 +246,4 @@ interface DynamicLayoutEventListeners {


<br/>
<br/>
<br/>
Original file line number Diff line number Diff line change
@@ -1,15 +1,113 @@
<script lang="ts" setup>
import {
computed, reactive,
} from 'vue';

import PDynamicField from '@/data-display/dynamic/dynamic-field/PDynamicField.vue';
import type { DynamicFieldProps } from '@/data-display/dynamic/dynamic-field/type';
import type { DynamicField } from '@/data-display/dynamic/dynamic-field/type/field-schema';
import type { DynamicLayoutBaseProps, DynamicLayoutFieldHandler, DynamicLayoutTypeOptions } from '@/data-display/dynamic/dynamic-layout/type';
import type { ItemOptions, DynamicLayoutType } from '@/data-display/dynamic/dynamic-layout/type/layout-schema';
import { getValueByPath } from '@/data-display/dynamic/helper';
import PHeading from '@/data-display/heading/PHeading.vue';
import { DEFINITION_TABLE_STYLE_TYPE } from '@/data-display/tables/definition-table/config';
import PDefinitionTable from '@/data-display/tables/definition-table/PDefinitionTable.vue';
import type { DefinitionData, DefinitionField } from '@/data-display/tables/definition-table/type';
import { I18nConnector } from '@/translations';

// NOTE: Due to the vue version issue of mirinae, it is not possible to define a type with generics using defineProps.
interface Props extends DynamicLayoutBaseProps {
name: string;
type: DynamicLayoutType;
options: ItemOptions;
data?: any;
typeOptions?: DynamicLayoutTypeOptions;
fieldHandler?: DynamicLayoutFieldHandler;
}

const props = withDefaults(defineProps<Props>(), {
options: () => ({}) as ItemOptions,
data: () => ({}),
typeOptions: () => ({}),
fieldHandler: undefined,
});

const state = reactive({
layoutName: computed(() => (props.options.translation_id ? I18nConnector.i18n.t(props.options.translation_id) : props.name)),
isFieldEmpty: computed<boolean>(() => !props.options.fields || props.options.fields.length === 0),
isRootDataEmpty: computed<boolean>(() => !state.rootData || Object.keys(state.rootData).length === 0),
fields: computed<DefinitionField[]>(() => {
if (state.isFieldEmpty) {
if (state.isRootDataEmpty) return [];
return Object.keys(state.rootData).map((key) => ({
label: key,
name: key,
}));
}
const locale = I18nConnector.i18n.locale;
return props.options.fields.map((d) => ({
label: d.options?.translation_id ? I18nConnector.i18n.t(d.options.translation_id as string, locale) : d.name,
name: d.key,
disableCopy: !!d.options?.disable_copy,
} as DefinitionField));
}),
rootData: computed<DefinitionData>(() => {
if (props.options.root_path) {
return getValueByPath(props.data, props.options.root_path) ?? {};
}
if (Array.isArray(props.data) || typeof props.data === 'string') return {};
return props.data;
}),
loading: computed<boolean|undefined>(() => (props.typeOptions === undefined ? undefined : props.typeOptions.loading)),
timezone: computed<string>(() => props.typeOptions?.timezone || 'UTC'),
});

const getFieldData = (rootData, dataPath: string, type?: string): any => {
if (type === 'more') {
return rootData;
}
return getValueByPath(rootData, dataPath);
};

const dynamicFieldSlots = computed<Record<string, DynamicFieldProps>>(() => {
const res = {};
if (!props.options.fields) return res;

props.options.fields.forEach((ds: DynamicField, i) => {
const item: DynamicFieldProps = {
type: ds.type || 'text',
options: { ...ds.options },
extraData: { ...ds, index: i },
data: getFieldData(state.rootData, ds.key, ds.type),
};

if (item.options.translation_id) delete item.options.translation_id;

if (ds.type === 'datetime') {
item.typeOptions = { timezone: state.timezone };
} else if (ds.type === 'more') {
item.typeOptions = { displayKey: ds.key };
}

res[`data-${i}`] = item;
});

return res;
});

</script>
<template>
<div>
<p-heading v-if="layoutName"
<p-heading v-if="state.layoutName"
class="pt-8 px-4 pb-4"
heading-type="sub"
>
{{ layoutName }}
{{ state.layoutName }}
</p-heading>
<p-definition-table :fields="fields"
:data="rootData"
:style-type="options.styleType"
:loading="loading"
<p-definition-table :fields="state.fields"
:data="state.rootData"
:style-type="options.styleType ?? DEFINITION_TABLE_STYLE_TYPE.primary"
:loading="state.loading"
v-on="$listeners"
>
<template v-for="(item, slotName) of dynamicFieldSlots"
Expand All @@ -32,117 +130,3 @@
</p-definition-table>
</div>
</template>

<script lang="ts">
import {
computed, getCurrentInstance, reactive, toRefs,
} from 'vue';
import type { Vue } from 'vue/types/vue';

import PDynamicField from '@/data-display/dynamic/dynamic-field/PDynamicField.vue';
import type { DynamicFieldProps } from '@/data-display/dynamic/dynamic-field/type';
import type { DynamicField } from '@/data-display/dynamic/dynamic-field/type/field-schema';
import type { ItemDynamicLayoutProps } from '@/data-display/dynamic/dynamic-layout/templates/item/type';
import { getValueByPath } from '@/data-display/dynamic/helper';
import PHeading from '@/data-display/heading/PHeading.vue';
import PDefinitionTable from '@/data-display/tables/definition-table/PDefinitionTable.vue';
import type { DefinitionData, DefinitionField } from '@/data-display/tables/definition-table/type';

export default {
name: 'PDynamicLayoutItem',
components: {
PDynamicField,
PHeading,
PDefinitionTable,
},
props: {
name: {
type: String,
required: true,
},
options: {
type: Object,
default: () => ({}),
},
data: {
type: [Object, Array, String],
default: undefined,
},
fetchOptions: {
type: Object,
default: undefined,
},
typeOptions: {
type: Object,
default: undefined,
},
fieldHandler: {
type: Function,
default: undefined,
},
},
setup(props: ItemDynamicLayoutProps) {
const vm = getCurrentInstance()?.proxy as Vue;

const state = reactive({
layoutName: computed(() => (props.options.translation_id ? vm.$t(props.options.translation_id) : props.name)),
fields: computed<DefinitionField[]>(() => {
if (!props.options.fields) return [];
const locale = vm.$i18n.locale;
return props.options.fields.map((d) => ({
label: d.options?.translation_id ? vm.$t(d.options.translation_id as string, locale) : d.name,
name: d.key,
disableCopy: !!d.options?.disable_copy,
} as DefinitionField));
}),
rootData: computed<DefinitionData>(() => {
if (props.options.root_path) {
return getValueByPath(props.data, props.options.root_path) ?? {};
}
if (Array.isArray(props.data) || typeof props.data === 'string') return {};
return props.data;
}),
loading: computed<boolean|undefined>(() => (props.typeOptions === undefined ? undefined : props.typeOptions.loading)),
timezone: computed<string>(() => props.typeOptions?.timezone || 'UTC'),
});

const getFieldData = (rootData, dataPath: string, type?: string): any => {
if (type === 'more') {
return rootData;
}
return getValueByPath(rootData, dataPath);
};

const dynamicFieldSlots = computed(() => {
const res = {};
if (!props.options.fields) return res;

props.options.fields.forEach((ds: DynamicField, i) => {
const item: DynamicFieldProps = {
type: ds.type || 'text',
options: { ...ds.options },
extraData: { ...ds, index: i },
data: getFieldData(state.rootData, ds.key, ds.type),
};

if (item.options.translation_id) delete item.options.translation_id;

if (ds.type === 'datetime') {
item.typeOptions = { timezone: state.timezone };
} else if (ds.type === 'more') {
item.typeOptions = { displayKey: ds.key };
}

res[`data-${i}`] = item;
});

return res;
});

return {
...toRefs(state),
dynamicFieldSlots,
};
},
};
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ import * as itemStories from './item.stories';
<Meta of={itemStories} />

# Item Type
This is an item type implemented with the `p-definition-table` component. The layout is dynamically generated according to the `options.fields` defined in the schema. For the options of `options.fields`, please refer to the `Dynamic Field` component.

If `options.fields` is not provided, the layout is generated as a key-value pair of 1 depth by default. Therefore, it can be used when the data is in the following code.

```json
{
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
```


<br/>
<br/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,22 @@ export interface DynamicLayoutTypeOptions {
sortable?: boolean;
}

export interface DynamicLayoutProps<
SchemaOptions = DynamicLayoutOptions,
> {
// NOTE: Due to the version issue of mirinae vue, it is not possible to define a type with generics using defineProps, so DynamicLayoutBaseProps is added and used.
export interface DynamicLayoutBaseProps {
name: string;
type: DynamicLayoutType;
options: SchemaOptions;
data?: any;
fetchOptions?: DynamicLayoutFetchOptions;
typeOptions?: DynamicLayoutTypeOptions;
fieldHandler?: DynamicLayoutFieldHandler;
}

export interface DynamicLayoutProps<
SchemaOptions = DynamicLayoutOptions,
> extends DynamicLayoutBaseProps {
options: SchemaOptions;
}

export interface DynamicLayoutFieldExtraData extends DynamicField {
index: number;
}
Expand Down
Loading