@nocobase/client-v2 2.2.0-alpha.4 → 2.2.0-alpha.6
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/es/BaseApplication.d.ts +2 -0
- package/es/RouterManager.d.ts +15 -0
- package/es/entry-actions/EntryActionManager.d.ts +24 -0
- package/es/entry-actions/index.d.ts +9 -0
- package/es/flow/admin-shell/BaseLayoutModel.d.ts +6 -0
- package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +7 -0
- package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +2 -1
- package/es/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.d.ts +24 -0
- package/es/flow/admin-shell/admin-layout/index.d.ts +1 -0
- package/es/flow/admin-shell/admin-layout/useApplications.d.ts +7 -2
- package/es/flow/models/base/ActionModelCore.d.ts +2 -0
- package/es/flow/routeTransientInputArgs.d.ts +14 -0
- package/es/index.d.ts +4 -0
- package/es/index.mjs +178 -124
- package/es/utils/markdownSanitize.d.ts +11 -0
- package/lib/index.js +164 -110
- package/package.json +7 -7
- package/src/BaseApplication.tsx +2 -0
- package/src/RouterManager.tsx +142 -1
- package/src/__tests__/RouterManager.test.ts +125 -0
- package/src/entry-actions/EntryActionManager.ts +76 -0
- package/src/entry-actions/index.ts +10 -0
- package/src/flow/FlowPage.tsx +29 -2
- package/src/flow/__tests__/FlowPage.test.tsx +152 -25
- package/src/flow/__tests__/FlowRoute.test.tsx +547 -2
- package/src/flow/actions/__tests__/dataScopeFilter.test.ts +62 -0
- package/src/flow/actions/__tests__/openView.defineProps.route.test.tsx +80 -20
- package/src/flow/actions/dataScopeFilter.ts +10 -1
- package/src/flow/actions/openView.tsx +21 -1
- package/src/flow/admin-shell/BaseLayoutModel.tsx +111 -0
- package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +184 -42
- package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +1016 -119
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +41 -4
- package/src/flow/admin-shell/admin-layout/AppListRender.tsx +13 -2
- package/src/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.tsx +289 -0
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +359 -2
- package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +48 -0
- package/src/flow/admin-shell/admin-layout/index.ts +1 -0
- package/src/flow/admin-shell/admin-layout/useApplications.tsx +81 -12
- package/src/flow/common/Markdown/Display.tsx +14 -3
- package/src/flow/common/Markdown/Edit.tsx +19 -7
- package/src/flow/components/FlowRoute.tsx +128 -16
- package/src/flow/internal/components/Markdown/util.ts +2 -1
- package/src/flow/models/base/ActionModel.tsx +10 -8
- package/src/flow/models/base/ActionModelCore.tsx +5 -0
- package/src/flow/models/fields/TextareaFieldModel.tsx +42 -19
- package/src/flow/models/fields/__tests__/TextareaFieldModel.test.tsx +32 -1
- package/src/flow/models/topbar/TopbarActionModel.tsx +78 -3
- package/src/flow/routeTransientInputArgs.ts +27 -0
- package/src/index.ts +4 -0
- package/src/nocobase-buildin-plugin/index.tsx +7 -1
- package/src/utils/markdownSanitize.ts +88 -0
|
@@ -285,13 +285,44 @@ function SetIsMobileLayout(props: { isMobile: boolean; children: any; model?: Ad
|
|
|
285
285
|
const flowEngine = useFlowEngine();
|
|
286
286
|
const adminLayoutModel = props.model || flowEngine.getModel<AdminLayoutModel>(ADMIN_LAYOUT_MODEL_UID);
|
|
287
287
|
|
|
288
|
-
|
|
288
|
+
useLayoutEffect(() => {
|
|
289
289
|
adminLayoutModel?.setIsMobileLayout(props.isMobile);
|
|
290
290
|
}, [adminLayoutModel, props.isMobile]);
|
|
291
291
|
|
|
292
292
|
return props.children;
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
+
function AdminLayoutContentWithMobileState(props: {
|
|
296
|
+
isMobile: boolean;
|
|
297
|
+
model?: AdminLayoutModel;
|
|
298
|
+
designable?: boolean;
|
|
299
|
+
layout?: AdminLayoutModel['layout'];
|
|
300
|
+
onContentElementChange?: (element: HTMLDivElement | null) => void;
|
|
301
|
+
}) {
|
|
302
|
+
const modelRef = useRef(props.model);
|
|
303
|
+
const isMobileRef = useRef(props.isMobile);
|
|
304
|
+
const onContentElementChangeRef = useRef(props.onContentElementChange);
|
|
305
|
+
|
|
306
|
+
modelRef.current = props.model;
|
|
307
|
+
isMobileRef.current = props.isMobile;
|
|
308
|
+
onContentElementChangeRef.current = props.onContentElementChange;
|
|
309
|
+
|
|
310
|
+
const handleContentElementChange = useCallback((element: HTMLDivElement | null) => {
|
|
311
|
+
if (element) {
|
|
312
|
+
modelRef.current?.setIsMobileLayout(isMobileRef.current);
|
|
313
|
+
}
|
|
314
|
+
onContentElementChangeRef.current?.(element);
|
|
315
|
+
}, []);
|
|
316
|
+
|
|
317
|
+
return (
|
|
318
|
+
<AdminLayoutContent
|
|
319
|
+
designable={props.designable}
|
|
320
|
+
layout={props.layout}
|
|
321
|
+
onContentElementChange={handleContentElementChange}
|
|
322
|
+
/>
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
295
326
|
const DesignerButtonMenuItem: FC<{ item: AdminLayoutMenuNode; fallbackParentRoute?: NocoBaseDesktopRoute }> = (
|
|
296
327
|
props,
|
|
297
328
|
) => {
|
|
@@ -410,8 +441,8 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
410
441
|
);
|
|
411
442
|
const designable = !isMobileSider && preferredFlowSettingsEnabled;
|
|
412
443
|
const { styles } = useHeaderStyle();
|
|
413
|
-
const { appList } = useApplications();
|
|
414
|
-
const appListRender = useAppListRender();
|
|
444
|
+
const { appList, appSwitcherModel } = useApplications(adminLayoutModel);
|
|
445
|
+
const appListRender = useAppListRender(appSwitcherModel);
|
|
415
446
|
const flowSettingsSyncRef = useRef(0);
|
|
416
447
|
const desiredFlowSettingsEnabledRef = useRef(false);
|
|
417
448
|
const handleLayoutContentElementChange = useCallback(
|
|
@@ -634,6 +665,10 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
634
665
|
z-index: 2000 !important;
|
|
635
666
|
}
|
|
636
667
|
|
|
668
|
+
.ant-pro-layout-apps-popover .ant-popover-content {
|
|
669
|
+
margin-top: 12px;
|
|
670
|
+
}
|
|
671
|
+
|
|
637
672
|
.ant-pro-layout-apps-icon {
|
|
638
673
|
color: ${customToken.colorTextHeaderMenu || 'rgba(255, 255, 255, 0.85)'};
|
|
639
674
|
}
|
|
@@ -717,7 +752,9 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
717
752
|
<SetIsMobileLayout isMobile={isMobile} model={adminLayoutModel}>
|
|
718
753
|
<ConfigProvider theme={isMobile ? mobileTheme : theme}>
|
|
719
754
|
<GlobalStyle />
|
|
720
|
-
<
|
|
755
|
+
<AdminLayoutContentWithMobileState
|
|
756
|
+
isMobile={isMobile}
|
|
757
|
+
model={adminLayoutModel}
|
|
721
758
|
designable={designable}
|
|
722
759
|
layout={adminLayoutModel?.layout}
|
|
723
760
|
onContentElementChange={handleLayoutContentElementChange}
|
|
@@ -10,17 +10,28 @@
|
|
|
10
10
|
import type { AppListProps } from '@ant-design/pro-layout/es/components/AppsLogoComponents/types';
|
|
11
11
|
import { css } from '@emotion/css';
|
|
12
12
|
import { Card, Typography, theme } from 'antd';
|
|
13
|
+
import { observer } from '@nocobase/flow-engine';
|
|
13
14
|
import React from 'react';
|
|
15
|
+
import type { AppSwitcherActionPanelModel } from './AppSwitcherActionPanelModel';
|
|
14
16
|
|
|
15
17
|
function renderIcon(icon: React.ReactNode | (() => React.ReactNode)) {
|
|
16
18
|
return typeof icon === 'function' ? icon() : icon;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
const AppSwitcherActionsContent = observer(
|
|
22
|
+
(props: { model: AppSwitcherActionPanelModel }) => <>{props.model.renderContent()}</>,
|
|
23
|
+
{ displayName: 'AppSwitcherActionsContent' },
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export function useAppListRender(appSwitcherModel?: AppSwitcherActionPanelModel) {
|
|
20
27
|
const { token } = theme.useToken();
|
|
21
28
|
|
|
22
29
|
return React.useCallback(
|
|
23
30
|
(appList: AppListProps) => {
|
|
31
|
+
if (appSwitcherModel?.hasActions() || appSwitcherModel?.context.flowSettingsEnabled) {
|
|
32
|
+
return <AppSwitcherActionsContent model={appSwitcherModel} />;
|
|
33
|
+
}
|
|
34
|
+
|
|
24
35
|
const columnCount = Math.min(Math.max(appList.length, 1), 2);
|
|
25
36
|
|
|
26
37
|
return (
|
|
@@ -134,6 +145,6 @@ export function useAppListRender() {
|
|
|
134
145
|
</div>
|
|
135
146
|
);
|
|
136
147
|
},
|
|
137
|
-
[token],
|
|
148
|
+
[appSwitcherModel, token],
|
|
138
149
|
);
|
|
139
150
|
}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { SettingOutlined } from '@ant-design/icons';
|
|
11
|
+
import { css } from '@emotion/css';
|
|
12
|
+
import { Icon } from '../../../components';
|
|
13
|
+
import { ActionModel } from '../../models/base';
|
|
14
|
+
import {
|
|
15
|
+
AddSubModelButton,
|
|
16
|
+
DndProvider,
|
|
17
|
+
DragHandler,
|
|
18
|
+
Droppable,
|
|
19
|
+
FlowModel,
|
|
20
|
+
FlowModelRenderer,
|
|
21
|
+
FlowSettingsButton,
|
|
22
|
+
mergeSubModelItems,
|
|
23
|
+
observer,
|
|
24
|
+
type SubModelItem,
|
|
25
|
+
type SubModelItemsType,
|
|
26
|
+
} from '@nocobase/flow-engine';
|
|
27
|
+
import { Avatar, Button, Card, Tooltip, Typography } from 'antd';
|
|
28
|
+
import React from 'react';
|
|
29
|
+
|
|
30
|
+
const defaultIconColors = ['#3d8bff', '#00a8b5', '#35b26b', '#f5a623', '#ff7a45', '#e85d75', '#9254de', '#597ef7'];
|
|
31
|
+
|
|
32
|
+
function getDefaultIconColor(title?: React.ReactNode) {
|
|
33
|
+
const source = typeof title === 'string' ? title : '';
|
|
34
|
+
const hash = Array.from(source).reduce((sum, char) => sum + char.charCodeAt(0), 0);
|
|
35
|
+
return defaultIconColors[hash % defaultIconColors.length];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type AppSwitcherActionPanelStructure = {
|
|
39
|
+
subModels: {
|
|
40
|
+
actions?: ActionModel[];
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function isRenderableActionModel(action: unknown): action is ActionModel {
|
|
45
|
+
if (!action || typeof action !== 'object') {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const candidate = action as Partial<ActionModel>;
|
|
50
|
+
return typeof candidate.getTitle === 'function' && typeof candidate.onClick === 'function';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type EntryActionAvailability = {
|
|
54
|
+
isEntryActionAvailable?: () => boolean;
|
|
55
|
+
getEntryActionUnavailableMessage?: () => string | undefined;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
function isEntryActionUnavailable(action: ActionModel) {
|
|
59
|
+
const candidate = action as ActionModel & EntryActionAvailability;
|
|
60
|
+
return typeof candidate.isEntryActionAvailable === 'function' && !candidate.isEntryActionAvailable();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getEntryActionUnavailableMessage(action: ActionModel) {
|
|
64
|
+
const candidate = action as ActionModel & EntryActionAvailability;
|
|
65
|
+
return candidate.getEntryActionUnavailableMessage?.();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export class AppSwitcherActionPanelModel extends FlowModel<AppSwitcherActionPanelStructure> {
|
|
69
|
+
getConfigureActionsItems(): SubModelItemsType {
|
|
70
|
+
const linkAction: SubModelItem = {
|
|
71
|
+
key: 'app-switcher:link',
|
|
72
|
+
label: this.context.t('Link'),
|
|
73
|
+
createModelOptions: {
|
|
74
|
+
use: 'LinkActionModel',
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
const jsAction: SubModelItem = {
|
|
78
|
+
key: 'app-switcher:js-action',
|
|
79
|
+
label: this.context.t('JS action'),
|
|
80
|
+
createModelOptions: {
|
|
81
|
+
use: 'JSActionModel',
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return mergeSubModelItems([[linkAction], this.context.app.entryActionManager.getItems('app-switcher'), [jsAction]]);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
renderConfigureActions() {
|
|
89
|
+
return <AppSwitcherConfigureActionsButton model={this} />;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private getRenderableActions(options: { includeHidden?: boolean; includeUnavailable?: boolean } = {}) {
|
|
93
|
+
return this.mapSubModels('actions', (action) => action)
|
|
94
|
+
.filter(isRenderableActionModel)
|
|
95
|
+
.filter((action) => options.includeUnavailable || !isEntryActionUnavailable(action))
|
|
96
|
+
.filter((action) => options.includeHidden || !action.hidden);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
hasActions() {
|
|
100
|
+
return this.getRenderableActions().length > 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
renderContent() {
|
|
104
|
+
const token = this.context.themeToken;
|
|
105
|
+
const designable = !!this.context.flowSettingsEnabled;
|
|
106
|
+
const actions = this.getRenderableActions({ includeHidden: designable, includeUnavailable: designable });
|
|
107
|
+
const columnCount = Math.min(Math.max(actions.length || 1, 1), 2);
|
|
108
|
+
const buttonResetClass = css`
|
|
109
|
+
&.ant-btn {
|
|
110
|
+
display: block;
|
|
111
|
+
width: 100%;
|
|
112
|
+
height: 100%;
|
|
113
|
+
padding: 0;
|
|
114
|
+
border: none;
|
|
115
|
+
box-shadow: none;
|
|
116
|
+
background: none;
|
|
117
|
+
color: ${token.colorText};
|
|
118
|
+
text-align: left;
|
|
119
|
+
}
|
|
120
|
+
&.ant-btn > span {
|
|
121
|
+
display: block;
|
|
122
|
+
width: 100%;
|
|
123
|
+
height: 100%;
|
|
124
|
+
}
|
|
125
|
+
.ant-btn-icon {
|
|
126
|
+
display: none;
|
|
127
|
+
}
|
|
128
|
+
`;
|
|
129
|
+
const contentClass = css`
|
|
130
|
+
max-width: calc(100vw - 48px);
|
|
131
|
+
max-height: calc(100vh - 48px);
|
|
132
|
+
overflow: auto;
|
|
133
|
+
padding: ${token.paddingXS}px;
|
|
134
|
+
`;
|
|
135
|
+
const listClass = css`
|
|
136
|
+
display: grid;
|
|
137
|
+
grid-template-columns: repeat(${columnCount}, 260px);
|
|
138
|
+
gap: ${token.marginXXS}px;
|
|
139
|
+
margin: 0;
|
|
140
|
+
padding: 0;
|
|
141
|
+
list-style: none;
|
|
142
|
+
`;
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<div className={contentClass}>
|
|
146
|
+
{actions.length ? (
|
|
147
|
+
<DndProvider>
|
|
148
|
+
<ul className={listClass}>
|
|
149
|
+
{actions.map((action) => {
|
|
150
|
+
const { icon = 'AppstoreOutlined', color } = action.props;
|
|
151
|
+
const title = action.getTitle();
|
|
152
|
+
const entryActionUnavailable = isEntryActionUnavailable(action);
|
|
153
|
+
const renderActionContent = (compact = false) => (
|
|
154
|
+
<Card
|
|
155
|
+
bordered={false}
|
|
156
|
+
className={css`
|
|
157
|
+
height: 56px;
|
|
158
|
+
border-radius: ${token.borderRadius}px;
|
|
159
|
+
box-shadow: none !important;
|
|
160
|
+
opacity: ${compact ? token.opacityLoading : 1};
|
|
161
|
+
|
|
162
|
+
&:hover {
|
|
163
|
+
background: ${token.colorBgTextHover};
|
|
164
|
+
box-shadow: none !important;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.ant-card-body {
|
|
168
|
+
height: 100%;
|
|
169
|
+
padding: ${token.paddingXS}px;
|
|
170
|
+
display: flex;
|
|
171
|
+
align-items: center;
|
|
172
|
+
gap: ${token.marginXS}px;
|
|
173
|
+
}
|
|
174
|
+
`}
|
|
175
|
+
styles={{ body: { background: 'transparent' } }}
|
|
176
|
+
>
|
|
177
|
+
<Avatar
|
|
178
|
+
size={32}
|
|
179
|
+
shape="square"
|
|
180
|
+
icon={<Icon type={icon as string} />}
|
|
181
|
+
style={{
|
|
182
|
+
flex: '0 0 auto',
|
|
183
|
+
borderRadius: 6,
|
|
184
|
+
background: color || getDefaultIconColor(title),
|
|
185
|
+
}}
|
|
186
|
+
/>
|
|
187
|
+
<Typography.Text
|
|
188
|
+
ellipsis
|
|
189
|
+
title={typeof title === 'string' ? title : undefined}
|
|
190
|
+
style={{
|
|
191
|
+
minWidth: 0,
|
|
192
|
+
color: token.colorText,
|
|
193
|
+
fontSize: token.fontSize,
|
|
194
|
+
lineHeight: token.lineHeight,
|
|
195
|
+
}}
|
|
196
|
+
>
|
|
197
|
+
{title}
|
|
198
|
+
</Typography.Text>
|
|
199
|
+
</Card>
|
|
200
|
+
);
|
|
201
|
+
action.enableEditDanger = false;
|
|
202
|
+
action.enableEditType = false;
|
|
203
|
+
action.enableEditIconOnly = false;
|
|
204
|
+
action.enableEditColor = true;
|
|
205
|
+
action.renderButton = () => {
|
|
206
|
+
if (entryActionUnavailable) {
|
|
207
|
+
return (
|
|
208
|
+
<Tooltip title={getEntryActionUnavailableMessage(action)}>
|
|
209
|
+
<Button className={buttonResetClass} onClick={action.onClick.bind(action)}>
|
|
210
|
+
{renderActionContent(true)}
|
|
211
|
+
</Button>
|
|
212
|
+
</Tooltip>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
return (
|
|
216
|
+
<Button className={buttonResetClass} onClick={action.onClick.bind(action)}>
|
|
217
|
+
{renderActionContent()}
|
|
218
|
+
</Button>
|
|
219
|
+
);
|
|
220
|
+
};
|
|
221
|
+
action.renderHiddenInConfig = () => (
|
|
222
|
+
<Tooltip
|
|
223
|
+
title={
|
|
224
|
+
entryActionUnavailable
|
|
225
|
+
? getEntryActionUnavailableMessage(action)
|
|
226
|
+
: this.context.t('The button is hidden and only visible when the UI Editor is active')
|
|
227
|
+
}
|
|
228
|
+
>
|
|
229
|
+
<Button className={buttonResetClass} onClick={action.onClick.bind(action)}>
|
|
230
|
+
{renderActionContent(true)}
|
|
231
|
+
</Button>
|
|
232
|
+
</Tooltip>
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<li key={action.uid} style={{ width: 260, height: 56, listStyle: 'none' }}>
|
|
237
|
+
<Droppable model={action}>
|
|
238
|
+
<FlowModelRenderer
|
|
239
|
+
model={action}
|
|
240
|
+
showFlowSettings={{ showBackground: false, showBorder: false, toolbarPosition: 'above' }}
|
|
241
|
+
extraToolbarItems={
|
|
242
|
+
designable
|
|
243
|
+
? [
|
|
244
|
+
{
|
|
245
|
+
key: 'drag-handler',
|
|
246
|
+
component: DragHandler,
|
|
247
|
+
sort: 1,
|
|
248
|
+
},
|
|
249
|
+
]
|
|
250
|
+
: []
|
|
251
|
+
}
|
|
252
|
+
/>
|
|
253
|
+
</Droppable>
|
|
254
|
+
</li>
|
|
255
|
+
);
|
|
256
|
+
})}
|
|
257
|
+
</ul>
|
|
258
|
+
</DndProvider>
|
|
259
|
+
) : (
|
|
260
|
+
<div style={{ width: 260, minHeight: token.marginXS }} />
|
|
261
|
+
)}
|
|
262
|
+
{designable && <div style={{ marginTop: token.marginXS }}>{this.renderConfigureActions()}</div>}
|
|
263
|
+
</div>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
AppSwitcherActionPanelModel.define({
|
|
269
|
+
label: 'App switcher actions',
|
|
270
|
+
children: false,
|
|
271
|
+
createModelOptions: {
|
|
272
|
+
use: 'AppSwitcherActionPanelModel',
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
const AppSwitcherConfigureActionsButton = observer(({ model }: { model: AppSwitcherActionPanelModel }) => {
|
|
277
|
+
const entryActionsRevision = model.context.app.entryActionManager.revision;
|
|
278
|
+
return (
|
|
279
|
+
<AddSubModelButton
|
|
280
|
+
key={`app-switcher-add-actions-${entryActionsRevision}`}
|
|
281
|
+
model={model}
|
|
282
|
+
items={model.getConfigureActionsItems()}
|
|
283
|
+
subModelKey="actions"
|
|
284
|
+
keepDropdownOpen
|
|
285
|
+
>
|
|
286
|
+
<FlowSettingsButton icon={<SettingOutlined />}>{model.context.t('Actions')}</FlowSettingsButton>
|
|
287
|
+
</AddSubModelButton>
|
|
288
|
+
);
|
|
289
|
+
});
|