Finish settings > datamodel > collections (#414)

* Add missing slot to readme

* Show and order all collections

* Render correct icon for each collection type

* Add ctx menu on collections overview

* Use subdued colors in icons by default

* Add delete collection flow

* Add delete flow

* Move options to separate component

* Add manage / unmanage toggles

* Add collections filter

* Make non-nav list item icons subdued

* Tweak where loader shows up when managing

* Pass item key to v-table
This commit is contained in:
Rijk van Zanten
2020-04-14 13:28:39 -04:00
committed by GitHub
parent cec5a09cde
commit 97c8f763ab
11 changed files with 407 additions and 39 deletions

View File

@@ -16,28 +16,59 @@
<settings-navigation />
</template>
<v-table
:headers.sync="tableHeaders"
:items="items"
@click:row="openCollection"
show-resize
>
<template #item.icon="{ item }">
<v-icon class="icon" :class="{ hidden: item.hidden }" :name="item.icon" />
</template>
<template #drawer>
<collections-filter v-model="activeTypes" />
</template>
<template #item.collection="{ item }">
<span class="collection" :class="{ hidden: item.hidden }">
{{ item.name }}
</span>
</template>
<div class="padding-box">
<v-table
:headers.sync="tableHeaders"
:items="items"
@click:row="openCollection"
show-resize
fixed-header
item-key="collection"
>
<template #item.icon="{ item }">
<v-icon
class="icon"
:class="{
hidden: item.hidden,
system: item.collection.startsWith('directus_'),
unmanaged:
item.managed === false &&
item.collection.startsWith('directus_') === false,
}"
:name="item.icon"
/>
</template>
<template #item.note="{ item }">
<span class="note" :class="{ hidden: item.hidden }">
{{ item.note }}
</span>
</template>
</v-table>
<template #item.collection="{ item }">
<span
class="collection"
:class="{
hidden: item.hidden,
system: item.collection.startsWith('directus_'),
unmanaged:
item.managed === false &&
item.collection.startsWith('directus_') === false,
}"
>
{{ item.name }}
</span>
</template>
<template #item.note="{ item }">
<span class="note" :class="{ hidden: item.hidden }">
{{ item.note }}
</span>
</template>
<template #item-append="{ item }">
<collection-options :collection="item" />
</template>
</v-table>
</div>
<new-collection v-model="addNewActive" />
</private-view>
@@ -53,11 +84,16 @@ import useCollectionsStore from '@/stores/collections';
import { Collection } from '@/stores/collections/types';
import useProjectsStore from '@/stores/projects';
import router from '@/router';
import { sortBy } from 'lodash';
import CollectionOptions from './components/collection-options';
import CollectionsFilter from './components/collections-filter';
export default defineComponent({
components: { SettingsNavigation, NewCollection },
components: { SettingsNavigation, NewCollection, CollectionOptions, CollectionsFilter },
setup() {
const addNewActive = ref(false);
const activeTypes = ref(['visible', 'hidden', 'unmanaged']);
const collectionsStore = useCollectionsStore();
const tableHeaders = ref<HeaderRaw[]>([
@@ -67,27 +103,104 @@ export default defineComponent({
sortable: false,
},
{
text: i18n.tc('collection', 0),
text: i18n.t('name'),
value: 'collection',
},
{
text: i18n.t('note'),
text: i18n.t('description'),
value: 'note',
},
]);
const items = computed(() => {
return collectionsStore.state.collections.filter(
({ collection }) => collection.startsWith('directus_') === false
);
});
return { addNewActive, tableHeaders, items, openCollection };
function openCollection({ collection }: Collection) {
const { currentProjectKey } = useProjectsStore().state;
router.push(`/${currentProjectKey}/settings/data-model/${collection}`);
}
const { items } = useItems();
return {
addNewActive,
tableHeaders,
items,
openCollection,
activeTypes,
};
function useItems() {
const visible = computed(() => {
return sortBy(
collectionsStore.state.collections.filter(
(collection) =>
collection.collection.startsWith('directus_') === false &&
collection.managed === true &&
collection.hidden === false
),
'collection'
);
});
const hidden = computed(() => {
return sortBy(
collectionsStore.state.collections
.filter(
(collection) =>
collection.collection.startsWith('directus_') === false &&
collection.managed === true &&
collection.hidden === true
)
.map((collection) => ({ ...collection, icon: 'visibility_off' })),
'collection'
);
});
const system = computed(() => {
return sortBy(
collectionsStore.state.collections
.filter(
(collection) => collection.collection.startsWith('directus_') === true
)
.map((collection) => ({ ...collection, icon: 'settings' })),
'collection'
);
});
const unmanaged = computed(() => {
return sortBy(
collectionsStore.state.collections
.filter(
(collection) => collection.collection.startsWith('directus_') === false
)
.filter((collection) => collection.managed === false)
.map((collection) => ({ ...collection, icon: 'block' })),
'collection'
);
});
const items = computed(() => {
const items = [];
if (activeTypes.value.includes('visible')) {
items.push(visible.value);
}
if (activeTypes.value.includes('hidden')) {
items.push(hidden.value);
}
if (activeTypes.value.includes('unmanaged')) {
items.push(unmanaged.value);
}
if (activeTypes.value.includes('system')) {
items.push(system.value);
}
return items.flat();
});
return { items };
}
},
});
</script>
@@ -101,10 +214,24 @@ export default defineComponent({
color: var(--foreground-subdued);
}
.v-table {
.system {
color: var(--primary);
}
.unmanaged {
color: var(--warning);
}
.padding-box {
padding: var(--content-padding);
}
.v-table {
--v-table-sticky-offset-top: 64px;
display: contents;
}
.header-icon {
--v-button-color-disabled: var(--warning);
--v-button-background-color-disabled: var(--warning-alt);

View File

@@ -0,0 +1,129 @@
<template>
<v-button
v-if="
collection.managed === false && collection.collection.startsWith('directus_') === false
"
x-small
@click="toggleManaged(true)"
:loading="savingManaged"
>
{{ $t('manage') }}
</v-button>
<v-menu
v-else-if="collection.collection.startsWith('directus_') === false"
placement="left-start"
show-arrow
close-on-content-click
:disabled="savingManaged"
>
<template #activator="{ toggle }">
<v-progress-circular small v-if="savingManaged" indeterminate />
<v-icon v-else name="more_vert" @click="toggle" class="ctx-toggle" />
</template>
<v-list dense>
<v-list-item @click="toggleManaged(false)">
<v-list-item-icon>
<v-icon name="block" />
</v-list-item-icon>
<v-list-item-content>
{{ $t('unmanage_collection') }}
</v-list-item-content>
</v-list-item>
<v-dialog v-model="deleteActive">
<template #activator="{ on }">
<v-list-item @click="on">
<v-list-item-icon>
<v-icon name="delete" />
</v-list-item-icon>
<v-list-item-content>
{{ $t('delete_collection') }}
</v-list-item-content>
</v-list-item>
</template>
<v-card>
<v-card-title>{{ $t('delete_collection_are_you_sure') }}</v-card-title>
<v-card-actions>
<v-button :disabled="deleting" secondary @click="deleteActive = null">
{{ $t('cancel') }}
</v-button>
<v-button :loading="deleting" class="delete" @click="deleteCollection">
{{ $t('delete_collection') }}
</v-button>
</v-card-actions>
</v-card>
</v-dialog>
</v-list>
</v-menu>
</template>
<script lang="ts">
import { defineComponent, PropType, ref } from '@vue/composition-api';
import { Collection } from '@/stores/collections/types';
import useCollectionsStore from '@/stores/collections';
export default defineComponent({
props: {
collection: {
type: Object as PropType<Collection>,
required: true,
},
},
setup(props) {
const collectionsStore = useCollectionsStore();
const { deleting, deleteActive, deleteCollection } = useDelete();
const { savingManaged, toggleManaged } = useManage();
return { deleting, deleteActive, deleteCollection, savingManaged, toggleManaged };
function useDelete() {
const deleting = ref(false);
const deleteActive = ref(false);
return { deleting, deleteActive, deleteCollection };
async function deleteCollection() {
deleting.value = true;
try {
await collectionsStore.deleteCollection(props.collection.collection);
deleteActive.value = false;
} finally {
deleting.value = false;
}
}
}
function useManage() {
const savingManaged = ref(false);
return { savingManaged, toggleManaged };
async function toggleManaged(on: boolean) {
savingManaged.value = true;
try {
await collectionsStore.updateCollection(props.collection.collection, {
managed: on,
});
} finally {
savingManaged.value = false;
}
}
}
},
});
</script>
<style lang="scss" scoped>
.v-button.delete {
--v-button-background-color: var(--danger);
}
.ctx-toggle {
--v-icon-color: var(--foreground-subdued);
&:hover {
--v-icon-color: var(--foreground-normal);
}
}
</style>

View File

@@ -0,0 +1,4 @@
import CollectionOptions from './collection-options.vue';
export { CollectionOptions };
export default CollectionOptions;

View File

@@ -0,0 +1,40 @@
<template>
<drawer-detail class="collections-filter" icon="filter_list" :title="$tc('collection', 2)">
<div class="type-label label">{{ $t('collections_shown') }}</div>
<v-checkbox value="visible" v-model="_value" :label="$t('visible_collections')" />
<v-checkbox value="hidden" v-model="_value" :label="$t('hidden_collections')" />
<v-checkbox value="unmanaged" v-model="_value" :label="$t('unmanaged_collections')" />
<v-checkbox value="system" v-model="_value" :label="$t('system_collections')" />
</drawer-detail>
</template>
<script lang="ts">
import { defineComponent, computed, PropType } from '@vue/composition-api';
export default defineComponent({
props: {
value: {
type: Array as PropType<string[]>,
required: true,
},
},
setup(props, { emit }) {
const _value = computed({
get() {
return props.value;
},
set(newVal) {
emit('input', newVal);
},
});
return { _value };
},
});
</script>
<style lang="scss" scoped>
.label {
margin-bottom: 8px;
}
</style>

View File

@@ -0,0 +1,4 @@
import CollectionsFilter from './collections-filter.vue';
export { CollectionsFilter };
export default CollectionsFilter;