refactor(NcCollectionList): refactor code

- migrate service from Class to simple functions
- migrate store to composable
- make NcCollectionListItem a visual component
- update avatar styles
- use RouterLink for relative paths in the same app
- update message.pot translations
- add docs

Signed-off-by: Maksim Sukharev <antreesy.web@gmail.com>

(cherry picked from commit 94177fec58)
Signed-off-by: Maksim Sukharev <antreesy.web@gmail.com>
This commit is contained in:
Maksim Sukharev 2024-07-02 11:05:58 +02:00
Родитель 3cd6d7ad4d
Коммит 100bb3a6f8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 6349D071889BD1D5
10 изменённых файлов: 362 добавлений и 275 удалений

Просмотреть файл

@ -27,6 +27,9 @@ msgstr ""
msgid "Activities"
msgstr ""
msgid "Add to a project"
msgstr ""
msgid "Animals & Nature"
msgstr ""
@ -102,6 +105,9 @@ msgstr ""
msgid "Confirm changes"
msgstr ""
msgid "Connect items to a project to make them easier to find"
msgstr ""
msgid "Custom"
msgstr ""
@ -136,6 +142,15 @@ msgstr ""
msgid "External documentation for {name}"
msgstr ""
msgid "Failed to add the item to the project"
msgstr ""
msgid "Failed to create a project"
msgstr ""
msgid "Failed to rename the project"
msgstr ""
msgid "Favorite"
msgstr ""
@ -161,6 +176,9 @@ msgstr ""
msgid "Gold"
msgstr ""
msgid "Hide details"
msgstr ""
msgid "Hide password"
msgstr ""
@ -295,6 +313,9 @@ msgstr ""
msgid "Related team resources"
msgstr ""
msgid "Rename project"
msgstr ""
#. TRANSLATORS: A color name for RGB(191, 103, 139)
msgid "Rosy brown"
msgstr ""
@ -337,6 +358,9 @@ msgstr ""
msgid "Settings navigation"
msgstr ""
msgid "Show details"
msgstr ""
msgid "Show password"
msgstr ""
@ -370,6 +394,9 @@ msgstr ""
msgid "Travel & Places"
msgstr ""
msgid "Type to search for existing projects"
msgstr ""
msgid "Type to search time zone"
msgstr ""

Просмотреть файл

@ -3,6 +3,19 @@
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<docs>
Provides a Vue standalone component for Nextcloud Projects feature introduced in Nextcloud 16. Replaces deprecated `nextcloud-vue-collections` library.
Projects feature is deprecated since Nextcloud 25, and superseded by Related resources. See [NcRelatedResourcesPanel](#/Components/NcRelatedResourcesPanel) documentation for more information.
### Usage
To enable feature in Nextcloud, run following command:
```sh
occ config:system:set --value true 'projects.enabled'
```
</docs>
<template>
<ul v-if="collections && type && id" id="collection-list" class="collection-list">
<li @click="showSelect">
@ -12,7 +25,7 @@
<div id="collection-select-container">
<NcSelect ref="select"
v-model="value"
:aria-label-combobox="t('core', 'Add to a project')"
:aria-label-combobox="t('Add to a project')"
:options="options"
:placeholder="placeholder"
label="title"
@ -34,7 +47,7 @@
</span>
</template>
<p class="hint">
{{ t('core', 'Connect items to a project to make them easier to find') }}
{{ t('Connect items to a project to make them easier to find') }}
</p>
</NcSelect>
</div>
@ -44,37 +57,30 @@
{{ error }}
</li>
</transition>
<NcCollectionListItem v-for="collection in collections" :key="collection.id" :collection="collection" />
<NcCollectionListItem v-for="collection in collections"
:key="collection.id"
:collection="collection"
:error="collectionsError[collection.id]"
@rename-collection="renameCollectionFromItem"
@remove-resource="removeResourceFromCollection" />
</ul>
</template>
<script>
import debounce from 'debounce'
import { t } from '@nextcloud/l10n'
import { ref } from 'vue'
import { t } from '../../l10n.js'
import NcAvatar from '../NcAvatar/index.js'
import NcSelect from '../NcSelect/index.js'
import NcCollectionListItem from './NcCollectionListItem.vue'
import { state, actions } from './collectionstore.js'
import { useCollections } from './useCollections.js'
import { searchService } from './service.ts'
const METHOD_CREATE_COLLECTION = 0
const METHOD_ADD_TO_COLLECTION = 1
const _debouncedSearch = debounce(
function(query, loading) {
if (query !== '') {
loading(true)
actions.search(query).then((collections) => {
this.searchCollections = collections
}).catch(e => {
console.error('Failed to search for collections', e)
}).finally(() => {
loading(false)
})
}
}, 500)
export default {
name: 'NcCollectionList',
@ -106,7 +112,9 @@ export default {
type: String,
default: '',
},
/**
* Whether the component is active (to start fetch resources)
*/
isActive: {
type: Boolean,
default: true,
@ -114,8 +122,38 @@ export default {
},
setup() {
const {
storedCollections,
fetchCollectionsByResource,
createCollection,
addResourceToCollection,
removeResourceFromCollection,
renameCollection,
} = useCollections()
const searchCollections = ref([])
const search = debounce(function(query, loading) {
if (query !== '') {
loading(true)
searchService(query).then(collections => {
searchCollections.value = collections
}).catch(e => {
console.error('Failed to search for collections', e)
}).finally(() => {
loading(false)
})
}
}, 500)
return {
state,
storedCollections,
fetchCollectionsByResource,
createCollection,
addResourceToCollection,
removeResourceFromCollection,
renameCollection,
searchCollections,
search,
}
},
@ -126,7 +164,7 @@ export default {
codes: undefined,
value: null,
model: {},
searchCollections: [],
collectionsError: {},
error: null,
isSelectOpen: false,
}
@ -134,18 +172,20 @@ export default {
computed: {
collections() {
return this.state.collections.filter((collection) => {
return typeof collection.resources.find((resource) => resource && resource.id === '' + this.id && resource.type === this.type) !== 'undefined'
})
return this.storedCollections.filter(collection => collection.resources
.some(resource => resource && resource.id === String(this.id) && resource.type === this.type),
)
},
placeholder() {
return this.isSelectOpen ? t('core', 'Type to search for existing projects') : t('core', 'Add to a project')
return this.isSelectOpen
? t('Type to search for existing projects')
: t('Add to a project')
},
options() {
const options = []
window.OCP.Collaboration.getTypes().sort().forEach((type) => {
window.OCP.Collaboration.getTypes().sort().forEach(type => {
options.push({
method: METHOD_CREATE_COLLECTION,
type,
@ -155,7 +195,7 @@ export default {
})
})
for (const index in this.searchCollections) {
if (this.collections.findIndex((collection) => collection.id === this.searchCollections[index].id) === -1) {
if (!this.collections.find(collection => collection.id === this.searchCollections[index].id)) {
options.push({
method: METHOD_ADD_TO_COLLECTION,
title: this.searchCollections[index].name,
@ -165,58 +205,43 @@ export default {
}
return options
},
resourceIdentifier() {
return {
resourceType: this.type,
resourceId: this.id,
isActive: this.isActive,
}
},
},
watch: {
type() {
if (this.isActive) {
actions.fetchCollectionsByResource({
resourceType: this.type,
resourceId: this.id,
})
}
resourceIdentifier: {
deep: true,
immediate: true,
handler(resourceIdentifier) {
if (!resourceIdentifier.isActive || !resourceIdentifier.resourceId || !resourceIdentifier.resourceType) {
return
}
this.fetchCollectionsByResource(resourceIdentifier)
},
},
id() {
if (this.isActive) {
actions.fetchCollectionsByResource({
resourceType: this.type,
resourceId: this.id,
})
}
},
isActive(isActive) {
if (isActive) {
actions.fetchCollectionsByResource({
resourceType: this.type,
resourceId: this.id,
})
}
},
},
mounted() {
actions.fetchCollectionsByResource({
resourceType: this.type,
resourceId: this.id,
})
},
methods: {
t,
select(selectedOption, id) {
select(selectedOption) {
if (selectedOption.method === METHOD_CREATE_COLLECTION) {
selectedOption.action().then((id) => {
actions.createCollection({
selectedOption.action().then(resourceId => {
this.createCollection({
baseResourceType: this.type,
baseResourceId: this.id,
resourceType: selectedOption.type,
resourceId: id,
resourceId,
name: this.name,
}).catch((e) => {
this.setError(t('core', 'Failed to create a project'), e)
this.setError(t('Failed to create a project'), e)
})
}).catch((e) => {
console.error('No resource selected', e)
@ -224,16 +249,14 @@ export default {
}
if (selectedOption.method === METHOD_ADD_TO_COLLECTION) {
actions.addResourceToCollection({
this.addResourceToCollection({
collectionId: selectedOption.collectionId, resourceType: this.type, resourceId: this.id,
}).catch((e) => {
this.setError(t('core', 'Failed to add the item to the project'), e)
this.setError(t('Failed to add the item to the project'), e)
})
}
},
search(query, loading) {
_debouncedSearch.bind(this)(query, loading)
this.value = null
},
showSelect() {
@ -241,14 +264,6 @@ export default {
this.$refs.select.$el.focus()
},
hideSelect() {
this.selectIsOpen = false
},
isVueComponent(object) {
return object._isVue
},
setError(error, e) {
console.error(error, e)
this.error = error
@ -256,6 +271,17 @@ export default {
this.error = null
}, 5000)
},
renameCollectionFromItem({ collectionId, name }) {
this.renameCollection({ collectionId, name })
.catch((e) => {
console.error(t('Failed to rename the project'), e)
this.collectionsError[collectionId] = t('Failed to rename the project')
setTimeout(() => {
this.collectionsError[collectionId] = null
}, 5000)
})
},
},
}
</script>
@ -267,11 +293,11 @@ export default {
.collection-list > li {
display: flex;
align-items: start;
align-items: center;
gap: 12px;
& > .avatar {
margin-top: auto;
margin-top: 0;
}
}
@ -326,6 +352,8 @@ div.avatar {
.avatar {
display: block;
width: 32px;
height: 32px;
background-color: var(--color-background-darker) !important;
}

Просмотреть файл

@ -10,7 +10,7 @@
class="collection-item-name"
title=""
@click="showDetails">{{ collection.name }}</span>
<form v-else :class="{'shouldshake': error.rename }" @submit.prevent="renameCollection">
<form v-else :class="{'should-shake': error }" @submit.prevent="renameCollection">
<input v-model="newName"
type="text"
autocomplete="off"
@ -18,37 +18,46 @@
<input type="submit" value="" class="icon-confirm">
</form>
<div v-if="!detailsOpen && newName === null" class="linked-icons">
<a v-for="resource in limitedResources(collection)"
<component :is="getComponent(resource).component"
v-for="resource in resources.slice(0, 2)"
:key="resource.type + '|' + resource.id"
:title="resource.name"
:href="resource.link"
:class="typeClass(resource)"><img :src="iconUrl(resource)"></a>
:to="getComponent(resource).to"
:href="getComponent(resource).href"
:class="typeClass(resource)">
<img :src="iconUrl(resource)" :alt="resource.name">
</component>
</div>
<span v-if="newName === null" class="sharingOptionsGroup">
<NcActions>
<NcActionButton icon="icon-info"
@click.prevent="toggleDetails">
{{ detailsOpen ? t('core', 'Hide details') : t('core', 'Show details') }}
{{ detailsOpen ? t('Hide details') : t('Show details') }}
</NcActionButton>
<NcActionButton icon="icon-rename"
@click.prevent="openRename">
{{ t('core', 'Rename project') }}
{{ t('Rename project') }}
</NcActionButton>
</NcActions>
</span>
<transition name="fade">
<div v-if="error.rename" class="error">
{{ error.rename }}
<div v-if="error" class="error">
{{ error }}
</div>
</transition>
<transition name="fade">
<ul v-if="detailsOpen" class="resource-list-details">
<li v-for="resource in collection.resources"
<li v-for="resource in resources"
:key="resource.type + '|' + resource.id"
:class="typeClass(resource)">
<a :href="resource.link"><img :src="iconUrl(resource)"><span class="resource-name">{{ resource.name || '' }}</span></a>
<component :is="getComponent(resource).component"
:to="getComponent(resource).to"
:href="getComponent(resource).href">
<img :src="iconUrl(resource)" :alt="resource.name">
<span class="resource-name">{{ resource.name || '' }}</span>
</component>
<span class="icon-close" @click="removeResource(collection, resource)" />
</li>
</ul>
@ -57,15 +66,13 @@
</template>
<script>
import { set } from 'vue'
import { t } from '@nextcloud/l10n'
import { t } from '../../l10n.js'
import { getRoute } from '../NcRichText/autolink.js'
import NcActions from '../NcActions/index.js'
import NcActionButton from '../NcActionButton/index.js'
import NcAvatar from '../NcAvatar/index.js'
import { actions } from './collectionstore.js'
export default {
name: 'NcCollectionListItem',
@ -80,13 +87,19 @@ export default {
type: Object,
default: null,
},
error: {
type: String,
default: undefined,
},
},
emits: ['remove-resource', 'rename-collection'],
data() {
return {
detailsOpen: false,
newName: null,
error: {},
}
},
@ -99,8 +112,19 @@ export default {
return (resource) => 'resource-type-' + resource.type
},
limitedResources() {
return (collection) => collection.resources ? collection.resources.slice(0, 2) : []
resources() {
// invalid resources come from server as empty array ([]) and not an object
return this.collection.resources?.filter(resource => !Array.isArray(resource)) ?? []
},
getComponent() {
return (resource) => {
const route = getRoute(this.$router, resource.link)
return route
? { component: 'router-link', to: route, href: undefined }
: { component: 'a', to: undefined, href: resource.link }
}
},
iconUrl() {
@ -127,13 +151,11 @@ export default {
this.detailsOpen = true
},
hideDetails() {
this.detailsOpen = false
},
removeResource(collection, resource) {
actions.removeResource({
collectionId: collection.id, resourceType: resource.type, resourceId: resource.id,
this.$emit('remove-resource', {
collectionId: collection.id,
resourceType: resource.type,
resourceId: resource.id,
})
},
@ -142,22 +164,13 @@ export default {
},
renameCollection() {
if (this.newName === '') {
this.newName = null
return
if (this.newName) {
this.$emit('rename-collection', {
collectionId: this.collection.id,
name: this.newName,
})
}
actions.renameCollection({
collectionId: this.collection.id,
name: this.newName,
}).then((collection) => {
this.newName = null
}).catch((e) => {
this.$set(this.error, 'rename', t('core', 'Failed to rename the project'))
console.error(e)
setTimeout(() => {
set(this.error, 'rename', null)
}, 3000)
})
this.newName = null
},
},
}
@ -206,7 +219,7 @@ li.collection-list-item {
margin-bottom: 0 !important;
.collection-avatar {
margin-top: 6px;
margin-top: 0;
}
form, .collection-item-name {
@ -286,7 +299,7 @@ li.collection-list-item {
}
}
.shouldshake {
.should-shake {
animation: shake 0.6s 1 linear;
}

Просмотреть файл

@ -1,71 +0,0 @@
/**
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import axios from '@nextcloud/axios'
import { generateOcsUrl } from '@nextcloud/router'
class CollectionService {
constructor() {
this.http = axios
}
listCollection(collectionId) {
return this.http.get(generateOcsUrl('collaboration/resources/collections/{collectionId}', { collectionId }))
}
renameCollection(collectionId, collectionName) {
return this.http.put(generateOcsUrl('collaboration/resources/collections/{collectionId}', { collectionId }), {
collectionName,
}).then(result => {
return result.data.ocs.data
})
}
getCollectionsByResource(resourceType, resourceId) {
return this.http.get(generateOcsUrl('collaboration/resources/{resourceType}/{resourceId}', { resourceType, resourceId }))
.then(result => {
return result.data.ocs.data
})
}
createCollection(resourceType, resourceId, name) {
return this.http.post(generateOcsUrl('collaboration/resources/{resourceType}/{resourceId}', { resourceType, resourceId }), {
name,
})
.then((response) => {
return response.data.ocs.data
})
}
addResource(collectionId, resourceType, resourceId) {
resourceId = '' + resourceId
return this.http.post(generateOcsUrl('collaboration/resources/collections/{collectionId}', { collectionId }), {
resourceType,
resourceId,
}).then((response) => {
return response.data.ocs.data
})
}
removeResource(collectionId, resourceType, resourceId) {
return this.http.delete(generateOcsUrl('collaboration/resources/collections/{collectionId}', { collectionId }), { params: { resourceType, resourceId } })
.then((response) => {
return response.data.ocs.data
})
}
search(query) {
return this.http.get(generateOcsUrl('collaboration/resources/collections/search/{query}', { query }))
.then((response) => {
return response.data.ocs.data
})
}
}
const service = new CollectionService()
export default service

Просмотреть файл

@ -1,82 +0,0 @@
/**
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { reactive, set } from 'vue'
import service from './collectionservice.js'
const state = reactive({
collections: [],
})
const mutations = {
addCollections(collections) {
set(state, 'collections', collections)
},
addCollection(collection) {
state.collections.push(collection)
},
removeCollection(collectionId) {
set(state, 'collections', state.collections.filter(item => item.id !== collectionId))
},
updateCollection(collection) {
const index = state.collections.findIndex((_item) => _item.id === collection.id)
if (index !== -1) {
set(state.collections, index, collection)
} else {
state.collections.push(collection)
}
},
}
const actions = {
fetchCollectionsByResource({ resourceType, resourceId }) {
return service.getCollectionsByResource(resourceType, resourceId).then((collections) => {
mutations.addCollections(collections)
return collections
})
},
createCollection({ baseResourceType, baseResourceId, resourceType, resourceId, name }) {
return service.createCollection(baseResourceType, baseResourceId, name).then((collection) => {
mutations.addCollection(collection)
actions.addResourceToCollection({
collectionId: collection.id,
resourceType,
resourceId,
})
})
},
renameCollection({ collectionId, name }) {
return service.renameCollection(collectionId, name).then((collection) => {
mutations.updateCollection(collection)
return collection
})
},
addResourceToCollection({ collectionId, resourceType, resourceId }) {
return service.addResource(collectionId, resourceType, resourceId).then((collection) => {
mutations.updateCollection(collection)
return collection
})
},
removeResource({ collectionId, resourceType, resourceId }) {
return service.removeResource(collectionId, resourceType, resourceId).then((collection) => {
if (collection.resources.length > 0) {
mutations.updateCollection(collection)
} else {
mutations.removeCollection(collection)
}
})
},
search(query) {
return service.search(query)
},
}
const store = {
actions,
state,
}
export default store
export { actions, state }

Просмотреть файл

@ -2,5 +2,4 @@
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
export { default as NcCollectionListItem } from './NcCollectionListItem.vue'
export { default } from './NcCollectionList.vue'

Просмотреть файл

@ -0,0 +1,80 @@
/**
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import axios, { type AxiosResponse } from '@nextcloud/axios'
import { generateOcsUrl } from '@nextcloud/router'
import type { OCSResponse } from '@nextcloud/typings/ocs'
/**
* Extracts the OCS data from a response
* @param response OCS response
*/
function extractOcsData(response: AxiosResponse<OCSResponse>) {
return response.data.ocs.data
}
/**
* Lists all collections
* @param collectionId Collection ID
*/
export function listCollectionService(collectionId: number) {
return axios.get(generateOcsUrl('collaboration/resources/collections/{collectionId}', { collectionId })).then(extractOcsData)
}
/**
* Renames a collection
* @param collectionId Collection ID
* @param collectionName New collection name
*/
export function renameCollectionService(collectionId: number, collectionName: string) {
return axios.put(generateOcsUrl('collaboration/resources/collections/{collectionId}', { collectionId }), { collectionName }).then(extractOcsData)
}
/**
* Lists all collections for a resource
* @param resourceType Resource type
* @param resourceId Resource ID
*/
export function getCollectionsByResourceService(resourceType: string, resourceId: string) {
return axios.get(generateOcsUrl('collaboration/resources/{resourceType}/{resourceId}', { resourceType, resourceId })).then(extractOcsData)
}
/**
* Creates a collection
* @param resourceType Resource type
* @param resourceId Resource ID
* @param name Collection name
*/
export function createCollectionService(resourceType: string, resourceId: string, name: string) {
return axios.post(generateOcsUrl('collaboration/resources/{resourceType}/{resourceId}', { resourceType, resourceId }), { name }).then(extractOcsData)
}
/**
* Adds a resource to a collection
* @param collectionId Collection ID
* @param resourceType Resource type
* @param resourceId Resource ID
*/
export function addResourceService(collectionId: number, resourceType: string, resourceId: string) {
return axios.post(generateOcsUrl('collaboration/resources/collections/{collectionId}', { collectionId }), { resourceType, resourceId }).then(extractOcsData)
}
/**
* Removes a resource from a collection
* @param collectionId Collection ID
* @param resourceType Resource type
* @param resourceId Resource ID
*/
export function removeResourceService(collectionId: number, resourceType: string, resourceId: string) {
return axios.delete(generateOcsUrl('collaboration/resources/collections/{collectionId}', { collectionId }), { params: { resourceType, resourceId } }).then(extractOcsData)
}
/**
* Searches for collections
* @param query Search query
*/
export function searchService(query: string) {
return axios.get(generateOcsUrl('collaboration/resources/collections/search/{query}', { query })).then(extractOcsData)
}

Просмотреть файл

@ -0,0 +1,92 @@
/**
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { ref, set } from 'vue'
import {
renameCollectionService,
getCollectionsByResourceService,
createCollectionService,
addResourceService,
removeResourceService,
} from './service.ts'
/**
* Use collections composable
*/
export function useCollections() {
// State
const storedCollections = ref([])
// Mutations
const addCollections = (collections) => {
set(storedCollections, 'value', collections)
}
const addCollection = (collection) => {
set(storedCollections, 'value', [...storedCollections.value, collection])
}
const removeCollection = (collectionId) => {
set(storedCollections, 'value', storedCollections.value.filter(item => item.id !== collectionId))
}
const updateCollection = (collection) => {
const index = storedCollections.value.findIndex(item => item.id === collection.id)
if (index !== -1) {
set(storedCollections.value, index, collection)
} else {
addCollection(collection)
}
}
// Actions
const fetchCollectionsByResource = async ({ resourceType, resourceId }) => {
const collections = await getCollectionsByResourceService(resourceType, resourceId)
addCollections(collections)
}
const createCollection = async ({ baseResourceType, baseResourceId, resourceType, resourceId, name }) => {
const collection = await createCollectionService(baseResourceType, baseResourceId, name)
addCollection(collection)
await addResourceToCollection({
collectionId: collection.id,
resourceType,
resourceId,
})
}
const renameCollection = async ({ collectionId, name }) => {
const collection = await renameCollectionService(collectionId, name)
updateCollection(collection)
}
const addResourceToCollection = async ({ collectionId, resourceType, resourceId }) => {
const collection = await addResourceService(collectionId, resourceType, String(resourceId))
updateCollection(collection)
}
const removeResourceFromCollection = async ({ collectionId, resourceType, resourceId }) => {
const collection = await removeResourceService(collectionId, resourceType, String(resourceId))
if (collection.resources.length > 0) {
updateCollection(collection)
} else {
removeCollection(collectionId)
}
}
return {
storedCollections,
fetchCollectionsByResource,
createCollection,
renameCollection,
addResourceToCollection,
removeResourceFromCollection,
}
}

Просмотреть файл

@ -37,7 +37,7 @@ export { default as NcBreadcrumbs } from './NcBreadcrumbs/index.js'
export { default as NcButton } from './NcButton/index'
export { default as NcCheckboxRadioSwitch } from './NcCheckboxRadioSwitch/index.js'
export { default as NcChip } from './NcChip/index'
export { default as NcCollectionList, NcCollectionListItem } from './NcCollectionList/index.js'
export { default as NcCollectionList } from './NcCollectionList/index.js'
export { default as NcColorPicker } from './NcColorPicker/index.js'
export { default as NcContent } from './NcContent/index.js'
export { default as NcCounterBubble } from './NcCounterBubble/index.js'

Просмотреть файл

@ -120,6 +120,7 @@ module.exports = async () => {
'src/components/NcAppSidebar*/*.vue',
'src/components/NcBreadcrumb*/*.vue',
'src/components/NcCheckboxRadioSwitch/NcCheckboxContent.vue',
'src/components/NcCollectionList/!(NcCollectionList).vue',
'src/components/NcContent/*.vue',
'src/components/NcDashboard*/*.vue',
'src/components/NcDialog*/*.vue',