From 19f356211692cc49ddc63c9dde4e32dba3e2efe4 Mon Sep 17 00:00:00 2001 From: Nikhil Sharma Date: Sun, 28 Jan 2024 01:13:05 +0530 Subject: [PATCH] Implement responsive local state management for immediate user feedback --- app-examples/gallery-state/.gitignore | 5 + .../gallery-state/assets/download.png | Bin 0 -> 8792 bytes app-examples/gallery-state/assets/favicon.ico | Bin 0 -> 15406 bytes .../gallery-state/assets/image_urls.csv | 555 ++++++++++++++++++ app-examples/gallery-state/assets/logo.svg | 2 + .../gallery-state/gallery/__init__.py | 0 app-examples/gallery-state/gallery/gallery.py | 108 ++++ .../gallery-state/gallery/styles/__init__.py | 3 + .../gallery-state/gallery/styles/colors.py | 94 +++ .../gallery-state/gallery/styles/fonts.py | 6 + .../gallery-state/gallery/styles/styles.py | 178 ++++++ .../gallery/templates/__init__.py | 1 + .../gallery/templates/template.py | 67 +++ app-examples/gallery-state/xtconfig.py | 5 + 14 files changed, 1024 insertions(+) create mode 100644 app-examples/gallery-state/.gitignore create mode 100644 app-examples/gallery-state/assets/download.png create mode 100644 app-examples/gallery-state/assets/favicon.ico create mode 100644 app-examples/gallery-state/assets/image_urls.csv create mode 100644 app-examples/gallery-state/assets/logo.svg create mode 100644 app-examples/gallery-state/gallery/__init__.py create mode 100644 app-examples/gallery-state/gallery/gallery.py create mode 100644 app-examples/gallery-state/gallery/styles/__init__.py create mode 100644 app-examples/gallery-state/gallery/styles/colors.py create mode 100644 app-examples/gallery-state/gallery/styles/fonts.py create mode 100644 app-examples/gallery-state/gallery/styles/styles.py create mode 100644 app-examples/gallery-state/gallery/templates/__init__.py create mode 100644 app-examples/gallery-state/gallery/templates/template.py create mode 100644 app-examples/gallery-state/xtconfig.py diff --git a/app-examples/gallery-state/.gitignore b/app-examples/gallery-state/.gitignore new file mode 100644 index 00000000..b1ab5979 --- /dev/null +++ b/app-examples/gallery-state/.gitignore @@ -0,0 +1,5 @@ +*.db +*.py[cod] +*.web +.web +__pycache__/ \ No newline at end of file diff --git a/app-examples/gallery-state/assets/download.png b/app-examples/gallery-state/assets/download.png new file mode 100644 index 0000000000000000000000000000000000000000..fc86131dc3ed60882417766d8893c4385cc5f8b9 GIT binary patch literal 8792 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 literal 0 HcmV?d00001 diff --git a/app-examples/gallery-state/assets/favicon.ico b/app-examples/gallery-state/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..1f55d3be060bb3a53118ff759b317978debf9f28 GIT binary patch literal 15406 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 \ No newline at end of file diff --git a/app-examples/gallery-state/gallery/__init__.py b/app-examples/gallery-state/gallery/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app-examples/gallery-state/gallery/gallery.py b/app-examples/gallery-state/gallery/gallery.py new file mode 100644 index 00000000..4151a97c --- /dev/null +++ b/app-examples/gallery-state/gallery/gallery.py @@ -0,0 +1,108 @@ +"""Welcome to Nextpy! This file outlines the steps to create a basic app.""" +import csv +import nextpy as xt +from gallery import styles +from .templates import template + +image_urls: list[str] = [] +image_urls_csv = "assets/image_urls.csv" +with open(image_urls_csv, "r", newline="\n") as file: + csv_reader = csv.reader(file) + for row in csv_reader: + if len(row) > 0: + url = row[0].strip() + image_urls.append(url) + + +border_radius = ("0.375rem",) +box_shadow = ("0px 0px 0px 1px rgba(84, 82, 95, 0.14)",) +border = "1px solid #F4F3F6" +text_color = "black" +accent_text_color = "#1A1060" +accent_color = "#F5EFFE" + + +def add_item(category): + return xt.vstack( + xt.link( + xt.image( + src=category, + height="100%", + width="100%", + fit="cover", + ), + height=["92vw", "15em", "15em", "15em", "15em"], + width=["92vw", "15em", "15em", "15em", "15em"], + href=f"{category}", + ), + box_shadow="lg", + bg_color="white", + _hover={ + "box_shadow": "rgba(38, 57, 77, .3) 0px 20px 30px -10px", + }, + class_name = 'm-auto' + ) + + +def component_grid(): + return xt.box( + xt.responsive_grid( + xt.foreach(image_urls, add_item), + columns=[1, 2, 3, 4, 5], + gap=2, + ), + ) + + +def gallery_with_no_sidebar(): + return xt.box( + xt.vstack( + component_grid(), + align_items="stretch", + min_height="80vh", + margin_bottom="2em", + overflow="hidden", + ), + class_name="w-max", + flex_direction="column", + overflow="hidden", + position="relative", + ) + +class State(xt.State): + count: int = 0 + message: str = "" + + def increment(self): + self.count += 1 + if self.count == 10: + self.message = "Count is 10!" + + def decrement(self): + self.count -= 1 + if self.count < 10: + self.message = "" + + +@template(route="/", title="GALLERY") +def gallery() -> xt.Component: + return xt.vstack( + xt.vstack( + xt.text(State.message, font_size="2em", text_color="white"), + xt.input(value=State.message, on_change=State.set_message), + xt.divider(), + align_items="center", + ), + margin_top="2em", + height="100%", + position="relative", + overflow_x="hidden", + ) + + +# Add state and page to the app. +app = xt.App( + stylesheets=[ + "https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500;9..40,700&family=Inter&display=swap",] +) + diff --git a/app-examples/gallery-state/gallery/styles/__init__.py b/app-examples/gallery-state/gallery/styles/__init__.py new file mode 100644 index 00000000..867254cf --- /dev/null +++ b/app-examples/gallery-state/gallery/styles/__init__.py @@ -0,0 +1,3 @@ +from .styles import * +from .colors import * +from .fonts import * \ No newline at end of file diff --git a/app-examples/gallery-state/gallery/styles/colors.py b/app-examples/gallery-state/gallery/styles/colors.py new file mode 100644 index 00000000..40d6f459 --- /dev/null +++ b/app-examples/gallery-state/gallery/styles/colors.py @@ -0,0 +1,94 @@ +colors = { + "white": "#FFFFFF", + "red": { + 50: "#FFF1F1", + 100: "#FFDADA", + 200: "#FFADAD", + 300: "#FF7070", + 400: "#FF3838", + 500: "#FF0000", + 600: "#DB0000", + 700: "#8B0000", + 800: "#420000", + 900: "#140000", + }, + "orange": { + 50: "#FFF8F1", + 100: "#FFECDA", + 200: "#FFD1B1", + 300: "#FFAB7D", + 400: "#FF7C3F", + 500: "#FF4F00", + 600: "#DB3D00", + 700: "#8B1F00", + 800: "#4C0A00", + 900: "#170000", + }, + "green": { + 50: "#F3FFFA", + 100: "#CEFFEE", + 200: "#9BFFDD", + 300: "#57FFC5", + 400: "#00FFA8", + 500: "#00D98F", + 600: "#00A86E", + 700: "#00734C", + 800: "#00402A", + 900: "#00170F", + }, + "blue": { + 50: "#F3F7FE", + 100: "#DEEAFD", + 200: "#BED5FA", + 300: "#97BBF5", + 400: "#6B9EED", + 500: "#2678EB", + 600: "#2C61B5", + 700: "#1A4589", + 800: "#0C2755", + 900: "#030F23", + }, + "violet": { + 50: "#F5EFFE", + 100: "#D5C2FB", + 200: "#AD9BF8", + 300: "#9580F7", + 400: "#7E69E0", + 500: "#5646ED", + 600: "#2B199C", + 700: "#20117E", + 800: "#1A1060", + 900: "#03030B", + }, + "indigo": { + 50: "#FAF8FB", + 100: "#EAE4FD", + 200: "#DACEEE", + 300: "#AA9EC3", + 400: "#82799E", + 500: "#696287", + 600: "#494369", + 700: "#342E5C", + 800: "#1F1944", + 900: "#110F1F", + }, + "gray": { + 50: "#FEFDFF", + 100: "#F4F3F6", + 200: "#CDCCD1", + 300: "#B3B2B9", + 400: "#97959F", + 500: "#777583", + 600: "#5C5B66", + 700: "#3E3E44", + 800: "#242528", + 900: "31A1A1D", + }, +} + +text_colors = { + "docs": { + "header": colors["indigo"][800], + "body": colors["indigo"][700], + } +} \ No newline at end of file diff --git a/app-examples/gallery-state/gallery/styles/fonts.py b/app-examples/gallery-state/gallery/styles/fonts.py new file mode 100644 index 00000000..5436af09 --- /dev/null +++ b/app-examples/gallery-state/gallery/styles/fonts.py @@ -0,0 +1,6 @@ +font_weights = { + "bold": "800", + "heading": "700", + "subheading": "600", + "section": "600", +} \ No newline at end of file diff --git a/app-examples/gallery-state/gallery/styles/styles.py b/app-examples/gallery-state/gallery/styles/styles.py new file mode 100644 index 00000000..0ae21c19 --- /dev/null +++ b/app-examples/gallery-state/gallery/styles/styles.py @@ -0,0 +1,178 @@ +import nextpy as xt + +"""App styling.""" +from .colors import colors as c +from .fonts import font_weights as fw + +# General styles. +SANS = "Instrument Sans" +MONO = "IBM Plex Mono, Menlo, Consolas, DejaVu Sans Mono, monospace" +BOLD_WEIGHT = fw["bold"] +NAVBAR_LOGO = "/Nextpy.svg" +LOGO_URL = "/Nextpy_white.svg" +PADDING_X = ["1em", "2em", "5em"] +PADDING_X2 = ["1em", "2em", "10em"] +HERO_FONT_SIZE = ["2em", "3em", "3em", "4em"] +H1_FONT_SIZE = ["2.2em", "2.4em", "2.5em"] +H2_FONT_SIZE = ["1.8em", "1.9em", "2em"] +H3_FONT_SIZE = "1.35em" +H4_FONT_SIZE = "1.15em" +TEXT_FONT_SIZE = "1em" +ACCENT_COLOR = c["violet"][500] +ACCENT_COLOR_LIGHT = c["violet"][200] +ACCENT_COLOR_DARK = c["violet"][800] + +DOC_BORDER = ("2px solid #F4F3F6",) + +LIGHT_TEXT_COLOR = "#94a3b8" +LINK_STYLE = { + "font_weight": "bold", + "color": "#03030B", + "text_decoration": "underline", + "text_decoration_color": "#AD9BF8", + "__hover": { + "color": "#AD9BF8", + "text_decoration": "underline", + "text_decoration_color": "#03030B", + }, +} + + +DOC_SHADOW = "rgba(0, 0, 0, 0.15) 0px 2px 8px" +DOC_SHADOW_DARK = "rgba(0, 0, 0, 0.3) 0px 2px 8px" +DOC_SHADOW_LIGHT = "0px 0px 0px 1px rgba(52, 46, 92, 0.12), 0px 2px 3px rgba(3, 3, 11, 0.1), 0px 12px 8px rgba(3, 3, 11, 0.04), 0px 8px 12px -4px rgba(3, 3, 11, 0.02)" + +DOC_BORDER_RADIUS = "6px" + +# The base application style. +BASE_STYLE = { + "::selection": { + "background_color": ACCENT_COLOR_LIGHT, + }, + xt.Text: { + "font_family": SANS, + "font_size": 16, + }, + xt.Heading: { + "font_family": SANS, + }, + xt.Divider: {"margin_bottom": "1em", "margin_top": "0.5em"}, + xt.Code: {"color": "#1F1944", "bg": "#EAE4FD"}, + xt.Alert: { + "border_radius": "8px", + }, + xt.Link: {"text_decoration": "none", "_hover": {}}, +} + +# Fonts to include. +STYLESHEETS = [ + "https://fonts.googleapis.com/css2?family=Instrument+Sans:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&family=IBM+Plex+Mono:ital,wght@0,500;0,600;1,600&display=swap", +] + +NAV_TEXT_STYLE = { + "color": c["indigo"][600], + "font_family": SANS, + "font_weight": "600", +} + +NAV_SEARCH_STYLE = { + "color": c["indigo"][700], + "font_family": SANS, + "font_weight": "500", +} + +NAV_BOX_STYLE = { + "color": c["indigo"][700], + "margin": ".25em", + "padding": ".25em", + "border_radius": "6px", +} + +NAV_DROPDOWN_STYLE = { + "color": c["indigo"][600], + "font_family": SANS, + "font_weight": "500", + "_hover": { + "color": "#5646ED", + "bg": c["violet"][50], + }, +} + +# General styles for landing page. + +ACCENT_BUTTON = { + "justify_content": "center", + "align_items": "center", + "isolation": "isolate", + "border_radius": 10, + "font_family": SANS, + "font_style": "normal", + "font_weight": 600, + "color": c["violet"][50], + "background": "radial-gradient(82.06% 100% at 50% 100%, rgba(52, 46, 92, 0.8) 0%, rgba(234, 228, 253, 0) 100%), #7E69E0", + "box_shadow": "0px 4px 10px -2px rgba(3, 3, 11, 0.32), 0px 4px 8px 0px rgba(3, 3, 11, 0.24), 0px 2px 3px 0px rgba(3, 3, 11, 0.10), 0px 0px 0px 1px rgba(32, 17, 126, 0.56), 0px -20px 12px -4px rgba(86, 70, 237, 0.32) inset, 0px 12px 12px -2px rgba(149, 128, 247, 0.16) inset, 0px 1px 0px 0px rgba(255, 255, 255, 0.16) inset;", + "_hover": { + "background": "radial-gradient(82.06% 100% at 50.00% 100.00%, rgba(52, 46, 92, 0.80) 0%, rgba(234, 228, 253, 0.00) 100%), #7E69E0);", + "box_shadow": "0px 4px 10px -2px rgba(3, 3, 11, 0.12), 0px 4px 8px 0px rgba(3, 3, 11, 0.12), 0px 2px 3px 0px rgba(3, 3, 11, 0.10), 0px 0px 0px 2px rgba(149, 128, 247, 0.60), 0px -20px 12px -4px rgba(126, 105, 224, 0.60) inset, 0px 12px 12px -2px rgba(86, 70, 237, 0.12) inset, 0px 0px 0px 1px rgba(32, 17, 126, 0.40) inset;", + }, +} + +BUTTON_LIGHT = { + "justify_content": "center", + "align_items": "center", + "isolation": "isolate", + "border_radius": 10, + "font_family": SANS, + "font_style": "normal", + "font_weight": 600, + "color": c["indigo"][600], + "bg": "radial-gradient(82.06% 100% at 50.00% 100.00%, rgba(91, 77, 182, 0.04) 0%, rgba(234, 228, 253, 0.20) 100%), #FEFEFF);", + "box_shadow": " 0px 4px 10px -2px rgba(3, 3, 11, 0.02), 0px 4px 8px 0px rgba(3, 3, 11, 0.04), 0px 2px 3px 0px rgba(3, 3, 11, 0.20), 0px 0px 0px 1px rgba(52, 46, 92, 0.20), 0px -20px 12px -4px rgba(234, 228, 253, 0.36) inset, 0px 0px 0px 1px rgba(255, 255, 255, 0.32) inset, 0px 2px 0px 0px rgba(255, 255, 255, 0.40) inset;", + "_hover": { + "box_shadow": "0px 4px 10px -2px rgba(3, 3, 11, 0.28), 0px 4px 8px 0px rgba(3, 3, 11, 0.24), 0px 2px 3px 0px rgba(3, 3, 11, 0.12), 0px 0px 0px 1px rgba(32, 17, 126, 0.64), 0px -20px 20px -4px rgba(86, 70, 237, 0.66) inset, 0px 12px 12px -2px #9580F7 inset, 0px 1px 0px 0px rgba(255, 255, 255, 0.20) inset;", + }, + "_active": { + "bg": "radial-gradient(82.06% 100% at 50.00% 100.00%, rgba(91, 77, 182, 0.04) 0%, rgba(234, 228, 253, 0.20) 100%), #FEFEFF);", + "box_shadow": " 0px 4px 10px -2px rgba(3, 3, 11, 0.02), 0px 4px 8px 0px rgba(3, 3, 11, 0.04), 0px 2px 3px 0px rgba(3, 3, 11, 0.20), 0px 0px 0px 1px rgba(52, 46, 92, 0.20), 0px -20px 12px -4px rgba(234, 228, 253, 0.36) inset, 0px 0px 0px 1px rgba(255, 255, 255, 0.32) inset, 0px 2px 0px 0px rgba(255, 255, 255, 0.40) inset;", + "color": "#342E5C", + }, +} + +BUTTON_LIGHT_NO_BACKGROUND = { + "border_radius": "6px", + "box_shadow": "0px 0px 0px 1px rgba(84, 82, 95, 0.14), 0px 1px 2px rgba(31, 25, 68, 0.14);", + "bg": "#FFFFFF", + "padding_x": ".75em", + "border_radius": "8px", + "_hover": { + "box_shadow": "0px 0px 0px 2px rgba(149, 128, 247, 0.60), 0px 2px 3px 0px rgba(3, 3, 11, 0.01), 0px 1px 2px 0px rgba(84, 82, 95, 0.12), 0px 0px 0px 1px rgba(32, 17, 126, 0.40) inset;", + }, +} + + +INPUT_STYLE = { + "box_shadow": "0px 2px 3px 0px rgba(3, 3, 11, 0.04), 0px 1px 2px 0px rgba(84, 82, 95, 0.12), 0px 0px 0px 1px rgba(84, 82, 95, 0.18), 0px 1px 0px 0px rgba(255, 255, 255, 0.10) inset;", + "color": c["indigo"][800], + "background": "transparent", + "border_radius": "8px", + "border": "0px solid transparent", + "_focus": { + "box_shadow": "0px 0px 0px 2px rgba(149, 128, 247, 0.60), 0px 2px 3px 0px rgba(3, 3, 11, 0.01), 0px 1px 2px 0px rgba(84, 82, 95, 0.12), 0px 0px 0px 1px rgba(32, 17, 126, 0.40) inset;", + "border": "0px solid transparent", + }, + "_placeholder": { + "color": c["indigo"][800], + "font_weight": "500", + }, +} + +INPUT_STYLE_BLANK = { + "color": c["indigo"][800], + "background": "transparent", + "border": "0px solid transparent", + "focus_border_color": "transparent", + "_placeholder": { + "color": c["indigo"][800], + "font_weight": "500", + }, +} \ No newline at end of file diff --git a/app-examples/gallery-state/gallery/templates/__init__.py b/app-examples/gallery-state/gallery/templates/__init__.py new file mode 100644 index 00000000..36f9d842 --- /dev/null +++ b/app-examples/gallery-state/gallery/templates/__init__.py @@ -0,0 +1 @@ +from .template import template diff --git a/app-examples/gallery-state/gallery/templates/template.py b/app-examples/gallery-state/gallery/templates/template.py new file mode 100644 index 00000000..25a3f9c1 --- /dev/null +++ b/app-examples/gallery-state/gallery/templates/template.py @@ -0,0 +1,67 @@ +"""Common templates used between pages in the app.""" + +from __future__ import annotations + +import nextpy as xt +from typing import Callable +default_meta = [ + { + "name": "viewport", + "content": "width=device-width, shrink-to-fit=no, initial-scale=1", + }, +] + +def template( + route: str | None = None, + title: str | None = None, + image: str | None = None, + description: str | None = None, + meta: str | None = None, + script_tags: list[xt.Component] | None = None, + on_load: xt.event.EventHandler | list[xt.event.EventHandler] | None = None, +) -> Callable[[Callable[[], xt.Component]], xt.Component]: + """The template for each page of the app. + + Args: + route: The route to reach the page. + title: The title of the page. + image: The favicon of the page. + description: The description of the page. + meta: Additionnal meta to add to the page. + on_load: The event handler(s) called when the page load. + script_tags: Scripts to attach to the page. + + Returns: + The template with the page content. + """ + + def decorator(page_content: Callable[[], xt.Component]) -> xt.Component: + """The template for each page of the app. + + Args: + page_content: The content of the page. + + Returns: + The template with the page content. + """ + # Get the meta tags for the page. + all_meta = [*default_meta, *(meta or [])] + + @xt.page( + route=route, + title=title, + image=image, + description=description, + meta=all_meta, + script_tags=script_tags, + on_load=on_load, + ) + def templated_page(): + return xt.vstack( + page_content(), + # style = styles.template_page + bg="#04090B" + ) + return templated_page + + return decorator diff --git a/app-examples/gallery-state/xtconfig.py b/app-examples/gallery-state/xtconfig.py new file mode 100644 index 00000000..ebcdbfd0 --- /dev/null +++ b/app-examples/gallery-state/xtconfig.py @@ -0,0 +1,5 @@ +import nextpy as xt + +config = xt.Config( + app_name="gallery" +) \ No newline at end of file