b11f77df2a
## PR Description - Uses `expr-eval` to enable front components (SDK plugins) to define conditional availability as declarative expressions. - Moves shared types and constants to `twenty-shared` - Introduces a `conditionalAvailabilityExpression` field on `CommandMenuItemEntity`, allowing command menu items to store an `expr-eval` compatible expression string that is evaluated against a CommandMenuContext to determine if the item should be shown. - Creates an esbuild transform plugin `conditional-availability-transform-plugin` in `twenty-sdk` that converts TypeScript conditional availability expressions into `expr-eval` compatible syntax at build time, so SDK developers can write natural TS expressions that get transformed to evaluable strings. - Removes deprecated `forceRegisteredActionsByKey` state and its usage. - Creates `useCommandMenuContext` hook that builds the full `CommandMenuContext` object from React state, which is then passed to `useCommandMenuItemFrontComponentActions` for evaluating conditional availability expressions.
112 lines
4.5 KiB
TypeScript
112 lines
4.5 KiB
TypeScript
import { type ActivityTargetWithTargetRecord } from '@/activities/types/ActivityTargetObject';
|
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
|
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
|
import { useMultipleRecordPickerOpen } from '@/object-record/record-picker/multiple-record-picker/hooks/useMultipleRecordPickerOpen';
|
|
import { useMultipleRecordPickerPerformSearch } from '@/object-record/record-picker/multiple-record-picker/hooks/useMultipleRecordPickerPerformSearch';
|
|
import { multipleRecordPickerPickableMorphItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerPickableMorphItemsComponentState';
|
|
import { multipleRecordPickerSearchFilterComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchFilterComponentState';
|
|
import { multipleRecordPickerSearchableObjectMetadataItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchableObjectMetadataItemsComponentState';
|
|
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
|
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
|
import { useCallback } from 'react';
|
|
import { useStore } from 'jotai';
|
|
|
|
type OpenActivityTargetCellEditModeProps = {
|
|
recordPickerInstanceId: string;
|
|
activityTargetObjectRecords: ActivityTargetWithTargetRecord[];
|
|
};
|
|
|
|
// TODO: deprecate this once we are supporting one to many through relations
|
|
export const useOpenActivityTargetCellEditMode = () => {
|
|
const store = useStore();
|
|
const { performSearch: multipleRecordPickerPerformSearch } =
|
|
useMultipleRecordPickerPerformSearch();
|
|
const { openMultipleRecordPicker } = useMultipleRecordPickerOpen();
|
|
|
|
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
|
|
|
const openActivityTargetCellEditMode = useCallback(
|
|
({
|
|
recordPickerInstanceId,
|
|
activityTargetObjectRecords,
|
|
}: OpenActivityTargetCellEditModeProps) => {
|
|
const objectMetadataItems = store
|
|
.get(objectMetadataItemsState.atom)
|
|
.filter(
|
|
(objectMetadataItem) =>
|
|
objectMetadataItem.isSearchable &&
|
|
objectMetadataItem.isActive &&
|
|
objectMetadataItem.nameSingular !== CoreObjectNameSingular.Task &&
|
|
objectMetadataItem.nameSingular !== CoreObjectNameSingular.Note &&
|
|
objectMetadataItem.nameSingular !==
|
|
CoreObjectNameSingular.WorkspaceMember,
|
|
);
|
|
|
|
store.set(
|
|
multipleRecordPickerPickableMorphItemsComponentState.atomFamily({
|
|
instanceId: recordPickerInstanceId,
|
|
}),
|
|
activityTargetObjectRecords.map((activityTargetObjectRecord) => ({
|
|
recordId: activityTargetObjectRecord.targetObject.id,
|
|
objectMetadataId:
|
|
activityTargetObjectRecord.targetObjectMetadataItem.id,
|
|
isSelected: true,
|
|
isMatchingSearchFilter: true,
|
|
})),
|
|
);
|
|
|
|
store.set(
|
|
multipleRecordPickerSearchableObjectMetadataItemsComponentState.atomFamily(
|
|
{
|
|
instanceId: recordPickerInstanceId,
|
|
},
|
|
),
|
|
objectMetadataItems,
|
|
);
|
|
|
|
store.set(
|
|
multipleRecordPickerSearchFilterComponentState.atomFamily({
|
|
instanceId: recordPickerInstanceId,
|
|
}),
|
|
'',
|
|
);
|
|
|
|
openMultipleRecordPicker(recordPickerInstanceId);
|
|
|
|
multipleRecordPickerPerformSearch({
|
|
multipleRecordPickerInstanceId: recordPickerInstanceId,
|
|
forceSearchFilter: '',
|
|
forceSearchableObjectMetadataItems: objectMetadataItems,
|
|
forcePickableMorphItems: activityTargetObjectRecords.map(
|
|
(activityTargetObjectRecord) => ({
|
|
recordId: activityTargetObjectRecord.targetObject.id,
|
|
objectMetadataId:
|
|
activityTargetObjectRecord.targetObjectMetadataItem.id,
|
|
isSelected: true,
|
|
isMatchingSearchFilter: true,
|
|
}),
|
|
),
|
|
});
|
|
|
|
pushFocusItemToFocusStack({
|
|
focusId: recordPickerInstanceId,
|
|
component: {
|
|
type: FocusComponentType.DROPDOWN,
|
|
instanceId: recordPickerInstanceId,
|
|
},
|
|
globalHotkeysConfig: {
|
|
enableGlobalHotkeysConflictingWithKeyboard: false,
|
|
},
|
|
});
|
|
},
|
|
[
|
|
multipleRecordPickerPerformSearch,
|
|
openMultipleRecordPicker,
|
|
pushFocusItemToFocusStack,
|
|
store,
|
|
],
|
|
);
|
|
|
|
return { openActivityTargetCellEditMode };
|
|
};
|