mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2024-11-21 03:15:49 +08:00
migrate components and traits to v2 eval
This commit is contained in:
parent
e790ab1604
commit
d976e2d526
@ -4,7 +4,6 @@ import { Static, Type } from "@sinclair/typebox";
|
||||
import { Box as BaseBox } from "@chakra-ui/react";
|
||||
import { ComponentImplementation } from "../../registry";
|
||||
import Slot from "../_internal/Slot";
|
||||
import { useExpression } from "../../store";
|
||||
import { pick } from "lodash";
|
||||
|
||||
const CssGlobals = Type.KeyOf(
|
||||
@ -278,14 +277,6 @@ const Box: ComponentImplementation<Static<typeof StyleSchema>> = ({
|
||||
...restProps
|
||||
}) => {
|
||||
const styleProps = pick(restProps, StyleProps);
|
||||
Object.entries(styleProps).forEach((item) => {
|
||||
const key = item[0] as keyof typeof styleProps;
|
||||
const value = item[1];
|
||||
if (typeof value === "string" && value.startsWith("{{")) {
|
||||
const raw = useExpression(value);
|
||||
styleProps[key] = raw;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<BaseBox {...styleProps}>
|
||||
|
@ -4,7 +4,6 @@ import { Static, Type } from "@sinclair/typebox";
|
||||
import { Button as BaseButton } from "@chakra-ui/react";
|
||||
import Text, { TextProps, TextPropertySchema } from "../_internal/Text";
|
||||
import { ComponentImplementation } from "../../registry";
|
||||
import { useExpression } from "../../store";
|
||||
|
||||
const Button: ComponentImplementation<{
|
||||
text: TextProps["value"];
|
||||
@ -19,10 +18,9 @@ const Button: ComponentImplementation<{
|
||||
colorScheme,
|
||||
isLoading,
|
||||
}) => {
|
||||
const raw = useExpression(text.raw);
|
||||
useEffect(() => {
|
||||
mergeState({ value: raw });
|
||||
}, [raw]);
|
||||
mergeState({ value: text.raw });
|
||||
}, [text.raw]);
|
||||
|
||||
const ref = useRef<HTMLButtonElement>(null);
|
||||
useEffect(() => {
|
||||
@ -35,7 +33,7 @@ const Button: ComponentImplementation<{
|
||||
|
||||
return (
|
||||
<BaseButton {...{ colorScheme, isLoading }} ref={ref} onClick={onClick}>
|
||||
<Text value={{ ...text, raw }} />
|
||||
<Text value={text} />
|
||||
</BaseButton>
|
||||
);
|
||||
};
|
||||
|
@ -1,16 +1,22 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { Input as BaseInput, InputGroup, InputLeftAddon, InputLeftElement, InputRightAddon, InputRightElement } from "@chakra-ui/react";
|
||||
import React, { useEffect } from "react";
|
||||
import {
|
||||
Input as BaseInput,
|
||||
InputGroup,
|
||||
InputLeftAddon,
|
||||
InputLeftElement,
|
||||
InputRightAddon,
|
||||
InputRightElement,
|
||||
} from "@chakra-ui/react";
|
||||
import { createComponent } from "@meta-ui/core";
|
||||
import { Static, Type } from "@sinclair/typebox";
|
||||
import { ComponentImplementation } from "../../registry";
|
||||
|
||||
|
||||
const VariantPropertySchema = Type.KeyOf(
|
||||
Type.Object({
|
||||
outline: Type.String(),
|
||||
unstyled: Type.String(),
|
||||
filled: Type.String(),
|
||||
flushed: Type.String()
|
||||
flushed: Type.String(),
|
||||
})
|
||||
);
|
||||
|
||||
@ -21,7 +27,7 @@ const SizePropertySchema = Type.KeyOf(
|
||||
sm: Type.String(),
|
||||
md: Type.String(),
|
||||
lg: Type.String(),
|
||||
xs: Type.String()
|
||||
xs: Type.String(),
|
||||
})
|
||||
);
|
||||
|
||||
@ -32,15 +38,15 @@ const IsRequiredPropertySchema = Type.Optional(Type.Boolean());
|
||||
const AppendElementPropertySchema = Type.Union([
|
||||
Type.Object({
|
||||
type: Type.KeyOf(Type.Object({ addon: Type.String() })),
|
||||
children: Type.Optional(Type.String()) // TODO: ReactNode
|
||||
children: Type.Optional(Type.String()), // TODO: ReactNode
|
||||
}),
|
||||
Type.Object({
|
||||
type: Type.KeyOf(Type.Object({ element: Type.String() })),
|
||||
children: Type.Optional(Type.String()), // TODO: ReactNode
|
||||
fontSize: Type.Optional(Type.String()),
|
||||
color: Type.Optional(Type.String()),
|
||||
})
|
||||
])
|
||||
}),
|
||||
]);
|
||||
|
||||
const Input: ComponentImplementation<{
|
||||
variant?: Static<typeof VariantPropertySchema>;
|
||||
@ -60,10 +66,11 @@ const Input: ComponentImplementation<{
|
||||
isRequired,
|
||||
left,
|
||||
right,
|
||||
mergeState
|
||||
mergeState,
|
||||
}) => {
|
||||
const [value, setValue] = React.useState(""); // TODO: pin input
|
||||
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => setValue(event.target.value);
|
||||
const onChange = (event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setValue(event.target.value);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ value });
|
||||
@ -71,11 +78,19 @@ const Input: ComponentImplementation<{
|
||||
|
||||
return (
|
||||
<InputGroup size={size}>
|
||||
{left ? left.type === "addon"
|
||||
? <InputLeftAddon children={left.children} />
|
||||
: <InputLeftElement children={left.children} fontSize={left.fontSize} color={left.color} />
|
||||
: <></>
|
||||
}
|
||||
{left ? (
|
||||
left.type === "addon" ? (
|
||||
<InputLeftAddon children={left.children} />
|
||||
) : (
|
||||
<InputLeftElement
|
||||
children={left.children}
|
||||
fontSize={left.fontSize}
|
||||
color={left.color}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<BaseInput
|
||||
variant={variant}
|
||||
placeholder={placeholder}
|
||||
@ -84,13 +99,22 @@ const Input: ComponentImplementation<{
|
||||
isRequired={isRequired}
|
||||
onChange={onChange}
|
||||
/>
|
||||
{right ? right.type === "addon"
|
||||
? <InputRightAddon children={right.children} />
|
||||
: <InputRightElement children={right.children} fontSize={right.fontSize} color={right.color} />
|
||||
: <></>}
|
||||
{right ? (
|
||||
right.type === "addon" ? (
|
||||
<InputRightAddon children={right.children} />
|
||||
) : (
|
||||
<InputRightElement
|
||||
children={right.children}
|
||||
fontSize={right.fontSize}
|
||||
color={right.color}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</InputGroup>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const StateSchema = Type.Object({
|
||||
value: Type.String(),
|
||||
@ -106,42 +130,42 @@ export default {
|
||||
spec: {
|
||||
properties: [
|
||||
{
|
||||
name: 'variant',
|
||||
...VariantPropertySchema
|
||||
name: "variant",
|
||||
...VariantPropertySchema,
|
||||
},
|
||||
{
|
||||
name: 'placeholder',
|
||||
...PlaceholderPropertySchema
|
||||
name: "placeholder",
|
||||
...PlaceholderPropertySchema,
|
||||
},
|
||||
{
|
||||
name: 'size',
|
||||
...SizePropertySchema
|
||||
name: "size",
|
||||
...SizePropertySchema,
|
||||
},
|
||||
{
|
||||
name: 'focusBorderColor',
|
||||
...FocusBorderColorPropertySchema
|
||||
name: "focusBorderColor",
|
||||
...FocusBorderColorPropertySchema,
|
||||
},
|
||||
{
|
||||
name: 'isDisabled',
|
||||
...IsDisabledPropertySchema
|
||||
name: "isDisabled",
|
||||
...IsDisabledPropertySchema,
|
||||
},
|
||||
{
|
||||
name: 'isRequired',
|
||||
...IsRequiredPropertySchema
|
||||
name: "isRequired",
|
||||
...IsRequiredPropertySchema,
|
||||
},
|
||||
{
|
||||
name: 'left',
|
||||
...AppendElementPropertySchema
|
||||
name: "left",
|
||||
...AppendElementPropertySchema,
|
||||
},
|
||||
{
|
||||
name: 'right',
|
||||
...AppendElementPropertySchema
|
||||
}
|
||||
name: "right",
|
||||
...AppendElementPropertySchema,
|
||||
},
|
||||
],
|
||||
acceptTraits: [],
|
||||
state: StateSchema,
|
||||
methods: [],
|
||||
}
|
||||
},
|
||||
}),
|
||||
impl: Input,
|
||||
}
|
||||
};
|
||||
|
@ -4,19 +4,17 @@ import { Type } from "@sinclair/typebox";
|
||||
import { createComponent } from "@meta-ui/core";
|
||||
import { ComponentImplementation } from "../../registry";
|
||||
import Text, { TextProps, TextPropertySchema } from "../_internal/Text";
|
||||
import { useExpression } from "../../store";
|
||||
|
||||
const Kbd: ComponentImplementation<{
|
||||
text: TextProps["value"];
|
||||
}> = ({ text, mergeState }) => {
|
||||
const raw = useExpression(text.raw);
|
||||
useEffect(() => {
|
||||
mergeState({ value: raw });
|
||||
}, [raw]);
|
||||
mergeState({ value: text.raw });
|
||||
}, [text.raw]);
|
||||
|
||||
return (
|
||||
<BaseKbd>
|
||||
<Text value={{ ...text, raw }} />
|
||||
<Text value={text} />
|
||||
</BaseKbd>
|
||||
);
|
||||
};
|
||||
|
@ -23,7 +23,7 @@ const SizePropertySchema = Type.KeyOf(
|
||||
sm: Type.String(),
|
||||
md: Type.String(),
|
||||
lg: Type.String(),
|
||||
xs: Type.String()
|
||||
xs: Type.String(),
|
||||
})
|
||||
);
|
||||
|
||||
@ -31,19 +31,19 @@ const CustomerStepStylePropertySchema = Type.Object({
|
||||
bg: Type.Optional(Type.String()),
|
||||
children: Type.Optional(Type.String()),
|
||||
_active: Type.Object(Type.Object({ bg: Type.String() })),
|
||||
})
|
||||
});
|
||||
|
||||
const NumberInput: ComponentImplementation<{
|
||||
defaultValue?: Static<typeof DefaultValuePropertySchema>
|
||||
min?: Static<typeof MinPropertySchema>
|
||||
max?: Static<typeof MaxPropertySchema>
|
||||
step?: Static<typeof StepPropertySchema>
|
||||
precision?: Static<typeof PrecisionPropertySchema>
|
||||
clampValueOnBlur?: Static<typeof ClampValueOnBlurPropertySchema>
|
||||
allowMouseWheel?: Static<typeof AllowMouseWheelPropertySchema>
|
||||
size?: Static<typeof SizePropertySchema>
|
||||
customerIncrement?: Static<typeof CustomerStepStylePropertySchema>
|
||||
customerDecrement?: Static<typeof CustomerStepStylePropertySchema>
|
||||
defaultValue?: Static<typeof DefaultValuePropertySchema>;
|
||||
min?: Static<typeof MinPropertySchema>;
|
||||
max?: Static<typeof MaxPropertySchema>;
|
||||
step?: Static<typeof StepPropertySchema>;
|
||||
precision?: Static<typeof PrecisionPropertySchema>;
|
||||
clampValueOnBlur?: Static<typeof ClampValueOnBlurPropertySchema>;
|
||||
allowMouseWheel?: Static<typeof AllowMouseWheelPropertySchema>;
|
||||
size?: Static<typeof SizePropertySchema>;
|
||||
customerIncrement?: Static<typeof CustomerStepStylePropertySchema>;
|
||||
customerDecrement?: Static<typeof CustomerStepStylePropertySchema>;
|
||||
}> = ({
|
||||
defaultValue = 0,
|
||||
min,
|
||||
@ -55,10 +55,11 @@ const NumberInput: ComponentImplementation<{
|
||||
size,
|
||||
customerIncrement,
|
||||
customerDecrement,
|
||||
mergeState
|
||||
mergeState,
|
||||
}) => {
|
||||
const [value, setValue] = useState(defaultValue)
|
||||
const onChange = (valueAsString: string, valueAsNumber: number) => setValue(valueAsNumber);
|
||||
const [value, setValue] = useState(defaultValue);
|
||||
const onChange = (valueAsString: string, valueAsNumber: number) =>
|
||||
setValue(valueAsNumber);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ value });
|
||||
@ -83,7 +84,7 @@ const NumberInput: ComponentImplementation<{
|
||||
</NumberInputStepper>
|
||||
</BaseNumberInput>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const StateSchema = Type.Object({
|
||||
value: Type.Number(),
|
||||
@ -99,50 +100,50 @@ export default {
|
||||
spec: {
|
||||
properties: [
|
||||
{
|
||||
name: 'defaultValue',
|
||||
...DefaultValuePropertySchema
|
||||
name: "defaultValue",
|
||||
...DefaultValuePropertySchema,
|
||||
},
|
||||
{
|
||||
name: 'min',
|
||||
...MinPropertySchema
|
||||
name: "min",
|
||||
...MinPropertySchema,
|
||||
},
|
||||
{
|
||||
name: 'max',
|
||||
...MaxPropertySchema
|
||||
name: "max",
|
||||
...MaxPropertySchema,
|
||||
},
|
||||
{
|
||||
name: 'step',
|
||||
...StepPropertySchema
|
||||
name: "step",
|
||||
...StepPropertySchema,
|
||||
},
|
||||
{
|
||||
name: 'precision',
|
||||
...PrecisionPropertySchema
|
||||
name: "precision",
|
||||
...PrecisionPropertySchema,
|
||||
},
|
||||
{
|
||||
name: 'clampValueOnBlur',
|
||||
...ClampValueOnBlurPropertySchema
|
||||
name: "clampValueOnBlur",
|
||||
...ClampValueOnBlurPropertySchema,
|
||||
},
|
||||
{
|
||||
name: 'allowMouseWheel',
|
||||
...AllowMouseWheelPropertySchema
|
||||
name: "allowMouseWheel",
|
||||
...AllowMouseWheelPropertySchema,
|
||||
},
|
||||
{
|
||||
name: 'size',
|
||||
...SizePropertySchema
|
||||
name: "size",
|
||||
...SizePropertySchema,
|
||||
},
|
||||
{
|
||||
name: 'customerIncrement',
|
||||
...CustomerStepStylePropertySchema
|
||||
name: "customerIncrement",
|
||||
...CustomerStepStylePropertySchema,
|
||||
},
|
||||
{
|
||||
name: 'customerDecrement',
|
||||
...CustomerStepStylePropertySchema
|
||||
name: "customerDecrement",
|
||||
...CustomerStepStylePropertySchema,
|
||||
},
|
||||
],
|
||||
acceptTraits: [],
|
||||
state: StateSchema,
|
||||
methods: [],
|
||||
}
|
||||
},
|
||||
}),
|
||||
impl: NumberInput,
|
||||
}
|
||||
};
|
||||
|
@ -3,7 +3,6 @@ import { Table as BaseTable, Thead, Tr, Th, Tbody, Td } from "@chakra-ui/react";
|
||||
import { ComponentImplementation } from "../../registry";
|
||||
import { createComponent } from "@meta-ui/core";
|
||||
import { Static, Type } from "@sinclair/typebox";
|
||||
import { useExpression } from "../../store";
|
||||
|
||||
function normalizeData(data: Static<typeof DataPropertySchema>): {
|
||||
normalizedData: Array<Record<string, string>>;
|
||||
@ -28,8 +27,7 @@ function normalizeData(data: Static<typeof DataPropertySchema>): {
|
||||
const Table: ComponentImplementation<{
|
||||
data: Static<typeof DataPropertySchema>;
|
||||
size: Static<typeof SizePropertySchema>;
|
||||
}> = ({ data: _data, size, mergeState }) => {
|
||||
const data = useExpression(_data) || [];
|
||||
}> = ({ data = [], size, mergeState }) => {
|
||||
const { normalizedData, keys } = normalizeData(data);
|
||||
useEffect(() => {
|
||||
mergeState({ data });
|
||||
@ -59,7 +57,7 @@ const Table: ComponentImplementation<{
|
||||
);
|
||||
};
|
||||
|
||||
const DataPropertySchema = Type.Union([Type.Array(Type.Any()), Type.String()]);
|
||||
const DataPropertySchema = Type.Array(Type.Any());
|
||||
const SizePropertySchema = Type.KeyOf(
|
||||
Type.Object({
|
||||
sm: Type.String(),
|
||||
|
@ -3,16 +3,13 @@ import { createComponent } from "@meta-ui/core";
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import { ComponentImplementation } from "../../registry";
|
||||
import _Text, { TextProps, TextPropertySchema } from "../_internal/Text";
|
||||
import { useExpression } from "../../store";
|
||||
|
||||
const Text: ComponentImplementation<TextProps> = ({ value, mergeState }) => {
|
||||
const raw = useExpression(value.raw);
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ value: raw });
|
||||
}, [raw]);
|
||||
mergeState({ value: value.raw });
|
||||
}, [value.raw]);
|
||||
|
||||
return <_Text value={{ ...value, raw }} />;
|
||||
return <_Text value={value} />;
|
||||
};
|
||||
|
||||
const StateSchema = Type.Object({
|
||||
|
@ -3,16 +3,14 @@ import { createComponent } from "@meta-ui/core";
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import Text, { TextProps, TextPropertySchema } from "../_internal/Text";
|
||||
import { ComponentImplementation } from "../../registry";
|
||||
import { useExpression } from "../../store";
|
||||
|
||||
const Button: ComponentImplementation<{
|
||||
text: TextProps["value"];
|
||||
onClick?: () => void;
|
||||
}> = ({ text, mergeState, subscribeMethods, onClick }) => {
|
||||
const raw = useExpression(text.raw);
|
||||
useEffect(() => {
|
||||
mergeState({ value: raw });
|
||||
}, [raw]);
|
||||
mergeState({ value: text.raw });
|
||||
}, [text.raw]);
|
||||
|
||||
const ref = useRef<HTMLButtonElement>(null);
|
||||
useEffect(() => {
|
||||
@ -25,7 +23,7 @@ const Button: ComponentImplementation<{
|
||||
|
||||
return (
|
||||
<button ref={ref} onClick={onClick}>
|
||||
<Text value={{ ...text, raw }} />
|
||||
<Text value={text} />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
@ -4,7 +4,7 @@ import { Static, Type } from "@sinclair/typebox";
|
||||
import { nanoid } from "nanoid";
|
||||
import { debounce, throttle, delay } from "lodash";
|
||||
import { TraitImplementation } from "../../registry";
|
||||
import { emitter, evalInContext, useStore } from "../../store";
|
||||
import { emitter } from "../../store";
|
||||
|
||||
const useEventTrait: TraitImplementation<{
|
||||
events: Static<typeof EventsPropertySchema>;
|
||||
@ -36,18 +36,15 @@ const useEventTrait: TraitImplementation<{
|
||||
for (const event of events) {
|
||||
const handler = () => {
|
||||
let disabled = false;
|
||||
const currentStoreState = useStore.getState();
|
||||
if (typeof event.disabled === "boolean") {
|
||||
disabled = event.disabled;
|
||||
} else if (typeof event.disabled === "string") {
|
||||
disabled = evalInContext(event.disabled, currentStoreState);
|
||||
}
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
emitter.emit(event.componentId, {
|
||||
name: event.method.name,
|
||||
parameters: evalInContext(event.method.parameters, currentStoreState),
|
||||
parameters: event.method.parameters,
|
||||
});
|
||||
};
|
||||
if (!handlerMap.current[event.event]) {
|
||||
@ -85,7 +82,7 @@ const EventsPropertySchema = Type.Array(
|
||||
componentId: Type.String(),
|
||||
method: Type.Object({
|
||||
name: Type.String(),
|
||||
parameters: Type.String(),
|
||||
parameters: Type.Any(),
|
||||
}),
|
||||
wait: Type.Object({
|
||||
type: Type.KeyOf(
|
||||
@ -97,7 +94,7 @@ const EventsPropertySchema = Type.Array(
|
||||
),
|
||||
time: Type.Number(),
|
||||
}),
|
||||
disabled: Type.Union([Type.Boolean(), Type.String()]),
|
||||
disabled: Type.Boolean(),
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -2,7 +2,6 @@ import { useCallback, useEffect, useMemo } from "react";
|
||||
import { createTrait } from "@meta-ui/core";
|
||||
import { Static, Type } from "@sinclair/typebox";
|
||||
import { TraitImplementation } from "../../registry";
|
||||
import { evalInContext, useExpression, useStore } from "../../store";
|
||||
|
||||
const useFetchTrait: TraitImplementation<FetchPropertySchema> = ({
|
||||
name,
|
||||
@ -18,8 +17,6 @@ const useFetchTrait: TraitImplementation<FetchPropertySchema> = ({
|
||||
return _lazy === undefined ? method.toLowerCase() !== "get" : _lazy;
|
||||
}, [method, _lazy]);
|
||||
|
||||
const urlExpression = useExpression(url);
|
||||
|
||||
const fetchData = useCallback(() => {
|
||||
// before fetching, initial data
|
||||
mergeState({
|
||||
@ -34,12 +31,11 @@ const useFetchTrait: TraitImplementation<FetchPropertySchema> = ({
|
||||
if (_headers) {
|
||||
for (let i = 0; i < _headers.length; i++) {
|
||||
const header = _headers[i];
|
||||
const value = evalInContext(_headers[i].value, useStore.getState());
|
||||
headers.append(header.key, value);
|
||||
headers.append(header.key, _headers[i].value);
|
||||
}
|
||||
}
|
||||
// fetch data
|
||||
fetch(urlExpression, {
|
||||
fetch(url, {
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
@ -81,7 +77,7 @@ const useFetchTrait: TraitImplementation<FetchPropertySchema> = ({
|
||||
});
|
||||
}
|
||||
);
|
||||
}, [urlExpression, method, _headers, body, lazy]);
|
||||
}, [url, method, _headers, body, lazy]);
|
||||
|
||||
// intialize data
|
||||
useEffect(() => {
|
||||
@ -96,11 +92,11 @@ const useFetchTrait: TraitImplementation<FetchPropertySchema> = ({
|
||||
|
||||
// non lazy query, listen to the change and query;
|
||||
useEffect(() => {
|
||||
if (lazy || !urlExpression) {
|
||||
if (lazy || !url) {
|
||||
return;
|
||||
}
|
||||
fetchData();
|
||||
}, [urlExpression, method, _headers, body, lazy]);
|
||||
}, [url, method, _headers, body, lazy]);
|
||||
|
||||
// only subscribe non lazy fetch trait
|
||||
if (lazy) {
|
||||
|
@ -2,14 +2,12 @@ import React from "react";
|
||||
import { createTrait } from "@meta-ui/core";
|
||||
import { Static, Type } from "@sinclair/typebox";
|
||||
import { TraitImplementation } from "../../registry";
|
||||
import { useExpression } from "../../store";
|
||||
|
||||
type HiddenProps = {
|
||||
hidden: Static<typeof HiddenPropertySchema>;
|
||||
};
|
||||
|
||||
const Hidden: React.FC<HiddenProps> = ({ hidden: _hidden, children }) => {
|
||||
const hidden = useExpression(_hidden.toString());
|
||||
const Hidden: React.FC<HiddenProps> = ({ hidden, children }) => {
|
||||
if (hidden) {
|
||||
return null;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user