From 319b550e9f2fa19d2f7abd38fc6eaba6d9701256 Mon Sep 17 00:00:00 2001 From: daya0576 Date: Mon, 3 Feb 2025 11:00:51 +0800 Subject: [PATCH] Revert responsive layout --- README.md | 4 +- beaverhabits/configs.py | 2 +- beaverhabits/frontend/index_page.py | 138 ++++++++++------------------ 3 files changed, 54 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index 2635221..f4c2246 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,8 @@ To avoid [permission issues](https://github.com/daya0576/beaverhabits/discussion | **MAX_USER_COUNT**(int) | By setting it to `1`, you can prevent others from signing up in the future. | | **ENABLE_IOS_STANDALONE**(bool) | Experiential feature to enable standalone mode on iOS. The default setting is `false`. | | **INDEX_SHOW_HABIT_COUNT**(bool) | To display total completed count along with the habit name on the index page. The default setting is `false`. | -| **INDEX_HABIT_DATE_COLUMNS**(int) | Customize the date columns count for the index page. The default value is `5`. | -| **INDEX_HABIT_NAME_WIDTH**(int) | Customize the habit name width for the index page. The default value is `115`. | +| **INDEX_HABIT_NAME_COLUMNS**(int) | Customize the habit name column count for the index page. The default value is `5`. | +| **INDEX_HABIT_DATE_COLUMNS**(int) | Customize the date column count for the index page. The default value is `2`. | | **TRUSTED_EMAIL_HEADER**(str) | Delegate authentication to an authenticating reverse proxy that passes in the user's details in HTTP headers, e.g. `Cf-Access-Authenticated-User-Email`. An existing account is required. | | **TRUSTED_LOCAL_EMAIL**(str) | Disables authentication entirely. A new account with the specified email will be created if it does not exist. | diff --git a/beaverhabits/configs.py b/beaverhabits/configs.py index ec33a06..84eb729 100644 --- a/beaverhabits/configs.py +++ b/beaverhabits/configs.py @@ -44,7 +44,7 @@ class Settings(BaseSettings): ENABLE_DESKTOP_ALGIN_CENTER: bool = True INDEX_SHOW_HABIT_COUNT: bool = False - INDEX_HABIT_NAME_WIDTH: int = 115 + INDEX_HABIT_NAME_COLUMNS: int = 2 INDEX_HABIT_DATE_COLUMNS: int = 5 def is_dev(self): diff --git a/beaverhabits/frontend/index_page.py b/beaverhabits/frontend/index_page.py index 477ea9a..fbeed65 100644 --- a/beaverhabits/frontend/index_page.py +++ b/beaverhabits/frontend/index_page.py @@ -1,7 +1,6 @@ import datetime import os -from contextlib import contextmanager -from typing import Iterable +from typing import List from nicegui import ui @@ -12,113 +11,78 @@ from beaverhabits.storage.meta import get_root_path from beaverhabits.storage.storage import Habit, HabitList, HabitListBuilder, HabitStatus -HABIT_LIST_RECORD_COUNT = settings.INDEX_HABIT_DATE_COLUMNS -LEFT_ITEM_CLASSES = ( - f"w-[{settings.INDEX_HABIT_NAME_WIDTH}px] " - f"sm:w-[{settings.INDEX_HABIT_NAME_WIDTH + 13}px]" - "wrap self-center" -) -RIGHT_ITEM_CLASSES = "w-10 self-center" +def grid(columns, rows): + g = ui.grid(columns=columns, rows=rows) + g.classes("w-full gap-0 items-center") + return g def week_headers(days: list[datetime.date]): - if settings.INDEX_SHOW_HABIT_COUNT: - yield "Sum" for day in days: yield day.strftime("%a") + if settings.INDEX_SHOW_HABIT_COUNT: + yield "Sum" def day_headers(days: list[datetime.date]): - if settings.INDEX_SHOW_HABIT_COUNT: - yield "#" for day in days: yield day.strftime("%d") + if settings.INDEX_SHOW_HABIT_COUNT: + yield "#" -@contextmanager -def row(): - with ui.row().classes("pl-4 pr-1 py-0").classes("no-wrap gap-0"): - yield - - -@contextmanager -def card(): - with ui.card().classes("shadow-none gap-1.5 p-0"): - with row(): - yield - - -@contextmanager -def flex(height: int): - # Responsive flex container - with ui.element("div") as f: - # Auto hide flex items when it overflows the flex parent - f.classes("flex flex-row-reverse justify-start") - # Auto ajust gap with screen size - f.classes("gap-x-[5px] sm:gap-x-[8px]") - f.classes(f"overflow-hidden h-{height}") - yield f - - -def name(habit: Habit): - # truncate name - redirect_page = os.path.join(get_root_path(), "habits", str(habit.id)) - name = link(habit.name, target=redirect_page) - name.classes(LEFT_ITEM_CLASSES) - - -def headers(labels: Iterable[str]): - with flex(4): - for text in labels: - label = ui.label(text) - label.classes(RIGHT_ITEM_CLASSES) - label.style( - "font-size: 85%; font-weight: 500; color: #9e9e9e; text-align: center" - ) +@ui.refreshable +def habit_list_ui(days: list[datetime.date], active_habits: List[Habit]): + # Calculate column count + name_columns, date_columns = 2 * settings.INDEX_HABIT_NAME_COLUMNS, 2 + count_columns = 2 if settings.INDEX_SHOW_HABIT_COUNT else 0 + columns = name_columns + len(days) * date_columns + count_columns + + row_compat_classes = "pl-4 pr-0 py-0" + left_classes, right_classes = ( + # grid 4 + f"col-span-{name_columns} truncate", + # grid 2 2 2 2 2 + f"col-span-{date_columns} px-1.5 place-self-center", + ) + header_styles = "font-size: 85%; font-weight: 500; color: #9e9e9e" + + with ui.column().classes("gap-1.5"): + # Date Headers + with grid(columns, 2).classes(row_compat_classes): + for it in (week_headers(days), day_headers(days)): + ui.label("").classes(left_classes) + for label in it: + ui.label(label).classes(right_classes).style(header_styles) + # Habit List + for habit in active_habits: + with ui.card().classes(row_compat_classes).classes("shadow-none"): + with grid(columns, 1): + # truncate name + root_path = get_root_path() + redirect_page = os.path.join(root_path, "habits", str(habit.id)) + name = link(habit.name, target=redirect_page).classes(left_classes) + name.style(f"max-width: {52 * name_columns / date_columns}px;") -def checkboxes(habit: Habit, days: list[datetime.date]): - with flex(10): - if settings.INDEX_SHOW_HABIT_COUNT: - with ui.element("div").classes("w-10 h-10 flex place-content-center"): - IndexBadge(habit) + ticked_days = set(habit.ticked_days) + for day in days: + checkbox = HabitCheckBox(habit, day, day in ticked_days) + checkbox.classes(right_classes) - ticked_days = set(habit.ticked_days) - for day in days: - checkbox = HabitCheckBox(habit, day, day in ticked_days) - checkbox.classes(RIGHT_ITEM_CLASSES) + if settings.INDEX_SHOW_HABIT_COUNT: + IndexBadge(habit).classes(right_classes) -@ui.refreshable -def habit_list_ui(days: list[datetime.date], habit_list: HabitList): - active_habits = HabitListBuilder(habit_list).status(HabitStatus.ACTIVE).build() +def index_page_ui(days: list[datetime.date], habits: HabitList): + active_habits = HabitListBuilder(habits).status(HabitStatus.ACTIVE).build() if not active_habits: - ui.label("List is empty.") + ui.label("List is empty.").classes("mx-auto w-80") return - days = list(reversed(days)) - - # Date Headers - with ui.column().classes("gap-2"): - with ui.column().classes("gap-0.5"): - for it in (week_headers(days), day_headers(days)): - with row(): - ui.label("").classes(LEFT_ITEM_CLASSES) - headers(it) - - with ui.column().classes("gap-1.5"): - # Habit List - for habit in active_habits: - with card(): - # Truncated habit name - name(habit) - checkboxes(habit, days) - - -def index_page_ui(days: list[datetime.date], habits: HabitList): with layout(): - habit_list_ui(days, habits) + habit_list_ui(days, active_habits) # Prevent long press context menu for svg image elements ui.context.client.on_connect(javascript.prevent_context_menu)