diff --git a/assets/themes/dark.css b/assets/themes/dark.css index 14421cc5..722c5c78 100644 --- a/assets/themes/dark.css +++ b/assets/themes/dark.css @@ -269,6 +269,8 @@ /* 背景色 - 滑块 */ --bg-color-thumb: rgb(var(--gray-color-900) / 1); + /* 背景色 - 强调 */ + --bg-color-info: rgb(var(--blue-color-050) / 1); /* 背景色 - 危险 */ --bg-color-danger: rgb(var(--red-color-050) / 1); /* 背景色 - 警告 */ @@ -296,12 +298,14 @@ --font-color-10: rgba(255, 255, 255 / 1); --font-color-b8: rgba(255, 255, 255 / 1); + /* 字体颜色 - 强调 */ + --font-color-info: rgb(var(--blue-color-900) / 1); /* 字体颜色 - 警告 */ - --font-color-warn: rgb(var(--orange-color-500) / 1); + --font-color-warn: rgb(var(--orange-color-900) / 1); /* 字体颜色 - 危险 */ - --font-color-danger: rgb(var(--red-color-700) / 1); + --font-color-danger: rgb(var(--red-color-700) / 0.9); /* 字体颜色 - 成功 */ - --font-color-success: rgb(var(--green-color-700) / 1); + --font-color-success: rgb(var(--green-color-900) / 1); /* 字体颜色 - 禁用 */ --font-color-disabled: rgb(var(--gray-color-600) / 0.9); /* 字体颜色 - 高亮 */ @@ -309,6 +313,14 @@ /* 图标颜色-默认 */ --icon-color: rgb(var(--gray-color-700) / 1); + /* 图标颜色 - 强调 */ + --icon-color-info: rgb(var(--blue-color-500) / 1); + /* 图标颜色 - 警告 */ + --icon-color-warn: rgb(var(--orange-color-500) / 1); + /* 图标颜色 - 危险 */ + --icon-color-danger: rgb(var(--red-color-700) / 1); + /* 图标颜色 - 成功 */ + --icon-color-success: rgb(var(--green-color-700) / 1); /* 波纹动画颜色 */ --wave-color-0: rgb(var(--heliotrope-color-600) / 0.1); diff --git a/assets/themes/light.css b/assets/themes/light.css index 4f9a513f..47bbd849 100644 --- a/assets/themes/light.css +++ b/assets/themes/light.css @@ -269,6 +269,8 @@ /* 背景色 - 滑块 */ --bg-color-thumb: rgb(var(--gray-color-000) / 1); + /* 背景色 - 强调 */ + --bg-color-info: rgb(var(--blue-color-050) / 1); /* 背景色 - 危险 */ --bg-color-danger: rgb(var(--red-color-050) / 1); /* 背景色 - 警告 */ @@ -309,6 +311,14 @@ /* 图标颜色-默认 */ --icon-color: rgb(var(--gray-color-700) / 1); + /* 字体颜色 - 高亮 */ + --icon-color-info: rgb(var(--blue-color-600) / 1); + /* 字体颜色 - 警告 */ + --icon-color-warn: rgb(var(--orange-color-600) / 1); + /* 字体颜色 - 危险 */ + --icon-color-danger: rgb(var(--red-color-700) / 1); + /* 字体颜色 - 成功 */ + --icon-color-success: rgb(var(--green-color-700) / 1); /* 波纹动画颜色 */ --wave-color-1: rgb(82 11 239 / 0.05); diff --git a/config/compiler/plugins.ts b/config/compiler/plugins.ts index 1b1fd47b..ab031959 100644 --- a/config/compiler/plugins.ts +++ b/config/compiler/plugins.ts @@ -9,7 +9,11 @@ import { join, resolve } from 'path'; import { assign, compact } from '@busymango/utils'; import { parse } from '@dotenvx/dotenvx'; import { RsdoctorRspackPlugin } from '@rsdoctor/rspack-plugin'; -import type { RspackPluginFunction, RspackPluginInstance } from '@rspack/core'; +import type { + RspackPluginFunction, + RspackPluginInstance, + WebpackPluginInstance, +} from '@rspack/core'; import { rspack } from '@rspack/core'; import ReactRefreshRspackPlugin from '@rspack/plugin-react-refresh'; @@ -32,7 +36,7 @@ const doctor = new RsdoctorRspackPlugin({ export const iPlugins = ( env: 'dev' | 'test' | 'prod' = 'dev' -): RspackPlugin[] => { +): (RspackPlugin | WebpackPluginInstance)[] => { const dotenv = assign<{ THEME: string; ENV_NAME: string; @@ -80,7 +84,7 @@ export const iPlugins = ( build: env !== 'dev', mode: 'write-references', }, - }) as unknown as null, + }), env === 'test' && doctor, env === 'dev' && new CSSVarTSEmitPlugin({ diff --git a/docs/components/data-view/directive.mdx b/docs/components/feedback/directive.mdx similarity index 68% rename from docs/components/data-view/directive.mdx rename to docs/components/feedback/directive.mdx index b4feec93..9eba0a96 100644 --- a/docs/components/data-view/directive.mdx +++ b/docs/components/feedback/directive.mdx @@ -10,4 +10,8 @@ ### 基本用法 - \ No newline at end of file + + +### 基本用法 + + \ No newline at end of file diff --git a/examples/directive/advanced.tsx b/examples/directive/advanced.tsx new file mode 100644 index 00000000..f50cd0ad --- /dev/null +++ b/examples/directive/advanced.tsx @@ -0,0 +1,60 @@ +import { AnimatePresence } from 'framer-motion'; +import { produce } from 'immer'; +import { create } from 'zustand'; + +import { isNonEmptyString } from '@busymango/is-esm'; + +import { + IControlWrap, + IDirective, + IFlex, + ISignLine, + ITextArea, +} from '@/components'; + +type DirectiveStore = { + directive?: string; + mutation(recipe: (state: DirectiveStore) => void): void; +}; + +const useDirectiveStore = create((set) => ({ + mutation: (recipe) => { + set(produce(recipe)); + }, +})); + +const App: React.FC = () => { + const { directive, mutation } = useDirectiveStore(); + + return ( + + } + variant="bordered" + onSuffixClick={() => { + mutation((state) => { + state.directive = ''; + }); + }} + > + { + mutation((state) => { + state.directive = target.value; + }); + }} + /> + + + {isNonEmptyString(directive) && ( + } title="通用"> + {directive} + + )} + + + ); +}; + +export default App; diff --git a/examples/directive/basic.tsx b/examples/directive/basic.tsx index 720bc874..5374944f 100644 --- a/examples/directive/basic.tsx +++ b/examples/directive/basic.tsx @@ -1,18 +1,45 @@ -import { Fragment } from 'react/jsx-runtime'; +import { VariantControl } from '@examples/widgets'; -import { ISignLine } from '@/components'; -import { IDirective } from '@/components/widgets/directive'; +import { IDirective, IFlex, ISignLine } from '@/components'; const App: React.FC = () => ( - - } - title="通用" - > - } title="成功"> - - - + + {({ variant }) => ( + + } + title="通用" + variant={variant} + > + 这是一个指示。 + + } + status="success" + title="成功" + variant={variant} + > + 这是一个成功的指示。 + + } + status="danger" + title="危险" + variant={variant} + > + 这是一个危险的指示。 + + } + status="warn" + title="警告" + variant={variant} + > + 这是一个包含警告意的指示。 + + + )} + ); export default App; diff --git a/examples/widgets/variant-control/index.tsx b/examples/widgets/variant-control/index.tsx index 391d1f6d..ebc9a37d 100644 --- a/examples/widgets/variant-control/index.tsx +++ b/examples/widgets/variant-control/index.tsx @@ -72,9 +72,9 @@ export function VariantControl( ({ value }))} diff --git a/rspress.config.ts b/rspress.config.ts index e085c128..7126d4ac 100644 --- a/rspress.config.ts +++ b/rspress.config.ts @@ -1,3 +1,4 @@ +import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; import { readFileSync } from 'fs'; import { join, resolve } from 'path'; import { defineConfig } from 'rspress/config'; @@ -52,6 +53,16 @@ export default defineConfig({ resourceQuery: /react/, options: { icon: false, typescript: true }, }); + + config.plugins?.push( + new ForkTsCheckerWebpackPlugin({ + typescript: { + build: config.mode !== 'development', + mode: 'write-references', + }, + }) + ); + return config; }, }, diff --git a/src/components/widgets/control/control.warp.scss b/src/components/widgets/control/control.warp.scss index 0ff57f94..4d696989 100644 --- a/src/components/widgets/control/control.warp.scss +++ b/src/components/widgets/control/control.warp.scss @@ -72,10 +72,10 @@ color: var(--font-color-disabled); } &:not(.disabled) { - &.error { + &.danger { color: var(--font-color-danger); } - &.warning { + &.warn { color: var(--font-color-warn); } } @@ -85,10 +85,10 @@ background-color: var(--natural-color-disabled); } &:not(.disabled):not(.read-pretty) { - &.error { + &.danger { background-color: var(--bg-color-danger); } - &.warning { + &.warn { background-color: var(--bg-color-warn); } &.success, @@ -110,7 +110,7 @@ background-color: var(--natural-color-disabled); } &:not(.disabled) { - &.error { + &.danger { border-color: var(--danger-color); &:hover { @@ -132,7 +132,7 @@ border-color: var(--secondary-color-active); } } - &.warning { + &.warn { border-color: var(--warn-color); &:hover { diff --git a/src/components/widgets/control/hooks/control.state.tsx b/src/components/widgets/control/hooks/control.state.tsx index 4857daf6..4c42f4ae 100644 --- a/src/components/widgets/control/hooks/control.state.tsx +++ b/src/components/widgets/control/hooks/control.state.tsx @@ -3,7 +3,7 @@ import { useLayoutEffect, useRef, useState } from 'react'; import { isUndefined } from '@busymango/is-esm'; import { useMemoFunc } from '@/hooks'; -import { isInputElement } from '@/utils'; +import { isInputElement, isTextAreaElement } from '@/utils'; export interface ControlParams { /** 输入法是否介入中 */ @@ -17,6 +17,13 @@ export interface ControlComponentProps { onChange?: (value: E, ...args: Args) => void; } +export const onTextAreaCatch = ( + event: React.ChangeEvent +) => { + const { target } = event ?? {}; + if (isTextAreaElement(target)) return target.value; +}; + export const onInputCatch = (event: React.ChangeEvent) => { const { target } = event ?? {}; if (isInputElement(target)) return target.value; diff --git a/src/components/widgets/control/models/index.tsx b/src/components/widgets/control/models/index.tsx index 33a606f4..92464a2f 100644 --- a/src/components/widgets/control/models/index.tsx +++ b/src/components/widgets/control/models/index.tsx @@ -20,7 +20,7 @@ export type ControlPattern = | 'readPretty'; /** 控件校验状态 */ -export type ControlUIStatus = 'vaildating' | 'error' | 'warning' | 'success'; +export type ControlUIStatus = 'vaildating' | 'danger' | 'warn' | 'success'; export type ControlValue = ReactInputProps['value'] | null; diff --git a/src/components/widgets/directive/index.scss b/src/components/widgets/directive/index.scss index 22e491cb..24036fe1 100644 --- a/src/components/widgets/directive/index.scss +++ b/src/components/widgets/directive/index.scss @@ -1,9 +1,81 @@ .wrap { display: grid; + grid-template-rows: max-content; + grid-template-columns: max-content minmax(0, 1fr); + + border-width: 1px; + border-style: solid; + box-sizing: border-box; + padding: var(--gap-03) var(--gap-05); + border-radius: var(--border-radius-04); + + &.with-icon { + gap: var(--gap-03); + } + + &:not(.with-icon) { + } +} + +.filled { + border-color: transparent; +} +.borderered { + border-color: currentColor; } -.header, -.with-icon { +.info { + color: var(--font-color-info); + + .icon { + color: var(--icon-color-info); + } + .article { + color: var(--font-color-info); + } + &.filled { + background-color: var(--bg-color-info); + } +} +.warn { + color: var(--font-color-warn); + + .icon { + color: var(--icon-color-warn); + } + .article { + color: var(--font-color-warn); + } + &.filled { + background-color: var(--bg-color-warn); + } +} +.danger { + color: var(--font-color-danger); + .icon { + color: var(--icon-color-danger); + } + .article { + color: var(--font-color-danger); + } + &.filled { + background-color: var(--bg-color-danger); + } +} +.success { + color: var(--font-color-success); + .icon { + color: var(--icon-color-success); + } + .article { + color: var(--font-color-success); + } + &.filled { + background-color: var(--bg-color-success); + } +} + +.header { grid-template-rows: max-content; grid-template-columns: max-content minmax(0, 1fr); } diff --git a/src/components/widgets/directive/index.tsx b/src/components/widgets/directive/index.tsx index a5ee4ea9..7f32f102 100644 --- a/src/components/widgets/directive/index.tsx +++ b/src/components/widgets/directive/index.tsx @@ -1,28 +1,56 @@ +import { forwardRef, useImperativeHandle, useRef } from 'react'; import classNames from 'classnames'; +import type { HTMLMotionProps } from 'framer-motion'; import { motion } from 'framer-motion'; +import type { OmitOf } from '@busymango/utils'; + import type { ReactCFC } from '@/models'; import { isReactNode } from '@/utils'; +import { ISVGWrap } from '../svg-wrap'; + import * as styles from './index.scss'; -export interface IDirectiveProps { +export interface IDirectiveProps + extends React.PropsWithChildren, + OmitOf, 'title' | 'children'> { icon?: React.ReactNode; title?: React.ReactNode; extra?: React.ReactNode; + variant?: 'filled' | 'borderered'; + status?: 'success' | 'danger' | 'warn' | 'info'; } -export const IDirective: ReactCFC = (props) => { - const { icon, title, extra, children } = props; +export const IDirective: ReactCFC = forwardRef< + HTMLDivElement, + IDirectiveProps +>(function IDirective(props, iForwardRef) { + const { + icon, + title, + extra, + status = 'info', + variant = 'filled', + children, + } = props; + + const target = useRef(null); + + useImperativeHandle(iForwardRef, () => target.current!); return ( -
{icon}
-
+
{icon && {icon}}
+
{title} {extra} @@ -31,4 +59,4 @@ export const IDirective: ReactCFC = (props) => {
); -}; +}); diff --git a/src/components/widgets/form-field/index.scss b/src/components/widgets/form-field/index.scss index cd925b14..e40bf21b 100644 --- a/src/components/widgets/form-field/index.scss +++ b/src/components/widgets/form-field/index.scss @@ -98,8 +98,8 @@ margin: unset; } - &.error, - &.warning, + &.danger, + &.warn, &.success, &.waiting { font-size: var(--font-size-04); diff --git a/src/components/widgets/form-field/index.tsx b/src/components/widgets/form-field/index.tsx index e6bec5d7..53f78763 100644 --- a/src/components/widgets/form-field/index.tsx +++ b/src/components/widgets/form-field/index.tsx @@ -39,7 +39,7 @@ export const IFieldGrid: ReactCFC = (props) => { useResizeObserver( ref, - ({ scrollWidth }) => { + ({ target: { scrollWidth } }) => { if (scrollWidth <= 260) { setIMode('vertical'); } else if (scrollWidth <= 430) { diff --git a/src/components/widgets/form-field/models/index.tsx b/src/components/widgets/form-field/models/index.tsx index 96fcb1bb..56d83d2d 100644 --- a/src/components/widgets/form-field/models/index.tsx +++ b/src/components/widgets/form-field/models/index.tsx @@ -9,7 +9,7 @@ import type { } from '../../control'; import type { IFlexProps } from '../../flex/models'; -export type IFieldStatus = 'error' | 'success' | 'warning'; +export type IFieldStatus = 'danger' | 'success' | 'warn'; export type IFieldGridMode = | ControlUIDirection diff --git a/src/components/widgets/index.tsx b/src/components/widgets/index.tsx index cd6c07f6..ddd4e5cf 100644 --- a/src/components/widgets/index.tsx +++ b/src/components/widgets/index.tsx @@ -7,6 +7,7 @@ export * from './checkbox'; export * from './chip'; export * from './collapsible'; export * from './control'; +export * from './directive'; export * from './flex'; export * from './form-field'; export * from './form-wrap'; diff --git a/src/components/widgets/motion-panel/index.scss b/src/components/widgets/motion-panel/index.scss index 7ece9a24..0bba8a54 100644 --- a/src/components/widgets/motion-panel/index.scss +++ b/src/components/widgets/motion-panel/index.scss @@ -13,8 +13,8 @@ height: max-content; } - &.error, - &.warning, + &.warn, + &.danger, &.success, &.waiting { font-size: inherit; diff --git a/src/components/widgets/motion-panel/index.tsx b/src/components/widgets/motion-panel/index.tsx index 9ab95f28..964a0de3 100644 --- a/src/components/widgets/motion-panel/index.tsx +++ b/src/components/widgets/motion-panel/index.tsx @@ -2,12 +2,12 @@ * @description 消息反馈组件 */ -import { useRef, useState } from 'react'; +import { useRef } from 'react'; import classNames from 'classnames'; import type { Target, Transition } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion'; -import { useMemoFunc, useRecord, useResizeObserver } from '@/hooks'; +import { useRecord } from '@/hooks'; import type { ReactCFC } from '@/models'; import type { IMotionPanelProps, IMotionPanelRender } from './models'; @@ -34,21 +34,13 @@ export const IMotionPanel: ReactCFC = (props) => { const target = useRef(null); - const [height, setHeight] = useState(); - const record = useRecord(children, visible && ghosting); - const iSyncHeight = useMemoFunc(({ scrollHeight }: HTMLElement) => { - scrollHeight !== height && setHeight(scrollHeight); - }); - - useResizeObserver(target, iSyncHeight, { enabled: visible }); - return ( {visible && ( ( { +export const iTrigonOpposite = (hypotenuse: number, degrees = 30) => { const radians = degrees * (Math.PI / 180); return Math.sin(radians) * hypotenuse; }; @@ -19,7 +19,7 @@ export const iTrigoOpposite = (hypotenuse: number, degrees = 30) => { * @param degrees 角度 * @returns 邻边 */ -export const iTrigoAdjacent = (hypotenuse: number, degrees = 30) => { +export const iTrigonAdjacent = (hypotenuse: number, degrees = 30) => { const radians = degrees * (Math.PI / 180); return Math.cos(radians) * hypotenuse; }; @@ -30,10 +30,10 @@ export const iTrigoAdjacent = (hypotenuse: number, degrees = 30) => { * @param y 中心点y * @param r 以r为半径寻找正三角三个点坐标 */ -export const iTrigo = (x: number, y: number, r: number) => { +export const iTrigon = (x: number, y: number, r: number) => { const point1 = `${x} ${y - r}`; - const point2 = `${x - iTrigoAdjacent(r)} ${y + iTrigoOpposite(r)}`; - const point3 = `${x + iTrigoAdjacent(r)} ${y + iTrigoOpposite(r)}`; + const point2 = `${x - iTrigonAdjacent(r)} ${y + iTrigonOpposite(r)}`; + const point3 = `${x + iTrigonAdjacent(r)} ${y + iTrigonOpposite(r)}`; return `M${point1} L${point2} L${point3}Z`; }; @@ -103,8 +103,8 @@ export const iAnimateLine = (type?: ISignType): Target[] => { { d: [ 'M512 512', - `L${512 + iTrigoAdjacent(112)} ${512 + iTrigoOpposite(112)}`, - `L${512 + iTrigoAdjacent(224)} ${512 + iTrigoOpposite(224)}`, + `L${512 + iTrigonAdjacent(112)} ${512 + iTrigonOpposite(112)}`, + `L${512 + iTrigonAdjacent(224)} ${512 + iTrigonOpposite(224)}`, ].join(' '), }, ]; @@ -129,7 +129,7 @@ export const iAnimateLine = (type?: ISignType): Target[] => { { d: 'M704 320 L512 512 L320 704' }, ]; case 'informer': - return [{ d: 'M512 256 L512 256 L512 608' }, { d: iTrigo(512, 768, 8) }]; + return [{ d: 'M512 256 L512 256 L512 608' }, { d: iTrigon(512, 768, 8) }]; default: return []; } diff --git a/src/components/widgets/sign/line.tsx b/src/components/widgets/sign/line.tsx index 1ae49b59..e71281c1 100644 --- a/src/components/widgets/sign/line.tsx +++ b/src/components/widgets/sign/line.tsx @@ -3,13 +3,13 @@ import { AnimatePresence, motion } from 'framer-motion'; import { IDollarPath } from './dollar'; import { IHelperPath } from './helper'; -import { iAnimateLine, initial, transition } from './helpers'; +import { iAnimateLine, initial, iTrigon, transition } from './helpers'; import { IMagnifierPath } from './magnifier'; import type { ISignLineProps } from './models'; export const ISignLine = forwardRef( function SignLine( - { type, rect, ring, style, animate, ...others }, + { type, rect, ring, style, animate, trigon, ...others }, iForwardRef ) { const ref = useRef(null); @@ -47,8 +47,16 @@ export const ISignLine = forwardRef( transition={transition} /> )} - - + {trigon && ( + + )} {rect && ( ( transition={transition} /> ))} - - {type === 'dollar' && } {type === 'helper' && } {type === 'magnifier' && } diff --git a/src/components/widgets/sign/magnifier.tsx b/src/components/widgets/sign/magnifier.tsx index 0abfd00b..d66b27f4 100644 --- a/src/components/widgets/sign/magnifier.tsx +++ b/src/components/widgets/sign/magnifier.tsx @@ -4,8 +4,8 @@ import { motion } from 'framer-motion'; import { iCirclePath, initial, - iTrigoAdjacent, - iTrigoOpposite, + iTrigonAdjacent, + iTrigonOpposite, transition, } from './helpers'; @@ -22,9 +22,9 @@ const MagnifierPath: React.FC = () => ( { type?: ISignType; ring?: boolean; rect?: boolean; + trigon?: boolean; } diff --git a/src/components/widgets/snackbar/hooks/index.tsx b/src/components/widgets/snackbar/hooks/index.tsx index 3262f8b5..e3bd25f7 100644 --- a/src/components/widgets/snackbar/hooks/index.tsx +++ b/src/components/widgets/snackbar/hooks/index.tsx @@ -27,7 +27,7 @@ export const snackbar = { return await useSnackbars.getState().emit(options); }, ...(() => - (['info', 'error', 'warn', 'success'] satisfies ISnackbarStatus[]).reduce( + (['info', 'danger', 'warn', 'success'] satisfies ISnackbarStatus[]).reduce( (accom, status) => ({ ...accom, [status]: async (config: OmitOf, 'status'>) => { diff --git a/src/components/widgets/snackbar/index.scss b/src/components/widgets/snackbar/index.scss index f912ef67..5ff1664c 100644 --- a/src/components/widgets/snackbar/index.scss +++ b/src/components/widgets/snackbar/index.scss @@ -61,7 +61,7 @@ color: rgb(var(--warn-color) / 1); } } -.error { +.danger { .icon { color: rgb(var(--error-color) / 1); } diff --git a/src/components/widgets/snackbar/isnackbar.tsx b/src/components/widgets/snackbar/isnackbar.tsx index 74b30897..398821a8 100644 --- a/src/components/widgets/snackbar/isnackbar.tsx +++ b/src/components/widgets/snackbar/isnackbar.tsx @@ -1,11 +1,4 @@ -import { - forwardRef, - useDeferredValue, - useEffect, - useImperativeHandle, - useRef, - useState, -} from 'react'; +import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react'; import classNames from 'classnames'; import type { AnimationDefinition, @@ -19,12 +12,7 @@ import { isFinite, isObject, isTrue } from '@busymango/is-esm'; import { isEqual } from '@busymango/utils'; import type { EventStateParams } from '@/hooks'; -import { - useEventState, - useMemoFunc, - useResizeObserver, - useTimeout, -} from '@/hooks'; +import { useEventState, useMemoFunc, useTimeout } from '@/hooks'; import type { ReactTargetType } from '@/models'; import type { ISignType } from '../sign'; @@ -36,8 +24,8 @@ import type { ISnackbarProps } from './models'; import * as styles from './index.scss'; -const iAnimate = (height?: number): Target => ({ - height, +const iAnimate = (): Target => ({ + height: 'auto', scale: 1, opacity: 1, originY: 0, @@ -68,7 +56,7 @@ const iSnackbarSign = (status?: ISnackbarProps['status']): ISignType => { switch (status) { case 'success': return 'tick'; - case 'error': + case 'danger': return 'cross'; default: return 'informer'; @@ -94,16 +82,10 @@ export const ISnackbar = forwardRef( const [scope, iShakeAnimate] = useShakeAnimate(); - const [height, setHeight] = useState(); - const isHover = useEventState(iHoverParams(scope)); const iDestory = useSnackbars(({ destory }) => destory); - useResizeObserver(scope, ({ scrollHeight }) => { - if (height !== scrollHeight) setHeight(scrollHeight); - }); - useImperativeHandle(ref, () => scope.current); const destory = useMemoFunc(() => iDestory(id)); @@ -135,7 +117,7 @@ export const ISnackbar = forwardRef( return ( void; }; -export type ISnackbarStatus = 'success' | 'info' | 'error' | 'warn'; +export type ISnackbarStatus = 'success' | 'info' | 'danger' | 'warn'; export interface ISnackbarProps extends OmitOf, 'id'> { id: React.Key; diff --git a/src/components/widgets/textarea/index.tsx b/src/components/widgets/textarea/index.tsx index 79c5bc03..f13243cf 100644 --- a/src/components/widgets/textarea/index.tsx +++ b/src/components/widgets/textarea/index.tsx @@ -1,11 +1,11 @@ import { forwardRef, Fragment, useImperativeHandle, useRef } from 'react'; import classNames from 'classnames'; -import { useMemoFunc, useResizeObserver } from '@/hooks'; +import { useResizeObserver } from '@/hooks'; -import { useControlState } from '../control'; +import { onTextAreaCatch, useControlState } from '../control'; import { iTextareaSize } from './helpers'; -import type { ITextAreaEvent, ITextAreaProps, ITextareaRef } from './models'; +import type { ITextAreaProps, ITextareaRef } from './models'; import * as iStyles from '@/styles/widgets.scss'; import * as styles from './index.scss'; @@ -29,7 +29,10 @@ export const ITextArea = forwardRef( useImperativeHandle(ref, () => input, [input]); - const [value, onChange] = useControlState(other); + const [value, iChange] = useControlState({ + onCatch: onTextAreaCatch, + ...other, + }); useResizeObserver(shadow, () => { const { current: iInput } = input; @@ -47,10 +50,6 @@ export const ITextArea = forwardRef( } }); - const iChange = useMemoFunc(({ target }: ITextAreaEvent) => { - onChange?.(target.value); - }); - return (