feat: remove lodash dependency (#743)
Co-authored-by: Dominik Pschenitschni <mail@celement.de> Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/743 Reviewed-by: konrad <k@knt.li> Co-authored-by: dpschen <dpschen@noreply.kolaente.de> Co-committed-by: dpschen <dpschen@noreply.kolaente.de>
This commit is contained in:
parent
a76d115baf
commit
faa2daa876
20 changed files with 116 additions and 89 deletions
|
@ -353,7 +353,7 @@ describe('Task', () => {
|
|||
.first()
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification')
|
||||
cy.get('.global-notification', { timeout: 4000 })
|
||||
.should('contain', 'Success')
|
||||
cy.get('.task-view .details.labels-list .multiselect .input-wrapper span.tag')
|
||||
.should('exist')
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {seed} from './seed'
|
||||
import merge from 'lodash/merge'
|
||||
|
||||
/**
|
||||
* A factory makes it easy to seed the database with data.
|
||||
|
@ -25,7 +24,10 @@ export class Factory {
|
|||
const data = []
|
||||
|
||||
for (let i = 1; i <= count; i++) {
|
||||
const entry = merge(this.factory(), override)
|
||||
const entry = {
|
||||
...this.factory(),
|
||||
...override,
|
||||
}
|
||||
for (const e in entry) {
|
||||
if(typeof entry[e] === 'function') {
|
||||
entry[e] = entry[e](i)
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
"dompurify": "2.3.3",
|
||||
"highlight.js": "11.2.0",
|
||||
"is-touch-device": "1.0.1",
|
||||
"lodash": "4.17.21",
|
||||
"marked": "3.0.4",
|
||||
"register-service-worker": "1.7.2",
|
||||
"snake-case": "3.0.4",
|
||||
|
|
|
@ -178,9 +178,8 @@ import Fancycheckbox from '../../input/fancycheckbox'
|
|||
import flatPickr from 'vue-flatpickr-component'
|
||||
import 'flatpickr/dist/flatpickr.css'
|
||||
|
||||
import {includesById} from '@/helpers/utils'
|
||||
import {formatISO} from 'date-fns'
|
||||
import differenceWith from 'lodash/differenceWith'
|
||||
|
||||
import PrioritySelect from '@/components/tasks/partials/prioritySelect.vue'
|
||||
import PercentDoneSelect from '@/components/tasks/partials/percentDoneSelect.vue'
|
||||
import Multiselect from '@/components/input/multiselect.vue'
|
||||
|
@ -269,13 +268,7 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
foundLabels() {
|
||||
const labels = (Object.values(this.$store.state.labels.labels).filter(l => {
|
||||
return l.title.toLowerCase().includes(this.labelQuery.toLowerCase())
|
||||
}) ?? [])
|
||||
|
||||
return differenceWith(labels, this.labels, (first, second) => {
|
||||
return first.id === second.id
|
||||
})
|
||||
return this.$store.getters['labels/filterLabelsByQuery'](this.labels, this.query)
|
||||
},
|
||||
flatPickerConfig() {
|
||||
return {
|
||||
|
@ -309,8 +302,13 @@ export default {
|
|||
this.prepareRelatedObjectFilter('namespace')
|
||||
|
||||
this.prepareSingleValue('labels')
|
||||
const labelIds = (typeof this.filters.labels === 'string' ? this.filters.labels : '').split(',').map(i => parseInt(i))
|
||||
this.labels = (Object.values(this.$store.state.labels.labels).filter(l => labelIds.includes(l.id)) ?? [])
|
||||
|
||||
const labels = typeof this.filters.labels === 'string'
|
||||
? this.filters.labels
|
||||
: ''
|
||||
const labelIds = labels.split(',').map(i => parseInt(i))
|
||||
|
||||
this.labels = this.$store.getters['labels/getLabelsByIds'](labelIds)
|
||||
},
|
||||
removePropertyFromFilter(propertyName) {
|
||||
// Because of the way arrays work, we can only ever remove one element at once.
|
||||
|
@ -533,10 +531,10 @@ export default {
|
|||
|
||||
this[`${kind}Service`].getAll({}, {s: query})
|
||||
.then(response => {
|
||||
// Filter the results to not include users who are already assigneid
|
||||
this.$set(this, `found${kind}`, differenceWith(response, this[kind], (first, second) => {
|
||||
return first.id === second.id
|
||||
}))
|
||||
// Filter users from the results who are already assigned
|
||||
const unassignedUsers = response.filter(({id}) => !includesById(this[kind], id))
|
||||
|
||||
this.$set(this, `found${kind}`, unassignedUsers)
|
||||
})
|
||||
.catch(e => {
|
||||
this.$message.error(e)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import TaskCollectionService from '@/services/taskCollection'
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
|
||||
// FIXME: merge with DEFAULT_PARAMS in filters.vue
|
||||
const DEFAULT_PARAMS = {
|
||||
|
@ -83,7 +82,7 @@ export default {
|
|||
this.tasks = r
|
||||
this.currentPage = page
|
||||
|
||||
this.loadedList = cloneDeep(currentList)
|
||||
this.loadedList = JSON.parse(JSON.stringify(currentList))
|
||||
})
|
||||
.catch(e => {
|
||||
this.$message.error(e)
|
||||
|
|
|
@ -29,8 +29,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import differenceWith from 'lodash/differenceWith'
|
||||
|
||||
import {includesById} from '@/helpers/utils'
|
||||
import UserModel from '../../../models/user'
|
||||
import ListUserService from '../../../services/listUsers'
|
||||
import TaskAssigneeService from '../../../services/taskAssignee'
|
||||
|
@ -111,9 +110,9 @@ export default {
|
|||
this.listUserService.getAll({listId: this.listId}, {s: query})
|
||||
.then(response => {
|
||||
// Filter the results to not include users who are already assigned
|
||||
this.$set(this, 'foundUsers', differenceWith(response, this.assignees, (first, second) => {
|
||||
return first.id === second.id
|
||||
}))
|
||||
const filteredResponse = response.filter(({id}) => !includesById(this.assignees, id))
|
||||
|
||||
this.$set(this, 'foundUsers', filteredResponse)
|
||||
})
|
||||
.catch(e => {
|
||||
this.$message.error(e)
|
||||
|
|
|
@ -38,8 +38,6 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import differenceWith from 'lodash/differenceWith'
|
||||
|
||||
import LabelModel from '../../../models/label'
|
||||
import LabelTaskService from '../../../services/labelTask'
|
||||
|
||||
|
@ -83,13 +81,7 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
foundLabels() {
|
||||
const labels = (Object.values(this.$store.state.labels.labels).filter(l => {
|
||||
return l.title.toLowerCase().includes(this.query.toLowerCase())
|
||||
}) ?? [])
|
||||
|
||||
return differenceWith(labels, this.labels, (first, second) => {
|
||||
return first.id === second.id
|
||||
})
|
||||
return this.$store.getters['labels/filterLabelsByQuery'](this.labels, this.query)
|
||||
},
|
||||
loading() {
|
||||
return this.labelTaskService.loading || (this.$store.state[LOADING] && this.$store.state[LOADING_MODULE] === 'labels')
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
export function findIndexById(array : [], id : string | number) {
|
||||
return array.findIndex(({id: currentId}) => currentId === id)
|
||||
}
|
22
src/helpers/utils.ts
Normal file
22
src/helpers/utils.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
export function findIndexById(array : [], id : string | number) {
|
||||
return array.findIndex(({id: currentId}) => currentId === id)
|
||||
}
|
||||
|
||||
export function includesById(array: [], id: string | number) {
|
||||
return array.some(({id: currentId}) => currentId === id)
|
||||
}
|
||||
|
||||
// https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_isnil
|
||||
export function isNil(value: any) {
|
||||
return value == null
|
||||
}
|
||||
|
||||
export function omitBy(obj: {}, check: (value: any) => Boolean): {} {
|
||||
if (isNil(obj)) {
|
||||
return {}
|
||||
}
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj).filter(([, value]) => !check(value)),
|
||||
)
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
import defaults from 'lodash/defaults'
|
||||
import isNil from 'lodash/isNil'
|
||||
import omitBy from 'lodash/omitBy'
|
||||
import {objectToCamelCase} from '@/helpers/case'
|
||||
import {omitBy, isNil} from '@/helpers/utils'
|
||||
|
||||
export default class AbstractModel {
|
||||
|
||||
|
@ -16,11 +14,14 @@ export default class AbstractModel {
|
|||
* @param data
|
||||
*/
|
||||
constructor(data) {
|
||||
|
||||
data = objectToCamelCase(data)
|
||||
|
||||
// Put all data in our model while overriding those with a value of null or undefined with their defaults
|
||||
defaults(this, omitBy(data, isNil), this.defaults())
|
||||
Object.assign(
|
||||
this,
|
||||
this.defaults(),
|
||||
omitBy(data, isNil),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import TeamShareBaseModel from './teamShareBase'
|
||||
import merge from 'lodash/merge'
|
||||
|
||||
export default class TeamListModel extends TeamShareBaseModel {
|
||||
defaults() {
|
||||
return merge(
|
||||
super.defaults(),
|
||||
{
|
||||
return {
|
||||
...super.defaults(),
|
||||
listId: 0,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,11 @@
|
|||
import UserModel from './user'
|
||||
import merge from 'lodash/merge'
|
||||
|
||||
export default class TeamMemberModel extends UserModel {
|
||||
defaults() {
|
||||
return merge(
|
||||
super.defaults(),
|
||||
{
|
||||
return {
|
||||
...super.defaults(),
|
||||
admin: false,
|
||||
teamId: 0,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,10 @@
|
|||
import TeamShareBaseModel from './teamShareBase'
|
||||
import merge from 'lodash/merge'
|
||||
|
||||
export default class TeamNamespaceModel extends TeamShareBaseModel {
|
||||
defaults() {
|
||||
return merge(
|
||||
super.defaults(),
|
||||
{
|
||||
return {
|
||||
...super.defaults(),
|
||||
namespaceId: 0,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,11 @@
|
|||
import UserShareBaseModel from './userShareBase'
|
||||
import merge from 'lodash/merge'
|
||||
|
||||
// This class extends the user share model with a 'rights' parameter which is used in sharing
|
||||
export default class UserListModel extends UserShareBaseModel {
|
||||
defaults() {
|
||||
return merge(
|
||||
super.defaults(),
|
||||
{
|
||||
return {
|
||||
...super.defaults(),
|
||||
listId: 0,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,11 @@
|
|||
import UserShareBaseModel from './userShareBase'
|
||||
import merge from 'lodash/merge'
|
||||
|
||||
// This class extends the user share model with a 'rights' parameter which is used in sharing
|
||||
export default class UserNamespaceModel extends UserShareBaseModel {
|
||||
defaults() {
|
||||
return merge(
|
||||
super.defaults(),
|
||||
{
|
||||
return {
|
||||
...super.defaults(),
|
||||
namespaceId: 0,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
import axios from 'axios'
|
||||
import reduce from 'lodash/reduce'
|
||||
import replace from 'lodash/replace'
|
||||
import {objectToSnakeCase} from '@/helpers/case'
|
||||
import {getToken} from '@/helpers/auth'
|
||||
|
||||
|
@ -157,9 +155,10 @@ export default class AbstractService {
|
|||
*/
|
||||
getReplacedRoute(path, pathparams) {
|
||||
let replacements = this.getRouteReplacements(path, pathparams)
|
||||
return reduce(replacements, function (result, value, parameter) {
|
||||
return replace(result, parameter, value)
|
||||
}, path)
|
||||
return Object.entries(replacements).reduce(
|
||||
(result, [parameter, value]) => result.replace(parameter, value),
|
||||
path,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Vue from 'vue'
|
||||
|
||||
import {findIndexById} from '@/helpers/find'
|
||||
import {findIndexById} from '@/helpers/utils'
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import Vue from 'vue'
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
|
||||
import BucketService from '../../services/bucket'
|
||||
import {filterObject} from '@/helpers/filterObject'
|
||||
|
@ -206,7 +205,7 @@ export default {
|
|||
const cancel = setLoading(ctx, 'kanban')
|
||||
ctx.commit('setBucketLoading', {bucketId: bucketId, loading: true})
|
||||
|
||||
const params = cloneDeep(ps)
|
||||
const params = JSON.parse(JSON.stringify(ps))
|
||||
|
||||
params.sort_by = 'kanban_position'
|
||||
params.order_by = 'asc'
|
||||
|
|
|
@ -2,10 +2,37 @@ import LabelService from '@/services/label'
|
|||
import Vue from 'vue'
|
||||
import {setLoading} from '@/store/helper'
|
||||
|
||||
/**
|
||||
* Returns the labels by id if found
|
||||
* @param {Object} state
|
||||
* @param {Array} ids
|
||||
* @returns {Array}
|
||||
*/
|
||||
function getLabelsByIds(state, ids) {
|
||||
return Object.values(state.labels).filter(({id}) => ids.includes(id))
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a list of labels is available in the store and filters them then query
|
||||
* @param {Object} state
|
||||
* @param {Array} labels
|
||||
* @param {String} query
|
||||
* @returns {Array}
|
||||
*/
|
||||
function filterLabelsByQuery(state, labels, query) {
|
||||
const labelIds = labels.map(({id}) => id)
|
||||
const foundLabels = getLabelsByIds(state, labelIds)
|
||||
const labelQuery = query.toLowerCase()
|
||||
|
||||
return foundLabels.filter(({title}) => {
|
||||
return !title.toLowerCase().includes(labelQuery)
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
// The state is an object which has the label ids as keys.
|
||||
state: () => ({
|
||||
// The labels are stored as an object which has the label ids as keys.
|
||||
labels: {},
|
||||
loaded: false,
|
||||
}),
|
||||
|
@ -25,6 +52,14 @@ export default {
|
|||
state.loaded = loaded
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
getLabelsByIds(state) {
|
||||
return (ids) => getLabelsByIds(state, ids)
|
||||
},
|
||||
filterLabelsByQuery(state) {
|
||||
return (...arr) => filterLabelsByQuery(state, ...arr)
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
loadAllLabels(ctx, {forceLoad} = {}) {
|
||||
if (ctx.state.loaded && !forceLoad) {
|
||||
|
|
|
@ -5511,7 +5511,7 @@ lodash.truncate@^4.4.2:
|
|||
resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
|
||||
integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=
|
||||
|
||||
lodash@4.17.21, lodash@4.x, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0:
|
||||
lodash@4.x, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
|
Loading…
Reference in a new issue