Skip to content

Commit fc0858e

Browse files
committed
Added JSON preview to entities
1 parent b8bcb3a commit fc0858e

File tree

6 files changed

+74
-18
lines changed

6 files changed

+74
-18
lines changed

packages/firecms_core/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
"markdown-it": "^14.1.0",
6363
"notistack": "^3.0.2",
6464
"object-hash": "^3.0.0",
65+
"prism-react-renderer": "^2.4.1",
6566
"react-dropzone": "^14.3.5",
6667
"react-fast-compare": "^3.2.2",
6768
"react-image-file-resizer": "^0.4.8",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Highlight, themes } from "prism-react-renderer"
2+
import { useModeController } from "../hooks";
3+
4+
export function EntityJsonPreview({ values }: { values: object }) {
5+
const code = JSON.stringify(values, null, "\t");
6+
const { mode } = useModeController();
7+
8+
return <Highlight
9+
theme={mode === "dark" ? themes.vsDark : themes.vsLight}
10+
code={code}
11+
language="json">
12+
{({
13+
className,
14+
style,
15+
tokens,
16+
getLineProps,
17+
getTokenProps
18+
}) => (
19+
<pre style={{ ...style, background: "inherit" }} className={"container mx-auto p-8 rounded text-sm"}>
20+
{tokens.map((line, i) => (
21+
<div key={i} {...getLineProps({ line })} style={{ textWrap: "wrap" }}>
22+
{line.map((token, key) => (
23+
<span key={key} {...getTokenProps({ token })} />
24+
))}
25+
</div>
26+
))}
27+
</pre>
28+
)}
29+
</Highlight>
30+
}

packages/firecms_core/src/core/EntityEditView.tsx

+30-7
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ import {
1616
useFireCMSContext,
1717
useLargeLayout
1818
} from "../hooks";
19-
import { CircularProgress, cls, defaultBorderMixin, Tab, Tabs, Typography } from "@firecms/ui";
19+
import { CircularProgress, cls, CodeIcon, defaultBorderMixin, Tab, Tabs, Typography } from "@firecms/ui";
2020
import { getEntityFromCache } from "../util/entity_cache";
2121
import { EntityForm, EntityFormProps } from "../form";
2222
import { EntityEditViewFormActions } from "./EntityEditViewFormActions";
23+
import { EntityJsonPreview } from "../components/EntityJsonPreview";
2324

24-
const MAIN_TAB_VALUE = "main_##Q$SC^#S6";
25+
export const MAIN_TAB_VALUE = "__main_##Q$SC^#S6";
26+
export const JSON_TAB_VALUE = "__json";
2527

2628
export type OnUpdateParams = {
2729
entity: Entity<any>,
@@ -141,7 +143,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
141143
barActions,
142144
status,
143145
setStatus,
144-
formProps,
146+
formProps
145147
}: EntityEditViewProps<M> & {
146148
entity?: Entity<M>,
147149
cachedDirtyValues?: Partial<M>, // dirty cached entity in memory
@@ -186,7 +188,8 @@ export function EntityEditViewInner<M extends Record<string, any>>({
186188
const subcollectionsCount = subcollections?.length ?? 0;
187189
const customViews = collection.entityViews;
188190
const customViewsCount = customViews?.length ?? 0;
189-
const hasAdditionalViews = customViewsCount > 0 || subcollectionsCount > 0;
191+
const includeJsonView = true;
192+
const hasAdditionalViews = customViewsCount > 0 || subcollectionsCount > 0 || includeJsonView;
190193

191194
const {
192195
resolvedEntityViews,
@@ -229,6 +232,17 @@ export function EntityEditViewInner<M extends Record<string, any>>({
229232

230233
const globalLoading = dataLoading && !usedEntity;
231234

235+
const jsonView = <div
236+
className={cls("relative flex-1 h-full overflow-auto w-full",
237+
{ "hidden": selectedTab !== JSON_TAB_VALUE })}
238+
key={"json_view"}
239+
role="tabpanel">
240+
<ErrorBoundary>
241+
<EntityJsonPreview
242+
values={formContext?.values ?? {}}/>
243+
</ErrorBoundary>
244+
</div>;
245+
232246
const subCollectionsViews = subcollections && subcollections.map((subcollection) => {
233247
const subcollectionId = subcollection.id ?? subcollection.path;
234248
const fullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollectionId)}` : undefined;
@@ -269,7 +283,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
269283
path,
270284
entityId,
271285
selectedTab: value === MAIN_TAB_VALUE ? undefined : value,
272-
collection,
286+
collection
273287
});
274288
}
275289
};
@@ -303,7 +317,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
303317
onSaved={(params) => {
304318
const res = {
305319
...params,
306-
selectedTab: MAIN_TAB_VALUE === selectedTab ? undefined : selectedTab,
320+
selectedTab: MAIN_TAB_VALUE === selectedTab ? undefined : selectedTab
307321
};
308322
onSaved?.(res);
309323
formProps?.onSaved?.(res);
@@ -350,13 +364,22 @@ export function EntityEditViewInner<M extends Record<string, any>>({
350364
onSideTabClick(value);
351365
}}>
352366

367+
{includeJsonView && <Tab
368+
disabled={!hasAdditionalViews}
369+
value={JSON_TAB_VALUE}
370+
innerClassName={"block"}
371+
className={"text-sm"}>
372+
<CodeIcon size={"small"}/>
373+
</Tab>}
374+
353375
<Tab
354376
disabled={!hasAdditionalViews}
355377
value={MAIN_TAB_VALUE}
356378
className={"text-sm min-w-[120px]"}>
357379
{collection.singularName ?? collection.name}
358380
</Tab>
359381

382+
360383
{customViewTabs}
361384

362385
{subcollectionTabs}
@@ -369,7 +392,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
369392
</div>
370393
: entityView}
371394

372-
{/*{secondaryForms}*/}
395+
{jsonView}
373396

374397
{customViewsView}
375398

packages/firecms_core/src/internal/useBuildSideEntityController.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
import { ADDITIONAL_TAB_WIDTH, CONTAINER_FULL_WIDTH, FORM_CONTAINER_WIDTH } from "./common";
2121
import { useCustomizationController, useLargeLayout } from "../hooks";
2222
import { EntitySidePanel } from "../core/EntitySidePanel";
23+
import { JSON_TAB_VALUE } from "../core/EntityEditView";
2324

2425
const NEW_URL_HASH = "new_side";
2526
const SIDE_URL_HASH = "side";
@@ -31,7 +32,7 @@ export function getEntityViewWidth(props: EntitySidePanelProps<any>, small: bool
3132
selectedSecondaryForm
3233
} = resolvedSelectedEntityView(props.collection?.entityViews, customizationController, props.selectedTab);
3334

34-
const shouldUseSmallLayout = !props.selectedTab || Boolean(selectedSecondaryForm);
35+
const shouldUseSmallLayout = !props.selectedTab || props.selectedTab === JSON_TAB_VALUE || Boolean(selectedSecondaryForm);
3536

3637
let resolvedWidth: string | undefined;
3738
if (props.width) {

packages/ui/src/components/Button.tsx

+10-9
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,22 @@
22
import React from "react";
33
import { cls } from "../util";
44

5-
export type ButtonProps<P extends React.ElementType> =
6-
Omit<(P extends "button" ? React.ButtonHTMLAttributes<HTMLButtonElement> : React.ComponentProps<P>), "onClick">
7-
& {
5+
export type ButtonProps<C extends React.ElementType = "button"> = {
6+
children?: React.ReactNode;
87
variant?: "filled" | "neutral" | "outlined" | "text";
98
disabled?: boolean;
109
color?: "primary" | "secondary" | "text" | "error";
1110
size?: "small" | "medium" | "large" | "xl" | "2xl";
1211
startIcon?: React.ReactNode;
1312
fullWidth?: boolean;
1413
className?: string;
15-
onClick?: React.MouseEventHandler<any>
16-
};
14+
component?: C;
15+
onClick?: React.MouseEventHandler<any>;
16+
} & React.ComponentPropsWithoutRef<C>;
1717

1818
const ButtonInner = React.forwardRef<
19-
ButtonProps<React.ElementType<any>>
19+
HTMLButtonElement,
20+
ButtonProps<React.ElementType>
2021
>(({
2122
children,
2223
className,
@@ -44,8 +45,8 @@ const ButtonInner = React.forwardRef<
4445
"border border-surface-accent-200 bg-surface-accent-200 hover:bg-surface-accent-300 focus:ring-surface-accent-400 shadow hover:ring-1 hover:ring-surface-accent-400 text-text-primary hover:text-text-primary dark:text-text-primary-dark hover:dark:text-text-primary-dark": variant === "filled" && color === "text" && !disabled,
4546

4647
// Text Variants
47-
"border border-transparent text-primary hover:text-primary hover:bg-surface-accent-200 dark:hover:bg-surface-900": variant === "text" && color === "primary" && !disabled,
48-
"border border-transparent text-secondary hover:text-secondary hover:bg-secondary-bg": variant === "text" && color === "secondary" && !disabled,
48+
"border border-transparent text-primary hover:text-primary hover:bg-surface-accent-200 hover:bg-opacity-75 dark:hover:bg-surface-accent-800": variant === "text" && color === "primary" && !disabled,
49+
"border border-transparent text-secondary hover:text-secondary hover:bg-surface-accent-200 hover:bg-opacity-75 dark:hover:bg-surface-accent-800": variant === "text" && color === "secondary" && !disabled,
4950
"border border-transparent text-red-500 hover:text-red-500 hover:bg-red-500 hover:bg-opacity-10": variant === "text" && color === "error" && !disabled,
5051
"border border-transparent text-text-primary hover:text-text-primary dark:text-text-primary-dark hover:dark:text-text-primary-dark hover:bg-surface-accent-200 hover:dark:bg-surface-700": variant === "text" && color === "text" && !disabled,
5152

@@ -83,7 +84,7 @@ const ButtonInner = React.forwardRef<
8384
ref={ref}
8485
onClick={props.onClick}
8586
className={cls(startIcon ? "pl-3" : "", baseClasses, buttonClasses, sizeClasses, className)}
86-
{...(props as React.ComponentPropsWithRef<any>)}>
87+
{...props}>
8788
{startIcon}
8889
{children}
8990
</Component>

packages/ui/src/components/Menubar.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export function MenubarTrigger({
4444
return (
4545
<MenubarPrimitive.Trigger
4646
onSelect={onSelect}
47-
className={cls("py-2 px-3 outline-none select-none font-medium leading-none rounded text-text-primary dark:text-text-primary-dark text-[13px] flex items-center justify-between gap-[2px] data-[highlighted]:bg-surface-accent-100 data-[highlighted]:dark:bg-surface-800 data-[state=open]:bg-surface-accent-100 data-[state=open]:dark:bg-surface-800 hover:bg-surface-accent-200 hover:bg-opacity-75 dark:hover:bg-surface-700 dark:hover:bg-opacity-50",
47+
className={cls("py-2 px-3 outline-none select-none font-medium leading-none rounded text-text-primary dark:text-text-primary-dark text-[13px] flex items-center justify-between gap-[2px] data-[highlighted]:bg-surface-accent-100 data-[highlighted]:dark:bg-surface-800 data-[state=open]:bg-surface-accent-100 data-[state=open]:dark:bg-surface-800 hover:bg-surface-accent-200 hover:bg-opacity-75 dark:hover:bg-surface-accent-800",
4848
className)}>
4949
{children}
5050
</MenubarPrimitive.Trigger>

0 commit comments

Comments
 (0)