@directus/composables 10.1.2 → 10.1.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@directus/composables",
3
- "version": "10.1.2",
3
+ "version": "10.1.4",
4
4
  "description": "Shared Vue composables for Directus use",
5
5
  "homepage": "https://directus.io",
6
6
  "repository": {
@@ -26,21 +26,23 @@
26
26
  "lodash-es": "4.17.21",
27
27
  "nanoid": "4.0.2",
28
28
  "vue": "3.3.4",
29
- "@directus/constants": "10.2.3",
30
- "@directus/utils": "10.0.10"
29
+ "@directus/constants": "11.0.0",
30
+ "@directus/utils": "11.0.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/lodash-es": "4.17.7",
34
34
  "@vitest/coverage-c8": "0.31.1",
35
35
  "@vue/test-utils": "2.3.2",
36
- "typescript": "5.0.4",
36
+ "tsup": "7.2.0",
37
+ "typescript": "5.2.2",
37
38
  "vitest": "0.31.1",
38
- "@directus/tsconfig": "1.0.0",
39
- "@directus/types": "10.1.6"
39
+ "@directus/extensions": "0.0.1",
40
+ "@directus/tsconfig": "1.0.1",
41
+ "@directus/types": "11.0.0"
40
42
  },
41
43
  "scripts": {
42
- "build": "tsc --project tsconfig.prod.json",
43
- "dev": "tsc --watch",
44
+ "build": "tsup src/index.ts --format=esm --dts",
45
+ "dev": "tsup src/index.ts --format=esm --dts --watch",
44
46
  "test": "vitest --watch=false"
45
47
  }
46
48
  }
@@ -1,13 +0,0 @@
1
- import type { AppCollection, Field } from '@directus/types';
2
- import type { 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;
@@ -1,48 +0,0 @@
1
- import { computed, ref } from 'vue';
2
- import { useStores } from './use-system.js';
3
- export function useCollection(collectionKey) {
4
- const { useCollectionsStore, useFieldsStore } = useStores();
5
- const collectionsStore = useCollectionsStore();
6
- const fieldsStore = useFieldsStore();
7
- const collection = typeof collectionKey === 'string' ? ref(collectionKey) : collectionKey;
8
- const info = computed(() => {
9
- return (collectionsStore.collections.find(({ collection: key }) => key === collection.value) || null);
10
- });
11
- const fields = computed(() => {
12
- if (!collection.value)
13
- return [];
14
- return fieldsStore.getFieldsForCollectionSorted(collection.value);
15
- });
16
- const defaults = computed(() => {
17
- if (!fields.value)
18
- return {};
19
- const defaults = {};
20
- for (const field of fields.value) {
21
- if (field.schema !== null && 'default_value' in field.schema) {
22
- defaults[field.field] = field.schema.default_value;
23
- }
24
- }
25
- return defaults;
26
- });
27
- const primaryKeyField = computed(() => {
28
- return (fields.value.find((field) => field.collection === collection.value && field.schema?.is_primary_key === true) ||
29
- null);
30
- });
31
- const userCreatedField = computed(() => {
32
- return fields.value?.find((field) => (field.meta?.special || []).includes('user_created')) || null;
33
- });
34
- const sortField = computed(() => {
35
- return info.value?.meta?.sort_field || null;
36
- });
37
- const isSingleton = computed(() => {
38
- return info.value?.meta?.singleton === true;
39
- });
40
- const accountabilityScope = computed(() => {
41
- if (!info.value)
42
- return null;
43
- if (!info.value.meta)
44
- return null;
45
- return info.value.meta.accountability;
46
- });
47
- return { info, fields, defaults, primaryKeyField, userCreatedField, sortField, isSingleton, accountabilityScope };
48
- }
@@ -1,17 +0,0 @@
1
- import type { 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 | null>, 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[] | null>, items: Ref<any[]>, emit: (event: string[] | null) => void): UsableCustomSelectionMultiple;
17
- export {};
@@ -1,88 +0,0 @@
1
- import { nanoid } from 'nanoid';
2
- import { computed, ref, watch } from 'vue';
3
- export function useCustomSelection(currentValue, items, emit) {
4
- const localOtherValue = ref('');
5
- const otherValue = computed({
6
- get() {
7
- return localOtherValue.value || (usesOtherValue.value ? currentValue.value : '');
8
- },
9
- set(newValue) {
10
- if (newValue === null) {
11
- localOtherValue.value = '';
12
- emit(null);
13
- }
14
- else {
15
- localOtherValue.value = newValue;
16
- emit(newValue);
17
- }
18
- },
19
- });
20
- const usesOtherValue = computed(() => {
21
- if (items.value === null)
22
- return false;
23
- // Check if set value is one of the existing keys
24
- const values = items.value.map((item) => item.value);
25
- return (currentValue.value !== null && currentValue.value.length > 0 && values.includes(currentValue.value) === false);
26
- });
27
- return { otherValue, usesOtherValue };
28
- }
29
- export function useCustomSelectionMultiple(currentValues, items, emit) {
30
- const otherValues = ref([]);
31
- watch(currentValues, (newValue) => {
32
- if (newValue === null)
33
- return;
34
- if (!Array.isArray(newValue))
35
- return;
36
- if (items.value === null)
37
- return;
38
- newValue.forEach((value) => {
39
- if (items.value === null)
40
- return;
41
- const values = items.value.map((item) => item.value);
42
- const existsInValues = values.includes(value);
43
- if (!existsInValues) {
44
- const other = otherValues.value.map((o) => o.value);
45
- const existsInOtherValues = other.includes(value);
46
- if (!existsInOtherValues) {
47
- addOtherValue(value);
48
- }
49
- }
50
- });
51
- }, { immediate: true });
52
- return { otherValues, addOtherValue, setOtherValue };
53
- function addOtherValue(value = '') {
54
- otherValues.value = [
55
- ...otherValues.value,
56
- {
57
- key: nanoid(),
58
- value: value,
59
- },
60
- ];
61
- }
62
- function setOtherValue(key, newValue) {
63
- const previousValue = otherValues.value.find((o) => o.key === key);
64
- const valueWithoutPrevious = (currentValues.value || []).filter((val) => val !== previousValue?.value);
65
- if (newValue === null) {
66
- otherValues.value = otherValues.value.filter((o) => o.key !== key);
67
- if (valueWithoutPrevious.length === 0) {
68
- emit(null);
69
- }
70
- else {
71
- emit(valueWithoutPrevious);
72
- }
73
- }
74
- else {
75
- otherValues.value = otherValues.value.map((otherValue) => {
76
- if (otherValue.key === key)
77
- otherValue.value = newValue;
78
- return otherValue;
79
- });
80
- if (valueWithoutPrevious.length === currentValues.value?.length) {
81
- emit(valueWithoutPrevious);
82
- }
83
- else {
84
- emit([...valueWithoutPrevious, newValue]);
85
- }
86
- }
87
- }
88
- }
@@ -1,10 +0,0 @@
1
- import type { 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
- };
@@ -1,22 +0,0 @@
1
- import { isNil } from 'lodash-es';
2
- import { isRef, onMounted, onUnmounted, ref } from 'vue';
3
- export function useElementSize(target) {
4
- const width = ref(0);
5
- const height = ref(0);
6
- const resizeObserver = new ResizeObserver(([entry]) => {
7
- if (entry === undefined)
8
- return;
9
- width.value = entry.contentRect.width;
10
- height.value = entry.contentRect.height;
11
- });
12
- onMounted(() => {
13
- const t = isRef(target) ? target.value : target;
14
- if (!isNil(t)) {
15
- resizeObserver.observe(t);
16
- }
17
- });
18
- onUnmounted(() => {
19
- resizeObserver.disconnect();
20
- });
21
- return { width, height };
22
- }
@@ -1,5 +0,0 @@
1
- import type { Field } from '@directus/types';
2
- import type { ComputedRef, Ref } 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
- };
@@ -1,18 +0,0 @@
1
- import { computed } from 'vue';
2
- export function useFilterFields(fields, filters) {
3
- const fieldGroups = computed(() => {
4
- const acc = {};
5
- for (const name in filters) {
6
- acc[name] = [];
7
- }
8
- return fields.value.reduce((acc, field) => {
9
- for (const name in filters) {
10
- if (filters[name](field) === false)
11
- continue;
12
- acc[name].push(field);
13
- }
14
- return acc;
15
- }, acc);
16
- });
17
- return { fieldGroups };
18
- }
@@ -1,45 +0,0 @@
1
- import type { 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 {};
@@ -1,183 +0,0 @@
1
- import { isEqual, isNil } from 'lodash-es';
2
- import { computed, inject, nextTick, onBeforeUnmount, provide, ref, shallowRef, watch } from 'vue';
3
- export function useGroupable(options) {
4
- // Injects the registration / toggle functions from the parent scope
5
- const parentFunctions = inject(options?.group || 'item-group', null);
6
- if (isNil(parentFunctions)) {
7
- return {
8
- active: ref(false),
9
- toggle: () => {
10
- // Do nothing
11
- },
12
- activate: () => {
13
- // Do nothing
14
- },
15
- deactivate: () => {
16
- // Do nothing
17
- },
18
- };
19
- }
20
- const { register, unregister, toggle, selection, } = parentFunctions;
21
- let startActive = false;
22
- if (options?.active?.value === true)
23
- startActive = true;
24
- if (options?.value && selection.value.includes(options.value))
25
- startActive = true;
26
- const active = ref(startActive);
27
- const item = { active, value: options?.value };
28
- register(item);
29
- if (options?.active !== undefined && options.watch === true) {
30
- watch(options.active, () => {
31
- if (options.active === undefined)
32
- return;
33
- if (options.active.value === true) {
34
- if (active.value === false)
35
- toggle(item);
36
- active.value = true;
37
- }
38
- if (options.active.value === false) {
39
- if (active.value === true)
40
- toggle(item);
41
- active.value = false;
42
- }
43
- });
44
- }
45
- onBeforeUnmount(() => unregister(item));
46
- return {
47
- active,
48
- toggle: () => {
49
- toggle(item);
50
- },
51
- activate: () => {
52
- if (active.value === false)
53
- toggle(item);
54
- },
55
- deactivate: () => {
56
- if (active.value === true)
57
- toggle(item);
58
- },
59
- };
60
- }
61
- /**
62
- * Used to make a component a group parent component. Provides the registration / toggle functions
63
- * to its group children
64
- */
65
- export function useGroupableParent(state = {}, options = {}, group = 'item-group') {
66
- // References to the active state and value of the individual child items
67
- const items = shallowRef([]);
68
- // Internal copy of the selection. This allows the composition to work without the state option
69
- // being passed
70
- const internalSelection = ref([]);
71
- // Uses either the internal state, or the passed in state. Will call the onSelectionChange
72
- // handler if it's passed
73
- const selection = computed({
74
- get() {
75
- if (!isNil(state.selection) && !isNil(state.selection.value)) {
76
- return state.selection.value;
77
- }
78
- return internalSelection.value;
79
- },
80
- set(newSelection) {
81
- if (!isNil(state.onSelectionChange)) {
82
- state.onSelectionChange(newSelection);
83
- }
84
- internalSelection.value = [...newSelection];
85
- },
86
- });
87
- // Provide the needed functions to all children groupable components. Note: nested item groups
88
- // will override the item-group namespace, making nested item groups possible.
89
- provide(group, { register, unregister, toggle, selection });
90
- // Whenever the value of the selection changes, we have to update all the children's internal
91
- // states. If not, you can have an activated item that's not actually active.
92
- watch(selection, updateChildren, { immediate: true });
93
- // It takes a tick before all children are rendered, this will make sure the start state of the
94
- // children matches the start selection
95
- nextTick().then(updateChildren);
96
- watch(() => options?.mandatory?.value, (newValue, oldValue) => {
97
- if (isEqual(newValue, oldValue))
98
- return;
99
- // If you're required to select a value, make sure a value is selected on first render
100
- if (!selection.value || (selection.value.length === 0 && options?.mandatory?.value === true)) {
101
- if (items.value[0])
102
- selection.value = [getValueForItem(items.value[0])];
103
- }
104
- });
105
- // These aren't exported with any particular use in mind. It's mostly for testing purposes.
106
- // Treat them as readonly.
107
- return { items, selection, internalSelection, getValueForItem, updateChildren };
108
- // Register a child within the context of this group
109
- function register(item) {
110
- items.value = [...items.value, item];
111
- const value = getValueForItem(item);
112
- // If you're required to select a value, make sure a value is selected on first render
113
- if (selection.value.length === 0 && options?.mandatory?.value === true && items.value.length === 1) {
114
- selection.value = [value];
115
- }
116
- if (item.active.value && selection.value.includes(value) === false) {
117
- toggle(item);
118
- }
119
- }
120
- // Remove a child within the context of this group. Needed to avoid memory leaks.
121
- function unregister(item) {
122
- items.value = items.value.filter((existingItem) => {
123
- return existingItem !== item;
124
- });
125
- }
126
- // Toggle the active state for the given item
127
- function toggle(item) {
128
- if (options?.multiple?.value === true) {
129
- toggleMultiple(item);
130
- }
131
- else {
132
- toggleSingle(item);
133
- }
134
- if (!isNil(state.onToggle)) {
135
- state.onToggle(item);
136
- }
137
- }
138
- function toggleSingle(item) {
139
- const itemValue = getValueForItem(item);
140
- if (selection.value[0] === itemValue && options?.mandatory?.value !== true) {
141
- selection.value = [];
142
- return;
143
- }
144
- if (selection.value[0] !== itemValue) {
145
- selection.value = [itemValue];
146
- }
147
- }
148
- function toggleMultiple(item) {
149
- const itemValue = getValueForItem(item);
150
- // Remove the item if it is already selected. Don't remove it if it's the last item and
151
- // the mandatory option is set
152
- if (selection.value.includes(itemValue)) {
153
- if (options?.mandatory?.value === true && selection.value.length === 1) {
154
- updateChildren();
155
- return;
156
- }
157
- selection.value = selection.value.filter((value) => value !== itemValue);
158
- return;
159
- }
160
- // Don't add it if when we're already at the maximum number of selections
161
- if (options?.max?.value && options.max.value !== -1 && selection.value.length >= options.max.value) {
162
- // Even though we don't alter selection, we should flush the internal active state of
163
- // the children to make sure we don't have any invalid internal active states
164
- updateChildren();
165
- return;
166
- }
167
- // Add the selected item to the selection
168
- selection.value = [...selection.value, itemValue];
169
- }
170
- // Converts the item reference into the value that's used in the selection. This value is either
171
- // the index of the item in the items array (by default), or the custom value that's passed in
172
- // the groupable composition
173
- function getValueForItem(item) {
174
- return item.value || items.value.findIndex((child) => item === child);
175
- }
176
- // Loop over all children and make sure their internal active state matches the selection array
177
- // of the parent
178
- function updateChildren() {
179
- items.value.forEach((item) => {
180
- item.active.value = selection.value.includes(getValueForItem(item));
181
- });
182
- }
183
- }
@@ -1,29 +0,0 @@
1
- import type { Item, Query } from '@directus/types';
2
- import type { 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
- limit: Ref<Query['limit']> | ComputedRef<Query['limit']> | WritableComputedRef<Query['limit']>;
22
- sort: Ref<Query['sort']> | ComputedRef<Query['sort']> | WritableComputedRef<Query['sort']>;
23
- search: Ref<Query['search']> | ComputedRef<Query['search']> | WritableComputedRef<Query['search']>;
24
- filter: Ref<Query['filter']> | ComputedRef<Query['filter']> | WritableComputedRef<Query['filter']>;
25
- page: Ref<Query['page']> | WritableComputedRef<Query['page']>;
26
- alias?: Ref<Query['alias']> | ComputedRef<Query['alias']> | WritableComputedRef<Query['alias']>;
27
- deep?: Ref<Query['deep']> | ComputedRef<Query['deep']> | WritableComputedRef<Query['deep']>;
28
- };
29
- export declare function useItems(collection: Ref<string | null>, query: ComputedQuery): UsableItems;