|
1 | 1 | import flexdown |
2 | 2 | import reflex as rx |
| 3 | +import reflex_ui as ui |
3 | 4 |
|
| 5 | +from pcweb.constants import REFLEX_ASSETS_CDN |
4 | 6 | from pcweb.styles.colors import c_color |
5 | 7 | from pcweb.styles.fonts import base, code |
6 | 8 | from pcweb.templates.docpage import ( |
@@ -510,31 +512,123 @@ class QuoteBlock(flexdown.blocks.MarkdownBlock): |
510 | 512 |
|
511 | 513 | include_indicators = True |
512 | 514 |
|
513 | | - def render(self, env) -> rx.Component: |
| 515 | + def _parse(self, env) -> dict[str, str]: |
514 | 516 | lines = self.get_lines(env) |
515 | 517 | quote_content = [] |
516 | | - name = "" |
517 | | - role = "" |
| 518 | + data = { |
| 519 | + "name": "", |
| 520 | + "role": "", |
| 521 | + "image": "", |
| 522 | + "variant": "small", |
| 523 | + } |
| 524 | + |
518 | 525 | for line in lines[1:-1]: # Skip the first and last lines (indicators) |
519 | 526 | if line.startswith("- name:"): |
520 | | - name = line.split(":", 1)[1].strip() |
| 527 | + data["name"] = line.split(":", 1)[1].strip() |
521 | 528 | elif line.startswith("- role:"): |
522 | | - role = line.split(":", 1)[1].strip() |
| 529 | + data["role"] = line.split(":", 1)[1].strip() |
| 530 | + elif line.startswith("- image:"): |
| 531 | + data["image"] = line.split(":", 1)[1].strip() |
| 532 | + elif line.startswith("- variant:"): |
| 533 | + data["variant"] = line.split(":", 1)[1].strip().lower() |
523 | 534 | else: |
524 | 535 | quote_content.append(line) |
525 | 536 |
|
526 | | - quote_text = "\n".join(quote_content).strip() |
| 537 | + data["quote_text"] = "\n".join(quote_content).strip() |
| 538 | + return data |
527 | 539 |
|
528 | | - return rx.box( |
529 | | - rx.text(f'"{quote_text}"', class_name="text-slate-11 font-base italic"), |
530 | | - rx.box( |
531 | | - rx.text(name, class_name="text-slate-11 font-base"), |
532 | | - rx.text(role, class_name="text-slate-10 font-base"), |
533 | | - class_name="flex flex-col gap-0.5", |
| 540 | + def _author(self, name: str, role: str, class_name: str = "") -> rx.Component: |
| 541 | + return rx.el.div( |
| 542 | + rx.el.span( |
| 543 | + name, |
| 544 | + class_name="text-xs font-mono uppercase font-[415] text-m-slate-12 dark:text-m-slate-3", |
| 545 | + ), |
| 546 | + rx.el.span( |
| 547 | + role, |
| 548 | + class_name="text-xs font-mono font-[415] text-m-slate-7 dark:text-m-slate-6 uppercase", |
| 549 | + ), |
| 550 | + class_name=ui.cn("flex flex-col gap-0.5", class_name), |
| 551 | + ) |
| 552 | + |
| 553 | + def _avatar( |
| 554 | + self, name: str, image: str, class_name: str = "" |
| 555 | + ) -> rx.Component | None: |
| 556 | + if not image: |
| 557 | + return None |
| 558 | + avatar_class = ui.cn("rounded-full object-cover aspect-square", class_name) |
| 559 | + return rx.image( |
| 560 | + src=f"{REFLEX_ASSETS_CDN}case_studies/people/{image}", |
| 561 | + alt=f"{name} profile picture", |
| 562 | + class_name=avatar_class, |
| 563 | + ) |
| 564 | + |
| 565 | + def _render_medium(self, data: dict[str, str]) -> rx.Component: |
| 566 | + return rx.el.div( |
| 567 | + rx.el.div( |
| 568 | + self._avatar(data["name"], data["image"], class_name="size-6"), |
| 569 | + class_name="p-4 shrink-0 lg:border-r border-m-slate-6 dark:border-m-slate-7 border-dashed max-lg:border-b", |
| 570 | + ), |
| 571 | + rx.el.span( |
| 572 | + f'"{data["quote_text"]}"', |
| 573 | + class_name="text-m-slate-12 dark:text-m-slate-3 text-base font-[575] p-4 bg-white-1 dark:bg-m-slate-11", |
| 574 | + ), |
| 575 | + class_name="flex lg:flex-row flex-col border border-dashed border-m-slate-6 dark:border-m-slate-7 mt-2 mb-6 rounded-lg overflow-hidden", |
| 576 | + ) |
| 577 | + |
| 578 | + def _render_small(self, data: dict[str, str]) -> rx.Component: |
| 579 | + return rx.el.div( |
| 580 | + rx.el.span( |
| 581 | + f'"{data["quote_text"]}"', |
| 582 | + class_name="text-m-slate-12 dark:text-m-slate-3 text-lg font-[575] p-6 lg:border-r border-m-slate-6 dark:border-m-slate-7 border-dashed max-lg:border-b bg-white-1 dark:bg-m-slate-11", |
| 583 | + ), |
| 584 | + rx.el.div( |
| 585 | + rx.el.div( |
| 586 | + self._author(data["name"], data["role"]), |
| 587 | + class_name="text-end text-nowrap", |
| 588 | + ), |
| 589 | + self._avatar(data["name"], data["image"], class_name="size-14"), |
| 590 | + class_name="flex flex-row gap-6 items-center p-6 shrink-0", |
534 | 591 | ), |
535 | | - class_name="flex flex-col gap-4 border-l-[3px] border-slate-4 pl-6 mt-2 mb-6", |
| 592 | + class_name="flex lg:flex-row flex-col border border-dashed border-m-slate-6 dark:border-m-slate-7 mt-2 mb-6 rounded-lg overflow-hidden", |
536 | 593 | ) |
537 | 594 |
|
| 595 | + def _render_big(self, data: dict[str, str]) -> rx.Component: |
| 596 | + return rx.el.div( |
| 597 | + rx.el.div( |
| 598 | + rx.el.span( |
| 599 | + f"{data['quote_text']}", |
| 600 | + class_name="text-m-slate-12 dark:text-m-slate-3 text-2xl font-[575]", |
| 601 | + ), |
| 602 | + rx.el.div( |
| 603 | + self._avatar(data["name"], data["image"], class_name="size-6"), |
| 604 | + self._author( |
| 605 | + data["name"], |
| 606 | + data["role"], |
| 607 | + class_name="flex-row gap-3.5 items-center", |
| 608 | + ), |
| 609 | + class_name="flex flex-row gap-3.5 items-center", |
| 610 | + ), |
| 611 | + class_name="flex flex-col gap-12 pr-[12.5rem] relative z-10", |
| 612 | + ), |
| 613 | + rx.image( |
| 614 | + src=f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/quote_squares.svg", |
| 615 | + loading="lazy", |
| 616 | + alt="Quote icon", |
| 617 | + class_name="absolute right-0 inset-y-0 h-[calc(100%)] min-h-full w-auto origin-right pointer-events-none object-contain object-right", |
| 618 | + ), |
| 619 | + class_name="flex flex-col dark:border bg-white-1 dark:bg-m-slate-11 dark:border-m-slate-9 mt-2 mb-6 overflow-hidden shadow-[0_0_0_1px_rgba(0,0,0,0.12)_inset,0_6px_12px_0_rgba(0,0,0,0.06),0_1px_1px_0_rgba(0,0,0,0.01),0_4px_6px_0_rgba(0,0,0,0.02)] rounded-xl py-8 px-8 relative", |
| 620 | + ) |
| 621 | + |
| 622 | + def render(self, env) -> rx.Component: |
| 623 | + data = self._parse(env) |
| 624 | + renderers = { |
| 625 | + "small": self._render_small, |
| 626 | + "medium": self._render_medium, |
| 627 | + "big": self._render_big, |
| 628 | + } |
| 629 | + renderer = renderers.get(data["variant"], self._render_small) |
| 630 | + return renderer(data) |
| 631 | + |
538 | 632 |
|
539 | 633 | class TabsBlock(flexdown.blocks.Block): |
540 | 634 | """A block that displays content in tabs.""" |
|
0 commit comments