add ref to chakra-ui components

This commit is contained in:
Bowen Tan 2022-02-10 15:32:12 +08:00
parent cb3d7e9dd2
commit f40c42fa8a
26 changed files with 79 additions and 41 deletions

View File

@ -295,7 +295,7 @@ export default implementRuntimeComponent({
styleSlots: ['content'],
events: [],
},
})(({ customStyle, slotsElements, ...restProps }) => {
})(({ customStyle, slotsElements, $ref, ...restProps }) => {
const styleProps = pick(restProps, StyleProps);
return (
<BaseBox
@ -309,6 +309,7 @@ export default implementRuntimeComponent({
className={css`
${customStyle?.content}
`}
ref={$ref}
>
{slotsElements.content}
</BaseBox>

View File

@ -80,6 +80,7 @@ export default implementRuntimeComponent({
colorScheme,
mergeState,
customStyle,
$ref,
}) => {
const groupContext = useCheckboxGroupContext();
let _defaultIsChecked = false;
@ -133,6 +134,7 @@ export default implementRuntimeComponent({
className={css`
${customStyle?.content}
`}
ref={$ref}
>
<Text value={text} />
</BaseCheckbox>

View File

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react';
import { Type } from '@sinclair/typebox';
import { CheckboxGroup as BaseCheckboxGroup } from '@chakra-ui/react';
import { Box, CheckboxGroup as BaseCheckboxGroup } from '@chakra-ui/react';
import { implementRuntimeComponent } from '@sunmao-ui/runtime';
import { SizePropertySchema, IsDisabledSchema } from './Checkbox';
@ -40,20 +40,22 @@ export default implementRuntimeComponent({
styleSlots: [],
events: [],
},
})(({ size, defaultValue, isDisabled, slotsElements, mergeState }) => {
})(({ size, defaultValue, isDisabled, slotsElements, mergeState, $ref }) => {
const [value, setValue] = useState(defaultValue);
useEffect(() => {
mergeState({ value });
}, [mergeState, value]);
return (
<BaseCheckboxGroup
size={size}
defaultValue={defaultValue}
isDisabled={isDisabled}
onChange={val => setValue(val)}
>
{slotsElements.content}
</BaseCheckboxGroup>
<Box ref={$ref}>
<BaseCheckboxGroup
size={size}
defaultValue={defaultValue}
isDisabled={isDisabled}
onChange={val => setValue(val)}
>
{slotsElements.content}
</BaseCheckboxGroup>
</Box>
);
});

View File

@ -75,6 +75,7 @@ export default implementRuntimeComponent({
colorScheme: 'blue',
},
customStyle,
$ref,
}) => {
const [isOpen, setIsOpen] = useState(false);
const [title, setTitle] = useState(customerTitle || '');
@ -129,6 +130,7 @@ export default implementRuntimeComponent({
<AlertDialogContent
className={`${customStyle?.content}`}
{...(containerRef.current ? dialogContentProps : {})}
ref={$ref}
>
<AlertDialogHeader>{title}</AlertDialogHeader>
<AlertDialogBody>{slotsElements.content}</AlertDialogBody>

View File

@ -25,12 +25,13 @@ export default implementRuntimeComponent({
styleSlots: ['content'],
events: [],
},
})(({ customStyle }) => {
})(({ customStyle, $ref }) => {
return (
<Divider
className={css`
${customStyle?.content}
`}
ref={$ref}
/>
);
});

View File

@ -48,6 +48,7 @@ export default implementRuntimeComponent({
slotsElements,
childrenMap,
component,
$ref,
}) => {
const [invalidArray, setInvalidArray] = useState<boolean[]>([]);
const [isFormInvalid, setIsFormInvalid] = useState<boolean>(false);
@ -151,6 +152,7 @@ export default implementRuntimeComponent({
className={css`
${customStyle?.content}
`}
ref={$ref}
>
{slotsElements.content}
{hideSubmit ? undefined : (

View File

@ -69,6 +69,7 @@ export default implementRuntimeComponent({
slotsElements,
childrenMap,
component,
$ref,
}) => {
const [inputValue, setInputValue] = useState('');
// don't show Invalid state on component mount
@ -147,6 +148,7 @@ export default implementRuntimeComponent({
className={css`
${customStyle?.content}
`}
ref={$ref}
>
<HStack width="full">
<FormLabel flex="0 0 auto" width="33%" margin="auto 0">

View File

@ -129,6 +129,7 @@ export default implementRuntimeComponent({
crossOrigin,
callbackMap,
customStyle,
$ref,
}) => {
const style = boxSize
? css`
@ -154,6 +155,7 @@ export default implementRuntimeComponent({
ignoreFallback={ignoreFallback}
borderRadius={borderRadius}
fallbackSrc={fallbackSrc}
ref={$ref}
/>
);
}

View File

@ -39,7 +39,7 @@ export default implementRuntimeComponent({
styleSlots: ['content'],
events: [],
},
})(({ text, mergeState, customStyle }) => {
})(({ text, mergeState, customStyle, $ref }) => {
useEffect(() => {
mergeState({ value: text.raw });
}, [mergeState, text.raw]);
@ -49,6 +49,7 @@ export default implementRuntimeComponent({
className={css`
${customStyle?.content}
`}
ref={$ref}
>
<Text value={text} />
</BaseKbd>

View File

@ -37,7 +37,7 @@ export default implementRuntimeComponent({
styleSlots: ['content'],
events: [],
},
})(({ text, href, isExternal, customStyle }) => {
})(({ text, href, isExternal, customStyle, $ref }) => {
return (
<Link
href={href}
@ -46,6 +46,7 @@ export default implementRuntimeComponent({
className={css`
${customStyle?.content}
`}
ref={$ref}
>
<Text value={text} />
</Link>

View File

@ -56,7 +56,7 @@ export default implementRuntimeComponent({
styleSlots: ['content'],
events: [],
},
})(({ listData, template, app, services, customStyle }) => {
})(({ listData, template, app, services, customStyle, $ref }) => {
if (!listData) {
return null;
}
@ -88,6 +88,7 @@ export default implementRuntimeComponent({
className={css`
${customStyle?.content}
`}
ref={$ref}
>
{listItems}
</BaseList>

View File

@ -96,6 +96,7 @@ export default implementRuntimeComponent({
variant,
mergeState,
customStyle,
$ref,
}) => {
useEffect(() => {
const newValue = (defaultValue || []).map(o => o.value);
@ -113,6 +114,7 @@ export default implementRuntimeComponent({
className={css`
${customStyle?.content}
`}
ref={$ref}
>
<BaseMultiSelect
isMulti

View File

@ -86,6 +86,7 @@ export default implementRuntimeComponent({
mergeState,
subscribeMethods,
customStyle,
$ref,
}) => {
const [value, setValue] = useState(defaultValue);
const onChange = (_: string, valueAsNumber: number) => setValue(valueAsNumber || 0);
@ -125,6 +126,7 @@ export default implementRuntimeComponent({
className={css`
${customStyle?.content}
`}
ref={$ref}
>
<NumberInputField />
<NumberInputStepper>

View File

@ -74,6 +74,7 @@ export default implementRuntimeComponent({
colorScheme,
mergeState,
customStyle,
$ref,
}) => {
useEffect(() => {
mergeState({ value: text.raw });
@ -99,6 +100,7 @@ export default implementRuntimeComponent({
className={css`
${customStyle?.content}
`}
ref={$ref}
>
<Text value={text} />
</BaseRadio>

View File

@ -38,7 +38,7 @@ export default implementRuntimeComponent({
styleSlots: ['content'],
events: [],
},
})(({ defaultValue, isNumerical, slotsElements, mergeState, customStyle }) => {
})(({ defaultValue, isNumerical, slotsElements, mergeState, customStyle, $ref }) => {
const [value, setValue] = useState(defaultValue);
useEffect(() => {
@ -56,6 +56,7 @@ export default implementRuntimeComponent({
className={css`
${customStyle?.content}
`}
ref={$ref}
>
{slotsElements.content}
</BaseRadioGroup>

View File

@ -24,7 +24,7 @@ export default implementRuntimeComponent({
styleSlots: [],
events: [],
},
})(({ slotsElements }) => {
})(({ slotsElements, $ref }) => {
return (
<ChakraProvider
theme={extendTheme({
@ -32,7 +32,7 @@ export default implementRuntimeComponent({
useSystemColorMode: false,
})}
>
<>{slotsElements.root}</>
<div ref={$ref}>{slotsElements.root}</div>
</ChakraProvider>
);
});

View File

@ -95,6 +95,7 @@ export default implementRuntimeComponent({
variant,
mergeState,
customStyle,
$ref,
}) => {
const [value, setValue] = useState<string | undefined>(defaultValue);
@ -123,6 +124,7 @@ export default implementRuntimeComponent({
className={css`
${customStyle?.content}
`}
ref={$ref}
>
{options.map(opt => (
<option key={opt.value} value={opt.value}>

View File

@ -70,9 +70,9 @@ export default implementRuntimeComponent({
styleSlots: [],
events: [],
},
})(({ direction, wrap, align, justify, spacing, slotsElements }) => {
})(({ direction, wrap, align, justify, spacing, slotsElements, $ref }) => {
return (
<BaseStack {...{ direction, wrap, align, justify, spacing }}>
<BaseStack {...{ direction, wrap, align, justify, spacing }} ref={$ref}>
{slotsElements.content}
</BaseStack>
);

View File

@ -32,6 +32,7 @@ export const TableImpl = implementTable(
mergeState,
services,
app,
$ref,
}) => {
const [selectedItem, setSelectedItem] = useState<Record<string, any> | undefined>();
const [selectedItems, setSelectedItems] = useState<Array<Record<string, any>>>([]);
@ -86,7 +87,7 @@ export const TableImpl = implementTable(
let newSelectedItems;
if (isItemSelected(item)) {
newSelectedItems = selectedItems.filter(
selectedItem => selectedItem[majorKey] != item[majorKey]
selectedItem => selectedItem[majorKey] !== item[majorKey]
);
} else {
newSelectedItems = selectedItems.concat(item);
@ -115,7 +116,7 @@ export const TableImpl = implementTable(
isIndeterminate={isIndeterminate}
checked={isAllChecked}
onChange={onChange}
></Checkbox>
/>
</Th>
);
}, [data, isMultiSelect, selectedItems.length, updateSelectedItems]);
@ -169,7 +170,7 @@ export const TableImpl = implementTable(
key="$checkbox"
onClick={onClickCheckbox}
>
<Checkbox size="lg" isChecked={isSelected}></Checkbox>
<Checkbox size="lg" isChecked={isSelected} />
</Td>
);
@ -222,6 +223,7 @@ export const TableImpl = implementTable(
borderColor="gray.200"
borderRadius="base"
overflow="auto"
ref={$ref}
>
{!data ? loadingSpinner : tableContent}
</Box>

View File

@ -47,7 +47,7 @@ export default implementRuntimeComponent({
events: [],
},
})(props => {
const { tabNames, mergeState, initialSelectedTabIndex, customStyle, slotsElements } =
const { tabNames, mergeState, initialSelectedTabIndex, customStyle, slotsElements, $ref } =
props;
const [selectedTabIndex, setSelectedTabIndex] = useState(initialSelectedTabIndex ?? 0);
@ -62,6 +62,7 @@ export default implementRuntimeComponent({
<BaseTabs
defaultIndex={initialSelectedTabIndex}
onChange={idx => setSelectedTabIndex(idx)}
ref={$ref}
>
<TabList>
{tabNames.map((name, idx) => (

View File

@ -68,6 +68,7 @@ export default implementRuntimeComponent({
isDisabled,
defaultIsOpen,
slotsElements,
$ref,
}) => {
return (
/*
@ -82,6 +83,7 @@ export default implementRuntimeComponent({
isDisabled={isDisabled}
defaultIsOpen={defaultIsOpen}
shouldWrapChildren={shouldWrapChildren}
ref={$ref}
>
{slotsElements.content}
</Tooltip>

View File

@ -124,8 +124,8 @@ export const EditorMask: React.FC<Props> = observer((props: Props) => {
const getMaskPosition = useCallback(
(componentId: string) => {
if (!wrapperRect.current) return;
const rect = rects[componentId];
if (!wrapperRect.current || !rect) return;
return {
id: componentId,
style: {

View File

@ -8,10 +8,14 @@ import {
RuntimeApplication,
RuntimeComponentSchema,
} from '@sunmao-ui/core';
import { UIServices, ModuleSchema } from '../../types';
import { ImplWrapper } from './ImplWrapper';
import { watch } from '../../utils/watchReactivity';
import { ImplementedRuntimeModule, EventHandlerSchema } from '../../types';
import {
ImplementedRuntimeModule,
EventHandlerSchema,
UIServices,
ModuleSchema,
} from '../../types';
import { resolveChildrenMap } from '../../utils/resolveChildrenMap';
type Props = Static<typeof ModuleSchema> & {
@ -20,19 +24,20 @@ type Props = Static<typeof ModuleSchema> & {
app?: RuntimeApplication;
};
export const ModuleRenderer: React.FC<Props> = props => {
export const ModuleRenderer = React.forwardRef<HTMLDivElement, Props>((props, ref) => {
const { type, services } = props;
try {
const moduleSpec = services.registry.getModuleByType(type);
return <ModuleRendererContent {...props} moduleSpec={moduleSpec} />;
} catch {
return <span>Cannot find Module {type}.</span>;
return <div ref={ref}>Cannot find Module {type}.</div>;
}
};
});
const ModuleRendererContent: React.FC<
const ModuleRendererContent = React.forwardRef<
HTMLDivElement,
Props & { moduleSpec: ImplementedRuntimeModule }
> = props => {
>((props, ref) => {
const { moduleSpec, properties, handlers, evalScope, services, app } = props;
const moduleId = services.stateManager.maskedEval(props.id, true, evalScope) as string;
@ -159,8 +164,8 @@ const ModuleRendererContent: React.FC<
});
}, [evaledModuleTemplate, services, app]);
return <>{result}</>;
};
return <div ref={ref}>{result}</div>;
});
function parseTypeComponents(
c: Application['spec']['components'][0]

View File

@ -1,3 +1,4 @@
import React from 'react';
import styled from '@emotion/styled';
import ReactMarkdown from 'react-markdown';
import { Static, Type } from '@sinclair/typebox';
@ -17,14 +18,14 @@ export type TextProps = {
cssStyle?: string;
};
const Text: React.FC<TextProps> = ({ value, cssStyle }) => {
const Text = React.forwardRef<HTMLDivElement, TextProps>(({ value, cssStyle }, ref) => {
const text = typeof value.raw === 'string' ? value.raw : `${value.raw}`;
if (value.format === 'md') {
const Div = styled.div`
${cssStyle}
`;
return (
<Div>
<Div ref={ref}>
<ReactMarkdown>{text}</ReactMarkdown>
</Div>
);
@ -34,7 +35,7 @@ const Text: React.FC<TextProps> = ({ value, cssStyle }) => {
const Span = styled.span`
${cssStyle}
`;
return <Span>{text}</Span>;
};
return <Span ref={ref}>{text}</Span>;
});
export default Text;

View File

@ -27,7 +27,7 @@ export default implementRuntimeComponent({
styleSlots: [],
events: [],
},
})(({ id, type, properties, handlers, services, app }) => {
})(({ id, type, properties, handlers, services, app, $ref }) => {
if (!type) {
return <span>Please choose a module to render.</span>;
}
@ -43,6 +43,7 @@ export default implementRuntimeComponent({
handlers={handlers}
services={services}
app={app}
ref={$ref}
/>
);
});

View File

@ -37,6 +37,6 @@ export default implementRuntimeComponent({
styleSlots: ['content'],
events: [],
},
})(({ value, customStyle }) => {
return <_Text value={value} cssStyle={customStyle?.content} />;
})(({ value, customStyle, $ref }) => {
return <_Text value={value} cssStyle={customStyle?.content} ref={$ref} />;
});