mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2024-11-21 03:15:49 +08:00
Merge pull request #174 from webzard-io/refactor/style-trait
refactor style trait
This commit is contained in:
commit
968be3744f
@ -82,9 +82,13 @@ export const ApplicationFixture: Record<string, Application> = {
|
||||
{
|
||||
type: 'core/v1/style',
|
||||
properties: {
|
||||
styleSlot: 'content',
|
||||
style: "{{!usersTable.selectedItem ? 'display: none' : ''}}",
|
||||
},
|
||||
styles: [
|
||||
{
|
||||
styleSlot: 'content',
|
||||
style: "{{!usersTable.selectedItem ? 'display: none' : ''}}",
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -110,7 +114,7 @@ export const ApplicationFixture: Record<string, Application> = {
|
||||
},
|
||||
{
|
||||
type: 'core/v1/style',
|
||||
properties: { styleSlot: 'content', style: 'padding: 0; border: none' },
|
||||
properties: { styles: [{styleSlot: 'content', style: 'padding: 0; border: none' }]},
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -174,7 +178,7 @@ export const ApplicationFixture: Record<string, Application> = {
|
||||
},
|
||||
{
|
||||
type: 'core/v1/style',
|
||||
properties: { styleSlot: 'content', style: 'padding: 0; border: none' },
|
||||
properties: { styles: [{styleSlot: 'content', style: 'padding: 0; border: none' }]},
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -241,7 +245,6 @@ export const ApplicationFixture: Record<string, Application> = {
|
||||
type: 'core/v1/slot',
|
||||
properties: { container: { id: 'hstack4', slot: 'content' } },
|
||||
},
|
||||
{ type: 'core/v1/style', properties: { string: { kind: {}, type: {} } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -255,7 +258,7 @@ export const ApplicationFixture: Record<string, Application> = {
|
||||
},
|
||||
{
|
||||
type: 'core/v1/style',
|
||||
properties: { styleSlot: 'content', style: 'padding: 0; border: none' },
|
||||
properties: { styles: [{styleSlot: 'content', style: 'padding: 0; border: none' }]},
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -281,7 +284,7 @@ export const ApplicationFixture: Record<string, Application> = {
|
||||
},
|
||||
{
|
||||
type: 'core/v1/style',
|
||||
properties: { styleSlot: 'content', style: 'padding: 0; border: none' },
|
||||
properties: { styles: [{styleSlot: 'content', style: 'padding: 0; border: none' }]},
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -329,7 +332,7 @@ export const ApplicationFixture: Record<string, Application> = {
|
||||
},
|
||||
{
|
||||
type: 'core/v1/style',
|
||||
properties: { styleSlot: 'content', style: 'padding: 0; border: none' },
|
||||
properties: { styles: [{styleSlot: 'content', style: 'padding: 0; border: none' }]},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -15,6 +15,7 @@ export const CssEditor: React.FC<{
|
||||
onBlur?: (v: string) => void;
|
||||
}> = ({ defaultCode, onChange, onBlur }) => {
|
||||
const style = css`
|
||||
width: 100%;
|
||||
.CodeMirror {
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
@ -44,6 +45,8 @@ export const CssEditor: React.FC<{
|
||||
},
|
||||
theme: 'ayu-mirage',
|
||||
});
|
||||
} else {
|
||||
cm.current.setValue(defaultCode);
|
||||
}
|
||||
const changeHandler = (instance: CodeMirror.Editor) => {
|
||||
onChange?.(instance.getValue());
|
||||
@ -57,7 +60,7 @@ export const CssEditor: React.FC<{
|
||||
cm.current?.off('change', changeHandler);
|
||||
cm.current?.off('blur', blurHandler);
|
||||
};
|
||||
}, [defaultCode]);
|
||||
}, [onBlur, onChange, defaultCode]);
|
||||
|
||||
return <Box className={style} ref={wrapperEl}></Box>;
|
||||
};
|
||||
|
@ -1,61 +1,178 @@
|
||||
import { useMemo } from 'react';
|
||||
import { FormControl, FormLabel, VStack, Box } from '@chakra-ui/react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import {
|
||||
FormControl,
|
||||
FormLabel,
|
||||
VStack,
|
||||
HStack,
|
||||
IconButton,
|
||||
Text,
|
||||
Select,
|
||||
} from '@chakra-ui/react';
|
||||
import { ApplicationComponent } from '@sunmao-ui/core';
|
||||
import { Registry } from '@sunmao-ui/runtime';
|
||||
import { CssEditor } from '../../../components/CodeEditor';
|
||||
import { eventBus } from '../../../eventBus';
|
||||
import { genOperation } from '../../../operations';
|
||||
import { AddIcon, CloseIcon } from '@chakra-ui/icons';
|
||||
import produce from 'immer';
|
||||
import { formWrapperCSS } from '../style';
|
||||
|
||||
type Props = {
|
||||
registry: Registry;
|
||||
component: ApplicationComponent;
|
||||
};
|
||||
|
||||
type Styles = Array<{
|
||||
styleSlot: string;
|
||||
style: string;
|
||||
}>;
|
||||
|
||||
export const StyleTraitForm: React.FC<Props> = props => {
|
||||
const { component, registry } = props;
|
||||
|
||||
const styleSlots = useMemo(() => {
|
||||
return registry.getComponentByType(component.type).spec.styleSlots;
|
||||
}, [component, registry]);
|
||||
const styles = useMemo(() => {
|
||||
return component.traits.filter(t => t.type === 'core/v1/style');
|
||||
}, [component]);
|
||||
|
||||
if (!styleSlots.length) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<VStack width="full">
|
||||
<Box fontWeight="bold" textAlign="left" width="100%">
|
||||
Styles
|
||||
</Box>
|
||||
{styleSlots.map(styleSlot => {
|
||||
const styleTrait = styles.find(s => s.properties.styleSlot === styleSlot);
|
||||
if (!styleTrait) {
|
||||
|
||||
const styleTraitIndex = useMemo(() => {
|
||||
return component.traits.findIndex(t => t.type === 'core/v1/style');
|
||||
}, [component]);
|
||||
|
||||
const styleTrait = component.traits[styleTraitIndex];
|
||||
const styles = (styleTrait?.properties.styles as Styles) || [];
|
||||
|
||||
const createStyleTrait = () => {
|
||||
eventBus.send(
|
||||
'operation',
|
||||
genOperation('createTrait', {
|
||||
componentId: component.id,
|
||||
traitType: 'core/v1/style',
|
||||
properties: {
|
||||
styles: [
|
||||
{
|
||||
styleSlot: styleSlots[0],
|
||||
style: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const updateStyles = useCallback(
|
||||
(newStyles: Styles) => {
|
||||
eventBus.send(
|
||||
'operation',
|
||||
genOperation('modifyTraitProperty', {
|
||||
componentId: component.id,
|
||||
traitIndex: styleTraitIndex,
|
||||
properties: {
|
||||
styles: newStyles,
|
||||
},
|
||||
})
|
||||
);
|
||||
},
|
||||
[component, styleTraitIndex]
|
||||
);
|
||||
|
||||
const addStyle = useCallback(() => {
|
||||
const newStyles: Styles = styles.concat({
|
||||
styleSlot: styleSlots[0],
|
||||
style: '',
|
||||
});
|
||||
updateStyles(newStyles);
|
||||
}, [updateStyles, styleSlots, styles]);
|
||||
|
||||
const onClickCreate = () => {
|
||||
if (!styleTrait) {
|
||||
createStyleTrait();
|
||||
} else {
|
||||
addStyle();
|
||||
}
|
||||
};
|
||||
|
||||
const changeStyleContent = useCallback(
|
||||
(i: number, value: string) => {
|
||||
const newStyles = produce(styles, draft => {
|
||||
draft[i].style = value;
|
||||
});
|
||||
updateStyles(newStyles);
|
||||
},
|
||||
[updateStyles, styles]
|
||||
);
|
||||
|
||||
const changeStyleSlot = useCallback(
|
||||
(i: number, newSlot: string) => {
|
||||
const newStyles = produce(styles, draft => {
|
||||
draft[i].styleSlot = newSlot;
|
||||
});
|
||||
updateStyles(newStyles);
|
||||
},
|
||||
[updateStyles, styles]
|
||||
);
|
||||
|
||||
const styleForms = useMemo(
|
||||
() =>
|
||||
styles.map(({ style, styleSlot }, i) => {
|
||||
if (styles.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const removeStyle = () => {
|
||||
const newStyles = styles.filter((_, j) => j !== i);
|
||||
updateStyles(newStyles);
|
||||
};
|
||||
return (
|
||||
<FormControl id={styleSlot} key={styleSlot}>
|
||||
<FormLabel>{styleSlot}</FormLabel>
|
||||
<CssEditor
|
||||
defaultCode={styleTrait.properties.style as string}
|
||||
onBlur={v =>
|
||||
eventBus.send(
|
||||
'operation',
|
||||
genOperation('modifyTraitProperty', {
|
||||
componentId: component.id,
|
||||
traitIndex: component.traits.indexOf(styleTrait),
|
||||
properties: {
|
||||
styleSlot,
|
||||
style: v,
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
<VStack key={`${styleSlot}-${i}`} css={formWrapperCSS} spacing="2">
|
||||
<FormControl id={styleSlot}>
|
||||
<FormLabel marginInlineEnd="0">
|
||||
<HStack width="full" justify="space-between">
|
||||
<Text>Style Slot</Text>
|
||||
<IconButton
|
||||
aria-label="remove style"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
colorScheme="red"
|
||||
icon={<CloseIcon />}
|
||||
onClick={removeStyle}
|
||||
/>
|
||||
</HStack>
|
||||
</FormLabel>
|
||||
<Select
|
||||
value={styleSlot}
|
||||
onChange={e => changeStyleSlot(i, e.target.value)}
|
||||
>
|
||||
{styleSlots.map(s => (
|
||||
<option key={s} value={s}>
|
||||
{s}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<CssEditor defaultCode={style} onBlur={v => changeStyleContent(i, v)} />
|
||||
</VStack>
|
||||
);
|
||||
})}
|
||||
}),
|
||||
[styles, changeStyleContent, changeStyleSlot, updateStyles]
|
||||
);
|
||||
|
||||
return (
|
||||
<VStack width="full">
|
||||
<HStack width="full" justify="space-between">
|
||||
<strong>Styles</strong>
|
||||
<IconButton
|
||||
aria-label="Styles"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
colorScheme="blue"
|
||||
icon={<AddIcon />}
|
||||
onClick={onClickCreate}
|
||||
/>
|
||||
</HStack>
|
||||
{styleForms}
|
||||
</VStack>
|
||||
);
|
||||
};
|
||||
|
@ -2,22 +2,25 @@ import { createTrait } from '@sunmao-ui/core';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { TraitImplementation } from '../../types/RuntimeSchema';
|
||||
|
||||
const StyleTrait: TraitImplementation<Static<typeof PropsSchema>> = ({
|
||||
styleSlot,
|
||||
style,
|
||||
}) => {
|
||||
const StyleTrait: TraitImplementation<Static<typeof PropsSchema>> = ({ styles }) => {
|
||||
const customStyle: Record<string, string> = {};
|
||||
styles.forEach(style => {
|
||||
customStyle[style.styleSlot] = style.style;
|
||||
});
|
||||
return {
|
||||
props: {
|
||||
customStyle: {
|
||||
[styleSlot]: style,
|
||||
},
|
||||
customStyle,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const PropsSchema = Type.Object({
|
||||
styleSlot: Type.String(),
|
||||
style: Type.String(),
|
||||
styles: Type.Array(
|
||||
Type.Object({
|
||||
styleSlot: Type.String(),
|
||||
style: Type.String(),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
export default {
|
||||
|
Loading…
Reference in New Issue
Block a user