@ngbplatform/ui 1.3.0
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/LICENSE +201 -0
- package/README.md +44 -0
- package/package.json +61 -0
- package/public/favicon.svg +6 -0
- package/public/silent-check-sso.html +8 -0
- package/src/assets/ngb_logo.svg +17 -0
- package/src/assets/ngb_logo_color.svg +17 -0
- package/src/index.ts +1023 -0
- package/src/ngb/README.md +27 -0
- package/src/ngb/accounting/NgbAccountingPeriodClosingPage.vue +1157 -0
- package/src/ngb/accounting/NgbChartOfAccountEditor.vue +590 -0
- package/src/ngb/accounting/NgbChartOfAccountsPage.vue +420 -0
- package/src/ngb/accounting/NgbGeneralJournalEntryEditPage.vue +796 -0
- package/src/ngb/accounting/NgbGeneralJournalEntryLinesEditor.vue +454 -0
- package/src/ngb/accounting/NgbGeneralJournalEntryListPage.vue +186 -0
- package/src/ngb/accounting/api.ts +79 -0
- package/src/ngb/accounting/generalJournalEntry.ts +127 -0
- package/src/ngb/accounting/generalJournalEntryApi.ts +112 -0
- package/src/ngb/accounting/generalJournalEntryTypes.ts +155 -0
- package/src/ngb/accounting/navigation.ts +83 -0
- package/src/ngb/accounting/periodClosing.ts +75 -0
- package/src/ngb/accounting/periodClosingApi.ts +60 -0
- package/src/ngb/accounting/periodClosingTypes.ts +73 -0
- package/src/ngb/accounting/types.ts +57 -0
- package/src/ngb/api/audit.ts +23 -0
- package/src/ngb/api/catalogs.ts +50 -0
- package/src/ngb/api/contracts.ts +285 -0
- package/src/ngb/api/documents.ts +170 -0
- package/src/ngb/api/http.ts +446 -0
- package/src/ngb/api/lookups.ts +14 -0
- package/src/ngb/api/types.ts +6 -0
- package/src/ngb/auth/index.ts +13 -0
- package/src/ngb/auth/keycloak.ts +374 -0
- package/src/ngb/auth/router.ts +35 -0
- package/src/ngb/auth/types.ts +12 -0
- package/src/ngb/auth/useAuthStore.ts +209 -0
- package/src/ngb/command-palette/NgbCommandPaletteDialog.vue +342 -0
- package/src/ngb/command-palette/NgbCommandPaletteEmptyState.vue +55 -0
- package/src/ngb/command-palette/NgbCommandPaletteFooterHints.vue +29 -0
- package/src/ngb/command-palette/api.ts +10 -0
- package/src/ngb/command-palette/config.ts +16 -0
- package/src/ngb/command-palette/presentation.ts +85 -0
- package/src/ngb/command-palette/search.ts +130 -0
- package/src/ngb/command-palette/staticItems.ts +243 -0
- package/src/ngb/command-palette/storage.ts +45 -0
- package/src/ngb/command-palette/store.ts +716 -0
- package/src/ngb/command-palette/types.ts +120 -0
- package/src/ngb/command-palette/useCommandPaletteHotkeys.ts +27 -0
- package/src/ngb/command-palette/useCommandPalettePageContext.ts +18 -0
- package/src/ngb/components/NgbConfirmDialog.vue +63 -0
- package/src/ngb/components/NgbDialog.vue +44 -0
- package/src/ngb/components/NgbDrawer.vue +156 -0
- package/src/ngb/components/NgbHeaderActionCluster.vue +140 -0
- package/src/ngb/components/NgbModalShell.vue +99 -0
- package/src/ngb/components/NgbToolbar.vue +14 -0
- package/src/ngb/components/forms/NgbFormLayout.vue +13 -0
- package/src/ngb/components/forms/NgbFormRow.vue +21 -0
- package/src/ngb/components/forms/NgbFormSection.vue +16 -0
- package/src/ngb/components/forms/NgbValidationSummary.vue +18 -0
- package/src/ngb/components/navigation/NgbNavigationTree.vue +68 -0
- package/src/ngb/components/navigation/NgbTreeNode.vue +94 -0
- package/src/ngb/components/register/NgbColumnChooser.vue +101 -0
- package/src/ngb/components/register/NgbRegisterGrid.vue +473 -0
- package/src/ngb/components/register/registerTypes.ts +51 -0
- package/src/ngb/components/register/useRegisterColumnResize.ts +99 -0
- package/src/ngb/components/register/useRegisterColumnState.ts +220 -0
- package/src/ngb/components/register/useRegisterRows.ts +209 -0
- package/src/ngb/components/register/useRegisterViewport.ts +56 -0
- package/src/ngb/editor/NgbDocumentEffectsPage.vue +512 -0
- package/src/ngb/editor/NgbDocumentFlowPage.vue +409 -0
- package/src/ngb/editor/NgbDocumentPrintPage.vue +712 -0
- package/src/ngb/editor/NgbEditorDiscardDialog.vue +40 -0
- package/src/ngb/editor/NgbEntityAuditSidebar.vue +288 -0
- package/src/ngb/editor/NgbEntityEditor.vue +250 -0
- package/src/ngb/editor/NgbEntityEditorDrawerActions.vue +88 -0
- package/src/ngb/editor/NgbEntityEditorHeader.vue +114 -0
- package/src/ngb/editor/catalogNavigation.ts +13 -0
- package/src/ngb/editor/config.ts +252 -0
- package/src/ngb/editor/documentCopyDraft.ts +245 -0
- package/src/ngb/editor/documentEffects.ts +43 -0
- package/src/ngb/editor/documentEffectsDateFormatting.ts +18 -0
- package/src/ngb/editor/documentNavigation.ts +115 -0
- package/src/ngb/editor/documentStatus.ts +45 -0
- package/src/ngb/editor/entityEditorErrors.ts +162 -0
- package/src/ngb/editor/entityEditorPersistence.ts +241 -0
- package/src/ngb/editor/extensions.ts +40 -0
- package/src/ngb/editor/types.ts +295 -0
- package/src/ngb/editor/useConfiguredEntityEditorDocumentActions.ts +183 -0
- package/src/ngb/editor/useDocumentEditorDrawerState.ts +152 -0
- package/src/ngb/editor/useEditorDrawerState.ts +88 -0
- package/src/ngb/editor/useEntityEditorBusinessContext.ts +97 -0
- package/src/ngb/editor/useEntityEditorCapabilities.ts +190 -0
- package/src/ngb/editor/useEntityEditorCommandPalette.ts +154 -0
- package/src/ngb/editor/useEntityEditorCommitHandlers.ts +102 -0
- package/src/ngb/editor/useEntityEditorHeaderActions.ts +253 -0
- package/src/ngb/editor/useEntityEditorLeaveGuard.ts +66 -0
- package/src/ngb/editor/useEntityEditorNavigationActions.ts +235 -0
- package/src/ngb/editor/useEntityEditorOutputs.ts +59 -0
- package/src/ngb/editor/useEntityEditorPageActions.ts +57 -0
- package/src/ngb/editor/useRouteQueryEditorDrawer.ts +173 -0
- package/src/ngb/env/runtimeConfig.ts +25 -0
- package/src/ngb/lookup/config.ts +42 -0
- package/src/ngb/lookup/defaultConfig.ts +136 -0
- package/src/ngb/lookup/navigation.ts +76 -0
- package/src/ngb/lookup/prefetch.ts +40 -0
- package/src/ngb/lookup/store.ts +261 -0
- package/src/ngb/metadata/NgbDocumentListFiltersDrawer.vue +78 -0
- package/src/ngb/metadata/NgbDocumentPeriodFilter.vue +53 -0
- package/src/ngb/metadata/NgbEntityForm.vue +84 -0
- package/src/ngb/metadata/NgbEntityFormFieldsBlock.vue +110 -0
- package/src/ngb/metadata/NgbEntityListPageHeader.vue +80 -0
- package/src/ngb/metadata/NgbFilterFieldControl.vue +104 -0
- package/src/ngb/metadata/NgbMetadataCatalogEditPage.vue +42 -0
- package/src/ngb/metadata/NgbMetadataCatalogListPage.vue +269 -0
- package/src/ngb/metadata/NgbMetadataDocumentEditPage.vue +68 -0
- package/src/ngb/metadata/NgbMetadataDocumentListPage.vue +353 -0
- package/src/ngb/metadata/NgbMetadataFieldRenderer.vue +124 -0
- package/src/ngb/metadata/NgbMetadataLookupControl.vue +92 -0
- package/src/ngb/metadata/NgbRecycleBinFilter.vue +53 -0
- package/src/ngb/metadata/NgbRegisterPageLayout.vue +143 -0
- package/src/ngb/metadata/config.ts +28 -0
- package/src/ngb/metadata/dataTypes.ts +27 -0
- package/src/ngb/metadata/entityForm.ts +114 -0
- package/src/ngb/metadata/entityModel.ts +34 -0
- package/src/ngb/metadata/entityValueFormatting.ts +83 -0
- package/src/ngb/metadata/fieldRendererState.ts +84 -0
- package/src/ngb/metadata/filtering.ts +153 -0
- package/src/ngb/metadata/lookup.ts +25 -0
- package/src/ngb/metadata/monthPagedListQuery.ts +108 -0
- package/src/ngb/metadata/normalization.ts +100 -0
- package/src/ngb/metadata/referenceHydration.ts +99 -0
- package/src/ngb/metadata/register.ts +135 -0
- package/src/ngb/metadata/routePages.ts +86 -0
- package/src/ngb/metadata/store.ts +35 -0
- package/src/ngb/metadata/types.ts +276 -0
- package/src/ngb/metadata/useMetadataListFilters.ts +306 -0
- package/src/ngb/metadata/useMetadataRegisterPageData.ts +246 -0
- package/src/ngb/metadata/useValidationFocus.ts +49 -0
- package/src/ngb/primitives/NgbBadge.vue +31 -0
- package/src/ngb/primitives/NgbButton.vue +53 -0
- package/src/ngb/primitives/NgbCheckbox.vue +33 -0
- package/src/ngb/primitives/NgbDatePicker.vue +141 -0
- package/src/ngb/primitives/NgbIcon.vue +545 -0
- package/src/ngb/primitives/NgbInput.vue +57 -0
- package/src/ngb/primitives/NgbLookup.vue +264 -0
- package/src/ngb/primitives/NgbMonthPicker.vue +132 -0
- package/src/ngb/primitives/NgbMultiSelect.vue +179 -0
- package/src/ngb/primitives/NgbPeriodPicker.vue +125 -0
- package/src/ngb/primitives/NgbPickerNavButton.vue +31 -0
- package/src/ngb/primitives/NgbPickerPopover.vue +79 -0
- package/src/ngb/primitives/NgbRadioGroup.vue +53 -0
- package/src/ngb/primitives/NgbSelect.vue +108 -0
- package/src/ngb/primitives/NgbStatusIcon.vue +63 -0
- package/src/ngb/primitives/NgbSwitch.vue +42 -0
- package/src/ngb/primitives/NgbTabs.vue +116 -0
- package/src/ngb/primitives/NgbToastHost.vue +49 -0
- package/src/ngb/primitives/NgbTooltip.vue +27 -0
- package/src/ngb/primitives/iconNames.ts +83 -0
- package/src/ngb/primitives/toast.ts +66 -0
- package/src/ngb/primitives/useAsyncComboboxQuery.ts +85 -0
- package/src/ngb/primitives/useFloatingDropdownPosition.ts +129 -0
- package/src/ngb/reporting/NgbReportComposerCollectionSection.vue +106 -0
- package/src/ngb/reporting/NgbReportComposerPanel.vue +1187 -0
- package/src/ngb/reporting/NgbReportDateRangeFilter.vue +54 -0
- package/src/ngb/reporting/NgbReportPage.vue +1124 -0
- package/src/ngb/reporting/NgbReportSheet.vue +467 -0
- package/src/ngb/reporting/api.ts +35 -0
- package/src/ngb/reporting/composer.ts +904 -0
- package/src/ngb/reporting/config.ts +120 -0
- package/src/ngb/reporting/defaultConfig.ts +15 -0
- package/src/ngb/reporting/lookupFilters.ts +43 -0
- package/src/ngb/reporting/navigation.ts +181 -0
- package/src/ngb/reporting/pageHelpers.ts +73 -0
- package/src/ngb/reporting/pageSession.ts +92 -0
- package/src/ngb/reporting/paging.ts +66 -0
- package/src/ngb/reporting/types.ts +333 -0
- package/src/ngb/router/backNavigation.ts +122 -0
- package/src/ngb/router/queryParams.ts +129 -0
- package/src/ngb/router/queryState.ts +121 -0
- package/src/ngb/router/routeAliases.ts +66 -0
- package/src/ngb/router/routeParams.ts +15 -0
- package/src/ngb/router/shareLink.ts +39 -0
- package/src/ngb/security/NgbAccessAuditPanel.vue +32 -0
- package/src/ngb/security/NgbAccessDeniedState.vue +27 -0
- package/src/ngb/security/NgbEffectiveAccessPanel.vue +60 -0
- package/src/ngb/security/NgbPermissionMatrix.vue +160 -0
- package/src/ngb/security/NgbRoleEditorPage.vue +319 -0
- package/src/ngb/security/NgbRolesPage.vue +125 -0
- package/src/ngb/security/NgbUserEditorPage.vue +615 -0
- package/src/ngb/security/NgbUsersPage.vue +125 -0
- package/src/ngb/security/api.ts +91 -0
- package/src/ngb/security/audit.ts +21 -0
- package/src/ngb/security/permissions.ts +65 -0
- package/src/ngb/security/types.ts +157 -0
- package/src/ngb/security/useAccessStore.ts +69 -0
- package/src/ngb/site/NgbDashboardAsOfToolbar.vue +100 -0
- package/src/ngb/site/NgbDashboardStatusBanner.vue +91 -0
- package/src/ngb/site/NgbLogo.vue +44 -0
- package/src/ngb/site/NgbPageHeader.vue +49 -0
- package/src/ngb/site/NgbSiteShell.vue +286 -0
- package/src/ngb/site/NgbSiteSidebar.vue +270 -0
- package/src/ngb/site/NgbTopBar.vue +425 -0
- package/src/ngb/site/NgbTrendChart.vue +224 -0
- package/src/ngb/site/dashboardData.ts +324 -0
- package/src/ngb/site/mainMenuStore.ts +49 -0
- package/src/ngb/site/types.ts +31 -0
- package/src/ngb/site/useDashboardPageState.ts +98 -0
- package/src/ngb/site/useTheme.ts +95 -0
- package/src/ngb/utils/clone.ts +43 -0
- package/src/ngb/utils/dateValues.ts +107 -0
- package/src/ngb/utils/errorMessage.ts +14 -0
- package/src/ngb/utils/guid.ts +21 -0
- package/src/ngb/utils/stableValue.ts +21 -0
- package/src/ngb/utils/storage.ts +135 -0
- package/src/styles/tailwind.css +165 -0
- package/src/vite/publicAssets.ts +104 -0
- package/vite-public-assets.js +68 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import type { NgbIconName } from '../primitives/iconNames';
|
|
2
|
+
import type {
|
|
3
|
+
DocumentStatusValue,
|
|
4
|
+
LookupHint,
|
|
5
|
+
LookupSource,
|
|
6
|
+
LookupStoreApi,
|
|
7
|
+
RecordPayload,
|
|
8
|
+
} from '../metadata/types';
|
|
9
|
+
|
|
10
|
+
export type EditorKind = 'catalog' | 'document';
|
|
11
|
+
export type EditorMode = 'page' | 'drawer';
|
|
12
|
+
export type EditorChangeReason = 'markForDeletion' | 'unmarkForDeletion' | 'post' | 'unpost';
|
|
13
|
+
|
|
14
|
+
export type EntityEditorContext = {
|
|
15
|
+
kind: EditorKind;
|
|
16
|
+
typeCode: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type EntityEditorFlags = {
|
|
20
|
+
canSave: boolean;
|
|
21
|
+
isDirty: boolean;
|
|
22
|
+
loading: boolean;
|
|
23
|
+
saving: boolean;
|
|
24
|
+
canExpand: boolean;
|
|
25
|
+
canDelete: boolean;
|
|
26
|
+
canMarkForDeletion: boolean;
|
|
27
|
+
canUnmarkForDeletion: boolean;
|
|
28
|
+
canPost: boolean;
|
|
29
|
+
canUnpost: boolean;
|
|
30
|
+
canShowAudit: boolean;
|
|
31
|
+
canShareLink: boolean;
|
|
32
|
+
extras?: Record<string, boolean | undefined>;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type EntityHeaderIconAction = {
|
|
36
|
+
key: string;
|
|
37
|
+
title: string;
|
|
38
|
+
icon: NgbIconName;
|
|
39
|
+
disabled?: boolean;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type DocumentHeaderCoreActionKey =
|
|
43
|
+
| 'openCompactPage'
|
|
44
|
+
| 'openFullPage'
|
|
45
|
+
| 'copyDocument'
|
|
46
|
+
| 'printDocument'
|
|
47
|
+
| 'toggleMarkForDeletion'
|
|
48
|
+
| 'save'
|
|
49
|
+
| 'togglePost'
|
|
50
|
+
| 'openEffectsPage'
|
|
51
|
+
| 'openDocumentFlowPage'
|
|
52
|
+
| 'openAuditLog'
|
|
53
|
+
| 'copyShareLink';
|
|
54
|
+
|
|
55
|
+
export type DocumentHeaderActionKey = DocumentHeaderCoreActionKey | (string & {});
|
|
56
|
+
|
|
57
|
+
export type DocumentHeaderActionItem = {
|
|
58
|
+
key: DocumentHeaderActionKey;
|
|
59
|
+
title: string;
|
|
60
|
+
icon: NgbIconName;
|
|
61
|
+
disabled?: boolean;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type DocumentHeaderActionGroup = {
|
|
65
|
+
key: string;
|
|
66
|
+
label: string;
|
|
67
|
+
items: DocumentHeaderActionItem[];
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export type DocumentRecord = {
|
|
71
|
+
id: string;
|
|
72
|
+
number?: string | null;
|
|
73
|
+
display?: string | null;
|
|
74
|
+
payload: RecordPayload;
|
|
75
|
+
status: DocumentStatusValue;
|
|
76
|
+
isMarkedForDeletion?: boolean;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export type RelationshipGraphNode = {
|
|
80
|
+
nodeId: string;
|
|
81
|
+
kind: number;
|
|
82
|
+
typeCode: string;
|
|
83
|
+
entityId: string;
|
|
84
|
+
title: string;
|
|
85
|
+
subtitle?: string | null;
|
|
86
|
+
documentStatus?: DocumentStatusValue | null;
|
|
87
|
+
depth?: number | null;
|
|
88
|
+
amount?: number | null;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export type RelationshipGraphEdge = {
|
|
92
|
+
fromNodeId: string;
|
|
93
|
+
toNodeId: string;
|
|
94
|
+
relationshipType: string;
|
|
95
|
+
label?: string | null;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export type RelationshipGraph = {
|
|
99
|
+
nodes: RelationshipGraphNode[];
|
|
100
|
+
edges: RelationshipGraphEdge[];
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export type EffectAccount = {
|
|
104
|
+
accountId: string;
|
|
105
|
+
code: string;
|
|
106
|
+
name: string;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export type EffectDimensionValue = {
|
|
110
|
+
dimensionId: string;
|
|
111
|
+
valueId: string;
|
|
112
|
+
display: string;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export type EffectResourceValue = {
|
|
116
|
+
code: string;
|
|
117
|
+
value: number;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export type AccountingEntryEffect = {
|
|
121
|
+
entryId: string | number;
|
|
122
|
+
documentId?: string | null;
|
|
123
|
+
occurredAtUtc: string;
|
|
124
|
+
debitAccount?: EffectAccount | null;
|
|
125
|
+
creditAccount?: EffectAccount | null;
|
|
126
|
+
debitAccountId?: string | null;
|
|
127
|
+
creditAccountId?: string | null;
|
|
128
|
+
amount: number;
|
|
129
|
+
isStorno?: boolean;
|
|
130
|
+
debitDimensionSetId?: string | null;
|
|
131
|
+
creditDimensionSetId?: string | null;
|
|
132
|
+
debitDimensions?: EffectDimensionValue[] | null;
|
|
133
|
+
creditDimensions?: EffectDimensionValue[] | null;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export type OperationalRegisterMovementEffect = {
|
|
137
|
+
registerId?: string | null;
|
|
138
|
+
registerCode: string;
|
|
139
|
+
registerName?: string | null;
|
|
140
|
+
movementId: string | number;
|
|
141
|
+
documentId?: string | null;
|
|
142
|
+
occurredAtUtc: string;
|
|
143
|
+
periodMonth?: string | null;
|
|
144
|
+
isStorno?: boolean;
|
|
145
|
+
dimensionSetId?: string | null;
|
|
146
|
+
dimensions?: EffectDimensionValue[] | null;
|
|
147
|
+
resources: EffectResourceValue[] | Record<string, unknown>;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export type ReferenceRegisterWriteEffect = {
|
|
151
|
+
registerId?: string | null;
|
|
152
|
+
registerCode: string;
|
|
153
|
+
registerName?: string | null;
|
|
154
|
+
recordId: string | number;
|
|
155
|
+
documentId?: string | null;
|
|
156
|
+
periodUtc?: string | null;
|
|
157
|
+
periodBucketUtc?: string | null;
|
|
158
|
+
recordedAtUtc: string;
|
|
159
|
+
dimensionSetId?: string | null;
|
|
160
|
+
dimensions?: EffectDimensionValue[] | null;
|
|
161
|
+
fields: Record<string, unknown>;
|
|
162
|
+
isTombstone: boolean;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export type DocumentUiActionReason = {
|
|
166
|
+
errorCode: string;
|
|
167
|
+
message: string;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export type DocumentUiEffects = {
|
|
171
|
+
isPosted: boolean;
|
|
172
|
+
canEdit: boolean;
|
|
173
|
+
canPost: boolean;
|
|
174
|
+
canUnpost: boolean;
|
|
175
|
+
canRepost: boolean;
|
|
176
|
+
canApply: boolean;
|
|
177
|
+
disabledReasons?: Record<string, DocumentUiActionReason[]> | null;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export type DocumentEffects = {
|
|
181
|
+
accountingEntries: AccountingEntryEffect[];
|
|
182
|
+
operationalRegisterMovements: OperationalRegisterMovementEffect[];
|
|
183
|
+
referenceRegisterWrites: ReferenceRegisterWriteEffect[];
|
|
184
|
+
ui?: DocumentUiEffects | null;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
export type EntityEditorHandle<TDocumentEffects = DocumentEffects | null> = {
|
|
188
|
+
save: () => Promise<void>;
|
|
189
|
+
load: () => Promise<void>;
|
|
190
|
+
openFullPage: () => void;
|
|
191
|
+
openCompactPage: () => void;
|
|
192
|
+
closePage: () => void;
|
|
193
|
+
toggleMarkForDeletion: () => void;
|
|
194
|
+
togglePost: () => void;
|
|
195
|
+
markForDeletion: () => Promise<void>;
|
|
196
|
+
unmarkForDeletion: () => Promise<void>;
|
|
197
|
+
deleteEntity: () => Promise<void>;
|
|
198
|
+
post: () => Promise<void>;
|
|
199
|
+
unpost: () => Promise<void>;
|
|
200
|
+
getDocumentEffects: () => TDocumentEffects;
|
|
201
|
+
reloadDocumentEffects: () => Promise<TDocumentEffects>;
|
|
202
|
+
copyShareLink: () => Promise<void>;
|
|
203
|
+
copyDocument: () => void;
|
|
204
|
+
printDocument: () => void;
|
|
205
|
+
openAuditLog: () => void;
|
|
206
|
+
openAudit: () => void;
|
|
207
|
+
closeAuditLog: () => void;
|
|
208
|
+
getIsDirty: () => boolean;
|
|
209
|
+
getCanSave: () => boolean;
|
|
210
|
+
getFlags: () => EntityEditorFlags;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export type AuditFieldChange = {
|
|
214
|
+
fieldPath: string;
|
|
215
|
+
oldValueJson?: string | null;
|
|
216
|
+
newValueJson?: string | null;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
export type AuditActor = {
|
|
220
|
+
userId?: string | null;
|
|
221
|
+
displayName?: string | null;
|
|
222
|
+
email?: string | null;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export type AuditEvent = {
|
|
226
|
+
auditEventId: string;
|
|
227
|
+
entityKind: number;
|
|
228
|
+
entityId: string;
|
|
229
|
+
actionCode: string;
|
|
230
|
+
actor?: AuditActor | null;
|
|
231
|
+
occurredAtUtc: string;
|
|
232
|
+
correlationId?: string | null;
|
|
233
|
+
metadataJson?: string | null;
|
|
234
|
+
changes: AuditFieldChange[];
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export type AuditCursor = {
|
|
238
|
+
occurredAtUtc: string;
|
|
239
|
+
auditEventId: string;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export type AuditLogPage = {
|
|
243
|
+
items: AuditEvent[];
|
|
244
|
+
nextCursor?: AuditCursor | null;
|
|
245
|
+
limit: number;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
export type EditorAuditLoadOptions = {
|
|
249
|
+
afterOccurredAtUtc?: string | null;
|
|
250
|
+
afterAuditEventId?: string | null;
|
|
251
|
+
limit?: number;
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
export type EditorAuditBehavior = {
|
|
255
|
+
hiddenFieldNames?: string[];
|
|
256
|
+
explicitFieldLabels?: Record<string, string>;
|
|
257
|
+
actionTitles?: Record<string, string>;
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
export type EditorDocumentEffectsBehavior = {
|
|
261
|
+
prefetchRelatedLabels?: (args: {
|
|
262
|
+
documentType: string;
|
|
263
|
+
documentId: string;
|
|
264
|
+
effects: DocumentEffects;
|
|
265
|
+
lookupStore: LookupStoreApi | null;
|
|
266
|
+
}) => Promise<void> | void;
|
|
267
|
+
resolveAccountLabel?: (args: {
|
|
268
|
+
account: EffectAccount | null | undefined;
|
|
269
|
+
accountId: string | null | undefined;
|
|
270
|
+
lookupStore: LookupStoreApi | null;
|
|
271
|
+
}) => string;
|
|
272
|
+
resolveDimensionDisplay?: (args: {
|
|
273
|
+
item: EffectDimensionValue | null | undefined;
|
|
274
|
+
lookupStore: LookupStoreApi | null;
|
|
275
|
+
}) => string;
|
|
276
|
+
resolveFieldValue?: (args: {
|
|
277
|
+
documentType: string;
|
|
278
|
+
documentId: string;
|
|
279
|
+
document: DocumentRecord | null;
|
|
280
|
+
fieldKey: string;
|
|
281
|
+
value: unknown;
|
|
282
|
+
fields: Record<string, unknown>;
|
|
283
|
+
lookupStore: LookupStoreApi | null;
|
|
284
|
+
}) => string | null | undefined;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
export type EditorPrintLookupHintArgs = {
|
|
288
|
+
documentType: string;
|
|
289
|
+
fieldKey: string;
|
|
290
|
+
lookup?: LookupSource | null;
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
export type EditorPrintBehavior = {
|
|
294
|
+
resolveLookupHint?: (args: EditorPrintLookupHintArgs) => LookupHint | null;
|
|
295
|
+
};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { computed, type ComputedRef, type Ref, ref, watch } from 'vue';
|
|
2
|
+
|
|
3
|
+
import { getDocumentDerivationActions, deriveDocument } from '../api/documents';
|
|
4
|
+
import type { DocumentDerivationActionDto } from '../api/contracts';
|
|
5
|
+
import type { EntityFormModel } from '../metadata/types';
|
|
6
|
+
import type { DocumentTypeMetadata } from '../metadata/types';
|
|
7
|
+
import { resolveNgbEditorDocumentActions, resolveNgbEditorRouting } from './config';
|
|
8
|
+
import type { EditorErrorState } from './entityEditorErrors';
|
|
9
|
+
import type { DocumentHeaderActionGroup, DocumentHeaderActionItem, DocumentUiEffects, EditorKind } from './types';
|
|
10
|
+
|
|
11
|
+
type DocumentMetadataStoreLike = {
|
|
12
|
+
ensureDocumentType: (documentType: string) => Promise<DocumentTypeMetadata>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type UseConfiguredEntityEditorDocumentActionsArgs = {
|
|
16
|
+
kind: ComputedRef<EditorKind>;
|
|
17
|
+
typeCode: ComputedRef<string>;
|
|
18
|
+
currentId: ComputedRef<string | null>;
|
|
19
|
+
model: Ref<EntityFormModel>;
|
|
20
|
+
uiEffects: ComputedRef<DocumentUiEffects | null>;
|
|
21
|
+
loading: ComputedRef<boolean>;
|
|
22
|
+
saving: ComputedRef<boolean>;
|
|
23
|
+
requestNavigate: (to: string | null | undefined) => void;
|
|
24
|
+
metadataStore: DocumentMetadataStoreLike;
|
|
25
|
+
setEditorError: (value: EditorErrorState | null) => void;
|
|
26
|
+
normalizeEditorError: (cause: unknown) => EditorErrorState;
|
|
27
|
+
loadDerivationActions?: (documentType: string, id: string) => Promise<DocumentDerivationActionDto[]>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function useConfiguredEntityEditorDocumentActions(
|
|
31
|
+
args: UseConfiguredEntityEditorDocumentActionsArgs,
|
|
32
|
+
) {
|
|
33
|
+
const configuredActions = computed(() => {
|
|
34
|
+
if (args.kind.value !== 'document') return [];
|
|
35
|
+
if (!args.currentId.value) return [];
|
|
36
|
+
|
|
37
|
+
return resolveNgbEditorDocumentActions({
|
|
38
|
+
context: {
|
|
39
|
+
kind: 'document',
|
|
40
|
+
typeCode: args.typeCode.value,
|
|
41
|
+
},
|
|
42
|
+
documentId: args.currentId.value,
|
|
43
|
+
model: args.model.value,
|
|
44
|
+
uiEffects: args.uiEffects.value,
|
|
45
|
+
loading: args.loading.value,
|
|
46
|
+
saving: args.saving.value,
|
|
47
|
+
navigate: args.requestNavigate,
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const derivationActions = ref<DocumentDerivationActionDto[]>([]);
|
|
52
|
+
const derivationTitles = ref<Record<string, string>>({});
|
|
53
|
+
const loadDerivationActions = args.loadDerivationActions ?? getDocumentDerivationActions;
|
|
54
|
+
|
|
55
|
+
watch(
|
|
56
|
+
[args.kind, args.typeCode, args.currentId],
|
|
57
|
+
([kind, typeCode, documentId], _, onCleanup) => {
|
|
58
|
+
derivationActions.value = [];
|
|
59
|
+
derivationTitles.value = {};
|
|
60
|
+
|
|
61
|
+
if (kind !== 'document' || !documentId) return;
|
|
62
|
+
|
|
63
|
+
let cancelled = false;
|
|
64
|
+
onCleanup(() => {
|
|
65
|
+
cancelled = true;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
void (async () => {
|
|
69
|
+
try {
|
|
70
|
+
const actions = await loadDerivationActions(typeCode, documentId);
|
|
71
|
+
const titleEntries = await Promise.all(actions.map(async (action) => {
|
|
72
|
+
const title = await resolveDerivationTitle(args.metadataStore, action);
|
|
73
|
+
return [action.code, title] as const;
|
|
74
|
+
}));
|
|
75
|
+
|
|
76
|
+
if (cancelled) return;
|
|
77
|
+
|
|
78
|
+
derivationActions.value = actions;
|
|
79
|
+
derivationTitles.value = Object.fromEntries(titleEntries);
|
|
80
|
+
} catch {
|
|
81
|
+
if (cancelled) return;
|
|
82
|
+
derivationActions.value = [];
|
|
83
|
+
derivationTitles.value = {};
|
|
84
|
+
}
|
|
85
|
+
})();
|
|
86
|
+
},
|
|
87
|
+
{ immediate: true },
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const derivedCreateActions = computed(() =>
|
|
91
|
+
derivationActions.value
|
|
92
|
+
.map((action) => ({
|
|
93
|
+
action,
|
|
94
|
+
title: derivationTitles.value[action.code] ?? action.name,
|
|
95
|
+
}))
|
|
96
|
+
.sort((left, right) => left.title.localeCompare(right.title, undefined, { sensitivity: 'base' }))
|
|
97
|
+
.map(({ action, title }) => ({
|
|
98
|
+
item: {
|
|
99
|
+
key: `derive:${action.code}`,
|
|
100
|
+
title,
|
|
101
|
+
icon: 'file-text' as const,
|
|
102
|
+
disabled: args.loading.value || args.saving.value,
|
|
103
|
+
},
|
|
104
|
+
group: {
|
|
105
|
+
key: 'create',
|
|
106
|
+
label: 'Create',
|
|
107
|
+
},
|
|
108
|
+
run: async () => {
|
|
109
|
+
const relationshipType = action.relationshipCodes
|
|
110
|
+
.map((value) => String(value ?? '').trim())
|
|
111
|
+
.find((value) => value.length > 0);
|
|
112
|
+
|
|
113
|
+
if (!relationshipType || !args.currentId.value) return;
|
|
114
|
+
|
|
115
|
+
const document = await deriveDocument(action.toTypeCode, {
|
|
116
|
+
sourceDocumentId: args.currentId.value,
|
|
117
|
+
relationshipType,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
args.requestNavigate(resolveNgbEditorRouting().buildDocumentFullPageUrl(action.toTypeCode, document.id));
|
|
121
|
+
},
|
|
122
|
+
})));
|
|
123
|
+
|
|
124
|
+
const actions = computed(() => [...configuredActions.value, ...derivedCreateActions.value]);
|
|
125
|
+
|
|
126
|
+
const extraPrimaryActions = computed<DocumentHeaderActionItem[]>(() =>
|
|
127
|
+
actions.value
|
|
128
|
+
.filter((action) => !action.group)
|
|
129
|
+
.map((action) => action.item),
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const extraMoreActionGroups = computed<DocumentHeaderActionGroup[]>(() => {
|
|
133
|
+
const buckets = new Map<string, DocumentHeaderActionGroup>();
|
|
134
|
+
|
|
135
|
+
for (const action of actions.value) {
|
|
136
|
+
if (!action.group) continue;
|
|
137
|
+
|
|
138
|
+
const current = buckets.get(action.group.key) ?? {
|
|
139
|
+
key: action.group.key,
|
|
140
|
+
label: action.group.label,
|
|
141
|
+
items: [],
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
current.items.push(action.item);
|
|
145
|
+
buckets.set(action.group.key, current);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return Array.from(buckets.values());
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
function handleConfiguredAction(actionKey: string): boolean {
|
|
152
|
+
const match = actions.value.find((action) => action.item.key === actionKey);
|
|
153
|
+
if (!match) return false;
|
|
154
|
+
if (match.item.disabled) return true;
|
|
155
|
+
|
|
156
|
+
args.setEditorError(null);
|
|
157
|
+
void Promise.resolve(match.run()).catch((cause) => {
|
|
158
|
+
args.setEditorError(args.normalizeEditorError(cause));
|
|
159
|
+
});
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
extraPrimaryActions,
|
|
165
|
+
extraMoreActionGroups,
|
|
166
|
+
handleConfiguredAction,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function resolveDerivationTitle(
|
|
171
|
+
metadataStore: DocumentMetadataStoreLike,
|
|
172
|
+
action: DocumentDerivationActionDto,
|
|
173
|
+
): Promise<string> {
|
|
174
|
+
try {
|
|
175
|
+
const metadata = await metadataStore.ensureDocumentType(action.toTypeCode);
|
|
176
|
+
const title = String(metadata.displayName ?? '').trim();
|
|
177
|
+
if (title) return title;
|
|
178
|
+
} catch {
|
|
179
|
+
// Ignore metadata lookup failures and fall back to the derivation definition name.
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return String(action.name ?? '').trim() || action.toTypeCode;
|
|
183
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { computed, ref, watch, type ComputedRef } from 'vue';
|
|
2
|
+
import type { RouteLocationNormalizedLoaded, Router } from 'vue-router';
|
|
3
|
+
|
|
4
|
+
import type { EntityFormModel, RecordPayload } from '../metadata/types';
|
|
5
|
+
import { normalizeSingleQueryValue, replaceCleanRouteQuery } from '../router/queryParams';
|
|
6
|
+
import { readDocumentCopyDraft, type DocumentCopyDraftSnapshot } from './documentCopyDraft';
|
|
7
|
+
import { buildDocumentFullPageUrl } from './documentNavigation';
|
|
8
|
+
|
|
9
|
+
export const DOCUMENT_EDITOR_DRAWER_QUERY_KEYS = ['panel', 'id', 'copyDraft'] as const;
|
|
10
|
+
|
|
11
|
+
const DOCUMENT_EDITOR_DRAWER_QUERY_KEY_SET = new Set<string>(DOCUMENT_EDITOR_DRAWER_QUERY_KEYS);
|
|
12
|
+
|
|
13
|
+
export type DocumentEditorDrawerMode = 'closed' | 'new' | 'edit';
|
|
14
|
+
|
|
15
|
+
export type UseDocumentEditorDrawerStateArgs = {
|
|
16
|
+
route: RouteLocationNormalizedLoaded;
|
|
17
|
+
router: Router;
|
|
18
|
+
documentType: ComputedRef<string>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function isDocumentEditorDrawerQueryKey(key: string): boolean {
|
|
22
|
+
return DOCUMENT_EDITOR_DRAWER_QUERY_KEY_SET.has(String(key));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function useDocumentEditorDrawerState(
|
|
26
|
+
args: UseDocumentEditorDrawerStateArgs,
|
|
27
|
+
) {
|
|
28
|
+
const drawerMode = ref<DocumentEditorDrawerMode>('closed');
|
|
29
|
+
const drawerId = ref<string | null>(null);
|
|
30
|
+
const drawerCopyDraftToken = ref<string | null>(null);
|
|
31
|
+
|
|
32
|
+
const isPanelOpen = computed(() =>
|
|
33
|
+
drawerMode.value === 'new' || (drawerMode.value === 'edit' && !!drawerId.value),
|
|
34
|
+
);
|
|
35
|
+
const currentEditorId = computed(() =>
|
|
36
|
+
drawerMode.value === 'edit' ? drawerId.value : null,
|
|
37
|
+
);
|
|
38
|
+
const initialCopyDraft = computed<DocumentCopyDraftSnapshot | null>(() =>
|
|
39
|
+
drawerMode.value === 'new' && args.documentType.value
|
|
40
|
+
? readDocumentCopyDraft(drawerCopyDraftToken.value, args.documentType.value)
|
|
41
|
+
: null,
|
|
42
|
+
);
|
|
43
|
+
const initialFields = computed<EntityFormModel | null>(() => initialCopyDraft.value?.fields ?? null);
|
|
44
|
+
const initialParts = computed<RecordPayload['parts'] | null>(() => initialCopyDraft.value?.parts ?? null);
|
|
45
|
+
const expandTo = computed(() => {
|
|
46
|
+
if (!args.documentType.value) return null;
|
|
47
|
+
if (drawerMode.value === 'new') return buildDocumentFullPageUrl(args.documentType.value);
|
|
48
|
+
if (currentEditorId.value) return buildDocumentFullPageUrl(args.documentType.value, currentEditorId.value);
|
|
49
|
+
return null;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
function setDrawerState(
|
|
53
|
+
mode: DocumentEditorDrawerMode,
|
|
54
|
+
id: string | null = null,
|
|
55
|
+
copyDraftToken: string | null = null,
|
|
56
|
+
) {
|
|
57
|
+
drawerMode.value = mode;
|
|
58
|
+
drawerId.value = mode === 'edit' ? id : null;
|
|
59
|
+
drawerCopyDraftToken.value = mode === 'new' ? copyDraftToken : null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function clearDrawerQueryIfPresent() {
|
|
63
|
+
if (
|
|
64
|
+
args.route.query.panel == null
|
|
65
|
+
&& args.route.query.id == null
|
|
66
|
+
&& args.route.query.copyDraft == null
|
|
67
|
+
) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
await replaceCleanRouteQuery(args.route, args.router, {
|
|
72
|
+
panel: undefined,
|
|
73
|
+
id: undefined,
|
|
74
|
+
copyDraft: undefined,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function openCreateDrawer(copyDraftToken: string | null = null) {
|
|
79
|
+
setDrawerState('new', null, copyDraftToken);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function openEditDrawer(id: string) {
|
|
83
|
+
const normalizedId = normalizeSingleQueryValue(id);
|
|
84
|
+
if (!normalizedId) return;
|
|
85
|
+
setDrawerState('edit', normalizedId);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function reopenCreatedDocument(id: string) {
|
|
89
|
+
openEditDrawer(id);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function closeDrawer() {
|
|
93
|
+
setDrawerState('closed');
|
|
94
|
+
await clearDrawerQueryIfPresent();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function resetDrawerState() {
|
|
98
|
+
setDrawerState('closed');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
watch(
|
|
102
|
+
() => [args.route.query.panel, args.route.query.id, args.route.query.copyDraft, args.documentType.value] as const,
|
|
103
|
+
([panel, id, copyDraft]) => {
|
|
104
|
+
const normalizedPanel = normalizeSingleQueryValue(panel).toLowerCase();
|
|
105
|
+
const normalizedId = normalizeSingleQueryValue(id);
|
|
106
|
+
const copyDraftToken = normalizeSingleQueryValue(copyDraft) || null;
|
|
107
|
+
|
|
108
|
+
if (normalizedPanel === 'new') {
|
|
109
|
+
openCreateDrawer(copyDraftToken);
|
|
110
|
+
void clearDrawerQueryIfPresent();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (normalizedPanel === 'edit' && normalizedId) {
|
|
115
|
+
openEditDrawer(normalizedId);
|
|
116
|
+
void clearDrawerQueryIfPresent();
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{ immediate: true },
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
watch(
|
|
123
|
+
() => args.documentType.value,
|
|
124
|
+
() => {
|
|
125
|
+
if (
|
|
126
|
+
args.route.query.panel != null
|
|
127
|
+
|| args.route.query.id != null
|
|
128
|
+
|| args.route.query.copyDraft != null
|
|
129
|
+
) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
resetDrawerState();
|
|
134
|
+
},
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
drawerMode,
|
|
139
|
+
isPanelOpen,
|
|
140
|
+
currentEditorId,
|
|
141
|
+
initialCopyDraft,
|
|
142
|
+
initialFields,
|
|
143
|
+
initialParts,
|
|
144
|
+
expandTo,
|
|
145
|
+
openCreateDrawer,
|
|
146
|
+
openEditDrawer,
|
|
147
|
+
reopenCreatedDocument,
|
|
148
|
+
closeDrawer,
|
|
149
|
+
resetDrawerState,
|
|
150
|
+
clearDrawerQueryIfPresent,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { ref } from 'vue';
|
|
2
|
+
|
|
3
|
+
import type { EntityEditorFlags } from './types';
|
|
4
|
+
|
|
5
|
+
function createDefaultFlags(): EntityEditorFlags {
|
|
6
|
+
return {
|
|
7
|
+
canSave: false,
|
|
8
|
+
isDirty: false,
|
|
9
|
+
loading: false,
|
|
10
|
+
saving: false,
|
|
11
|
+
canExpand: false,
|
|
12
|
+
canDelete: false,
|
|
13
|
+
canMarkForDeletion: false,
|
|
14
|
+
canUnmarkForDeletion: false,
|
|
15
|
+
canPost: false,
|
|
16
|
+
canUnpost: false,
|
|
17
|
+
canShowAudit: false,
|
|
18
|
+
canShareLink: false,
|
|
19
|
+
extras: undefined,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function useEditorDrawerState() {
|
|
24
|
+
const drawerTitle = ref('');
|
|
25
|
+
const drawerSubtitle = ref<string | undefined>(undefined);
|
|
26
|
+
const editorFlags = ref<EntityEditorFlags>(createDefaultFlags());
|
|
27
|
+
const discardOpen = ref(false);
|
|
28
|
+
let discardPromise: Promise<boolean> | null = null;
|
|
29
|
+
let discardResolve: ((value: boolean) => void) | null = null;
|
|
30
|
+
|
|
31
|
+
function resetDrawerHeading() {
|
|
32
|
+
drawerTitle.value = '';
|
|
33
|
+
drawerSubtitle.value = undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function handleEditorFlags(next: EntityEditorFlags) {
|
|
37
|
+
editorFlags.value = next;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function handleEditorState(next: { title: string; subtitle?: string }) {
|
|
41
|
+
drawerTitle.value = next.title;
|
|
42
|
+
drawerSubtitle.value = next.subtitle;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function requestDiscard(): Promise<boolean> {
|
|
46
|
+
if (discardOpen.value && discardPromise) return discardPromise;
|
|
47
|
+
|
|
48
|
+
discardOpen.value = true;
|
|
49
|
+
discardPromise = new Promise<boolean>((resolve) => {
|
|
50
|
+
discardResolve = resolve;
|
|
51
|
+
});
|
|
52
|
+
return discardPromise;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function discardConfirm() {
|
|
56
|
+
discardOpen.value = false;
|
|
57
|
+
discardResolve?.(true);
|
|
58
|
+
discardResolve = null;
|
|
59
|
+
discardPromise = null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function discardCancel() {
|
|
63
|
+
discardOpen.value = false;
|
|
64
|
+
discardResolve?.(false);
|
|
65
|
+
discardResolve = null;
|
|
66
|
+
discardPromise = null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function beforeCloseDrawer(): Promise<boolean> {
|
|
70
|
+
if (discardOpen.value) return false;
|
|
71
|
+
if (!editorFlags.value.isDirty) return true;
|
|
72
|
+
return await requestDiscard();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
drawerTitle,
|
|
77
|
+
drawerSubtitle,
|
|
78
|
+
editorFlags,
|
|
79
|
+
discardOpen,
|
|
80
|
+
handleEditorFlags,
|
|
81
|
+
handleEditorState,
|
|
82
|
+
resetDrawerHeading,
|
|
83
|
+
requestDiscard,
|
|
84
|
+
discardConfirm,
|
|
85
|
+
discardCancel,
|
|
86
|
+
beforeCloseDrawer,
|
|
87
|
+
};
|
|
88
|
+
}
|