Уценка
Легкий и свободный от зависимостей способ отрисовки уценки в Reflex с использованием Tailwind Typography.
Большинство библиотек компонентов, включая Radix, поставляют компонент Markdown, который анализирует и отображает уценку на клиенте, в браузере, при каждом рендеринге. Это разумный вариант по умолчанию, но это также означает доставку анализатора клиенту и сопоставление каждого токена уценки с компонентом.
На этой странице показан другой шаблон: один раз проанализировать уценку в HTML на сервере с помощью библиотеки Python markdown, а затем передать необработанный HTML в rx.html с прикрепленным классом Tailwind Typography. Здесь нет синтаксического анализатора на стороне клиента и сопоставления компонентов с токенами, только HTML и CSS.
Это не компонент с API, который нужно изучить. Это небольшая служебная функция плюс класс CSS, которые вы должны скопировать и адаптировать.
Как это работает
Три части работают вместе:
markdown— это библиотека Python, которая преобразует строку уценки в строку HTML.- Класс prose, построенный на
@tailwindcss/typography, стилизует необработанные HTML-элементы (h1,p,table,blockquoteи т. д.) в соответствии с вашим пользовательским интерфейсом. rx.htmlотображает необработанную HTML-строку как компонент с примененным классом prose.
def render_markdown(text: str) -> rx.Component:
if not text.strip():
return rx.el.div()
html = markdown.markdown(text, extensions=["fenced_code", "tables"])
return rx.html(html, class_name="prose-content")
Вот и весь шаблон. Все, что происходит после этого момента, включая то, какие расширения включить, как стилизовать класс и вообще выполнять ли постобработку HTML, не является обязательным и зависит от вашего проекта.
Настройка
Установить уценку
pip install markdown
Расширения, о которых стоит знать:
Полный список в Python-Markdown extension docs.
Добавить типографику попутного ветра
Поскольку @tailwindcss/typography обрабатывается как файл JavaScript, его следует добавлять непосредственно в плагины rxconfig.py.
import reflex as rx
from reflex.plugins.shared_tailwind import TailwindConfig
config = rx.Config(
...,
plugins=[
rx.plugins.TailwindV4Plugin(
config=TailwindConfig(plugins=["@tailwindcss/typography"])
),
],
)
Написать урок прозы
Класс prose из Typography не имеет стиля, но имеет собственную структуру; большинство проектов добавляют класс поверх него, чтобы добавить свою собственную тему. Ниже приведен один из примеров. Считайте имя класса, цвета и интервалы заполнителями, а не требованиями.
Два правила стоит соблюдать независимо от темы, поскольку они исправляют особенности рендеринга, а не выражают мнение о стиле:
- Сброс содержимого
::before/::afterнаcodeиblockquote. Типографика по умолчанию добавляет декоративные кавычки, чего не хотят большинство нередакционных пользовательских интерфейсов. - Сброс отступов и фона
pre > code. Без него вы получите двойной фон, поскольку какpre, так иcodeвнутри него стилизованы.
@layer components {
/* name and theme this however fits your project */
.prose-content {
@apply prose dark:prose-invert max-w-none w-full;
/* fixes double-styling on code blocks */
@apply prose-pre:p-0
prose-pre:overflow-x-auto
prose-pre:before:content-none
prose-pre:after:content-none
prose-code:before:content-none
prose-code:after:content-none;
/* everything below is a styling choice, not a requirement */
@apply prose-code:rounded-md
prose-code:bg-muted
prose-code:px-1.5
prose-code:py-0.5
prose-code:text-sm
prose-pre:rounded-lg
prose-pre:bg-muted
prose-pre:border;
}
}
Собери это вместе
from components.markdown import render_markdown
def page() -> rx.Component:
return render_markdown("""
# Welcome
Some **markdown** content, rendered straight to HTML.
""")
Как выглядят элементы уценки
Все, что приведено ниже, представляет собой реальную уценку, прошедшую через render_markdown и оформленную с использованием собственного класса прозы этого сайта. Это один из возможных вариантов, а не единственный.
Заголовки
Типографские стили h1–h6 прямо из коробки. При необходимости переопределите модификаторы prose-h1:* / prose-h2:*.
# Page title
## Section heading
Форматирование текста
Маркеры кода, выделенные жирным шрифтом, курсивом и строчные, сопоставляются со своими HTML-эквивалентами.
Вы можете использовать жирный текст, курсив или inline code внутри обычного абзаца.
You can use **bold text**, _italic text_, or `inline code`.
Списки
- Маркеры списка можно изменить с помощью
prose-li:marker:*. - Упорядоченные и неупорядоченные списки наследуют стандартный типографский интервал.
- Вложенные списки работают «из коробки»
- First item
- Second item
- Nested item
Ссылки
Links наследует стандартную обработку подчеркивания при наведении в Typography. Сделайте рестайлинг с помощью prose-a:*.
Блоки кода
Блоки изолированного кода происходят из расширения fenced_code. Типографика объединяет их в пару pre > code, а исправление двойного стиля из приведенного выше CSS сохраняет эту пару чистой.
def hello(name: str) -> str:
return f"Hello, {name}!"
Блоковые кавычки
Цитата по умолчанию в Typography представляет собой курсив по левой границе. Некоторые проекты предпочитают вместо этого выноску с рамкой. Оба разумны; измените стиль с помощью prose-blockquote:*.
Блоковая цитата, оформленная в соответствии с вашим содержанием.
> A blockquote, styled however fits your content.
Необязательно: обработка переполнения таблицы
Таблицы Markdown отображаются как простые элементы <table>, которые могут переполнять свой контейнер на узких экранах или при длинном контенте. Это не характерно для данного подхода к рендерингу; это общая проблема с таблицами HTML. Но это достаточно распространено в таблицах с уценкой, поэтому на это стоит обратить внимание.
Один из способов справиться с этим — выполнить постобработку сгенерированного HTML и обернуть каждый <table> в прокручиваемый контейнер до того, как он достигнет rx.html. Это один из нескольких вариантов. С таким же успехом вы можете воспользоваться утилитой overflow-x-auto для родительского элемента, запросом контейнера CSS или оставить таблицы развернутыми, если в вашем контенте никогда не бывает широких таблиц.
from bs4 import BeautifulSoup
def wrap_tables(html: str) -> str:
soup = BeautifulSoup(html, "html.parser")
for table in soup.find_all("table"):
wrapper = soup.new_tag("div")
wrapper["class"] = "w-full overflow-x-auto"
table.wrap(wrapper)
return str(soup)
| Column A | Column B |
| -------- | -------- |
| Value 1 | Value 2 |
Если вы пойдете по этому пути, вам потребуется beautifulsoup4:
pip install beautifulsoup4
def render_markdown(text: str) -> rx.Component:
if not text.strip():
return rx.el.div()
html = markdown.markdown(text, extensions=["fenced_code", "tables"])
html = wrap_tables(html) # optional
return rx.html(html, class_name="prose-content")