@questpie/admin 3.5.2 → 3.5.3
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/client/blocks/block-renderer.d.mts +2 -2
- package/dist/client/builder/types/collection-types.d.mts +9 -0
- package/dist/client/components/actions/action-dialog.mjs +5 -0
- package/dist/client/components/fields/rich-text-editor/bubble-menu.mjs +7 -0
- package/dist/client/components/fields/rich-text-editor/extensions.mjs +17 -1
- package/dist/client/components/fields/rich-text-editor/index.d.mts +2 -1
- package/dist/client/components/fields/rich-text-editor/index.mjs +35 -74
- package/dist/client/components/fields/rich-text-editor/slash-commands.mjs +30 -7
- package/dist/client/components/fields/rich-text-editor/toolbar.mjs +1 -312
- package/dist/client/components/fields/rich-text-editor/types.d.mts +4 -0
- package/dist/client/components/fields/rich-text-editor/types.mjs +1 -1
- package/dist/client/components/fields/rich-text-editor/utils.mjs +6 -12
- package/dist/client/components/filter-builder/filter-builder-sheet.mjs +75 -22
- package/dist/client/components/ui/dropdown-menu.mjs +1 -34
- package/dist/client/hooks/query-access.d.mts +9 -0
- package/dist/client/hooks/query-access.mjs +20 -0
- package/dist/client/hooks/typed-hooks.d.mts +4 -2
- package/dist/client/hooks/typed-hooks.mjs +30 -29
- package/dist/client/hooks/use-reactive-fields.d.mts +1 -0
- package/dist/client/hooks/use-reactive-fields.mjs +16 -1
- package/dist/client/hooks/use-server-actions.mjs +12 -1
- package/dist/client/hooks/use-view-state.mjs +15 -7
- package/dist/client/lib/view-filter-utils.mjs +30 -0
- package/dist/client/preview/block-scope-context.d.mts +2 -2
- package/dist/client/preview/preview-banner.d.mts +2 -2
- package/dist/client/preview/preview-field.d.mts +4 -4
- package/dist/client/styles/base.css +69 -77
- package/dist/client/utils/build-field-definitions-from-schema.mjs +1 -0
- package/dist/client/views/auth/accept-invite-form.d.mts +2 -2
- package/dist/client/views/auth/auth-layout.d.mts +3 -3
- package/dist/client/views/auth/login-form.d.mts +2 -2
- package/dist/client/views/auth/reset-password-form.d.mts +2 -2
- package/dist/client/views/collection/auto-form-fields.mjs +3 -2
- package/dist/client/views/collection/cells/primitive-cells.mjs +9 -6
- package/dist/client/views/collection/columns/build-columns.mjs +3 -1
- package/dist/client/views/collection/field-renderer.mjs +11 -3
- package/dist/client/views/collection/form-view.mjs +207 -202
- package/dist/client/views/collection/list-view.mjs +581 -183
- package/dist/client/views/collection/outline.mjs +44 -19
- package/dist/client/views/collection/quick-filter-bar.mjs +45 -0
- package/dist/client/views/collection/table-view.mjs +60 -16
- package/dist/client/views/globals/global-form-view.mjs +12 -9
- package/dist/client/views/layout/admin-layout.mjs +1 -1
- package/dist/client/views/layout/admin-sidebar.mjs +20 -14
- package/dist/client/views/layout/admin-theme.mjs +5 -4
- package/dist/client.mjs +1 -1
- package/dist/components/rich-text/rich-text-renderer.d.mts +5 -5
- package/dist/components/rich-text/rich-text-renderer.mjs +5 -2
- package/dist/index.mjs +1 -1
- package/dist/modules/admin.d.mts +1 -1
- package/dist/server/augmentation/actions.d.mts +4 -3
- package/dist/server/augmentation/dashboard.d.mts +11 -11
- package/dist/server/augmentation/form-layout.d.mts +11 -6
- package/dist/server/augmentation/index.d.mts +7 -0
- package/dist/server/augmentation/sidebar.d.mts +8 -8
- package/dist/server/codegen/admin-client-template.mjs +7 -6
- package/dist/server/fields/index.d.mts +1 -1
- package/dist/server/fields/rich-text.d.mts +16 -17
- package/dist/server/fields/rich-text.mjs +18 -7
- package/dist/server/i18n/messages/cs.mjs +2 -0
- package/dist/server/i18n/messages/de.mjs +2 -0
- package/dist/server/i18n/messages/en.mjs +4 -0
- package/dist/server/i18n/messages/es.mjs +2 -0
- package/dist/server/i18n/messages/fr.mjs +2 -0
- package/dist/server/i18n/messages/pl.mjs +2 -0
- package/dist/server/i18n/messages/pt.mjs +2 -0
- package/dist/server/i18n/messages/sk.mjs +2 -0
- package/dist/server/modules/admin/block/block-builder.d.mts +0 -8
- package/dist/server/modules/admin/block/introspection.d.mts +2 -2
- package/dist/server/modules/admin/collections/account.d.mts +53 -52
- package/dist/server/modules/admin/collections/admin-locks.d.mts +4 -3
- package/dist/server/modules/admin/collections/admin-preferences.d.mts +38 -37
- package/dist/server/modules/admin/collections/admin-saved-views.d.mts +50 -49
- package/dist/server/modules/admin/collections/apikey.d.mts +72 -71
- package/dist/server/modules/admin/collections/assets.d.mts +42 -41
- package/dist/server/modules/admin/collections/session.d.mts +46 -45
- package/dist/server/modules/admin/collections/user.d.mts +67 -66
- package/dist/server/modules/admin/collections/verification.d.mts +39 -38
- package/dist/server/modules/admin/index.d.mts +3 -3
- package/dist/server/modules/admin/routes/admin-config.d.mts +2 -2
- package/dist/server/modules/admin/routes/admin-config.mjs +39 -23
- package/dist/server/modules/admin/routes/execute-action.mjs +28 -8
- package/dist/server/modules/admin/routes/locales.d.mts +2 -2
- package/dist/server/modules/admin/routes/preview.d.mts +11 -11
- package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
- package/dist/server/modules/admin/routes/reactive.mjs +2 -2
- package/dist/server/modules/admin/routes/route-helpers.d.mts +11 -7
- package/dist/server/modules/admin/routes/setup.d.mts +7 -7
- package/dist/server/modules/admin/routes/translations.d.mts +4 -4
- package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
- package/dist/server/modules/admin/routes/widget-data.mjs +12 -4
- package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +27 -27
- package/dist/server/modules/audit/.generated/module.d.mts +6 -6
- package/dist/server/modules/audit/collections/audit-log.d.mts +40 -39
- package/dist/server/plugin.mjs +3 -3
- package/dist/server.d.mts +1 -1
- package/dist/shared/types/index.d.mts +1 -0
- package/dist/shared/types/saved-views.types.d.mts +14 -7
- package/dist/shared.d.mts +3 -2
- package/package.json +4 -3
|
@@ -34,12 +34,11 @@ function stableGroupKey(value) {
|
|
|
34
34
|
if (typeof value === "object") return getId(value) ?? JSON.stringify(value);
|
|
35
35
|
return String(value);
|
|
36
36
|
}
|
|
37
|
-
function shouldExpand(key, depth, outline,
|
|
38
|
-
if (collapsedKeys.has(key)) return false;
|
|
37
|
+
function shouldExpand(key, depth, outline, toggledKeys) {
|
|
39
38
|
const defaultExpanded = outline?.defaultExpanded ?? true;
|
|
40
|
-
|
|
41
|
-
if (
|
|
42
|
-
return
|
|
39
|
+
const defaultValue = defaultExpanded === true ? true : defaultExpanded === false ? false : depth === 0;
|
|
40
|
+
if (toggledKeys.has(key)) return !defaultValue;
|
|
41
|
+
return defaultValue;
|
|
43
42
|
}
|
|
44
43
|
function compareByOrder(a, b, order) {
|
|
45
44
|
if (Array.isArray(order)) {
|
|
@@ -86,7 +85,8 @@ function buildFieldRows(level, docs, depth, levelIndex, ctx, scopeKey) {
|
|
|
86
85
|
}
|
|
87
86
|
return Array.from(groups.values()).sort((a, b) => compareByOrder(a, b, level.order)).flatMap((group) => {
|
|
88
87
|
const rowKey = `${scopeKey}/field:${level.field}:${group.key}`;
|
|
89
|
-
const expanded = shouldExpand(rowKey, depth, ctx.outline, ctx.
|
|
88
|
+
const expanded = shouldExpand(rowKey, depth, ctx.outline, ctx.toggledKeys);
|
|
89
|
+
const meta = ctx.metaForValue(group.value, level.field);
|
|
90
90
|
return [{
|
|
91
91
|
kind: "group",
|
|
92
92
|
key: rowKey,
|
|
@@ -94,7 +94,9 @@ function buildFieldRows(level, docs, depth, levelIndex, ctx, scopeKey) {
|
|
|
94
94
|
depth,
|
|
95
95
|
count: group.docs.length,
|
|
96
96
|
expandable: true,
|
|
97
|
-
collapsed: !expanded
|
|
97
|
+
collapsed: !expanded,
|
|
98
|
+
...meta?.icon != null && { icon: meta.icon },
|
|
99
|
+
...meta?.className && { className: meta.className }
|
|
98
100
|
}, ...expanded ? buildLevelRows(group.docs, depth + 1, levelIndex + 1, ctx, rowKey) : []];
|
|
99
101
|
});
|
|
100
102
|
}
|
|
@@ -118,7 +120,7 @@ function buildRelationFieldRows(level, docs, depth, levelIndex, ctx, scopeKey) {
|
|
|
118
120
|
}
|
|
119
121
|
return Array.from(groups.values()).sort((a, b) => compareByOrder(a, b, level.order)).flatMap((group) => {
|
|
120
122
|
const rowKey = `${scopeKey}/relation-field:${fieldPath}:${group.key}`;
|
|
121
|
-
const expanded = shouldExpand(rowKey, depth, ctx.outline, ctx.
|
|
123
|
+
const expanded = shouldExpand(rowKey, depth, ctx.outline, ctx.toggledKeys);
|
|
122
124
|
return [{
|
|
123
125
|
kind: "group",
|
|
124
126
|
key: rowKey,
|
|
@@ -151,7 +153,22 @@ function buildEdgeRows(level, docs, depth, levelIndex, ctx, scopeKey) {
|
|
|
151
153
|
const childId = getId(childValue);
|
|
152
154
|
if (!parentId || !childId || !docsById.has(childId)) continue;
|
|
153
155
|
if (docsById.has(parentId) || !isRecord(parentValue)) continue;
|
|
154
|
-
docsById.set(parentId, parentValue);
|
|
156
|
+
docsById.set(parentId, ctx.allDocsById?.get(parentId) ?? parentValue);
|
|
157
|
+
changed = true;
|
|
158
|
+
}
|
|
159
|
+
if (!changed) break;
|
|
160
|
+
}
|
|
161
|
+
if (ctx.allDocsById) for (let pass = 0; pass < ctx.maxDepth; pass++) {
|
|
162
|
+
let changed = false;
|
|
163
|
+
for (const edge of edgeDocs) {
|
|
164
|
+
const parentId = getId(getPathValue(edge, level.parentField));
|
|
165
|
+
const childId = getId(getPathValue(edge, level.childField));
|
|
166
|
+
if (!parentId || !childId) continue;
|
|
167
|
+
if (!docsById.has(parentId)) continue;
|
|
168
|
+
if (docsById.has(childId)) continue;
|
|
169
|
+
const childDoc = ctx.allDocsById.get(childId);
|
|
170
|
+
if (!childDoc) continue;
|
|
171
|
+
docsById.set(childId, childDoc);
|
|
155
172
|
changed = true;
|
|
156
173
|
}
|
|
157
174
|
if (!changed) break;
|
|
@@ -201,7 +218,7 @@ function buildEdgeRows(level, docs, depth, levelIndex, ctx, scopeKey) {
|
|
|
201
218
|
}
|
|
202
219
|
for (const [key, group] of groups) {
|
|
203
220
|
const rowKey = `${scopeKey}/edge-group:${level.collection}:${parentId}:${key}`;
|
|
204
|
-
const expanded = shouldExpand(rowKey, nextDepth, ctx.outline, ctx.
|
|
221
|
+
const expanded = shouldExpand(rowKey, nextDepth, ctx.outline, ctx.toggledKeys);
|
|
205
222
|
rows.push({
|
|
206
223
|
kind: "group",
|
|
207
224
|
key: rowKey,
|
|
@@ -223,18 +240,15 @@ function buildEdgeRows(level, docs, depth, levelIndex, ctx, scopeKey) {
|
|
|
223
240
|
if (!doc || ancestorIds.has(id)) return;
|
|
224
241
|
const rowKey = `${scopeKey}/record:${id}`;
|
|
225
242
|
const hasChildren = (childrenByParent.get(id) ?? []).some((child) => docsById.has(child.childId));
|
|
226
|
-
const expanded = hasChildren && shouldExpand(rowKey, rowDepth, ctx.outline, ctx.collapsedKeys);
|
|
227
243
|
rows.push({
|
|
228
244
|
kind: "record",
|
|
229
245
|
key: rowKey,
|
|
230
246
|
id,
|
|
231
247
|
doc,
|
|
232
|
-
depth: rowDepth
|
|
233
|
-
expandable: hasChildren,
|
|
234
|
-
collapsed: hasChildren ? !expanded : void 0
|
|
248
|
+
depth: rowDepth
|
|
235
249
|
});
|
|
236
250
|
visited.add(id);
|
|
237
|
-
if (hasChildren
|
|
251
|
+
if (hasChildren) {
|
|
238
252
|
const nextAncestors = new Set(ancestorIds);
|
|
239
253
|
nextAncestors.add(id);
|
|
240
254
|
pushChildRows(id, rowDepth + 1, branchDepth, nextAncestors);
|
|
@@ -297,7 +311,7 @@ function buildPathRows(level, docs, depth, levelIndex, ctx, scopeKey) {
|
|
|
297
311
|
function walk(node, rowDepth) {
|
|
298
312
|
const rows = [];
|
|
299
313
|
for (const child of Array.from(node.children.values()).sort((a, b) => a.label.localeCompare(b.label))) {
|
|
300
|
-
const expanded = shouldExpand(child.key, rowDepth, ctx.outline, ctx.
|
|
314
|
+
const expanded = shouldExpand(child.key, rowDepth, ctx.outline, ctx.toggledKeys);
|
|
301
315
|
rows.push({
|
|
302
316
|
kind: "synthetic",
|
|
303
317
|
key: child.key,
|
|
@@ -342,15 +356,26 @@ function buildLevelRows(docs, depth, levelIndex, ctx, scopeKey = "root") {
|
|
|
342
356
|
if (level.kind === "edge") return buildEdgeRows(level, docs, depth, levelIndex, ctx, scopeKey);
|
|
343
357
|
return buildPathRows(level, docs, depth, levelIndex, ctx, scopeKey);
|
|
344
358
|
}
|
|
345
|
-
|
|
359
|
+
const noMeta = () => void 0;
|
|
360
|
+
function buildOutlineRows({ docs, outline, edgesByCollection = {}, toggledKeys, collapsedKeys, labelForValue = defaultLabelForValue, metaForValue = noMeta, maxDepth, allDocs }) {
|
|
346
361
|
const levels = outline?.levels?.filter(Boolean) ?? [];
|
|
362
|
+
let allDocsById;
|
|
363
|
+
if (allDocs && allDocs.length > 0) {
|
|
364
|
+
allDocsById = /* @__PURE__ */ new Map();
|
|
365
|
+
for (const doc of allDocs) {
|
|
366
|
+
const id = getId(doc);
|
|
367
|
+
if (id) allDocsById.set(id, doc);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
347
370
|
const ctx = {
|
|
348
371
|
levels,
|
|
349
372
|
outline,
|
|
350
373
|
edgesByCollection,
|
|
351
|
-
|
|
374
|
+
toggledKeys: new Set(toggledKeys ?? collapsedKeys ?? []),
|
|
352
375
|
labelForValue,
|
|
353
|
-
|
|
376
|
+
metaForValue,
|
|
377
|
+
maxDepth: maxDepth ?? outline?.maxDepth ?? DEFAULT_MAX_DEPTH,
|
|
378
|
+
allDocsById
|
|
354
379
|
};
|
|
355
380
|
if (levels.length === 0) return buildLevelRows(docs, 0, 0, {
|
|
356
381
|
...ctx,
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useResolveText } from "../../i18n/hooks.mjs";
|
|
4
|
+
import { cn } from "../../lib/utils.mjs";
|
|
5
|
+
import { ComponentRenderer } from "../../components/component-renderer.mjs";
|
|
6
|
+
import { Button } from "../../components/ui/button.mjs";
|
|
7
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "../../components/ui/tooltip.mjs";
|
|
8
|
+
import { cloneFilters, filtersEqual } from "../../lib/view-filter-utils.mjs";
|
|
9
|
+
import "react";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
|
|
12
|
+
//#region src/client/views/collection/quick-filter-bar.tsx
|
|
13
|
+
function QuickFilterBar({ quickFilters, currentFilters, onApply }) {
|
|
14
|
+
const resolveText = useResolveText();
|
|
15
|
+
const visibleFilters = quickFilters?.filter((filter) => filter.filters) ?? [];
|
|
16
|
+
if (visibleFilters.length === 0) return null;
|
|
17
|
+
return /* @__PURE__ */ jsx("div", {
|
|
18
|
+
className: "flex min-w-0 flex-wrap items-center gap-2",
|
|
19
|
+
children: visibleFilters.map((filter) => {
|
|
20
|
+
const active = filtersEqual(currentFilters, filter.filters);
|
|
21
|
+
const label = resolveText(filter.label, filter.id);
|
|
22
|
+
const description = resolveText(filter.description);
|
|
23
|
+
const button = /* @__PURE__ */ jsxs(Button, {
|
|
24
|
+
variant: active ? "secondary" : "outline",
|
|
25
|
+
size: "sm",
|
|
26
|
+
className: cn("gap-1.5 text-xs", active && "border-border-strong bg-muted text-foreground"),
|
|
27
|
+
"aria-pressed": active,
|
|
28
|
+
onClick: () => onApply(cloneFilters(filter.filters)),
|
|
29
|
+
children: [/* @__PURE__ */ jsx(ComponentRenderer, {
|
|
30
|
+
reference: filter.icon,
|
|
31
|
+
additionalProps: { className: "size-4 shrink-0" }
|
|
32
|
+
}), /* @__PURE__ */ jsx("span", { children: label })]
|
|
33
|
+
}, filter.id);
|
|
34
|
+
if (!description) return button;
|
|
35
|
+
return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: button }), /* @__PURE__ */ jsx(TooltipContent, {
|
|
36
|
+
side: "bottom",
|
|
37
|
+
align: "start",
|
|
38
|
+
children: description
|
|
39
|
+
})] }, filter.id);
|
|
40
|
+
})
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
//#endregion
|
|
45
|
+
export { QuickFilterBar };
|
|
@@ -3,6 +3,7 @@ import { cn } from "../../lib/utils.mjs";
|
|
|
3
3
|
import { selectRealtime, useAdminStore } from "../../runtime/provider.mjs";
|
|
4
4
|
import { useSafeContentLocales } from "../../runtime/content-locales-provider.mjs";
|
|
5
5
|
import { useScopedLocale } from "../../runtime/locale-scope.mjs";
|
|
6
|
+
import { resolveIconElement } from "../../components/component-renderer.mjs";
|
|
6
7
|
import { flattenOptions } from "../../components/primitives/types.mjs";
|
|
7
8
|
import { resolveOptionLabelForValue } from "../../components/primitives/option-label.mjs";
|
|
8
9
|
import { Button } from "../../components/ui/button.mjs";
|
|
@@ -14,6 +15,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "../../components/ui/too
|
|
|
14
15
|
import { Checkbox } from "../../components/ui/checkbox.mjs";
|
|
15
16
|
import { createActionRegistryProxy } from "../../builder/types/action-registry.mjs";
|
|
16
17
|
import { ActionButton } from "../../components/actions/action-button.mjs";
|
|
18
|
+
import { adminCollectionKey } from "../../hooks/query-access.mjs";
|
|
17
19
|
import { useCollectionFields } from "../../hooks/use-collection-fields.mjs";
|
|
18
20
|
import { useSuspenseCollectionMeta } from "../../hooks/use-collection-meta.mjs";
|
|
19
21
|
import { ActionDialog } from "../../components/actions/action-dialog.mjs";
|
|
@@ -44,6 +46,7 @@ import { useRealtimeHighlight } from "../../hooks/use-realtime-highlight.mjs";
|
|
|
44
46
|
import { useDeleteSavedView, useSaveView, useSavedViews } from "../../hooks/use-saved-views.mjs";
|
|
45
47
|
import { useViewState } from "../../hooks/use-view-state.mjs";
|
|
46
48
|
import { BulkActionToolbar } from "./bulk-action-toolbar.mjs";
|
|
49
|
+
import { QuickFilterBar } from "./quick-filter-bar.mjs";
|
|
47
50
|
import { Icon } from "@iconify/react";
|
|
48
51
|
import * as React from "react";
|
|
49
52
|
import { Suspense, useMemo, useState } from "react";
|
|
@@ -258,6 +261,8 @@ function mapListSchemaToConfig(list) {
|
|
|
258
261
|
const config = {};
|
|
259
262
|
if (list.columns?.length) config.columns = list.columns;
|
|
260
263
|
if (list.defaultSort) config.defaultSort = list.defaultSort;
|
|
264
|
+
if (list.defaultFilters?.length) config.defaultFilters = list.defaultFilters;
|
|
265
|
+
if (list.quickFilters?.length) config.quickFilters = list.quickFilters;
|
|
261
266
|
if (list.orderable) config.orderable = list.orderable;
|
|
262
267
|
if (Array.isArray(list.searchable) && list.searchable.length) {
|
|
263
268
|
config.searchFields = list.searchable;
|
|
@@ -398,6 +403,7 @@ function TableView(props) {
|
|
|
398
403
|
*/
|
|
399
404
|
function TableViewInner({ collection, config, viewConfig, navigate, basePath = "/admin", showSearch = true, showFilters = true, showToolbar = true, realtime, headerActions, emptyState, actionsConfig }) {
|
|
400
405
|
"use no memo";
|
|
406
|
+
const collectionKey = adminCollectionKey(collection);
|
|
401
407
|
const globalRealtimeConfig = useAdminStore(selectRealtime);
|
|
402
408
|
const { fields: resolvedFields, schema } = useCollectionFields(collection, { fallbackFields: config?.fields });
|
|
403
409
|
const { collections: uploadCollections } = useUploadCollection();
|
|
@@ -475,10 +481,16 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
475
481
|
const orderField = "order";
|
|
476
482
|
const orderDirection = typeof orderableConfig === "object" ? orderableConfig.direction ?? "asc" : "asc";
|
|
477
483
|
const orderStep = typeof orderableConfig === "object" ? orderableConfig.step ?? 10 : 10;
|
|
478
|
-
const
|
|
484
|
+
const defaultFilters = useMemo(() => resolvedListConfig?.defaultFilters ?? [], [resolvedListConfig?.defaultFilters]);
|
|
485
|
+
const viewState = useViewState(defaultColumns, useMemo(() => ({
|
|
479
486
|
realtime: resolvedRealtime,
|
|
480
|
-
groupBy: defaultGroupBy
|
|
481
|
-
|
|
487
|
+
groupBy: defaultGroupBy,
|
|
488
|
+
filters: defaultFilters
|
|
489
|
+
}), [
|
|
490
|
+
resolvedRealtime,
|
|
491
|
+
defaultGroupBy,
|
|
492
|
+
defaultFilters
|
|
493
|
+
]), collection, user?.id);
|
|
482
494
|
const effectiveRealtime = viewState.config.realtime ?? resolvedRealtime;
|
|
483
495
|
const visibleColumnsForExpansion = viewState.config.visibleColumns.length > 0 ? viewState.config.visibleColumns : defaultColumns;
|
|
484
496
|
const expandedFields = useMemo(() => autoExpandFields({
|
|
@@ -492,7 +504,7 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
492
504
|
visibleColumnsForExpansion,
|
|
493
505
|
collectionMeta?.relations
|
|
494
506
|
]);
|
|
495
|
-
const isKnownSortField = React.useCallback((field) => !!field && (field === "_title" || !!resolvedFields?.[field]), [resolvedFields]);
|
|
507
|
+
const isKnownSortField = React.useCallback((field) => !!field && (field === "_title" || field === "createdAt" || field === "updatedAt" || !!resolvedFields?.[field]), [resolvedFields]);
|
|
496
508
|
const hasOrderField = isKnownSortField(orderField);
|
|
497
509
|
const canUseOrderableSort = isOrderableEnabled && hasOrderField;
|
|
498
510
|
const effectiveSort = useMemo(() => {
|
|
@@ -666,15 +678,15 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
666
678
|
limit: 100,
|
|
667
679
|
highlights: true
|
|
668
680
|
}, { enabled: isSearching });
|
|
669
|
-
const { data: listData, isLoading: listLoading, error: listError } = useCollectionList(
|
|
681
|
+
const { data: listData, isLoading: listLoading, error: listError } = useCollectionList(collectionKey, queryOptions, { enabled: !isSearching }, { realtime: effectiveRealtime });
|
|
670
682
|
const isLoading = isSearching ? searchLoading : listLoading;
|
|
671
683
|
const isSearchActive = isSearching && searchFetching;
|
|
672
684
|
const { data: savedViewsData, isLoading: savedViewsLoading } = useSavedViews(collection, user?.id);
|
|
673
685
|
const saveViewMutation = useSaveView(collection, user?.id);
|
|
674
686
|
const deleteViewMutation = useDeleteSavedView(collection, user?.id);
|
|
675
|
-
const deleteMutation = useCollectionDelete(
|
|
676
|
-
const restoreMutation = useCollectionRestore(
|
|
677
|
-
const updateBatchMutation = useCollectionUpdateBatch(
|
|
687
|
+
const deleteMutation = useCollectionDelete(collectionKey);
|
|
688
|
+
const restoreMutation = useCollectionRestore(collectionKey);
|
|
689
|
+
const updateBatchMutation = useCollectionUpdateBatch(collectionKey);
|
|
678
690
|
const availableFields = useMemo(() => {
|
|
679
691
|
return getAllAvailableFields(resolvedFields, { meta: collectionMeta });
|
|
680
692
|
}, [resolvedFields, collectionMeta]);
|
|
@@ -875,13 +887,23 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
875
887
|
orderDirection,
|
|
876
888
|
viewState
|
|
877
889
|
]);
|
|
878
|
-
const hasViewOptionsState = hasActiveFilters || !!viewState.config.groupBy || viewState.config.visibleColumns.length !== defaultColumns.length || !!viewState.config.includeDeleted;
|
|
890
|
+
const hasViewOptionsState = hasActiveFilters || !!viewState.config.sortConfig || !!viewState.config.groupBy || viewState.config.visibleColumns.length !== defaultColumns.length || !!viewState.config.includeDeleted;
|
|
879
891
|
const clearFilters = () => {
|
|
880
892
|
viewState.setConfig({
|
|
881
893
|
...viewState.config,
|
|
882
894
|
filters: []
|
|
883
895
|
});
|
|
884
896
|
};
|
|
897
|
+
const applyQuickFilters = React.useCallback((filters) => {
|
|
898
|
+
viewState.setConfig((current) => ({
|
|
899
|
+
...current,
|
|
900
|
+
filters,
|
|
901
|
+
pagination: {
|
|
902
|
+
...current.pagination ?? { pageSize: 25 },
|
|
903
|
+
page: 1
|
|
904
|
+
}
|
|
905
|
+
}));
|
|
906
|
+
}, [viewState]);
|
|
885
907
|
const exitReorderMode = React.useCallback(() => {
|
|
886
908
|
setOptimisticOrderIds(null);
|
|
887
909
|
setIsReorderMode(false);
|
|
@@ -992,6 +1014,14 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
992
1014
|
const groupField = groupableFields.find((field) => field.name === groupBy);
|
|
993
1015
|
const collapsedGroups = new Set(viewState.config.collapsedGroups ?? []);
|
|
994
1016
|
const serverGroups = !isSearching ? listData?.groups : void 0;
|
|
1017
|
+
const iconForValue = (value) => {
|
|
1018
|
+
if (groupField?.type !== "select") return null;
|
|
1019
|
+
const options = groupField.options?.options;
|
|
1020
|
+
if (!Array.isArray(options)) return null;
|
|
1021
|
+
const option = flattenOptions(options).find((opt) => String(opt.value) === String(value));
|
|
1022
|
+
if (!option?.icon) return null;
|
|
1023
|
+
return resolveIconElement(option.icon);
|
|
1024
|
+
};
|
|
995
1025
|
if (serverGroups?.length) {
|
|
996
1026
|
const rowsById = new Map(rows.map((row) => [row.id, row]));
|
|
997
1027
|
return serverGroups.flatMap((group) => {
|
|
@@ -1003,6 +1033,7 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
1003
1033
|
type: "group",
|
|
1004
1034
|
key: groupKey,
|
|
1005
1035
|
label,
|
|
1036
|
+
icon: iconForValue(group.value),
|
|
1006
1037
|
count: group.count,
|
|
1007
1038
|
collapsed
|
|
1008
1039
|
}, ...collapsed ? [] : groupRows.map((row) => ({
|
|
@@ -1013,7 +1044,8 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
1013
1044
|
}
|
|
1014
1045
|
const groups = /* @__PURE__ */ new Map();
|
|
1015
1046
|
for (const row of rows) {
|
|
1016
|
-
const
|
|
1047
|
+
const rawValue = row.original?.[groupBy];
|
|
1048
|
+
const valueLabel = stringifyGroupValue(rawValue, groupField, resolveText, t, uiLocale, t("common.noValue"));
|
|
1017
1049
|
const groupKey = `${groupBy}:${valueLabel}`;
|
|
1018
1050
|
const group = groups.get(groupKey);
|
|
1019
1051
|
if (group) {
|
|
@@ -1022,8 +1054,9 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
1022
1054
|
}
|
|
1023
1055
|
groups.set(groupKey, {
|
|
1024
1056
|
label: valueLabel,
|
|
1057
|
+
value: rawValue,
|
|
1025
1058
|
rows: [row],
|
|
1026
|
-
sortIndex: getGroupSortIndex(
|
|
1059
|
+
sortIndex: getGroupSortIndex(rawValue, groupField)
|
|
1027
1060
|
});
|
|
1028
1061
|
}
|
|
1029
1062
|
return Array.from(groups.entries()).sort(([, a], [, b]) => a.sortIndex - b.sortIndex).flatMap(([key, group]) => {
|
|
@@ -1032,6 +1065,7 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
1032
1065
|
type: "group",
|
|
1033
1066
|
key,
|
|
1034
1067
|
label: group.label,
|
|
1068
|
+
icon: iconForValue(group.value),
|
|
1035
1069
|
count: group.rows.length,
|
|
1036
1070
|
collapsed
|
|
1037
1071
|
}, ...collapsed ? [] : group.rows.map((row) => ({
|
|
@@ -1212,6 +1246,11 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
1212
1246
|
containerClassName: "h-10"
|
|
1213
1247
|
})
|
|
1214
1248
|
}),
|
|
1249
|
+
/* @__PURE__ */ jsx(QuickFilterBar, {
|
|
1250
|
+
quickFilters: resolvedListConfig?.quickFilters,
|
|
1251
|
+
currentFilters: viewState.config.filters,
|
|
1252
|
+
onApply: applyQuickFilters
|
|
1253
|
+
}),
|
|
1215
1254
|
isReorderMode && canUseOrderableSort && /* @__PURE__ */ jsxs("div", {
|
|
1216
1255
|
className: "border-border/70 bg-muted/30 text-muted-foreground flex min-h-10 items-center justify-between gap-3 border-y px-3 py-2 font-mono text-xs",
|
|
1217
1256
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
@@ -1320,16 +1359,20 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
1320
1359
|
children: /* @__PURE__ */ jsxs("button", {
|
|
1321
1360
|
type: "button",
|
|
1322
1361
|
"aria-expanded": !entry.collapsed,
|
|
1323
|
-
className: "text-muted-foreground hover:text-foreground focus-visible:ring-ring/40 -ml-1 inline-flex min-h-8 items-center gap-2 rounded-md px-1
|
|
1362
|
+
className: "text-muted-foreground hover:text-foreground focus-visible:ring-ring/40 -ml-1 inline-flex min-h-8 items-center gap-2 rounded-md px-1 text-xs font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none",
|
|
1324
1363
|
onClick: () => viewState.toggleCollapsedGroup(entry.key),
|
|
1325
1364
|
children: [
|
|
1326
1365
|
/* @__PURE__ */ jsx(Icon, {
|
|
1327
|
-
icon:
|
|
1328
|
-
className: "size-3
|
|
1366
|
+
icon: "ph:caret-right-bold",
|
|
1367
|
+
className: cn("size-3 shrink-0 transition-transform", !entry.collapsed && "rotate-90")
|
|
1368
|
+
}),
|
|
1369
|
+
entry.icon && /* @__PURE__ */ jsx("span", {
|
|
1370
|
+
className: "size-4 shrink-0",
|
|
1371
|
+
children: entry.icon
|
|
1329
1372
|
}),
|
|
1330
1373
|
/* @__PURE__ */ jsx("span", { children: entry.label }),
|
|
1331
1374
|
groupingConfig?.showCounts !== false && /* @__PURE__ */ jsx("span", {
|
|
1332
|
-
className: "
|
|
1375
|
+
className: "text-muted-foreground/60 tabular-nums",
|
|
1333
1376
|
children: entry.count
|
|
1334
1377
|
})
|
|
1335
1378
|
]
|
|
@@ -1543,7 +1586,8 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
1543
1586
|
savedViewsLoading,
|
|
1544
1587
|
onSaveView: handleSaveView,
|
|
1545
1588
|
onDeleteView: handleDeleteView,
|
|
1546
|
-
supportsSoftDelete: collectionMeta?.softDelete ?? false
|
|
1589
|
+
supportsSoftDelete: collectionMeta?.softDelete ?? false,
|
|
1590
|
+
defaultFilters
|
|
1547
1591
|
}),
|
|
1548
1592
|
dialogAction && /* @__PURE__ */ jsx(ActionDialog, {
|
|
1549
1593
|
open: !!dialogAction,
|
|
@@ -12,13 +12,13 @@ import { Checkbox } from "../../components/ui/checkbox.mjs";
|
|
|
12
12
|
import { DateTimeInput } from "../../components/primitives/date-input.mjs";
|
|
13
13
|
import { ConfirmationDialog } from "../../components/actions/confirmation-dialog.mjs";
|
|
14
14
|
import { useGlobalFields } from "../../hooks/use-global-fields.mjs";
|
|
15
|
+
import { ReactiveFieldStatesProvider, useReactiveFields } from "../../hooks/use-reactive-fields.mjs";
|
|
15
16
|
import { AutoFormFields } from "../collection/auto-form-fields.mjs";
|
|
16
17
|
import { EmptyState } from "../../components/ui/empty-state.mjs";
|
|
17
18
|
import { HistorySidebar } from "../../components/history-sidebar.mjs";
|
|
18
19
|
import { useGlobal, useGlobalRevertVersion, useGlobalUpdate, useGlobalVersions } from "../../hooks/use-global.mjs";
|
|
19
20
|
import { useGlobalServerValidation } from "../../hooks/use-server-validation.mjs";
|
|
20
21
|
import { useSidebarSearchParam } from "../../hooks/use-sidebar-search-param.mjs";
|
|
21
|
-
import { useReactiveFields } from "../../hooks/use-reactive-fields.mjs";
|
|
22
22
|
import { useTransitionStage } from "../../hooks/use-transition-stage.mjs";
|
|
23
23
|
import { detectManyToManyRelations, hasManyToManyRelations } from "../../utils/detect-relations.mjs";
|
|
24
24
|
import { shouldHandleAdminShortcut } from "../../utils/keyboard-shortcuts.mjs";
|
|
@@ -293,7 +293,7 @@ function GlobalFormView({ global: globalName, config, viewConfig, registry, show
|
|
|
293
293
|
if (globalData) form.reset(globalData);
|
|
294
294
|
}, [form, globalData]);
|
|
295
295
|
const reactiveConfigs = React.useMemo(() => extractReactiveConfigs(globalSchema), [globalSchema]);
|
|
296
|
-
useReactiveFields({
|
|
296
|
+
const { fieldStates: reactiveFieldStates } = useReactiveFields({
|
|
297
297
|
collection: globalName,
|
|
298
298
|
mode: "global",
|
|
299
299
|
reactiveConfigs,
|
|
@@ -463,13 +463,16 @@ function GlobalFormView({ global: globalName, config, viewConfig, registry, show
|
|
|
463
463
|
t
|
|
464
464
|
})
|
|
465
465
|
] })
|
|
466
|
-
}), /* @__PURE__ */ jsx(
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
466
|
+
}), /* @__PURE__ */ jsx(ReactiveFieldStatesProvider, {
|
|
467
|
+
fieldStates: reactiveFieldStates,
|
|
468
|
+
children: /* @__PURE__ */ jsx(AutoFormFields, {
|
|
469
|
+
collection: globalName,
|
|
470
|
+
mode: "global",
|
|
471
|
+
config: resolvedConfig,
|
|
472
|
+
registry,
|
|
473
|
+
resolvedFields: schemaFields,
|
|
474
|
+
schema: globalSchema
|
|
475
|
+
})
|
|
473
476
|
})]
|
|
474
477
|
}),
|
|
475
478
|
isHistoryOpen && /* @__PURE__ */ jsx(HistorySidebar, {
|
|
@@ -163,7 +163,7 @@ function AdminLayout({ LinkComponent, activeRoute, basePath = "/admin", brandNam
|
|
|
163
163
|
}),
|
|
164
164
|
secondaryRailConfig?.placement !== "right" && secondaryRail,
|
|
165
165
|
/* @__PURE__ */ jsxs(SidebarInset, {
|
|
166
|
-
className: "qa-admin-layout__content bg-background flex h-svh flex-col overflow-hidden md:rounded-
|
|
166
|
+
className: "qa-admin-layout__content bg-background flex h-svh scrollbar-none flex-col overflow-hidden md:rounded-t-2xl",
|
|
167
167
|
children: [
|
|
168
168
|
shouldShowHeader && header && /* @__PURE__ */ jsx("header", {
|
|
169
169
|
className: "qa-admin-layout__header border-border-subtle border-b",
|
|
@@ -5,7 +5,7 @@ import { useSafeContentLocales } from "../../runtime/content-locales-provider.mj
|
|
|
5
5
|
import { ComponentRenderer } from "../../components/component-renderer.mjs";
|
|
6
6
|
import { Button } from "../../components/ui/button.mjs";
|
|
7
7
|
import { getFlagUrl } from "../../utils/locale-to-flag.mjs";
|
|
8
|
-
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem,
|
|
8
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "../../components/ui/dropdown-menu.mjs";
|
|
9
9
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../../components/ui/tooltip.mjs";
|
|
10
10
|
import { useAdminConfig } from "../../hooks/use-admin-config.mjs";
|
|
11
11
|
import { useLazyComponent } from "../../utils/use-lazy-component.mjs";
|
|
@@ -440,6 +440,7 @@ function UserFooter({ theme = "system", setTheme, showThemeToggle }) {
|
|
|
440
440
|
icon: "ph:monitor"
|
|
441
441
|
}
|
|
442
442
|
], [t]);
|
|
443
|
+
const currentThemeOption = themeOptions.find((option) => option.value === theme) ?? themeOptions[2];
|
|
443
444
|
const handleThemeChange = React.useCallback((value) => {
|
|
444
445
|
setTheme?.(value);
|
|
445
446
|
}, [setTheme]);
|
|
@@ -530,21 +531,26 @@ function UserFooter({ theme = "system", setTheme, showThemeToggle }) {
|
|
|
530
531
|
className: "size-4"
|
|
531
532
|
}), t("auth.myAccount")]
|
|
532
533
|
}),
|
|
533
|
-
shouldShowThemeToggle && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
534
|
-
|
|
535
|
-
/* @__PURE__ */
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
children: themeOptions.map((option) => /* @__PURE__ */ jsxs(DropdownMenuRadioItem, {
|
|
540
|
-
value: option.value,
|
|
541
|
-
children: [/* @__PURE__ */ jsx(Icon, {
|
|
534
|
+
shouldShowThemeToggle && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(DropdownMenuSeparator, {}), /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [/* @__PURE__ */ jsxs(DropdownMenuSubTrigger, { children: [/* @__PURE__ */ jsx(Icon, { icon: currentThemeOption.icon }), t("ui.toggleTheme")] }), /* @__PURE__ */ jsx(DropdownMenuSubContent, {
|
|
535
|
+
className: "min-w-40",
|
|
536
|
+
children: themeOptions.map((option) => /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
537
|
+
onClick: () => handleThemeChange(option.value),
|
|
538
|
+
children: [
|
|
539
|
+
/* @__PURE__ */ jsx(Icon, {
|
|
542
540
|
icon: option.icon,
|
|
543
541
|
className: "size-4"
|
|
544
|
-
}),
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
542
|
+
}),
|
|
543
|
+
/* @__PURE__ */ jsx("span", {
|
|
544
|
+
className: "flex-1",
|
|
545
|
+
children: option.label
|
|
546
|
+
}),
|
|
547
|
+
option.value === theme && /* @__PURE__ */ jsx(Icon, {
|
|
548
|
+
icon: "ph:check",
|
|
549
|
+
className: "text-foreground size-4"
|
|
550
|
+
})
|
|
551
|
+
]
|
|
552
|
+
}, option.value))
|
|
553
|
+
})] })] }),
|
|
548
554
|
hasMultipleUiLocales && /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [/* @__PURE__ */ jsxs(DropdownMenuSubTrigger, { children: [/* @__PURE__ */ jsx(Icon, { icon: "ph:globe" }), t("locale.uiLanguage")] }), /* @__PURE__ */ jsx(DropdownMenuSubContent, { children: uiLocaleOptions.map((locale) => /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
549
555
|
onClick: () => setUiLocale(locale.code),
|
|
550
556
|
children: [
|
|
@@ -10,10 +10,11 @@ function getStoredAdminTheme() {
|
|
|
10
10
|
}
|
|
11
11
|
function useManagedAdminTheme(controlledTheme, controlledSetTheme, options = {}) {
|
|
12
12
|
const enabled = options.enabled ?? true;
|
|
13
|
-
const
|
|
14
|
-
const
|
|
13
|
+
const isControlled = controlledTheme !== void 0 && controlledSetTheme !== void 0;
|
|
14
|
+
const [uncontrolledTheme, setUncontrolledTheme] = React.useState(() => controlledTheme ?? getStoredAdminTheme());
|
|
15
|
+
const theme = isControlled ? controlledTheme : uncontrolledTheme;
|
|
15
16
|
const setTheme = React.useCallback((next) => {
|
|
16
|
-
if (
|
|
17
|
+
if (isControlled) {
|
|
17
18
|
controlledSetTheme(next);
|
|
18
19
|
return;
|
|
19
20
|
}
|
|
@@ -21,7 +22,7 @@ function useManagedAdminTheme(controlledTheme, controlledSetTheme, options = {})
|
|
|
21
22
|
try {
|
|
22
23
|
window.localStorage.setItem(ADMIN_THEME_STORAGE_KEY, next);
|
|
23
24
|
} catch {}
|
|
24
|
-
}, [controlledSetTheme]);
|
|
25
|
+
}, [controlledSetTheme, isControlled]);
|
|
25
26
|
React.useEffect(() => {
|
|
26
27
|
if (!enabled) return;
|
|
27
28
|
const root = document.documentElement;
|
package/dist/client.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { EMPTY_BLOCK_CONTENT, isBlockContent } from "./client/blocks/types.mjs";
|
|
|
9
9
|
import { FocusProvider, parsePreviewFieldPath, scrollFieldIntoView, useFocus, useFocusOptional, useIsBlockFocused, useIsFieldFocused } from "./client/contexts/focus-context.mjs";
|
|
10
10
|
import { useIsDesktop, useIsMobile, useMediaQuery } from "./client/hooks/use-media-query.mjs";
|
|
11
11
|
import { buildValidationSchema, buildZodFromIntrospection, createFormSchema } from "./client/builder/validation.mjs";
|
|
12
|
+
import { useReactiveFields } from "./client/hooks/use-reactive-fields.mjs";
|
|
12
13
|
import { isAdminToPreviewMessage, isPreviewToAdminMessage } from "./client/preview/types.mjs";
|
|
13
14
|
import { useGlobal, useGlobalRevertVersion, useGlobalUpdate, useGlobalVersions } from "./client/hooks/use-global.mjs";
|
|
14
15
|
import { useSearchParamToggle } from "./client/hooks/use-search-param-toggle.mjs";
|
|
@@ -16,7 +17,6 @@ import { useSidebarSearchParam } from "./client/hooks/use-sidebar-search-param.m
|
|
|
16
17
|
import { useCollectionCreate, useCollectionDelete, useCollectionItem, useCollectionList, useCollectionRestore, useCollectionRevertVersion, useCollectionUpdate, useCollectionVersions } from "./client/hooks/use-collection.mjs";
|
|
17
18
|
import { createAdminAuthClient, useAuthClient } from "./client/hooks/use-auth.mjs";
|
|
18
19
|
import { useCurrentUser } from "./client/hooks/use-current-user.mjs";
|
|
19
|
-
import { useReactiveFields } from "./client/hooks/use-reactive-fields.mjs";
|
|
20
20
|
import { AdminLink } from "./client/components/admin-link.mjs";
|
|
21
21
|
import { page } from "./client/builder/page/page.mjs";
|
|
22
22
|
import { widget } from "./client/builder/widget/widget.mjs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime20 from "react/jsx-runtime";
|
|
2
2
|
|
|
3
3
|
//#region src/components/rich-text/rich-text-renderer.d.ts
|
|
4
4
|
/**
|
|
@@ -10,9 +10,9 @@ type TipTapNode = {
|
|
|
10
10
|
text?: string;
|
|
11
11
|
marks?: Array<{
|
|
12
12
|
type: string;
|
|
13
|
-
attrs?: Record<string,
|
|
13
|
+
attrs?: Record<string, unknown>;
|
|
14
14
|
}>;
|
|
15
|
-
attrs?: Record<string,
|
|
15
|
+
attrs?: Record<string, unknown>;
|
|
16
16
|
};
|
|
17
17
|
type TipTapDoc = {
|
|
18
18
|
type: "doc";
|
|
@@ -53,7 +53,7 @@ interface RichTextRendererProps {
|
|
|
53
53
|
/**
|
|
54
54
|
* TipTap JSON content to render
|
|
55
55
|
*/
|
|
56
|
-
content:
|
|
56
|
+
content: unknown;
|
|
57
57
|
/**
|
|
58
58
|
* Custom styles for elements
|
|
59
59
|
*/
|
|
@@ -98,6 +98,6 @@ declare function RichTextRenderer({
|
|
|
98
98
|
content,
|
|
99
99
|
styles: customStyles,
|
|
100
100
|
className
|
|
101
|
-
}: RichTextRendererProps):
|
|
101
|
+
}: RichTextRendererProps): react_jsx_runtime20.JSX.Element | null;
|
|
102
102
|
//#endregion
|
|
103
103
|
export { RichTextRenderer, RichTextStyles, TipTapDoc, TipTapNode };
|
|
@@ -30,6 +30,9 @@ const defaultStyles = {
|
|
|
30
30
|
strike: "line-through",
|
|
31
31
|
underline: "underline"
|
|
32
32
|
};
|
|
33
|
+
function isRenderableTipTapDoc(content) {
|
|
34
|
+
return !!content && typeof content === "object" && content.type === "doc" && Array.isArray(content.content);
|
|
35
|
+
}
|
|
33
36
|
/**
|
|
34
37
|
* Renders a single TipTap node
|
|
35
38
|
*/
|
|
@@ -85,7 +88,7 @@ function renderNode(node, index, styles) {
|
|
|
85
88
|
}
|
|
86
89
|
return textNode;
|
|
87
90
|
}
|
|
88
|
-
const nodeKey = `${node.type}-${
|
|
91
|
+
const nodeKey = `${node.type}-${index}`;
|
|
89
92
|
const children = node.content?.map((child, i) => renderNode(child, i, styles));
|
|
90
93
|
switch (node.type) {
|
|
91
94
|
case "doc": return /* @__PURE__ */ jsx("div", { children }, nodeKey);
|
|
@@ -198,7 +201,7 @@ function renderNode(node, index, styles) {
|
|
|
198
201
|
* ```
|
|
199
202
|
*/
|
|
200
203
|
function RichTextRenderer({ content, styles: customStyles, className }) {
|
|
201
|
-
if (!content ||
|
|
204
|
+
if (!isRenderableTipTapDoc(content) || content.content.length === 0) return null;
|
|
202
205
|
const styles = {
|
|
203
206
|
...defaultStyles,
|
|
204
207
|
...customStyles
|
package/dist/index.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { EMPTY_BLOCK_CONTENT, isBlockContent } from "./client/blocks/types.mjs";
|
|
|
9
9
|
import { FocusProvider, parsePreviewFieldPath, scrollFieldIntoView, useFocus, useFocusOptional, useIsBlockFocused, useIsFieldFocused } from "./client/contexts/focus-context.mjs";
|
|
10
10
|
import { useIsDesktop, useIsMobile, useMediaQuery } from "./client/hooks/use-media-query.mjs";
|
|
11
11
|
import { buildValidationSchema, buildZodFromIntrospection, createFormSchema } from "./client/builder/validation.mjs";
|
|
12
|
+
import { useReactiveFields } from "./client/hooks/use-reactive-fields.mjs";
|
|
12
13
|
import { isAdminToPreviewMessage, isPreviewToAdminMessage } from "./client/preview/types.mjs";
|
|
13
14
|
import { useGlobal, useGlobalRevertVersion, useGlobalUpdate, useGlobalVersions } from "./client/hooks/use-global.mjs";
|
|
14
15
|
import { useSearchParamToggle } from "./client/hooks/use-search-param-toggle.mjs";
|
|
@@ -16,7 +17,6 @@ import { useSidebarSearchParam } from "./client/hooks/use-sidebar-search-param.m
|
|
|
16
17
|
import { useCollectionCreate, useCollectionDelete, useCollectionItem, useCollectionList, useCollectionRestore, useCollectionRevertVersion, useCollectionUpdate, useCollectionVersions } from "./client/hooks/use-collection.mjs";
|
|
17
18
|
import { createAdminAuthClient, useAuthClient } from "./client/hooks/use-auth.mjs";
|
|
18
19
|
import { useCurrentUser } from "./client/hooks/use-current-user.mjs";
|
|
19
|
-
import { useReactiveFields } from "./client/hooks/use-reactive-fields.mjs";
|
|
20
20
|
import { AdminLink } from "./client/components/admin-link.mjs";
|
|
21
21
|
import { page } from "./client/builder/page/page.mjs";
|
|
22
22
|
import { widget } from "./client/builder/widget/widget.mjs";
|
package/dist/modules/admin.d.mts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { FilterOperator, FilterRule, SortConfig, ViewConfiguration } from "../shared/types/saved-views.types.mjs";
|
|
1
2
|
import { ExecuteActionRequest, ExecuteActionResponse, actionFunctions, executeAction, executeActionFn, getActionsConfig, getActionsConfigFn } from "../server/modules/admin/routes/execute-action.mjs";
|
|
2
3
|
import { PreviewTokenPayload, createPreviewFunctions, createPreviewTokenVerifier, verifyPreviewTokenDirect } from "../server/modules/admin/routes/preview.mjs";
|
|
3
4
|
import { batchReactive, fieldOptions, reactiveFunctions } from "../server/modules/admin/routes/reactive.mjs";
|
|
4
5
|
import { createFirstAdmin, isSetupRequired, setupFunctions } from "../server/modules/admin/routes/setup.mjs";
|
|
5
6
|
import { fetchWidgetData, widgetDataFunctions } from "../server/modules/admin/routes/widget-data.mjs";
|
|
6
7
|
import { AdminCollections } from "../server/modules/admin/.generated/module.mjs";
|
|
7
|
-
import { FilterOperator, FilterRule, SortConfig, ViewConfiguration } from "../shared/types/saved-views.types.mjs";
|
|
8
8
|
import { savedViewsCollection } from "../server/modules/admin-preferences/collections/saved-views.mjs";
|
|
9
9
|
import { AdminModule, adminModule, adminRoutes } from "../server/modules/admin/index.mjs";
|
|
10
10
|
export { type AdminCollections, type AdminModule, type ExecuteActionRequest, type ExecuteActionResponse, type FilterOperator, type FilterRule, type PreviewTokenPayload, type SortConfig, type ViewConfiguration, actionFunctions, adminModule, adminRoutes, batchReactive, createFirstAdmin, createPreviewFunctions, createPreviewTokenVerifier, executeAction, executeActionFn, fetchWidgetData, fieldOptions, getActionsConfig, getActionsConfigFn, isSetupRequired, reactiveFunctions, savedViewsCollection, setupFunctions, verifyPreviewTokenDirect, widgetDataFunctions };
|