@lowdefy/client 5.2.0 → 5.4.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.
@@ -69,9 +69,11 @@ 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
+ getLocale: ()=>lowdefy.i18n?.active ?? lowdefy.i18n?.defaultLocale,
72
73
  registerEvent: block.registerEvent,
73
74
  registerMethod: block.registerMethod,
74
75
  setValue: block.setValue,
76
+ translate: lowdefy._internal.translate,
75
77
  triggerEvent: block.triggerEvent
76
78
  }),
77
79
  basePath: lowdefy.basePath,
@@ -106,8 +108,10 @@ const CategorySwitch = ({ block, Blocks, context, loading, lowdefy })=>{
106
108
  layout: block.eval.layout
107
109
  }, /*#__PURE__*/ React.createElement(Component, {
108
110
  methods: Object.assign(block.methods, {
111
+ getLocale: ()=>lowdefy.i18n?.active ?? lowdefy.i18n?.defaultLocale,
109
112
  registerEvent: block.registerEvent,
110
113
  registerMethod: block.registerMethod,
114
+ translate: lowdefy._internal.translate,
111
115
  triggerEvent: block.triggerEvent
112
116
  }),
113
117
  basePath: lowdefy.basePath,
@@ -51,8 +51,10 @@ 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
+ getLocale: ()=>lowdefy.i18n?.active ?? lowdefy.i18n?.defaultLocale,
54
55
  registerEvent: block.registerEvent,
55
56
  registerMethod: block.registerMethod,
57
+ translate: lowdefy._internal.translate,
56
58
  triggerEvent: block.triggerEvent
57
59
  }),
58
60
  basePath: lowdefy.basePath,
@@ -51,8 +51,10 @@ const InputContainer = ({ 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
+ getLocale: ()=>lowdefy.i18n?.active ?? lowdefy.i18n?.defaultLocale,
54
55
  registerEvent: block.registerEvent,
55
56
  registerMethod: block.registerMethod,
57
+ translate: lowdefy._internal.translate,
56
58
  triggerEvent: block.triggerEvent,
57
59
  setValue: block.setValue,
58
60
  moveItemDown: block.moveItemDown,
@@ -56,12 +56,14 @@ 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
+ getLocale: ()=>lowdefy.i18n?.active ?? lowdefy.i18n?.defaultLocale,
59
60
  moveItemDown: block.moveItemDown,
60
61
  moveItemUp: block.moveItemUp,
61
62
  pushItem: block.pushItem,
62
63
  registerEvent: block.registerEvent,
63
64
  registerMethod: block.registerMethod,
64
65
  removeItem: block.removeItem,
66
+ translate: lowdefy._internal.translate,
65
67
  triggerEvent: block.triggerEvent,
66
68
  unshiftItem: block.unshiftItem
67
69
  }),
@@ -8,7 +8,7 @@ const createLinkComponent = (lowdefy, Link)=>{
8
8
  className: className,
9
9
  style: style,
10
10
  rel: rel,
11
- "aria-label": ariaLabel ?? 'back',
11
+ "aria-label": ariaLabel ?? lowdefy._internal.translate('client.backAriaLabel'),
12
12
  onClick: (...params)=>{
13
13
  lowdefy._internal.router.back();
14
14
  onClick(...params);
@@ -92,18 +92,19 @@ function displayKey(key) {
92
92
  if (key.length === 1) return key.toUpperCase();
93
93
  return key;
94
94
  }
95
- function createShortcutBadge() {
95
+ function createShortcutBadge(lowdefy) {
96
96
  function ShortcutBadge({ shortcut }) {
97
97
  if (!shortcut) return null;
98
98
  const primary = Array.isArray(shortcut) ? shortcut[0] : shortcut;
99
99
  if (!primary) return null;
100
100
  const segments = parseShortcut(primary);
101
+ const thenLabel = lowdefy?._internal?.translate ? lowdefy._internal.translate('client.shortcutThen') : 'then';
101
102
  return /*#__PURE__*/ React.createElement("span", {
102
103
  className: styles['shortcut-badge']
103
104
  }, segments.map((segment, i)=>segment === 'then' ? /*#__PURE__*/ React.createElement("span", {
104
105
  key: i,
105
106
  className: styles['shortcut-then']
106
- }, "then") : /*#__PURE__*/ React.createElement("kbd", {
107
+ }, thenLabel) : /*#__PURE__*/ React.createElement("kbd", {
107
108
  key: i,
108
109
  className: styles['shortcut-kbd']
109
110
  }, segment)));
package/dist/index.js CHANGED
@@ -16,3 +16,4 @@
16
16
  export default Client;
17
17
  export { default as getOrCreateAntdCssContainer } from './getOrCreateAntdCssContainer.js';
18
18
  export { default as useDarkMode } from './useDarkMode.js';
19
+ export { default as useLocale } from './useLocale.js';
@@ -12,7 +12,8 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import createCallAPI from './createCallAPI.js';
15
+ */ import { translate } from '@lowdefy/helpers';
16
+ import createCallAPI from './createCallAPI.js';
16
17
  import createAuthMethods from './auth/createAuthMethods.js';
17
18
  import createCallRequest from './createCallRequest.js';
18
19
  import createIcon from './createIcon.js';
@@ -29,7 +30,7 @@ function initLowdefyContext({ auth, Components, config, lowdefy, router, stage,
29
30
  blockMetas: types.blockMetas ?? {},
30
31
  components: {
31
32
  Icon: createIcon(types.icons),
32
- ShortcutBadge: createShortcutBadge()
33
+ ShortcutBadge: createShortcutBadge(lowdefy)
33
34
  },
34
35
  displayMessage: ({ content })=>{
35
36
  console.log(content);
@@ -56,6 +57,7 @@ function initLowdefyContext({ auth, Components, config, lowdefy, router, stage,
56
57
  lowdefy.basePath = router.basePath;
57
58
  lowdefy.contexts = {};
58
59
  lowdefy.inputs = {};
60
+ lowdefy.lowdefyApp = config.rootConfig.lowdefyApp;
59
61
  lowdefy.lowdefyGlobal = config.rootConfig.lowdefyGlobal;
60
62
  lowdefy.theme = config.rootConfig.theme ?? {};
61
63
  lowdefy._internal.callAPI = createCallAPI(lowdefy);
@@ -63,6 +65,11 @@ function initLowdefyContext({ auth, Components, config, lowdefy, router, stage,
63
65
  lowdefy._internal.callRequest = createCallRequest(lowdefy);
64
66
  lowdefy._internal.components.Link = createLinkComponent(lowdefy, Components.Link);
65
67
  lowdefy._internal.link = setupLink(lowdefy);
68
+ lowdefy._internal.translate = (key, values)=>translate({
69
+ key,
70
+ values,
71
+ i18n: lowdefy.i18n
72
+ });
66
73
  lowdefy._internal.updateBlock = (blockId)=>lowdefy._internal.updaters[blockId] && lowdefy._internal.updaters[blockId]();
67
74
  lowdefy._internal.logger = createBrowserLogger();
68
75
  lowdefy._internal.handleError = createHandleError(lowdefy);
package/dist/request.js CHANGED
@@ -12,7 +12,7 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import { serializer } from '@lowdefy/helpers';
15
+ */ import { serializer, translate } from '@lowdefy/helpers';
16
16
  async function request({ url, method = 'GET', body }) {
17
17
  const res = await fetch(url, {
18
18
  method,
@@ -26,7 +26,9 @@ async function request({ url, method = 'GET', body }) {
26
26
  if (body?.['~e']) {
27
27
  throw serializer.deserialize(body);
28
28
  }
29
- throw new Error(body.message || 'Request error');
29
+ throw new Error(body.message || translate({
30
+ key: 'client.requestError'
31
+ }));
30
32
  }
31
33
  return res.json();
32
34
  }
package/dist/setupLink.js CHANGED
@@ -23,7 +23,7 @@ function setupLink(lowdefy) {
23
23
  const handle = window.open(`${url}${query ? `?${query}` : ''}`, '_blank');
24
24
  if (!handle) {
25
25
  lowdefy._internal.displayMessage({
26
- content: 'A popup blocker may be preventing the application from opening the page. Approve the popup to continue.',
26
+ content: lowdefy._internal.translate('client.popupBlocked'),
27
27
  status: 'info',
28
28
  duration: 10
29
29
  });
@@ -0,0 +1,125 @@
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 { useCallback, useEffect, useMemo, useState } from 'react';
16
+ import dayjs from 'dayjs';
17
+ function pickBest(candidates, supported) {
18
+ for (const candidate of candidates ?? []){
19
+ if (supported.includes(candidate)) return candidate;
20
+ const primary = candidate.split('-')[0];
21
+ const match = supported.find((code)=>code.split('-')[0] === primary);
22
+ if (match) return match;
23
+ }
24
+ return undefined;
25
+ }
26
+ function resolveActive({ userPreference, browserLocale, defaultLocale, supportedCodes }) {
27
+ if (userPreference !== 'auto' && supportedCodes.includes(userPreference)) {
28
+ return userPreference;
29
+ }
30
+ if (browserLocale) return browserLocale;
31
+ return defaultLocale;
32
+ }
33
+ async function loadAntdLocale({ active, antdLocaleLoaders }) {
34
+ if (!active || !antdLocaleLoaders?.[active]) return null;
35
+ const mod = await antdLocaleLoaders[active]();
36
+ return mod?.default ?? mod;
37
+ }
38
+ async function loadAntdXLocale({ active, antdXLocaleLoaders }) {
39
+ if (!active || !antdXLocaleLoaders?.[active]) return null;
40
+ const mod = await antdXLocaleLoaders[active]();
41
+ return mod?.default ?? mod;
42
+ }
43
+ function useLocale({ i18n, antdLocaleLoaders, antdXLocaleLoaders, dayjsLocaleMap }) {
44
+ const supportedCodes = useMemo(()=>(i18n?.locales ?? []).map((l)=>l.code), [
45
+ i18n?.locales
46
+ ]);
47
+ const [userPreference, setUserPreference] = useState(()=>{
48
+ return window.localStorage?.getItem('lowdefy_locale') ?? 'auto';
49
+ });
50
+ const browserLocale = useMemo(()=>{
51
+ if (supportedCodes.length === 0) return undefined;
52
+ const candidates = window.navigator?.languages ?? [
53
+ window.navigator?.language
54
+ ].filter(Boolean);
55
+ return pickBest(candidates, supportedCodes);
56
+ }, [
57
+ supportedCodes
58
+ ]);
59
+ const setPreference = useCallback((code)=>{
60
+ if (code === 'auto') {
61
+ window.localStorage?.removeItem('lowdefy_locale');
62
+ } else {
63
+ window.localStorage?.setItem('lowdefy_locale', code);
64
+ }
65
+ setUserPreference(code ?? 'auto');
66
+ }, []);
67
+ window.__lowdefy_setLocale = setPreference;
68
+ const active = resolveActive({
69
+ userPreference,
70
+ browserLocale,
71
+ defaultLocale: i18n?.defaultLocale,
72
+ supportedCodes
73
+ });
74
+ window.__lowdefy_locale = active;
75
+ window.__lowdefy_supported_locales = i18n?.locales ?? [];
76
+ const [antdLocale, setAntdLocale] = useState(null);
77
+ const [antdXLocale, setAntdXLocale] = useState(null);
78
+ useEffect(()=>{
79
+ let cancelled = false;
80
+ loadAntdLocale({
81
+ active,
82
+ antdLocaleLoaders
83
+ }).then((locale)=>{
84
+ if (!cancelled) setAntdLocale(locale);
85
+ });
86
+ return ()=>{
87
+ cancelled = true;
88
+ };
89
+ }, [
90
+ active,
91
+ antdLocaleLoaders
92
+ ]);
93
+ useEffect(()=>{
94
+ let cancelled = false;
95
+ loadAntdXLocale({
96
+ active,
97
+ antdXLocaleLoaders
98
+ }).then((locale)=>{
99
+ if (!cancelled) setAntdXLocale(locale);
100
+ });
101
+ return ()=>{
102
+ cancelled = true;
103
+ };
104
+ }, [
105
+ active,
106
+ antdXLocaleLoaders
107
+ ]);
108
+ useEffect(()=>{
109
+ const dayjsId = dayjsLocaleMap?.[active];
110
+ if (dayjsId) {
111
+ dayjs.locale(dayjsId);
112
+ }
113
+ }, [
114
+ active,
115
+ dayjsLocaleMap
116
+ ]);
117
+ return {
118
+ active,
119
+ userPreference,
120
+ setPreference,
121
+ antdLocale,
122
+ antdXLocale
123
+ };
124
+ }
125
+ export default useLocale;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowdefy/client",
3
- "version": "5.2.0",
3
+ "version": "5.4.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Lowdefy Client",
6
6
  "homepage": "https://lowdefy.com",
@@ -35,19 +35,20 @@
35
35
  "dependencies": {
36
36
  "@ant-design/icons": "6.1.0",
37
37
  "antd": "6.3.1",
38
- "@lowdefy/block-utils": "5.2.0",
39
- "@lowdefy/engine": "5.2.0",
40
- "@lowdefy/errors": "5.2.0",
41
- "@lowdefy/helpers": "5.2.0",
42
- "@lowdefy/layout": "5.2.0",
43
- "@lowdefy/logger": "5.2.0",
38
+ "@lowdefy/block-utils": "5.4.0",
39
+ "@lowdefy/engine": "5.4.0",
40
+ "@lowdefy/errors": "5.4.0",
41
+ "@lowdefy/helpers": "5.4.0",
42
+ "@lowdefy/layout": "5.4.0",
43
+ "@lowdefy/logger": "5.4.0",
44
+ "dayjs": "1.11.19",
44
45
  "react": "18.2.0",
45
46
  "react-dom": "18.2.0",
46
47
  "tinykeys": "^3.0.0"
47
48
  },
48
49
  "devDependencies": {
49
50
  "@jest/globals": "28.1.3",
50
- "@lowdefy/jest-yaml-transform": "5.2.0",
51
+ "@lowdefy/jest-yaml-transform": "5.4.0",
51
52
  "@swc/cli": "0.8.0",
52
53
  "@swc/core": "1.15.18",
53
54
  "@swc/jest": "0.2.39",