slice-machine-ui 1.16.1-dev-next-release.0 → 1.16.1-dev-next-release.1

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 (29) hide show
  1. package/lib/builders/CustomTypeBuilder/SliceZone/List.tsx +12 -7
  2. package/lib/builders/CustomTypeBuilder/SliceZone/index.tsx +14 -12
  3. package/out/404.html +1 -1
  4. package/out/[lib]/[sliceName]/[variation]/screenshot.html +1 -1
  5. package/out/[lib]/[sliceName]/[variation]/simulator.html +1 -1
  6. package/out/[lib]/[sliceName]/[variation].html +1 -1
  7. package/out/_next/static/{h37WLcHVDHmbz47jhppT5 → E4K6KdIR2Dj6QhKo68tHE}/_buildManifest.js +1 -1
  8. package/out/_next/static/chunks/933-446b840f98e98471.js +1 -0
  9. package/out/_next/static/chunks/pages/{_app-aff70c74b7bb0ef6.js → _app-661015bd74d41ec7.js} +5 -5
  10. package/out/_next/static/chunks/pages/custom-types/{[customTypeId]-1baa6af0ddf0ab6b.js → [customTypeId]-375f234af61fdf94.js} +1 -1
  11. package/out/_next/static/chunks/pages/page-types/{[pageTypeId]-708b782db680b804.js → [pageTypeId]-7bf582e0368b1713.js} +1 -1
  12. package/out/changelog.html +1 -1
  13. package/out/changes.html +1 -1
  14. package/out/custom-types/[customTypeId].html +1 -1
  15. package/out/custom-types.html +1 -1
  16. package/out/index.html +1 -1
  17. package/out/page-types/[pageTypeId].html +1 -1
  18. package/out/slices.html +1 -1
  19. package/package.json +4 -4
  20. package/src/domain/slice.ts +51 -3
  21. package/src/features/slices/convertLegacySlice/ConvertLegacySliceAsNewSliceDialog.tsx +151 -0
  22. package/src/features/slices/convertLegacySlice/ConvertLegacySliceAsNewVariationDialog.tsx +233 -0
  23. package/src/features/slices/convertLegacySlice/ConvertLegacySliceButton.css.ts +15 -0
  24. package/src/features/slices/convertLegacySlice/ConvertLegacySliceButton.tsx +268 -0
  25. package/src/features/slices/convertLegacySlice/ConvertLegacySliceMergeWithIdenticalDialog.tsx +106 -0
  26. package/src/features/slices/convertLegacySlice/types.ts +37 -0
  27. package/src/features/slices/sliceCards/NonSharedSliceViewCard.tsx +18 -5
  28. package/out/_next/static/chunks/14-9205b47277b0e0f4.js +0 -1
  29. /package/out/_next/static/{h37WLcHVDHmbz47jhppT5 → E4K6KdIR2Dj6QhKo68tHE}/_ssgManifest.js +0 -0
@@ -0,0 +1,151 @@
1
+ import type { FC } from "react";
2
+ import { useSelector } from "react-redux";
3
+ import { Formik } from "formik";
4
+ import {
5
+ Box,
6
+ Dialog,
7
+ DialogActions,
8
+ DialogContent,
9
+ DialogHeader,
10
+ ScrollArea,
11
+ FormInput,
12
+ Text,
13
+ Select,
14
+ SelectItem,
15
+ } from "@prismicio/editor-ui";
16
+
17
+ import { SliceMachineStoreType } from "@src/redux/type";
18
+ import { getRemoteSlices } from "@src/modules/slices";
19
+ import { pascalize } from "@lib/utils/str";
20
+ import { validateSliceModalValues as validateAsNewSliceValues } from "@components/Forms/formsValidator";
21
+
22
+ import * as styles from "./ConvertLegacySliceButton.css";
23
+ import { DialogProps } from "./types";
24
+
25
+ export const ConvertLegacySliceAsNewSliceDialog: FC<DialogProps> = ({
26
+ isOpen,
27
+ close,
28
+ onSubmit,
29
+ isLoading,
30
+ slice,
31
+ libraries,
32
+ }) => {
33
+ const { remoteSlices } = useSelector((store: SliceMachineStoreType) => ({
34
+ remoteSlices: getRemoteSlices(store),
35
+ }));
36
+
37
+ return (
38
+ <Dialog
39
+ open={isOpen}
40
+ onOpenChange={(open) => !open && close()}
41
+ size={{ width: 448, height: "auto" }}
42
+ >
43
+ <DialogHeader title="Upgrade slice" />
44
+ <DialogContent>
45
+ <Formik
46
+ validateOnChange
47
+ validateOnMount
48
+ initialValues={{
49
+ _init: false,
50
+ from: libraries[0]?.name,
51
+ sliceName: pascalize(slice.key),
52
+ }}
53
+ validate={(values) => {
54
+ return validateAsNewSliceValues(values, libraries, remoteSlices);
55
+ }}
56
+ onSubmit={(values) => {
57
+ onSubmit({ libraryID: values.from, sliceID: values.sliceName });
58
+ }}
59
+ >
60
+ {(formik) => {
61
+ return (
62
+ <form id="convert-legacy-slice-as-new-slice-dialog">
63
+ <Box display="flex" flexDirection="column">
64
+ <ScrollArea className={styles.scrollArea}>
65
+ <Text variant="normal" color="grey11">
66
+ This will create a new slice with the same fields. The new
67
+ slice will replace the legacy slice in all of your types,
68
+ and the existing slice content will be re-mapped to the
69
+ new slice.
70
+ </Text>
71
+ <Text variant="normal" color="grey11">
72
+ This will not migrate your component. You will need to do
73
+ that manually.
74
+ </Text>
75
+ <Box display="flex" flexDirection="column" gap={4}>
76
+ <label className={styles.label}>
77
+ <Text variant="bold">Display name*</Text>
78
+ {typeof formik.errors.sliceName === "string" ? (
79
+ <Text variant="small" color="tomato10">
80
+ {formik.errors.sliceName}
81
+ </Text>
82
+ ) : null}
83
+ </label>
84
+ <FormInput
85
+ placeholder={`Pascalised Slice API ID (e.g. ${pascalize(
86
+ slice.key
87
+ )})`}
88
+ error={typeof formik.errors.sliceName === "string"}
89
+ value={formik.values.sliceName}
90
+ onValueChange={(value) =>
91
+ void formik.setFieldValue(
92
+ "sliceName",
93
+ value.slice(0, 30)
94
+ )
95
+ }
96
+ data-cy="slice-name-input"
97
+ />
98
+ <Text variant="normal" color="grey11">
99
+ A display name for the slice
100
+ </Text>
101
+ </Box>
102
+ <Box display="flex" flexDirection="column" gap={4}>
103
+ <label className={styles.label}>
104
+ <Text variant="bold">Slice library*</Text>
105
+ {typeof formik.errors.from === "string" ? (
106
+ <Text variant="small" color="tomato10">
107
+ {formik.errors.from}
108
+ </Text>
109
+ ) : null}
110
+ </label>
111
+ <Select
112
+ size="medium"
113
+ variant="secondary"
114
+ startIcon="folder"
115
+ flexContent={true}
116
+ value={formik.values.from}
117
+ onValueChange={(value) =>
118
+ value
119
+ ? void formik.setFieldValue("from", value)
120
+ : null
121
+ }
122
+ >
123
+ {libraries.map((library) => (
124
+ <SelectItem key={library.name} value={library.name}>
125
+ {library.name}
126
+ </SelectItem>
127
+ ))}
128
+ </Select>
129
+ <Text variant="normal" color="grey11">
130
+ The library where we'll store your slice
131
+ </Text>
132
+ </Box>
133
+ </ScrollArea>
134
+ <DialogActions
135
+ ok={{
136
+ text: "Upgrade",
137
+ onClick: () => void formik.submitForm(),
138
+ loading: isLoading,
139
+ disabled: !formik.isValid,
140
+ }}
141
+ cancel={{ text: "Cancel" }}
142
+ />
143
+ </Box>
144
+ </form>
145
+ );
146
+ }}
147
+ </Formik>
148
+ </DialogContent>
149
+ </Dialog>
150
+ );
151
+ };
@@ -0,0 +1,233 @@
1
+ import { useState, type FC } from "react";
2
+ import { Formik } from "formik";
3
+ import {
4
+ Box,
5
+ Dialog,
6
+ DialogActions,
7
+ DialogContent,
8
+ DialogHeader,
9
+ ScrollArea,
10
+ FormInput,
11
+ Text,
12
+ Select,
13
+ SelectItem,
14
+ } from "@prismicio/editor-ui";
15
+
16
+ import { Variation } from "@models/common/Variation";
17
+ import { LibraryUI } from "@models/common/LibraryUI";
18
+
19
+ import * as styles from "./ConvertLegacySliceButton.css";
20
+ import { DialogProps } from "./types";
21
+
22
+ type FormValues = {
23
+ libraryID: string;
24
+ sliceID: string;
25
+ variationID: string;
26
+ variationName: string;
27
+ };
28
+
29
+ export const ConvertLegacySliceAsNewVariationDialog: FC<DialogProps> = ({
30
+ isOpen,
31
+ close,
32
+ onSubmit,
33
+ isLoading,
34
+ slice,
35
+ sliceName,
36
+ libraries,
37
+ localSharedSlices,
38
+ }) => {
39
+ const [inferIDFromName, setInferIDFromName] = useState(true);
40
+
41
+ return (
42
+ <Dialog
43
+ open={isOpen}
44
+ onOpenChange={(open) => !open && close()}
45
+ size={{ width: 448, height: "auto" }}
46
+ >
47
+ <DialogHeader title="Convert to slice variation" />
48
+ <DialogContent>
49
+ <Formik
50
+ validateOnChange
51
+ initialValues={{
52
+ libraryID: localSharedSlices[0]?.from,
53
+ sliceID: localSharedSlices[0]?.model.id,
54
+ variationID: Variation.generateId(slice.key),
55
+ variationName: sliceName,
56
+ }}
57
+ validate={(values) => {
58
+ return validateAsNewVariationValues(values, libraries);
59
+ }}
60
+ onSubmit={(values) => {
61
+ onSubmit(values);
62
+ }}
63
+ >
64
+ {(formik) => {
65
+ return (
66
+ <form id="convert-legacy-slice-as-new-variation-dialog">
67
+ <Box display="flex" flexDirection="column">
68
+ <ScrollArea className={styles.scrollArea}>
69
+ <Text variant="normal" color="grey11">
70
+ If you have multiple slices that are similar, you can
71
+ combine them as variations of the same slice.
72
+ </Text>
73
+ <Box display="flex" flexDirection="column" gap={4}>
74
+ <label className={styles.label}>
75
+ <Text variant="bold">Target slice*</Text>
76
+ {typeof formik.errors.libraryID === "string" ? (
77
+ <Text variant="small" color="tomato10">
78
+ {formik.errors.libraryID}
79
+ </Text>
80
+ ) : null}
81
+ {typeof formik.errors.sliceID === "string" ? (
82
+ <Text variant="small" color="tomato10">
83
+ {formik.errors.sliceID}
84
+ </Text>
85
+ ) : null}
86
+ </label>
87
+ <Select
88
+ size="medium"
89
+ variant="secondary"
90
+ startIcon="viewDay"
91
+ flexContent={true}
92
+ value={`${formik.values.libraryID}::${formik.values.sliceID}`}
93
+ onValueChange={(value) => {
94
+ if (value) {
95
+ const [libraryID, sliceID] = value.split("::");
96
+ void formik.setFieldValue("libraryID", libraryID);
97
+ void formik.setFieldValue("sliceID", sliceID);
98
+ }
99
+ }}
100
+ >
101
+ {localSharedSlices.map((slice) => (
102
+ <SelectItem
103
+ key={`${slice.from}::${slice.model.id}`}
104
+ value={`${slice.from}::${slice.model.id}`}
105
+ >
106
+ {slice.from} {">"} {slice.model.name} (
107
+ {slice.model.id})
108
+ </SelectItem>
109
+ ))}
110
+ </Select>
111
+ <Text variant="normal" color="grey11">
112
+ Choose the slice to which you would like to add this
113
+ variation.
114
+ </Text>
115
+ </Box>
116
+ <Box display="flex" flexDirection="column" gap={4}>
117
+ <label className={styles.label}>
118
+ <Text variant="bold">Variation name*</Text>
119
+ {typeof formik.errors.variationName === "string" ? (
120
+ <Text variant="small" color="tomato10">
121
+ {formik.errors.variationName}
122
+ </Text>
123
+ ) : null}
124
+ </label>
125
+ <FormInput
126
+ placeholder={sliceName}
127
+ error={typeof formik.errors.variationName === "string"}
128
+ value={formik.values.variationName}
129
+ onValueChange={(value) => {
130
+ const values = {
131
+ ...formik.values,
132
+ variationName: value.slice(0, 30),
133
+ };
134
+
135
+ if (inferIDFromName) {
136
+ values.variationID = Variation.generateId(
137
+ values.variationName
138
+ );
139
+ }
140
+
141
+ formik.setValues(values, true);
142
+ }}
143
+ data-cy="variation-name-input"
144
+ />
145
+ </Box>
146
+ <Box display="flex" flexDirection="column" gap={4}>
147
+ <label className={styles.label}>
148
+ <Text variant="bold">ID*</Text>
149
+ {typeof formik.errors.variationID === "string" ? (
150
+ <Text variant="small" color="tomato10">
151
+ {formik.errors.variationID}
152
+ </Text>
153
+ ) : null}
154
+ </label>
155
+ <FormInput
156
+ placeholder={Variation.generateId(slice.key)}
157
+ error={typeof formik.errors.variationID === "string"}
158
+ value={formik.values.variationID}
159
+ onValueChange={(value) => {
160
+ setInferIDFromName(false);
161
+ void formik.setFieldValue(
162
+ "variationID",
163
+ Variation.generateId(value.slice(0, 30))
164
+ );
165
+ }}
166
+ data-cy="variation-id-input"
167
+ />
168
+ </Box>
169
+ </ScrollArea>
170
+ <DialogActions
171
+ ok={{
172
+ text: "Convert",
173
+ onClick: () => void formik.submitForm(),
174
+ loading: isLoading,
175
+ disabled: !formik.isValid,
176
+ }}
177
+ cancel={{ text: "Cancel" }}
178
+ />
179
+ </Box>
180
+ </form>
181
+ );
182
+ }}
183
+ </Formik>
184
+ </DialogContent>
185
+ </Dialog>
186
+ );
187
+ };
188
+
189
+ const validateAsNewVariationValues = (
190
+ values: FormValues,
191
+ libraries: readonly LibraryUI[]
192
+ ): Partial<Record<keyof FormValues, string>> => {
193
+ const errors: Partial<Record<keyof FormValues, string>> = {};
194
+
195
+ if (!values.libraryID) {
196
+ errors.libraryID = "Cannot be empty.";
197
+ }
198
+ const library = libraries.find(
199
+ (library) => library.path === values.libraryID
200
+ );
201
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
202
+ if (!errors.libraryID && !library) {
203
+ errors.libraryID = "Does not exist.";
204
+ }
205
+
206
+ if (!values.sliceID) {
207
+ errors.sliceID = "Cannot be empty.";
208
+ }
209
+ const slice = library?.components.find(
210
+ (component) => component.model.id === values.sliceID
211
+ );
212
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
213
+ if (!errors.sliceID && !slice) {
214
+ errors.sliceID = "Does not exist.";
215
+ }
216
+
217
+ if (!values.variationName) {
218
+ errors.variationName = "Cannot be empty.";
219
+ }
220
+
221
+ if (!values.variationID) {
222
+ errors.variationID = "Cannot be empty.";
223
+ } else {
224
+ const variationIDs =
225
+ slice?.model.variations.map((variation) => variation.id) ?? [];
226
+
227
+ if (variationIDs.includes(values.variationID)) {
228
+ errors.variationID = "Slice variation ID is already taken.";
229
+ }
230
+ }
231
+
232
+ return errors;
233
+ };
@@ -0,0 +1,15 @@
1
+ import { sprinkles } from "@prismicio/editor-ui";
2
+
3
+ export const scrollArea = sprinkles({
4
+ height: "100%",
5
+ display: "flex",
6
+ gap: 8,
7
+ paddingInline: 16,
8
+ paddingBlock: 16,
9
+ });
10
+
11
+ export const label = sprinkles({
12
+ display: "inline-flex",
13
+ alignItems: "center",
14
+ gap: 4,
15
+ });
@@ -0,0 +1,268 @@
1
+ import { useMemo, useState, type FC } from "react";
2
+ import { useSelector } from "react-redux";
3
+ import {
4
+ DropdownMenu,
5
+ DropdownMenuContent,
6
+ DropdownMenuItem,
7
+ DropdownMenuTrigger,
8
+ Icon,
9
+ Button,
10
+ } from "@prismicio/editor-ui";
11
+
12
+ import { NonSharedSliceInSliceZone } from "@models/common/CustomType/sliceZone";
13
+ import { ComponentUI } from "@models/common/ComponentUI";
14
+ import { CustomTypes } from "@models/common/CustomType";
15
+ import { getLibraries } from "@src/modules/slices";
16
+ import { SliceMachineStoreType } from "@src/redux/type";
17
+ import { managerClient } from "@src/managerClient";
18
+ import { getState, telemetry } from "@src/apiClient";
19
+ import useSliceMachineActions from "@src/modules/useSliceMachineActions";
20
+ import { ToasterType } from "@src/modules/toaster";
21
+ import { getFieldMappingFingerprint } from "@src/domain/slice";
22
+
23
+ import { NonSharedSliceViewCardProps } from "../sliceCards/NonSharedSliceViewCard";
24
+ import { ConvertLegacySliceAsNewSliceDialog } from "./ConvertLegacySliceAsNewSliceDialog";
25
+ import { ConvertLegacySliceAsNewVariationDialog } from "./ConvertLegacySliceAsNewVariationDialog";
26
+ import { ConvertLegacySliceMergeWithIdenticalDialog } from "./ConvertLegacySliceMergeWithIdenticalDialog";
27
+ import {
28
+ ConvertLegacySliceAndTrackArgs,
29
+ IdenticalSlice,
30
+ LegacySliceConversionType,
31
+ } from "./types";
32
+
33
+ type ConvertLegacySliceButtonProps = NonSharedSliceViewCardProps;
34
+
35
+ export const ConvertLegacySliceButton: FC<ConvertLegacySliceButtonProps> = ({
36
+ slice,
37
+ path,
38
+ }) => {
39
+ const {
40
+ refreshState,
41
+ openToaster,
42
+ initCustomTypeStore,
43
+ saveCustomTypeSuccess,
44
+ } = useSliceMachineActions();
45
+
46
+ const [isLoading, setIsLoading] = useState(false);
47
+ const [dialog, setDialog] = useState<LegacySliceConversionType | undefined>();
48
+
49
+ const { libraries: allLibraries } = useSelector(
50
+ (store: SliceMachineStoreType) => ({
51
+ libraries: getLibraries(store),
52
+ })
53
+ );
54
+
55
+ const sliceName =
56
+ slice.value.type === "Slice"
57
+ ? slice.value.fieldset ?? slice.key
58
+ : slice.key;
59
+ const libraries = allLibraries.filter((library) => library.isLocal);
60
+ const localSharedSlices = libraries
61
+ .map((library) => library.components)
62
+ .flat();
63
+ const identicalSlices = useIdenticalSlices(
64
+ slice,
65
+ sliceName,
66
+ localSharedSlices
67
+ );
68
+
69
+ const convertLegacySliceAndTrack = async (
70
+ args: ConvertLegacySliceAndTrackArgs
71
+ ) => {
72
+ if (!dialog) {
73
+ return;
74
+ }
75
+
76
+ setIsLoading(true);
77
+
78
+ void telemetry.track({
79
+ event: "legacy-slice:converted",
80
+ id: args.sliceID,
81
+ variation: args.variationID ?? "default",
82
+ library: args.libraryID,
83
+ conversionType: dialog,
84
+ });
85
+
86
+ const { errors } =
87
+ await managerClient.slices.convertLegacySliceToSharedSlice({
88
+ model: slice.value,
89
+ src: {
90
+ ...path,
91
+ sliceID: slice.key,
92
+ },
93
+ dest: {
94
+ libraryID: args.libraryID,
95
+ sliceID: args.sliceID,
96
+ variationName: args.variationName ?? "Default",
97
+ variationID: args.variationID ?? "default",
98
+ },
99
+ });
100
+
101
+ if (errors.length) {
102
+ console.error(`Could not convert slice \`${sliceName}\``, errors);
103
+
104
+ openToaster(
105
+ `Could not convert slice \`${sliceName}\``,
106
+ ToasterType.ERROR
107
+ );
108
+
109
+ throw errors;
110
+ }
111
+
112
+ const { model: customType, errors: customTypeReadErrors } =
113
+ await managerClient.customTypes.readCustomType({
114
+ id: path.customTypeID,
115
+ });
116
+
117
+ if (customTypeReadErrors.length || !customType) {
118
+ console.error(
119
+ `Could not refresh custom type view \`${path.customTypeID}\``,
120
+ customTypeReadErrors
121
+ );
122
+
123
+ openToaster(
124
+ `Could not refresh custom type view \`${path.customTypeID}\``,
125
+ ToasterType.ERROR
126
+ );
127
+
128
+ return;
129
+ }
130
+
131
+ // TODO(DT-1453): Remove the need of the global getState
132
+ const serverState = await getState();
133
+ // Update Redux store
134
+ refreshState(serverState);
135
+
136
+ setIsLoading(false);
137
+ setDialog(undefined);
138
+ switch (dialog) {
139
+ case "as_new_slice":
140
+ openToaster(
141
+ `${sliceName} has been upgraded to a new slice ${args.libraryID} > ${args.sliceID}`,
142
+ ToasterType.SUCCESS
143
+ );
144
+ break;
145
+
146
+ case "as_new_variation":
147
+ openToaster(
148
+ `${sliceName} has been converted as a variation of ${args.libraryID} > ${args.sliceID}`,
149
+ ToasterType.SUCCESS
150
+ );
151
+ break;
152
+
153
+ case "merge_with_identical":
154
+ default:
155
+ openToaster(
156
+ `${sliceName} has been merged with ${args.libraryID} > ${args.sliceID}`,
157
+ ToasterType.SUCCESS
158
+ );
159
+ break;
160
+ }
161
+
162
+ const customTypeSM = CustomTypes.toSM(customType);
163
+ initCustomTypeStore(customTypeSM, customTypeSM);
164
+ saveCustomTypeSuccess(customType);
165
+ };
166
+
167
+ const formProps = {
168
+ path,
169
+ slice,
170
+ sliceName,
171
+ libraries,
172
+ localSharedSlices,
173
+ identicalSlices,
174
+ close: () => setDialog(undefined),
175
+ onSubmit: convertLegacySliceAndTrack,
176
+ isLoading,
177
+ };
178
+
179
+ return (
180
+ <>
181
+ <DropdownMenu>
182
+ <DropdownMenuTrigger>
183
+ <Button
184
+ data-cy="convert-legacy-slice"
185
+ startIcon="refresh"
186
+ endIcon="arrowDropDown"
187
+ size="medium"
188
+ variant="secondary"
189
+ >
190
+ Migrate legacy slice
191
+ </Button>
192
+ </DropdownMenuTrigger>
193
+ <DropdownMenuContent align="start">
194
+ <DropdownMenuItem
195
+ startIcon={<Icon name="folder" size="large" />}
196
+ description="Use it with new types"
197
+ onSelect={() => setDialog("as_new_slice")}
198
+ >
199
+ Upgrade slice
200
+ </DropdownMenuItem>
201
+ <DropdownMenuItem
202
+ startIcon={<Icon name="viewDay" size="large" />}
203
+ description="Add it to another slice"
204
+ onSelect={() => setDialog("as_new_variation")}
205
+ disabled={!localSharedSlices.length}
206
+ >
207
+ Convert to slice variation
208
+ </DropdownMenuItem>
209
+ <DropdownMenuItem
210
+ startIcon={<Icon name="driveFileMove" size="large" />}
211
+ description="Combine identical slices"
212
+ onSelect={() => setDialog("merge_with_identical")}
213
+ disabled={!identicalSlices.length}
214
+ >
215
+ Merge with another slice
216
+ </DropdownMenuItem>
217
+ </DropdownMenuContent>
218
+ </DropdownMenu>
219
+ <ConvertLegacySliceAsNewSliceDialog
220
+ {...formProps}
221
+ isOpen={dialog === "as_new_slice"}
222
+ />
223
+ <ConvertLegacySliceAsNewVariationDialog
224
+ {...formProps}
225
+ isOpen={dialog === "as_new_variation"}
226
+ />
227
+ <ConvertLegacySliceMergeWithIdenticalDialog
228
+ {...formProps}
229
+ isOpen={dialog === "merge_with_identical"}
230
+ />
231
+ </>
232
+ );
233
+ };
234
+
235
+ const useIdenticalSlices = (
236
+ slice: NonSharedSliceInSliceZone,
237
+ sliceName: string,
238
+ localSharedSlices: ComponentUI[]
239
+ ) => {
240
+ return useMemo<IdenticalSlice[]>(() => {
241
+ const results: IdenticalSlice[] = [];
242
+
243
+ const sliceFields = getFieldMappingFingerprint(slice.value, sliceName);
244
+
245
+ for (const sharedSlice of localSharedSlices) {
246
+ for (const variation of sharedSlice.model.variations) {
247
+ const variationFields = getFieldMappingFingerprint(
248
+ variation,
249
+ sharedSlice.model.name
250
+ );
251
+
252
+ if (
253
+ sliceFields.primary === variationFields.primary &&
254
+ sliceFields.items === variationFields.items
255
+ ) {
256
+ results.push({
257
+ libraryID: sharedSlice.from,
258
+ sliceID: sharedSlice.model.id,
259
+ variationID: variation.id,
260
+ path: `${sharedSlice.from}::${sharedSlice.model.id}::${variation.id}`,
261
+ });
262
+ }
263
+ }
264
+ }
265
+
266
+ return results;
267
+ }, [slice, sliceName, localSharedSlices]);
268
+ };