@directus/composables
Advanced tools
Comparing version 10.1.3 to 10.1.4
@@ -1,10 +0,156 @@ | ||
export * from './use-collection.js'; | ||
export * from './use-custom-selection.js'; | ||
export * from './use-element-size.js'; | ||
export * from './use-filter-fields.js'; | ||
export * from './use-groupable.js'; | ||
export * from './use-items.js'; | ||
export * from './use-layout.js'; | ||
export * from './use-size-class.js'; | ||
export * from './use-sync.js'; | ||
export * from './use-system.js'; | ||
import { AppCollection, Field, Item, Query, RefRecord } from '@directus/types'; | ||
import { ComputedRef, Ref, WritableComputedRef, Component } from 'vue'; | ||
import { AppExtensionConfigs } from '@directus/extensions'; | ||
import { AxiosInstance } from 'axios'; | ||
type UsableCollection = { | ||
info: ComputedRef<AppCollection | null>; | ||
fields: ComputedRef<Field[]>; | ||
defaults: ComputedRef<Record<string, any>>; | ||
primaryKeyField: ComputedRef<Field | null>; | ||
userCreatedField: ComputedRef<Field | null>; | ||
sortField: ComputedRef<string | null>; | ||
isSingleton: ComputedRef<boolean>; | ||
accountabilityScope: ComputedRef<'all' | 'activity' | null>; | ||
}; | ||
declare function useCollection(collectionKey: string | Ref<string | null>): UsableCollection; | ||
type UsableCustomSelection = { | ||
otherValue: Ref<string | null>; | ||
usesOtherValue: ComputedRef<boolean>; | ||
}; | ||
declare function useCustomSelection(currentValue: Ref<string | null>, items: Ref<any[]>, emit: (event: string | null) => void): UsableCustomSelection; | ||
type OtherValue = { | ||
key: string; | ||
value: string; | ||
}; | ||
type UsableCustomSelectionMultiple = { | ||
otherValues: Ref<OtherValue[]>; | ||
addOtherValue: (value?: string) => void; | ||
setOtherValue: (key: string, newValue: string | null) => void; | ||
}; | ||
declare function useCustomSelectionMultiple(currentValues: Ref<string[] | null>, items: Ref<any[]>, emit: (event: string[] | null) => void): UsableCustomSelectionMultiple; | ||
declare global { | ||
interface Window { | ||
ResizeObserver: any; | ||
} | ||
} | ||
declare function useElementSize<T extends Element>(target: T | Ref<T> | Ref<undefined>): { | ||
width: Ref<number>; | ||
height: Ref<number>; | ||
}; | ||
declare function useFilterFields<T extends string>(fields: Ref<Field[]>, filters: Record<T, (field: Field) => boolean>): { | ||
fieldGroups: ComputedRef<Record<Extract<T, string>, Field[]>>; | ||
}; | ||
type GroupableInstance = { | ||
active: Ref<boolean>; | ||
value: string | number | undefined; | ||
}; | ||
/** | ||
* Used to make child item part of the group context. Needs to be used in a component that is a child | ||
* of a component that has the `useGroupableParent` composition enabled | ||
*/ | ||
type GroupableOptions = { | ||
value?: string | number; | ||
group?: string; | ||
active?: Ref<boolean>; | ||
watch?: boolean; | ||
}; | ||
type UsableGroupable = { | ||
active: Ref<boolean>; | ||
toggle: () => void; | ||
activate: () => void; | ||
deactivate: () => void; | ||
}; | ||
declare function useGroupable(options?: GroupableOptions): UsableGroupable; | ||
type GroupableParentState = { | ||
selection?: Ref<(string | number)[] | undefined> | Ref<readonly (string | number)[] | undefined>; | ||
onSelectionChange?: (newSelectionValues: readonly (string | number)[]) => void; | ||
onToggle?: (item: GroupableInstance) => void; | ||
}; | ||
type GroupableParentOptions = { | ||
mandatory?: Ref<boolean>; | ||
max?: Ref<number>; | ||
multiple?: Ref<boolean>; | ||
}; | ||
type UsableGroupableParent = { | ||
items: Ref<GroupableInstance[]>; | ||
selection: Ref<readonly (string | number)[]>; | ||
internalSelection: Ref<(string | number)[]>; | ||
getValueForItem: (item: GroupableInstance) => string | number; | ||
updateChildren: () => void; | ||
}; | ||
/** | ||
* Used to make a component a group parent component. Provides the registration / toggle functions | ||
* to its group children | ||
*/ | ||
declare function useGroupableParent(state?: GroupableParentState, options?: GroupableParentOptions, group?: string): UsableGroupableParent; | ||
type ManualSortData = { | ||
item: string | number; | ||
to: string | number; | ||
}; | ||
type UsableItems = { | ||
itemCount: Ref<number | null>; | ||
totalCount: Ref<number | null>; | ||
items: Ref<Item[]>; | ||
totalPages: ComputedRef<number>; | ||
loading: Ref<boolean>; | ||
error: Ref<any>; | ||
changeManualSort: (data: ManualSortData) => Promise<void>; | ||
getItems: () => Promise<void>; | ||
getTotalCount: () => Promise<void>; | ||
getItemCount: () => Promise<void>; | ||
}; | ||
type ComputedQuery = { | ||
fields: Ref<Query['fields']> | ComputedRef<Query['fields']> | WritableComputedRef<Query['fields']>; | ||
limit: Ref<Query['limit']> | ComputedRef<Query['limit']> | WritableComputedRef<Query['limit']>; | ||
sort: Ref<Query['sort']> | ComputedRef<Query['sort']> | WritableComputedRef<Query['sort']>; | ||
search: Ref<Query['search']> | ComputedRef<Query['search']> | WritableComputedRef<Query['search']>; | ||
filter: Ref<Query['filter']> | ComputedRef<Query['filter']> | WritableComputedRef<Query['filter']>; | ||
page: Ref<Query['page']> | WritableComputedRef<Query['page']>; | ||
alias?: Ref<Query['alias']> | ComputedRef<Query['alias']> | WritableComputedRef<Query['alias']>; | ||
deep?: Ref<Query['deep']> | ComputedRef<Query['deep']> | WritableComputedRef<Query['deep']>; | ||
}; | ||
declare function useItems(collection: Ref<string | null>, query: ComputedQuery): UsableItems; | ||
declare function useLayout<Options = any, Query = any>(layoutId: Ref<string | null>): { | ||
layoutWrapper: ComputedRef<Component>; | ||
}; | ||
declare const sizeProps: { | ||
xSmall: { | ||
type: BooleanConstructor; | ||
default: boolean; | ||
}; | ||
small: { | ||
type: BooleanConstructor; | ||
default: boolean; | ||
}; | ||
large: { | ||
type: BooleanConstructor; | ||
default: boolean; | ||
}; | ||
xLarge: { | ||
type: BooleanConstructor; | ||
default: boolean; | ||
}; | ||
}; | ||
interface SizeProps { | ||
xSmall?: boolean; | ||
small?: boolean; | ||
large?: boolean; | ||
xLarge?: boolean; | ||
} | ||
declare function useSizeClass<T>(props: T & SizeProps): ComputedRef<string | null>; | ||
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]>; | ||
declare function useStores(): Record<string, any>; | ||
declare function useApi(): AxiosInstance; | ||
declare function useExtensions(): RefRecord<AppExtensionConfigs>; | ||
export { ComputedQuery, GroupableInstance, GroupableOptions, ManualSortData, UsableCollection, UsableCustomSelection, UsableGroupable, UsableItems, sizeProps, useApi, useCollection, useCustomSelection, useCustomSelectionMultiple, useElementSize, useExtensions, useFilterFields, useGroupable, useGroupableParent, useItems, useLayout, useSizeClass, useStores, useSync }; |
@@ -1,10 +0,733 @@ | ||
export * from './use-collection.js'; | ||
export * from './use-custom-selection.js'; | ||
export * from './use-element-size.js'; | ||
export * from './use-filter-fields.js'; | ||
export * from './use-groupable.js'; | ||
export * from './use-items.js'; | ||
export * from './use-layout.js'; | ||
export * from './use-size-class.js'; | ||
export * from './use-sync.js'; | ||
export * from './use-system.js'; | ||
// src/use-collection.ts | ||
import { computed, ref } from "vue"; | ||
// src/use-system.ts | ||
import { API_INJECT, EXTENSIONS_INJECT, STORES_INJECT } from "@directus/constants"; | ||
import { inject } from "vue"; | ||
function useStores() { | ||
const stores = inject(STORES_INJECT); | ||
if (!stores) | ||
throw new Error("[useStores]: The stores could not be found."); | ||
return stores; | ||
} | ||
function useApi() { | ||
const api = inject(API_INJECT); | ||
if (!api) | ||
throw new Error("[useApi]: The api could not be found."); | ||
return api; | ||
} | ||
function useExtensions() { | ||
const extensions = inject(EXTENSIONS_INJECT); | ||
if (!extensions) | ||
throw new Error("[useExtensions]: The extensions could not be found."); | ||
return extensions; | ||
} | ||
// src/use-collection.ts | ||
function useCollection(collectionKey) { | ||
const { useCollectionsStore, useFieldsStore } = useStores(); | ||
const collectionsStore = useCollectionsStore(); | ||
const fieldsStore = useFieldsStore(); | ||
const collection = typeof collectionKey === "string" ? ref(collectionKey) : collectionKey; | ||
const info = computed(() => { | ||
return collectionsStore.collections.find(({ collection: key }) => key === collection.value) || null; | ||
}); | ||
const fields = computed(() => { | ||
if (!collection.value) | ||
return []; | ||
return fieldsStore.getFieldsForCollectionSorted(collection.value); | ||
}); | ||
const defaults = computed(() => { | ||
if (!fields.value) | ||
return {}; | ||
const defaults2 = {}; | ||
for (const field of fields.value) { | ||
if (field.schema !== null && "default_value" in field.schema) { | ||
defaults2[field.field] = field.schema.default_value; | ||
} | ||
} | ||
return defaults2; | ||
}); | ||
const primaryKeyField = computed(() => { | ||
return fields.value.find((field) => field.collection === collection.value && field.schema?.is_primary_key === true) || null; | ||
}); | ||
const userCreatedField = computed(() => { | ||
return fields.value?.find((field) => (field.meta?.special || []).includes("user_created")) || null; | ||
}); | ||
const sortField = computed(() => { | ||
return info.value?.meta?.sort_field || null; | ||
}); | ||
const isSingleton = computed(() => { | ||
return info.value?.meta?.singleton === true; | ||
}); | ||
const accountabilityScope = computed(() => { | ||
if (!info.value) | ||
return null; | ||
if (!info.value.meta) | ||
return null; | ||
return info.value.meta.accountability; | ||
}); | ||
return { info, fields, defaults, primaryKeyField, userCreatedField, sortField, isSingleton, accountabilityScope }; | ||
} | ||
// src/use-custom-selection.ts | ||
import { nanoid } from "nanoid"; | ||
import { computed as computed2, ref as ref2, watch } from "vue"; | ||
function useCustomSelection(currentValue, items, emit) { | ||
const localOtherValue = ref2(""); | ||
const otherValue = computed2({ | ||
get() { | ||
return localOtherValue.value || (usesOtherValue.value ? currentValue.value : ""); | ||
}, | ||
set(newValue) { | ||
if (newValue === null) { | ||
localOtherValue.value = ""; | ||
emit(null); | ||
} else { | ||
localOtherValue.value = newValue; | ||
emit(newValue); | ||
} | ||
} | ||
}); | ||
const usesOtherValue = computed2(() => { | ||
if (items.value === null) | ||
return false; | ||
const values = items.value.map((item) => item.value); | ||
return currentValue.value !== null && currentValue.value.length > 0 && values.includes(currentValue.value) === false; | ||
}); | ||
return { otherValue, usesOtherValue }; | ||
} | ||
function useCustomSelectionMultiple(currentValues, items, emit) { | ||
const otherValues = ref2([]); | ||
watch( | ||
currentValues, | ||
(newValue) => { | ||
if (newValue === null) | ||
return; | ||
if (!Array.isArray(newValue)) | ||
return; | ||
if (items.value === null) | ||
return; | ||
newValue.forEach((value) => { | ||
if (items.value === null) | ||
return; | ||
const values = items.value.map((item) => item.value); | ||
const existsInValues = values.includes(value); | ||
if (!existsInValues) { | ||
const other = otherValues.value.map((o) => o.value); | ||
const existsInOtherValues = other.includes(value); | ||
if (!existsInOtherValues) { | ||
addOtherValue(value); | ||
} | ||
} | ||
}); | ||
}, | ||
{ immediate: true } | ||
); | ||
return { otherValues, addOtherValue, setOtherValue }; | ||
function addOtherValue(value = "") { | ||
otherValues.value = [ | ||
...otherValues.value, | ||
{ | ||
key: nanoid(), | ||
value | ||
} | ||
]; | ||
} | ||
function setOtherValue(key, newValue) { | ||
const previousValue = otherValues.value.find((o) => o.key === key); | ||
const valueWithoutPrevious = (currentValues.value || []).filter( | ||
(val) => val !== previousValue?.value | ||
); | ||
if (newValue === null) { | ||
otherValues.value = otherValues.value.filter((o) => o.key !== key); | ||
if (valueWithoutPrevious.length === 0) { | ||
emit(null); | ||
} else { | ||
emit(valueWithoutPrevious); | ||
} | ||
} else { | ||
otherValues.value = otherValues.value.map((otherValue) => { | ||
if (otherValue.key === key) | ||
otherValue.value = newValue; | ||
return otherValue; | ||
}); | ||
if (valueWithoutPrevious.length === currentValues.value?.length) { | ||
emit(valueWithoutPrevious); | ||
} else { | ||
emit([...valueWithoutPrevious, newValue]); | ||
} | ||
} | ||
} | ||
} | ||
// src/use-element-size.ts | ||
import { isNil } from "lodash-es"; | ||
import { isRef, onMounted, onUnmounted, ref as ref3 } from "vue"; | ||
function useElementSize(target) { | ||
const width = ref3(0); | ||
const height = ref3(0); | ||
const resizeObserver = new ResizeObserver(([entry]) => { | ||
if (entry === void 0) | ||
return; | ||
width.value = entry.contentRect.width; | ||
height.value = entry.contentRect.height; | ||
}); | ||
onMounted(() => { | ||
const t = isRef(target) ? target.value : target; | ||
if (!isNil(t)) { | ||
resizeObserver.observe(t); | ||
} | ||
}); | ||
onUnmounted(() => { | ||
resizeObserver.disconnect(); | ||
}); | ||
return { width, height }; | ||
} | ||
// src/use-filter-fields.ts | ||
import { computed as computed3 } from "vue"; | ||
function useFilterFields(fields, filters) { | ||
const fieldGroups = computed3(() => { | ||
const acc = {}; | ||
for (const name in filters) { | ||
acc[name] = []; | ||
} | ||
return fields.value.reduce((acc2, field) => { | ||
for (const name in filters) { | ||
if (filters[name](field) === false) | ||
continue; | ||
acc2[name].push(field); | ||
} | ||
return acc2; | ||
}, acc); | ||
}); | ||
return { fieldGroups }; | ||
} | ||
// src/use-groupable.ts | ||
import { isEqual, isNil as isNil2 } from "lodash-es"; | ||
import { computed as computed4, inject as inject2, nextTick, onBeforeUnmount, provide, ref as ref4, shallowRef, watch as watch2 } from "vue"; | ||
function useGroupable(options) { | ||
const parentFunctions = inject2(options?.group || "item-group", null); | ||
if (isNil2(parentFunctions)) { | ||
return { | ||
active: ref4(false), | ||
toggle: () => { | ||
}, | ||
activate: () => { | ||
}, | ||
deactivate: () => { | ||
} | ||
}; | ||
} | ||
const { | ||
register, | ||
unregister, | ||
toggle, | ||
selection | ||
} = parentFunctions; | ||
let startActive = false; | ||
if (options?.active?.value === true) | ||
startActive = true; | ||
if (options?.value && selection.value.includes(options.value)) | ||
startActive = true; | ||
const active = ref4(startActive); | ||
const item = { active, value: options?.value }; | ||
register(item); | ||
if (options?.active !== void 0 && options.watch === true) { | ||
watch2(options.active, () => { | ||
if (options.active === void 0) | ||
return; | ||
if (options.active.value === true) { | ||
if (active.value === false) | ||
toggle(item); | ||
active.value = true; | ||
} | ||
if (options.active.value === false) { | ||
if (active.value === true) | ||
toggle(item); | ||
active.value = false; | ||
} | ||
}); | ||
} | ||
onBeforeUnmount(() => unregister(item)); | ||
return { | ||
active, | ||
toggle: () => { | ||
toggle(item); | ||
}, | ||
activate: () => { | ||
if (active.value === false) | ||
toggle(item); | ||
}, | ||
deactivate: () => { | ||
if (active.value === true) | ||
toggle(item); | ||
} | ||
}; | ||
} | ||
function useGroupableParent(state = {}, options = {}, group = "item-group") { | ||
const items = shallowRef([]); | ||
const internalSelection = ref4([]); | ||
const selection = computed4({ | ||
get() { | ||
if (!isNil2(state.selection) && !isNil2(state.selection.value)) { | ||
return state.selection.value; | ||
} | ||
return internalSelection.value; | ||
}, | ||
set(newSelection) { | ||
if (!isNil2(state.onSelectionChange)) { | ||
state.onSelectionChange(newSelection); | ||
} | ||
internalSelection.value = [...newSelection]; | ||
} | ||
}); | ||
provide(group, { register, unregister, toggle, selection }); | ||
watch2(selection, updateChildren, { immediate: true }); | ||
nextTick().then(updateChildren); | ||
watch2( | ||
() => options?.mandatory?.value, | ||
(newValue, oldValue) => { | ||
if (isEqual(newValue, oldValue)) | ||
return; | ||
if (!selection.value || selection.value.length === 0 && options?.mandatory?.value === true) { | ||
if (items.value[0]) | ||
selection.value = [getValueForItem(items.value[0])]; | ||
} | ||
} | ||
); | ||
return { items, selection, internalSelection, getValueForItem, updateChildren }; | ||
function register(item) { | ||
items.value = [...items.value, item]; | ||
const value = getValueForItem(item); | ||
if (selection.value.length === 0 && options?.mandatory?.value === true && items.value.length === 1) { | ||
selection.value = [value]; | ||
} | ||
if (item.active.value && selection.value.includes(value) === false) { | ||
toggle(item); | ||
} | ||
} | ||
function unregister(item) { | ||
items.value = items.value.filter((existingItem) => { | ||
return existingItem !== item; | ||
}); | ||
} | ||
function toggle(item) { | ||
if (options?.multiple?.value === true) { | ||
toggleMultiple(item); | ||
} else { | ||
toggleSingle(item); | ||
} | ||
if (!isNil2(state.onToggle)) { | ||
state.onToggle(item); | ||
} | ||
} | ||
function toggleSingle(item) { | ||
const itemValue = getValueForItem(item); | ||
if (selection.value[0] === itemValue && options?.mandatory?.value !== true) { | ||
selection.value = []; | ||
return; | ||
} | ||
if (selection.value[0] !== itemValue) { | ||
selection.value = [itemValue]; | ||
} | ||
} | ||
function toggleMultiple(item) { | ||
const itemValue = getValueForItem(item); | ||
if (selection.value.includes(itemValue)) { | ||
if (options?.mandatory?.value === true && selection.value.length === 1) { | ||
updateChildren(); | ||
return; | ||
} | ||
selection.value = selection.value.filter((value) => value !== itemValue); | ||
return; | ||
} | ||
if (options?.max?.value && options.max.value !== -1 && selection.value.length >= options.max.value) { | ||
updateChildren(); | ||
return; | ||
} | ||
selection.value = [...selection.value, itemValue]; | ||
} | ||
function getValueForItem(item) { | ||
return item.value || items.value.findIndex((child) => item === child); | ||
} | ||
function updateChildren() { | ||
items.value.forEach((item) => { | ||
item.active.value = selection.value.includes(getValueForItem(item)); | ||
}); | ||
} | ||
} | ||
// src/use-items.ts | ||
import { getEndpoint, moveInArray } from "@directus/utils"; | ||
import axios from "axios"; | ||
import { isEqual as isEqual2, throttle } from "lodash-es"; | ||
import { computed as computed5, ref as ref5, unref, watch as watch3 } from "vue"; | ||
function useItems(collection, query) { | ||
const api = useApi(); | ||
const { primaryKeyField } = useCollection(collection); | ||
const { fields, limit, sort, search, filter, page, alias: queryAlias, deep: queryDeep } = query; | ||
const alias = queryAlias ?? ref5(); | ||
const deep = queryDeep ?? ref5(); | ||
const endpoint = computed5(() => { | ||
if (!collection.value) | ||
return null; | ||
return getEndpoint(collection.value); | ||
}); | ||
const items = ref5([]); | ||
const loading = ref5(false); | ||
const error = ref5(null); | ||
const itemCount = ref5(null); | ||
const totalCount = ref5(null); | ||
const totalPages = computed5(() => { | ||
if (itemCount.value === null) | ||
return 1; | ||
if (itemCount.value < (unref(limit) ?? 100)) | ||
return 1; | ||
return Math.ceil(itemCount.value / (unref(limit) ?? 100)); | ||
}); | ||
const existingRequests = { | ||
items: null, | ||
total: null, | ||
filter: null | ||
}; | ||
let loadingTimeout = null; | ||
const fetchItems = throttle(getItems, 500); | ||
watch3( | ||
[collection, limit, sort, search, filter, fields, page, alias, deep], | ||
async (after, before) => { | ||
if (isEqual2(after, before)) | ||
return; | ||
const [newCollection, newLimit, newSort, newSearch, newFilter] = after; | ||
const [oldCollection, oldLimit, oldSort, oldSearch, oldFilter] = before; | ||
if (!newCollection || !query) | ||
return; | ||
if (newCollection !== oldCollection) { | ||
reset(); | ||
} | ||
if (!isEqual2(newFilter, oldFilter) || !isEqual2(newSort, oldSort) || newLimit !== oldLimit || newSearch !== oldSearch) { | ||
if (oldCollection) { | ||
page.value = 1; | ||
} | ||
} | ||
if (newCollection !== oldCollection || !isEqual2(newFilter, oldFilter) || newSearch !== oldSearch) { | ||
getItemCount(); | ||
} | ||
fetchItems(); | ||
}, | ||
{ deep: true, immediate: true } | ||
); | ||
return { | ||
itemCount, | ||
totalCount, | ||
items, | ||
totalPages, | ||
loading, | ||
error, | ||
changeManualSort, | ||
getItems, | ||
getItemCount, | ||
getTotalCount | ||
}; | ||
async function getItems() { | ||
if (!endpoint.value) | ||
return; | ||
let isCurrentRequestCanceled = false; | ||
if (existingRequests.items) | ||
existingRequests.items.abort(); | ||
existingRequests.items = new AbortController(); | ||
error.value = null; | ||
if (loadingTimeout) { | ||
clearTimeout(loadingTimeout); | ||
} | ||
loadingTimeout = setTimeout(() => { | ||
loading.value = true; | ||
}, 150); | ||
if (unref(totalCount) === null) { | ||
getTotalCount(); | ||
} | ||
let fieldsToFetch = [...unref(fields) ?? []]; | ||
if (!unref(fields)?.includes("*") && primaryKeyField.value && fieldsToFetch.includes(primaryKeyField.value.field) === false) { | ||
fieldsToFetch.push(primaryKeyField.value.field); | ||
} | ||
fieldsToFetch = fieldsToFetch.filter((field) => field.startsWith("$") === false); | ||
try { | ||
const response = await api.get(endpoint.value, { | ||
params: { | ||
limit: unref(limit), | ||
fields: fieldsToFetch, | ||
...alias ? { alias: unref(alias) } : {}, | ||
sort: unref(sort), | ||
page: unref(page), | ||
search: unref(search), | ||
filter: unref(filter), | ||
deep: unref(deep) | ||
}, | ||
signal: existingRequests.items.signal | ||
}); | ||
let fetchedItems = response.data.data; | ||
existingRequests.items = null; | ||
if (collection.value === "directus_files") { | ||
fetchedItems = fetchedItems.map((file) => ({ | ||
...file, | ||
$thumbnail: file | ||
})); | ||
} | ||
items.value = fetchedItems; | ||
if (page && fetchedItems.length === 0 && page?.value !== 1) { | ||
page.value = 1; | ||
} | ||
} catch (err) { | ||
if (axios.isCancel(err)) { | ||
isCurrentRequestCanceled = true; | ||
} else { | ||
error.value = err; | ||
} | ||
} finally { | ||
if (loadingTimeout && !isCurrentRequestCanceled) { | ||
clearTimeout(loadingTimeout); | ||
loadingTimeout = null; | ||
} | ||
if (!loadingTimeout) | ||
loading.value = false; | ||
} | ||
} | ||
function reset() { | ||
items.value = []; | ||
totalCount.value = null; | ||
itemCount.value = null; | ||
} | ||
async function changeManualSort({ item, to }) { | ||
const pk = primaryKeyField.value?.field; | ||
if (!pk) | ||
return; | ||
const fromIndex = items.value.findIndex((existing) => existing[pk] === item); | ||
const toIndex = items.value.findIndex((existing) => existing[pk] === to); | ||
items.value = moveInArray(items.value, fromIndex, toIndex); | ||
const endpoint2 = computed5(() => `/utils/sort/${collection.value}`); | ||
await api.post(endpoint2.value, { item, to }); | ||
} | ||
async function getTotalCount() { | ||
if (!endpoint.value) | ||
return; | ||
try { | ||
if (existingRequests.total) | ||
existingRequests.total.abort(); | ||
existingRequests.total = new AbortController(); | ||
const aggregate = primaryKeyField.value ? { | ||
countDistinct: primaryKeyField.value.field | ||
} : { | ||
count: "*" | ||
}; | ||
const response = await api.get(endpoint.value, { | ||
params: { | ||
aggregate | ||
}, | ||
signal: existingRequests.total.signal | ||
}); | ||
const count = primaryKeyField.value ? Number(response.data.data[0].countDistinct[primaryKeyField.value.field]) : Number(response.data.data[0].count); | ||
existingRequests.total = null; | ||
totalCount.value = count; | ||
} catch (err) { | ||
if (!axios.isCancel(err)) { | ||
throw err; | ||
} | ||
} | ||
} | ||
async function getItemCount() { | ||
if (!endpoint.value) | ||
return; | ||
try { | ||
if (existingRequests.filter) | ||
existingRequests.filter.abort(); | ||
existingRequests.filter = new AbortController(); | ||
const aggregate = primaryKeyField.value ? { | ||
countDistinct: primaryKeyField.value.field | ||
} : { | ||
count: "*" | ||
}; | ||
const response = await api.get(endpoint.value, { | ||
params: { | ||
filter: unref(filter), | ||
search: unref(search), | ||
aggregate | ||
}, | ||
signal: existingRequests.filter.signal | ||
}); | ||
const count = primaryKeyField.value ? Number(response.data.data[0].countDistinct[primaryKeyField.value.field]) : Number(response.data.data[0].count); | ||
existingRequests.filter = null; | ||
itemCount.value = count; | ||
} catch (err) { | ||
if (!axios.isCancel(err)) { | ||
throw err; | ||
} | ||
} | ||
} | ||
} | ||
// src/use-layout.ts | ||
import { computed as computed6, defineComponent, reactive, toRefs } from "vue"; | ||
var NAME_SUFFIX = "wrapper"; | ||
var WRITABLE_PROPS = ["selection", "layoutOptions", "layoutQuery"]; | ||
function isWritableProp(prop) { | ||
return WRITABLE_PROPS.includes(prop); | ||
} | ||
function createLayoutWrapper(layout) { | ||
return defineComponent({ | ||
name: `${layout.id}-${NAME_SUFFIX}`, | ||
props: { | ||
collection: { | ||
type: String, | ||
required: true | ||
}, | ||
selection: { | ||
type: Array, | ||
default: () => [] | ||
}, | ||
layoutOptions: { | ||
type: Object, | ||
default: () => ({}) | ||
}, | ||
layoutQuery: { | ||
type: Object, | ||
default: () => ({}) | ||
}, | ||
layoutProps: { | ||
type: Object, | ||
default: () => ({}) | ||
}, | ||
filter: { | ||
type: Object, | ||
default: null | ||
}, | ||
filterUser: { | ||
type: Object, | ||
default: null | ||
}, | ||
filterSystem: { | ||
type: Object, | ||
default: null | ||
}, | ||
search: { | ||
type: String, | ||
default: null | ||
}, | ||
showSelect: { | ||
type: String, | ||
default: "multiple" | ||
}, | ||
selectMode: { | ||
type: Boolean, | ||
default: false | ||
}, | ||
readonly: { | ||
type: Boolean, | ||
default: false | ||
}, | ||
resetPreset: { | ||
type: Function, | ||
default: null | ||
}, | ||
clearFilters: { | ||
type: Function, | ||
default: null | ||
} | ||
}, | ||
emits: WRITABLE_PROPS.map((prop) => `update:${prop}`), | ||
setup(props, { emit }) { | ||
const state = reactive({ ...layout.setup(props, { emit }), ...toRefs(props) }); | ||
for (const key in state) { | ||
state[`onUpdate:${key}`] = (value) => { | ||
if (isWritableProp(key)) { | ||
emit(`update:${key}`, value); | ||
} else if (!Object.keys(props).includes(key)) { | ||
state[key] = value; | ||
} | ||
}; | ||
} | ||
return { state }; | ||
}, | ||
render(ctx) { | ||
return ctx.$slots.default !== void 0 ? ctx.$slots.default({ layoutState: ctx.state }) : null; | ||
} | ||
}); | ||
} | ||
function useLayout(layoutId) { | ||
const { layouts } = useExtensions(); | ||
const layoutWrappers = computed6(() => layouts.value.map((layout) => createLayoutWrapper(layout))); | ||
const layoutWrapper = computed6(() => { | ||
const layout = layoutWrappers.value.find((layout2) => layout2.name === `${layoutId.value}-${NAME_SUFFIX}`); | ||
if (layout === void 0) { | ||
return layoutWrappers.value.find((layout2) => layout2.name === `tabular-${NAME_SUFFIX}`); | ||
} | ||
return layout; | ||
}); | ||
return { layoutWrapper }; | ||
} | ||
// src/use-size-class.ts | ||
import { computed as computed7 } from "vue"; | ||
var sizeProps = { | ||
xSmall: { | ||
type: Boolean, | ||
default: false | ||
}, | ||
small: { | ||
type: Boolean, | ||
default: false | ||
}, | ||
large: { | ||
type: Boolean, | ||
default: false | ||
}, | ||
xLarge: { | ||
type: Boolean, | ||
default: false | ||
} | ||
}; | ||
function useSizeClass(props) { | ||
const sizeClass = computed7(() => { | ||
if (props.xSmall) | ||
return "x-small"; | ||
if (props.small) | ||
return "small"; | ||
if (props.large) | ||
return "large"; | ||
if (props.xLarge) | ||
return "x-large"; | ||
return null; | ||
}); | ||
return sizeClass; | ||
} | ||
// src/use-sync.ts | ||
import { computed as computed8 } from "vue"; | ||
function useSync(props, key, emit) { | ||
return computed8({ | ||
get() { | ||
return props[key]; | ||
}, | ||
set(newVal) { | ||
emit(`update:${key}`, newVal); | ||
} | ||
}); | ||
} | ||
export { | ||
sizeProps, | ||
useApi, | ||
useCollection, | ||
useCustomSelection, | ||
useCustomSelectionMultiple, | ||
useElementSize, | ||
useExtensions, | ||
useFilterFields, | ||
useGroupable, | ||
useGroupableParent, | ||
useItems, | ||
useLayout, | ||
useSizeClass, | ||
useStores, | ||
useSync | ||
}; |
{ | ||
"name": "@directus/composables", | ||
"version": "10.1.3", | ||
"version": "10.1.4", | ||
"description": "Shared Vue composables for Directus use", | ||
@@ -29,4 +29,4 @@ "homepage": "https://directus.io", | ||
"vue": "3.3.4", | ||
"@directus/constants": "10.2.3", | ||
"@directus/utils": "10.0.11" | ||
"@directus/constants": "11.0.0", | ||
"@directus/utils": "11.0.0" | ||
}, | ||
@@ -37,12 +37,14 @@ "devDependencies": { | ||
"@vue/test-utils": "2.3.2", | ||
"typescript": "5.0.4", | ||
"tsup": "7.2.0", | ||
"typescript": "5.2.2", | ||
"vitest": "0.31.1", | ||
"@directus/tsconfig": "1.0.0", | ||
"@directus/types": "10.1.6" | ||
"@directus/extensions": "0.0.1", | ||
"@directus/tsconfig": "1.0.1", | ||
"@directus/types": "11.0.0" | ||
}, | ||
"scripts": { | ||
"build": "tsc --project tsconfig.prod.json", | ||
"dev": "tsc --watch", | ||
"build": "tsup src/index.ts --format=esm --dts", | ||
"dev": "tsup src/index.ts --format=esm --dts --watch", | ||
"test": "vitest --watch=false" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
30207
9
5
869
1
+ Added@directus/constants@11.0.0(transitive)
+ Added@directus/utils@11.0.0(transitive)
- Removed@directus/constants@10.2.3(transitive)
- Removed@directus/utils@10.0.11(transitive)
Updated@directus/constants@11.0.0
Updated@directus/utils@11.0.0