mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-12 23:32:55 +01:00
Updated project to React 19
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import classNames from 'classnames';
|
||||
import React, { ChangeEventHandler, cloneElement, FocusEventHandler, MouseEventHandler } from 'react';
|
||||
import React, { ChangeEventHandler, cloneElement, FocusEventHandler, isValidElement, MouseEventHandler, ReactElement } from 'react';
|
||||
|
||||
import { InputNotification } from '@noodl-types/globalInputTypes';
|
||||
|
||||
@@ -113,7 +113,7 @@ export function Checkbox({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{children && <div className={css['ChildContainer']}>{cloneElement(children, { isChecked })}</div>}
|
||||
{children && isValidElement(children) && <div className={css['ChildContainer']}>{cloneElement(children as ReactElement<{ isChecked?: boolean }>, { isChecked })}</div>}
|
||||
{label && <InputLabelSection label={label} />}
|
||||
</label>
|
||||
);
|
||||
|
||||
@@ -89,7 +89,7 @@ export function CoreBaseDialog({
|
||||
}, 50);
|
||||
}, [isVisible]);
|
||||
|
||||
const dialogRef = useRef<HTMLDivElement>();
|
||||
const dialogRef = useRef<HTMLDivElement>(null);
|
||||
const [dialogPosition, setDialogPosition] = useState({
|
||||
x: 0,
|
||||
y: 0,
|
||||
|
||||
@@ -45,7 +45,7 @@ export function Carousel({ activeIndex, items, indicator }: CarouselProps) {
|
||||
<div style={{ overflow: 'hidden' }}>
|
||||
<HStack UNSAFE_style={{ width: items.length * 100 + '%' }}>
|
||||
{items.map((item, index) => (
|
||||
<VStack key={index} ref={(ref) => (sliderRefs.current[index] = ref)} UNSAFE_style={{ width: '100%' }}>
|
||||
<VStack key={index} ref={(ref) => { sliderRefs.current[index] = ref; }} UNSAFE_style={{ width: '100%' }}>
|
||||
{item.slot}
|
||||
</VStack>
|
||||
))}
|
||||
|
||||
@@ -91,6 +91,10 @@ export function Columns({
|
||||
}}
|
||||
>
|
||||
{toArray(children).map((child, i) => {
|
||||
// Skip non-element children (null, undefined, boolean)
|
||||
if (!React.isValidElement(child)) {
|
||||
return child;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="column-item"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import classNames from 'classnames';
|
||||
import React, { cloneElement, MouseEventHandler } from 'react';
|
||||
import React, { cloneElement, isValidElement, MouseEventHandler, ReactElement } from 'react';
|
||||
|
||||
import { Icon, IconName, IconSize } from '@noodl-core-ui/components/common/Icon';
|
||||
import { BaseDialog, BaseDialogProps } from '@noodl-core-ui/components/layout/BaseDialog';
|
||||
@@ -100,7 +100,10 @@ export function MenuDialog({
|
||||
{item.label}
|
||||
</Label>
|
||||
</div>
|
||||
{item.component && cloneElement(item.component(() => onClose()))}
|
||||
{item.component && (() => {
|
||||
const component = item.component(() => onClose());
|
||||
return isValidElement(component) ? cloneElement(component as ReactElement) : component;
|
||||
})()}
|
||||
<div className={css['EndSlot']}>
|
||||
{item.endSlot && typeof item.endSlot === 'string' && <Text>{item.endSlot}</Text>}
|
||||
{item.endSlot && typeof item.endSlot !== 'string' && item.endSlot}
|
||||
|
||||
@@ -1,92 +1,91 @@
|
||||
import { Slot } from '@noodl-core-ui/types/global';
|
||||
import classNames from 'classnames';
|
||||
import React, { CSSProperties, useLayoutEffect, useRef, useState } from 'react';
|
||||
import css from './PopupSection.module.scss';
|
||||
|
||||
/**
|
||||
* TODO:
|
||||
* - Remove style prop and replace with "isInactive" prop
|
||||
*/
|
||||
|
||||
export interface PopupSectionProps {
|
||||
children?: Slot;
|
||||
title?: string;
|
||||
className?: string;
|
||||
maxContentHeight?: number;
|
||||
|
||||
hasBottomBorder?: boolean;
|
||||
hasYPadding?: boolean;
|
||||
isCenteringChildren?: boolean;
|
||||
|
||||
style?: CSSProperties;
|
||||
contentContainerStyle?: CSSProperties;
|
||||
}
|
||||
|
||||
export function PopupSection({
|
||||
children,
|
||||
title,
|
||||
className,
|
||||
maxContentHeight,
|
||||
|
||||
hasBottomBorder,
|
||||
hasYPadding,
|
||||
isCenteringChildren,
|
||||
|
||||
style,
|
||||
contentContainerStyle
|
||||
}: PopupSectionProps) {
|
||||
const contentRef = useRef<HTMLDivElement>();
|
||||
const [shouldScroll, setShouldScroll] = useState(false);
|
||||
const [hasBeenCalculated, setHasBeenCalculated] = useState(false);
|
||||
|
||||
function checkIfShouldScroll() {
|
||||
if (!contentRef.current || !maxContentHeight) return false;
|
||||
if (contentRef.current.getBoundingClientRect().height >= maxContentHeight) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (contentRef.current) {
|
||||
setShouldScroll(checkIfShouldScroll());
|
||||
setHasBeenCalculated(true);
|
||||
}
|
||||
}, [contentRef, children]);
|
||||
|
||||
return (
|
||||
<section
|
||||
className={classNames([css['Root'], hasBottomBorder && css['has-bottom-border'], className])}
|
||||
style={{
|
||||
...style,
|
||||
overflowY: hasBeenCalculated ? undefined : 'hidden',
|
||||
height: hasBeenCalculated ? undefined : 0
|
||||
}}
|
||||
>
|
||||
{title && (
|
||||
<header className={css['Header']}>
|
||||
<h2 className={css['Title']}>{title}</h2>
|
||||
</header>
|
||||
)}
|
||||
|
||||
{children ? (
|
||||
<div
|
||||
className={classNames([
|
||||
css['Content'],
|
||||
hasYPadding && css['has-y-padding'],
|
||||
isCenteringChildren && css['is-centering-children']
|
||||
])}
|
||||
ref={contentRef}
|
||||
style={{
|
||||
...contentContainerStyle,
|
||||
height: shouldScroll ? maxContentHeight : undefined,
|
||||
// @ts-expect-error
|
||||
overflowY: shouldScroll ? 'overlay' : undefined
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
) : (
|
||||
<div ref={contentRef} />
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
import { Slot } from '@noodl-core-ui/types/global';
|
||||
import classNames from 'classnames';
|
||||
import React, { CSSProperties, useLayoutEffect, useRef, useState } from 'react';
|
||||
import css from './PopupSection.module.scss';
|
||||
|
||||
/**
|
||||
* TODO:
|
||||
* - Remove style prop and replace with "isInactive" prop
|
||||
*/
|
||||
|
||||
export interface PopupSectionProps {
|
||||
children?: Slot;
|
||||
title?: string;
|
||||
className?: string;
|
||||
maxContentHeight?: number;
|
||||
|
||||
hasBottomBorder?: boolean;
|
||||
hasYPadding?: boolean;
|
||||
isCenteringChildren?: boolean;
|
||||
|
||||
style?: CSSProperties;
|
||||
contentContainerStyle?: CSSProperties;
|
||||
}
|
||||
|
||||
export function PopupSection({
|
||||
children,
|
||||
title,
|
||||
className,
|
||||
maxContentHeight,
|
||||
|
||||
hasBottomBorder,
|
||||
hasYPadding,
|
||||
isCenteringChildren,
|
||||
|
||||
style,
|
||||
contentContainerStyle
|
||||
}: PopupSectionProps) {
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
const [shouldScroll, setShouldScroll] = useState(false);
|
||||
const [hasBeenCalculated, setHasBeenCalculated] = useState(false);
|
||||
|
||||
function checkIfShouldScroll() {
|
||||
if (!contentRef.current || !maxContentHeight) return false;
|
||||
if (contentRef.current.getBoundingClientRect().height >= maxContentHeight) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (contentRef.current) {
|
||||
setShouldScroll(checkIfShouldScroll());
|
||||
setHasBeenCalculated(true);
|
||||
}
|
||||
}, [contentRef, children]);
|
||||
|
||||
return (
|
||||
<section
|
||||
className={classNames([css['Root'], hasBottomBorder && css['has-bottom-border'], className])}
|
||||
style={{
|
||||
...style,
|
||||
overflowY: hasBeenCalculated ? undefined : 'hidden',
|
||||
height: hasBeenCalculated ? undefined : 0
|
||||
}}
|
||||
>
|
||||
{title && (
|
||||
<header className={css['Header']}>
|
||||
<h2 className={css['Title']}>{title}</h2>
|
||||
</header>
|
||||
)}
|
||||
|
||||
{children ? (
|
||||
<div
|
||||
className={classNames([
|
||||
css['Content'],
|
||||
hasYPadding && css['has-y-padding'],
|
||||
isCenteringChildren && css['is-centering-children']
|
||||
])}
|
||||
ref={contentRef}
|
||||
style={{
|
||||
...contentContainerStyle,
|
||||
height: shouldScroll ? maxContentHeight : undefined,
|
||||
overflowY: shouldScroll ? 'overlay' : undefined
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
) : (
|
||||
<div ref={contentRef} />
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export function PropertyPanelSelectInput({
|
||||
hasSmallText
|
||||
}: PropertyPanelSelectInputProps) {
|
||||
const [isSelectCollapsed, setIsSelectCollapsed] = useState(true);
|
||||
const rootRef = useRef<HTMLDivElement>();
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const displayValue = properties?.options.find((option) => option.value === value)?.label;
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ export function PropertyPanelSliderInput({
|
||||
}
|
||||
|
||||
const thumbPercentage = useMemo(
|
||||
() => linearMap(parseInt(value.toString()), properties.min, properties.max, 0, 100),
|
||||
() => linearMap(parseInt(value.toString()), Number(properties.min), Number(properties.max), 0, 100),
|
||||
[value, properties]
|
||||
);
|
||||
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
import React, {
|
||||
JSXElementConstructor,
|
||||
ReactChild,
|
||||
ReactElement,
|
||||
ReactFragment,
|
||||
ReactPortal,
|
||||
ReactText
|
||||
} from 'react';
|
||||
|
||||
export interface UnsafeStyleProps {
|
||||
UNSAFE_className?: string;
|
||||
UNSAFE_style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
// FIXME: add generics to be able to specify what exact components are allowed?
|
||||
export type SingleSlot =
|
||||
| ReactElement<TSFixme, TSFixme>
|
||||
| ReactFragment
|
||||
| ReactPortal
|
||||
| boolean
|
||||
| null
|
||||
| undefined;
|
||||
|
||||
export type Slot = SingleSlot | SingleSlot[];
|
||||
import React, { ReactElement, ReactPortal } from 'react';
|
||||
|
||||
export interface UnsafeStyleProps {
|
||||
UNSAFE_className?: string;
|
||||
UNSAFE_style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
// FIXME: add generics to be able to specify what exact components are allowed?
|
||||
// Note: ReactFragment removed in React 19, using React.ReactNode for fragments
|
||||
export type SingleSlot =
|
||||
| ReactElement<TSFixme, TSFixme>
|
||||
| Iterable<React.ReactNode>
|
||||
| ReactPortal
|
||||
| boolean
|
||||
| null
|
||||
| undefined;
|
||||
|
||||
export type Slot = SingleSlot | SingleSlot[];
|
||||
|
||||
Reference in New Issue
Block a user