@lowdefy/client 6.1.0 → 7.0.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/dist/Client.js +54 -17
- package/dist/Context.js +30 -12
- package/dist/HtmlOverlay.js +127 -0
- package/dist/IconProvider.js +24 -0
- package/dist/MountEvents.js +3 -3
- package/dist/adapters/createRouter.js +9 -3
- package/dist/adapters/url.js +1 -7
- package/dist/auth/createAuthMethods.js +1034 -41
- package/dist/block/Block.js +6 -3
- package/dist/block/CategorySwitch.js +6 -0
- package/dist/block/Container.js +3 -0
- package/dist/block/InputContainer.js +8 -2
- package/dist/block/List.js +3 -0
- package/dist/block/LoadingBlock.js +6 -12
- package/dist/block/LoadingContainer.js +6 -1
- package/dist/block/LoadingList.js +6 -1
- package/dist/block/loadingBlockMethods.js +26 -0
- package/dist/claimCallerReload.js +38 -0
- package/dist/createAntdIconConfig.js +91 -0
- package/dist/createCallAPI.js +6 -2
- package/dist/createCallRequest.js +6 -2
- package/dist/createHandleError.js +6 -4
- package/dist/createIcon.js +138 -36
- package/dist/createPageLifecycleManager.js +172 -0
- package/dist/formatIconTitle.js +26 -0
- package/dist/getActiveLocale.js +27 -0
- package/dist/getContextKey.js +29 -0
- package/dist/getMessageIcon.js +43 -0
- package/dist/hasOtherRoles.js +30 -0
- package/dist/index.js +1 -0
- package/dist/initLowdefyContext.js +103 -4
- package/dist/isOtherCaller.js +29 -0
- package/dist/recordCallerStamp.js +31 -0
- package/dist/renderIconNodes.js +46 -0
- package/dist/request.js +72 -5
- package/dist/setLowdefyUser.js +32 -0
- package/dist/setupLink.js +19 -13
- package/dist/shouldReloadForAuthError.js +39 -0
- package/dist/shouldReloadForBuild.js +44 -0
- package/dist/shouldReloadForCaller.js +50 -0
- package/dist/storage/clearUserStorage.js +36 -0
- package/dist/storage/clearUserStorageOnOwnerChange.js +41 -0
- package/dist/storage/createStorage.js +48 -0
- package/dist/storage/listUserStorageKeys.js +36 -0
- package/dist/storage/readStorageItem.js +31 -0
- package/dist/storage/userStorageKey.js +24 -0
- package/dist/storage/userStoragePrefix.js +25 -0
- package/dist/useDarkMode.js +17 -4
- package/dist/useLocale.js +18 -5
- package/dist/websocket/createWebSocketClient.js +234 -98
- package/package.json +11 -10
package/dist/block/Block.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import { ErrorBoundary } from '@lowdefy/block-utils';
|
|
17
17
|
import CategorySwitch from './CategorySwitch.js';
|
|
18
18
|
import MountEvents from '../MountEvents.js';
|
|
19
|
-
const Block = ({ block, Blocks, context, lowdefy, parentLoading })=>{
|
|
19
|
+
const Block = ({ block, Blocks, context, lowdefy, parentLoading, renderHead })=>{
|
|
20
20
|
const [updates, setUpdate] = useState(0);
|
|
21
21
|
const loggedErrorsRef = useRef(new Set());
|
|
22
22
|
context._internal.updaters[block.id] = ()=>setUpdate(updates + 1);
|
|
@@ -48,7 +48,9 @@ const Block = ({ block, Blocks, context, lowdefy, parentLoading })=>{
|
|
|
48
48
|
configKey: block.eval?.configKey,
|
|
49
49
|
onError: handleError,
|
|
50
50
|
properties: block.eval?.properties
|
|
51
|
-
},
|
|
51
|
+
}, renderHead?.({
|
|
52
|
+
properties: block.eval.properties
|
|
53
|
+
}), /*#__PURE__*/ React.createElement(MountEvents, {
|
|
52
54
|
context: context,
|
|
53
55
|
triggerEvent: async ()=>{
|
|
54
56
|
context._internal.lowdefy._internal.progress.dispatch({
|
|
@@ -76,7 +78,8 @@ const Block = ({ block, Blocks, context, lowdefy, parentLoading })=>{
|
|
|
76
78
|
lowdefy._internal.progress.dispatch({
|
|
77
79
|
type: 'done'
|
|
78
80
|
});
|
|
79
|
-
}
|
|
81
|
+
},
|
|
82
|
+
waitForMount: Object.hasOwn(block.Events.events, 'onMount')
|
|
80
83
|
}, (eventLoading)=>/*#__PURE__*/ React.createElement(CategorySwitch, {
|
|
81
84
|
block: block,
|
|
82
85
|
Blocks: Blocks,
|
|
@@ -69,7 +69,9 @@ const CategorySwitch = ({ block, Blocks, context, loading, lowdefy })=>{
|
|
|
69
69
|
layout: block.eval.layout
|
|
70
70
|
}, /*#__PURE__*/ React.createElement(Component, {
|
|
71
71
|
methods: Object.assign(block.methods, {
|
|
72
|
+
getAppStorageKey: lowdefy._internal.storage.appKey,
|
|
72
73
|
getLocale: ()=>lowdefy.i18n?.active ?? lowdefy.i18n?.defaultLocale,
|
|
74
|
+
getUserStorageKey: lowdefy._internal.storage.userKey,
|
|
73
75
|
registerEvent: block.registerEvent,
|
|
74
76
|
registerMethod: block.registerMethod,
|
|
75
77
|
setValue: block.setValue,
|
|
@@ -85,6 +87,7 @@ const CategorySwitch = ({ block, Blocks, context, loading, lowdefy })=>{
|
|
|
85
87
|
loading: loading,
|
|
86
88
|
menus: lowdefy.menus,
|
|
87
89
|
pageId: lowdefy.pageId,
|
|
90
|
+
pathParams: context.pathParams,
|
|
88
91
|
properties: block.eval.properties,
|
|
89
92
|
required: block.eval.required,
|
|
90
93
|
styles: block.eval.style ?? {},
|
|
@@ -108,7 +111,9 @@ const CategorySwitch = ({ block, Blocks, context, loading, lowdefy })=>{
|
|
|
108
111
|
layout: block.eval.layout
|
|
109
112
|
}, /*#__PURE__*/ React.createElement(Component, {
|
|
110
113
|
methods: Object.assign(block.methods, {
|
|
114
|
+
getAppStorageKey: lowdefy._internal.storage.appKey,
|
|
111
115
|
getLocale: ()=>lowdefy.i18n?.active ?? lowdefy.i18n?.defaultLocale,
|
|
116
|
+
getUserStorageKey: lowdefy._internal.storage.userKey,
|
|
112
117
|
registerEvent: block.registerEvent,
|
|
113
118
|
registerMethod: block.registerMethod,
|
|
114
119
|
translate: lowdefy._internal.translate,
|
|
@@ -123,6 +128,7 @@ const CategorySwitch = ({ block, Blocks, context, loading, lowdefy })=>{
|
|
|
123
128
|
loading: loading,
|
|
124
129
|
menus: lowdefy.menus,
|
|
125
130
|
pageId: lowdefy.pageId,
|
|
131
|
+
pathParams: context.pathParams,
|
|
126
132
|
properties: block.eval.properties,
|
|
127
133
|
required: block.eval.required,
|
|
128
134
|
styles: block.eval.style ?? {},
|
package/dist/block/Container.js
CHANGED
|
@@ -51,7 +51,9 @@ const Container = ({ block, Blocks, Component, context, loading, lowdefy })=>{
|
|
|
51
51
|
layout: block.eval.layout
|
|
52
52
|
}, /*#__PURE__*/ React.createElement(Component, {
|
|
53
53
|
methods: Object.assign(block.methods, {
|
|
54
|
+
getAppStorageKey: lowdefy._internal.storage.appKey,
|
|
54
55
|
getLocale: ()=>lowdefy.i18n?.active ?? lowdefy.i18n?.defaultLocale,
|
|
56
|
+
getUserStorageKey: lowdefy._internal.storage.userKey,
|
|
55
57
|
registerEvent: block.registerEvent,
|
|
56
58
|
registerMethod: block.registerMethod,
|
|
57
59
|
translate: lowdefy._internal.translate,
|
|
@@ -67,6 +69,7 @@ const Container = ({ block, Blocks, Component, context, loading, lowdefy })=>{
|
|
|
67
69
|
loading: loading,
|
|
68
70
|
menus: lowdefy.menus,
|
|
69
71
|
pageId: lowdefy.pageId,
|
|
72
|
+
pathParams: context.pathParams,
|
|
70
73
|
properties: block.eval.properties,
|
|
71
74
|
required: block.eval.required,
|
|
72
75
|
styles: block.eval.style ?? {},
|
|
@@ -24,7 +24,10 @@ const InputContainer = ({ block, Blocks, Component, context, loading, lowdefy })
|
|
|
24
24
|
const slots = Blocks.subSlots[block.id][0].slots;
|
|
25
25
|
Object.keys(slots).forEach((slotKey, i)=>{
|
|
26
26
|
if (slots[slotKey].blocks.length === 0) return;
|
|
27
|
-
|
|
27
|
+
// A block may pass the loading its slot's blocks inherit (`{ loading }`), for slots that are
|
|
28
|
+
// its chrome rather than its content: the Table keeps its toolbar and bulk action blocks
|
|
29
|
+
// usable while its own rows load. By default they inherit the block's loading.
|
|
30
|
+
content[slotKey] = (contentStyle, { loading: slotLoading = loading } = {})=>/*#__PURE__*/ React.createElement(Area, {
|
|
28
31
|
area: block.eval.slots[slotKey],
|
|
29
32
|
areaKey: slotKey,
|
|
30
33
|
style: {
|
|
@@ -41,7 +44,7 @@ const InputContainer = ({ block, Blocks, Component, context, loading, lowdefy })
|
|
|
41
44
|
context: context,
|
|
42
45
|
key: `co-${bl.blockId}-${k}`,
|
|
43
46
|
lowdefy: lowdefy,
|
|
44
|
-
parentLoading:
|
|
47
|
+
parentLoading: slotLoading
|
|
45
48
|
})));
|
|
46
49
|
});
|
|
47
50
|
return /*#__PURE__*/ React.createElement(BlockLayout, {
|
|
@@ -51,7 +54,9 @@ const InputContainer = ({ block, Blocks, Component, context, loading, lowdefy })
|
|
|
51
54
|
layout: block.eval.layout
|
|
52
55
|
}, /*#__PURE__*/ React.createElement(Component, {
|
|
53
56
|
methods: Object.assign(block.methods, {
|
|
57
|
+
getAppStorageKey: lowdefy._internal.storage.appKey,
|
|
54
58
|
getLocale: ()=>lowdefy.i18n?.active ?? lowdefy.i18n?.defaultLocale,
|
|
59
|
+
getUserStorageKey: lowdefy._internal.storage.userKey,
|
|
55
60
|
registerEvent: block.registerEvent,
|
|
56
61
|
registerMethod: block.registerMethod,
|
|
57
62
|
translate: lowdefy._internal.translate,
|
|
@@ -73,6 +78,7 @@ const InputContainer = ({ block, Blocks, Component, context, loading, lowdefy })
|
|
|
73
78
|
loading: loading,
|
|
74
79
|
menus: lowdefy.menus,
|
|
75
80
|
pageId: lowdefy.pageId,
|
|
81
|
+
pathParams: context.pathParams,
|
|
76
82
|
properties: block.eval.properties,
|
|
77
83
|
required: block.eval.required,
|
|
78
84
|
styles: block.eval.style ?? {},
|
package/dist/block/List.js
CHANGED
|
@@ -56,7 +56,9 @@ const List = ({ block, Blocks, Component, context, loading, lowdefy })=>{
|
|
|
56
56
|
layout: block.eval.layout
|
|
57
57
|
}, /*#__PURE__*/ React.createElement(Component, {
|
|
58
58
|
methods: Object.assign(block.methods, {
|
|
59
|
+
getAppStorageKey: lowdefy._internal.storage.appKey,
|
|
59
60
|
getLocale: ()=>lowdefy.i18n?.active ?? lowdefy.i18n?.defaultLocale,
|
|
61
|
+
getUserStorageKey: lowdefy._internal.storage.userKey,
|
|
60
62
|
moveItemDown: block.moveItemDown,
|
|
61
63
|
moveItemUp: block.moveItemUp,
|
|
62
64
|
pushItem: block.pushItem,
|
|
@@ -78,6 +80,7 @@ const List = ({ block, Blocks, Component, context, loading, lowdefy })=>{
|
|
|
78
80
|
loading: loading,
|
|
79
81
|
menus: lowdefy.menus,
|
|
80
82
|
pageId: lowdefy.pageId,
|
|
83
|
+
pathParams: context.pathParams,
|
|
81
84
|
properties: block.eval.properties,
|
|
82
85
|
required: block.eval.required,
|
|
83
86
|
styles: block.eval.style ?? {},
|
|
@@ -16,17 +16,7 @@
|
|
|
16
16
|
import { BlockLayout } from '@lowdefy/layout';
|
|
17
17
|
import LoadingContainer from './LoadingContainer.js';
|
|
18
18
|
import LoadingList from './LoadingList.js';
|
|
19
|
-
|
|
20
|
-
moveItemDown: ()=>{},
|
|
21
|
-
moveItemUp: ()=>{},
|
|
22
|
-
pushItem: ()=>{},
|
|
23
|
-
registerEvent: ()=>{},
|
|
24
|
-
registerMethod: ()=>{},
|
|
25
|
-
removeItem: ()=>{},
|
|
26
|
-
setValue: ()=>{},
|
|
27
|
-
triggerEvent: ()=>{},
|
|
28
|
-
unshiftItem: ()=>{}
|
|
29
|
-
};
|
|
19
|
+
import loadingBlockMethods from './loadingBlockMethods.js';
|
|
30
20
|
const LoadingBlock = ({ blockClass, blockId, blockLayout, blockProperties, blockStyle, context, lowdefy, skeleton })=>{
|
|
31
21
|
let Component = lowdefy._internal.blockComponents[skeleton.type];
|
|
32
22
|
useEffect(()=>{
|
|
@@ -74,7 +64,11 @@ const LoadingBlock = ({ blockClass, blockId, blockLayout, blockProperties, block
|
|
|
74
64
|
components: lowdefy._internal.components,
|
|
75
65
|
key: `s-${blockId}-${skeleton.id}`,
|
|
76
66
|
menus: lowdefy.menus,
|
|
77
|
-
methods:
|
|
67
|
+
methods: {
|
|
68
|
+
...loadingBlockMethods,
|
|
69
|
+
getAppStorageKey: lowdefy._internal.storage.appKey,
|
|
70
|
+
getUserStorageKey: lowdefy._internal.storage.userKey
|
|
71
|
+
},
|
|
78
72
|
pageId: lowdefy.pageId,
|
|
79
73
|
properties: skeleton.properties ?? blockProperties
|
|
80
74
|
}));
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/ import React from 'react';
|
|
16
16
|
import { Area, BlockLayout } from '@lowdefy/layout';
|
|
17
17
|
import LoadingBlock from './LoadingBlock.js';
|
|
18
|
+
import loadingBlockMethods from './loadingBlockMethods.js';
|
|
18
19
|
const LoadingContainer = ({ blockClass, blockId, blockLayout, blockProperties, blockStyle, Component, context, lowdefy, skeleton })=>{
|
|
19
20
|
const content = {};
|
|
20
21
|
// eslint-disable-next-line prefer-destructuring
|
|
@@ -46,7 +47,11 @@ const LoadingContainer = ({ blockClass, blockId, blockLayout, blockProperties, b
|
|
|
46
47
|
content: content,
|
|
47
48
|
key: skeleton.id,
|
|
48
49
|
menus: lowdefy.menus,
|
|
49
|
-
methods: {
|
|
50
|
+
methods: {
|
|
51
|
+
...loadingBlockMethods,
|
|
52
|
+
getAppStorageKey: lowdefy._internal.storage.appKey,
|
|
53
|
+
getUserStorageKey: lowdefy._internal.storage.userKey
|
|
54
|
+
},
|
|
50
55
|
pageId: lowdefy.pageId,
|
|
51
56
|
properties: skeleton.properties ?? blockProperties
|
|
52
57
|
}));
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/ import React from 'react';
|
|
16
16
|
import { Area, BlockLayout } from '@lowdefy/layout';
|
|
17
17
|
import LoadingBlock from './LoadingBlock.js';
|
|
18
|
+
import loadingBlockMethods from './loadingBlockMethods.js';
|
|
18
19
|
const LoadingList = ({ blockClass, blockId, blockLayout, blockProperties, blockStyle, Component, context, lowdefy, skeleton })=>{
|
|
19
20
|
const content = {};
|
|
20
21
|
const contentList = [];
|
|
@@ -50,7 +51,11 @@ const LoadingList = ({ blockClass, blockId, blockLayout, blockProperties, blockS
|
|
|
50
51
|
components: lowdefy._internal.components,
|
|
51
52
|
list: contentList,
|
|
52
53
|
menus: lowdefy.menus,
|
|
53
|
-
methods: {
|
|
54
|
+
methods: {
|
|
55
|
+
...loadingBlockMethods,
|
|
56
|
+
getAppStorageKey: lowdefy._internal.storage.appKey,
|
|
57
|
+
getUserStorageKey: lowdefy._internal.storage.userKey
|
|
58
|
+
},
|
|
54
59
|
pageId: lowdefy.pageId,
|
|
55
60
|
properties: skeleton.properties ?? blockProperties
|
|
56
61
|
}));
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ const loadingBlockMethods = {
|
|
16
|
+
moveItemDown: ()=>{},
|
|
17
|
+
moveItemUp: ()=>{},
|
|
18
|
+
pushItem: ()=>{},
|
|
19
|
+
registerEvent: ()=>{},
|
|
20
|
+
registerMethod: ()=>{},
|
|
21
|
+
removeItem: ()=>{},
|
|
22
|
+
setValue: ()=>{},
|
|
23
|
+
triggerEvent: ()=>{},
|
|
24
|
+
unshiftItem: ()=>{}
|
|
25
|
+
};
|
|
26
|
+
export default loadingBlockMethods;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ // sessionStorage key holding when this tab last reloaded to follow its caller.
|
|
16
|
+
const RELOADED_FOR_CALLER_KEY = 'lowdefy.reloadedForCaller';
|
|
17
|
+
const RELOAD_INTERVAL_MS = 10000;
|
|
18
|
+
// True when this tab may reload because its caller changed (another person or
|
|
19
|
+
// organization, or the session ended), and records the reload. A tab reloads
|
|
20
|
+
// at most once per 10 seconds for its caller: if the freshly loaded page still
|
|
21
|
+
// disagrees with the server, reloading again would loop. Callers fail the call
|
|
22
|
+
// instead, so no data shows for the wrong caller either way.
|
|
23
|
+
function claimCallerReload({ window }) {
|
|
24
|
+
const now = Date.now();
|
|
25
|
+
try {
|
|
26
|
+
const last = Number(window.sessionStorage.getItem(RELOADED_FOR_CALLER_KEY));
|
|
27
|
+
if (Math.abs(now - last) < RELOAD_INTERVAL_MS) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
window.sessionStorage.setItem(RELOADED_FOR_CALLER_KEY, String(now));
|
|
31
|
+
} catch (error) {
|
|
32
|
+
// Browsers with storage disabled throw on sessionStorage access. Without
|
|
33
|
+
// the record the loop guard cannot work, so do not reload.
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
export default claimCallerReload;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import React from 'react';
|
|
16
|
+
import { ConfigProvider } from 'antd';
|
|
17
|
+
// Icon keys for a ConfigProvider around the page tree, so antd's own chrome
|
|
18
|
+
// draws with the app's icon set. Tag is left out: a context closeIcon makes
|
|
19
|
+
// every Tag closable (antd's useClosable), so the Tag block passes its own.
|
|
20
|
+
function createAntdIconConfig({ Icon }) {
|
|
21
|
+
function chromeIcon(name, properties) {
|
|
22
|
+
return /*#__PURE__*/ React.createElement(Icon, {
|
|
23
|
+
properties: {
|
|
24
|
+
name,
|
|
25
|
+
title: '',
|
|
26
|
+
...properties
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
// antd renders a function menu.expandIcon as a component, so it is created
|
|
31
|
+
// once here to keep its identity, and it adds the expand-icon class itself.
|
|
32
|
+
function MenuExpandIcon({ isOpen }) {
|
|
33
|
+
const { getPrefixCls } = React.useContext(ConfigProvider.ConfigContext);
|
|
34
|
+
return /*#__PURE__*/ React.createElement(Icon, {
|
|
35
|
+
className: `${getPrefixCls('menu')}-submenu-expand-icon`,
|
|
36
|
+
properties: {
|
|
37
|
+
name: 'chevron-down',
|
|
38
|
+
rotate: isOpen ? 180 : 0,
|
|
39
|
+
title: ''
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
const closeIcon = chromeIcon('close');
|
|
44
|
+
return {
|
|
45
|
+
alert: {
|
|
46
|
+
closeIcon,
|
|
47
|
+
errorIcon: chromeIcon('error'),
|
|
48
|
+
infoIcon: chromeIcon('info'),
|
|
49
|
+
successIcon: chromeIcon('success'),
|
|
50
|
+
warningIcon: chromeIcon('warning')
|
|
51
|
+
},
|
|
52
|
+
breadcrumb: {
|
|
53
|
+
dropdownIcon: chromeIcon('chevron-down')
|
|
54
|
+
},
|
|
55
|
+
button: {
|
|
56
|
+
loadingIcon: chromeIcon('loading', {
|
|
57
|
+
spin: true
|
|
58
|
+
})
|
|
59
|
+
},
|
|
60
|
+
collapse: {
|
|
61
|
+
expandIcon: ({ isActive })=>chromeIcon('chevron-right', {
|
|
62
|
+
rotate: isActive ? 90 : 0
|
|
63
|
+
})
|
|
64
|
+
},
|
|
65
|
+
datePicker: {
|
|
66
|
+
suffixIcon: chromeIcon('calendar')
|
|
67
|
+
},
|
|
68
|
+
drawer: {
|
|
69
|
+
closeIcon
|
|
70
|
+
},
|
|
71
|
+
menu: {
|
|
72
|
+
expandIcon: MenuExpandIcon
|
|
73
|
+
},
|
|
74
|
+
modal: {
|
|
75
|
+
closeIcon
|
|
76
|
+
},
|
|
77
|
+
tabs: {
|
|
78
|
+
moreIcon: chromeIcon('more')
|
|
79
|
+
},
|
|
80
|
+
timePicker: {
|
|
81
|
+
suffixIcon: chromeIcon('clock')
|
|
82
|
+
},
|
|
83
|
+
tour: {
|
|
84
|
+
closeIcon
|
|
85
|
+
},
|
|
86
|
+
treeSelect: {
|
|
87
|
+
switcherIcon: chromeIcon('chevron-down')
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export default createAntdIconConfig;
|
package/dist/createCallAPI.js
CHANGED
|
@@ -13,16 +13,20 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ import request from './request.js';
|
|
16
|
-
function createCallAPI(
|
|
16
|
+
function createCallAPI(lowdefy) {
|
|
17
|
+
const { basePath, buildId } = lowdefy;
|
|
17
18
|
function callAPI({ payload, endpointId, pageId, blockId }) {
|
|
18
19
|
return request({
|
|
20
|
+
buildId,
|
|
19
21
|
url: `${basePath}/api/endpoints/${endpointId}`,
|
|
20
22
|
method: 'POST',
|
|
21
23
|
body: {
|
|
22
24
|
payload,
|
|
23
25
|
pageId,
|
|
24
26
|
blockId
|
|
25
|
-
}
|
|
27
|
+
},
|
|
28
|
+
reloadOnAuthorizationError: false,
|
|
29
|
+
lowdefy
|
|
26
30
|
});
|
|
27
31
|
}
|
|
28
32
|
return callAPI;
|
|
@@ -13,16 +13,20 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ import request from './request.js';
|
|
16
|
-
function createCallRequest(
|
|
16
|
+
function createCallRequest(lowdefy) {
|
|
17
|
+
const { basePath, buildId } = lowdefy;
|
|
17
18
|
function callRequest({ actionId, blockId, pageId, payload, requestId }) {
|
|
18
19
|
return request({
|
|
20
|
+
buildId,
|
|
19
21
|
url: `${basePath}/api/request/${pageId}/${requestId}`,
|
|
20
22
|
method: 'POST',
|
|
21
23
|
body: {
|
|
22
24
|
actionId,
|
|
23
25
|
blockId,
|
|
24
26
|
payload
|
|
25
|
-
}
|
|
27
|
+
},
|
|
28
|
+
reloadOnAuthorizationError: true,
|
|
29
|
+
lowdefy
|
|
26
30
|
});
|
|
27
31
|
}
|
|
28
32
|
return callRequest;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ import { getDevError } from '@lowdefy/engine';
|
|
16
|
-
import {
|
|
16
|
+
import { expectedErrorNames } from '@lowdefy/errors';
|
|
17
17
|
import { serializer } from '@lowdefy/helpers';
|
|
18
18
|
function createHandleError(lowdefy) {
|
|
19
19
|
const loggedErrors = new Set();
|
|
@@ -36,9 +36,11 @@ function createHandleError(lowdefy) {
|
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
loggedErrors.add(errorKey);
|
|
39
|
-
//
|
|
40
|
-
// the
|
|
41
|
-
|
|
39
|
+
// An expected outcome - a UserError, or an auth gate's refusal the server
|
|
40
|
+
// already logged as one warning line - logs to the browser console only,
|
|
41
|
+
// never sent to the server or the error bar. Matched by name, not
|
|
42
|
+
// instanceof: plugins bundle their own @lowdefy/errors copy.
|
|
43
|
+
if (expectedErrorNames.has(error?.name)) {
|
|
42
44
|
logger.error(shown);
|
|
43
45
|
return;
|
|
44
46
|
}
|
package/dist/createIcon.js
CHANGED
|
@@ -15,7 +15,11 @@
|
|
|
15
15
|
*/ import React from 'react';
|
|
16
16
|
import { omit, type } from '@lowdefy/helpers';
|
|
17
17
|
import Icon from '@ant-design/icons';
|
|
18
|
+
import LucideIcon from 'lucide-react/dist/esm/Icon.mjs';
|
|
19
|
+
import { useLucideContext } from 'lucide-react/dist/esm/context.mjs';
|
|
18
20
|
import { cn, withBlockDefaults, ErrorBoundary } from '@lowdefy/block-utils';
|
|
21
|
+
import formatIconTitle from './formatIconTitle.js';
|
|
22
|
+
import renderIconNodes from './renderIconNodes.js';
|
|
19
23
|
import iconStyles from './style.module.css';
|
|
20
24
|
const lowdefyProps = [
|
|
21
25
|
'actionLog',
|
|
@@ -27,65 +31,163 @@ const lowdefyProps = [
|
|
|
27
31
|
'loading',
|
|
28
32
|
'menus',
|
|
29
33
|
'pageId',
|
|
34
|
+
'pathParams',
|
|
30
35
|
'registerEvent',
|
|
31
36
|
'registerMethod',
|
|
32
37
|
'schemaErrors',
|
|
33
38
|
'styles',
|
|
34
39
|
'validation'
|
|
35
40
|
];
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
// Drawn while every icon loads: an empty svg keeps the icon's box, so nothing
|
|
42
|
+
// shifts and no fallback flashes before the icon arrives.
|
|
43
|
+
const PENDING_ICON = {
|
|
44
|
+
node: []
|
|
45
|
+
};
|
|
46
|
+
function createIcon({ icons, loadAllIcons }) {
|
|
47
|
+
// Without a loader the map already holds every icon the app bundles.
|
|
48
|
+
let allIconsLoaded = type.isNone(loadAllIcons);
|
|
49
|
+
// icons is the live icon map: page loads, dev JIT and loadAllIcons add names
|
|
50
|
+
// to it, so every lookup happens at render.
|
|
51
|
+
function getIconData(name) {
|
|
52
|
+
if (type.isString(name) && Object.hasOwn(icons, name)) {
|
|
53
|
+
return icons[name];
|
|
42
54
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
// A name missing from the page's icons may be one of the app's, so the first
|
|
58
|
+
// miss waits for every icon to load before it draws the fallback. After a
|
|
59
|
+
// load, a missing name is unknown. A failed load (logged by the loader)
|
|
60
|
+
// draws the fallback, and a later miss tries again.
|
|
61
|
+
function useIconData(name) {
|
|
62
|
+
const data = getIconData(name);
|
|
63
|
+
const [settledName, setSettledName] = React.useState(null);
|
|
64
|
+
const pending = data === null && type.isString(name) && !allIconsLoaded && settledName !== name;
|
|
65
|
+
React.useEffect(()=>{
|
|
66
|
+
if (!pending) return undefined;
|
|
67
|
+
let mounted = true;
|
|
68
|
+
loadAllIcons().then(()=>{
|
|
69
|
+
allIconsLoaded = true;
|
|
70
|
+
}, ()=>undefined).then(()=>{
|
|
71
|
+
if (mounted) setSettledName(name);
|
|
72
|
+
});
|
|
73
|
+
return ()=>{
|
|
74
|
+
mounted = false;
|
|
75
|
+
};
|
|
76
|
+
}, [
|
|
77
|
+
pending,
|
|
78
|
+
name
|
|
79
|
+
]);
|
|
80
|
+
return {
|
|
81
|
+
data,
|
|
82
|
+
pending
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function IconSvg({ data, nonScalingStroke, svgProps, title }) {
|
|
86
|
+
return /*#__PURE__*/ React.createElement(LucideIcon, {
|
|
87
|
+
icon: {
|
|
88
|
+
node: [],
|
|
89
|
+
size: data.size,
|
|
90
|
+
width: data.width,
|
|
91
|
+
height: data.height
|
|
92
|
+
},
|
|
93
|
+
...data.attrs,
|
|
94
|
+
...svgProps
|
|
95
|
+
}, [
|
|
96
|
+
title ? /*#__PURE__*/ React.createElement("title", {
|
|
97
|
+
key: "title"
|
|
98
|
+
}, title) : null,
|
|
99
|
+
...renderIconNodes({
|
|
100
|
+
node: data.node,
|
|
101
|
+
nonScalingStroke
|
|
102
|
+
})
|
|
103
|
+
]);
|
|
104
|
+
}
|
|
105
|
+
function IconBlock({ blockId, classNames = {}, events, methods, onClick, properties, styles = {}, ...props }) {
|
|
106
|
+
const lucideContext = useLucideContext();
|
|
47
107
|
const propertiesObj = type.isString(properties) ? {
|
|
48
108
|
name: properties
|
|
49
109
|
} : properties;
|
|
110
|
+
const { data, pending } = useIconData(propertiesObj.name);
|
|
50
111
|
const spin = (propertiesObj.spin || events.onClick?.loading) && !propertiesObj.disableLoadingIcon;
|
|
51
|
-
const
|
|
112
|
+
const title = propertiesObj.title ?? formatIconTitle(propertiesObj.name);
|
|
113
|
+
const labelled = Boolean(title) || !type.isNone(props['aria-label']);
|
|
114
|
+
const nonScalingStroke = propertiesObj.nonScalingStroke ?? lucideContext.nonScalingStroke;
|
|
115
|
+
const triggerClick = events.onClick && (()=>methods.triggerEvent({
|
|
116
|
+
name: 'onClick'
|
|
117
|
+
}));
|
|
118
|
+
const svgProps = {
|
|
52
119
|
id: blockId,
|
|
53
120
|
className: cn(classNames.element, {
|
|
54
121
|
[iconStyles['icon-spin']]: spin
|
|
55
122
|
}),
|
|
56
123
|
style: {
|
|
57
124
|
cursor: onClick || events.onClick ? 'pointer' : undefined,
|
|
125
|
+
// CSS colour and transform, not SVG attributes: every set draws with
|
|
126
|
+
// currentColor, so one colour reaches stroke- and fill-based icons.
|
|
127
|
+
color: propertiesObj.color,
|
|
128
|
+
transform: propertiesObj.rotate ? `rotate(${propertiesObj.rotate}deg)` : undefined,
|
|
58
129
|
...styles.element
|
|
59
130
|
},
|
|
60
|
-
rotate: propertiesObj.rotate,
|
|
61
|
-
color: propertiesObj.color,
|
|
62
|
-
title: propertiesObj.title ?? formatTitle(propertiesObj.name),
|
|
63
131
|
size: propertiesObj.size,
|
|
64
|
-
|
|
132
|
+
strokeWidth: propertiesObj.strokeWidth,
|
|
133
|
+
'aria-hidden': labelled ? undefined : 'true',
|
|
134
|
+
onClick: onClick ?? triggerClick,
|
|
65
135
|
...omit(props, lowdefyProps)
|
|
66
136
|
};
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
137
|
+
const missingSvgProps = {
|
|
138
|
+
...svgProps,
|
|
139
|
+
style: {
|
|
140
|
+
...svgProps.style,
|
|
141
|
+
color: '#F00'
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const missingIcon = /*#__PURE__*/ React.createElement(IconSvg, {
|
|
145
|
+
data: getIconData('icon-missing'),
|
|
146
|
+
nonScalingStroke: nonScalingStroke,
|
|
147
|
+
svgProps: missingSvgProps,
|
|
148
|
+
title: title
|
|
149
|
+
});
|
|
150
|
+
if (spin) {
|
|
151
|
+
return /*#__PURE__*/ React.createElement(IconSvg, {
|
|
152
|
+
data: getIconData('loading'),
|
|
153
|
+
nonScalingStroke: nonScalingStroke,
|
|
154
|
+
svgProps: svgProps,
|
|
155
|
+
title: title
|
|
156
|
+
});
|
|
70
157
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
158
|
+
if (pending) {
|
|
159
|
+
return /*#__PURE__*/ React.createElement(IconSvg, {
|
|
160
|
+
data: PENDING_ICON,
|
|
161
|
+
nonScalingStroke: nonScalingStroke,
|
|
162
|
+
svgProps: svgProps,
|
|
163
|
+
title: title
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
if (!data) {
|
|
167
|
+
return missingIcon;
|
|
168
|
+
}
|
|
169
|
+
return(// Keyed so an icon that failed recovers when its name changes.
|
|
170
|
+
/*#__PURE__*/ React.createElement(ErrorBoundary, {
|
|
171
|
+
key: propertiesObj.name,
|
|
172
|
+
fallback: ()=>missingIcon
|
|
173
|
+
}, /*#__PURE__*/ React.createElement(IconSvg, {
|
|
174
|
+
data: data,
|
|
175
|
+
nonScalingStroke: nonScalingStroke,
|
|
176
|
+
svgProps: svgProps,
|
|
177
|
+
title: title
|
|
84
178
|
})));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
179
|
+
}
|
|
180
|
+
// antd's Icon renders `component` with React.createElement, so the component
|
|
181
|
+
// must keep its identity across renders or React remounts the <svg> every
|
|
182
|
+
// time. The per-render props travel through a render-function child instead.
|
|
183
|
+
const IconHost = ({ children })=>children();
|
|
184
|
+
// antd components add their classes (collapse arrow, tree switcher, menu
|
|
185
|
+
// expand icon) to the icon element they are given; their CSS expects them on
|
|
186
|
+
// the .anticon span, as on antd's own icons.
|
|
187
|
+
const AntIcon = ({ className, ...all })=>/*#__PURE__*/ React.createElement(Icon, {
|
|
188
|
+
className: className,
|
|
189
|
+
component: IconHost
|
|
190
|
+
}, ()=>/*#__PURE__*/ React.createElement(IconBlock, all));
|
|
89
191
|
return withBlockDefaults(AntIcon);
|
|
90
|
-
}
|
|
192
|
+
}
|
|
91
193
|
export default createIcon;
|