@commercetools-demo/puck-page-manager 0.5.1 → 0.6.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.
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/PageManager.tsx
2
- import { useState } from "react";
2
+ import { useCallback, useState } from "react";
3
3
  import {
4
4
  BrowserRouter,
5
5
  Switch,
@@ -11,46 +11,87 @@ import {
11
11
  import {
12
12
  PuckApiProvider,
13
13
  usePuckPages,
14
+ usePuckTemplates,
14
15
  usePuckApiContext
15
16
  } from "@commercetools-demo/puck-api";
16
- import { PuckEditor, defaultPuckConfig } from "@commercetools-demo/puck-editor";
17
+ import {
18
+ PropertiesResizer,
19
+ PuckEditor,
20
+ UnsavedChangesDialog,
21
+ defaultPuckConfig
22
+ } from "@commercetools-demo/puck-editor";
17
23
  import { PuckRenderer } from "@commercetools-demo/puck-renderer";
18
- import DataTable from "@commercetools-uikit/data-table";
19
- import PrimaryButton from "@commercetools-uikit/primary-button";
20
- import SecondaryButton from "@commercetools-uikit/secondary-button";
21
- import FlatButton from "@commercetools-uikit/flat-button";
22
- import Card from "@commercetools-uikit/card";
23
- import Spacings from "@commercetools-uikit/spacings";
24
- import Text from "@commercetools-uikit/text";
25
- import LoadingSpinner from "@commercetools-uikit/loading-spinner";
26
- import TextInput from "@commercetools-uikit/text-input";
27
- import Label from "@commercetools-uikit/label";
28
- import { PlusThinIcon, AngleLeftIcon } from "@commercetools-uikit/icons";
29
- import { jsx, jsxs } from "react/jsx-runtime";
30
- var DEFAULT_CONFIG = {
31
- ...defaultPuckConfig,
32
- components: { ...defaultPuckConfig.components }
33
- };
34
- var StatusBadge = ({ variant }) => {
35
- const styles = variant === "published" ? { background: "var(--color-success-95)", color: "var(--color-success-40)", border: "1px solid var(--color-success-85)" } : variant === "draft" ? { background: "var(--color-warning-95)", color: "var(--color-warning-40)", border: "1px solid var(--color-warning-85)" } : { background: "var(--color-neutral-95)", color: "var(--color-neutral-50)", border: "1px solid var(--color-neutral-85)" };
24
+
25
+ // src/EnsureIntlProvider.tsx
26
+ import { useContext } from "react";
27
+ import { IntlContext, IntlProvider, ReactIntlErrorCode } from "react-intl";
28
+ import { Fragment, jsx } from "react/jsx-runtime";
29
+ var EnsureIntlProvider = ({ children }) => {
30
+ const intl = useContext(IntlContext);
31
+ if (intl) {
32
+ return /* @__PURE__ */ jsx(Fragment, { children });
33
+ }
36
34
  return /* @__PURE__ */ jsx(
37
- "span",
35
+ IntlProvider,
38
36
  {
39
- style: {
40
- ...styles,
41
- display: "inline-flex",
42
- alignItems: "center",
43
- padding: "2px 8px",
44
- borderRadius: "var(--border-radius-20)",
45
- fontSize: "var(--font-size-10)",
46
- fontWeight: "var(--font-weight-600)",
47
- marginRight: "4px",
48
- whiteSpace: "nowrap"
37
+ locale: "en",
38
+ defaultLocale: "en",
39
+ messages: {},
40
+ onError: (err) => {
41
+ if (err.code === ReactIntlErrorCode.MISSING_TRANSLATION) return;
42
+ console.error(err);
49
43
  },
50
- children: variant === "published" ? "Published" : variant === "draft" ? "Draft" : "No state"
44
+ children
51
45
  }
52
46
  );
53
47
  };
48
+
49
+ // src/EnsureNimbusProvider.tsx
50
+ import { NimbusProvider } from "@commercetools/nimbus";
51
+ import { jsx as jsx2 } from "react/jsx-runtime";
52
+ var EnsureNimbusProvider = ({
53
+ locale = "en",
54
+ children
55
+ }) => /* @__PURE__ */ jsx2(NimbusProvider, { locale, children });
56
+
57
+ // src/PageManager.tsx
58
+ import {
59
+ Badge,
60
+ Button,
61
+ Card,
62
+ DataTable,
63
+ Dialog,
64
+ FormField,
65
+ Icon,
66
+ IconButton,
67
+ LoadingSpinner,
68
+ Select,
69
+ Stack,
70
+ Text,
71
+ TextInput
72
+ } from "@commercetools/nimbus";
73
+ import {
74
+ Add,
75
+ ChevronLeft,
76
+ Close,
77
+ Delete,
78
+ Edit,
79
+ Visibility
80
+ } from "@commercetools/nimbus-icons";
81
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
82
+ var DEFAULT_CONFIG = {
83
+ ...defaultPuckConfig,
84
+ components: { ...defaultPuckConfig.components }
85
+ };
86
+ var STATUS_BADGE = {
87
+ draft: { colorPalette: "warning", label: "Draft" },
88
+ published: { colorPalette: "positive", label: "Published" },
89
+ none: { colorPalette: "neutral", label: "No state" }
90
+ };
91
+ var StatusBadge = ({ variant }) => {
92
+ const meta = STATUS_BADGE[variant];
93
+ return /* @__PURE__ */ jsx3(Badge, { colorPalette: meta.colorPalette, size: "xs", children: meta.label });
94
+ };
54
95
  var NAV_BAR_STYLE = {
55
96
  position: "sticky",
56
97
  top: 0,
@@ -59,26 +100,23 @@ var NAV_BAR_STYLE = {
59
100
  gap: "12px",
60
101
  padding: "8px 16px",
61
102
  background: "var(--color-surface, #fff)",
62
- borderBottom: "1px solid var(--color-neutral-90)",
103
+ borderBottom: "1px solid var(--color-neutral-90, #e0e0e0)",
63
104
  zIndex: 200,
64
105
  flexShrink: 0
65
106
  };
66
- var COLUMNS = [
67
- { key: "name", label: "Name" },
68
- { key: "slug", label: "Slug" },
69
- { key: "status", label: "Status" },
70
- { key: "updatedAt", label: "Updated" },
71
- { key: "actions", label: "Actions", shouldIgnoreRowClick: true }
72
- ];
73
107
  var PageList = ({ backButton }) => {
74
108
  const history = useHistory();
75
109
  const { pages, loading, error, createPage, deletePage, refresh } = usePuckPages();
110
+ const { templates } = usePuckTemplates("page");
76
111
  const [creating, setCreating] = useState(false);
77
112
  const [newName, setNewName] = useState("");
78
113
  const [newSlug, setNewSlug] = useState("");
114
+ const [templateKey, setTemplateKey] = useState("");
79
115
  const [formError, setFormError] = useState("");
80
116
  const [submitting, setSubmitting] = useState(false);
81
117
  const [deleting, setDeleting] = useState(null);
118
+ const [pendingDelete, setPendingDelete] = useState(null);
119
+ const [search, setSearch] = useState("");
82
120
  const handleCreate = async () => {
83
121
  if (!newName.trim()) {
84
122
  setFormError("Name is required");
@@ -95,10 +133,15 @@ var PageList = ({ backButton }) => {
95
133
  name: newName.trim(),
96
134
  slug: newSlug.trim().startsWith("/") ? newSlug.trim() : `/${newSlug.trim()}`
97
135
  };
136
+ if (templateKey) {
137
+ const template = templates.find((t) => t.key === templateKey);
138
+ if (template) input.puckData = template.value.puckData;
139
+ }
98
140
  const created = await createPage(input);
99
141
  setCreating(false);
100
142
  setNewName("");
101
143
  setNewSlug("");
144
+ setTemplateKey("");
102
145
  history.push(`/${created.key}/edit`, { pageName: created.value.name });
103
146
  } catch (err) {
104
147
  setFormError(err.message);
@@ -107,206 +150,323 @@ var PageList = ({ backButton }) => {
107
150
  }
108
151
  };
109
152
  const handleDelete = async (page) => {
110
- if (!confirm(`Delete "${page.value.name}"? This cannot be undone.`)) return;
111
153
  setDeleting(page.key);
112
154
  try {
113
155
  await deletePage(page.key);
114
156
  await refresh();
157
+ setPendingDelete(null);
115
158
  } finally {
116
159
  setDeleting(null);
117
160
  }
118
161
  };
119
162
  if (loading) {
120
- return /* @__PURE__ */ jsx("div", { style: { padding: "64px", display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ jsx(LoadingSpinner, {}) });
163
+ return /* @__PURE__ */ jsx3("div", { style: { padding: "64px", display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ jsx3(LoadingSpinner, {}) });
121
164
  }
122
165
  if (error) {
123
- return /* @__PURE__ */ jsx("div", { style: { padding: "32px" }, children: /* @__PURE__ */ jsxs(Text.Body, { tone: "negative", children: [
166
+ return /* @__PURE__ */ jsx3("div", { style: { padding: "32px" }, children: /* @__PURE__ */ jsxs(Text, { color: "critical.11", children: [
124
167
  "Error: ",
125
168
  error
126
169
  ] }) });
127
170
  }
128
- const rows = pages.map((p) => ({ ...p, id: p.key }));
129
- return /* @__PURE__ */ jsx("div", { style: { maxWidth: "1200px", margin: "0 auto", padding: "32px 24px" }, children: /* @__PURE__ */ jsxs(Spacings.Stack, { scale: "l", children: [
130
- /* @__PURE__ */ jsxs(Spacings.Inline, { justifyContent: "space-between", alignItems: "center", children: [
131
- /* @__PURE__ */ jsxs(Spacings.Inline, { scale: "m", alignItems: "center", children: [
132
- backButton,
133
- /* @__PURE__ */ jsx(Text.Headline, { as: "h1", children: "Puck Pages" })
134
- ] }),
135
- /* @__PURE__ */ jsx(
136
- PrimaryButton,
171
+ const term = search.trim().toLowerCase();
172
+ const filteredPages = term ? pages.filter(
173
+ (p) => p.value.name.toLowerCase().includes(term) || p.value.slug.toLowerCase().includes(term)
174
+ ) : pages;
175
+ const rows = filteredPages.map((p) => ({ ...p, id: p.key }));
176
+ const columns = [
177
+ {
178
+ id: "name",
179
+ header: "Name",
180
+ accessor: (row) => row.value.name,
181
+ render: ({ row }) => /* @__PURE__ */ jsx3(Text, { fontWeight: "bold", children: row.value.name })
182
+ },
183
+ {
184
+ id: "slug",
185
+ header: "Slug",
186
+ accessor: (row) => row.value.slug,
187
+ render: ({ row }) => /* @__PURE__ */ jsx3(
188
+ "code",
137
189
  {
138
- label: "New Page",
139
- iconLeft: /* @__PURE__ */ jsx(PlusThinIcon, {}),
140
- onClick: () => setCreating(true)
190
+ style: {
191
+ background: "#f4f4f4",
192
+ padding: "2px 6px",
193
+ borderRadius: "4px",
194
+ fontSize: "11px",
195
+ fontFamily: "monospace"
196
+ },
197
+ children: row.value.slug
141
198
  }
142
199
  )
143
- ] }),
144
- creating && /* @__PURE__ */ jsx(Card, { insetScale: "l", children: /* @__PURE__ */ jsxs(Spacings.Stack, { scale: "m", children: [
145
- /* @__PURE__ */ jsx(Text.Subheadline, { as: "h4", isBold: true, children: "Create New Page" }),
146
- /* @__PURE__ */ jsxs(Spacings.Inline, { scale: "m", children: [
147
- /* @__PURE__ */ jsx("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsxs(Spacings.Stack, { scale: "xs", children: [
148
- /* @__PURE__ */ jsx(Label, { htmlFor: "new-page-name", children: "Name *" }),
149
- /* @__PURE__ */ jsx(
150
- TextInput,
151
- {
152
- id: "new-page-name",
153
- value: newName,
154
- onChange: (e) => setNewName(e.target.value),
155
- placeholder: "Home Page"
156
- }
157
- )
158
- ] }) }),
159
- /* @__PURE__ */ jsx("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsxs(Spacings.Stack, { scale: "xs", children: [
160
- /* @__PURE__ */ jsx(Label, { htmlFor: "new-page-slug", children: "Slug *" }),
161
- /* @__PURE__ */ jsx(
162
- TextInput,
163
- {
164
- id: "new-page-slug",
165
- value: newSlug,
166
- onChange: (e) => setNewSlug(e.target.value),
167
- placeholder: "/home"
168
- }
169
- )
170
- ] }) })
171
- ] }),
172
- formError && /* @__PURE__ */ jsx(Text.Body, { tone: "negative", children: formError }),
173
- /* @__PURE__ */ jsxs(Spacings.Inline, { scale: "s", children: [
174
- /* @__PURE__ */ jsx(
175
- PrimaryButton,
200
+ },
201
+ {
202
+ id: "status",
203
+ header: "Status",
204
+ accessor: () => "",
205
+ isSortable: false,
206
+ render: ({ row }) => /* @__PURE__ */ jsxs(Stack, { direction: "row", gap: "100", wrap: "wrap", children: [
207
+ row.states.draft && /* @__PURE__ */ jsx3(StatusBadge, { variant: "draft" }),
208
+ row.states.published && /* @__PURE__ */ jsx3(StatusBadge, { variant: "published" }),
209
+ !row.states.draft && !row.states.published && /* @__PURE__ */ jsx3(StatusBadge, { variant: "none" })
210
+ ] })
211
+ },
212
+ {
213
+ id: "updatedAt",
214
+ header: "Updated",
215
+ accessor: (row) => row.value.updatedAt,
216
+ render: ({ row }) => /* @__PURE__ */ jsx3(Text, { fontSize: "xs", color: "neutral.11", children: new Date(row.value.updatedAt).toLocaleString() })
217
+ },
218
+ {
219
+ id: "actions",
220
+ header: "Actions",
221
+ accessor: () => "",
222
+ isSortable: false,
223
+ render: ({ row }) => /* @__PURE__ */ jsxs(Stack, { direction: "row", gap: "100", alignItems: "center", children: [
224
+ /* @__PURE__ */ jsx3(
225
+ IconButton,
176
226
  {
177
- label: submitting ? "Creating\u2026" : "Create",
178
- onClick: () => void handleCreate(),
179
- isDisabled: submitting
227
+ "aria-label": `Edit ${row.value.name}`,
228
+ variant: "ghost",
229
+ size: "xs",
230
+ onPress: () => history.push(`/${row.key}/edit`, { pageName: row.value.name }),
231
+ children: /* @__PURE__ */ jsx3(Edit, {})
180
232
  }
181
233
  ),
182
- /* @__PURE__ */ jsx(
183
- SecondaryButton,
234
+ /* @__PURE__ */ jsx3(
235
+ IconButton,
184
236
  {
185
- label: "Cancel",
186
- onClick: () => {
187
- setCreating(false);
188
- setFormError("");
189
- }
237
+ "aria-label": `Preview ${row.value.name}`,
238
+ variant: "ghost",
239
+ size: "xs",
240
+ onPress: () => history.push(`/${row.key}/preview`, { pageName: row.value.name }),
241
+ children: /* @__PURE__ */ jsx3(Visibility, {})
242
+ }
243
+ ),
244
+ /* @__PURE__ */ jsx3(
245
+ IconButton,
246
+ {
247
+ "aria-label": `Delete ${row.value.name}`,
248
+ variant: "ghost",
249
+ colorPalette: "critical",
250
+ size: "xs",
251
+ isDisabled: deleting === row.key,
252
+ onPress: () => setPendingDelete(row),
253
+ children: /* @__PURE__ */ jsx3(Delete, {})
190
254
  }
191
255
  )
192
256
  ] })
193
- ] }) }),
194
- pages.length === 0 && !creating ? /* @__PURE__ */ jsxs(Spacings.Stack, { scale: "m", alignItems: "center", children: [
195
- /* @__PURE__ */ jsx(Text.Body, { tone: "secondary", children: "No pages yet." }),
196
- /* @__PURE__ */ jsx(
197
- PrimaryButton,
198
- {
199
- label: "Create first page",
200
- iconLeft: /* @__PURE__ */ jsx(PlusThinIcon, {}),
201
- onClick: () => setCreating(true)
202
- }
203
- )
204
- ] }) : pages.length > 0 ? /* @__PURE__ */ jsx(
205
- DataTable,
206
- {
207
- columns: COLUMNS,
208
- rows,
209
- itemRenderer: (row, column) => {
210
- switch (column.key) {
211
- case "name":
212
- return /* @__PURE__ */ jsxs(Spacings.Stack, { scale: "xs", children: [
213
- /* @__PURE__ */ jsx(Text.Body, { isBold: true, children: row.value.name }),
214
- /* @__PURE__ */ jsx(Text.Detail, { tone: "secondary", children: row.key })
215
- ] });
216
- case "slug":
217
- return /* @__PURE__ */ jsx(
218
- "code",
219
- {
220
- style: {
221
- background: "var(--color-neutral-95)",
222
- padding: "2px 6px",
223
- borderRadius: "var(--border-radius-4)",
224
- fontSize: "var(--font-size-10)",
225
- fontFamily: "monospace"
226
- },
227
- children: row.value.slug
228
- }
229
- );
230
- case "status":
231
- return /* @__PURE__ */ jsxs("span", { children: [
232
- row.states.draft && /* @__PURE__ */ jsx(StatusBadge, { variant: "draft" }),
233
- row.states.published && /* @__PURE__ */ jsx(StatusBadge, { variant: "published" }),
234
- !row.states.draft && !row.states.published && /* @__PURE__ */ jsx(StatusBadge, { variant: "none" })
235
- ] });
236
- case "updatedAt":
237
- return /* @__PURE__ */ jsx(Text.Body, { tone: "secondary", children: new Date(row.value.updatedAt).toLocaleString() });
238
- case "actions":
239
- return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
240
- /* @__PURE__ */ jsx(
241
- PrimaryButton,
242
- {
243
- label: "Edit",
244
- size: "20",
245
- onClick: () => history.push(`/${row.key}/edit`, { pageName: row.value.name })
246
- }
247
- ),
248
- /* @__PURE__ */ jsx(
249
- SecondaryButton,
250
- {
251
- label: "Preview",
252
- size: "20",
253
- onClick: () => history.push(`/${row.key}/preview`, { pageName: row.value.name })
254
- }
255
- ),
256
- /* @__PURE__ */ jsx(
257
- FlatButton,
258
- {
259
- tone: "critical",
260
- label: deleting === row.key ? "\u2026" : "Delete",
261
- isDisabled: deleting === row.key,
262
- onClick: () => void handleDelete(row)
263
- }
264
- )
265
- ] });
266
- default:
267
- return null;
257
+ }
258
+ ];
259
+ return /* @__PURE__ */ jsxs("div", { style: { maxWidth: "1200px", margin: "0 auto", padding: "32px 24px" }, children: [
260
+ /* @__PURE__ */ jsxs(Stack, { direction: "column", gap: "600", children: [
261
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "center", children: [
262
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", gap: "400", alignItems: "center", children: [
263
+ backButton,
264
+ /* @__PURE__ */ jsx3(Text, { as: "h1", fontSize: "2xl", fontWeight: "700", children: "Pages" })
265
+ ] }),
266
+ /* @__PURE__ */ jsxs(Button, { variant: "solid", onPress: () => setCreating(true), children: [
267
+ /* @__PURE__ */ jsx3(Icon, { as: Add }),
268
+ " New Page"
269
+ ] })
270
+ ] }),
271
+ creating && /* @__PURE__ */ jsx3(Card.Root, { variant: "outlined", children: /* @__PURE__ */ jsx3(Card.Body, { children: /* @__PURE__ */ jsxs(Stack, { direction: "column", gap: "400", children: [
272
+ /* @__PURE__ */ jsx3(Text, { as: "h4", fontSize: "xl", fontWeight: "700", children: "Create New Page" }),
273
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", gap: "400", children: [
274
+ /* @__PURE__ */ jsx3("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsxs(FormField.Root, { isRequired: true, children: [
275
+ /* @__PURE__ */ jsx3(FormField.Label, { children: "Name" }),
276
+ /* @__PURE__ */ jsx3(FormField.Input, { children: /* @__PURE__ */ jsx3(
277
+ TextInput,
278
+ {
279
+ value: newName,
280
+ onChange: (v) => setNewName(v),
281
+ placeholder: "Home Page"
282
+ }
283
+ ) })
284
+ ] }) }),
285
+ /* @__PURE__ */ jsx3("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsxs(FormField.Root, { isRequired: true, children: [
286
+ /* @__PURE__ */ jsx3(FormField.Label, { children: "Slug" }),
287
+ /* @__PURE__ */ jsx3(FormField.Input, { children: /* @__PURE__ */ jsx3(
288
+ TextInput,
289
+ {
290
+ value: newSlug,
291
+ onChange: (v) => setNewSlug(v),
292
+ placeholder: "/home"
293
+ }
294
+ ) })
295
+ ] }) })
296
+ ] }),
297
+ /* @__PURE__ */ jsxs(FormField.Root, { children: [
298
+ /* @__PURE__ */ jsx3(FormField.Label, { children: "Template" }),
299
+ /* @__PURE__ */ jsx3(FormField.Input, { children: /* @__PURE__ */ jsx3(
300
+ Select.Root,
301
+ {
302
+ "aria-label": "Template",
303
+ selectedKey: templateKey || "empty",
304
+ onSelectionChange: (key) => setTemplateKey(key == null || key === "empty" ? "" : String(key)),
305
+ children: /* @__PURE__ */ jsxs(Select.Options, { children: [
306
+ /* @__PURE__ */ jsx3(Select.Option, { id: "empty", children: "Empty" }),
307
+ templates.map((t) => /* @__PURE__ */ jsx3(Select.Option, { id: t.key, children: t.value.name }, t.key))
308
+ ] })
309
+ }
310
+ ) })
311
+ ] }),
312
+ formError && /* @__PURE__ */ jsx3(Text, { color: "critical.11", children: formError }),
313
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", gap: "200", children: [
314
+ /* @__PURE__ */ jsx3(Button, { variant: "solid", onPress: () => void handleCreate(), isDisabled: submitting, children: submitting ? "Creating\u2026" : "Create" }),
315
+ /* @__PURE__ */ jsx3(
316
+ Button,
317
+ {
318
+ variant: "outline",
319
+ onPress: () => {
320
+ setCreating(false);
321
+ setFormError("");
322
+ },
323
+ children: "Cancel"
324
+ }
325
+ )
326
+ ] })
327
+ ] }) }) }),
328
+ pages.length === 0 && !creating ? /* @__PURE__ */ jsxs(Stack, { direction: "column", gap: "400", alignItems: "center", children: [
329
+ /* @__PURE__ */ jsx3(Text, { color: "neutral.11", children: "No pages yet." }),
330
+ /* @__PURE__ */ jsxs(Button, { variant: "solid", onPress: () => setCreating(true), children: [
331
+ /* @__PURE__ */ jsx3(Icon, { as: Add }),
332
+ " Create first page"
333
+ ] })
334
+ ] }) : pages.length > 0 ? /* @__PURE__ */ jsxs(Stack, { direction: "column", gap: "400", children: [
335
+ /* @__PURE__ */ jsx3("div", { style: { maxWidth: 360 }, children: /* @__PURE__ */ jsx3(
336
+ TextInput,
337
+ {
338
+ "aria-label": "Search pages",
339
+ placeholder: "Search by name or path\u2026",
340
+ value: search,
341
+ onChange: (v) => setSearch(v),
342
+ width: "100%",
343
+ trailingElement: search !== "" ? /* @__PURE__ */ jsx3(
344
+ IconButton,
345
+ {
346
+ "aria-label": "Clear search",
347
+ variant: "ghost",
348
+ colorPalette: "neutral",
349
+ size: "2xs",
350
+ onPress: () => setSearch(""),
351
+ children: /* @__PURE__ */ jsx3(Close, {})
352
+ }
353
+ ) : void 0
268
354
  }
269
- }
355
+ ) }),
356
+ /* @__PURE__ */ jsxs("div", { className: "puck-page-list", children: [
357
+ /* @__PURE__ */ jsx3("style", { children: `
358
+ .puck-page-list .pin-rows-column-header,
359
+ .puck-page-list [data-slot="pin-row-cell"] { display: none !important; }
360
+ ` }),
361
+ /* @__PURE__ */ jsx3(DataTable, { columns, rows, "aria-label": "Pages" })
362
+ ] })
363
+ ] }) : null
364
+ ] }),
365
+ /* @__PURE__ */ jsx3(
366
+ Dialog.Root,
367
+ {
368
+ isOpen: pendingDelete !== null,
369
+ onOpenChange: (open) => {
370
+ if (!open) setPendingDelete(null);
371
+ },
372
+ children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
373
+ /* @__PURE__ */ jsxs(Dialog.Header, { children: [
374
+ /* @__PURE__ */ jsx3(Dialog.Title, { children: "Delete page?" }),
375
+ /* @__PURE__ */ jsx3(Dialog.CloseTrigger, {})
376
+ ] }),
377
+ /* @__PURE__ */ jsx3(Dialog.Body, { children: /* @__PURE__ */ jsxs(Text, { children: [
378
+ "Are you sure you want to delete",
379
+ " ",
380
+ /* @__PURE__ */ jsx3(Text, { as: "span", fontWeight: "700", children: pendingDelete?.value.name }),
381
+ "? This cannot be undone."
382
+ ] }) }),
383
+ /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
384
+ /* @__PURE__ */ jsx3(Button, { slot: "close", variant: "outline", isDisabled: deleting !== null, children: "Cancel" }),
385
+ /* @__PURE__ */ jsx3(
386
+ Button,
387
+ {
388
+ colorPalette: "critical",
389
+ isDisabled: deleting !== null,
390
+ onPress: () => {
391
+ if (pendingDelete) void handleDelete(pendingDelete);
392
+ },
393
+ children: deleting !== null ? "Deleting\u2026" : "Delete"
394
+ }
395
+ )
396
+ ] })
397
+ ] })
270
398
  }
271
- ) : null
272
- ] }) });
399
+ )
400
+ ] });
273
401
  };
274
402
  var PageEditorRoute = ({ config, backButton }) => {
275
403
  const { pageKey } = useParams();
276
404
  const history = useHistory();
277
405
  const location = useLocation();
278
- const { baseURL, projectKey, businessUnitKey, jwtToken } = usePuckApiContext();
406
+ const { baseURL, projectKey, businessUnitKey, jwtToken, locale } = usePuckApiContext();
279
407
  const pageName = location.state?.pageName ?? pageKey ?? "Page";
408
+ const [isDirty, setIsDirty] = useState(false);
409
+ const [pendingNav, setPendingNav] = useState(null);
410
+ const guardedNavigate = useCallback(
411
+ (navFn) => {
412
+ if (isDirty) {
413
+ setPendingNav(() => navFn);
414
+ } else {
415
+ navFn();
416
+ }
417
+ },
418
+ [isDirty]
419
+ );
280
420
  return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", height: "100%" }, children: [
281
421
  /* @__PURE__ */ jsxs("div", { style: NAV_BAR_STYLE, children: [
282
422
  backButton,
283
- backButton && /* @__PURE__ */ jsx(Text.Body, { tone: "secondary", children: "/" }),
284
- /* @__PURE__ */ jsx(
285
- FlatButton,
423
+ backButton && /* @__PURE__ */ jsx3(Text, { color: "neutral.11", children: "/" }),
424
+ /* @__PURE__ */ jsxs(
425
+ Button,
286
426
  {
287
- label: "Pages",
288
- icon: /* @__PURE__ */ jsx(AngleLeftIcon, {}),
289
- iconPosition: "left",
290
- onClick: () => history.push("/")
427
+ variant: "ghost",
428
+ onPress: () => guardedNavigate(() => history.push("/")),
429
+ children: [
430
+ /* @__PURE__ */ jsx3(Icon, { as: ChevronLeft }),
431
+ " Pages"
432
+ ]
291
433
  }
292
434
  ),
293
- /* @__PURE__ */ jsx(Text.Body, { tone: "secondary", children: "/" }),
294
- /* @__PURE__ */ jsx(Text.Body, { isBold: true, children: pageName })
435
+ /* @__PURE__ */ jsx3(Text, { color: "neutral.11", children: "/" }),
436
+ /* @__PURE__ */ jsx3(Text, { fontWeight: "bold", children: pageName })
295
437
  ] }),
296
- /* @__PURE__ */ jsx("div", { style: { flex: 1, overflow: "hidden" }, children: /* @__PURE__ */ jsx(
297
- PuckEditor,
298
- {
299
- baseURL,
300
- projectKey,
301
- businessUnitKey,
302
- jwtToken: jwtToken ?? "",
303
- pageKey,
304
- config,
305
- onError: (err) => {
306
- console.error("[PageManager] editor error:", err);
438
+ /* @__PURE__ */ jsxs("div", { className: "puck-editor-fill", style: { flex: 1, overflow: "hidden" }, children: [
439
+ /* @__PURE__ */ jsx3(
440
+ PuckEditor,
441
+ {
442
+ baseURL,
443
+ projectKey,
444
+ businessUnitKey,
445
+ jwtToken: jwtToken ?? "",
446
+ locale,
447
+ pageKey,
448
+ config,
449
+ onDirtyChange: setIsDirty,
450
+ onPreview: () => guardedNavigate(
451
+ () => history.push(`/${pageKey}/preview`, { pageName })
452
+ ),
453
+ onError: (err) => {
454
+ console.error("[PageManager] editor error:", err);
455
+ }
307
456
  }
457
+ ),
458
+ /* @__PURE__ */ jsx3(PropertiesResizer, {})
459
+ ] }),
460
+ /* @__PURE__ */ jsx3(
461
+ UnsavedChangesDialog,
462
+ {
463
+ isOpen: pendingNav !== null,
464
+ onOpenChange: (open) => {
465
+ if (!open) setPendingNav(null);
466
+ },
467
+ onConfirm: () => pendingNav?.()
308
468
  }
309
- ) })
469
+ )
310
470
  ] });
311
471
  };
312
472
  var PagePreviewRoute = ({ config, backButton }) => {
@@ -317,53 +477,36 @@ var PagePreviewRoute = ({ config, backButton }) => {
317
477
  return /* @__PURE__ */ jsxs("div", { children: [
318
478
  /* @__PURE__ */ jsxs("div", { style: NAV_BAR_STYLE, children: [
319
479
  backButton,
320
- backButton && /* @__PURE__ */ jsx(Text.Body, { tone: "secondary", children: "/" }),
321
- /* @__PURE__ */ jsx(
322
- FlatButton,
323
- {
324
- label: "Pages",
325
- icon: /* @__PURE__ */ jsx(AngleLeftIcon, {}),
326
- iconPosition: "left",
327
- onClick: () => history.push("/")
328
- }
329
- ),
330
- /* @__PURE__ */ jsx(Text.Body, { tone: "secondary", children: "/" }),
331
- /* @__PURE__ */ jsx(Text.Body, { isBold: true, children: pageName }),
332
- /* @__PURE__ */ jsx(
333
- "span",
334
- {
335
- style: {
336
- background: "var(--color-primary-95)",
337
- color: "var(--color-primary-25)",
338
- border: "1px solid var(--color-primary-85)",
339
- display: "inline-flex",
340
- alignItems: "center",
341
- padding: "2px 10px",
342
- borderRadius: "var(--border-radius-20)",
343
- fontSize: "var(--font-size-10)",
344
- fontWeight: "var(--font-weight-600)"
345
- },
346
- children: "Preview"
347
- }
348
- )
480
+ backButton && /* @__PURE__ */ jsx3(Text, { color: "neutral.11", children: "/" }),
481
+ /* @__PURE__ */ jsxs(Button, { variant: "ghost", onPress: () => history.push("/"), children: [
482
+ /* @__PURE__ */ jsx3(Icon, { as: ChevronLeft }),
483
+ " Pages"
484
+ ] }),
485
+ /* @__PURE__ */ jsx3(Text, { color: "neutral.11", children: "/" }),
486
+ /* @__PURE__ */ jsx3(Text, { fontWeight: "bold", children: pageName }),
487
+ /* @__PURE__ */ jsx3(Badge, { colorPalette: "primary", size: "xs", children: "Preview" }),
488
+ /* @__PURE__ */ jsx3("div", { style: { marginLeft: "auto" }, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "xs", onPress: () => history.goBack(), children: [
489
+ /* @__PURE__ */ jsx3(Icon, { as: Close }),
490
+ " Close preview"
491
+ ] }) })
349
492
  ] }),
350
- /* @__PURE__ */ jsx(PuckRenderer, { pageKey, mode: "preview", config })
493
+ /* @__PURE__ */ jsx3(PuckRenderer, { pageKey, mode: "preview", config })
351
494
  ] });
352
495
  };
353
496
  var PageManagerInner = ({ config, backButton }) => /* @__PURE__ */ jsxs(Switch, { children: [
354
- /* @__PURE__ */ jsx(Route, { exact: true, path: "/", render: () => /* @__PURE__ */ jsx(PageList, { backButton }) }),
355
- /* @__PURE__ */ jsx(
497
+ /* @__PURE__ */ jsx3(Route, { exact: true, path: "/", render: () => /* @__PURE__ */ jsx3(PageList, { backButton }) }),
498
+ /* @__PURE__ */ jsx3(
356
499
  Route,
357
500
  {
358
501
  path: "/:pageKey/edit",
359
- render: () => /* @__PURE__ */ jsx(PageEditorRoute, { config, backButton })
502
+ render: () => /* @__PURE__ */ jsx3(PageEditorRoute, { config, backButton })
360
503
  }
361
504
  ),
362
- /* @__PURE__ */ jsx(
505
+ /* @__PURE__ */ jsx3(
363
506
  Route,
364
507
  {
365
508
  path: "/:pageKey/preview",
366
- render: () => /* @__PURE__ */ jsx(PagePreviewRoute, { config, backButton })
509
+ render: () => /* @__PURE__ */ jsx3(PagePreviewRoute, { config, backButton })
367
510
  }
368
511
  )
369
512
  ] });
@@ -373,18 +516,20 @@ var PageManager = ({
373
516
  projectKey,
374
517
  businessUnitKey,
375
518
  jwtToken,
519
+ locale,
376
520
  config = DEFAULT_CONFIG,
377
521
  backButton
378
- }) => /* @__PURE__ */ jsx(
522
+ }) => /* @__PURE__ */ jsx3(EnsureNimbusProvider, { locale, children: /* @__PURE__ */ jsx3(EnsureIntlProvider, { children: /* @__PURE__ */ jsx3(
379
523
  PuckApiProvider,
380
524
  {
381
525
  baseURL,
382
526
  projectKey,
383
527
  businessUnitKey,
384
528
  jwtToken,
385
- children: /* @__PURE__ */ jsx(BrowserRouter, { basename: parentUrl, children: /* @__PURE__ */ jsx(PageManagerInner, { config, backButton }) })
529
+ locale,
530
+ children: /* @__PURE__ */ jsx3(BrowserRouter, { basename: parentUrl, children: /* @__PURE__ */ jsx3(PageManagerInner, { config, backButton }) })
386
531
  }
387
- );
532
+ ) }) });
388
533
  export {
389
534
  PageManager
390
535
  };