Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Source/.storybook/preview.css
Original file line number Diff line number Diff line change
@@ -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);
Expand Down
57 changes: 48 additions & 9 deletions Source/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -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' },
],
},
};
2 changes: 1 addition & 1 deletion Source/CommandDialog/CommandStepper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ export const CommandStepperContent = ({
const existingStyle = existing.style as Record<string, unknown> | undefined;
return {
...existing,
style: { ...existingStyle, backgroundColor: bgColor, color: '#fff' }
style: { ...existingStyle, backgroundColor: bgColor, color: 'var(--primary-color-text)' }
};
}
}
Expand Down
9 changes: 6 additions & 3 deletions Source/CommandDialog/StepperCommandDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ const StepperCommandDialogWrapper = <TCommand extends object, TResponse = object
const [visitedSteps, setVisitedSteps] = useState<Set<number>>(new Set([0]));
const [stepErrors, setStepErrors] = useState<boolean[]>([]);

// 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();
Expand Down Expand Up @@ -174,13 +177,13 @@ const StepperCommandDialogWrapper = <TCommand extends object, TResponse = object
};

const headerElement = (
<div className="inline-flex align-items-center justify-content-center gap-2">
<span className="font-bold white-space-nowrap">{title}</span>
<div className="inline-flex items-center justify-center gap-2">
<span className="font-bold whitespace-nowrap">{title}</span>
</div>
);

const footer = (
<div className="flex align-items-center w-full gap-3">
<div className="flex items-center w-full gap-3">
{!isFirstStep && (
<Button
label={previousLabel}
Expand Down
2 changes: 1 addition & 1 deletion Source/Common/FormElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface FormElementProps {

export const FormElement = (props: FormElementProps) => {
return (
<div className="card flex flex-column md:flex-row gap-3">
<div className="flex flex-col md:flex-row gap-3">
<div className="p-inputgroup flex-1">
<span className="p-inputgroup-addon">
{props.icon}
Expand Down
14 changes: 11 additions & 3 deletions Source/DataPage/DataPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export interface ColumnProps {
}

export const MenuItems = ({ children }: MenuItemsProps) => {
const context = React.useContext(DataPageContext);
const context = useDataPageContext();

const isDisabled = useMemo(() => {
return !context.selectedItem;
Expand Down Expand Up @@ -62,7 +62,7 @@ export const MenuItems = ({ children }: MenuItemsProps) => {

export const Columns = ({ children }: ColumnProps) => {

const context = React.useContext(DataPageContext);
const context = useDataPageContext();

if (context.query.prototype instanceof QueryFor) {
return (
Expand Down Expand Up @@ -96,7 +96,15 @@ interface IDataPageContext extends DataPageProps<any, any, any> {
onSelectionChanged: (e: DataTableSelectionSingleChangeEvent<any>) => void;
}

const DataPageContext = React.createContext<IDataPageContext>(null as any);
const DataPageContext = React.createContext<IDataPageContext | null>(null);

function useDataPageContext(): IDataPageContext {
const context = React.useContext(DataPageContext);
if (!context) {
throw new Error('useDataPageContext must be used within a DataPage component');
}
return context;
}

/**
* Props for the DataPage component
Expand Down
11 changes: 6 additions & 5 deletions Source/Dialogs/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,21 @@ export const Dialog = ({
yesLabel = 'Yes',
noLabel = 'No'
}: DialogProps) => {
// Try to get dialog context, but allow it to be undefined for standalone usage
// useDialogContext() is called unconditionally on every render — the try/catch only suppresses
// the exception when 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();
contextCloseDialog = context?.closeDialog;
} catch {
// No context available - dialog is being used standalone
contextCloseDialog = undefined;
}

const isDialogValid = isValid !== false;
const headerElement = (
<div className="inline-flex align-items-center justify-content-center gap-2">
<span className="font-bold white-space-nowrap">{title}</span>
<div className="inline-flex items-center justify-center gap-2">
<span className="font-bold whitespace-nowrap">{title}</span>
</div>
);

Expand Down Expand Up @@ -142,7 +143,7 @@ export const Dialog = ({
};

const footer = (
<div className="flex flex-wrap justify-content-start gap-3">
<div className="flex flex-wrap justify-start gap-3">
{getFooterInterior()}
</div>
);
Expand Down
22 changes: 11 additions & 11 deletions Source/ObjectContentEditor/ObjectContentEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,12 @@ export const ObjectContentEditor = ({ object, timestamp, schema, editMode = fals
};

const rowStyle: React.CSSProperties = {
borderBottom: '1px solid rgba(255,255,255,0.1)',
borderBottom: '1px solid var(--surface-border)',
};

const labelStyle: React.CSSProperties = {
padding: '8px 12px',
color: 'rgba(255,255,255,0.6)',
color: 'var(--text-color-secondary)',
textAlign: 'left',
fontWeight: 500,
width: '140px',
Expand All @@ -158,7 +158,7 @@ export const ObjectContentEditor = ({ object, timestamp, schema, editMode = fals

const valueStyle: React.CSSProperties = {
padding: '8px 12px',
color: '#fff',
color: 'var(--text-color)',
textAlign: 'left',
};

Expand Down Expand Up @@ -258,15 +258,15 @@ export const ObjectContentEditor = ({ object, timestamp, schema, editMode = fals

if (property.type === 'array') {
return (
<div className="flex align-items-center gap-2" style={{ color: 'rgba(255,255,255,0.6)', fontStyle: 'italic' }}>
<div className="flex items-center gap-2" style={{ color: 'var(--text-color-secondary)', fontStyle: 'italic' }}>
<span>Array editing not yet supported</span>
</div>
);
}

if (property.type === 'object') {
return (
<div className="flex align-items-center gap-2" style={{ color: 'rgba(255,255,255,0.6)', fontStyle: 'italic' }}>
<div className="flex items-center gap-2" style={{ color: 'var(--text-color-secondary)', fontStyle: 'italic' }}>
<span>Object editing not yet supported</span>
</div>
);
Expand Down Expand Up @@ -306,7 +306,7 @@ export const ObjectContentEditor = ({ object, timestamp, schema, editMode = fals
if (Array.isArray(value)) {
return (
<div
className="flex align-items-center gap-2 cursor-pointer"
className="flex items-center gap-2 cursor-pointer"
onClick={() => navigateToProperty(propertyName)}
style={{ color: 'var(--primary-color)', display: 'flex', alignItems: 'center' }}
>
Expand All @@ -319,7 +319,7 @@ export const ObjectContentEditor = ({ object, timestamp, schema, editMode = fals
if (typeof value === 'object') {
return (
<div
className="flex align-items-center gap-2 cursor-pointer"
className="flex items-center gap-2 cursor-pointer"
onClick={() => navigateToProperty(propertyName)}
style={{ color: 'var(--primary-color)', display: 'flex', alignItems: 'center' }}
>
Expand All @@ -334,7 +334,7 @@ export const ObjectContentEditor = ({ object, timestamp, schema, editMode = fals

const renderTable = () => {
if (Array.isArray(currentData)) {
if (currentData.length === 0) return <div style={{ padding: '12px', color: 'rgba(255,255,255,0.6)' }}>Empty array</div>;
if (currentData.length === 0) return <div style={{ padding: '12px', color: 'var(--text-color-secondary)' }}>Empty array</div>;

const firstItem = currentData[0];
if (typeof firstItem === 'object' && firstItem !== null && !Array.isArray(firstItem)) {
Expand All @@ -346,7 +346,7 @@ export const ObjectContentEditor = ({ object, timestamp, schema, editMode = fals
{currentData.map((item, index) => (
<React.Fragment key={index}>
{index > 0 && (
<tr style={{ height: '8px', background: 'rgba(255,255,255,0.05)' }}>
<tr style={{ height: '8px', background: 'var(--surface-hover)' }}>
<td colSpan={2}></td>
</tr>
)}
Expand Down Expand Up @@ -433,10 +433,10 @@ export const ObjectContentEditor = ({ object, timestamp, schema, editMode = fals
<div style={{
marginTop: '20px',
padding: '12px',
background: 'rgba(100, 150, 255, 0.1)',
background: 'var(--highlight-bg)',
borderRadius: '8px',
fontSize: '12px',
color: 'rgba(255,255,255,0.6)'
color: 'var(--text-color-secondary)'
}}>
Snapshot captured: {timestamp.toLocaleString()}
</div>
Expand Down
5 changes: 1 addition & 4 deletions Source/PivotViewer/PivotViewer.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
:root {
color-scheme: dark;
}

.pivot-viewer {
color-scheme: dark;
position: relative;
/* CRITICAL: Don't set explicit height - let parent control it via container constraints */
display: flex;
Expand Down
6 changes: 3 additions & 3 deletions Source/SchemaEditor/TypeCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export const TypeCell = ({
const isNavigable = itemType === 'object';
return (
<div
className="flex align-items-center gap-2 w-full"
className="flex items-center gap-2 w-full"
style={{ height: '100%' }}
data-pr-tooltip={isNavigable ? 'Click to navigate to item definition' : undefined}
data-pr-position="top"
Expand All @@ -113,7 +113,7 @@ export const TypeCell = ({
} else if (rowData.type === 'object') {
return (
<div
className="flex align-items-center gap-2 w-full"
className="flex items-center gap-2 w-full"
style={{ height: '100%' }}
data-pr-tooltip="Click to navigate to object properties"
data-pr-position="top"
Expand All @@ -130,7 +130,7 @@ export const TypeCell = ({
}

return (
<div className="flex align-items-center gap-2 w-full" style={{ minHeight: '2.5rem' }}>
<div className="flex items-center gap-2 w-full" style={{ minHeight: '2.5rem' }}>
<Dropdown
value={currentValue}
options={allTypeOptions}
Expand Down
Loading
Loading