Update/tweak groups (#7229)

* Split detail/raw groups, tweak accordion

* Add update groups migration
This commit is contained in:
Rijk van Zanten
2021-08-06 00:27:26 +02:00
committed by GitHub
parent faa71c7595
commit 627d843aed
9 changed files with 254 additions and 116 deletions

View File

@@ -0,0 +1,35 @@
import { Knex } from 'knex';
export async function up(knex: Knex): Promise<void> {
const groups = await knex.select('*').from('directus_fields').where({ interface: 'group-standard' });
const raw = [];
const detail = [];
for (const group of groups) {
const options = typeof group.options === 'string' ? JSON.parse(group.options) : group.options || {};
if (options.showHeader === true) {
detail.push(group);
} else {
raw.push(group);
}
}
for (const field of raw) {
await knex('directus_fields').update({ interface: 'group-raw' }).where({ id: field.id });
}
for (const field of detail) {
await knex('directus_fields').update({ interface: 'group-detail' }).where({ id: field.id });
}
}
export async function down(knex: Knex): Promise<void> {
await knex('directus_fields')
.update({
interface: 'group-standard',
})
.where({ interface: 'group-detail' })
.orWhere({ interface: 'group-raw' });
}

View File

@@ -1,5 +1,5 @@
<template>
<v-item-group v-model="selection" scope="group-accordion" class="group-accordion" :multiple="multiple">
<v-item-group v-model="selection" scope="group-accordion" class="group-accordion" :multiple="accordionMode === false">
<accordion-section
v-for="accordionField in rootFields"
:key="accordionField.field"
@@ -14,7 +14,7 @@
:loading="loading"
:validation-errors="validationErrors"
:group="field.meta.id"
:multiple="multiple"
:multiple="accordionMode === false"
@apply="$emit('apply', $event)"
@toggleAll="toggleAll"
/>
@@ -72,9 +72,9 @@ export default defineComponent({
default: () => [],
},
multiple: {
accordionMode: {
type: Boolean,
default: false,
default: true,
},
start: {
type: String,
@@ -107,7 +107,7 @@ export default defineComponent({
return { rootFields, selection, toggleAll };
function toggleAll() {
if (props.multiple === false) return;
if (props.accordionMode === true) return;
if (selection.value.length === rootFields.value.length) {
selection.value = [];

View File

@@ -13,24 +13,24 @@ export default defineInterface({
groups: ['group'],
options: [
{
field: 'multiple',
field: 'accordionMode',
type: 'boolean',
name: '$t:allow_multiple',
name: '$t:interfaces.group-accordion.accordion_mode',
meta: {
interface: 'boolean',
options: {
label: '$t:allow_multiple_to_be_open',
label: '$t:interfaces.group-accordion.max_one_section_open',
},
width: 'half',
},
schema: {
default_value: false,
default_value: true,
},
},
{
field: 'start',
type: 'string',
name: '$t:interfaces.group-accordion.start',
name: '$t:start',
schema: {
default_value: 'closed',
},
@@ -52,8 +52,8 @@ export default defineInterface({
conditions: [
{
rule: {
multiple: {
_eq: true,
accordionMode: {
_eq: false,
},
},
options: {

View File

@@ -0,0 +1,125 @@
<template>
<v-detail class="group-detail">
<template #activator="{ toggle, active }">
<v-divider
:style="{
'--v-divider-label-color': headerColor,
}"
:class="{ active }"
:inline-title="false"
:start-open="start === 'open'"
large
@click="toggle"
>
<template v-if="headerIcon" #icon><v-icon :name="headerIcon" class="header-icon" /></template>
<template v-if="field.name">
<span class="title">{{ field.name }}</span>
</template>
<v-icon class="expand-icon" name="expand_more" />
</v-divider>
</template>
<v-form
:initial-values="initialValues"
:fields="fields"
:model-value="values"
:primary-key="primaryKey"
:group="field.meta.id"
:validation-errors="validationErrors"
:loading="loading"
:batch-mode="batchMode"
@update:model-value="$emit('apply', $event)"
/>
</v-detail>
</template>
<script lang="ts">
import { Field } from '@directus/shared/types';
import { defineComponent, PropType } from 'vue';
import { ValidationError } from '@directus/shared/types';
export default defineComponent({
name: 'InterfaceGroupRaw',
props: {
field: {
type: Object as PropType<Field>,
required: true,
},
fields: {
type: Array as PropType<Field[]>,
required: true,
},
values: {
type: Object as PropType<Record<string, unknown>>,
required: true,
},
initialValues: {
type: Object as PropType<Record<string, unknown>>,
required: true,
},
disabled: {
type: Boolean,
default: false,
},
batchMode: {
type: Boolean,
default: false,
},
batchActiveFields: {
type: Array as PropType<string[]>,
default: () => [],
},
primaryKey: {
type: [Number, String],
required: true,
},
loading: {
type: Boolean,
default: false,
},
validationErrors: {
type: Array as PropType<ValidationError[]>,
default: () => [],
},
start: {
type: String,
enum: ['open', 'closed'],
default: 'open',
},
headerIcon: {
type: String,
default: null,
},
headerColor: {
type: String,
default: null,
},
},
emits: ['apply'],
});
</script>
<style scoped>
.v-form {
padding-top: calc(var(--form-vertical-gap) / 2);
}
.v-divider {
cursor: pointer;
}
.v-divider .expand-icon {
float: right;
transform: rotate(90deg) !important;
transition: transform var(--fast) var(--transition);
}
.v-divider.active .expand-icon {
transform: rotate(0) !important;
}
.header-icon {
margin-right: 12px !important;
}
</style>

View File

@@ -0,0 +1,56 @@
import { defineInterface } from '@directus/shared/utils';
import InterfaceGroupDetail from './group-detail.vue';
export default defineInterface({
id: 'group-detail',
name: '$t:interfaces.group-detail.name',
description: '$t:interfaces.group-detail.description',
icon: 'menu_open',
component: InterfaceGroupDetail,
groups: ['group'],
types: ['alias'],
options: [
{
field: 'start',
name: '$t:start',
type: 'string',
schema: {
default_value: 'open',
},
meta: {
interface: 'select-dropdown',
width: 'full',
options: {
choices: [
{
text: '$t:interfaces.group-detail.start_open',
value: 'open',
},
{
text: '$t:interfaces.group-detail.start_closed',
value: 'closed',
},
],
},
},
},
{
field: 'headerIcon',
name: '$t:interfaces.group-detail.header_icon',
type: 'string',
meta: {
interface: 'select-icon',
width: 'half',
},
},
{
field: 'headerColor',
name: '$t:interfaces.group-detail.header_color',
type: 'string',
meta: {
interface: 'select-color',
width: 'half',
},
},
],
});

View File

@@ -1,19 +1,5 @@
<template>
<div class="group-standard">
<v-divider
v-if="showHeader"
:style="{
'--v-divider-label-color': headerColor,
}"
:inline-title="false"
large
>
<template v-if="headerIcon" #icon><v-icon :name="headerIcon" /></template>
<template v-if="field.name">
<span class="title">{{ field.name }}</span>
</template>
</v-divider>
<div class="group-raw">
<v-form
:initial-values="initialValues"
:fields="fields"
@@ -22,7 +8,6 @@
:group="field.meta.id"
:validation-errors="validationErrors"
:loading="loading"
:batch-mode="batchMode"
@update:model-value="$emit('apply', $event)"
/>
</div>
@@ -31,8 +16,7 @@
<script lang="ts">
import { Field } from '@directus/shared/types';
import { defineComponent, PropType } from 'vue';
import { ValidationError } from '@directus/shared/types';
import { ValidationError } from '@/types';
export default defineComponent({
name: 'InterfaceGroupRaw',
props: {
@@ -76,26 +60,7 @@ export default defineComponent({
type: Array as PropType<ValidationError[]>,
default: () => [],
},
showHeader: {
type: Boolean,
default: false,
},
headerIcon: {
type: String,
default: null,
},
headerColor: {
type: String,
default: null,
},
},
emits: ['apply'],
});
</script>
<style scoped>
.v-divider {
margin-bottom: calc(var(--form-vertical-gap) / 2);
}
</style>

View File

@@ -0,0 +1,13 @@
import { defineInterface } from '@directus/shared/utils';
import InterfaceGroupRaw from './group-raw.vue';
export default defineInterface({
id: 'group-raw',
name: '$t:interfaces.group-raw.name',
description: '$t:interfaces.group-raw.description',
icon: 'view_in_ar',
component: InterfaceGroupRaw,
groups: ['group'],
types: ['alias'],
options: [],
});

View File

@@ -1,63 +0,0 @@
import { defineInterface } from '@directus/shared/utils';
import InterfaceGroupStandard from './group-standard.vue';
export default defineInterface({
id: 'group-standard',
name: '$t:interfaces.group-standard.name',
description: '$t:interfaces.group-standard.description',
icon: 'view_in_ar',
component: InterfaceGroupStandard,
groups: ['group'],
types: ['alias'],
options: [
{
field: 'showHeader',
name: '$t:interfaces.group-standard.show_header',
type: 'boolean',
meta: {
interface: 'boolean',
width: 'half',
},
},
{
field: 'headerIcon',
name: '$t:interfaces.group-standard.header_icon',
type: 'string',
meta: {
interface: 'select-icon',
width: 'half',
readonly: true,
conditions: [
{
rule: {
showHeader: {
_eq: true,
},
},
readonly: false,
},
],
},
},
{
field: 'headerColor',
name: '$t:interfaces.group-standard.header_color',
type: 'string',
meta: {
interface: 'select-color',
width: 'half',
readonly: true,
conditions: [
{
rule: {
showHeader: {
_eq: true,
},
},
readonly: false,
},
],
},
},
],
});

View File

@@ -912,14 +912,16 @@ value: Value
view_project: View Project
weeks: {}
report_error: Report Error
start: Start
interfaces:
group-accordion:
name: Accordion
description: Display fields or groups as accordion sections
start: Start
all_closed: All Closed
first_opened: First Opened
all_opened: All Opened
accordion_mode: Accordion Mode
max_one_section_open: Max 1 Section Open
presentation-links:
presentation-links: Button Links
links: Links
@@ -1126,12 +1128,17 @@ interfaces:
value_path: Value Path
trigger: Trigger
rate: Rate
group-standard:
name: Standard Group
description: Show the fields as normal with an optional group header.
group-raw:
name: Raw Group
description: Render the fields as-is
group-detail:
name: Detail Group
description: Render the fields as a collapsable section
show_header: Show Group Header
header_icon: Header Icon
header_color: Header Color
start_open: Start Open
start_closed: Start Closed
displays:
boolean:
boolean: Boolean