Skip to content

Commit 8e5b5bb

Browse files
committed
Django Admin Rich Editor via Astro
1 parent c4ed4ce commit 8e5b5bb

File tree

16 files changed

+275
-179
lines changed

16 files changed

+275
-179
lines changed

backend/custom_admin/src/components/invitation-letter-document-builder/editor-section.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from "@radix-ui/themes";
1111
import { Box } from "@radix-ui/themes";
1212
import { MoveDown, MoveUp, Pencil, Trash } from "lucide-react";
13-
import { Editor } from "./editor";
13+
import { RichEditor } from "../shared/rich-editor";
1414
import { useLocalData } from "./local-state";
1515

1616
export const EditorSection = ({
@@ -50,7 +50,7 @@ export const EditorSection = ({
5050
{isPage && <RemovePage onRemove={onRemove} />}
5151
</Flex>
5252
<Box height="var(--space-3)" />
53-
<Editor content={content} onUpdate={onUpdate} />
53+
<RichEditor content={content} onUpdate={onUpdate} />
5454
</Box>
5555
);
5656
};

backend/custom_admin/src/components/shared/base.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,26 @@ import { DndProvider } from "react-dnd";
44
import { HTML5Backend } from "react-dnd-html5-backend";
55

66
import "../shared/styles.css";
7+
import clsx from "clsx";
78
import { DjangoAdminEditorProvider } from "./django-admin-editor-modal";
89

910
type Props = {
1011
children: React.ReactNode;
12+
widget?: boolean;
1113
};
1214

1315
const client = new ApolloClient({
1416
uri: "/admin/graphql",
1517
cache: new InMemoryCache(),
1618
});
1719

18-
export const Base = ({ children }: Props) => {
20+
export const Base = ({ children, widget = false }: Props) => {
1921
return (
20-
<Theme>
22+
<Theme
23+
className={clsx({
24+
"is-widget-theme": widget,
25+
})}
26+
>
2127
<ApolloProvider client={client}>
2228
<DjangoAdminEditorProvider>
2329
<DndProvider backend={HTML5Backend}>{children}</DndProvider>

backend/custom_admin/src/components/shared/django-admin-layout.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import clsx from "clsx";
2-
import React, { Fragment } from "react";
32
import { getArg } from "./get-arg";
43

54
type Breadcrumb = {

backend/custom_admin/src/components/invitation-letter-document-builder/editor.tsx renamed to backend/custom_admin/src/components/shared/rich-editor/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const extensions = [
2525
}),
2626
];
2727

28-
export const Editor = ({
28+
export const RichEditor = ({
2929
content,
3030
onUpdate,
3131
}: {
@@ -46,9 +46,9 @@ export const Editor = ({
4646
});
4747

4848
return (
49-
<Card>
49+
<div className="border">
5050
<MenuBar editor={editor} />
5151
<EditorContent editor={editor} className="prose max-w-none p-4" />
52-
</Card>
52+
</div>
5353
);
5454
};

backend/custom_admin/src/components/invitation-letter-document-builder/menu-bar.tsx renamed to backend/custom_admin/src/components/shared/rich-editor/menu-bar.tsx

File renamed without changes.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { RichEditor } from "../../components/shared/rich-editor";
2+
import { Base } from "../shared/base";
3+
4+
export const RichEditorWidget = () => {
5+
return (
6+
<Base widget>
7+
<div>
8+
<RichEditor
9+
content="<p>Initial content</p>"
10+
onUpdate={(content) => console.log(content)}
11+
/>
12+
</div>
13+
</Base>
14+
);
15+
};

backend/custom_admin/src/custom-styles.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,9 @@
2121
animation: rt-dialog-content-hide 100ms cubic-bezier(0.16, 1, 0.3, 1);
2222
}
2323
}
24+
25+
.radix-themes.is-widget-theme {
26+
min-height: auto;
27+
width: 100%;
28+
height: 100%;
29+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
import "@radix-ui/themes/styles.css";
3+
import "../custom-styles.css";
4+
---
5+
<html>
6+
<body>
7+
<Fragment set:html={`
8+
{% extends 'admin/real_base.html' %}
9+
`} />
10+
</body>
11+
</html>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
import "@radix-ui/themes/styles.css";
3+
import "../../custom-styles.css";
4+
import { RichEditorWidget } from "../../components/widgets/rich-editor";
5+
---
6+
7+
<RichEditorWidget client:only />
Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,71 @@
11
import requests
2-
from django.template import TemplateDoesNotExist
32
from django.template.backends.django import (
43
DjangoTemplates,
5-
reraise,
6-
Template as BaseTemplate,
74
)
8-
from django.template import Template
5+
from django.forms.renderers import DjangoTemplates as FormsDjangoTemplates
6+
from django.template.loaders.base import Loader
7+
from django.template import Origin
98

109
from django.conf import settings
1110

1211

13-
class CustomAdminDjangoTemplate(DjangoTemplates):
12+
def _proxy_to_astro(template_name):
13+
# template_name contains astro/ in the name
14+
# which will end up in the view `astro_proxy`
15+
# which forwards the request to the astro server
16+
response = requests.get(f"http://127.0.0.1:8000/{template_name}")
17+
response.raise_for_status()
18+
return response.text
19+
20+
21+
def _can_be_proxied(template_name):
22+
if not settings.DEBUG:
23+
return False
24+
25+
return template_name == "admin/base.html" or template_name.startswith("astro/")
26+
27+
28+
class AstroContentLoader(Loader):
29+
def get_template_sources(self, template_name):
30+
# We make admin/base.html to be proxied to astro/admin-base.html
31+
# so that Astro can add styles and scripts to the page
32+
if template_name == "admin/base.html":
33+
yield Origin(
34+
name=template_name,
35+
template_name="astro/admin-base.html",
36+
loader=self,
37+
)
38+
39+
def get_contents(self, origin):
40+
template_name = origin.template_name
41+
42+
if not _can_be_proxied(template_name):
43+
return open("custom_admin/templates/" + template_name).read()
44+
45+
return _proxy_to_astro(template_name)
46+
47+
48+
class GetTemplate:
1449
def get_template(self, template_name):
15-
if not template_name.startswith("astro/") or not settings.DEBUG:
50+
if not _can_be_proxied(template_name):
1651
return super().get_template(template_name)
1752

18-
# Proxy the request to Astro
19-
response = requests.get(f"http://127.0.0.1:8000/{template_name}")
53+
text = _proxy_to_astro(template_name)
54+
55+
if hasattr(self, "from_string"):
56+
return self.from_string(text)
57+
58+
return self.engine.from_string(text)
59+
60+
61+
class CustomAdminDjangoTemplate(GetTemplate, DjangoTemplates):
62+
def __init__(self, *args, **kwargs):
63+
super().__init__(*args, **kwargs)
64+
65+
self.engine.loaders = [
66+
"custom_admin.template_backends.AstroContentLoader",
67+
] + self.engine.loaders
68+
2069

21-
try:
22-
return BaseTemplate(Template(response.text), backend=self)
23-
except TemplateDoesNotExist as exc:
24-
reraise(exc, self)
70+
class FormRenderer(GetTemplate, FormsDjangoTemplates):
71+
...

0 commit comments

Comments
 (0)