diff --git a/src/components/widgets/control/models/index.tsx b/src/components/widgets/control/models/index.tsx index d9b02c09..859c176d 100644 --- a/src/components/widgets/control/models/index.tsx +++ b/src/components/widgets/control/models/index.tsx @@ -1,4 +1,5 @@ import type React from 'react'; +import type { FocusEvent } from 'react'; import type { HTMLMotionProps } from 'framer-motion'; import type { OmitOf } from '@busymango/utils'; @@ -28,19 +29,20 @@ export type ControlOption = { title?: string; label?: React.ReactNode; disabled?: boolean; -}; + icon?: React.ReactNode; +} & Pick; export interface InteractionProps { ref: (node: HTMLElement | SVGElement | null) => void; - onBlur?(): void; - onFocus?(): void; - onClick?(): void; - onKeyUp?(): void; - onKeyDown?(): void; - onMouseDown?(): void; - onMouseMove?(): void; - onPointerDown?(): void; - onPointerEnter?(): void; + onBlur?(e: React.FocusEvent): void; + onFocus?(e: React.FocusEvent): void; + onClick?(e: React.MouseEvent): void; + onKeyUp?(e: React.KeyboardEvent): void; + onKeyDown?(e: React.KeyboardEvent): void; + onMouseDown?(e: React.MouseEvent): void; + onMouseMove?(e: React.MouseEvent): void; + onPointerDown?(e: React.UIEvent): void; + onPointerEnter?(e: React.UIEvent): void; } export type IControlVariant = 'filled' | 'standard' | 'bordered'; diff --git a/src/components/widgets/popover/helpers/index.ts b/src/components/widgets/popover/helpers/index.ts index e2763cbb..6ef50f1d 100644 --- a/src/components/widgets/popover/helpers/index.ts +++ b/src/components/widgets/popover/helpers/index.ts @@ -24,7 +24,9 @@ type MiddlewareOpts = { export const iFill = (mode?: IPopoverProps['mode']) => { switch (mode) { case 'tip': - return 'var(--bg-color-float)'; + return 'var(--bg-color-tip)'; + case 'confirm': + return 'var(--bg-color-tip)'; default: return 'var(--bg-color-card)'; } @@ -36,7 +38,6 @@ export const iFloatingMaxSize = ( ) => { const { elements } = params; const size = capitalize(mode); - console.log(params); const availableSize = params[`available${size}`]; const scrollSize = elements.floating[`scroll${size}`]; const maxSize = ifnot( diff --git a/src/components/widgets/popover/index.tsx b/src/components/widgets/popover/index.tsx index e719cad3..323904cb 100644 --- a/src/components/widgets/popover/index.tsx +++ b/src/components/widgets/popover/index.tsx @@ -17,7 +17,7 @@ import { useControlState } from '../control'; import { useFloatingMotion } from './hooks/motion'; import { ARROW_HEIGHT, ARROW_RADIUS, iFill, middlewares } from './helpers'; import { useInterax } from './hooks'; -import type { IPopoverProps, IPopoverRef } from './models'; +import type { IPopoverProps, IPopoverRef, IPopoverState } from './models'; import * as styles from './index.scss'; @@ -61,12 +61,17 @@ export const IPopover = forwardRef( const interax = useInterax(context, { mode, events }); + const states: IPopoverState = { open: context.open, placement }; + return ( - {children?.({ - ref: refs.setReference, - ...interax.getReferenceProps(), - })} + {children?.( + { + ref: refs.setReference, + ...interax.getReferenceProps(), + }, + states + )} {context.open && ( ; + export interface ApplyFloatingStyle { ( params: MiddlewareState & { @@ -33,5 +35,5 @@ export interface IPopoverProps mode?: 'tip' | 'over' | 'confirm'; trigger?: IPopoverEvent | IPopoverEvent[]; onApplyFloatingStyle?: ApplyFloatingStyle; - children?: (props: InteractionProps) => React.ReactNode; + children?: (props: InteractionProps, state: IPopoverState) => React.ReactNode; } diff --git a/src/components/widgets/selector/helpers/index.ts b/src/components/widgets/selector/helpers/index.ts index f01cf138..d0f11cfd 100644 --- a/src/components/widgets/selector/helpers/index.ts +++ b/src/components/widgets/selector/helpers/index.ts @@ -1,3 +1,7 @@ +import { isNonEmptyString, isString } from '@busymango/is-esm'; +import { ifnot } from '@busymango/utils'; + +import type { ControlOption } from '../../control'; import type { ISignType } from '../../sign'; import type { ISelectorState } from '../models'; @@ -16,3 +20,12 @@ export const iSignType = ({ return isShowClear ? 'cross' : iArrow; }; + +export const iPredicate = ( + { title, label }: ControlOption, + keyword?: string +) => { + if (!isNonEmptyString(keyword)) return true; + const text = title ?? ifnot(isString(label) && label); + return text?.toLowerCase()?.includes(keyword?.toLowerCase()) ?? false; +}; diff --git a/src/components/widgets/selector/hooks/index.tsx b/src/components/widgets/selector/hooks/index.tsx index 3373d211..10de3b07 100644 --- a/src/components/widgets/selector/hooks/index.tsx +++ b/src/components/widgets/selector/hooks/index.tsx @@ -1,13 +1,7 @@ import { useMemo } from 'react'; import { flushSync } from 'react-dom'; -import { - isNonEmptyString, - isObject, - isString, - isTrue, -} from '@busymango/is-esm'; -import { ifnot } from '@busymango/utils'; +import { isObject, isTrue } from '@busymango/is-esm'; import type { FloatingContext, MiddlewareState } from '@floating-ui/react'; import { autoUpdate, @@ -25,6 +19,7 @@ import { size2px } from '@/utils'; import type { ControlOption } from '../../control'; import { estimateSize } from '../../scrollable'; +import { iPredicate } from '../helpers'; import type { ISelectorPredicate, ISelectorProps } from '../models'; export const useIFloating = (params: { @@ -118,24 +113,14 @@ export const useFilterOptions = ( const { filter, keyword } = params; const predicate = useMemo(() => { - if (isObject(filter)) { - return filter?.predicate; - } - if (isTrue(filter)) { - return ({ title, label }: ControlOption, keyword?: string) => { - if (!isNonEmptyString(keyword)) return true; - const text = title ?? ifnot(isString(label) && label); - return text?.toLowerCase()?.includes(keyword?.toLowerCase()) ?? false; - }; - } + if (isTrue(filter)) return iPredicate; + if (isObject(filter)) return filter?.predicate; }, [filter]); return useMemo(() => { - if (predicate) { - return options?.filter((option) => { - return predicate(option, keyword); - }); - } - return options; + if (!predicate) return options; + return options?.filter((option) => { + return predicate(option, keyword); + }); }, [predicate, keyword, options]); }; diff --git a/src/components/widgets/sign/line.tsx b/src/components/widgets/sign/line.tsx index de2db150..7fe2c974 100644 --- a/src/components/widgets/sign/line.tsx +++ b/src/components/widgets/sign/line.tsx @@ -1,3 +1,4 @@ +import { forwardRef, useImperativeHandle, useRef } from 'react'; import { AnimatePresence, motion } from 'framer-motion'; import { IDollarPath } from './dollar'; @@ -6,68 +7,71 @@ import { iAnimateLine, initial, transition } from './helpers'; import { IMagnifierPath } from './magnifier'; import type { ISignLineProps } from './models'; -export const ISignLine: React.FC = ({ - type, - rect, - ring, - style, - ...others -}) => ( - - - {ring && ( - - )} - - - {rect && ( - - )} - - - {iAnimateLine(type).map((animate, index) => ( - - ))} - - - {type === 'dollar' && } - {type === 'helper' && } - {type === 'magnifier' && } - - +export const ISignLine = forwardRef( + function SignLine({ type, rect, ring, style, ...others }, iForwardRef) { + const ref = useRef(null); + + useImperativeHandle(iForwardRef, () => ref.current!, [ref]); + + return ( + + + {ring && ( + + )} + + + {rect && ( + + )} + + + {iAnimateLine(type).map((animate, index) => ( + + ))} + + + {type === 'dollar' && } + {type === 'helper' && } + {type === 'magnifier' && } + + + ); + } ); diff --git a/src/components/widgets/snackbar/hooks/index.tsx b/src/components/widgets/snackbar/hooks/index.tsx index 8052e2d4..3262f8b5 100644 --- a/src/components/widgets/snackbar/hooks/index.tsx +++ b/src/components/widgets/snackbar/hooks/index.tsx @@ -65,7 +65,6 @@ export const useSnackbars = create( ); }, emit: async (config: ISnackbarProps) => { - console.log(1); const { snackbars: previous, max } = get(); const assert = ({ id }: ISnackbarProps) => id === config.id; if (contains(previous, assert)) { diff --git a/src/components/widgets/wave/index.tsx b/src/components/widgets/wave/index.tsx index ca2c40f7..688f21a6 100644 --- a/src/components/widgets/wave/index.tsx +++ b/src/components/widgets/wave/index.tsx @@ -63,8 +63,6 @@ export const IWave: React.FC = (props) => { ({ clientWidth, clientHeight }) => { scope.current.style.width = `${clientWidth}px`; scope.current.style.height = `${clientHeight}px`; - - console.log(clientWidth, clientHeight); }, { debounce: 10 * FRAME2MS } ); @@ -98,3 +96,15 @@ export const IWaveWrap: ReactCFC = ({ {children} ); + +export const IWaveShell: React.FC<{ + children: (ref: React.MutableRefObject) => React.ReactNode; +}> = ({ children }) => { + const ref = useRef(null); + return ( + + + {children(ref)} + + ); +}; diff --git a/src/examples/selector/custom.tsx b/src/examples/selector/custom.tsx index 1f343a32..794a6e7f 100644 --- a/src/examples/selector/custom.tsx +++ b/src/examples/selector/custom.tsx @@ -20,11 +20,33 @@ import { IHighLighter, IInput, IOverflow, + IPopover, ISelector, + ISignLine, + IWaveShell, Scrollable, } from '@/components'; import { useToggle } from '@/hooks'; -import { iCompact } from '@/utils'; +import { iCompact, iPropagation, iThemeVariable } from '@/utils'; + +const iColorDisc = iCompact( + [ + 'orange', + 'sunset', + 'sunglow', + 'shamrock', + 'green', + 'viking', + 'malibu', + 'blue', + 'dodger', + 'heliotrope', + 'violet', + 'purple', + 'rosein', + 'red', + ].map((name) => `rgb(${iThemeVariable(`--${name}-color-600`)} / 1)`) +); const options = [ 'Oliver Hansen', @@ -37,7 +59,11 @@ const options = [ 'Bradley Wilkerson', 'Virginia Andrews', 'Kelly Snyder', -].map((value) => ({ value, label: value })); +].map((value, index) => ({ + value, + label: value, + color: iColorDisc[index], +})); const useSelectorStore = create<{ options: ControlOption[]; @@ -57,13 +83,19 @@ const iChipRender: ISelectorChipRender = ( { option, onClose }, { multiple } ) => { - const { label } = option ?? {}; + const { label, color } = option ?? {}; const content = label ?? 'UnknownRender'; return ( - {!multiple && content} + {!multiple && {content}} {multiple && ( - + {content} )} @@ -76,7 +108,7 @@ const iOptionRender: ISelectorOptionRender = ( { keyword } ) => ( - + Object.is(pre?.toLowerCase(), cur?.toLowerCase()) @@ -85,6 +117,56 @@ const iOptionRender: ISelectorOptionRender = ( keyword={keyword} /> + + {iColorDisc.map((color) => ( +
+ + {(ref) => ( +
+ )} + +
+ ))} + + } + mode="confirm" + > + {({ onClick, ...props }, { open }) => ( + { + iPropagation?.(event); + onClick?.(event); + }} + {...props} + /> + )} + );