@applicaster/zapp-react-native-ui-components 15.0.0-rc.129 → 15.0.0-rc.131

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 (30) hide show
  1. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/model.test.ts +80 -0
  2. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +187 -0
  3. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/selectors.test.ts +45 -0
  4. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/style.test.ts +49 -0
  5. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/model.ts +47 -0
  6. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +170 -0
  7. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/selectors.ts +26 -0
  8. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/style.ts +29 -0
  9. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/types.ts +37 -0
  10. package/Components/MasterCell/DefaultComponents/PressableView.tsx +196 -0
  11. package/Components/MasterCell/DefaultComponents/index.ts +2 -0
  12. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +46 -0
  13. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +126 -0
  14. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ButtonContainerView.ts +23 -0
  15. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +16 -0
  16. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabel.ts +67 -0
  17. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +32 -0
  18. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +191 -0
  19. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +140 -0
  20. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +222 -0
  21. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
  22. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +104 -0
  23. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +118 -0
  24. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +73 -0
  25. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +86 -0
  26. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +35 -52
  27. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +35 -171
  28. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +36 -145
  29. package/Components/MasterCell/elementMapper.tsx +1 -0
  30. package/package.json +5 -5
@@ -0,0 +1,80 @@
1
+ import { buildActionButtonsModel } from "../model";
2
+
3
+ describe("buildActionButtonsModel", () => {
4
+ it("returns null when the container is disabled", () => {
5
+ const configuration = {
6
+ mobile_buttons_container_buttons_enabled: false,
7
+ mobile_button_1_button_enabled: true,
8
+ };
9
+
10
+ const value = (key) => configuration[key];
11
+
12
+ expect(
13
+ buildActionButtonsModel({
14
+ configuration,
15
+ value,
16
+ containerPrefix: "mobile_buttons_container",
17
+ buttonPrefix: "mobile_button",
18
+ })
19
+ ).toBeNull();
20
+ });
21
+
22
+ it("returns explicit enabled slots and semantic container data", () => {
23
+ const configuration = {
24
+ mobile_buttons_container_buttons_enabled: true,
25
+ mobile_buttons_container_align: "right",
26
+ mobile_buttons_container_margin_top: 1,
27
+ mobile_buttons_container_margin_right: 2,
28
+ mobile_buttons_container_margin_bottom: 3,
29
+ mobile_buttons_container_margin_left: 4,
30
+ mobile_buttons_container_stacking: "vertical",
31
+ mobile_buttons_container_horizontal_gutter: 8,
32
+ mobile_buttons_container_vertical_gutter: 12,
33
+ mobile_buttons_container_independent_styles: false,
34
+ mobile_button_1_button_enabled: true,
35
+ mobile_button_2_button_enabled: false,
36
+ mobile_button_3_button_enabled: true,
37
+ };
38
+
39
+ const value = (key) => configuration[key];
40
+
41
+ expect(
42
+ buildActionButtonsModel({
43
+ configuration,
44
+ value,
45
+ containerPrefix: "mobile_buttons_container",
46
+ buttonPrefix: "mobile_button",
47
+ })
48
+ ).toEqual({
49
+ enabledSlots: [1, 3],
50
+ buttonsCount: 2,
51
+ container: {
52
+ horizontalAlign: "flex-end",
53
+ margins: {
54
+ top: 1,
55
+ right: 2,
56
+ bottom: 3,
57
+ left: 4,
58
+ },
59
+ stacking: "vertical",
60
+ horizontalGutter: 8,
61
+ verticalGutter: 12,
62
+ independentStyles: false,
63
+ },
64
+ buttons: [
65
+ {
66
+ slot: 1,
67
+ renderIndex: 0,
68
+ specificPrefix: "mobile_button_1",
69
+ stylePrefix: "mobile_button_1",
70
+ },
71
+ {
72
+ slot: 3,
73
+ renderIndex: 1,
74
+ specificPrefix: "mobile_button_3",
75
+ stylePrefix: "mobile_button_1",
76
+ },
77
+ ],
78
+ });
79
+ });
80
+ });
@@ -0,0 +1,187 @@
1
+ import {
2
+ insertBetweenLabelContainers,
3
+ insertBetweenLabels,
4
+ } from "../placement";
5
+
6
+ describe("ActionButtonsCore placement", () => {
7
+ const buttons = { type: "View", name: "buttons" };
8
+
9
+ const above_labels = [
10
+ { name: "above_label_1" },
11
+ { name: "above_label_2" },
12
+ { name: "above_label_3" },
13
+ ];
14
+
15
+ const below_labels = [
16
+ { name: "below_label_1" },
17
+ { name: "below_label_2" },
18
+ { name: "below_label_3" },
19
+ ];
20
+
21
+ it("inserts buttons after the matching label", () => {
22
+ expect(
23
+ insertBetweenLabels({ position: "below_label_2" }, buttons, below_labels)
24
+ ).toEqual([below_labels[0], below_labels[1], buttons, below_labels[2]]);
25
+ });
26
+
27
+ it("inserts buttons before the matching label", () => {
28
+ expect(
29
+ insertBetweenLabels({ position: "above_label_2" }, buttons, above_labels)
30
+ ).toEqual([above_labels[0], buttons, above_labels[1], above_labels[2]]);
31
+ });
32
+
33
+ it("prepends buttons only when on_top is allowed", () => {
34
+ expect(
35
+ insertBetweenLabels(
36
+ { position: "on_top", allowOnTop: true },
37
+ buttons,
38
+ below_labels
39
+ )
40
+ ).toEqual([buttons, ...below_labels]);
41
+
42
+ expect(
43
+ insertBetweenLabels(
44
+ { position: "on_top", allowOnTop: false },
45
+ buttons,
46
+ below_labels
47
+ )
48
+ ).toEqual(below_labels);
49
+ });
50
+
51
+ it("appends buttons when appendWhenMissing is enabled", () => {
52
+ expect(
53
+ insertBetweenLabels(
54
+ { position: "unknown", appendWhenMissing: true },
55
+ buttons,
56
+ below_labels
57
+ )
58
+ ).toEqual([...below_labels, buttons]);
59
+ });
60
+
61
+ it("returns labels unchanged when appendWhenMissing is disabled", () => {
62
+ expect(
63
+ insertBetweenLabels(
64
+ { position: "unknown", appendWhenMissing: false },
65
+ buttons,
66
+ below_labels
67
+ )
68
+ ).toEqual(below_labels);
69
+ });
70
+
71
+ const labelContainers = [
72
+ {
73
+ elements: [
74
+ {
75
+ elements: [{ name: "top_label_1" }, { name: "top_label_2" }],
76
+ },
77
+ ],
78
+ },
79
+ {
80
+ elements: [
81
+ {
82
+ elements: [{ name: "bottom_label_1" }],
83
+ },
84
+ ],
85
+ },
86
+ ];
87
+
88
+ it("inserts buttons after the matching label in a nested container", () => {
89
+ expect(
90
+ insertBetweenLabelContainers(
91
+ { position: "below_top_label_2" },
92
+ buttons,
93
+ labelContainers
94
+ )
95
+ ).toEqual([
96
+ {
97
+ elements: [
98
+ {
99
+ elements: [
100
+ { name: "top_label_1" },
101
+ { name: "top_label_2" },
102
+ buttons,
103
+ ],
104
+ },
105
+ ],
106
+ },
107
+ labelContainers[1],
108
+ ]);
109
+ });
110
+
111
+ it("inserts buttons before the matching label in a nested container", () => {
112
+ expect(
113
+ insertBetweenLabelContainers(
114
+ { position: "above_top_label_2" },
115
+ buttons,
116
+ labelContainers
117
+ )
118
+ ).toEqual([
119
+ {
120
+ elements: [
121
+ {
122
+ elements: [
123
+ { name: "top_label_1" },
124
+ buttons,
125
+ { name: "top_label_2" },
126
+ ],
127
+ },
128
+ ],
129
+ },
130
+ labelContainers[1],
131
+ ]);
132
+ });
133
+
134
+ it("prepends buttons into the first container only when on_top is allowed", () => {
135
+ expect(
136
+ insertBetweenLabelContainers(
137
+ { position: "on_top", allowOnTop: true },
138
+ buttons,
139
+ labelContainers
140
+ )
141
+ ).toEqual([
142
+ {
143
+ elements: [buttons, ...labelContainers[0].elements],
144
+ },
145
+ labelContainers[1],
146
+ ]);
147
+ });
148
+
149
+ it("appends buttons into the last container when configured", () => {
150
+ expect(
151
+ insertBetweenLabelContainers(
152
+ { position: "unknown", appendWhenMissing: true },
153
+ buttons,
154
+ labelContainers
155
+ )
156
+ ).toEqual([
157
+ labelContainers[0],
158
+ {
159
+ elements: [...labelContainers[1].elements, buttons],
160
+ },
161
+ ]);
162
+ });
163
+
164
+ it("inserts buttons after a matching label in a direct two-level container", () => {
165
+ const directLabelContainers = [
166
+ {
167
+ elements: [{ name: "top_label_1" }, { name: "top_label_2" }],
168
+ },
169
+ {
170
+ elements: [{ name: "bottom_label_1" }],
171
+ },
172
+ ];
173
+
174
+ expect(
175
+ insertBetweenLabelContainers(
176
+ { position: "below_top_label_2" },
177
+ buttons,
178
+ directLabelContainers
179
+ )
180
+ ).toEqual([
181
+ {
182
+ elements: [{ name: "top_label_1" }, { name: "top_label_2" }, buttons],
183
+ },
184
+ directLabelContainers[1],
185
+ ]);
186
+ });
187
+ });
@@ -0,0 +1,45 @@
1
+ import {
2
+ getButtonSlotPrefix,
3
+ getEnabledButtonSlots,
4
+ getStylePrefix,
5
+ } from "../selectors";
6
+
7
+ describe("ActionButtonsCore selectors", () => {
8
+ it("returns explicit enabled button slots without collapsing sparse slots", () => {
9
+ const configuration = {
10
+ mobile_button_1_button_enabled: true,
11
+ mobile_button_2_button_enabled: false,
12
+ mobile_button_3_button_enabled: true,
13
+ };
14
+
15
+ expect(getEnabledButtonSlots(configuration, "mobile_button")).toEqual([
16
+ 1, 3,
17
+ ]);
18
+ });
19
+
20
+ it("returns slot-based prefixes", () => {
21
+ expect(getButtonSlotPrefix("tv_buttons_button", 3)).toBe(
22
+ "tv_buttons_button_3"
23
+ );
24
+ });
25
+
26
+ it("reuses button 1 styles when independent styles are disabled", () => {
27
+ expect(
28
+ getStylePrefix({
29
+ slot: 3,
30
+ independentStyles: false,
31
+ buttonPrefix: "mobile_button",
32
+ })
33
+ ).toBe("mobile_button_1");
34
+ });
35
+
36
+ it("uses the explicit slot prefix when independent styles are enabled", () => {
37
+ expect(
38
+ getStylePrefix({
39
+ slot: 3,
40
+ independentStyles: true,
41
+ buttonPrefix: "mobile_button",
42
+ })
43
+ ).toBe("mobile_button_3");
44
+ });
45
+ });
@@ -0,0 +1,49 @@
1
+ import { buildContainerLayout, getContainerMargins } from "../style";
2
+
3
+ describe("ActionButtonsCore style helpers", () => {
4
+ it("returns numeric margins with zero defaults", () => {
5
+ const configuration = {
6
+ mobile_buttons_container_margin_top: "4",
7
+ mobile_buttons_container_margin_left: 6,
8
+ };
9
+
10
+ const value = (key) => configuration[key];
11
+
12
+ expect(getContainerMargins(value, "mobile_buttons_container")).toEqual({
13
+ top: 4,
14
+ right: 0,
15
+ bottom: 0,
16
+ left: 6,
17
+ });
18
+ });
19
+
20
+ it("returns semantic layout data using shared alignment mapping", () => {
21
+ const configuration = {
22
+ tv_buttons_container_align: "middle",
23
+ tv_buttons_container_margin_top: 1,
24
+ tv_buttons_container_margin_right: 2,
25
+ tv_buttons_container_margin_bottom: 3,
26
+ tv_buttons_container_margin_left: 4,
27
+ tv_buttons_container_stacking: "vertical",
28
+ tv_buttons_container_horizontal_gutter: 8,
29
+ tv_buttons_container_vertical_gutter: 12,
30
+ tv_buttons_container_independent_styles: true,
31
+ };
32
+
33
+ const value = (key) => configuration[key];
34
+
35
+ expect(buildContainerLayout(value, "tv_buttons_container")).toEqual({
36
+ horizontalAlign: "center",
37
+ margins: {
38
+ top: 1,
39
+ right: 2,
40
+ bottom: 3,
41
+ left: 4,
42
+ },
43
+ stacking: "vertical",
44
+ horizontalGutter: 8,
45
+ verticalGutter: 12,
46
+ independentStyles: true,
47
+ });
48
+ });
49
+ });
@@ -0,0 +1,47 @@
1
+ import {
2
+ getButtonSlotPrefix,
3
+ getEnabledButtonSlots,
4
+ getStylePrefix,
5
+ } from "./selectors";
6
+ import { buildContainerLayout } from "./style";
7
+ import { ActionButtonsModel, BuildActionButtonsModelOptions } from "./types";
8
+
9
+ export const buildActionButtonsModel = ({
10
+ configuration,
11
+ value,
12
+ containerPrefix,
13
+ buttonPrefix,
14
+ maxButtons = 3,
15
+ }: BuildActionButtonsModelOptions): ActionButtonsModel | null => {
16
+ if (!value(`${containerPrefix}_buttons_enabled`)) {
17
+ return null;
18
+ }
19
+
20
+ const enabledSlots = getEnabledButtonSlots(
21
+ configuration,
22
+ buttonPrefix,
23
+ maxButtons
24
+ );
25
+
26
+ if (enabledSlots.length === 0) {
27
+ return null;
28
+ }
29
+
30
+ const container = buildContainerLayout(value, containerPrefix);
31
+
32
+ return {
33
+ enabledSlots,
34
+ buttonsCount: enabledSlots.length,
35
+ container,
36
+ buttons: enabledSlots.map((slot, renderIndex) => ({
37
+ slot,
38
+ renderIndex,
39
+ specificPrefix: getButtonSlotPrefix(buttonPrefix, slot),
40
+ stylePrefix: getStylePrefix({
41
+ slot,
42
+ independentStyles: container.independentStyles,
43
+ buttonPrefix,
44
+ }),
45
+ })),
46
+ };
47
+ };
@@ -0,0 +1,170 @@
1
+ type Label = {
2
+ name?: string;
3
+ elements?: Array<Label | unknown>;
4
+ };
5
+
6
+ type LabelContainer = {
7
+ elements?: Array<Label | unknown>;
8
+ };
9
+
10
+ type LabelExtraContainer = {
11
+ elements?: Array<LabelContainer | unknown>;
12
+ };
13
+
14
+ type InsertOptions = {
15
+ position?: string;
16
+ allowOnTop?: boolean;
17
+ appendWhenMissing?: boolean;
18
+ };
19
+
20
+ const hasLabelName = (value: unknown): value is Label =>
21
+ !!value &&
22
+ typeof value === "object" &&
23
+ typeof (value as Label).name === "string" &&
24
+ (value as Label).name !== "";
25
+
26
+ const withButtons = (
27
+ labelName: string | undefined,
28
+ buttons: unknown,
29
+ labels: Label[]
30
+ ) =>
31
+ labels.reduce<Array<Label | unknown>>((acc, label) => {
32
+ if (!!label?.name && labelName?.includes(label.name)) {
33
+ // handle above_*
34
+ if (labelName?.startsWith("above_")) {
35
+ return [...acc, buttons, label];
36
+ }
37
+
38
+ // handle below_* or plain exact match (default: insert after (covers below_* AND plain names))
39
+ return [...acc, label, buttons];
40
+ }
41
+
42
+ return [...acc, label];
43
+ }, []);
44
+
45
+ const withButtonsInNestedViews = (
46
+ labelName: string | undefined,
47
+ buttons: unknown,
48
+ views: Array<LabelContainer | unknown>
49
+ ) => {
50
+ const hasDirectLabels = views.some((view) => hasLabelName(view));
51
+
52
+ if (hasDirectLabels) {
53
+ return withButtons(labelName, buttons, views as Label[]);
54
+ }
55
+
56
+ return views.map((view) => {
57
+ if (
58
+ !view ||
59
+ typeof view !== "object" ||
60
+ !Array.isArray((view as LabelContainer).elements)
61
+ ) {
62
+ return view;
63
+ }
64
+
65
+ return {
66
+ ...(view as LabelContainer),
67
+ elements: withButtons(
68
+ labelName,
69
+ buttons,
70
+ (view as LabelContainer).elements || []
71
+ ),
72
+ };
73
+ });
74
+ };
75
+
76
+ const containsNestedLabel = (
77
+ labelName: string | undefined,
78
+ labelContainers: LabelExtraContainer[]
79
+ ) =>
80
+ labelContainers.some((labelContainer) =>
81
+ (labelContainer.elements || []).some((view) => {
82
+ if (hasLabelName(view)) {
83
+ return labelName?.includes(view.name) ?? false;
84
+ }
85
+
86
+ if (
87
+ !view ||
88
+ typeof view !== "object" ||
89
+ !Array.isArray((view as LabelContainer).elements)
90
+ ) {
91
+ return false;
92
+ }
93
+
94
+ return ((view as LabelContainer).elements || []).some((label) =>
95
+ hasLabelName(label) ? (labelName?.includes(label.name) ?? false) : false
96
+ );
97
+ })
98
+ );
99
+
100
+ export const insertBetweenLabels = (
101
+ { position, allowOnTop = false, appendWhenMissing = false }: InsertOptions,
102
+ buttons: unknown,
103
+ labels: Label[] = []
104
+ ) => {
105
+ if (buttons == null) {
106
+ return labels;
107
+ }
108
+
109
+ if (allowOnTop && position === "on_top") {
110
+ return [buttons, ...labels];
111
+ }
112
+
113
+ const labelsWithButtons = withButtons(position, buttons, labels);
114
+ const inserted = labelsWithButtons.length !== labels.length;
115
+
116
+ if (!inserted && appendWhenMissing) {
117
+ return [...labels, buttons];
118
+ }
119
+
120
+ return inserted ? labelsWithButtons : [...labels];
121
+ };
122
+
123
+ export const insertBetweenLabelContainers = (
124
+ { position, allowOnTop = false, appendWhenMissing = false }: InsertOptions,
125
+ buttons: unknown,
126
+ labelContainers: LabelExtraContainer[] = []
127
+ ) => {
128
+ if (buttons == null) {
129
+ return labelContainers;
130
+ }
131
+
132
+ if (labelContainers.length === 0) {
133
+ return [buttons];
134
+ }
135
+
136
+ if (allowOnTop && position === "on_top") {
137
+ return labelContainers.map((labelContainer, index) =>
138
+ index === 0
139
+ ? {
140
+ ...labelContainer,
141
+ elements: [buttons, ...(labelContainer.elements || [])],
142
+ }
143
+ : labelContainer
144
+ );
145
+ }
146
+
147
+ const hasMatchingLabel = containsNestedLabel(position, labelContainers);
148
+
149
+ const labelContainersWithButtons = labelContainers.map((labelContainer) => ({
150
+ ...labelContainer,
151
+ elements: withButtonsInNestedViews(
152
+ position,
153
+ buttons,
154
+ labelContainer.elements || []
155
+ ),
156
+ }));
157
+
158
+ if (!hasMatchingLabel && appendWhenMissing) {
159
+ return labelContainers.map((labelContainer, index) =>
160
+ index === labelContainers.length - 1
161
+ ? {
162
+ ...labelContainer,
163
+ elements: [...(labelContainer.elements || []), buttons],
164
+ }
165
+ : labelContainer
166
+ );
167
+ }
168
+
169
+ return hasMatchingLabel ? labelContainersWithButtons : labelContainers;
170
+ };
@@ -0,0 +1,26 @@
1
+ export const getButtonSlotPrefix = (buttonPrefix: string, slot: number) =>
2
+ `${buttonPrefix}_${slot}`;
3
+
4
+ export const getEnabledButtonSlots = (
5
+ configuration: Record<string, unknown>,
6
+ buttonPrefix: string,
7
+ maxButtons = 3
8
+ ): number[] =>
9
+ Array.from({ length: maxButtons }, (_, index) => index + 1).filter((slot) =>
10
+ Boolean(
11
+ configuration[`${getButtonSlotPrefix(buttonPrefix, slot)}_button_enabled`]
12
+ )
13
+ );
14
+
15
+ export const getStylePrefix = ({
16
+ slot,
17
+ independentStyles,
18
+ buttonPrefix,
19
+ }: {
20
+ slot: number;
21
+ independentStyles: boolean;
22
+ buttonPrefix: string;
23
+ }) =>
24
+ independentStyles
25
+ ? getButtonSlotPrefix(buttonPrefix, slot)
26
+ : getButtonSlotPrefix(buttonPrefix, 1);
@@ -0,0 +1,29 @@
1
+ import { mapSelfAlignment } from "@applicaster/zapp-react-native-utils/cellUtils";
2
+ import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
3
+
4
+ import { ActionButtonsContainerLayout } from "./types";
5
+
6
+ export const getContainerMargins = (
7
+ value: (key: string) => unknown,
8
+ prefix: string
9
+ ) => ({
10
+ top: toNumberWithDefaultZero(value(`${prefix}_margin_top`)),
11
+ right: toNumberWithDefaultZero(value(`${prefix}_margin_right`)),
12
+ bottom: toNumberWithDefaultZero(value(`${prefix}_margin_bottom`)),
13
+ left: toNumberWithDefaultZero(value(`${prefix}_margin_left`)),
14
+ });
15
+
16
+ export const buildContainerLayout = (
17
+ value: (key: string) => unknown,
18
+ prefix: string
19
+ ): ActionButtonsContainerLayout => ({
20
+ horizontalAlign: mapSelfAlignment(value(`${prefix}_align`)),
21
+ margins: getContainerMargins(value, prefix),
22
+ stacking:
23
+ value(`${prefix}_stacking`) === "vertical" ? "vertical" : "horizontal",
24
+ horizontalGutter: toNumberWithDefaultZero(
25
+ value(`${prefix}_horizontal_gutter`)
26
+ ),
27
+ verticalGutter: toNumberWithDefaultZero(value(`${prefix}_vertical_gutter`)),
28
+ independentStyles: Boolean(value(`${prefix}_independent_styles`)),
29
+ });
@@ -0,0 +1,37 @@
1
+ export type ActionButtonSlot = number;
2
+
3
+ export type ActionButtonDescriptor = {
4
+ slot: ActionButtonSlot;
5
+ renderIndex: number;
6
+ specificPrefix: string;
7
+ stylePrefix: string;
8
+ };
9
+
10
+ export type ActionButtonsContainerLayout = {
11
+ horizontalAlign: string;
12
+ margins: {
13
+ top: number;
14
+ right: number;
15
+ bottom: number;
16
+ left: number;
17
+ };
18
+ stacking: "horizontal" | "vertical";
19
+ horizontalGutter: number;
20
+ verticalGutter: number;
21
+ independentStyles: boolean;
22
+ };
23
+
24
+ export type ActionButtonsModel = {
25
+ enabledSlots: ActionButtonSlot[];
26
+ buttonsCount: number;
27
+ container: ActionButtonsContainerLayout;
28
+ buttons: ActionButtonDescriptor[];
29
+ };
30
+
31
+ export type BuildActionButtonsModelOptions = {
32
+ configuration: Record<string, unknown>;
33
+ value: (key: string) => unknown;
34
+ containerPrefix: string;
35
+ buttonPrefix: string;
36
+ maxButtons?: number;
37
+ };