@lowdefy/blocks-antd 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.
Files changed (97) hide show
  1. package/dist/blocks/AutoComplete/AutoComplete.js +1 -0
  2. package/dist/blocks/AutoComplete/meta.js +2 -1
  3. package/dist/blocks/ButtonSelector/ButtonSelector.js +74 -27
  4. package/dist/blocks/ButtonSelector/meta.js +18 -4
  5. package/dist/blocks/Carousel/meta.js +16 -0
  6. package/dist/blocks/CheckboxSelector/CheckboxSelector.js +46 -14
  7. package/dist/blocks/CheckboxSelector/meta.js +7 -1
  8. package/dist/blocks/CheckboxSwitch/CheckboxSwitch.js +1 -0
  9. package/dist/blocks/CheckboxSwitch/meta.js +4 -1
  10. package/dist/blocks/ColorSelector/ColorSelector.js +1 -0
  11. package/dist/blocks/ColorSelector/meta.js +2 -1
  12. package/dist/blocks/ConfigProvider/ConfigProvider.js +1 -0
  13. package/dist/blocks/ConfigProvider/meta.js +7 -0
  14. package/dist/blocks/ConfirmModal/ConfirmModal.js +2 -2
  15. package/dist/blocks/ConfirmModal/meta.js +2 -4
  16. package/dist/blocks/DateRangeSelector/DateRangeSelector.js +4 -9
  17. package/dist/blocks/DateRangeSelector/meta.js +4 -8
  18. package/dist/blocks/DateSelector/DateSelector.js +4 -3
  19. package/dist/blocks/DateSelector/meta.js +4 -5
  20. package/dist/blocks/DateTimeSelector/DateTimeSelector.js +4 -3
  21. package/dist/blocks/DateTimeSelector/meta.js +4 -5
  22. package/dist/blocks/DropdownMenu/meta.js +46 -6
  23. package/dist/blocks/Label/Label.js +30 -5
  24. package/dist/blocks/Label/meta.js +8 -2
  25. package/dist/blocks/ListSelector/ListSelector.js +384 -0
  26. package/dist/blocks/ListSelector/e2e.js +40 -0
  27. package/dist/blocks/ListSelector/meta.js +215 -0
  28. package/dist/blocks/Menu/Menu.js +26 -80
  29. package/dist/blocks/Menu/meta.js +160 -64
  30. package/dist/blocks/MobileMenu/meta.js +50 -50
  31. package/dist/blocks/Modal/Modal.js +2 -2
  32. package/dist/blocks/Modal/meta.js +2 -4
  33. package/dist/blocks/MonthSelector/MonthSelector.js +4 -3
  34. package/dist/blocks/MonthSelector/meta.js +4 -5
  35. package/dist/blocks/MultipleSelector/MultipleSelector.js +41 -9
  36. package/dist/blocks/MultipleSelector/meta.js +24 -5
  37. package/dist/blocks/NumberInput/NumberInput.js +3 -1
  38. package/dist/blocks/NumberInput/meta.js +3 -3
  39. package/dist/blocks/PageHeaderMenu/PageHeaderMenu.js +12 -3
  40. package/dist/blocks/PageHeaderMenu/meta.js +8 -1
  41. package/dist/blocks/PageSidebarLayout/PageSidebarLayout.js +2 -1
  42. package/dist/blocks/PageSidebarLayout/meta.js +8 -1
  43. package/dist/blocks/PageSiderMenu/PageSiderMenu.js +2 -1
  44. package/dist/blocks/PageSiderMenu/meta.js +8 -1
  45. package/dist/blocks/PasswordInput/PasswordInput.js +1 -0
  46. package/dist/blocks/PasswordInput/meta.js +2 -1
  47. package/dist/blocks/PhoneNumberInput/PhoneNumberInput.js +1 -0
  48. package/dist/blocks/PhoneNumberInput/meta.js +2 -1
  49. package/dist/blocks/RadioSelector/RadioSelector.js +44 -14
  50. package/dist/blocks/RadioSelector/meta.js +7 -1
  51. package/dist/blocks/RatingSlider/meta.js +2 -1
  52. package/dist/blocks/SegmentedSelector/SegmentedSelector.js +10 -4
  53. package/dist/blocks/SegmentedSelector/meta.js +7 -4
  54. package/dist/blocks/Selector/Selector.js +55 -9
  55. package/dist/blocks/Selector/meta.js +24 -5
  56. package/dist/blocks/Slider/Slider.js +1 -0
  57. package/dist/blocks/Slider/meta.js +2 -1
  58. package/dist/blocks/Switch/Switch.js +1 -0
  59. package/dist/blocks/Switch/meta.js +2 -1
  60. package/dist/blocks/Tabs/Tabs.js +30 -43
  61. package/dist/blocks/Tabs/meta.js +8 -10
  62. package/dist/blocks/TextArea/TextArea.js +1 -0
  63. package/dist/blocks/TextArea/meta.js +2 -1
  64. package/dist/blocks/TextInput/TextInput.js +1 -0
  65. package/dist/blocks/TextInput/meta.js +2 -1
  66. package/dist/blocks/TreeInput/TreeInput.js +91 -0
  67. package/dist/blocks/TreeInput/e2e.js +33 -0
  68. package/dist/blocks/TreeInput/meta.js +68 -0
  69. package/dist/blocks/TreeMultipleSelector/TreeMultipleSelector.js +161 -0
  70. package/dist/blocks/TreeMultipleSelector/e2e.js +46 -0
  71. package/dist/blocks/TreeMultipleSelector/meta.js +128 -0
  72. package/dist/blocks/TreeSelector/TreeSelector.js +127 -88
  73. package/dist/blocks/TreeSelector/e2e.js +20 -9
  74. package/dist/blocks/TreeSelector/meta.js +70 -254
  75. package/dist/blocks/WeekSelector/WeekSelector.js +2 -1
  76. package/dist/blocks/WeekSelector/meta.js +3 -3
  77. package/dist/blocks/buildMenuItems.js +89 -26
  78. package/dist/blocks/headerActions.js +87 -3
  79. package/dist/blocks/normalizeItemClassAndStyle.js +77 -0
  80. package/dist/blocks.js +3 -0
  81. package/dist/e2e.js +3 -0
  82. package/dist/getContrastTextColor.js +45 -0
  83. package/dist/getOptionColorStyle.js +36 -0
  84. package/dist/getSelectedIndex.js +42 -0
  85. package/dist/getSelectorOptions.js +67 -0
  86. package/dist/getTreeData.js +94 -0
  87. package/dist/metas.js +3 -0
  88. package/dist/schemas/dataOptions.js +36 -0
  89. package/dist/schemas/index.js +1 -0
  90. package/dist/schemas/label.js +3 -1
  91. package/dist/schemas/labelTooltip.js +44 -0
  92. package/dist/schemas/options.js +7 -3
  93. package/dist/schemas/treeSelectTheme.js +125 -0
  94. package/dist/serializeSelectorValue.js +38 -0
  95. package/dist/useSelectorOptions.js +38 -0
  96. package/dist/useSetData.js +27 -0
  97. package/package.json +9 -7
@@ -17,12 +17,18 @@ import { ConfigProvider, Radio, Space } from 'antd';
17
17
  import { renderHtml, withBlockDefaults } from '@lowdefy/block-utils';
18
18
  import { type } from '@lowdefy/helpers';
19
19
  import Label from '../Label/Label.js';
20
- import getValueIndex from '../../getValueIndex.js';
21
- import getUniqueValues from '../../getUniqueValues.js';
20
+ import getSelectedIndex from '../../getSelectedIndex.js';
21
+ import useSelectorOptions from '../../useSelectorOptions.js';
22
22
  import withTheme from '../withTheme.js';
23
23
  const RadioGroup = Radio.Group;
24
24
  const RadioSelector = ({ blockId, classNames = {}, components, events, loading, properties, required, styles = {}, validation, value, methods })=>{
25
- const uniqueValueOptions = getUniqueValues(properties.options || []);
25
+ const uniqueValueOptions = useSelectorOptions({
26
+ properties,
27
+ methods
28
+ });
29
+ const selectedIndex = getSelectedIndex(value, uniqueValueOptions, {
30
+ properties
31
+ });
26
32
  const radioGroup = /*#__PURE__*/ React.createElement(RadioGroup, {
27
33
  id: `${blockId}_input`,
28
34
  className: classNames.element,
@@ -38,33 +44,57 @@ const RadioSelector = ({ blockId, classNames = {}, components, events, loading,
38
44
  }
39
45
  });
40
46
  },
41
- value: `${getValueIndex(value, uniqueValueOptions)}`
47
+ value: `${getSelectedIndex(value, uniqueValueOptions, {
48
+ properties
49
+ })}`
42
50
  }, /*#__PURE__*/ React.createElement(Space, {
43
51
  direction: properties.direction,
44
52
  wrap: type.isNone(properties.wrap) ? true : properties.wrap,
45
53
  align: type.isNone(properties.align) ? 'start' : properties.align
46
- }, uniqueValueOptions.map((opt, i)=>type.isPrimitive(opt) ? /*#__PURE__*/ React.createElement(Radio, {
47
- id: `${blockId}_${opt}`,
48
- key: i,
49
- value: `${i}`
50
- }, renderHtml({
51
- html: `${opt}`,
52
- methods
53
- })) : /*#__PURE__*/ React.createElement(Radio, {
54
+ }, uniqueValueOptions.map((opt, i)=>{
55
+ if (type.isPrimitive(opt)) {
56
+ return /*#__PURE__*/ React.createElement(Radio, {
57
+ id: `${blockId}_${opt}`,
58
+ key: i,
59
+ value: `${i}`
60
+ }, renderHtml({
61
+ html: `${opt}`,
62
+ methods
63
+ }));
64
+ }
65
+ const isSelected = `${i}` === selectedIndex;
66
+ const radio = /*#__PURE__*/ React.createElement(Radio, {
54
67
  id: `${blockId}_${i}`,
55
68
  key: i,
56
69
  value: `${i}`,
57
70
  disabled: opt.disabled,
58
- style: opt.style
71
+ style: {
72
+ ...opt.style,
73
+ ...isSelected && opt.color ? {
74
+ color: opt.color
75
+ } : {}
76
+ }
59
77
  }, type.isNone(opt.label) ? renderHtml({
60
78
  html: `${opt.value}`,
61
79
  methods
62
80
  }) : renderHtml({
63
81
  html: opt.label,
64
82
  methods
65
- })))));
83
+ }));
84
+ if (type.isNone(opt.color)) return radio;
85
+ // Per-option color: token override colors this radio's selected dot.
86
+ return /*#__PURE__*/ React.createElement(ConfigProvider, {
87
+ key: i,
88
+ theme: {
89
+ token: {
90
+ colorPrimary: opt.color
91
+ }
92
+ }
93
+ }, radio);
94
+ })));
66
95
  return /*#__PURE__*/ React.createElement(Label, {
67
96
  blockId: blockId,
97
+ methods: methods,
68
98
  classNames: classNames,
69
99
  components: components,
70
100
  events: events,
@@ -15,6 +15,7 @@
15
15
  */ import LabelMeta from '../Label/meta.js';
16
16
  import label from '../../schemas/label.js';
17
17
  import options from '../../schemas/options.js';
18
+ import { data, html, valueKey, primaryKey } from '../../schemas/dataOptions.js';
18
19
  import { disabled, inputTitle } from '../../schemas/inputProperties.js';
19
20
  export default {
20
21
  category: 'input',
@@ -34,7 +35,8 @@ export default {
34
35
  event: {
35
36
  value: 'The selected value.'
36
37
  }
37
- }
38
+ },
39
+ onTooltipClick: 'Trigger actions when the tooltip icon is clicked.'
38
40
  },
39
41
  properties: {
40
42
  type: 'object',
@@ -75,6 +77,10 @@ export default {
75
77
  },
76
78
  label,
77
79
  options,
80
+ data,
81
+ html,
82
+ valueKey,
83
+ primaryKey,
78
84
  title: inputTitle,
79
85
  theme: {
80
86
  type: 'object',
@@ -38,7 +38,8 @@ export default {
38
38
  event: {
39
39
  value: 'The selected rating value.'
40
40
  }
41
- }
41
+ },
42
+ onTooltipClick: 'Trigger actions when the tooltip icon is clicked.'
42
43
  },
43
44
  properties: {
44
45
  type: 'object',
@@ -17,13 +17,17 @@ import { Segmented } from 'antd';
17
17
  import { renderHtml, withBlockDefaults } from '@lowdefy/block-utils';
18
18
  import { type } from '@lowdefy/helpers';
19
19
  import Label from '../Label/Label.js';
20
- import getValueIndex from '../../getValueIndex.js';
21
- import getUniqueValues from '../../getUniqueValues.js';
20
+ import getSelectedIndex from '../../getSelectedIndex.js';
21
+ import useSelectorOptions from '../../useSelectorOptions.js';
22
22
  import withTheme from '../withTheme.js';
23
23
  const SegmentedSelector = ({ blockId, classNames = {}, components: { Icon }, events, loading, methods, properties, required, styles = {}, validation, value })=>{
24
- const uniqueValueOptions = getUniqueValues(properties.options || []);
24
+ const uniqueValueOptions = useSelectorOptions({
25
+ properties,
26
+ methods
27
+ });
25
28
  return /*#__PURE__*/ React.createElement(Label, {
26
29
  blockId: blockId,
30
+ methods: methods,
27
31
  classNames: classNames,
28
32
  components: {
29
33
  Icon
@@ -75,7 +79,9 @@ const SegmentedSelector = ({ blockId, classNames = {}, components: { Icon }, eve
75
79
  disabled: properties.disabled || loading,
76
80
  vertical: properties.vertical,
77
81
  shape: properties.shape,
78
- value: type.isNone(value) ? undefined : getValueIndex(value, properties.options || []),
82
+ value: type.isNone(value) ? undefined : getSelectedIndex(value, uniqueValueOptions, {
83
+ properties
84
+ }),
79
85
  onChange: (index)=>{
80
86
  const val = type.isPrimitive(uniqueValueOptions[index]) ? uniqueValueOptions[index] : uniqueValueOptions[index].value;
81
87
  methods.setValue(val);
@@ -14,6 +14,7 @@
14
14
  limitations under the License.
15
15
  */ import LabelMeta from '../Label/meta.js';
16
16
  import label from '../../schemas/label.js';
17
+ import { data, html, valueKey, primaryKey } from '../../schemas/dataOptions.js';
17
18
  import { disabled, inputTitle, sizeSmallDefaultLarge } from '../../schemas/inputProperties.js';
18
19
  export default {
19
20
  category: 'input',
@@ -34,7 +35,8 @@ export default {
34
35
  event: {
35
36
  value: 'The selected value.'
36
37
  }
37
- }
38
+ },
39
+ onTooltipClick: 'Trigger actions when the tooltip icon is clicked.'
38
40
  },
39
41
  properties: {
40
42
  type: 'object',
@@ -46,6 +48,10 @@ export default {
46
48
  description: "Option to fit width to its parent's width."
47
49
  },
48
50
  disabled,
51
+ data,
52
+ html,
53
+ valueKey,
54
+ primaryKey,
49
55
  options: {
50
56
  default: [],
51
57
  oneOf: [
@@ -75,9 +81,6 @@ export default {
75
81
  description: 'Options can either be an array of primitive values, or an array of label, value pairs.',
76
82
  items: {
77
83
  type: 'object',
78
- required: [
79
- 'value'
80
- ],
81
84
  properties: {
82
85
  label: {
83
86
  type: 'string',
@@ -15,18 +15,46 @@
15
15
  */ import React, { useState } from 'react';
16
16
  import { renderHtml, withBlockDefaults } from '@lowdefy/block-utils';
17
17
  import { get, type } from '@lowdefy/helpers';
18
- import { Select } from 'antd';
18
+ import { ConfigProvider, Select } from 'antd';
19
19
  import Label from '../Label/Label.js';
20
20
  import withTheme from '../withTheme.js';
21
- import getValueIndex from '../../getValueIndex.js';
22
- import getUniqueValues from '../../getUniqueValues.js';
21
+ import getSelectedIndex from '../../getSelectedIndex.js';
22
+ import useSelectorOptions from '../../useSelectorOptions.js';
23
+ import getContrastTextColor from '../../getContrastTextColor.js';
23
24
  const Option = Select.Option;
24
25
  const Selector = ({ blockId, classNames = {}, components: { Icon, Link }, events, loading, methods, properties, required, styles = {}, validation, value })=>{
25
26
  const [fetchState, setFetch] = useState(false);
26
27
  const [elementId] = useState((0 | Math.random() * 9e2) + 1e2);
27
- const uniqueValueOptions = getUniqueValues(properties.options || []);
28
+ const uniqueValueOptions = useSelectorOptions({
29
+ properties,
30
+ methods
31
+ });
32
+ // Color the whole selector with the selected option's color: `solid` fills the
33
+ // input, otherwise the border/text is colored.
34
+ const selectedIndex = getSelectedIndex(value, uniqueValueOptions, {
35
+ properties
36
+ });
37
+ const selectedOption = type.isNone(selectedIndex) ? undefined : uniqueValueOptions[selectedIndex];
38
+ const selectedColor = type.isObject(selectedOption) ? selectedOption.color : undefined;
39
+ const isSolid = properties.variant === 'solid';
40
+ // `solid` is not a valid antd Select input variant — use outlined for the frame.
41
+ let antdVariant = properties.variant;
42
+ if (isSolid) antdVariant = 'outlined';
43
+ if (properties.bordered === false) antdVariant = 'borderless';
44
+ let selectTheme;
45
+ if (selectedColor) {
46
+ const token = {
47
+ colorPrimary: selectedColor,
48
+ colorBorder: selectedColor
49
+ };
50
+ if (isSolid) token.colorBgContainer = selectedColor;
51
+ selectTheme = {
52
+ token
53
+ };
54
+ }
28
55
  return /*#__PURE__*/ React.createElement(Label, {
29
56
  blockId: blockId,
57
+ methods: methods,
30
58
  classNames: classNames,
31
59
  components: {
32
60
  Icon,
@@ -48,9 +76,11 @@ const Selector = ({ blockId, classNames = {}, components: { Icon, Link }, events
48
76
  }
49
77
  }, /*#__PURE__*/ React.createElement("div", {
50
78
  id: `${blockId}_${elementId}_popup`
51
- }), /*#__PURE__*/ React.createElement(Select, {
79
+ }), /*#__PURE__*/ React.createElement(ConfigProvider, {
80
+ theme: selectTheme
81
+ }, /*#__PURE__*/ React.createElement(Select, {
52
82
  id: `${blockId}_input`,
53
- variant: properties.bordered === false ? 'borderless' : properties.variant,
83
+ variant: antdVariant,
54
84
  className: classNames.element,
55
85
  classNames: {
56
86
  content: classNames.selector
@@ -63,6 +93,17 @@ const Selector = ({ blockId, classNames = {}, components: { Icon, Link }, events
63
93
  content: styles.selector
64
94
  },
65
95
  mode: "single",
96
+ labelRender: (labelProps)=>{
97
+ const opt = uniqueValueOptions[labelProps.value];
98
+ const color = type.isPrimitive(opt) ? undefined : opt?.color;
99
+ if (type.isNone(color)) return labelProps.label;
100
+ const textColor = isSolid ? getContrastTextColor(color) ?? '#fff' : color;
101
+ return /*#__PURE__*/ React.createElement("span", {
102
+ style: {
103
+ color: textColor
104
+ }
105
+ }, labelProps.label);
106
+ },
66
107
  autoFocus: properties.autoFocus,
67
108
  getPopupContainer: ()=>document.getElementById(`${blockId}_${elementId}_popup`),
68
109
  disabled: properties.disabled || loading,
@@ -137,7 +178,9 @@ const Selector = ({ blockId, classNames = {}, components: { Icon, Link }, events
137
178
  setFetch(false);
138
179
  }
139
180
  },
140
- value: getValueIndex(value, uniqueValueOptions)
181
+ value: getSelectedIndex(value, uniqueValueOptions, {
182
+ properties
183
+ })
141
184
  }, uniqueValueOptions.map((opt, i)=>type.isPrimitive(opt) ? /*#__PURE__*/ React.createElement(Option, {
142
185
  style: styles.options,
143
186
  className: classNames.options,
@@ -150,7 +193,10 @@ const Selector = ({ blockId, classNames = {}, components: { Icon, Link }, events
150
193
  })) : /*#__PURE__*/ React.createElement(Option, {
151
194
  style: {
152
195
  ...styles.options,
153
- ...opt.style
196
+ ...opt.style,
197
+ ...opt.color ? {
198
+ color: opt.color
199
+ } : {}
154
200
  },
155
201
  className: classNames.options,
156
202
  disabled: opt.disabled,
@@ -164,7 +210,7 @@ const Selector = ({ blockId, classNames = {}, components: { Icon, Link }, events
164
210
  }) : renderHtml({
165
211
  html: opt.label,
166
212
  methods
167
- })))))
213
+ }))))))
168
214
  }
169
215
  });
170
216
  };
@@ -15,6 +15,7 @@
15
15
  */ import LabelMeta from '../Label/meta.js';
16
16
  import label from '../../schemas/label.js';
17
17
  import icon from '../../schemas/icon.js';
18
+ import { data, html, valueKey, primaryKey } from '../../schemas/dataOptions.js';
18
19
  import { disabled, inputTitle, autoFocus, variant, bordered, allowClear, sizeSmallDefaultLarge } from '../../schemas/inputProperties.js';
19
20
  export default {
20
21
  category: 'input',
@@ -49,7 +50,8 @@ export default {
49
50
  event: {
50
51
  value: 'The search input value.'
51
52
  }
52
- }
53
+ },
54
+ onTooltipClick: 'Trigger actions when the tooltip icon is clicked.'
53
55
  },
54
56
  properties: {
55
57
  type: 'object',
@@ -72,6 +74,10 @@ export default {
72
74
  },
73
75
  label,
74
76
  disabled,
77
+ data,
78
+ html,
79
+ valueKey,
80
+ primaryKey,
75
81
  options: {
76
82
  default: [],
77
83
  oneOf: [
@@ -101,9 +107,6 @@ export default {
101
107
  description: 'Options can either be an array of primitive values, on an array of label, value pairs.',
102
108
  items: {
103
109
  type: 'object',
104
- required: [
105
- 'value'
106
- ],
107
110
  properties: {
108
111
  label: {
109
112
  type: 'string',
@@ -147,6 +150,13 @@ export default {
147
150
  docs: {
148
151
  displayType: 'yaml'
149
152
  }
153
+ },
154
+ color: {
155
+ type: 'string',
156
+ description: 'Color applied to the selected value shown in the input, and used to tint this option in the dropdown.',
157
+ docs: {
158
+ displayType: 'color'
159
+ }
150
160
  }
151
161
  }
152
162
  }
@@ -185,7 +195,16 @@ export default {
185
195
  description: "Name of an React-Icon (See <a href='https://react-icons.github.io/react-icons/'>all icons</a>) or properties of an Icon block to customize icon at the drop-down position of the selector."
186
196
  },
187
197
  title: inputTitle,
188
- variant,
198
+ variant: {
199
+ type: 'string',
200
+ enum: [
201
+ 'solid',
202
+ 'outlined',
203
+ 'filled',
204
+ 'borderless'
205
+ ],
206
+ description: 'Input variant. `solid` fills the whole input with the selected option color; `outlined` colors its border/text. `filled`/`borderless` are the antd input styles.'
207
+ },
189
208
  theme: {
190
209
  type: 'object',
191
210
  description: 'Antd design token overrides for this block. See <a href="https://ant.design/components/overview#design-token">antd design tokens</a>.',
@@ -20,6 +20,7 @@ import withTheme from '../withTheme.js';
20
20
  const SliderBlock = ({ blockId, classNames = {}, components: { Icon, Link }, events, loading, methods, properties, required, styles = {}, validation, value })=>{
21
21
  return /*#__PURE__*/ React.createElement(Label, {
22
22
  blockId: blockId,
23
+ methods: methods,
23
24
  classNames: classNames,
24
25
  components: {
25
26
  Icon,
@@ -31,7 +31,8 @@ export default {
31
31
  event: {
32
32
  value: 'The current slider value.'
33
33
  }
34
- }
34
+ },
35
+ onTooltipClick: 'Trigger actions when the tooltip icon is clicked.'
35
36
  },
36
37
  properties: {
37
38
  type: 'object',
@@ -79,6 +79,7 @@ const SwitchBlock = ({ blockId, classNames = {}, components: { Icon, Link }, eve
79
79
  });
80
80
  return /*#__PURE__*/ React.createElement(Label, {
81
81
  blockId: blockId,
82
+ methods: methods,
82
83
  classNames: classNames,
83
84
  components: {
84
85
  Icon,
@@ -38,7 +38,8 @@ export default {
38
38
  event: {
39
39
  value: 'The switch value.'
40
40
  }
41
- }
41
+ },
42
+ onTooltipClick: 'Trigger actions when the tooltip icon is clicked.'
42
43
  },
43
44
  properties: {
44
45
  type: 'object',
@@ -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 React, { useState, useEffect, useCallback } from 'react';
15
+ */ import React, { useState, useEffect, useCallback, useRef } from 'react';
16
16
  import { renderHtml, withBlockDefaults } from '@lowdefy/block-utils';
17
17
  import { Tabs } from 'antd';
18
18
  import withTheme from '../withTheme.js';
@@ -38,16 +38,36 @@ function TabsBlock({ blockId, classNames = {}, components: { Icon, ShortcutBadge
38
38
  additionalProps.tabBarExtraContent = content[properties.extraAreaKey] && content[properties.extraAreaKey]();
39
39
  }
40
40
  const [key, setKey] = useState(properties.defaultActiveKey ?? tabs[0].key);
41
+ // Read latest tabs inside the stable handler without re-binding listeners.
42
+ const tabsRef = useRef(tabs);
43
+ tabsRef.current = tabs;
44
+ // Fires the generic onChange plus, if the now-active tab declares one, its
45
+ // own dynamically-named event. Stable identity so shortcut listeners and the
46
+ // registered method don't re-bind on every render.
47
+ const fireTabChange = useCallback((activeKey)=>{
48
+ setKey(activeKey);
49
+ methods.triggerEvent({
50
+ name: 'onChange',
51
+ event: {
52
+ activeKey
53
+ }
54
+ });
55
+ const activeTab = tabsRef.current.find((tab)=>tab.key === activeKey);
56
+ if (activeTab?.eventName) {
57
+ methods.triggerEvent({
58
+ name: activeTab.eventName,
59
+ event: {
60
+ key: activeKey
61
+ }
62
+ });
63
+ }
64
+ }, [
65
+ methods
66
+ ]);
41
67
  useEffect(()=>{
42
68
  methods.registerMethod('setActiveKey', ({ activeKey })=>{
43
69
  if (activeKey !== key) {
44
- setKey(activeKey);
45
- methods.triggerEvent({
46
- name: 'onChange',
47
- event: {
48
- activeKey
49
- }
50
- });
70
+ fireTabChange(activeKey);
51
71
  }
52
72
  });
53
73
  });
@@ -56,51 +76,18 @@ function TabsBlock({ blockId, classNames = {}, components: { Icon, ShortcutBadge
56
76
  shortcut: tab.shortcut,
57
77
  disabled: tab.disabled
58
78
  }));
59
- const onShortcutMatch = useCallback((activeKey)=>{
60
- setKey(activeKey);
61
- methods.triggerEvent({
62
- name: 'onChange',
63
- event: {
64
- activeKey
65
- }
66
- });
67
- }, [
68
- methods
69
- ]);
70
79
  useItemShortcuts({
71
80
  items: shortcutItems,
72
- onMatch: onShortcutMatch
81
+ onMatch: fireTabChange
73
82
  });
74
83
  return /*#__PURE__*/ React.createElement(Tabs, {
75
84
  activeKey: key,
76
85
  animated: properties.animated !== undefined ? properties.animated : true,
77
86
  id: blockId,
78
- onChange: (activeKey)=>{
79
- setKey(activeKey);
80
- methods.triggerEvent({
81
- name: 'onChange',
82
- event: {
83
- activeKey
84
- }
85
- });
86
- },
87
+ onChange: (activeKey)=>fireTabChange(activeKey),
87
88
  size: properties.size ?? 'default',
88
89
  tabPlacement: properties.tabPlacement ?? 'top',
89
90
  type: properties.tabType ?? 'line',
90
- onTabScroll: ({ direction })=>methods.triggerEvent({
91
- name: 'onTabScroll',
92
- event: {
93
- direction
94
- }
95
- }),
96
- onTabClick: (key)=>{
97
- methods.triggerEvent({
98
- name: 'onTabClick',
99
- event: {
100
- key
101
- }
102
- });
103
- },
104
91
  className: classNames.element,
105
92
  classNames: {
106
93
  tabBar: classNames.tabBar,
@@ -26,21 +26,15 @@
26
26
  },
27
27
  events: {
28
28
  onChange: {
29
- description: 'Trigger action on tab change.',
29
+ description: 'Trigger action on any tab change.',
30
30
  event: {
31
31
  activeKey: 'The key of the active tab.'
32
32
  }
33
33
  },
34
- onTabScroll: {
35
- description: 'Trigger action on tab scroll.',
34
+ onTabSelect: {
35
+ description: 'Documentation reference not a fixed event name. When a tab becomes active, the `eventName` string declared on that `tabs[]` entry is triggered (in addition to onChange). Declare your named events under `events:` (e.g. `onProfileTab`, `onBillingTab`).',
36
36
  event: {
37
- direction: 'The scroll direction.'
38
- }
39
- },
40
- onTabClick: {
41
- description: 'Trigger action on tab click.',
42
- event: {
43
- key: 'The key of the clicked tab.'
37
+ key: 'The key of the now-active tab.'
44
38
  }
45
39
  }
46
40
  },
@@ -103,6 +97,10 @@
103
97
  type: 'string',
104
98
  description: 'Area key of the tab.'
105
99
  },
100
+ eventName: {
101
+ type: 'string',
102
+ description: 'Block-level event name to trigger when this tab becomes active, in addition to onChange. Declare an event with this name under `events:`. The event receives `{ key }` of the now-active tab.'
103
+ },
106
104
  disabled: {
107
105
  type: 'boolean',
108
106
  default: false,
@@ -23,6 +23,7 @@ const TextAreaComp = Input.TextArea;
23
23
  const TextAreaBlock = ({ blockId, classNames = {}, components, events, loading, properties, required, styles = {}, validation, value, methods })=>{
24
24
  return /*#__PURE__*/ React.createElement(Label, {
25
25
  blockId: blockId,
26
+ methods: methods,
26
27
  classNames: classNames,
27
28
  components: components,
28
29
  events: events,
@@ -36,7 +36,8 @@ export default {
36
36
  }
37
37
  },
38
38
  onFocus: 'Trigger action when text input gets focus.',
39
- onPressEnter: 'Trigger action when enter is pressed while text input is focused.'
39
+ onPressEnter: 'Trigger action when enter is pressed while text input is focused.',
40
+ onTooltipClick: 'Trigger actions when the tooltip icon is clicked.'
40
41
  },
41
42
  properties: {
42
43
  type: 'object',
@@ -21,6 +21,7 @@ import useRunAfterUpdate from '../../useRunAfterUpdate.js';
21
21
  const TextInput = ({ blockId, classNames = {}, components: { Icon, Link }, events, loading, methods, onChange, properties, required, styles = {}, validation, value })=>{
22
22
  return /*#__PURE__*/ React.createElement(Label, {
23
23
  blockId: blockId,
24
+ methods: methods,
24
25
  classNames: classNames,
25
26
  components: {
26
27
  Icon,
@@ -39,7 +39,8 @@ export default {
39
39
  }
40
40
  },
41
41
  onFocus: 'Trigger action when text input gets focus.',
42
- onPressEnter: 'Trigger action when enter is pressed while text input is focused.'
42
+ onPressEnter: 'Trigger action when enter is pressed while text input is focused.',
43
+ onTooltipClick: 'Trigger actions when the tooltip icon is clicked.'
43
44
  },
44
45
  properties: {
45
46
  type: 'object',