@directus/composables 9.25.0 → 9.25.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +10 -0
- package/dist/use-collection.d.ts +13 -0
- package/dist/use-custom-selection.d.ts +17 -0
- package/dist/use-element-size.d.ts +10 -0
- package/dist/use-filter-fields.d.ts +5 -0
- package/dist/use-groupable.d.ts +45 -0
- package/dist/use-items.d.ts +28 -0
- package/dist/use-layout.d.ts +4 -0
- package/dist/use-size-class.d.ts +27 -0
- package/dist/use-sync.d.ts +2 -0
- package/dist/use-system.d.ts +5 -0
- package/package.json +5 -5
- package/dist/use-items.test.js +0 -175
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './use-collection.js';
|
|
2
|
+
export * from './use-custom-selection.js';
|
|
3
|
+
export * from './use-element-size.js';
|
|
4
|
+
export * from './use-filter-fields.js';
|
|
5
|
+
export * from './use-groupable.js';
|
|
6
|
+
export * from './use-items.js';
|
|
7
|
+
export * from './use-layout.js';
|
|
8
|
+
export * from './use-size-class.js';
|
|
9
|
+
export * from './use-sync.js';
|
|
10
|
+
export * from './use-system.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AppCollection, Field } from '@directus/types';
|
|
2
|
+
import { ComputedRef, Ref } from 'vue';
|
|
3
|
+
export type UsableCollection = {
|
|
4
|
+
info: ComputedRef<AppCollection | null>;
|
|
5
|
+
fields: ComputedRef<Field[]>;
|
|
6
|
+
defaults: ComputedRef<Record<string, any>>;
|
|
7
|
+
primaryKeyField: ComputedRef<Field | null>;
|
|
8
|
+
userCreatedField: ComputedRef<Field | null>;
|
|
9
|
+
sortField: ComputedRef<string | null>;
|
|
10
|
+
isSingleton: ComputedRef<boolean>;
|
|
11
|
+
accountabilityScope: ComputedRef<'all' | 'activity' | null>;
|
|
12
|
+
};
|
|
13
|
+
export declare function useCollection(collectionKey: string | Ref<string | null>): UsableCollection;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ComputedRef, Ref } from 'vue';
|
|
2
|
+
export type UsableCustomSelection = {
|
|
3
|
+
otherValue: Ref<string | null>;
|
|
4
|
+
usesOtherValue: ComputedRef<boolean>;
|
|
5
|
+
};
|
|
6
|
+
export declare function useCustomSelection(currentValue: Ref<string>, items: Ref<any[]>, emit: (event: string | null) => void): UsableCustomSelection;
|
|
7
|
+
type OtherValue = {
|
|
8
|
+
key: string;
|
|
9
|
+
value: string;
|
|
10
|
+
};
|
|
11
|
+
type UsableCustomSelectionMultiple = {
|
|
12
|
+
otherValues: Ref<OtherValue[]>;
|
|
13
|
+
addOtherValue: (value?: string) => void;
|
|
14
|
+
setOtherValue: (key: string, newValue: string | null) => void;
|
|
15
|
+
};
|
|
16
|
+
export declare function useCustomSelectionMultiple(currentValues: Ref<string[]>, items: Ref<any[]>, emit: (event: string[] | null) => void): UsableCustomSelectionMultiple;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Field } from '@directus/types';
|
|
2
|
+
import { Ref, ComputedRef } from 'vue';
|
|
3
|
+
export declare function useFilterFields<T extends string>(fields: Ref<Field[]>, filters: Record<T, (field: Field) => boolean>): {
|
|
4
|
+
fieldGroups: ComputedRef<Record<Extract<T, string>, Field[]>>;
|
|
5
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
export type GroupableInstance = {
|
|
3
|
+
active: Ref<boolean>;
|
|
4
|
+
value: string | number | undefined;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Used to make child item part of the group context. Needs to be used in a component that is a child
|
|
8
|
+
* of a component that has the `useGroupableParent` composition enabled
|
|
9
|
+
*/
|
|
10
|
+
export type GroupableOptions = {
|
|
11
|
+
value?: string | number;
|
|
12
|
+
group?: string;
|
|
13
|
+
active?: Ref<boolean>;
|
|
14
|
+
watch?: boolean;
|
|
15
|
+
};
|
|
16
|
+
export type UsableGroupable = {
|
|
17
|
+
active: Ref<boolean>;
|
|
18
|
+
toggle: () => void;
|
|
19
|
+
activate: () => void;
|
|
20
|
+
deactivate: () => void;
|
|
21
|
+
};
|
|
22
|
+
export declare function useGroupable(options?: GroupableOptions): UsableGroupable;
|
|
23
|
+
type GroupableParentState = {
|
|
24
|
+
selection?: Ref<(string | number)[] | undefined> | Ref<readonly (string | number)[] | undefined>;
|
|
25
|
+
onSelectionChange?: (newSelectionValues: readonly (string | number)[]) => void;
|
|
26
|
+
onToggle?: (item: GroupableInstance) => void;
|
|
27
|
+
};
|
|
28
|
+
type GroupableParentOptions = {
|
|
29
|
+
mandatory?: Ref<boolean>;
|
|
30
|
+
max?: Ref<number>;
|
|
31
|
+
multiple?: Ref<boolean>;
|
|
32
|
+
};
|
|
33
|
+
type UsableGroupableParent = {
|
|
34
|
+
items: Ref<GroupableInstance[]>;
|
|
35
|
+
selection: Ref<readonly (string | number)[]>;
|
|
36
|
+
internalSelection: Ref<(string | number)[]>;
|
|
37
|
+
getValueForItem: (item: GroupableInstance) => string | number;
|
|
38
|
+
updateChildren: () => void;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Used to make a component a group parent component. Provides the registration / toggle functions
|
|
42
|
+
* to its group children
|
|
43
|
+
*/
|
|
44
|
+
export declare function useGroupableParent(state?: GroupableParentState, options?: GroupableParentOptions, group?: string): UsableGroupableParent;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Item, Query } from '@directus/types';
|
|
2
|
+
import { ComputedRef, Ref, WritableComputedRef } from 'vue';
|
|
3
|
+
export type ManualSortData = {
|
|
4
|
+
item: string | number;
|
|
5
|
+
to: string | number;
|
|
6
|
+
};
|
|
7
|
+
export type UsableItems = {
|
|
8
|
+
itemCount: Ref<number | null>;
|
|
9
|
+
totalCount: Ref<number | null>;
|
|
10
|
+
items: Ref<Item[]>;
|
|
11
|
+
totalPages: ComputedRef<number>;
|
|
12
|
+
loading: Ref<boolean>;
|
|
13
|
+
error: Ref<any>;
|
|
14
|
+
changeManualSort: (data: ManualSortData) => Promise<void>;
|
|
15
|
+
getItems: () => Promise<void>;
|
|
16
|
+
getTotalCount: () => Promise<void>;
|
|
17
|
+
getItemCount: () => Promise<void>;
|
|
18
|
+
};
|
|
19
|
+
export type ComputedQuery = {
|
|
20
|
+
fields: Ref<Query['fields']> | ComputedRef<Query['fields']> | WritableComputedRef<Query['fields']>;
|
|
21
|
+
alias?: Ref<Query['alias']> | ComputedRef<Query['alias']> | WritableComputedRef<Query['alias']>;
|
|
22
|
+
limit: Ref<Query['limit']> | ComputedRef<Query['limit']> | WritableComputedRef<Query['limit']>;
|
|
23
|
+
sort: Ref<Query['sort']> | ComputedRef<Query['sort']> | WritableComputedRef<Query['sort']>;
|
|
24
|
+
search: Ref<Query['search']> | ComputedRef<Query['search']> | WritableComputedRef<Query['search']>;
|
|
25
|
+
filter: Ref<Query['filter']> | ComputedRef<Query['filter']> | WritableComputedRef<Query['filter']>;
|
|
26
|
+
page: Ref<Query['page']> | WritableComputedRef<Query['page']>;
|
|
27
|
+
};
|
|
28
|
+
export declare function useItems(collection: Ref<string | null>, query: ComputedQuery): UsableItems;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ComputedRef } from 'vue';
|
|
2
|
+
export declare const sizeProps: {
|
|
3
|
+
xSmall: {
|
|
4
|
+
type: BooleanConstructor;
|
|
5
|
+
default: boolean;
|
|
6
|
+
};
|
|
7
|
+
small: {
|
|
8
|
+
type: BooleanConstructor;
|
|
9
|
+
default: boolean;
|
|
10
|
+
};
|
|
11
|
+
large: {
|
|
12
|
+
type: BooleanConstructor;
|
|
13
|
+
default: boolean;
|
|
14
|
+
};
|
|
15
|
+
xLarge: {
|
|
16
|
+
type: BooleanConstructor;
|
|
17
|
+
default: boolean;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
interface SizeProps {
|
|
21
|
+
xSmall?: boolean;
|
|
22
|
+
small?: boolean;
|
|
23
|
+
large?: boolean;
|
|
24
|
+
xLarge?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare function useSizeClass<T>(props: T & SizeProps): ComputedRef<string | null>;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AxiosInstance } from 'axios';
|
|
2
|
+
import type { AppExtensionConfigs, RefRecord } from '@directus/types';
|
|
3
|
+
export declare function useStores(): Record<string, any>;
|
|
4
|
+
export declare function useApi(): AxiosInstance;
|
|
5
|
+
export declare function useExtensions(): RefRecord<AppExtensionConfigs>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@directus/composables",
|
|
3
|
-
"version": "9.25.
|
|
3
|
+
"version": "9.25.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Shared Vue composables for Directus use",
|
|
6
6
|
"repository": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"main": "dist/index.js",
|
|
19
19
|
"files": [
|
|
20
20
|
"dist",
|
|
21
|
-
"!**/*.d.ts
|
|
21
|
+
"!**/*.test.{js,d.ts}"
|
|
22
22
|
],
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"access": "public"
|
|
@@ -31,14 +31,14 @@
|
|
|
31
31
|
"axios": "1.3.4",
|
|
32
32
|
"typescript": "4.9.5",
|
|
33
33
|
"vitest": "0.29.3",
|
|
34
|
-
"@directus/types": "9.25.
|
|
34
|
+
"@directus/types": "9.25.2"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"lodash-es": "4.17.21",
|
|
38
38
|
"nanoid": "4.0.2",
|
|
39
39
|
"vue": "3.2.47",
|
|
40
|
-
"@directus/constants": "9.25.
|
|
41
|
-
"@directus/utils": "9.25.
|
|
40
|
+
"@directus/constants": "9.25.2",
|
|
41
|
+
"@directus/utils": "9.25.2"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"build": "tsc --build",
|
package/dist/use-items.test.js
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import { flushPromises } from '@vue/test-utils';
|
|
2
|
-
import { isEqual } from 'lodash-es';
|
|
3
|
-
import { afterEach, expect, test, vi } from 'vitest';
|
|
4
|
-
import { computed, ref, unref } from 'vue';
|
|
5
|
-
import { useItems } from './use-items.js';
|
|
6
|
-
import { useCollection } from './use-collection.js';
|
|
7
|
-
const mockData = { id: 1 };
|
|
8
|
-
const mockCountData = { count: 2 };
|
|
9
|
-
const mockCountDistinctData = { countDistinct: { id: 3 } };
|
|
10
|
-
const mockPrimaryKeyField = {
|
|
11
|
-
collection: 'test_collection',
|
|
12
|
-
field: 'id',
|
|
13
|
-
name: 'id',
|
|
14
|
-
type: 'integer',
|
|
15
|
-
schema: null,
|
|
16
|
-
meta: null,
|
|
17
|
-
};
|
|
18
|
-
const mockApiGet = vi.fn();
|
|
19
|
-
const mockApiPost = vi.fn();
|
|
20
|
-
function isGetItemsRequest(config) {
|
|
21
|
-
if (!config.params)
|
|
22
|
-
return false;
|
|
23
|
-
return Object.keys(config.params).includes('fields');
|
|
24
|
-
}
|
|
25
|
-
function isTotalCountRequest(config) {
|
|
26
|
-
if (!config.params)
|
|
27
|
-
return false;
|
|
28
|
-
return isEqual(Object.keys(config.params), ['aggregate']);
|
|
29
|
-
}
|
|
30
|
-
function isFilterCountRequest(config) {
|
|
31
|
-
if (!config.params)
|
|
32
|
-
return false;
|
|
33
|
-
return isEqual(Object.keys(config.params), ['filter', 'search', 'aggregate']);
|
|
34
|
-
}
|
|
35
|
-
vi.mock('./use-system.js', () => ({
|
|
36
|
-
useApi: vi.fn().mockImplementation(() => ({
|
|
37
|
-
get: mockApiGet.mockImplementation((_path, config) => {
|
|
38
|
-
if (isTotalCountRequest(config) || isFilterCountRequest(config)) {
|
|
39
|
-
if (config.params.aggregate?.countDistinct)
|
|
40
|
-
return Promise.resolve({ data: { data: [mockCountDistinctData] } });
|
|
41
|
-
return Promise.resolve({ data: { data: [mockCountData] } });
|
|
42
|
-
}
|
|
43
|
-
return Promise.resolve({ data: { data: [mockData] } });
|
|
44
|
-
}),
|
|
45
|
-
post: mockApiPost,
|
|
46
|
-
})),
|
|
47
|
-
}));
|
|
48
|
-
vi.mock('./use-collection.js');
|
|
49
|
-
afterEach(() => {
|
|
50
|
-
vi.clearAllMocks();
|
|
51
|
-
});
|
|
52
|
-
test('should fetch filter count and total count only once', async () => {
|
|
53
|
-
vi.mocked(useCollection).mockReturnValueOnce({ primaryKeyField: computed(() => null) });
|
|
54
|
-
const { totalCount, itemCount } = useItems(ref('test_collection'), {
|
|
55
|
-
fields: ref(['*']),
|
|
56
|
-
limit: ref(1),
|
|
57
|
-
sort: ref(null),
|
|
58
|
-
search: ref(null),
|
|
59
|
-
filter: ref(null),
|
|
60
|
-
page: ref(1),
|
|
61
|
-
});
|
|
62
|
-
// Wait until computed values are updated
|
|
63
|
-
await flushPromises();
|
|
64
|
-
expect(unref(totalCount)).toBe(mockCountData.count);
|
|
65
|
-
expect(unref(itemCount)).toBe(mockCountData.count);
|
|
66
|
-
expect(mockApiGet.mock.calls.filter((call) => isGetItemsRequest(call[1])).length).toBe(1);
|
|
67
|
-
expect(mockApiGet.mock.calls.filter((call) => isTotalCountRequest(call[1])).length).toBe(1);
|
|
68
|
-
expect(mockApiGet.mock.calls.filter((call) => isFilterCountRequest(call[1])).length).toBe(1);
|
|
69
|
-
});
|
|
70
|
-
test('should fetch distinct filter count and total count only once', async () => {
|
|
71
|
-
vi.mocked(useCollection).mockReturnValueOnce({ primaryKeyField: computed(() => mockPrimaryKeyField) });
|
|
72
|
-
const { totalCount, itemCount } = useItems(ref('test_collection'), {
|
|
73
|
-
fields: ref(['*']),
|
|
74
|
-
limit: ref(1),
|
|
75
|
-
sort: ref(null),
|
|
76
|
-
search: ref(null),
|
|
77
|
-
filter: ref(null),
|
|
78
|
-
page: ref(1),
|
|
79
|
-
});
|
|
80
|
-
// Wait until computed values are updated
|
|
81
|
-
await flushPromises();
|
|
82
|
-
expect(unref(totalCount)).toBe(mockCountDistinctData.countDistinct.id);
|
|
83
|
-
expect(unref(itemCount)).toBe(mockCountDistinctData.countDistinct.id);
|
|
84
|
-
expect(mockApiGet.mock.calls.filter((call) => isGetItemsRequest(call[1])).length).toBe(1);
|
|
85
|
-
expect(mockApiGet.mock.calls.filter((call) => isTotalCountRequest(call[1])).length).toBe(1);
|
|
86
|
-
expect(mockApiGet.mock.calls.filter((call) => isFilterCountRequest(call[1])).length).toBe(1);
|
|
87
|
-
});
|
|
88
|
-
test('should not re-fetch filter count when changing fields query', async () => {
|
|
89
|
-
vi.mocked(useCollection).mockReturnValueOnce({ primaryKeyField: computed(() => null) });
|
|
90
|
-
const fields = ref(['*']);
|
|
91
|
-
useItems(ref('test_collection'), {
|
|
92
|
-
fields,
|
|
93
|
-
limit: ref(1),
|
|
94
|
-
sort: ref(null),
|
|
95
|
-
search: ref(null),
|
|
96
|
-
filter: ref(null),
|
|
97
|
-
page: ref(1),
|
|
98
|
-
});
|
|
99
|
-
// update fields query
|
|
100
|
-
fields.value = ['id'];
|
|
101
|
-
// Wait until computed values are updated
|
|
102
|
-
await flushPromises();
|
|
103
|
-
expect(mockApiGet.mock.calls.filter((call) => isFilterCountRequest(call[1])).length).toBe(1);
|
|
104
|
-
});
|
|
105
|
-
test('should re-fetch filter count when changing filters query', async () => {
|
|
106
|
-
vi.mocked(useCollection).mockReturnValueOnce({ primaryKeyField: computed(() => null) });
|
|
107
|
-
const filter = ref(null);
|
|
108
|
-
useItems(ref('test_collection'), {
|
|
109
|
-
fields: ref(['*']),
|
|
110
|
-
limit: ref(1),
|
|
111
|
-
sort: ref(null),
|
|
112
|
-
search: ref(null),
|
|
113
|
-
filter,
|
|
114
|
-
page: ref(1),
|
|
115
|
-
});
|
|
116
|
-
// update filter query
|
|
117
|
-
filter.value = { id: { _eq: 1 } };
|
|
118
|
-
// Wait until computed values are updated
|
|
119
|
-
await flushPromises();
|
|
120
|
-
expect(mockApiGet.mock.calls.filter((call) => isTotalCountRequest(call[1])).length).toBe(1);
|
|
121
|
-
expect(mockApiGet.mock.calls.filter((call) => isFilterCountRequest(call[1])).length).toBe(2);
|
|
122
|
-
});
|
|
123
|
-
test('should re-fetch filter count when changing search query', async () => {
|
|
124
|
-
vi.mocked(useCollection).mockReturnValueOnce({ primaryKeyField: computed(() => null) });
|
|
125
|
-
const search = ref(null);
|
|
126
|
-
useItems(ref('test_collection'), {
|
|
127
|
-
fields: ref(['*']),
|
|
128
|
-
limit: ref(1),
|
|
129
|
-
sort: ref(null),
|
|
130
|
-
search,
|
|
131
|
-
filter: ref(null),
|
|
132
|
-
page: ref(1),
|
|
133
|
-
});
|
|
134
|
-
// update search query
|
|
135
|
-
search.value = 'test';
|
|
136
|
-
// Wait until computed values are updated
|
|
137
|
-
await flushPromises();
|
|
138
|
-
expect(mockApiGet.mock.calls.filter((call) => isTotalCountRequest(call[1])).length).toBe(1);
|
|
139
|
-
expect(mockApiGet.mock.calls.filter((call) => isFilterCountRequest(call[1])).length).toBe(2);
|
|
140
|
-
});
|
|
141
|
-
test('should reset when collection changes', async () => {
|
|
142
|
-
vi.mocked(useCollection).mockReturnValueOnce({ primaryKeyField: computed(() => null) });
|
|
143
|
-
const collection = ref('old_collection');
|
|
144
|
-
const { items } = useItems(collection, {
|
|
145
|
-
fields: ref(['*']),
|
|
146
|
-
limit: ref(1),
|
|
147
|
-
sort: ref(null),
|
|
148
|
-
search: ref(null),
|
|
149
|
-
filter: ref(null),
|
|
150
|
-
page: ref(1),
|
|
151
|
-
});
|
|
152
|
-
// Wait until computed values are updated
|
|
153
|
-
await flushPromises();
|
|
154
|
-
expect(unref(items)).toEqual([mockData]);
|
|
155
|
-
// update collection ref
|
|
156
|
-
collection.value = 'new_collection';
|
|
157
|
-
// Wait until computed values are updated again
|
|
158
|
-
await flushPromises();
|
|
159
|
-
expect(unref(items)).toEqual([]);
|
|
160
|
-
});
|
|
161
|
-
test('should append $thumbnail to fetched items when collection is directus_files', async () => {
|
|
162
|
-
vi.mocked(useCollection).mockReturnValueOnce({ primaryKeyField: computed(() => null) });
|
|
163
|
-
const collection = ref('directus_files');
|
|
164
|
-
const { items } = useItems(collection, {
|
|
165
|
-
fields: ref(['*']),
|
|
166
|
-
limit: ref(1),
|
|
167
|
-
sort: ref(null),
|
|
168
|
-
search: ref(null),
|
|
169
|
-
filter: ref(null),
|
|
170
|
-
page: ref(1),
|
|
171
|
-
});
|
|
172
|
-
// Wait until computed values are updated
|
|
173
|
-
await flushPromises();
|
|
174
|
-
expect(unref(items)).toEqual([{ id: mockData.id, $thumbnail: mockData }]);
|
|
175
|
-
});
|