slice-machine-ui 2.20.5-alpha.jp-import-slice-base.2 → 2.20.5-alpha.jp-import-slices-2.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.
- package/out/404.html +1 -1
- package/out/_next/static/60BSwFmsh1-aNIyMe29hH/_buildManifest.js +1 -0
- package/out/_next/static/chunks/21-f3962d02eda0a46e.js +1 -0
- package/out/_next/static/chunks/{248-84a5987f0499b074.js → 248-2902bda47821f37b.js} +1 -1
- package/out/_next/static/chunks/{630-2bf927bca082a191.js → 630-4fe1e982234bd925.js} +1 -1
- package/out/_next/static/chunks/633-2d20c3e815c79c7f.js +1 -0
- package/out/_next/static/chunks/{647-aa094286bc248d52.js → 647-a29bee23cb9ac7a2.js} +1 -1
- package/out/_next/static/chunks/660-a3ec593dcfbb44c9.js +1 -0
- package/out/_next/static/chunks/6611d287.1714222015f895ea.js +28 -0
- package/out/_next/static/chunks/{882-48d61b2fabee28d8.js → 882-8ab59c0c72307c87.js} +1 -1
- package/out/_next/static/chunks/pages/{_app-651bb3e76e35a633.js → _app-a60aecdbaa4efedd.js} +217 -217
- package/out/_next/static/chunks/pages/{changelog-8514e0696e90a1b2.js → changelog-bc83e25e8d316ca9.js} +1 -1
- package/out/_next/static/chunks/pages/{changes-e66094f57453cf9c.js → changes-6f371ebe52dbe76a.js} +1 -1
- package/out/_next/static/chunks/pages/custom-types/{[customTypeId]-273e9a82c085b596.js → [customTypeId]-80defa14b6ea36f7.js} +1 -1
- package/out/_next/static/chunks/pages/{labs-56fd818a63553497.js → labs-16d118a80936d583.js} +1 -1
- package/out/_next/static/chunks/pages/page-types/{[pageTypeId]-3fa7667de1a790d9.js → [pageTypeId]-f4ff9ad74b44bf09.js} +1 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/{simulator-8c70298caf51bed0.js → simulator-873c0c89aaf50b3c.js} +1 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/{[variation]-22ffa2561ac66557.js → [variation]-0e9aa745ed9fe1ec.js} +1 -1
- package/out/_next/static/chunks/pages/slices-93fc784c443a996c.js +1 -0
- package/out/_next/static/chunks/{webpack-24ddb02bea621501.js → webpack-e986dd216b883a74.js} +1 -1
- package/out/_next/static/css/b30fbbc3ed00a725.css +1 -0
- package/out/changelog.html +1 -1
- package/out/changes.html +1 -1
- package/out/custom-types/[customTypeId].html +1 -1
- package/out/custom-types.html +1 -1
- package/out/index.html +1 -1
- package/out/labs.html +1 -1
- package/out/page-types/[pageTypeId].html +1 -1
- package/out/slices/[lib]/[sliceName]/[variation]/simulator.html +1 -1
- package/out/slices/[lib]/[sliceName]/[variation].html +1 -1
- package/out/slices.html +1 -1
- package/package.json +6 -6
- package/src/features/customTypes/customTypesBuilder/ImportSlicesFromLibraryModal/ImportSlicesFromLibraryModal.tsx +104 -285
- package/src/features/customTypes/customTypesBuilder/ImportSlicesFromLibraryModal/LibrarySlicesTab.tsx +125 -0
- package/src/features/customTypes/customTypesBuilder/ImportSlicesFromLibraryModal/LocalSlicesTab.tsx +67 -0
- package/src/features/customTypes/customTypesBuilder/ImportSlicesFromLibraryModal/ReuseExistingSlicesContext.tsx +279 -0
- package/src/features/customTypes/customTypesBuilder/ImportSlicesFromLibraryModal/hooks/useImportSlicesFromGithub.ts +7 -4
- package/src/features/customTypes/customTypesBuilder/SliceZoneBlankSlate.tsx +4 -18
- package/src/features/customTypes/customTypesBuilder/shared/getSubmitButtonLabel.ts +3 -5
- package/src/features/customTypes/customTypesBuilder/sliceCreationOptions.tsx +0 -14
- package/src/legacy/lib/builders/CustomTypeBuilder/SliceZone/UpdateSliceZoneModalList.tsx +1 -3
- package/src/legacy/lib/builders/CustomTypeBuilder/SliceZone/index.tsx +31 -79
- package/src/pages/slices.tsx +0 -30
- package/out/_next/static/CGK-vfrV6JByBnDLoS7iE/_buildManifest.js +0 -1
- package/out/_next/static/chunks/422-c7a16d95b75c9e1c.js +0 -1
- package/out/_next/static/chunks/489-c9535ef34da63d1a.js +0 -1
- package/out/_next/static/chunks/585-c89bb2471e85b9f8.js +0 -1
- package/out/_next/static/chunks/954-bedaaabf664584a0.js +0 -1
- package/out/_next/static/chunks/a6495ab1.00190ac98e794d8f.js +0 -28
- package/out/_next/static/chunks/pages/slices-76679cf064761d2b.js +0 -1
- package/out/_next/static/css/bc89f2cd4e4781f5.css +0 -1
- package/src/legacy/lib/builders/CustomTypeBuilder/SliceZone/UpdateSliceZoneModal.tsx +0 -72
- /package/out/_next/static/{CGK-vfrV6JByBnDLoS7iE → 60BSwFmsh1-aNIyMe29hH}/_ssgManifest.js +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slice-machine-ui",
|
|
3
|
-
"version": "2.20.5-alpha.jp-import-
|
|
3
|
+
"version": "2.20.5-alpha.jp-import-slices-2.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "A visual builder for your Slice Models with all the tools you need to generate data models and mock CMS content locally.",
|
|
6
6
|
"repository": {
|
|
@@ -35,16 +35,16 @@
|
|
|
35
35
|
"start-slicemachine": "./bin/start-slicemachine.cjs"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@slicemachine/manager": "0.26.5-alpha.jp-import-
|
|
39
|
-
"start-slicemachine": "0.12.70-alpha.jp-import-
|
|
38
|
+
"@slicemachine/manager": "0.26.5-alpha.jp-import-slices-2.1",
|
|
39
|
+
"start-slicemachine": "0.12.70-alpha.jp-import-slices-2.1"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@emotion/react": "11.11.1",
|
|
43
43
|
"@extractus/oembed-extractor": "3.1.8",
|
|
44
44
|
"@prismicio/client": "7.17.0",
|
|
45
|
-
"@prismicio/editor-fields": "0.4.
|
|
46
|
-
"@prismicio/editor-support": "0.4.
|
|
47
|
-
"@prismicio/editor-ui": "0.4.
|
|
45
|
+
"@prismicio/editor-fields": "0.4.89-alpha.jp-box-display-none.1",
|
|
46
|
+
"@prismicio/editor-support": "0.4.89-alpha.jp-box-display-none.1",
|
|
47
|
+
"@prismicio/editor-ui": "0.4.89-alpha.jp-box-display-none.1",
|
|
48
48
|
"@prismicio/mock": "0.7.1",
|
|
49
49
|
"@prismicio/mocks": "2.14.0",
|
|
50
50
|
"@prismicio/simulator": "0.1.4",
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Box,
|
|
3
|
-
Button,
|
|
4
|
-
Checkbox,
|
|
5
3
|
Dialog,
|
|
6
4
|
DialogActionButton,
|
|
7
5
|
DialogActions,
|
|
@@ -9,328 +7,149 @@ import {
|
|
|
9
7
|
DialogContent,
|
|
10
8
|
DialogDescription,
|
|
11
9
|
DialogHeader,
|
|
12
|
-
|
|
13
|
-
ScrollArea,
|
|
14
|
-
Text,
|
|
15
|
-
TextInput,
|
|
10
|
+
Tab,
|
|
16
11
|
} from "@prismicio/editor-ui";
|
|
17
12
|
import { SharedSlice } from "@prismicio/types-internal/lib/customtypes";
|
|
18
|
-
import {
|
|
19
|
-
import { toast } from "react-toastify";
|
|
13
|
+
import { ReactNode, useState } from "react";
|
|
20
14
|
|
|
21
|
-
import {
|
|
22
|
-
import { useOnboarding } from "@/features/onboarding/useOnboarding";
|
|
23
|
-
import { useAutoSync } from "@/features/sync/AutoSyncProvider";
|
|
24
|
-
import useSliceMachineActions from "@/modules/useSliceMachineActions";
|
|
15
|
+
import { ComponentUI } from "@/legacy/lib/models/common/ComponentUI";
|
|
25
16
|
|
|
26
17
|
import { getSubmitButtonLabel } from "../shared/getSubmitButtonLabel";
|
|
27
18
|
import { useExistingSlices } from "../shared/useExistingSlices";
|
|
28
19
|
import { useImportSlicesFromGithub } from "./hooks/useImportSlicesFromGithub";
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
|
|
20
|
+
import { LibrarySlicesTab } from "./LibrarySlicesTab";
|
|
21
|
+
import { LocalSlicesTab } from "./LocalSlicesTab";
|
|
22
|
+
import {
|
|
23
|
+
ReuseExistingSlicesProvider,
|
|
24
|
+
useReuseExistingSlicesContext,
|
|
25
|
+
} from "./ReuseExistingSlicesContext";
|
|
33
26
|
|
|
34
|
-
interface
|
|
27
|
+
interface ImportSlicesFromLibraryModalContent {
|
|
35
28
|
open: boolean;
|
|
36
|
-
location: "custom_type" | "page_type"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
langSmithUrl?: string;
|
|
41
|
-
}[];
|
|
42
|
-
library: string;
|
|
43
|
-
}) => void;
|
|
29
|
+
location: "custom_type" | "page_type";
|
|
30
|
+
typeName: string;
|
|
31
|
+
availableSlices?: ReadonlyArray<ComponentUI>;
|
|
32
|
+
onSuccess: (args: { slices: SharedSlice[]; library?: string }) => void;
|
|
44
33
|
onClose: () => void;
|
|
45
34
|
}
|
|
46
35
|
|
|
47
|
-
|
|
48
|
-
props:
|
|
36
|
+
function ImportSlicesFromLibraryModalContent(
|
|
37
|
+
props: ImportSlicesFromLibraryModalContent,
|
|
49
38
|
) {
|
|
50
|
-
const {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
39
|
+
const {
|
|
40
|
+
open,
|
|
41
|
+
location,
|
|
42
|
+
typeName,
|
|
43
|
+
availableSlices = [],
|
|
44
|
+
onSuccess,
|
|
45
|
+
onClose,
|
|
46
|
+
} = props;
|
|
47
|
+
const [selectedTab, setSelectedTab] = useState("local");
|
|
57
48
|
|
|
58
|
-
const { syncChanges } = useAutoSync();
|
|
59
|
-
const { createSliceSuccess, updateSliceMockSuccess } =
|
|
60
|
-
useSliceMachineActions();
|
|
61
|
-
const { completeStep } = useOnboarding();
|
|
62
49
|
const existingSlices = useExistingSlices({ open });
|
|
63
|
-
const {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
if (slices.length === 0) return;
|
|
68
|
-
|
|
69
|
-
// Set all slices as selected by default
|
|
70
|
-
const allSliceIds = new Set<string>();
|
|
71
|
-
for (const slice of slices) {
|
|
72
|
-
allSliceIds.add(slice.model.id);
|
|
73
|
-
}
|
|
74
|
-
setSelectedSliceIds(allSliceIds);
|
|
75
|
-
}, [slices]);
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Keeps track of the current instance id.
|
|
79
|
-
* When the modal is closed, the id is reset.
|
|
80
|
-
*/
|
|
81
|
-
const id = useRef(crypto.randomUUID());
|
|
50
|
+
const { resetSlices } = useImportSlicesFromGithub();
|
|
51
|
+
const { submit, isSubmitting, totalSelected, reset } =
|
|
52
|
+
useReuseExistingSlicesContext();
|
|
82
53
|
|
|
83
54
|
const onOpenChange = (open: boolean) => {
|
|
84
|
-
if (open ||
|
|
55
|
+
if (open || isSubmitting) return;
|
|
85
56
|
onClose();
|
|
86
|
-
|
|
87
|
-
setGithubUrl("");
|
|
88
|
-
setSelectedSliceIds(new Set());
|
|
57
|
+
reset();
|
|
89
58
|
resetSlices();
|
|
59
|
+
setSelectedTab("local");
|
|
90
60
|
};
|
|
91
61
|
|
|
92
|
-
const onSubmit = () => {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
files: slice.files,
|
|
99
|
-
componentContents: slice.componentContents,
|
|
100
|
-
mocks: slice.mocks,
|
|
101
|
-
screenshots: slice.screenshots,
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
return acc;
|
|
105
|
-
}, []);
|
|
106
|
-
if (!newSlices.length) {
|
|
107
|
-
toast.error("Please select at least one slice to import");
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Ensure ids and names are conflict-free against existing and newly-added slices
|
|
112
|
-
const conflictFreeSlices: NewSlice[] = [];
|
|
113
|
-
for (const s of newSlices) {
|
|
114
|
-
const adjustedModel = sliceWithoutConflicts({
|
|
115
|
-
existingSlices: existingSlices.current,
|
|
116
|
-
newSlices: conflictFreeSlices,
|
|
117
|
-
slice: s.model,
|
|
118
|
-
});
|
|
119
|
-
conflictFreeSlices.push({ ...s, model: adjustedModel });
|
|
62
|
+
const onSubmit = async () => {
|
|
63
|
+
try {
|
|
64
|
+
const result = await submit({ existingSlices, location, resetSlices });
|
|
65
|
+
onSuccess(result);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
// Error is already handled in the submit function
|
|
120
68
|
}
|
|
121
|
-
|
|
122
|
-
const currentId = id.current;
|
|
123
|
-
setIsCreatingSlices(true);
|
|
124
|
-
addSlices(conflictFreeSlices)
|
|
125
|
-
.then(async ({ slices, library }) => {
|
|
126
|
-
if (currentId !== id.current) return;
|
|
127
|
-
|
|
128
|
-
// Wait a moment to ensure all file writes are complete
|
|
129
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
130
|
-
|
|
131
|
-
const serverState = await getState();
|
|
132
|
-
|
|
133
|
-
// Ensure mocks are included in the libraries data before updating store
|
|
134
|
-
const librariesWithMocks = serverState.libraries.map((lib) => {
|
|
135
|
-
if (lib.name !== library) return lib;
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
...lib,
|
|
139
|
-
components: lib.components.map((component) => {
|
|
140
|
-
// Find the corresponding slice from newSlices to get its mocks
|
|
141
|
-
const importedSlice = conflictFreeSlices.find(
|
|
142
|
-
(s) => s.model.id === component.model.id,
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
// If mocks are already in component, use them; otherwise use imported mocks
|
|
146
|
-
const mocks =
|
|
147
|
-
component.mocks && component.mocks.length > 0
|
|
148
|
-
? component.mocks
|
|
149
|
-
: importedSlice?.mocks;
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
...component,
|
|
153
|
-
mocks: mocks ?? component.mocks,
|
|
154
|
-
};
|
|
155
|
-
}),
|
|
156
|
-
};
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
createSliceSuccess(librariesWithMocks);
|
|
160
|
-
|
|
161
|
-
// Also update mocks individually to ensure they're in the store
|
|
162
|
-
for (const slice of newSlices) {
|
|
163
|
-
if (
|
|
164
|
-
slice.mocks &&
|
|
165
|
-
Array.isArray(slice.mocks) &&
|
|
166
|
-
slice.mocks.length > 0
|
|
167
|
-
) {
|
|
168
|
-
updateSliceMockSuccess({
|
|
169
|
-
libraryID: library,
|
|
170
|
-
sliceID: slice.model.id,
|
|
171
|
-
mocks: slice.mocks,
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
syncChanges();
|
|
177
|
-
|
|
178
|
-
onSuccess({ slices, library });
|
|
179
|
-
|
|
180
|
-
setIsCreatingSlices(false);
|
|
181
|
-
id.current = crypto.randomUUID();
|
|
182
|
-
resetSlices();
|
|
183
|
-
|
|
184
|
-
void completeStep("createSlice");
|
|
185
|
-
|
|
186
|
-
for (const { model } of slices) {
|
|
187
|
-
void telemetry.track({
|
|
188
|
-
event: "slice:created",
|
|
189
|
-
id: model.id,
|
|
190
|
-
name: model.name,
|
|
191
|
-
library,
|
|
192
|
-
location,
|
|
193
|
-
mode: "import",
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
})
|
|
197
|
-
.catch(() => {
|
|
198
|
-
if (currentId !== id.current) return;
|
|
199
|
-
setIsCreatingSlices(false);
|
|
200
|
-
toast.error("An unexpected error happened while adding slices.");
|
|
201
|
-
});
|
|
202
69
|
};
|
|
203
70
|
|
|
204
|
-
const selectedSlices = slices.filter((slice) =>
|
|
205
|
-
selectedSliceIds.has(slice.model.id),
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
const allSelected =
|
|
209
|
-
slices.length > 0 && selectedSliceIds.size === slices.length;
|
|
210
|
-
const someSelected =
|
|
211
|
-
selectedSliceIds.size > 0 && selectedSliceIds.size < slices.length;
|
|
212
|
-
|
|
213
|
-
const handleSelectAll = (selected: boolean) => {
|
|
214
|
-
if (selected) {
|
|
215
|
-
const allSliceIds = new Set<string>();
|
|
216
|
-
for (const slice of slices) {
|
|
217
|
-
allSliceIds.add(slice.model.id);
|
|
218
|
-
}
|
|
219
|
-
setSelectedSliceIds(allSliceIds);
|
|
220
|
-
} else {
|
|
221
|
-
setSelectedSliceIds(new Set());
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
let selectAllLabel = "Select all slices";
|
|
226
|
-
if (allSelected) {
|
|
227
|
-
selectAllLabel = `Selected all slices (${selectedSliceIds.size})`;
|
|
228
|
-
} else if (someSelected) {
|
|
229
|
-
selectAllLabel = `${selectedSliceIds.size} of ${slices.length} selected`;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
71
|
return (
|
|
233
72
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
234
|
-
<DialogHeader title="
|
|
73
|
+
<DialogHeader title="Reuse an existing slice" />
|
|
235
74
|
<DialogContent gap={0}>
|
|
236
75
|
<DialogDescription hidden>
|
|
237
|
-
|
|
76
|
+
Select existing slices or import slices from a GitHub repository
|
|
238
77
|
</DialogDescription>
|
|
239
|
-
{
|
|
240
|
-
<Box
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
78
|
+
<Box flexDirection="column" flexGrow={1} minHeight={0}>
|
|
79
|
+
<Box
|
|
80
|
+
justifyContent="space-between"
|
|
81
|
+
padding={16}
|
|
82
|
+
border={{ bottom: true }}
|
|
83
|
+
>
|
|
84
|
+
<Box gap={8}>
|
|
85
|
+
<Tab
|
|
86
|
+
selected={selectedTab === "local"}
|
|
87
|
+
onClick={() => setSelectedTab("local")}
|
|
249
88
|
>
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
disabled={!githubUrl.trim() || isLoadingSlices}
|
|
259
|
-
loading={isLoadingSlices}
|
|
260
|
-
color="purple"
|
|
261
|
-
>
|
|
262
|
-
{isLoadingSlices ? "Loading slices..." : "Import from GitHub"}
|
|
263
|
-
</Button>
|
|
264
|
-
</Box>
|
|
89
|
+
Local Slices
|
|
90
|
+
</Tab>
|
|
91
|
+
<Tab
|
|
92
|
+
selected={selectedTab === "library"}
|
|
93
|
+
onClick={() => setSelectedTab("library")}
|
|
94
|
+
>
|
|
95
|
+
Library Slices
|
|
96
|
+
</Tab>
|
|
265
97
|
</Box>
|
|
98
|
+
{selectedTab === "library" && "<repo-select>"}
|
|
266
99
|
</Box>
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
border={{ bottom: true }}
|
|
275
|
-
>
|
|
276
|
-
<Box display="flex" alignItems="center" gap={8}>
|
|
277
|
-
<InlineLabel value={selectAllLabel}>
|
|
278
|
-
<Checkbox
|
|
279
|
-
checked={allSelected}
|
|
280
|
-
indeterminate={someSelected}
|
|
281
|
-
onCheckedChange={handleSelectAll}
|
|
282
|
-
/>
|
|
283
|
-
</InlineLabel>
|
|
284
|
-
</Box>
|
|
285
|
-
</Box>
|
|
286
|
-
<ScrollArea stableScrollbar={false}>
|
|
287
|
-
<Box
|
|
288
|
-
display="grid"
|
|
289
|
-
gridTemplateColumns="1fr 1fr 1fr"
|
|
290
|
-
gap={16}
|
|
291
|
-
padding={16}
|
|
292
|
-
>
|
|
293
|
-
{slices.map((slice) => {
|
|
294
|
-
return (
|
|
295
|
-
<SliceCard
|
|
296
|
-
model={slice.model}
|
|
297
|
-
thumbnailUrl={slice.thumbnailUrl}
|
|
298
|
-
key={slice.model.id}
|
|
299
|
-
selected={selectedSliceIds.has(slice.model.id)}
|
|
300
|
-
onSelectedChange={(selected) => {
|
|
301
|
-
if (selected) {
|
|
302
|
-
setSelectedSliceIds((prev) => {
|
|
303
|
-
const next = new Set(prev);
|
|
304
|
-
next.add(slice.model.id);
|
|
305
|
-
return next;
|
|
306
|
-
});
|
|
307
|
-
} else {
|
|
308
|
-
setSelectedSliceIds((prev) => {
|
|
309
|
-
const next = new Set(prev);
|
|
310
|
-
next.delete(slice.model.id);
|
|
311
|
-
return next;
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
}}
|
|
315
|
-
/>
|
|
316
|
-
);
|
|
317
|
-
})}
|
|
318
|
-
</Box>
|
|
319
|
-
</ScrollArea>
|
|
320
|
-
</>
|
|
321
|
-
)}
|
|
100
|
+
<TabContent selected={selectedTab === "local"}>
|
|
101
|
+
<LocalSlicesTab availableSlices={availableSlices} />
|
|
102
|
+
</TabContent>
|
|
103
|
+
<TabContent selected={selectedTab === "library"}>
|
|
104
|
+
<LibrarySlicesTab />
|
|
105
|
+
</TabContent>
|
|
106
|
+
</Box>
|
|
322
107
|
|
|
323
108
|
<DialogActions>
|
|
324
|
-
<DialogCancelButton disabled={
|
|
109
|
+
<DialogCancelButton disabled={isSubmitting} size="medium" />
|
|
325
110
|
<DialogActionButton
|
|
326
|
-
disabled={
|
|
327
|
-
loading={
|
|
328
|
-
onClick={onSubmit}
|
|
111
|
+
disabled={totalSelected === 0}
|
|
112
|
+
loading={isSubmitting}
|
|
113
|
+
onClick={() => void onSubmit()}
|
|
114
|
+
size="medium"
|
|
329
115
|
>
|
|
330
|
-
{getSubmitButtonLabel(location)} ({
|
|
116
|
+
{getSubmitButtonLabel(location, typeName)} ({totalSelected})
|
|
331
117
|
</DialogActionButton>
|
|
332
118
|
</DialogActions>
|
|
333
119
|
</DialogContent>
|
|
334
120
|
</Dialog>
|
|
335
121
|
);
|
|
336
122
|
}
|
|
123
|
+
|
|
124
|
+
interface TabContentProps {
|
|
125
|
+
children: ReactNode;
|
|
126
|
+
selected: boolean;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function TabContent(args: TabContentProps) {
|
|
130
|
+
const { children, selected } = args;
|
|
131
|
+
|
|
132
|
+
if (!selected) {
|
|
133
|
+
return (
|
|
134
|
+
<Box display="none" minHeight={0}>
|
|
135
|
+
{children}
|
|
136
|
+
</Box>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<Box display="flex" flexDirection="column" flexGrow={1} minHeight={0}>
|
|
142
|
+
{children}
|
|
143
|
+
</Box>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function ImportSlicesFromLibraryModal(
|
|
148
|
+
props: ImportSlicesFromLibraryModalContent,
|
|
149
|
+
) {
|
|
150
|
+
return (
|
|
151
|
+
<ReuseExistingSlicesProvider>
|
|
152
|
+
<ImportSlicesFromLibraryModalContent {...props} />
|
|
153
|
+
</ReuseExistingSlicesProvider>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Box,
|
|
3
|
+
Button,
|
|
4
|
+
Checkbox,
|
|
5
|
+
InlineLabel,
|
|
6
|
+
ScrollArea,
|
|
7
|
+
Text,
|
|
8
|
+
TextInput,
|
|
9
|
+
} from "@prismicio/editor-ui";
|
|
10
|
+
import { useMemo, useState } from "react";
|
|
11
|
+
|
|
12
|
+
import { useImportSlicesFromGithub } from "./hooks/useImportSlicesFromGithub";
|
|
13
|
+
import { useReuseExistingSlicesContext } from "./ReuseExistingSlicesContext";
|
|
14
|
+
import { SliceCard } from "./SliceCard";
|
|
15
|
+
|
|
16
|
+
export function LibrarySlicesTab() {
|
|
17
|
+
const [githubUrl, setGithubUrl] = useState("");
|
|
18
|
+
const { isLoadingSlices, handleImportFromGithub, slices } =
|
|
19
|
+
useImportSlicesFromGithub();
|
|
20
|
+
const { selectedLibrarySlices, toggleLibrarySlice, setAllLibrarySlices } =
|
|
21
|
+
useReuseExistingSlicesContext();
|
|
22
|
+
|
|
23
|
+
const { allSelected, someSelected } = useMemo(() => {
|
|
24
|
+
if (slices.length === 0) return { allSelected: false, someSelected: false };
|
|
25
|
+
return {
|
|
26
|
+
allSelected: slices.every((slice) =>
|
|
27
|
+
selectedLibrarySlices.some((s) => s.model.id === slice.model.id),
|
|
28
|
+
),
|
|
29
|
+
someSelected: slices.some((slice) =>
|
|
30
|
+
selectedLibrarySlices.some((s) => s.model.id === slice.model.id),
|
|
31
|
+
),
|
|
32
|
+
};
|
|
33
|
+
}, [slices, selectedLibrarySlices]);
|
|
34
|
+
|
|
35
|
+
const onSelectAll = (checked: boolean) => {
|
|
36
|
+
if (checked) {
|
|
37
|
+
setAllLibrarySlices(slices);
|
|
38
|
+
} else {
|
|
39
|
+
setAllLibrarySlices([]);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const onImport = async () => {
|
|
44
|
+
const fetchedSlices = await handleImportFromGithub(githubUrl);
|
|
45
|
+
setAllLibrarySlices(fetchedSlices);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
if (slices.length === 0) {
|
|
49
|
+
return (
|
|
50
|
+
<Box padding={16} height="100%" flexDirection="column" gap={16}>
|
|
51
|
+
<Box flexDirection="column" gap={8}>
|
|
52
|
+
<Box
|
|
53
|
+
display="flex"
|
|
54
|
+
flexDirection="column"
|
|
55
|
+
gap={8}
|
|
56
|
+
padding={16}
|
|
57
|
+
border
|
|
58
|
+
borderRadius={8}
|
|
59
|
+
>
|
|
60
|
+
<Text color="grey11">Import from GitHub</Text>
|
|
61
|
+
<TextInput
|
|
62
|
+
placeholder="https://github.com/username/repository"
|
|
63
|
+
value={githubUrl}
|
|
64
|
+
onValueChange={setGithubUrl}
|
|
65
|
+
/>
|
|
66
|
+
<Button
|
|
67
|
+
onClick={() => void onImport()}
|
|
68
|
+
disabled={!githubUrl.trim() || isLoadingSlices}
|
|
69
|
+
loading={isLoadingSlices}
|
|
70
|
+
color="purple"
|
|
71
|
+
>
|
|
72
|
+
{isLoadingSlices ? "Loading slices..." : "Import from GitHub"}
|
|
73
|
+
</Button>
|
|
74
|
+
</Box>
|
|
75
|
+
</Box>
|
|
76
|
+
</Box>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let selectAllLabel = "Select all slices";
|
|
81
|
+
if (allSelected) {
|
|
82
|
+
selectAllLabel = `Selected all slices (${selectedLibrarySlices.length})`;
|
|
83
|
+
} else if (someSelected) {
|
|
84
|
+
selectAllLabel = `${selectedLibrarySlices.length} of ${slices.length} selected`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<Box flexDirection="column" flexGrow={1} minHeight={0}>
|
|
89
|
+
<Box padding={{ block: 12, inline: 16 }} alignItems="center" gap={8}>
|
|
90
|
+
<InlineLabel value={selectAllLabel}>
|
|
91
|
+
<Checkbox
|
|
92
|
+
checked={allSelected}
|
|
93
|
+
indeterminate={someSelected && !allSelected}
|
|
94
|
+
onCheckedChange={onSelectAll}
|
|
95
|
+
/>
|
|
96
|
+
</InlineLabel>
|
|
97
|
+
</Box>
|
|
98
|
+
<ScrollArea stableScrollbar={false}>
|
|
99
|
+
<Box
|
|
100
|
+
display="grid"
|
|
101
|
+
gridTemplateColumns="1fr 1fr 1fr"
|
|
102
|
+
gap={16}
|
|
103
|
+
padding={{ inline: 16, bottom: 16 }}
|
|
104
|
+
>
|
|
105
|
+
{slices.map((slice) => {
|
|
106
|
+
const isSelected = selectedLibrarySlices.some(
|
|
107
|
+
(s) => s.model.id === slice.model.id,
|
|
108
|
+
);
|
|
109
|
+
return (
|
|
110
|
+
<SliceCard
|
|
111
|
+
model={slice.model}
|
|
112
|
+
thumbnailUrl={slice.thumbnailUrl}
|
|
113
|
+
key={slice.model.id}
|
|
114
|
+
selected={isSelected}
|
|
115
|
+
onSelectedChange={() => {
|
|
116
|
+
toggleLibrarySlice(slice);
|
|
117
|
+
}}
|
|
118
|
+
/>
|
|
119
|
+
);
|
|
120
|
+
})}
|
|
121
|
+
</Box>
|
|
122
|
+
</ScrollArea>
|
|
123
|
+
</Box>
|
|
124
|
+
);
|
|
125
|
+
}
|
package/src/features/customTypes/customTypesBuilder/ImportSlicesFromLibraryModal/LocalSlicesTab.tsx
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Box, ScrollArea } from "@prismicio/editor-ui";
|
|
2
|
+
|
|
3
|
+
import { SharedSliceCard } from "@/features/slices/sliceCards/SharedSliceCard";
|
|
4
|
+
import { ComponentUI } from "@/legacy/lib/models/common/ComponentUI";
|
|
5
|
+
|
|
6
|
+
import { useReuseExistingSlicesContext } from "./ReuseExistingSlicesContext";
|
|
7
|
+
|
|
8
|
+
interface LocalSlicesTabProps {
|
|
9
|
+
availableSlices: ReadonlyArray<ComponentUI>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function LocalSlicesTab(props: LocalSlicesTabProps) {
|
|
13
|
+
const { availableSlices } = props;
|
|
14
|
+
const { selectedLocalSlices, toggleLocalSlice } =
|
|
15
|
+
useReuseExistingSlicesContext();
|
|
16
|
+
|
|
17
|
+
if (availableSlices.length === 0) {
|
|
18
|
+
return (
|
|
19
|
+
<Box padding={16} height="100%" flexDirection="column" gap={16}>
|
|
20
|
+
<Box flexDirection="column" gap={8}>
|
|
21
|
+
<Box
|
|
22
|
+
display="flex"
|
|
23
|
+
flexDirection="column"
|
|
24
|
+
gap={8}
|
|
25
|
+
padding={16}
|
|
26
|
+
alignItems="center"
|
|
27
|
+
justifyContent="center"
|
|
28
|
+
>
|
|
29
|
+
No local slices available
|
|
30
|
+
</Box>
|
|
31
|
+
</Box>
|
|
32
|
+
</Box>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<ScrollArea stableScrollbar={false}>
|
|
38
|
+
<Box
|
|
39
|
+
display="grid"
|
|
40
|
+
gridTemplateColumns="1fr 1fr 1fr"
|
|
41
|
+
gap={16}
|
|
42
|
+
flexGrow={1}
|
|
43
|
+
padding={16}
|
|
44
|
+
minHeight={0}
|
|
45
|
+
>
|
|
46
|
+
{availableSlices.map((slice) => {
|
|
47
|
+
const isSelected = selectedLocalSlices.some(
|
|
48
|
+
(s) => s.model.id === slice.model.id,
|
|
49
|
+
);
|
|
50
|
+
return (
|
|
51
|
+
<SharedSliceCard
|
|
52
|
+
key={`${slice.from}-${slice.model.name}`}
|
|
53
|
+
action={{ type: "checkbox" }}
|
|
54
|
+
mode="selection"
|
|
55
|
+
onSelectedChange={() => {
|
|
56
|
+
toggleLocalSlice(slice);
|
|
57
|
+
}}
|
|
58
|
+
selected={isSelected}
|
|
59
|
+
slice={slice}
|
|
60
|
+
variant="outlined"
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
})}
|
|
64
|
+
</Box>
|
|
65
|
+
</ScrollArea>
|
|
66
|
+
);
|
|
67
|
+
}
|