@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.
@@ -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,10 @@
1
+ import { Ref } from 'vue';
2
+ declare global {
3
+ interface Window {
4
+ ResizeObserver: any;
5
+ }
6
+ }
7
+ export declare function useElementSize<T extends Element>(target: T | Ref<T> | Ref<undefined>): {
8
+ width: Ref<number>;
9
+ height: Ref<number>;
10
+ };
@@ -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,4 @@
1
+ import { Component, ComputedRef, Ref } from 'vue';
2
+ export declare function useLayout<Options = any, Query = any>(layoutId: Ref<string | null>): {
3
+ layoutWrapper: ComputedRef<Component>;
4
+ };
@@ -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,2 @@
1
+ import { Ref } from 'vue';
2
+ export declare function useSync<T, K extends keyof T & string, E extends (event: `update:${K}`, ...args: any[]) => void>(props: T, key: K, emit: E): Ref<T[K]>;
@@ -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.0",
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?(.map)"
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.0"
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.0",
41
- "@directus/utils": "9.25.0"
40
+ "@directus/constants": "9.25.2",
41
+ "@directus/utils": "9.25.2"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "tsc --build",
@@ -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
- });