From 8dd9a7af94e810e7d1f3ef5a0d4d5d7afabe8f4a Mon Sep 17 00:00:00 2001 From: woksin Date: Wed, 20 May 2026 10:05:25 +0200 Subject: [PATCH] refactor: improve theme handling and styling across components --- Source/.storybook/preview.css | 3 +- Source/.storybook/preview.js | 57 ++++++++++++++++--- Source/CommandDialog/CommandStepper.tsx | 2 +- Source/CommandDialog/StepperCommandDialog.tsx | 9 ++- Source/Common/FormElement.tsx | 2 +- Source/DataPage/DataPage.tsx | 14 ++++- Source/Dialogs/Dialog.tsx | 11 ++-- .../ObjectContentEditor.tsx | 22 +++---- Source/PivotViewer/PivotViewer.css | 5 +- Source/SchemaEditor/TypeCell.tsx | 6 +- Source/TimeMachine/EventsView.css | 34 +++++------ Source/TimeMachine/Properties.css | 57 +++++++++++++++++++ Source/TimeMachine/Properties.tsx | 47 +++++---------- Source/TimeMachine/TimeMachine.css | 56 ++++++++++++------ Source/package.json | 7 --- 15 files changed, 214 insertions(+), 118 deletions(-) create mode 100644 Source/TimeMachine/Properties.css diff --git a/Source/.storybook/preview.css b/Source/.storybook/preview.css index 8934873..50215c6 100644 --- a/Source/.storybook/preview.css +++ b/Source/.storybook/preview.css @@ -1,7 +1,6 @@ /* 1. Tailwind (includes preflight reset) — must come first */ @import "tailwindcss"; -/* 2. PrimeReact theme — must come AFTER Tailwind so it overrides the preflight reset */ -@import 'primereact/resources/themes/lara-dark-blue/theme.css'; +/* 2. PrimeReact theme is injected dynamically by the Storybook theme decorator */ html, body { background-color: var(--surface-ground); diff --git a/Source/.storybook/preview.js b/Source/.storybook/preview.js index 06e72d8..80b4556 100644 --- a/Source/.storybook/preview.js +++ b/Source/.storybook/preview.js @@ -1,17 +1,56 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +import React from 'react'; import 'primeicons/primeicons.css'; import './preview.css'; +import darkThemeUrl from 'primereact/resources/themes/lara-dark-blue/theme.css?url'; +import lightThemeUrl from 'primereact/resources/themes/lara-light-blue/theme.css?url'; + +export const globalTypes = { + theme: { + name: 'Theme', + description: 'PrimeReact theme', + defaultValue: 'dark', + toolbar: { + icon: 'paintbrush', + items: [ + { value: 'dark', title: 'Lara Dark Blue' }, + { value: 'light', title: 'Lara Light Blue' }, + ], + showName: true, + }, + }, +}; + +function applyThemeLink(href) { + let link = document.getElementById('primereact-theme'); + if (!link) { + link = document.createElement('link'); + link.id = 'primereact-theme'; + link.rel = 'stylesheet'; + document.head.appendChild(link); + } + if (link.href !== href) link.href = href; +} + +export const decorators = [ + (Story, context) => { + applyThemeLink(context.globals.theme === 'light' ? lightThemeUrl : darkThemeUrl); + return React.createElement(Story); + }, +]; export const parameters = { - actions: { argTypesRegex: '^on[A-Z].*' }, - controls: { expanded: true }, - backgrounds: { - default: 'dark', - values: [ - { name: 'dark', value: '#111827' }, - { name: 'surface-card', value: '#1f2937' }, - ], - }, + actions: { argTypesRegex: '^on[A-Z].*' }, + controls: { expanded: true }, + backgrounds: { + default: 'dark', + values: [ + { name: 'dark', value: '#111827' }, + { name: 'surface-card', value: '#1f2937' }, + { name: 'light', value: '#ffffff' }, + { name: 'surface-light', value: '#f8f9fa' }, + ], + }, }; diff --git a/Source/CommandDialog/CommandStepper.tsx b/Source/CommandDialog/CommandStepper.tsx index c65bee5..f246fb0 100644 --- a/Source/CommandDialog/CommandStepper.tsx +++ b/Source/CommandDialog/CommandStepper.tsx @@ -196,7 +196,7 @@ export const CommandStepperContent = ({ const existingStyle = existing.style as Record | undefined; return { ...existing, - style: { ...existingStyle, backgroundColor: bgColor, color: '#fff' } + style: { ...existingStyle, backgroundColor: bgColor, color: 'var(--primary-color-text)' } }; } } diff --git a/Source/CommandDialog/StepperCommandDialog.tsx b/Source/CommandDialog/StepperCommandDialog.tsx index 341376b..407e081 100644 --- a/Source/CommandDialog/StepperCommandDialog.tsx +++ b/Source/CommandDialog/StepperCommandDialog.tsx @@ -103,6 +103,9 @@ const StepperCommandDialogWrapper = >(new Set([0])); const [stepErrors, setStepErrors] = useState([]); + // useDialogContext() is called unconditionally on every render — the try/catch only suppresses + // the exception when the dialog is used standalone (outside a provider). React's Rules of Hooks + // are not violated because the hook is always called; the try/catch never skips the call. let contextCloseDialog: ((result: DialogResult) => void) | undefined; try { const context = useDialogContext(); @@ -174,13 +177,13 @@ const StepperCommandDialogWrapper = - {title} +
+ {title}
); const footer = ( -
+
{!isFirstStep && (