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