mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2024-11-21 03:15:49 +08:00
refactor chakra-ui-lib
This commit is contained in:
parent
70dad17dee
commit
93fa2244d4
@ -1,7 +1,6 @@
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { Box as BaseBox } from '@chakra-ui/react';
|
||||
import { ComponentImplementation, Slot, GRID_HEIGHT } from '@sunmao-ui/runtime';
|
||||
import { implementRuntimeComponent2, Slot, GRID_HEIGHT } from '@sunmao-ui/runtime';
|
||||
import { pick } from 'lodash-es';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
@ -270,11 +269,30 @@ const StyleSchema = Type.Partial(
|
||||
);
|
||||
const StyleProps = Object.keys(StyleSchema.properties);
|
||||
|
||||
const Box: ComponentImplementation<Static<typeof StyleSchema>> = ({
|
||||
slotsMap,
|
||||
customStyle,
|
||||
...restProps
|
||||
}) => {
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'box',
|
||||
displayName: 'Box',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
description: 'chakra-ui box',
|
||||
exampleProperties: {
|
||||
w: GRID_HEIGHT,
|
||||
h: GRID_HEIGHT,
|
||||
border: '1px solid black',
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
},
|
||||
spec: {
|
||||
properties: StyleSchema,
|
||||
state: Type.Object({}),
|
||||
methods: {},
|
||||
slots: ['content'],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
})(({ slotsMap, customStyle, ...restProps }) => {
|
||||
const styleProps = pick(restProps, StyleProps);
|
||||
return (
|
||||
<BaseBox
|
||||
@ -292,32 +310,4 @@ const Box: ComponentImplementation<Static<typeof StyleSchema>> = ({
|
||||
<Slot slotsMap={slotsMap} slot="content" />
|
||||
</BaseBox>
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'box',
|
||||
displayName: 'Box',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
description: 'chakra-ui box',
|
||||
exampleProperties: {
|
||||
w: GRID_HEIGHT,
|
||||
h: GRID_HEIGHT,
|
||||
border: '1px solid black',
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
},
|
||||
spec: {
|
||||
properties: StyleSchema,
|
||||
state: {},
|
||||
methods: {},
|
||||
slots: ['content'],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: Box,
|
||||
};
|
||||
});
|
||||
|
@ -1,47 +1,10 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { css } from '@emotion/css';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { Button as BaseButton } from '@chakra-ui/react';
|
||||
import { ComponentImplementation, Text, TextPropertySchema } from '@sunmao-ui/runtime';
|
||||
import { Text, TextPropertySchema, implementRuntimeComponent2 } from '@sunmao-ui/runtime';
|
||||
import { ColorSchemePropertySchema } from './Types/ColorScheme';
|
||||
|
||||
const Button: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
text,
|
||||
mergeState,
|
||||
subscribeMethods,
|
||||
callbackMap: callbacks,
|
||||
colorScheme,
|
||||
isLoading,
|
||||
customStyle,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
mergeState({ value: text.raw });
|
||||
}, [text.raw]);
|
||||
|
||||
const ref = useRef<HTMLButtonElement>(null);
|
||||
useEffect(() => {
|
||||
subscribeMethods({
|
||||
click() {
|
||||
ref.current?.click();
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<BaseButton
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
{...{ colorScheme, isLoading }}
|
||||
ref={ref}
|
||||
onClick={callbacks?.onClick}
|
||||
>
|
||||
<Text value={text} />
|
||||
</BaseButton>
|
||||
);
|
||||
};
|
||||
|
||||
const StateSchema = Type.Object({
|
||||
value: Type.String(),
|
||||
});
|
||||
@ -52,37 +15,68 @@ const PropsSchema = Type.Object({
|
||||
isLoading: Type.Optional(Type.Boolean()),
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'button',
|
||||
displayName: 'Button',
|
||||
description: 'chakra-ui button',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
text: {
|
||||
raw: 'text',
|
||||
format: 'plain',
|
||||
},
|
||||
colorScheme: 'blue',
|
||||
isLoading: false,
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'button',
|
||||
displayName: 'Button',
|
||||
description: 'chakra-ui button',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
text: {
|
||||
raw: 'text',
|
||||
format: 'plain',
|
||||
},
|
||||
exampleSize: [2, 1],
|
||||
colorScheme: 'blue',
|
||||
isLoading: false,
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: [
|
||||
{
|
||||
name: 'click',
|
||||
exampleSize: [2, 1],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {
|
||||
click: void 0,
|
||||
},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: ['onClick'],
|
||||
},
|
||||
})(
|
||||
({
|
||||
text,
|
||||
mergeState,
|
||||
subscribeMethods,
|
||||
callbackMap: callbacks,
|
||||
colorScheme,
|
||||
isLoading,
|
||||
customStyle,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
mergeState({ value: text.raw });
|
||||
}, [text.raw]);
|
||||
|
||||
const ref = useRef<HTMLButtonElement>(null);
|
||||
useEffect(() => {
|
||||
subscribeMethods({
|
||||
click() {
|
||||
ref.current?.click();
|
||||
},
|
||||
],
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: ['onClick'],
|
||||
},
|
||||
}),
|
||||
impl: Button,
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<BaseButton
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
{...{ colorScheme, isLoading }}
|
||||
ref={ref}
|
||||
onClick={callbacks?.onClick}
|
||||
>
|
||||
<Text value={text} />
|
||||
</BaseButton>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { Checkbox as BaseCheckbox, useCheckboxGroupContext } from '@chakra-ui/react';
|
||||
import { ComponentImplementation, Text, TextPropertySchema } from '@sunmao-ui/runtime';
|
||||
import { implementRuntimeComponent2, Text, TextPropertySchema } from '@sunmao-ui/runtime';
|
||||
import { ColorSchemePropertySchema } from './Types/ColorScheme';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
@ -16,84 +15,11 @@ export const SizePropertySchema = Type.KeyOf(
|
||||
);
|
||||
|
||||
export const CheckboxStateSchema = Type.Object({
|
||||
value: Type.String(),
|
||||
Text: Type.String(),
|
||||
value: Type.Union([Type.String(), Type.Number()]),
|
||||
text: Type.String(),
|
||||
checked: Type.Boolean(),
|
||||
});
|
||||
|
||||
const Checkbox: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
text,
|
||||
value,
|
||||
defaultIsChecked,
|
||||
isDisabled,
|
||||
isFocusable,
|
||||
isInValid,
|
||||
isReadOnly,
|
||||
isRequired,
|
||||
size,
|
||||
spacing,
|
||||
colorScheme,
|
||||
mergeState,
|
||||
customStyle,
|
||||
}) => {
|
||||
const groupContext = useCheckboxGroupContext();
|
||||
let _defaultIsChecked = false;
|
||||
if (typeof defaultIsChecked === 'boolean') {
|
||||
_defaultIsChecked = defaultIsChecked;
|
||||
} else if (groupContext) {
|
||||
_defaultIsChecked = groupContext.value.some(val => val === value);
|
||||
}
|
||||
const [checked, setChecked] = useState(_defaultIsChecked);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ text: text.raw });
|
||||
}, [text.raw]);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ value });
|
||||
}, [value]);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ checked });
|
||||
}, [checked]);
|
||||
|
||||
useEffect(() => {
|
||||
setChecked(!!defaultIsChecked);
|
||||
}, [setChecked, defaultIsChecked]);
|
||||
|
||||
const args: {
|
||||
colorScheme?: Static<typeof ColorSchemePropertySchema>;
|
||||
size?: Static<typeof SizePropertySchema>;
|
||||
} = {};
|
||||
|
||||
if (colorScheme) args.colorScheme = colorScheme;
|
||||
if (size) args.size = size;
|
||||
|
||||
return (
|
||||
<BaseCheckbox
|
||||
height="10"
|
||||
value={value}
|
||||
isChecked={checked}
|
||||
isDisabled={isDisabled}
|
||||
isFocusable={isFocusable}
|
||||
isInvalid={isInValid}
|
||||
isReadOnly={isReadOnly}
|
||||
isRequired={isRequired}
|
||||
size={size}
|
||||
spacing={spacing}
|
||||
colorScheme={colorScheme}
|
||||
onChange={e => {
|
||||
setChecked(e.target.checked);
|
||||
}}
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
>
|
||||
<Text value={text} />
|
||||
</BaseCheckbox>
|
||||
);
|
||||
};
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
text: TextPropertySchema,
|
||||
value: Type.Union([Type.String(), Type.Number()]),
|
||||
@ -108,35 +34,105 @@ const PropsSchema = Type.Object({
|
||||
colorScheme: ColorSchemePropertySchema,
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'checkbox',
|
||||
description: 'chakra-ui checkbox',
|
||||
displayName: 'Checkbox',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
text: {
|
||||
raw: 'Checkbox',
|
||||
format: 'plain',
|
||||
},
|
||||
value: 'checkbox 1',
|
||||
defaultIsChecked: true,
|
||||
isDisabled: false,
|
||||
size: 'md',
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'checkbox',
|
||||
description: 'chakra-ui checkbox',
|
||||
displayName: 'Checkbox',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
text: {
|
||||
raw: 'Checkbox',
|
||||
format: 'plain',
|
||||
},
|
||||
exampleSize: [3, 1],
|
||||
value: 'checkbox 1',
|
||||
defaultIsChecked: true,
|
||||
isDisabled: false,
|
||||
size: 'md',
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: CheckboxStateSchema,
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: Checkbox,
|
||||
};
|
||||
exampleSize: [3, 1],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: CheckboxStateSchema,
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
})(
|
||||
({
|
||||
text,
|
||||
value,
|
||||
defaultIsChecked,
|
||||
isDisabled,
|
||||
isFocusable,
|
||||
isInValid,
|
||||
isReadOnly,
|
||||
isRequired,
|
||||
size,
|
||||
spacing,
|
||||
colorScheme,
|
||||
mergeState,
|
||||
customStyle,
|
||||
}) => {
|
||||
const groupContext = useCheckboxGroupContext();
|
||||
let _defaultIsChecked = false;
|
||||
if (typeof defaultIsChecked === 'boolean') {
|
||||
_defaultIsChecked = defaultIsChecked;
|
||||
} else if (groupContext) {
|
||||
_defaultIsChecked = groupContext.value.some(val => val === value);
|
||||
}
|
||||
const [checked, setChecked] = useState(_defaultIsChecked);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ text: text.raw });
|
||||
}, [text.raw]);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ value });
|
||||
}, [value]);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ checked });
|
||||
}, [checked]);
|
||||
|
||||
useEffect(() => {
|
||||
setChecked(!!defaultIsChecked);
|
||||
}, [setChecked, defaultIsChecked]);
|
||||
|
||||
const args: {
|
||||
colorScheme?: Static<typeof ColorSchemePropertySchema>;
|
||||
size?: Static<typeof SizePropertySchema>;
|
||||
} = {};
|
||||
|
||||
if (colorScheme) args.colorScheme = colorScheme;
|
||||
if (size) args.size = size;
|
||||
|
||||
return (
|
||||
<BaseCheckbox
|
||||
height="10"
|
||||
value={value}
|
||||
isChecked={checked}
|
||||
isDisabled={isDisabled}
|
||||
isFocusable={isFocusable}
|
||||
isInvalid={isInValid}
|
||||
isReadOnly={isReadOnly}
|
||||
isRequired={isRequired}
|
||||
size={size}
|
||||
spacing={spacing}
|
||||
colorScheme={colorScheme}
|
||||
onChange={e => {
|
||||
setChecked(e.target.checked);
|
||||
}}
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
>
|
||||
<Text value={text} />
|
||||
</BaseCheckbox>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -1,25 +1,43 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { CheckboxGroup as BaseCheckboxGroup } from '@chakra-ui/react';
|
||||
import { ComponentImplementation, Slot } from '@sunmao-ui/runtime';
|
||||
import { implementRuntimeComponent2, Slot } from '@sunmao-ui/runtime';
|
||||
import { SizePropertySchema, IsDisabledSchema } from './Checkbox';
|
||||
|
||||
const DefaultValueSchema = Type.Optional(
|
||||
Type.Array(Type.Union([Type.String(), Type.Number()]))
|
||||
);
|
||||
const DefaultValueSchema = Type.Array(Type.Union([Type.String(), Type.Number()]));
|
||||
|
||||
const StateSchema = Type.Object({
|
||||
value: Type.String(),
|
||||
value: DefaultValueSchema,
|
||||
});
|
||||
|
||||
const CheckboxGroup: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
size,
|
||||
defaultValue,
|
||||
isDisabled,
|
||||
slotsMap,
|
||||
mergeState,
|
||||
}) => {
|
||||
const PropsSchema = Type.Object({
|
||||
size: SizePropertySchema,
|
||||
isDisabled: IsDisabledSchema,
|
||||
defaultValue: Type.Optional(DefaultValueSchema),
|
||||
});
|
||||
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'checkbox_group',
|
||||
displayName: 'Checkbox Group',
|
||||
description: 'chakra-ui checkbox group',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
defaultValue: [],
|
||||
},
|
||||
exampleSize: [3, 3],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
slots: ['content'],
|
||||
styleSlots: [],
|
||||
events: [],
|
||||
},
|
||||
})(({ size, defaultValue, isDisabled, slotsMap, mergeState }) => {
|
||||
const [value, setValue] = useState(defaultValue);
|
||||
useEffect(() => {
|
||||
mergeState({ value });
|
||||
@ -35,36 +53,4 @@ const CheckboxGroup: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
<Slot slotsMap={slotsMap} slot="content" />
|
||||
</BaseCheckboxGroup>
|
||||
);
|
||||
};
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
size: SizePropertySchema,
|
||||
isDisabled: IsDisabledSchema,
|
||||
defaultValue: DefaultValueSchema,
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'checkbox_group',
|
||||
displayName: 'Checkbox Group',
|
||||
description: 'chakra-ui checkbox group',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
defaultValue: [],
|
||||
},
|
||||
exampleSize: [3, 3],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
slots: ['content'],
|
||||
styleSlots: [],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: CheckboxGroup,
|
||||
};
|
||||
|
@ -1,6 +1,9 @@
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { ComponentImplementation, Slot, DIALOG_CONTAINER_ID } from '@sunmao-ui/runtime';
|
||||
import {
|
||||
implementRuntimeComponent2,
|
||||
Slot,
|
||||
DIALOG_CONTAINER_ID,
|
||||
} from '@sunmao-ui/runtime';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogBody,
|
||||
@ -12,7 +15,7 @@ import {
|
||||
ModalContentProps,
|
||||
ModalOverlayProps,
|
||||
} from '@chakra-ui/react';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { ColorSchemePropertySchema } from './Types/ColorScheme';
|
||||
|
||||
const HandleButtonPropertySchema = Type.Object({
|
||||
@ -20,104 +23,6 @@ const HandleButtonPropertySchema = Type.Object({
|
||||
colorScheme: ColorSchemePropertySchema,
|
||||
});
|
||||
|
||||
const Dialog: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
slotsMap,
|
||||
subscribeMethods,
|
||||
callbackMap: callbacks,
|
||||
title: customerTitle,
|
||||
disableConfirm,
|
||||
confirmButton = {
|
||||
text: 'confirm',
|
||||
colorScheme: 'red',
|
||||
},
|
||||
cancelButton = {
|
||||
text: 'cancel',
|
||||
colorScheme: 'blue',
|
||||
},
|
||||
customStyle,
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [title, setTitle] = useState(customerTitle || '');
|
||||
const cancelRef = useRef(null);
|
||||
const containerRef = useRef<HTMLElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
containerRef.current = document.getElementById(DIALOG_CONTAINER_ID);
|
||||
}, [containerRef]);
|
||||
|
||||
useEffect(() => {
|
||||
subscribeMethods({
|
||||
openDialog({ title }) {
|
||||
setIsOpen(true);
|
||||
setTitle(title);
|
||||
},
|
||||
confirmDialog() {
|
||||
setIsOpen(false);
|
||||
},
|
||||
cancelDialog() {
|
||||
setIsOpen(false);
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
const dialogContentProps: ModalContentProps = {
|
||||
position: 'absolute',
|
||||
width: 'full',
|
||||
containerProps: { position: 'absolute', width: 'full', height: 'full' },
|
||||
};
|
||||
|
||||
const dialogOverlayProps: ModalOverlayProps = {
|
||||
position: 'absolute',
|
||||
width: 'full',
|
||||
height: 'full',
|
||||
};
|
||||
|
||||
const portalProps = {
|
||||
appendToParentPortal: true,
|
||||
containerRef,
|
||||
};
|
||||
|
||||
return (
|
||||
<AlertDialog
|
||||
isOpen={isOpen}
|
||||
leastDestructiveRef={cancelRef}
|
||||
onClose={() => setIsOpen(false)}
|
||||
trapFocus={false}
|
||||
portalProps={containerRef.current ? portalProps : undefined}
|
||||
>
|
||||
<AlertDialogOverlay {...(containerRef.current ? dialogOverlayProps : {})}>
|
||||
<AlertDialogContent
|
||||
className={`${customStyle?.content}`}
|
||||
{...(containerRef.current ? dialogContentProps : {})}
|
||||
>
|
||||
<AlertDialogHeader>{title}</AlertDialogHeader>
|
||||
<AlertDialogBody>
|
||||
<Slot slotsMap={slotsMap} slot="content" />
|
||||
</AlertDialogBody>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<Button
|
||||
ref={cancelRef}
|
||||
colorScheme={cancelButton.colorScheme}
|
||||
onClick={callbacks?.cancelDialog}
|
||||
>
|
||||
{cancelButton.text}
|
||||
</Button>
|
||||
<Button
|
||||
disabled={disableConfirm}
|
||||
colorScheme={confirmButton.colorScheme}
|
||||
onClick={callbacks?.confirmDialog}
|
||||
ml={3}
|
||||
>
|
||||
{confirmButton.text}
|
||||
</Button>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogOverlay>
|
||||
</AlertDialog>
|
||||
);
|
||||
};
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
title: Type.Optional(Type.String()),
|
||||
confirmButton: HandleButtonPropertySchema,
|
||||
@ -125,44 +30,132 @@ const PropsSchema = Type.Object({
|
||||
disableConfirm: Type.Optional(Type.Boolean()),
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'dialog',
|
||||
displayName: 'Dialog',
|
||||
description: 'chakra_ui dialog',
|
||||
isDraggable: false,
|
||||
isResizable: false,
|
||||
exampleProperties: {
|
||||
title: 'Dialog',
|
||||
confirmButton: 'Confirm',
|
||||
cancelButton: 'Cancel',
|
||||
disableConfirm: false,
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'dialog',
|
||||
displayName: 'Dialog',
|
||||
description: 'chakra_ui dialog',
|
||||
isDraggable: false,
|
||||
isResizable: false,
|
||||
exampleProperties: {
|
||||
title: 'Dialog',
|
||||
confirmButton: 'Confirm',
|
||||
cancelButton: 'Cancel',
|
||||
disableConfirm: false,
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: {},
|
||||
methods: [
|
||||
{
|
||||
name: 'openDialog',
|
||||
parameters: Type.Object({
|
||||
title: Type.String(),
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'confirmDialog',
|
||||
},
|
||||
{
|
||||
name: 'cancelDialog',
|
||||
},
|
||||
],
|
||||
slots: ['content'],
|
||||
styleSlots: ['content'],
|
||||
events: ['cancelDialog', 'confirmDialog'],
|
||||
exampleSize: [6, 6],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: Type.Object({}),
|
||||
methods: {
|
||||
openDialog: Type.Object({
|
||||
title: Type.String(),
|
||||
}),
|
||||
confirmDialog: void 0,
|
||||
cancelDialog: void 0,
|
||||
},
|
||||
}),
|
||||
impl: Dialog,
|
||||
};
|
||||
slots: ['content'],
|
||||
styleSlots: ['content'],
|
||||
events: ['cancelDialog', 'confirmDialog'],
|
||||
},
|
||||
})(
|
||||
({
|
||||
slotsMap,
|
||||
subscribeMethods,
|
||||
callbackMap: callbacks,
|
||||
title: customerTitle,
|
||||
disableConfirm,
|
||||
confirmButton = {
|
||||
text: 'confirm',
|
||||
colorScheme: 'red',
|
||||
},
|
||||
cancelButton = {
|
||||
text: 'cancel',
|
||||
colorScheme: 'blue',
|
||||
},
|
||||
customStyle,
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [title, setTitle] = useState(customerTitle || '');
|
||||
const cancelRef = useRef(null);
|
||||
const containerRef = useRef<HTMLElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
containerRef.current = document.getElementById(DIALOG_CONTAINER_ID);
|
||||
}, [containerRef]);
|
||||
|
||||
useEffect(() => {
|
||||
subscribeMethods({
|
||||
openDialog({ title }) {
|
||||
setIsOpen(true);
|
||||
setTitle(title);
|
||||
},
|
||||
confirmDialog() {
|
||||
setIsOpen(false);
|
||||
},
|
||||
cancelDialog() {
|
||||
setIsOpen(false);
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
const dialogContentProps: ModalContentProps = {
|
||||
position: 'absolute',
|
||||
width: 'full',
|
||||
containerProps: { position: 'absolute', width: 'full', height: 'full' },
|
||||
};
|
||||
|
||||
const dialogOverlayProps: ModalOverlayProps = {
|
||||
position: 'absolute',
|
||||
width: 'full',
|
||||
height: 'full',
|
||||
};
|
||||
|
||||
const portalProps = {
|
||||
appendToParentPortal: true,
|
||||
containerRef,
|
||||
};
|
||||
|
||||
return (
|
||||
<AlertDialog
|
||||
isOpen={isOpen}
|
||||
leastDestructiveRef={cancelRef}
|
||||
onClose={() => setIsOpen(false)}
|
||||
trapFocus={false}
|
||||
portalProps={containerRef.current ? portalProps : undefined}
|
||||
>
|
||||
<AlertDialogOverlay {...(containerRef.current ? dialogOverlayProps : {})}>
|
||||
<AlertDialogContent
|
||||
className={`${customStyle?.content}`}
|
||||
{...(containerRef.current ? dialogContentProps : {})}
|
||||
>
|
||||
<AlertDialogHeader>{title}</AlertDialogHeader>
|
||||
<AlertDialogBody>
|
||||
<Slot slotsMap={slotsMap} slot="content" />
|
||||
</AlertDialogBody>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<Button
|
||||
ref={cancelRef}
|
||||
colorScheme={cancelButton.colorScheme}
|
||||
onClick={callbacks?.cancelDialog}
|
||||
>
|
||||
{cancelButton.text}
|
||||
</Button>
|
||||
<Button
|
||||
disabled={disableConfirm}
|
||||
colorScheme={confirmButton.colorScheme}
|
||||
onClick={callbacks?.confirmDialog}
|
||||
ml={3}
|
||||
>
|
||||
{confirmButton.text}
|
||||
</Button>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogOverlay>
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -1,9 +1,28 @@
|
||||
import { Divider } from '@chakra-ui/react';
|
||||
import { css } from '@emotion/css';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { ComponentImplementation } from '@sunmao-ui/runtime';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { implementRuntimeComponent2 } from '@sunmao-ui/runtime';
|
||||
|
||||
const DividerImpl: ComponentImplementation = ({ customStyle }) => {
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'divider',
|
||||
displayName: 'Divider',
|
||||
description: 'chakra-ui divider',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {},
|
||||
exampleSize: [4, 1],
|
||||
},
|
||||
spec: {
|
||||
properties: Type.Object({}),
|
||||
state: Type.Object({}),
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
})(({ customStyle }) => {
|
||||
return (
|
||||
<Divider
|
||||
className={css`
|
||||
@ -11,28 +30,4 @@ const DividerImpl: ComponentImplementation = ({ customStyle }) => {
|
||||
`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'divider',
|
||||
displayName: 'Divider',
|
||||
description: 'chakra-ui divider',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {},
|
||||
exampleSize: [4, 1],
|
||||
},
|
||||
spec: {
|
||||
properties: {},
|
||||
state: {},
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: DividerImpl,
|
||||
};
|
||||
});
|
||||
|
@ -1,164 +1,158 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
import { Type, Static } from '@sinclair/typebox';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { Button, VStack } from '@chakra-ui/react';
|
||||
import { ComponentImplementation, Slot, watch } from '@sunmao-ui/runtime';
|
||||
|
||||
const FormImpl: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
mergeState,
|
||||
subscribeMethods,
|
||||
hideSubmit,
|
||||
slotsMap,
|
||||
callbackMap,
|
||||
services,
|
||||
customStyle,
|
||||
}) => {
|
||||
const [invalidArray, setInvalidArray] = useState<boolean[]>([]);
|
||||
const [isFormInvalid, setIsFormInvalid] = useState<boolean>(false);
|
||||
const formDataRef = useRef<Record<string, any>>({});
|
||||
const formControlIds = useMemo<string[]>(() => {
|
||||
return (
|
||||
slotsMap?.get('content')?.map(slot => {
|
||||
return slot.id;
|
||||
}) || []
|
||||
);
|
||||
}, [slotsMap]);
|
||||
|
||||
useEffect(() => {
|
||||
setInvalidArray(
|
||||
formControlIds.map(fcid => {
|
||||
return services.stateManager.store[fcid].isInvalid;
|
||||
})
|
||||
);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const disable = invalidArray.some(v => v);
|
||||
setIsFormInvalid(disable);
|
||||
mergeState({
|
||||
disableSubmit: disable,
|
||||
});
|
||||
}, [invalidArray]);
|
||||
|
||||
useEffect(() => {
|
||||
subscribeMethods({
|
||||
resetForm() {
|
||||
formControlIds.forEach(fcId => {
|
||||
const inputId = services.stateManager.store[fcId].inputId;
|
||||
services.apiService.send('uiMethod', {
|
||||
componentId: inputId,
|
||||
name: 'resetInputValue',
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
}, [formControlIds]);
|
||||
|
||||
useEffect(() => {
|
||||
const stops: ReturnType<typeof watch>[] = [];
|
||||
formControlIds.forEach((fcId, i) => {
|
||||
// watch isInvalid
|
||||
let stop = watch(
|
||||
() => {
|
||||
return services.stateManager.store[fcId].isInvalid;
|
||||
},
|
||||
newV => {
|
||||
setInvalidArray(oldValidArray => {
|
||||
const newValidArray = [...oldValidArray];
|
||||
newValidArray[i] = newV;
|
||||
return newValidArray;
|
||||
});
|
||||
}
|
||||
);
|
||||
stops.push(stop);
|
||||
|
||||
// watch value
|
||||
stop = watch(
|
||||
() => {
|
||||
return services.stateManager.store[fcId].value;
|
||||
},
|
||||
newV => {
|
||||
const fcState = services.stateManager.store[fcId];
|
||||
formDataRef.current[fcState.fieldName] = newV;
|
||||
mergeState({ data: { ...formDataRef.current } });
|
||||
}
|
||||
);
|
||||
stops.push(stop);
|
||||
});
|
||||
|
||||
return () => {
|
||||
stops.forEach(s => {
|
||||
s();
|
||||
});
|
||||
};
|
||||
}, [formControlIds]);
|
||||
|
||||
const onSubmit = () => {
|
||||
callbackMap?.onSubmit();
|
||||
};
|
||||
|
||||
return (
|
||||
<VStack
|
||||
width="full"
|
||||
height="full"
|
||||
padding="4"
|
||||
background="white"
|
||||
border="1px solid"
|
||||
borderColor="gray.200"
|
||||
borderRadius="4"
|
||||
spacing="5"
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
>
|
||||
<Slot slotsMap={slotsMap} slot="content" />
|
||||
{hideSubmit ? undefined : (
|
||||
<Button
|
||||
marginInlineStart="auto !important"
|
||||
disabled={isFormInvalid}
|
||||
onClick={onSubmit}
|
||||
>
|
||||
提交
|
||||
</Button>
|
||||
)}
|
||||
</VStack>
|
||||
);
|
||||
};
|
||||
import { implementRuntimeComponent2, Slot, watch } from '@sunmao-ui/runtime';
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
hideSubmit: Type.Boolean(),
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'form',
|
||||
displayName: 'Form',
|
||||
description: 'chakra-ui form',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
hideSubmit: false,
|
||||
},
|
||||
exampleSize: [4, 6],
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'form',
|
||||
displayName: 'Form',
|
||||
description: 'chakra-ui form',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
hideSubmit: false,
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: Type.Object({
|
||||
data: Type.Any(),
|
||||
disableSubmit: Type.Boolean(),
|
||||
}),
|
||||
methods: [
|
||||
{
|
||||
name: 'resetForm',
|
||||
exampleSize: [4, 6],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: Type.Object({
|
||||
data: Type.Any(),
|
||||
disableSubmit: Type.Boolean(),
|
||||
}),
|
||||
methods: {
|
||||
resetForm: void 0,
|
||||
},
|
||||
slots: ['content'],
|
||||
styleSlots: ['content'],
|
||||
events: ['onSubmit'],
|
||||
},
|
||||
})(
|
||||
({
|
||||
mergeState,
|
||||
subscribeMethods,
|
||||
hideSubmit,
|
||||
slotsMap,
|
||||
callbackMap,
|
||||
services,
|
||||
customStyle,
|
||||
}) => {
|
||||
const [invalidArray, setInvalidArray] = useState<boolean[]>([]);
|
||||
const [isFormInvalid, setIsFormInvalid] = useState<boolean>(false);
|
||||
const formDataRef = useRef<Record<string, any>>({});
|
||||
const formControlIds = useMemo<string[]>(() => {
|
||||
return (
|
||||
slotsMap?.get('content')?.map(slot => {
|
||||
return slot.id;
|
||||
}) || []
|
||||
);
|
||||
}, [slotsMap]);
|
||||
|
||||
useEffect(() => {
|
||||
setInvalidArray(
|
||||
formControlIds.map(fcid => {
|
||||
return services.stateManager.store[fcid].isInvalid;
|
||||
})
|
||||
);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const disable = invalidArray.some(v => v);
|
||||
setIsFormInvalid(disable);
|
||||
mergeState({
|
||||
disableSubmit: disable,
|
||||
});
|
||||
}, [invalidArray]);
|
||||
|
||||
useEffect(() => {
|
||||
subscribeMethods({
|
||||
resetForm() {
|
||||
formControlIds.forEach(fcId => {
|
||||
const inputId = services.stateManager.store[fcId].inputId;
|
||||
services.apiService.send('uiMethod', {
|
||||
componentId: inputId,
|
||||
name: 'resetInputValue',
|
||||
});
|
||||
});
|
||||
},
|
||||
],
|
||||
slots: ['content'],
|
||||
styleSlots: ['content'],
|
||||
events: ['onSubmit'],
|
||||
},
|
||||
}),
|
||||
impl: FormImpl,
|
||||
};
|
||||
});
|
||||
}, [formControlIds]);
|
||||
|
||||
useEffect(() => {
|
||||
const stops: ReturnType<typeof watch>[] = [];
|
||||
formControlIds.forEach((fcId, i) => {
|
||||
// watch isInvalid
|
||||
let stop = watch(
|
||||
() => {
|
||||
return services.stateManager.store[fcId].isInvalid;
|
||||
},
|
||||
newV => {
|
||||
setInvalidArray(oldValidArray => {
|
||||
const newValidArray = [...oldValidArray];
|
||||
newValidArray[i] = newV;
|
||||
return newValidArray;
|
||||
});
|
||||
}
|
||||
);
|
||||
stops.push(stop);
|
||||
|
||||
// watch value
|
||||
stop = watch(
|
||||
() => {
|
||||
return services.stateManager.store[fcId].value;
|
||||
},
|
||||
newV => {
|
||||
const fcState = services.stateManager.store[fcId];
|
||||
formDataRef.current[fcState.fieldName] = newV;
|
||||
mergeState({ data: { ...formDataRef.current } });
|
||||
}
|
||||
);
|
||||
stops.push(stop);
|
||||
});
|
||||
|
||||
return () => {
|
||||
stops.forEach(s => {
|
||||
s();
|
||||
});
|
||||
};
|
||||
}, [formControlIds]);
|
||||
|
||||
const onSubmit = () => {
|
||||
callbackMap?.onSubmit();
|
||||
};
|
||||
|
||||
return (
|
||||
<VStack
|
||||
width="full"
|
||||
height="full"
|
||||
padding="4"
|
||||
background="white"
|
||||
border="1px solid"
|
||||
borderColor="gray.200"
|
||||
borderRadius="4"
|
||||
spacing="5"
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
>
|
||||
<Slot slotsMap={slotsMap} slot="content" />
|
||||
{hideSubmit ? undefined : (
|
||||
<Button
|
||||
marginInlineStart="auto !important"
|
||||
disabled={isFormInvalid}
|
||||
onClick={onSubmit}
|
||||
>
|
||||
提交
|
||||
</Button>
|
||||
)}
|
||||
</VStack>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { first } from 'lodash-es';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import {
|
||||
FormControl,
|
||||
@ -11,7 +10,7 @@ import {
|
||||
Text,
|
||||
} from '@chakra-ui/react';
|
||||
import { css } from '@emotion/css';
|
||||
import { ComponentImplementation, Slot, watch } from '@sunmao-ui/runtime';
|
||||
import { implementRuntimeComponent2, Slot, watch } from '@sunmao-ui/runtime';
|
||||
import { CheckboxStateSchema } from '../Checkbox';
|
||||
|
||||
const FormItemCSS = {
|
||||
@ -19,112 +18,6 @@ const FormItemCSS = {
|
||||
width: '66%',
|
||||
};
|
||||
|
||||
const FormControlImpl: ComponentImplementation<{
|
||||
label: string;
|
||||
fieldName: string;
|
||||
isRequired: boolean;
|
||||
helperText: string;
|
||||
}> = ({
|
||||
label,
|
||||
fieldName,
|
||||
isRequired,
|
||||
helperText,
|
||||
slotsMap,
|
||||
mergeState,
|
||||
services,
|
||||
customStyle,
|
||||
}) => {
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
// don't show Invalid state on component mount
|
||||
const [hideInvalid, setHideInvalid] = useState(true);
|
||||
const inputId = useMemo(() => first(slotsMap?.get('content'))?.id || '', []);
|
||||
const [validResult, setValidResult] = useState({
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
});
|
||||
const { isInvalid, errorMsg } = validResult;
|
||||
|
||||
useEffect(() => {
|
||||
if (!inputId) return;
|
||||
const stop = watch(
|
||||
() => {
|
||||
const inputState = services.stateManager.store[inputId];
|
||||
if (!inputState) return '';
|
||||
if (inputState.checked !== undefined) {
|
||||
// special treatment for checkbox
|
||||
return (inputState as Static<typeof CheckboxStateSchema>).checked;
|
||||
} else {
|
||||
return inputState.value;
|
||||
}
|
||||
},
|
||||
newV => {
|
||||
setInputValue(newV);
|
||||
}
|
||||
);
|
||||
setInputValue(services.stateManager.store[inputId].value);
|
||||
return stop;
|
||||
}, [inputId, setInputValue]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!inputId) return;
|
||||
const stop = watch(
|
||||
() => {
|
||||
return services.stateManager.store[inputId]?.validResult;
|
||||
},
|
||||
newV => {
|
||||
setValidResult(newV);
|
||||
}
|
||||
);
|
||||
if (services.stateManager.store[inputId]?.validResult) {
|
||||
setValidResult(services.stateManager.store[inputId].validResult);
|
||||
}
|
||||
return stop;
|
||||
}, [inputId, setValidResult]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!inputId) return;
|
||||
if (inputValue) {
|
||||
// After inputValue first change, begin to show Invalid state
|
||||
setHideInvalid(false);
|
||||
}
|
||||
mergeState({
|
||||
inputId: inputId,
|
||||
fieldName,
|
||||
isInvalid: !!(isInvalid || (!inputValue && isRequired)),
|
||||
value: inputValue,
|
||||
});
|
||||
}, [inputId, inputId, fieldName, isInvalid, isRequired, inputValue]);
|
||||
|
||||
const placeholder = <Text color="gray.200">Please Add Input Here</Text>;
|
||||
const slotView = <Slot {...FormItemCSS} slotsMap={slotsMap} slot="content" />;
|
||||
|
||||
return (
|
||||
<FormControl
|
||||
isRequired={isRequired}
|
||||
isInvalid={!hideInvalid && (isInvalid || (!inputValue && isRequired))}
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
alignItems="end"
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
>
|
||||
<HStack width="full">
|
||||
<FormLabel flex="0 0 auto" width="33%" margin="auto 0">
|
||||
{label}
|
||||
</FormLabel>
|
||||
{inputId ? slotView : placeholder}
|
||||
</HStack>
|
||||
{errorMsg ? (
|
||||
<FormErrorMessage {...FormItemCSS}>{errorMsg}</FormErrorMessage>
|
||||
) : undefined}
|
||||
{helperText ? (
|
||||
<FormHelperText {...FormItemCSS}>{helperText}</FormHelperText>
|
||||
) : undefined}
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
label: Type.String(),
|
||||
fieldName: Type.String(),
|
||||
@ -132,36 +25,134 @@ const PropsSchema = Type.Object({
|
||||
helperText: Type.String(),
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'formControl',
|
||||
isResizable: false,
|
||||
isDraggable: true,
|
||||
displayName: 'Form Control',
|
||||
description: 'chakra-ui formControl',
|
||||
exampleProperties: {
|
||||
label: 'name',
|
||||
fieldName: 'name',
|
||||
isRequired: false,
|
||||
helperText: '',
|
||||
},
|
||||
exampleSize: [4, 2],
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'formControl',
|
||||
isResizable: false,
|
||||
isDraggable: true,
|
||||
displayName: 'Form Control',
|
||||
description: 'chakra-ui formControl',
|
||||
exampleProperties: {
|
||||
label: 'name',
|
||||
fieldName: 'name',
|
||||
isRequired: false,
|
||||
helperText: '',
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: Type.Object({
|
||||
inputId: Type.String(),
|
||||
fieldName: Type.String(),
|
||||
isInvalid: Type.Boolean(),
|
||||
value: Type.Any(),
|
||||
}),
|
||||
methods: {},
|
||||
slots: ['content'],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: FormControlImpl,
|
||||
};
|
||||
exampleSize: [4, 2],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: Type.Object({
|
||||
inputId: Type.String(),
|
||||
fieldName: Type.String(),
|
||||
isInvalid: Type.Boolean(),
|
||||
value: Type.Any(),
|
||||
}),
|
||||
methods: {},
|
||||
slots: ['content'],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
})(
|
||||
({
|
||||
label,
|
||||
fieldName,
|
||||
isRequired,
|
||||
helperText,
|
||||
slotsMap,
|
||||
mergeState,
|
||||
services,
|
||||
customStyle,
|
||||
}) => {
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
// don't show Invalid state on component mount
|
||||
const [hideInvalid, setHideInvalid] = useState(true);
|
||||
const inputId = useMemo(() => first(slotsMap?.get('content'))?.id || '', []);
|
||||
const [validResult, setValidResult] = useState({
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
});
|
||||
const { isInvalid, errorMsg } = validResult;
|
||||
|
||||
useEffect(() => {
|
||||
if (!inputId) return;
|
||||
const stop = watch(
|
||||
() => {
|
||||
const inputState = services.stateManager.store[inputId];
|
||||
if (!inputState) return '';
|
||||
if (inputState.checked !== undefined) {
|
||||
// special treatment for checkbox
|
||||
return (inputState as Static<typeof CheckboxStateSchema>).checked;
|
||||
} else {
|
||||
return inputState.value;
|
||||
}
|
||||
},
|
||||
newV => {
|
||||
setInputValue(newV);
|
||||
}
|
||||
);
|
||||
setInputValue(services.stateManager.store[inputId].value);
|
||||
return stop;
|
||||
}, [inputId, setInputValue]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!inputId) return;
|
||||
const stop = watch(
|
||||
() => {
|
||||
return services.stateManager.store[inputId]?.validResult;
|
||||
},
|
||||
newV => {
|
||||
setValidResult(newV);
|
||||
}
|
||||
);
|
||||
if (services.stateManager.store[inputId]?.validResult) {
|
||||
setValidResult(services.stateManager.store[inputId].validResult);
|
||||
}
|
||||
return stop;
|
||||
}, [inputId, setValidResult]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!inputId) return;
|
||||
if (inputValue) {
|
||||
// After inputValue first change, begin to show Invalid state
|
||||
setHideInvalid(false);
|
||||
}
|
||||
mergeState({
|
||||
inputId: inputId,
|
||||
fieldName,
|
||||
isInvalid: !!(isInvalid || (!inputValue && isRequired)),
|
||||
value: inputValue,
|
||||
});
|
||||
}, [inputId, inputId, fieldName, isInvalid, isRequired, inputValue]);
|
||||
|
||||
const placeholder = <Text color="gray.200">Please Add Input Here</Text>;
|
||||
const slotView = <Slot {...FormItemCSS} slotsMap={slotsMap} slot="content" />;
|
||||
|
||||
return (
|
||||
<FormControl
|
||||
isRequired={isRequired}
|
||||
isInvalid={!hideInvalid && (isInvalid || (!inputValue && isRequired))}
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
alignItems="end"
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
>
|
||||
<HStack width="full">
|
||||
<FormLabel flex="0 0 auto" width="33%" margin="auto 0">
|
||||
{label}
|
||||
</FormLabel>
|
||||
{inputId ? slotView : placeholder}
|
||||
</HStack>
|
||||
{errorMsg ? (
|
||||
<FormErrorMessage {...FormItemCSS}>{errorMsg}</FormErrorMessage>
|
||||
) : undefined}
|
||||
{helperText ? (
|
||||
<FormHelperText {...FormItemCSS}>{helperText}</FormHelperText>
|
||||
) : undefined}
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { css } from '@emotion/css';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { HStack as BaseHStack } from '@chakra-ui/react';
|
||||
import { ComponentImplementation, Slot } from '@sunmao-ui/runtime';
|
||||
import { implementRuntimeComponent2, Slot } from '@sunmao-ui/runtime';
|
||||
import {
|
||||
DirectionSchema,
|
||||
FlexWrapSchema,
|
||||
@ -11,15 +10,36 @@ import {
|
||||
SpacingSchema,
|
||||
} from './Stack';
|
||||
|
||||
const HStack: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
direction,
|
||||
wrap,
|
||||
align,
|
||||
justify,
|
||||
spacing,
|
||||
slotsMap,
|
||||
customStyle,
|
||||
}) => {
|
||||
const PropsSchema = Type.Object({
|
||||
direction: DirectionSchema,
|
||||
wrap: FlexWrapSchema,
|
||||
align: AlignItemsSchema,
|
||||
justify: JustifyContentSchema,
|
||||
spacing: SpacingSchema,
|
||||
});
|
||||
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'hstack',
|
||||
description: 'chakra-ui hstack',
|
||||
displayName: 'HStack',
|
||||
exampleProperties: {
|
||||
spacing: '24px',
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: Type.Object({}),
|
||||
slots: ['content'],
|
||||
styleSlots: ['content'],
|
||||
methods: {},
|
||||
events: [],
|
||||
},
|
||||
})(({ direction, wrap, align, justify, spacing, slotsMap, customStyle }) => {
|
||||
return (
|
||||
<BaseHStack
|
||||
height="full"
|
||||
@ -37,33 +57,4 @@ const HStack: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
<Slot slotsMap={slotsMap} slot="content" />
|
||||
</BaseHStack>
|
||||
);
|
||||
};
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
direction: DirectionSchema,
|
||||
wrap: FlexWrapSchema,
|
||||
align: AlignItemsSchema,
|
||||
justify: JustifyContentSchema,
|
||||
spacing: SpacingSchema,
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'hstack',
|
||||
description: 'chakra-ui hstack',
|
||||
displayName: 'HStack',
|
||||
exampleProperties: {
|
||||
spacing: '24px',
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
slots: ['content'],
|
||||
styleSlots: ['content'],
|
||||
},
|
||||
}),
|
||||
impl: HStack,
|
||||
};
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { Image as BaseImage } from '@chakra-ui/react';
|
||||
import { css } from '@emotion/css';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { ComponentImplementation } from '@sunmao-ui/runtime';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { implementRuntimeComponent2 } from '@sunmao-ui/runtime';
|
||||
|
||||
const BoxSizePropertySchema = Type.Optional(
|
||||
Type.Union([
|
||||
@ -64,48 +63,6 @@ const BorderRadiusSchema = Type.Optional(
|
||||
])
|
||||
);
|
||||
|
||||
const Image: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
boxSize,
|
||||
src,
|
||||
alt,
|
||||
objectFit,
|
||||
borderRadius,
|
||||
fallbackSrc,
|
||||
ignoreFallback,
|
||||
htmlWidth,
|
||||
htmlHeight,
|
||||
crossOrigin,
|
||||
callbackMap,
|
||||
customStyle,
|
||||
}) => {
|
||||
const style = boxSize
|
||||
? css`
|
||||
${customStyle?.content}
|
||||
`
|
||||
: css`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
${customStyle?.content}
|
||||
`;
|
||||
return (
|
||||
<BaseImage
|
||||
className={style}
|
||||
src={src}
|
||||
alt={alt}
|
||||
objectFit={objectFit}
|
||||
boxSize={boxSize}
|
||||
onLoad={callbackMap?.onLoad}
|
||||
htmlHeight={htmlHeight}
|
||||
htmlWidth={htmlWidth}
|
||||
crossOrigin={crossOrigin}
|
||||
onError={callbackMap?.onError}
|
||||
ignoreFallback={ignoreFallback}
|
||||
borderRadius={borderRadius}
|
||||
fallbackSrc={fallbackSrc}
|
||||
></BaseImage>
|
||||
);
|
||||
};
|
||||
|
||||
const StateSchema = Type.Object({
|
||||
value: Type.String(),
|
||||
});
|
||||
@ -130,32 +87,71 @@ const PropsSchema = Type.Object({
|
||||
),
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'image',
|
||||
displayName: 'Image',
|
||||
description: 'chakra_ui image',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
src: 'https://bit.ly/dan-abramov',
|
||||
alt: 'dan-abramov',
|
||||
objectFit: 'cover',
|
||||
borderRadius: 5,
|
||||
fallbackSrc: 'https://via.placeholder.com/150',
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'image',
|
||||
displayName: 'Image',
|
||||
description: 'chakra_ui image',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
src: 'https://bit.ly/dan-abramov',
|
||||
alt: 'dan-abramov',
|
||||
objectFit: 'cover',
|
||||
borderRadius: 5,
|
||||
fallbackSrc: 'https://via.placeholder.com/150',
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: ['onLoad', 'onError'],
|
||||
},
|
||||
}),
|
||||
impl: Image,
|
||||
};
|
||||
exampleSize: [6, 6],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: ['onLoad', 'onError'],
|
||||
},
|
||||
})(
|
||||
({
|
||||
boxSize,
|
||||
src,
|
||||
alt,
|
||||
objectFit,
|
||||
borderRadius,
|
||||
fallbackSrc,
|
||||
ignoreFallback,
|
||||
htmlWidth,
|
||||
htmlHeight,
|
||||
crossOrigin,
|
||||
callbackMap,
|
||||
customStyle,
|
||||
}) => {
|
||||
const style = boxSize
|
||||
? css`
|
||||
${customStyle?.content}
|
||||
`
|
||||
: css`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
${customStyle?.content}
|
||||
`;
|
||||
return (
|
||||
<BaseImage
|
||||
className={style}
|
||||
src={src}
|
||||
alt={alt}
|
||||
objectFit={objectFit}
|
||||
boxSize={boxSize}
|
||||
onLoad={callbackMap?.onLoad}
|
||||
htmlHeight={htmlHeight}
|
||||
htmlWidth={htmlWidth}
|
||||
crossOrigin={crossOrigin}
|
||||
onError={callbackMap?.onError}
|
||||
ignoreFallback={ignoreFallback}
|
||||
borderRadius={borderRadius}
|
||||
fallbackSrc={fallbackSrc}
|
||||
></BaseImage>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -7,9 +7,8 @@ import {
|
||||
InputRightAddon,
|
||||
InputRightElement,
|
||||
} from '@chakra-ui/react';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { ComponentImplementation } from '@sunmao-ui/runtime';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { implementRuntimeComponent2 } from '@sunmao-ui/runtime';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
const AppendElementPropertySchema = Type.Union([
|
||||
@ -25,83 +24,6 @@ const AppendElementPropertySchema = Type.Union([
|
||||
}),
|
||||
]);
|
||||
|
||||
const Input: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
variant,
|
||||
placeholder,
|
||||
size,
|
||||
focusBorderColor,
|
||||
isDisabled,
|
||||
isRequired,
|
||||
left,
|
||||
right,
|
||||
mergeState,
|
||||
subscribeMethods,
|
||||
defaultValue,
|
||||
customStyle,
|
||||
}) => {
|
||||
const [value, setValue] = React.useState(defaultValue || ''); // TODO: pin input
|
||||
const onChange = (event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setValue(event.target.value);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ value });
|
||||
}, [value]);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(defaultValue || '');
|
||||
}, [defaultValue]);
|
||||
|
||||
useEffect(() => {
|
||||
subscribeMethods({
|
||||
setInputValue({ value }) {
|
||||
setValue(value);
|
||||
},
|
||||
resetInputValue() {
|
||||
setValue(defaultValue || '');
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<InputGroup size={size} background="white">
|
||||
{left ? (
|
||||
left.type === 'addon' ? (
|
||||
<InputLeftAddon>{left.children}</InputLeftAddon>
|
||||
) : (
|
||||
<InputLeftElement fontSize={left.fontSize} color={left.color}>
|
||||
{left.children}
|
||||
</InputLeftElement>
|
||||
)
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<BaseInput
|
||||
value={value}
|
||||
variant={variant}
|
||||
placeholder={placeholder}
|
||||
focusBorderColor={focusBorderColor}
|
||||
isDisabled={isDisabled}
|
||||
isRequired={isRequired}
|
||||
onChange={onChange}
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
/>
|
||||
{right ? (
|
||||
right.type === 'addon' ? (
|
||||
<InputRightAddon>{right.children}</InputRightAddon>
|
||||
) : (
|
||||
<InputRightElement fontSize={right.fontSize} color={right.color}>
|
||||
{right.children}
|
||||
</InputRightElement>
|
||||
)
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</InputGroup>
|
||||
);
|
||||
};
|
||||
|
||||
const StateSchema = Type.Object({
|
||||
value: Type.String(),
|
||||
});
|
||||
@ -132,43 +54,112 @@ const PropsSchema = Type.Object({
|
||||
defaultValue: Type.Optional(Type.String()),
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'input',
|
||||
displayName: 'Input',
|
||||
description: 'chakra_ui input',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
variant: 'outline',
|
||||
placeholder: 'Please input value',
|
||||
size: 'md',
|
||||
isDisabled: false,
|
||||
isRequired: false,
|
||||
defaultValue: '',
|
||||
},
|
||||
exampleSize: [4, 1],
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'input',
|
||||
displayName: 'Input',
|
||||
description: 'chakra_ui input',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
variant: 'outline',
|
||||
placeholder: 'Please input value',
|
||||
size: 'md',
|
||||
isDisabled: false,
|
||||
isRequired: false,
|
||||
defaultValue: '',
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: [
|
||||
{
|
||||
name: 'setInputValue',
|
||||
parameters: Type.Object({
|
||||
value: Type.String(),
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'resetInputValue',
|
||||
},
|
||||
],
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
exampleSize: [4, 1],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {
|
||||
setInputValue: Type.Object({
|
||||
value: Type.String(),
|
||||
}),
|
||||
resetInputValue: void 0,
|
||||
},
|
||||
}),
|
||||
impl: Input,
|
||||
};
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
})(
|
||||
({
|
||||
variant,
|
||||
placeholder,
|
||||
size,
|
||||
focusBorderColor,
|
||||
isDisabled,
|
||||
isRequired,
|
||||
left,
|
||||
right,
|
||||
mergeState,
|
||||
subscribeMethods,
|
||||
defaultValue,
|
||||
customStyle,
|
||||
}) => {
|
||||
const [value, setValue] = React.useState(defaultValue || ''); // TODO: pin input
|
||||
const onChange = (event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setValue(event.target.value);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ value });
|
||||
}, [value]);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(defaultValue || '');
|
||||
}, [defaultValue]);
|
||||
|
||||
useEffect(() => {
|
||||
subscribeMethods({
|
||||
setInputValue({ value }) {
|
||||
setValue(value);
|
||||
},
|
||||
resetInputValue() {
|
||||
setValue(defaultValue || '');
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<InputGroup size={size} background="white">
|
||||
{left ? (
|
||||
left.type === 'addon' ? (
|
||||
<InputLeftAddon>{left.children}</InputLeftAddon>
|
||||
) : (
|
||||
<InputLeftElement fontSize={left.fontSize} color={left.color}>
|
||||
{left.children}
|
||||
</InputLeftElement>
|
||||
)
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<BaseInput
|
||||
value={value}
|
||||
variant={variant}
|
||||
placeholder={placeholder}
|
||||
focusBorderColor={focusBorderColor}
|
||||
isDisabled={isDisabled}
|
||||
isRequired={isRequired}
|
||||
onChange={onChange}
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
/>
|
||||
{right ? (
|
||||
right.type === 'addon' ? (
|
||||
<InputRightAddon>{right.children}</InputRightAddon>
|
||||
) : (
|
||||
<InputRightElement fontSize={right.fontSize} color={right.color}>
|
||||
{right.children}
|
||||
</InputRightElement>
|
||||
)
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</InputGroup>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -1,15 +1,42 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Kbd as BaseKbd } from '@chakra-ui/react';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { ComponentImplementation, Text, TextPropertySchema } from '@sunmao-ui/runtime';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { implementRuntimeComponent2, Text, TextPropertySchema } from '@sunmao-ui/runtime';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
const Kbd: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
text,
|
||||
mergeState,
|
||||
customStyle,
|
||||
}) => {
|
||||
const StateSchema = Type.Object({
|
||||
value: Type.String(),
|
||||
});
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
text: TextPropertySchema,
|
||||
});
|
||||
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'kbd',
|
||||
displayName: 'Kbd',
|
||||
description: 'chakra-ui keyboard',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
text: {
|
||||
raw: 'enter',
|
||||
format: 'plain',
|
||||
},
|
||||
},
|
||||
exampleSize: [2, 1],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
})(({ text, mergeState, customStyle }) => {
|
||||
useEffect(() => {
|
||||
mergeState({ value: text.raw });
|
||||
}, [text.raw]);
|
||||
@ -23,41 +50,4 @@ const Kbd: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
<Text value={text} />
|
||||
</BaseKbd>
|
||||
);
|
||||
};
|
||||
|
||||
const StateSchema = Type.Object({
|
||||
value: Type.String(),
|
||||
});
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
text: TextPropertySchema,
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'kbd',
|
||||
displayName: 'Kbd',
|
||||
description: 'chakra-ui keyboard',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
text: {
|
||||
raw: 'enter',
|
||||
format: 'plain',
|
||||
},
|
||||
},
|
||||
exampleSize: [2, 1],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: Kbd,
|
||||
};
|
||||
|
@ -1,15 +1,40 @@
|
||||
import { Link } from '@chakra-ui/react';
|
||||
import { css } from '@emotion/css';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { ComponentImplementation, Text, TextPropertySchema } from '@sunmao-ui/runtime';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { implementRuntimeComponent2, Text, TextPropertySchema } from '@sunmao-ui/runtime';
|
||||
|
||||
const LinkImpl: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
text,
|
||||
href,
|
||||
isExternal,
|
||||
customStyle,
|
||||
}) => {
|
||||
const PropsSchema = Type.Object({
|
||||
text: TextPropertySchema,
|
||||
href: Type.String(),
|
||||
isExternal: Type.Optional(Type.Boolean()),
|
||||
});
|
||||
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'link',
|
||||
displayName: 'Link',
|
||||
description: 'chakra-ui link',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
text: {
|
||||
raw: 'link',
|
||||
format: 'plain',
|
||||
},
|
||||
href: 'https://www.google.com',
|
||||
},
|
||||
exampleSize: [2, 1],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: Type.Object({}),
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
})(({ text, href, isExternal, customStyle }) => {
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
@ -22,40 +47,4 @@ const LinkImpl: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
<Text value={text} />
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
text: TextPropertySchema,
|
||||
href: Type.String(),
|
||||
isExternal: Type.Optional(Type.Boolean()),
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'link',
|
||||
displayName: 'Link',
|
||||
description: 'chakra-ui link',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
text: {
|
||||
raw: 'link',
|
||||
format: 'plain',
|
||||
},
|
||||
href: 'https://www.google.com',
|
||||
},
|
||||
exampleSize: [2, 1],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: {},
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: LinkImpl,
|
||||
};
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { List as BaseList, ListItem as BaseListItem } from '@chakra-ui/react';
|
||||
import {
|
||||
ComponentImplementation,
|
||||
implementRuntimeComponent2,
|
||||
LIST_ITEM_EXP,
|
||||
LIST_ITEM_INDEX_EXP,
|
||||
RuntimeModuleSchema,
|
||||
@ -10,13 +9,51 @@ import {
|
||||
} from '@sunmao-ui/runtime';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
const List: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
listData,
|
||||
template,
|
||||
app,
|
||||
services,
|
||||
customStyle,
|
||||
}) => {
|
||||
const PropsSchema = Type.Object({
|
||||
listData: Type.Array(Type.Record(Type.String(), Type.String())),
|
||||
template: RuntimeModuleSchema,
|
||||
});
|
||||
|
||||
const exampleProperties = {
|
||||
listData: [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Bowen Tan',
|
||||
},
|
||||
],
|
||||
template: {
|
||||
id: 'listItemName-{{$listItem.id}}',
|
||||
type: 'core/v1/text',
|
||||
properties: {
|
||||
value: {
|
||||
raw: 'Name:{{$listItem.name}}',
|
||||
format: 'plain',
|
||||
},
|
||||
},
|
||||
traits: [],
|
||||
},
|
||||
};
|
||||
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'list',
|
||||
description: 'chakra-ui list',
|
||||
displayName: 'List',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties,
|
||||
exampleSize: [6, 6],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
methods: {},
|
||||
state: Type.Object({}),
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
})(({ listData, template, app, services, customStyle }) => {
|
||||
if (!listData) {
|
||||
return null;
|
||||
}
|
||||
@ -52,53 +89,4 @@ const List: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
{listItems}
|
||||
</BaseList>
|
||||
);
|
||||
};
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
listData: Type.Array(Type.Record(Type.String(), Type.String())),
|
||||
template: RuntimeModuleSchema,
|
||||
});
|
||||
|
||||
const exampleProperties = {
|
||||
listData: [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Bowen Tan',
|
||||
},
|
||||
],
|
||||
template: {
|
||||
id: 'listItemName-{{$listItem.id}}',
|
||||
type: 'core/v1/text',
|
||||
properties: {
|
||||
value: {
|
||||
raw: 'Name:{{$listItem.name}}',
|
||||
format: 'plain',
|
||||
},
|
||||
},
|
||||
traits: [],
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'list',
|
||||
description: 'chakra-ui list',
|
||||
displayName: 'List',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties,
|
||||
exampleSize: [6, 6],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
methods: {},
|
||||
state: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: List,
|
||||
};
|
||||
|
@ -1,58 +1,14 @@
|
||||
import { useEffect } from 'react';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { Select as BaseMultiSelect } from 'chakra-react-select';
|
||||
import { ComponentImplementation } from '@sunmao-ui/runtime';
|
||||
import { implementRuntimeComponent2 } from '@sunmao-ui/runtime';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
const StateSchema = Type.Object({
|
||||
value: Type.String(),
|
||||
value: Type.Array(Type.String()),
|
||||
});
|
||||
|
||||
const MultiSelect: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
options,
|
||||
placeholder,
|
||||
defaultValue,
|
||||
isRequired,
|
||||
isDisabled,
|
||||
size,
|
||||
variant,
|
||||
mergeState,
|
||||
customStyle,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
const newValue = (defaultValue || []).map(o => o.value);
|
||||
mergeState({ value: newValue });
|
||||
}, []);
|
||||
|
||||
const onChange = (options: Static<typeof OptionsSchema>) => {
|
||||
const newValue = options.map(o => o.value);
|
||||
mergeState({ value: newValue });
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
width="full"
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
>
|
||||
<BaseMultiSelect
|
||||
isMulti
|
||||
options={options}
|
||||
placeholder={placeholder}
|
||||
isRequired={isRequired}
|
||||
isDisabled={isDisabled}
|
||||
size={size}
|
||||
variant={variant}
|
||||
onChange={onChange}
|
||||
defaultValue={defaultValue}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const OptionsSchema = Type.Array(
|
||||
Type.Object({
|
||||
label: Type.String(),
|
||||
@ -107,8 +63,7 @@ const exampleProperties = {
|
||||
],
|
||||
};
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'multiSelect',
|
||||
@ -124,9 +79,48 @@ export default {
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: MultiSelect,
|
||||
};
|
||||
})(({
|
||||
options,
|
||||
placeholder,
|
||||
defaultValue,
|
||||
isRequired,
|
||||
isDisabled,
|
||||
size,
|
||||
variant,
|
||||
mergeState,
|
||||
customStyle,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
const newValue = (defaultValue || []).map(o => o.value);
|
||||
mergeState({ value: newValue });
|
||||
}, []);
|
||||
|
||||
const onChange = (options: Static<typeof OptionsSchema>) => {
|
||||
const newValue = options.map(o => o.value);
|
||||
mergeState({ value: newValue });
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
width="full"
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
>
|
||||
<BaseMultiSelect
|
||||
isMulti
|
||||
options={options}
|
||||
placeholder={placeholder}
|
||||
isRequired={isRequired}
|
||||
isDisabled={isDisabled}
|
||||
size={size}
|
||||
variant={variant}
|
||||
onChange={onChange}
|
||||
defaultValue={defaultValue}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
})
|
@ -6,74 +6,10 @@ import {
|
||||
NumberIncrementStepper,
|
||||
NumberDecrementStepper,
|
||||
} from '@chakra-ui/react';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { ComponentImplementation } from '@sunmao-ui/runtime';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { implementRuntimeComponent2 } from '@sunmao-ui/runtime';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
const NumberInput: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
defaultValue = 0,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
precision,
|
||||
clampValueOnBlur = true,
|
||||
allowMouseWheel = false,
|
||||
size,
|
||||
customerIncrement,
|
||||
customerDecrement,
|
||||
mergeState,
|
||||
subscribeMethods,
|
||||
customStyle,
|
||||
}) => {
|
||||
const [value, setValue] = useState(defaultValue);
|
||||
const onChange = (_: string, valueAsNumber: number) => setValue(valueAsNumber || 0);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ value });
|
||||
}, [value]);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(defaultValue || 0);
|
||||
}, [defaultValue]);
|
||||
|
||||
useEffect(() => {
|
||||
subscribeMethods({
|
||||
setInputValue({ value }) {
|
||||
setValue(value);
|
||||
},
|
||||
resetInputValue() {
|
||||
setValue(defaultValue);
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<BaseNumberInput
|
||||
background="white"
|
||||
defaultValue={defaultValue}
|
||||
value={value}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
precision={precision}
|
||||
clampValueOnBlur={clampValueOnBlur}
|
||||
allowMouseWheel={allowMouseWheel}
|
||||
size={size}
|
||||
onChange={onChange}
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
>
|
||||
<NumberInputField />
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper {...customerIncrement} />
|
||||
<NumberDecrementStepper {...customerDecrement} />
|
||||
</NumberInputStepper>
|
||||
</BaseNumberInput>
|
||||
);
|
||||
};
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
defaultValue: Type.Optional(Type.Number()),
|
||||
min: Type.Optional(Type.Number()),
|
||||
@ -106,38 +42,93 @@ const StateSchema = Type.Object({
|
||||
value: Type.Number(),
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'number_input',
|
||||
description: 'chakra_ui number input',
|
||||
displayName: 'Number Input',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
defaultValue: 0,
|
||||
},
|
||||
exampleSize: [4, 1],
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'number_input',
|
||||
description: 'chakra_ui number input',
|
||||
displayName: 'Number Input',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
defaultValue: 0,
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: [
|
||||
{
|
||||
name: 'setInputValue',
|
||||
parameters: Type.Object({
|
||||
value: Type.Number(),
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'resetInputValue',
|
||||
},
|
||||
],
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
exampleSize: [4, 1],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {
|
||||
setInputValue: Type.Object({
|
||||
value: Type.Number(),
|
||||
}),
|
||||
resetInputValue: void 0,
|
||||
},
|
||||
}),
|
||||
impl: NumberInput,
|
||||
};
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
})(
|
||||
({
|
||||
defaultValue = 0,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
precision,
|
||||
clampValueOnBlur = true,
|
||||
allowMouseWheel = false,
|
||||
size,
|
||||
customerIncrement,
|
||||
customerDecrement,
|
||||
mergeState,
|
||||
subscribeMethods,
|
||||
customStyle,
|
||||
}) => {
|
||||
const [value, setValue] = useState(defaultValue);
|
||||
const onChange = (_: string, valueAsNumber: number) => setValue(valueAsNumber || 0);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ value });
|
||||
}, [value]);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(defaultValue || 0);
|
||||
}, [defaultValue]);
|
||||
|
||||
useEffect(() => {
|
||||
subscribeMethods({
|
||||
setInputValue({ value }) {
|
||||
setValue(value);
|
||||
},
|
||||
resetInputValue() {
|
||||
setValue(defaultValue);
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<BaseNumberInput
|
||||
background="white"
|
||||
defaultValue={defaultValue}
|
||||
value={value}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
precision={precision}
|
||||
clampValueOnBlur={clampValueOnBlur}
|
||||
allowMouseWheel={allowMouseWheel}
|
||||
size={size}
|
||||
onChange={onChange}
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
>
|
||||
<NumberInputField />
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper {...customerIncrement} />
|
||||
<NumberDecrementStepper {...customerDecrement} />
|
||||
</NumberInputStepper>
|
||||
</BaseNumberInput>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -1,60 +1,14 @@
|
||||
import { useEffect } from 'react';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { Radio as BaseRadio } from '@chakra-ui/react';
|
||||
import { ComponentImplementation, Text, TextPropertySchema } from '@sunmao-ui/runtime';
|
||||
import { implementRuntimeComponent2, Text, TextPropertySchema } from '@sunmao-ui/runtime';
|
||||
import { ColorSchemePropertySchema } from './Types/ColorScheme';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
const StateSchema = Type.Object({
|
||||
value: Type.String(),
|
||||
value: Type.Union([Type.String(), Type.Number()]),
|
||||
});
|
||||
|
||||
const Radio: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
text,
|
||||
value,
|
||||
isDisabled,
|
||||
isFocusable,
|
||||
isInValid,
|
||||
isReadOnly,
|
||||
isRequired,
|
||||
name,
|
||||
size,
|
||||
spacing,
|
||||
colorScheme,
|
||||
mergeState,
|
||||
customStyle,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
mergeState({ text: text.raw });
|
||||
}, [text.raw]);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ value });
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<BaseRadio
|
||||
height="10"
|
||||
value={value}
|
||||
isDisabled={isDisabled}
|
||||
isFocusable={isFocusable}
|
||||
isInvalid={isInValid}
|
||||
isReadOnly={isReadOnly}
|
||||
isRequired={isRequired}
|
||||
name={name}
|
||||
size={size}
|
||||
spacing={spacing}
|
||||
colorScheme={colorScheme}
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
>
|
||||
<Text value={text} />
|
||||
</BaseRadio>
|
||||
);
|
||||
};
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
text: TextPropertySchema,
|
||||
value: Type.Union([Type.String(), Type.Number()]),
|
||||
@ -75,8 +29,7 @@ const PropsSchema = Type.Object({
|
||||
colorScheme: ColorSchemePropertySchema,
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'radio',
|
||||
@ -103,6 +56,47 @@ export default {
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: Radio,
|
||||
};
|
||||
})(({
|
||||
text,
|
||||
value,
|
||||
isDisabled,
|
||||
isFocusable,
|
||||
isInValid,
|
||||
isReadOnly,
|
||||
isRequired,
|
||||
name,
|
||||
size,
|
||||
spacing,
|
||||
colorScheme,
|
||||
mergeState,
|
||||
customStyle,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
mergeState({ value: text.raw });
|
||||
}, [text.raw]);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ value });
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<BaseRadio
|
||||
height="10"
|
||||
value={value}
|
||||
isDisabled={isDisabled}
|
||||
isFocusable={isFocusable}
|
||||
isInvalid={isInValid}
|
||||
isReadOnly={isReadOnly}
|
||||
isRequired={isRequired}
|
||||
name={name}
|
||||
size={size}
|
||||
spacing={spacing}
|
||||
colorScheme={colorScheme}
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
>
|
||||
<Text value={text} />
|
||||
</BaseRadio>
|
||||
);
|
||||
})
|
@ -1,21 +1,41 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { RadioGroup as BaseRadioGroup } from '@chakra-ui/react';
|
||||
import { ComponentImplementation, Slot } from '@sunmao-ui/runtime';
|
||||
import { implementRuntimeComponent2, Slot } from '@sunmao-ui/runtime';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
const StateSchema = Type.Object({
|
||||
value: Type.String(),
|
||||
value: Type.Union([Type.String(), Type.Number()]),
|
||||
});
|
||||
|
||||
const RadioGroup: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
defaultValue,
|
||||
isNumerical,
|
||||
slotsMap,
|
||||
mergeState,
|
||||
customStyle,
|
||||
}) => {
|
||||
const PropsSchema = Type.Object({
|
||||
defaultValue: Type.Union([Type.String(), Type.Number()]),
|
||||
isNumerical: Type.Optional(Type.Boolean()),
|
||||
});
|
||||
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'radio_group',
|
||||
displayName: 'RadioGroup',
|
||||
description: 'chakra-ui radio group',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
defaultValue: 0,
|
||||
isNumerical: true,
|
||||
},
|
||||
exampleSize: [3, 3],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
slots: ['content'],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
})(({ defaultValue, isNumerical, slotsMap, mergeState, customStyle }) => {
|
||||
const [value, setValue] = useState(defaultValue);
|
||||
|
||||
useEffect(() => {
|
||||
@ -37,36 +57,4 @@ const RadioGroup: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
<Slot slotsMap={slotsMap} slot="content" />
|
||||
</BaseRadioGroup>
|
||||
);
|
||||
};
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
defaultValue: Type.Union([Type.String(), Type.Number()]),
|
||||
isNumerical: Type.Optional(Type.Boolean()),
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'radio_group',
|
||||
displayName: 'RadioGroup',
|
||||
description: 'chakra-ui radio group',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
defaultValue: 0,
|
||||
isNumerical: true,
|
||||
},
|
||||
exampleSize: [3, 3],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
slots: ['content'],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: RadioGroup,
|
||||
};
|
||||
|
@ -1,8 +1,27 @@
|
||||
import { ChakraProvider, extendTheme } from '@chakra-ui/react';
|
||||
import { ComponentImplementation, Slot } from '@sunmao-ui/runtime';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { implementRuntimeComponent2, Slot } from '@sunmao-ui/runtime';
|
||||
|
||||
const Root: ComponentImplementation<Record<string, unknown>> = ({ slotsMap }) => {
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'root',
|
||||
displayName: 'Root',
|
||||
description: 'chakra-ui provider',
|
||||
isDraggable: false,
|
||||
isResizable: true,
|
||||
exampleProperties: {},
|
||||
exampleSize: [6, 6],
|
||||
},
|
||||
spec: {
|
||||
properties: Type.Object({}),
|
||||
state: Type.Object({}),
|
||||
methods: {},
|
||||
slots: ['root'],
|
||||
styleSlots: [],
|
||||
events: [],
|
||||
},
|
||||
})(({ slotsMap }) => {
|
||||
return (
|
||||
<ChakraProvider
|
||||
theme={extendTheme({
|
||||
@ -13,28 +32,4 @@ const Root: ComponentImplementation<Record<string, unknown>> = ({ slotsMap }) =>
|
||||
<Slot slotsMap={slotsMap} slot="root" />
|
||||
</ChakraProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'root',
|
||||
displayName: 'Root',
|
||||
description: 'chakra-ui provider',
|
||||
isDraggable: false,
|
||||
isResizable: true,
|
||||
exampleProperties: {},
|
||||
exampleSize: [6, 6],
|
||||
},
|
||||
spec: {
|
||||
properties: {},
|
||||
state: {},
|
||||
methods: {},
|
||||
slots: ['root'],
|
||||
styleSlots: [],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: Root,
|
||||
};
|
||||
});
|
||||
|
@ -1,66 +1,13 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { Select as BaseSelect } from '@chakra-ui/react';
|
||||
import { ComponentImplementation } from '@sunmao-ui/runtime';
|
||||
import { implementRuntimeComponent2 } from '@sunmao-ui/runtime';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
const StateSchema = Type.Object({
|
||||
value: Type.String(),
|
||||
});
|
||||
|
||||
const Select: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
options,
|
||||
placeholder,
|
||||
defaultValue,
|
||||
errorBorderColor,
|
||||
focusBorderColor,
|
||||
isDisabled,
|
||||
isInvalid,
|
||||
isReadOnly,
|
||||
isRequired,
|
||||
size,
|
||||
variant,
|
||||
mergeState,
|
||||
customStyle,
|
||||
}) => {
|
||||
const [value, setValue] = useState<string | undefined>(defaultValue);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(defaultValue);
|
||||
}, [defaultValue]);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ value: value });
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<BaseSelect
|
||||
background="white"
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
errorBorderColor={errorBorderColor}
|
||||
focusBorderColor={focusBorderColor}
|
||||
isDisabled={isDisabled}
|
||||
isInvalid={isInvalid}
|
||||
isReadOnly={isReadOnly}
|
||||
isRequired={isRequired}
|
||||
size={size}
|
||||
variant={variant}
|
||||
onChange={e => setValue(e.target.value)}
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
>
|
||||
{options.map(opt => (
|
||||
<option key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</option>
|
||||
))}
|
||||
</BaseSelect>
|
||||
);
|
||||
};
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
options: Type.Array(
|
||||
Type.Object({
|
||||
@ -111,26 +58,75 @@ const exampleProperties = {
|
||||
],
|
||||
};
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'select',
|
||||
displayName: 'Select',
|
||||
description: 'chakra-ui select',
|
||||
isResizable: true,
|
||||
isDraggable: true,
|
||||
exampleProperties,
|
||||
exampleSize: [4, 1],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: Select,
|
||||
};
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'select',
|
||||
displayName: 'Select',
|
||||
description: 'chakra-ui select',
|
||||
isResizable: true,
|
||||
isDraggable: true,
|
||||
exampleProperties,
|
||||
exampleSize: [4, 1],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
})(
|
||||
({
|
||||
options,
|
||||
placeholder,
|
||||
defaultValue,
|
||||
errorBorderColor,
|
||||
focusBorderColor,
|
||||
isDisabled,
|
||||
isInvalid,
|
||||
isReadOnly,
|
||||
isRequired,
|
||||
size,
|
||||
variant,
|
||||
mergeState,
|
||||
customStyle,
|
||||
}) => {
|
||||
const [value, setValue] = useState<string | undefined>(defaultValue);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(defaultValue);
|
||||
}, [defaultValue]);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ value: value });
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<BaseSelect
|
||||
background="white"
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
errorBorderColor={errorBorderColor}
|
||||
focusBorderColor={focusBorderColor}
|
||||
isDisabled={isDisabled}
|
||||
isInvalid={isInvalid}
|
||||
isReadOnly={isReadOnly}
|
||||
isRequired={isRequired}
|
||||
size={size}
|
||||
variant={variant}
|
||||
onChange={e => setValue(e.target.value)}
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
>
|
||||
{options.map(opt => (
|
||||
<option key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</option>
|
||||
))}
|
||||
</BaseSelect>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { Stack as BaseStack } from '@chakra-ui/react';
|
||||
import { ComponentImplementation, Slot } from '@sunmao-ui/runtime';
|
||||
import { implementRuntimeComponent2, Slot } from '@sunmao-ui/runtime';
|
||||
|
||||
export const DirectionSchema = Type.Optional(
|
||||
Type.Union([
|
||||
@ -25,32 +24,19 @@ export const DirectionSchema = Type.Optional(
|
||||
),
|
||||
])
|
||||
);
|
||||
export const FlexWrapSchema = Type.Optional(Type.KeyOf(
|
||||
Type.Object({
|
||||
nowrap: Type.String(),
|
||||
wrap: Type.String(),
|
||||
'wrap-reverse': Type.String(),
|
||||
})
|
||||
));
|
||||
export const FlexWrapSchema = Type.Optional(
|
||||
Type.KeyOf(
|
||||
Type.Object({
|
||||
nowrap: Type.String(),
|
||||
wrap: Type.String(),
|
||||
'wrap-reverse': Type.String(),
|
||||
})
|
||||
)
|
||||
);
|
||||
export const AlignItemsSchema = Type.Optional(Type.String());
|
||||
export const JustifyContentSchema = Type.Optional(Type.String());
|
||||
export const SpacingSchema = Type.Optional(Type.Union([Type.String(), Type.Number()]));
|
||||
|
||||
const Stack: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
direction,
|
||||
wrap,
|
||||
align,
|
||||
justify,
|
||||
spacing,
|
||||
slotsMap,
|
||||
}) => {
|
||||
return (
|
||||
<BaseStack {...{ direction, wrap, align, justify, spacing }}>
|
||||
<Slot slotsMap={slotsMap} slot="content" />
|
||||
</BaseStack>
|
||||
);
|
||||
};
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
direction: DirectionSchema,
|
||||
wrap: FlexWrapSchema,
|
||||
@ -59,29 +45,32 @@ const PropsSchema = Type.Object({
|
||||
spacing: SpacingSchema,
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'stack',
|
||||
displayName: 'Stack',
|
||||
description: 'chakra-ui stack',
|
||||
isResizable: true,
|
||||
isDraggable: true,
|
||||
exampleProperties: {
|
||||
direction: 'column',
|
||||
spacing: 10,
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'stack',
|
||||
displayName: 'Stack',
|
||||
description: 'chakra-ui stack',
|
||||
isResizable: true,
|
||||
isDraggable: true,
|
||||
exampleProperties: {
|
||||
direction: 'column',
|
||||
spacing: 10,
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: {},
|
||||
methods: {},
|
||||
slots: ['content'],
|
||||
styleSlots: [],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: Stack,
|
||||
};
|
||||
exampleSize: [6, 6],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: Type.Object({}),
|
||||
methods: {},
|
||||
slots: ['content'],
|
||||
styleSlots: [],
|
||||
events: [],
|
||||
},
|
||||
})(({ direction, wrap, align, justify, spacing, slotsMap }) => {
|
||||
return (
|
||||
<BaseStack {...{ direction, wrap, align, justify, spacing }}>
|
||||
<Slot slotsMap={slotsMap} slot="content" />
|
||||
</BaseStack>
|
||||
);
|
||||
});
|
||||
|
@ -11,227 +11,214 @@ import {
|
||||
Box,
|
||||
Spinner,
|
||||
} from '@chakra-ui/react';
|
||||
import { Static } from '@sinclair/typebox';
|
||||
import { TablePagination } from './Pagination';
|
||||
import { ComponentImplementation } from '@sunmao-ui/runtime';
|
||||
import {
|
||||
ColumnsPropertySchema,
|
||||
DataPropertySchema,
|
||||
IsMultiSelectPropertySchema,
|
||||
MajorKeyPropertySchema,
|
||||
RowsPerPagePropertySchema,
|
||||
TableSizePropertySchema,
|
||||
} from './TableTypes';
|
||||
|
||||
import { TableTd } from './TableTd';
|
||||
import { implementTable } from './spec';
|
||||
|
||||
type SortRule = {
|
||||
key: string;
|
||||
desc: boolean;
|
||||
};
|
||||
|
||||
export const TableImpl: ComponentImplementation<{
|
||||
data?: Static<typeof DataPropertySchema>;
|
||||
majorKey: Static<typeof MajorKeyPropertySchema>;
|
||||
rowsPerPage: Static<typeof RowsPerPagePropertySchema>;
|
||||
size: Static<typeof TableSizePropertySchema>;
|
||||
columns: Static<typeof ColumnsPropertySchema>;
|
||||
isMultiSelect: Static<typeof IsMultiSelectPropertySchema>;
|
||||
}> = ({
|
||||
data,
|
||||
majorKey,
|
||||
rowsPerPage,
|
||||
size,
|
||||
columns,
|
||||
isMultiSelect,
|
||||
mergeState,
|
||||
services,
|
||||
app,
|
||||
}) => {
|
||||
const [selectedItem, setSelectedItem] = useState<Record<string, any> | undefined>();
|
||||
const [selectedItems, setSelectedItems] = useState<Array<Record<string, any>>>([]);
|
||||
const [currentPage, setCurrentPage] = useState<number>(0);
|
||||
const [sortRule, setSortRule] = useState<SortRule | undefined>();
|
||||
const pageNumber = Math.ceil((data?.length || 0) / rowsPerPage);
|
||||
export const TableImpl = implementTable(
|
||||
({
|
||||
data,
|
||||
majorKey,
|
||||
rowsPerPage,
|
||||
size,
|
||||
columns,
|
||||
isMultiSelect,
|
||||
mergeState,
|
||||
services,
|
||||
app,
|
||||
}) => {
|
||||
const [selectedItem, setSelectedItem] = useState<Record<string, any> | undefined>();
|
||||
const [selectedItems, setSelectedItems] = useState<Array<Record<string, any>>>([]);
|
||||
const [currentPage, setCurrentPage] = useState<number>(0);
|
||||
const [sortRule, setSortRule] = useState<SortRule | undefined>();
|
||||
const pageNumber = Math.ceil((data?.length || 0) / rowsPerPage);
|
||||
|
||||
useEffect(() => {
|
||||
// reset table state when data source changes
|
||||
updateSelectedItems([]);
|
||||
updateSelectedItem(undefined);
|
||||
setCurrentPage(0);
|
||||
setSortRule(undefined);
|
||||
}, [data]);
|
||||
useEffect(() => {
|
||||
// reset table state when data source changes
|
||||
updateSelectedItems([]);
|
||||
updateSelectedItem(undefined);
|
||||
setCurrentPage(0);
|
||||
setSortRule(undefined);
|
||||
}, [data]);
|
||||
|
||||
const updateSelectedItems = (items: Array<Record<string, any>>) => {
|
||||
setSelectedItems(items);
|
||||
mergeState({ selectedItems: items });
|
||||
};
|
||||
|
||||
const updateSelectedItem = (item?: Record<string, any>) => {
|
||||
setSelectedItem(item);
|
||||
mergeState({ selectedItem: item });
|
||||
};
|
||||
|
||||
const sortedData = useMemo(() => {
|
||||
if (!sortRule) return data;
|
||||
const sorted = sortBy(data, sortRule.key);
|
||||
return sortRule.desc ? sorted.reverse() : sorted;
|
||||
}, [sortRule, data]);
|
||||
|
||||
const currentPageData = sortedData?.slice(
|
||||
currentPage * rowsPerPage,
|
||||
currentPage * rowsPerPage + rowsPerPage
|
||||
);
|
||||
|
||||
function isItemSelected(target: any) {
|
||||
if (isMultiSelect) {
|
||||
return selectedItems.findIndex(item => item[majorKey] === target[majorKey]) > -1;
|
||||
}
|
||||
return selectedItem && selectedItem[majorKey] === target[majorKey];
|
||||
}
|
||||
|
||||
function selectItem(item: any) {
|
||||
if (isMultiSelect) {
|
||||
let newSelectedItems;
|
||||
if (isItemSelected(item)) {
|
||||
newSelectedItems = selectedItems.filter(
|
||||
selectedItem => selectedItem[majorKey] != item[majorKey]
|
||||
);
|
||||
} else {
|
||||
newSelectedItems = selectedItems.concat(item);
|
||||
}
|
||||
updateSelectedItems(newSelectedItems);
|
||||
}
|
||||
updateSelectedItem(item);
|
||||
}
|
||||
|
||||
const allCheckbox = useMemo(() => {
|
||||
if (!data) return null;
|
||||
const isAllChecked = isMultiSelect && selectedItems.length === data.length;
|
||||
const isIndeterminate =
|
||||
selectedItems.length > 0 && selectedItems.length < data.length;
|
||||
const onChange = (e: any) => {
|
||||
if (e.target.checked) {
|
||||
updateSelectedItems(data);
|
||||
} else {
|
||||
updateSelectedItems([]);
|
||||
}
|
||||
const updateSelectedItems = (items: Array<Record<string, any>>) => {
|
||||
setSelectedItems(items);
|
||||
mergeState({ selectedItems: items });
|
||||
};
|
||||
return (
|
||||
<Th paddingX="4" paddingY="2" width="10" key="allCheckbox">
|
||||
<Checkbox
|
||||
size="lg"
|
||||
isIndeterminate={isIndeterminate}
|
||||
checked={isAllChecked}
|
||||
onChange={onChange}
|
||||
></Checkbox>
|
||||
</Th>
|
||||
|
||||
const updateSelectedItem = (item?: Record<string, any>) => {
|
||||
setSelectedItem(item);
|
||||
mergeState({ selectedItem: item });
|
||||
};
|
||||
|
||||
const sortedData = useMemo(() => {
|
||||
if (!sortRule) return data;
|
||||
const sorted = sortBy(data, sortRule.key);
|
||||
return sortRule.desc ? sorted.reverse() : sorted;
|
||||
}, [sortRule, data]);
|
||||
|
||||
const currentPageData = sortedData?.slice(
|
||||
currentPage * rowsPerPage,
|
||||
currentPage * rowsPerPage + rowsPerPage
|
||||
);
|
||||
}, [selectedItems.length, data]);
|
||||
|
||||
const tableContent = (
|
||||
<>
|
||||
<BaseTable size={size || 'md'}>
|
||||
<Thead>
|
||||
<Tr height="10">
|
||||
{isMultiSelect ? allCheckbox : undefined}
|
||||
{columns.map(({ title, key }) => {
|
||||
let sortArrow;
|
||||
if (sortRule && sortRule.key === key) {
|
||||
sortArrow = sortRule.desc ? '⬇️' : '⬆️';
|
||||
}
|
||||
function isItemSelected(target: any) {
|
||||
if (isMultiSelect) {
|
||||
return selectedItems.findIndex(item => item[majorKey] === target[majorKey]) > -1;
|
||||
}
|
||||
return selectedItem && selectedItem[majorKey] === target[majorKey];
|
||||
}
|
||||
|
||||
const onClick = () => {
|
||||
function selectItem(item: any) {
|
||||
if (isMultiSelect) {
|
||||
let newSelectedItems;
|
||||
if (isItemSelected(item)) {
|
||||
newSelectedItems = selectedItems.filter(
|
||||
selectedItem => selectedItem[majorKey] != item[majorKey]
|
||||
);
|
||||
} else {
|
||||
newSelectedItems = selectedItems.concat(item);
|
||||
}
|
||||
updateSelectedItems(newSelectedItems);
|
||||
}
|
||||
updateSelectedItem(item);
|
||||
}
|
||||
|
||||
const allCheckbox = useMemo(() => {
|
||||
if (!data) return null;
|
||||
const isAllChecked = isMultiSelect && selectedItems.length === data.length;
|
||||
const isIndeterminate =
|
||||
selectedItems.length > 0 && selectedItems.length < data.length;
|
||||
const onChange = (e: any) => {
|
||||
if (e.target.checked) {
|
||||
updateSelectedItems(data);
|
||||
} else {
|
||||
updateSelectedItems([]);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Th paddingX="4" paddingY="2" width="10" key="allCheckbox">
|
||||
<Checkbox
|
||||
size="lg"
|
||||
isIndeterminate={isIndeterminate}
|
||||
checked={isAllChecked}
|
||||
onChange={onChange}
|
||||
></Checkbox>
|
||||
</Th>
|
||||
);
|
||||
}, [selectedItems.length, data]);
|
||||
|
||||
const tableContent = (
|
||||
<>
|
||||
<BaseTable size={size || 'md'}>
|
||||
<Thead>
|
||||
<Tr height="10">
|
||||
{isMultiSelect ? allCheckbox : undefined}
|
||||
{columns.map(({ title, key }) => {
|
||||
let sortArrow;
|
||||
if (sortRule && sortRule.key === key) {
|
||||
setSortRule({ key, desc: !sortRule.desc });
|
||||
} else {
|
||||
setSortRule({ key, desc: true });
|
||||
sortArrow = sortRule.desc ? '⬇️' : '⬆️';
|
||||
}
|
||||
|
||||
const onClick = () => {
|
||||
if (sortRule && sortRule.key === key) {
|
||||
setSortRule({ key, desc: !sortRule.desc });
|
||||
} else {
|
||||
setSortRule({ key, desc: true });
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Th paddingX="4" paddingY="2" key={key} onClick={onClick}>
|
||||
{title}
|
||||
{sortArrow}
|
||||
</Th>
|
||||
);
|
||||
})}
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{currentPageData?.map((item, i) => {
|
||||
const isSelected = isItemSelected(item);
|
||||
let onClickToggle = true;
|
||||
const onClickCheckbox = (e: React.MouseEvent<HTMLElement>) => {
|
||||
// chakra-ui checkbox has a bug which will trigger onClick twice
|
||||
// so here I stopPropagation one of every two events
|
||||
// https://github.com/chakra-ui/chakra-ui/issues/2854
|
||||
if (onClickToggle) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
onClickToggle = !onClickToggle;
|
||||
};
|
||||
const checkbox = (
|
||||
<Td
|
||||
paddingX="4"
|
||||
paddingY="2"
|
||||
width="10"
|
||||
key="$checkbox"
|
||||
onClick={onClickCheckbox}
|
||||
>
|
||||
<Checkbox size="lg" isChecked={isSelected}></Checkbox>
|
||||
</Td>
|
||||
);
|
||||
|
||||
return (
|
||||
<Th paddingX="4" paddingY="2" key={key} onClick={onClick}>
|
||||
{title}
|
||||
{sortArrow}
|
||||
</Th>
|
||||
<Tr
|
||||
key={item[majorKey]}
|
||||
height="10"
|
||||
bgColor={isSelected ? 'yellow.100' : undefined}
|
||||
onClick={() => {
|
||||
selectItem(item);
|
||||
}}
|
||||
>
|
||||
{isMultiSelect ? checkbox : undefined}
|
||||
{columns.map(column => (
|
||||
<TableTd
|
||||
index={i}
|
||||
key={column.key}
|
||||
item={item}
|
||||
column={column}
|
||||
onClickItem={() => selectItem(item)}
|
||||
services={services}
|
||||
app={app}
|
||||
/>
|
||||
))}
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{currentPageData?.map((item, i) => {
|
||||
const isSelected = isItemSelected(item);
|
||||
let onClickToggle = true;
|
||||
const onClickCheckbox = (e: React.MouseEvent<HTMLElement>) => {
|
||||
// chakra-ui checkbox has a bug which will trigger onClick twice
|
||||
// so here I stopPropagation one of every two events
|
||||
// https://github.com/chakra-ui/chakra-ui/issues/2854
|
||||
if (onClickToggle) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
onClickToggle = !onClickToggle;
|
||||
};
|
||||
const checkbox = (
|
||||
<Td
|
||||
paddingX="4"
|
||||
paddingY="2"
|
||||
width="10"
|
||||
key="$checkbox"
|
||||
onClick={onClickCheckbox}
|
||||
>
|
||||
<Checkbox size="lg" isChecked={isSelected}></Checkbox>
|
||||
</Td>
|
||||
);
|
||||
</Tbody>
|
||||
</BaseTable>
|
||||
<TablePagination
|
||||
pageNumber={pageNumber}
|
||||
currentPage={currentPage}
|
||||
onChange={v => setCurrentPage(v)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<Tr
|
||||
key={item[majorKey]}
|
||||
height="10"
|
||||
bgColor={isSelected ? 'yellow.100' : undefined}
|
||||
onClick={() => {
|
||||
selectItem(item);
|
||||
}}
|
||||
>
|
||||
{isMultiSelect ? checkbox : undefined}
|
||||
{columns.map(column => (
|
||||
<TableTd
|
||||
index={i}
|
||||
key={column.key}
|
||||
item={item}
|
||||
column={column}
|
||||
onClickItem={() => selectItem(item)}
|
||||
services={services}
|
||||
app={app}
|
||||
/>
|
||||
))}
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
</Tbody>
|
||||
</BaseTable>
|
||||
<TablePagination
|
||||
pageNumber={pageNumber}
|
||||
currentPage={currentPage}
|
||||
onChange={v => setCurrentPage(v)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
const loadingSpinner = (
|
||||
<Box display="flex" height="full">
|
||||
<Spinner size="xl" margin="auto" />
|
||||
</Box>
|
||||
);
|
||||
|
||||
const loadingSpinner = (
|
||||
<Box display="flex" height="full">
|
||||
<Spinner size="xl" margin="auto" />
|
||||
</Box>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box
|
||||
width="full"
|
||||
height="full"
|
||||
background="white"
|
||||
border="1px solid"
|
||||
borderColor="gray.200"
|
||||
borderRadius="base"
|
||||
overflow="auto"
|
||||
>
|
||||
{!data ? loadingSpinner : tableContent}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<Box
|
||||
width="full"
|
||||
height="full"
|
||||
background="white"
|
||||
border="1px solid"
|
||||
borderColor="gray.200"
|
||||
borderRadius="base"
|
||||
overflow="auto"
|
||||
>
|
||||
{!data ? loadingSpinner : tableContent}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -1,64 +1,3 @@
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { TableImpl } from './Table';
|
||||
import {
|
||||
ColumnsPropertySchema,
|
||||
DataPropertySchema,
|
||||
MajorKeyPropertySchema,
|
||||
RowsPerPagePropertySchema,
|
||||
TableStateSchema,
|
||||
TableSizePropertySchema,
|
||||
IsMultiSelectPropertySchema,
|
||||
} from './TableTypes';
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
data: DataPropertySchema,
|
||||
majorKey: MajorKeyPropertySchema,
|
||||
rowsPerPage: RowsPerPagePropertySchema,
|
||||
size: TableSizePropertySchema,
|
||||
columns: ColumnsPropertySchema,
|
||||
isMultiSelect: IsMultiSelectPropertySchema,
|
||||
});
|
||||
|
||||
const exampleProperties = {
|
||||
data: [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Bowen Tan',
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
key: 'name',
|
||||
title: 'Name',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
majorKey: 'id',
|
||||
rowsPerPage: 5,
|
||||
isMultiSelect: false,
|
||||
};
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'table',
|
||||
displayName: 'Table',
|
||||
description: 'chakra-ui table',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties,
|
||||
exampleSize: [8, 6],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: TableStateSchema,
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: [],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: TableImpl,
|
||||
};
|
||||
export default TableImpl
|
||||
|
61
packages/chakra-ui-lib/src/components/Table/spec.ts
Normal file
61
packages/chakra-ui-lib/src/components/Table/spec.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { implementRuntimeComponent2 } from '@sunmao-ui/runtime';
|
||||
import {
|
||||
ColumnsPropertySchema,
|
||||
DataPropertySchema,
|
||||
MajorKeyPropertySchema,
|
||||
RowsPerPagePropertySchema,
|
||||
TableStateSchema,
|
||||
TableSizePropertySchema,
|
||||
IsMultiSelectPropertySchema,
|
||||
} from './TableTypes';
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
data: DataPropertySchema,
|
||||
majorKey: MajorKeyPropertySchema,
|
||||
rowsPerPage: RowsPerPagePropertySchema,
|
||||
size: TableSizePropertySchema,
|
||||
columns: ColumnsPropertySchema,
|
||||
isMultiSelect: IsMultiSelectPropertySchema,
|
||||
});
|
||||
|
||||
const exampleProperties = {
|
||||
data: [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Bowen Tan',
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
key: 'name',
|
||||
title: 'Name',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
majorKey: 'id',
|
||||
rowsPerPage: 5,
|
||||
isMultiSelect: false,
|
||||
};
|
||||
|
||||
export const implementTable = implementRuntimeComponent2({
|
||||
kind: 'Component',
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'table',
|
||||
displayName: 'Table',
|
||||
description: 'chakra-ui table',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties,
|
||||
exampleSize: [8, 6],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: TableStateSchema,
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: [],
|
||||
events: [],
|
||||
},
|
||||
})
|
@ -1,6 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import {
|
||||
Tabs as BaseTabs,
|
||||
TabList,
|
||||
@ -9,16 +8,42 @@ import {
|
||||
TabPanel,
|
||||
Text,
|
||||
} from '@chakra-ui/react';
|
||||
import { Type, Static } from '@sinclair/typebox';
|
||||
import { ComponentImplementation, getSlots } from '@sunmao-ui/runtime';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { implementRuntimeComponent2, getSlots } from '@sunmao-ui/runtime';
|
||||
|
||||
const Tabs: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
tabNames,
|
||||
mergeState,
|
||||
initialSelectedTabIndex,
|
||||
slotsMap,
|
||||
customStyle,
|
||||
}) => {
|
||||
const StateSchema = Type.Object({
|
||||
selectedTabIndex: Type.Number(),
|
||||
});
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
tabNames: Type.Array(Type.String()),
|
||||
initialSelectedTabIndex: Type.Optional(Type.Number()),
|
||||
});
|
||||
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'tabs',
|
||||
displayName: 'Tabs',
|
||||
description: 'chakra-ui tabs',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
tabNames: [],
|
||||
initialSelectedTabIndex: 0,
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
// tab slot is dynamic
|
||||
slots: ['content'],
|
||||
styleSlots: ['tabItem', 'tabContent'],
|
||||
events: [],
|
||||
},
|
||||
})(({ tabNames, mergeState, initialSelectedTabIndex, slotsMap, customStyle }) => {
|
||||
const [selectedTabIndex, setSelectedTabIndex] = useState(initialSelectedTabIndex ?? 0);
|
||||
|
||||
useEffect(() => {
|
||||
@ -62,41 +87,4 @@ const Tabs: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
</TabPanels>
|
||||
</BaseTabs>
|
||||
);
|
||||
};
|
||||
|
||||
const StateSchema = Type.Object({
|
||||
selectedTabIndex: Type.Number(),
|
||||
});
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
tabNames: Type.Array(Type.String()),
|
||||
initialSelectedTabIndex: Type.Optional(Type.Number()),
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'tabs',
|
||||
displayName: 'Tabs',
|
||||
description: 'chakra-ui tabs',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
tabNames: [],
|
||||
initialSelectedTabIndex: 0,
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
// tab slot is dynamic
|
||||
slots: ['content'],
|
||||
styleSlots: ['tabItem', 'tabContent'],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: Tabs,
|
||||
};
|
||||
|
@ -1,38 +1,8 @@
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { Tooltip } from '@chakra-ui/react';
|
||||
import { ComponentImplementation, Slot, TextPropertySchema } from '@sunmao-ui/runtime';
|
||||
import { implementRuntimeComponent2, Slot, TextPropertySchema } from '@sunmao-ui/runtime';
|
||||
import { ColorSchemePropertySchema } from './Types/ColorScheme';
|
||||
|
||||
const TooltipImpl: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
text,
|
||||
shouldWrapChildren,
|
||||
placement = 'auto',
|
||||
isOpen,
|
||||
hasArrow,
|
||||
isDisabled,
|
||||
defaultIsOpen,
|
||||
slotsMap,
|
||||
}) => {
|
||||
return (
|
||||
/*
|
||||
Chakra tooltip requires children to be created by forwardRef.
|
||||
If not, should add shouldWrapChildren.
|
||||
*/
|
||||
<Tooltip
|
||||
label={text}
|
||||
placement={placement}
|
||||
isOpen={isOpen}
|
||||
hasArrow={hasArrow}
|
||||
isDisabled={isDisabled}
|
||||
defaultIsOpen={defaultIsOpen}
|
||||
shouldWrapChildren={shouldWrapChildren}
|
||||
>
|
||||
<Slot slotsMap={slotsMap} slot="content" />
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
text: TextPropertySchema,
|
||||
colorScheme: ColorSchemePropertySchema,
|
||||
@ -64,28 +34,54 @@ const PropsSchema = Type.Object({
|
||||
),
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'tooltip',
|
||||
description: 'chakra-ui tooltip',
|
||||
displayName: 'Tooltip',
|
||||
isDraggable: false,
|
||||
isResizable: false,
|
||||
exampleProperties: {
|
||||
text: 'tooltip',
|
||||
},
|
||||
exampleSize: [2, 1],
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'tooltip',
|
||||
description: 'chakra-ui tooltip',
|
||||
displayName: 'Tooltip',
|
||||
isDraggable: false,
|
||||
isResizable: false,
|
||||
exampleProperties: {
|
||||
text: 'tooltip',
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: {},
|
||||
methods: {},
|
||||
slots: ['content'],
|
||||
styleSlots: [],
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: TooltipImpl,
|
||||
};
|
||||
exampleSize: [2, 1],
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: Type.Object({}),
|
||||
methods: {},
|
||||
slots: ['content'],
|
||||
styleSlots: [],
|
||||
events: [],
|
||||
},
|
||||
})(
|
||||
({
|
||||
text,
|
||||
shouldWrapChildren,
|
||||
placement = 'auto',
|
||||
isOpen,
|
||||
hasArrow,
|
||||
isDisabled,
|
||||
defaultIsOpen,
|
||||
slotsMap,
|
||||
}) => {
|
||||
return (
|
||||
/*
|
||||
Chakra tooltip requires children to be created by forwardRef.
|
||||
If not, should add shouldWrapChildren.
|
||||
*/
|
||||
<Tooltip
|
||||
label={text}
|
||||
placement={placement}
|
||||
isOpen={isOpen}
|
||||
hasArrow={hasArrow}
|
||||
isDisabled={isDisabled}
|
||||
defaultIsOpen={defaultIsOpen}
|
||||
shouldWrapChildren={shouldWrapChildren}
|
||||
>
|
||||
<Slot slotsMap={slotsMap} slot="content" />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { createComponent } from '@sunmao-ui/core';
|
||||
import { css } from '@emotion/css';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { VStack as BaseVStack } from '@chakra-ui/react';
|
||||
import { ComponentImplementation, Slot } from '@sunmao-ui/runtime';
|
||||
import { implementRuntimeComponent2, Slot } from '@sunmao-ui/runtime';
|
||||
import {
|
||||
DirectionSchema,
|
||||
FlexWrapSchema,
|
||||
@ -11,34 +10,6 @@ import {
|
||||
SpacingSchema,
|
||||
} from './Stack';
|
||||
|
||||
const VStack: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
direction,
|
||||
wrap,
|
||||
align,
|
||||
justify,
|
||||
spacing,
|
||||
slotsMap,
|
||||
customStyle,
|
||||
}) => {
|
||||
return (
|
||||
<BaseVStack
|
||||
width="full"
|
||||
height="full"
|
||||
padding="4"
|
||||
background="white"
|
||||
border="1px solid"
|
||||
borderColor="gray.200"
|
||||
borderRadius="4"
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
{...{ direction, wrap, align, justify, spacing }}
|
||||
>
|
||||
<Slot slotsMap={slotsMap} slot="content" />
|
||||
</BaseVStack>
|
||||
);
|
||||
};
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
direction: DirectionSchema,
|
||||
wrap: FlexWrapSchema,
|
||||
@ -47,20 +18,51 @@ const PropsSchema = Type.Object({
|
||||
spacing: SpacingSchema,
|
||||
});
|
||||
|
||||
export default {
|
||||
...createComponent({
|
||||
export default implementRuntimeComponent2({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'vstack',
|
||||
displayName: 'VStack',
|
||||
description: 'chakra-ui vstack',
|
||||
exampleProperties: {
|
||||
spacing: '24px',
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: Type.Object({}),
|
||||
slots: ['content'],
|
||||
styleSlots: ['content'],
|
||||
methods: {},
|
||||
events: [],
|
||||
},
|
||||
}),
|
||||
impl: VStack,
|
||||
};
|
||||
})(({
|
||||
direction,
|
||||
wrap,
|
||||
align,
|
||||
justify,
|
||||
spacing,
|
||||
slotsMap,
|
||||
customStyle,
|
||||
}) => {
|
||||
return (
|
||||
<BaseVStack
|
||||
width="full"
|
||||
height="full"
|
||||
padding="4"
|
||||
background="white"
|
||||
border="1px solid"
|
||||
borderColor="gray.200"
|
||||
borderRadius="4"
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
{...{ direction, wrap, align, justify, spacing }}
|
||||
>
|
||||
<Slot slotsMap={slotsMap} slot="content" />
|
||||
</BaseVStack>
|
||||
);
|
||||
})
|
@ -23,6 +23,7 @@ export function initSunmaoUI(dependencies = {}) {
|
||||
|
||||
export * from './utils/parseType';
|
||||
export * from './utils/parseTypeBox';
|
||||
export * from './utils/buildKit';
|
||||
export * from './utils/encodeDragDataTransfer';
|
||||
export * from './types/RuntimeSchema';
|
||||
export * from './types/TraitPropertiesSchema';
|
||||
|
@ -55,7 +55,7 @@ export type ImplementedRuntimeModule = RuntimeModuleSpec & {
|
||||
};
|
||||
|
||||
export type SunmaoLib = {
|
||||
components?: ImplementedRuntimeComponent[];
|
||||
components?: ImplementedRuntimeComponent2<string, string, string, string>[];
|
||||
traits?: ImplementedRuntimeTrait[];
|
||||
modules?: ImplementedRuntimeModule[];
|
||||
};
|
||||
|
@ -66,7 +66,7 @@ export type CallbackMap<K extends string> = Record<K, () => void>;
|
||||
export type SubscribeMethods<U> = (map: {
|
||||
[K in keyof U]: (parameters: U[K]) => void;
|
||||
}) => void;
|
||||
export type MergeState<T> = (partialState: T) => void;
|
||||
export type MergeState<T> = (partialState: Partial<T>) => void;
|
||||
|
||||
type RuntimeFunctions<TState, TMethods> = {
|
||||
mergeState: MergeState<TState>;
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
CreateComponentOptions2,
|
||||
RuntimeComponentSpec2,
|
||||
} from '@sunmao-ui/core';
|
||||
import { ComponentImplementation } from 'src/services/registry';
|
||||
import { ComponentImplementation } from '../services/registry';
|
||||
|
||||
export type ImplementedRuntimeComponent2<
|
||||
KMethodName extends string,
|
||||
|
Loading…
Reference in New Issue
Block a user