From 4faa131878b3f6c95493672af2a1dddf727dab9f Mon Sep 17 00:00:00 2001 From: Nathan Panchout Date: Sun, 25 Jan 2026 20:35:14 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8(front)=20update=20theme=20and=20st?= =?UTF-8?q?yles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update theme customization hook and CalendarList styles for improved visual consistency and dark mode support. Co-Authored-By: Claude Opus 4.5 --- .../calendar/components/CalendarList.scss | 315 +++++++++++++++++- .../src/hooks/useThemeCustomization.tsx | 9 +- 2 files changed, 310 insertions(+), 14 deletions(-) diff --git a/src/frontend/apps/calendars/src/features/calendar/components/CalendarList.scss b/src/frontend/apps/calendars/src/features/calendar/components/CalendarList.scss index c960800..4a73770 100644 --- a/src/frontend/apps/calendars/src/features/calendar/components/CalendarList.scss +++ b/src/frontend/apps/calendars/src/features/calendar/components/CalendarList.scss @@ -12,12 +12,38 @@ margin-bottom: 0.5rem; } - &__section-title { - font-size: 0.75rem; - font-weight: 600; + &__toggle-btn { + display: flex; + align-items: center; + gap: 0.25rem; + background: transparent; + border: none; + cursor: pointer; + padding: 0.25rem 0; + + &:hover { + .calendar-list__section-title { + color: var(--c--theme--colors--greyscale-700); + } + } + } + + &__toggle-icon { + font-size: 1.25rem; color: var(--c--theme--colors--greyscale-600); - text-transform: uppercase; - letter-spacing: 0.025em; + transition: transform 0.2s ease; + transform: rotate(-90deg); + + &--expanded { + transform: rotate(0deg); + } + } + + &__section-title { + font-size: 0.875rem; + font-weight: 500; + color: var(--c--theme--colors--greyscale-800); + transition: color 0.15s; } &__add-btn { @@ -46,19 +72,23 @@ &__items { display: flex; flex-direction: column; - gap: 0.25rem; + gap: 0.125rem; } &__item { display: flex; align-items: center; gap: 0.5rem; - padding: 0.375rem 0.25rem; + padding: 0.375rem 0.5rem; border-radius: 4px; transition: background-color 0.15s; &:hover { background-color: var(--c--theme--colors--greyscale-50); + + .calendar-list__options-btn { + opacity: 1; + } } // Override Cunningham checkbox styles for compact display @@ -71,9 +101,16 @@ } } + &__item-checkbox { + display: flex; + align-items: center; + gap: 0.25rem; + flex-shrink: 0; + } + &__color { - width: 0.75rem; - height: 0.75rem; + width: 0.25rem; + height: 1.25rem; border-radius: 2px; flex-shrink: 0; } @@ -81,12 +118,87 @@ &__name { flex: 1; font-size: 0.875rem; + font-weight: 500; color: var(--c--theme--colors--greyscale-800); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + &__item-actions { + position: relative; + display: flex; + align-items: center; + } + + &__options-btn { + display: flex; + align-items: center; + justify-content: center; + width: 1.5rem; + height: 1.5rem; + border: none; + background: transparent; + border-radius: 4px; + cursor: pointer; + color: var(--c--theme--colors--greyscale-500); + opacity: 0; + transition: opacity 0.15s, background-color 0.15s, color 0.15s; + + &:hover { + background-color: var(--c--theme--colors--greyscale-200); + color: var(--c--theme--colors--greyscale-700); + } + + .material-icons { + font-size: 1.125rem; + } + } + + &__menu { + position: absolute; + top: 100%; + right: 0; + z-index: 100; + min-width: 140px; + background: white; + border: 1px solid var(--c--theme--colors--greyscale-200); + border-radius: 6px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + padding: 0.25rem 0; + margin-top: 0.25rem; + } + + &__menu-item { + display: flex; + align-items: center; + gap: 0.5rem; + width: 100%; + padding: 0.5rem 0.75rem; + border: none; + background: transparent; + cursor: pointer; + font-size: 0.875rem; + color: var(--c--theme--colors--greyscale-700); + transition: background-color 0.15s; + + &:hover { + background-color: var(--c--theme--colors--greyscale-50); + } + + .material-icons { + font-size: 1rem; + } + + &--danger { + color: var(--c--theme--colors--danger-500); + + &:hover { + background-color: var(--c--theme--colors--danger-50); + } + } + } + &__badge { font-size: 0.625rem; padding: 0.125rem 0.375rem; @@ -97,3 +209,188 @@ font-weight: 500; } } + +// ============================================================================ +// Calendar Modal Styles (for use with Cunningham Modal) +// ============================================================================ + +.calendar-modal { + &__content { + display: flex; + flex-direction: column; + gap: 1rem; + } + + &__error { + margin-bottom: 8px; + padding: 12px 16px; + background-color: #fef2f2; + color: #dc2626; + border-radius: 8px; + font-size: 0.875rem; + border: 1px solid #fecaca; + } + + &__success { + margin-bottom: 8px; + padding: 12px 16px; + background-color: #f0fdf4; + color: #16a34a; + border-radius: 8px; + font-size: 0.875rem; + border: 1px solid #bbf7d0; + } + + &__field { + margin-bottom: 0; + } + + &__label { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 8px; + font-weight: 500; + font-size: 0.875rem; + color: #374151; + + .material-icons { + font-size: 1.125rem; + color: var(--c--theme--colors--greyscale-500); + } + } + + &__colors { + display: flex; + gap: 10px; + flex-wrap: wrap; + } + + &__color-btn { + width: 32px; + height: 32px; + border: 3px solid transparent; + border-radius: 50%; + cursor: pointer; + transition: transform 0.15s, box-shadow 0.15s; + + &:hover { + transform: scale(1.15); + } + + &--selected { + border-color: #1f2937; + box-shadow: 0 0 0 2px white, 0 0 0 4px #9ca3af; + } + } + + &__share-section { + margin-top: 0.5rem; + } + + &__share-divider { + height: 1px; + background-color: var(--c--theme--colors--greyscale-200); + margin-bottom: 1rem; + } + + &__share-input-row { + display: flex; + gap: 0.5rem; + align-items: flex-start; + + > div:first-child { + flex: 1; + } + + > button { + flex-shrink: 0; + margin-top: 0.25rem; + } + } + + &__share-hint { + margin-top: 0.5rem; + font-size: 0.75rem; + color: var(--c--theme--colors--greyscale-500); + } +} + +// ============================================================================ +// Subscription URL Modal Styles +// ============================================================================ + +.subscription-modal { + display: flex; + flex-direction: column; + gap: 1rem; + + &__description { + font-size: 0.875rem; + color: var(--c--theme--colors--greyscale-600); + margin: 0; + line-height: 1.5; + } + + &__loading { + display: flex; + align-items: center; + justify-content: center; + padding: 2rem; + color: var(--c--theme--colors--greyscale-500); + font-size: 0.875rem; + } + + &__url-container { + display: flex; + gap: 0.5rem; + align-items: stretch; + } + + &__url-input { + flex: 1; + padding: 0.75rem; + border: 1px solid var(--c--theme--colors--greyscale-300); + border-radius: 4px; + font-family: monospace; + font-size: 0.75rem; + background-color: var(--c--theme--colors--greyscale-50); + color: var(--c--theme--colors--greyscale-700); + overflow: hidden; + text-overflow: ellipsis; + + &:focus { + outline: none; + border-color: var(--c--theme--colors--primary-500); + } + } + + &__warning { + display: flex; + align-items: flex-start; + gap: 0.5rem; + padding: 0.75rem; + background-color: #fef3c7; + border: 1px solid #fcd34d; + border-radius: 4px; + + p { + margin: 0; + font-size: 0.75rem; + color: #92400e; + line-height: 1.4; + } + } + + &__warning-icon { + color: #d97706; + font-size: 1.125rem; + flex-shrink: 0; + } + + &__actions { + display: flex; + justify-content: flex-start; + padding-top: 0.5rem; + } +} diff --git a/src/frontend/apps/calendars/src/hooks/useThemeCustomization.tsx b/src/frontend/apps/calendars/src/hooks/useThemeCustomization.tsx index 9bf5acc..87772a5 100644 --- a/src/frontend/apps/calendars/src/hooks/useThemeCustomization.tsx +++ b/src/frontend/apps/calendars/src/hooks/useThemeCustomization.tsx @@ -1,5 +1,5 @@ import { useConfig } from "@/features/config/ConfigProvider"; -import { ThemeCustomization } from "@/features/api/types"; +import { ThemeCustomization, LocalizedRecord } from "@/features/api/types"; import { splitLocaleCode } from "@/features/i18n/utils"; import { useTranslation } from "react-i18next"; @@ -7,10 +7,9 @@ export const useThemeCustomization = (key: keyof ThemeCustomization) => { const { config } = useConfig(); const { i18n } = useTranslation(); const language = splitLocaleCode(i18n.language).language; - const themeCustomization = config?.theme_customization?.[key]; + const themeCustomization = config?.theme_customization?.[key] as LocalizedRecord | undefined; return { - ...themeCustomization?.default, - ...(themeCustomization?.[language as keyof typeof themeCustomization] ?? - {}), + ...(themeCustomization?.default ?? {}), + ...(themeCustomization?.[language] ?? {}), }; };