@lobehub/ui 5.9.2 → 5.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/Accordion/Accordion.mjs +2 -2
- package/es/Accordion/Accordion.mjs.map +1 -1
- package/es/Accordion/AccordionItem.mjs +2 -2
- package/es/Accordion/AccordionItem.mjs.map +1 -1
- package/es/Checkbox/Checkbox.mjs +2 -2
- package/es/Checkbox/Checkbox.mjs.map +1 -1
- package/es/Checkbox/CheckboxGroup.mjs +2 -2
- package/es/Checkbox/CheckboxGroup.mjs.map +1 -1
- package/es/CodeEditor/CodeEditor.mjs +2 -2
- package/es/CodeEditor/CodeEditor.mjs.map +1 -1
- package/es/ColorSwatches/ColorSwatches.mjs +2 -2
- package/es/ColorSwatches/ColorSwatches.mjs.map +1 -1
- package/es/DraggablePanel/DraggablePanel.mjs +2 -2
- package/es/DraggablePanel/DraggablePanel.mjs.map +1 -1
- package/es/DraggablePanel/components/DraggablePanelHeader.mjs +2 -2
- package/es/DraggablePanel/components/DraggablePanelHeader.mjs.map +1 -1
- package/es/DraggableSideNav/DraggableSideNav.mjs +2 -2
- package/es/DraggableSideNav/DraggableSideNav.mjs.map +1 -1
- package/es/EditableText/EditableText.mjs +2 -2
- package/es/EditableText/EditableText.mjs.map +1 -1
- package/es/EmojiPicker/EmojiPicker.mjs +3 -3
- package/es/EmojiPicker/EmojiPicker.mjs.map +1 -1
- package/es/HotkeyInput/HotkeyInput.mjs +2 -2
- package/es/HotkeyInput/HotkeyInput.mjs.map +1 -1
- package/es/ImageSelect/ImageSelect.mjs +2 -2
- package/es/ImageSelect/ImageSelect.mjs.map +1 -1
- package/es/SearchBar/SearchBar.mjs +2 -2
- package/es/SearchBar/SearchBar.mjs.map +1 -1
- package/es/Toc/TocMobile.mjs +2 -2
- package/es/Toc/TocMobile.mjs.map +1 -1
- package/es/base-ui/Modal/style.mjs +1 -0
- package/es/base-ui/Modal/style.mjs.map +1 -1
- package/es/base-ui/Switch/atoms.mjs +2 -2
- package/es/base-ui/Switch/atoms.mjs.map +1 -1
- package/es/chat/EditableMessage/EditableMessage.mjs +3 -3
- package/es/chat/EditableMessage/EditableMessage.mjs.map +1 -1
- package/es/chat/MessageModal/MessageModal.mjs +3 -3
- package/es/chat/MessageModal/MessageModal.mjs.map +1 -1
- package/es/mobile/TabBar/TabBar.mjs +2 -2
- package/es/mobile/TabBar/TabBar.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DraggableSideNav.mjs","names":["useControlledState","Flexbox"],"sources":["../../src/DraggableSideNav/DraggableSideNav.tsx"],"sourcesContent":["'use client';\n\nimport { useHover } from 'ahooks';\nimport { cx } from 'antd-style';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\nimport { Resizable, type ResizeCallback } from 're-resizable';\nimport {\n type CSSProperties,\n memo,\n useCallback,\n useEffect,\n useMemo,\n useReducer,\n useRef,\n} from 'react';\nimport useControlledState from 'use-merge-value';\n\nimport { Center, Flexbox } from '@/Flex';\nimport Icon from '@/Icon';\n\nimport { styles } from './style';\nimport type { DraggableSideNavProps } from './type';\n\nconst DEFAULT_MIN_WIDTH = 64; // 最小宽度即折叠宽度\nconst DEFAULT_EXPAND = true;\nconst DEFAULT_EXPANDED_WIDTH = 280;\nconst ANIMATION_DURATION = 300;\nconst COLLAPSE_ANIMATION_DELAY = 200;\n\n// Pre-define static objects to avoid recreating\nconst RESIZE_DISABLED = {\n bottom: false,\n bottomLeft: false,\n bottomRight: false,\n left: false,\n right: false,\n top: false,\n topLeft: false,\n topRight: false,\n};\n\n// State reducer for better state management\ninterface SideNavState {\n expandedWidth: number;\n internalWidth: number;\n isAnimating: boolean;\n isResizing: boolean;\n renderExpand: boolean;\n}\n\ntype SideNavAction =\n | { type: 'START_RESIZE' }\n | { type: 'STOP_RESIZE' }\n | { type: 'START_ANIMATION' }\n | { type: 'STOP_ANIMATION' }\n | { payload: number; type: 'SET_WIDTH' }\n | { payload: number; type: 'SET_EXPANDED_WIDTH' }\n | { payload: boolean; type: 'SET_RENDER_EXPAND' }\n | { payload: number; type: 'ANIMATE_EXPAND' }\n | { payload: number; type: 'ANIMATE_COLLAPSE' };\n\nfunction sideNavReducer(state: SideNavState, action: SideNavAction): SideNavState {\n switch (action.type) {\n case 'START_RESIZE': {\n return { ...state, isResizing: true };\n }\n case 'STOP_RESIZE': {\n return { ...state, isResizing: false };\n }\n case 'START_ANIMATION': {\n return { ...state, isAnimating: true };\n }\n case 'STOP_ANIMATION': {\n return { ...state, isAnimating: false };\n }\n case 'SET_WIDTH': {\n return { ...state, internalWidth: action.payload };\n }\n case 'SET_EXPANDED_WIDTH': {\n return { ...state, expandedWidth: action.payload };\n }\n case 'SET_RENDER_EXPAND': {\n return { ...state, renderExpand: action.payload };\n }\n case 'ANIMATE_EXPAND': {\n return { ...state, internalWidth: action.payload, renderExpand: true };\n }\n case 'ANIMATE_COLLAPSE': {\n return { ...state, internalWidth: action.payload };\n }\n default: {\n return state;\n }\n }\n}\n\nconst DraggableSideNav = memo<DraggableSideNavProps>(\n ({\n body,\n className,\n classNames,\n defaultExpand = DEFAULT_EXPAND,\n defaultWidth,\n expand,\n expandable = true,\n footer,\n header,\n maxWidth,\n minWidth = DEFAULT_MIN_WIDTH,\n onExpandChange,\n onWidthChange,\n onWidthDragging,\n placement = 'left',\n resizable = true,\n showBorder = true,\n showHandle = true,\n showHandleWhenCollapsed = false,\n showHandleHighlight = false,\n backgroundColor,\n styles: customStyles,\n width,\n ...rest\n }) => {\n const cssVariables = useMemo<Record<string, string>>(\n () => ({\n '--draggable-side-nav-bg': backgroundColor || '',\n }),\n [backgroundColor],\n );\n const ref = useRef<HTMLDivElement>(null);\n const isHovering = useHover(ref);\n\n // Expand state management\n const [isExpand, setIsExpand] = useControlledState(defaultExpand, {\n onChange: onExpandChange,\n value: expand,\n });\n\n // Use refs for animation timeouts to avoid memory leaks\n const animationTimeoutRef = useRef<any>(undefined);\n const collapseTimeoutRef = useRef<any>(undefined);\n\n // Compute default expanded width - memoize to avoid recalculation\n const computedDefaultExpandedWidth = useMemo(\n () => defaultWidth || DEFAULT_EXPANDED_WIDTH,\n [defaultWidth],\n );\n\n // Initialize state with useReducer for better performance\n const initialState: SideNavState = {\n expandedWidth: width ?? computedDefaultExpandedWidth,\n internalWidth: isExpand ? (width ?? computedDefaultExpandedWidth) : minWidth,\n isAnimating: false,\n isResizing: false,\n renderExpand: isExpand,\n };\n\n const [state, dispatch] = useReducer(sideNavReducer, initialState);\n\n // 计算折叠阈值:展开最小宽度和折叠宽度的中间值\n const collapseThreshold = useMemo(() => {\n return minWidth + (state.expandedWidth - minWidth) / 3;\n }, [minWidth, state.expandedWidth]);\n\n // Toggle expand state with smooth animation\n const toggleExpand = useCallback(() => {\n if (!expandable) return;\n\n // 在动画或拖拽期间阻止新的切换操作,避免状态混乱\n if (state.isAnimating || state.isResizing) return;\n\n // 清除之前的动画\n if (animationTimeoutRef.current) {\n clearTimeout(animationTimeoutRef.current);\n }\n\n // 立即设置动画状态,避免其他 useEffect 干扰\n dispatch({ type: 'START_ANIMATION' });\n setIsExpand(!isExpand);\n\n // 动画完成后重置状态 - 与宽度动画时长一致\n animationTimeoutRef.current = setTimeout(() => {\n dispatch({ type: 'STOP_ANIMATION' });\n }, ANIMATION_DURATION);\n }, [expandable, isExpand, setIsExpand, state.isAnimating, state.isResizing]);\n\n // 用于跟踪上一次的 expand 状态,以检测外部变化\n const prevExpandRef = useRef(isExpand);\n\n // 监听外部 expand prop 变化,触发动画\n useEffect(() => {\n // 检测到 expand 状态变化,且不在拖拽和动画中\n if (prevExpandRef.current !== isExpand && !state.isResizing && !state.isAnimating) {\n if (animationTimeoutRef.current) {\n clearTimeout(animationTimeoutRef.current);\n }\n\n // 立即设置动画状态,避免其他 useEffect 干扰\n dispatch({ type: 'START_ANIMATION' });\n\n animationTimeoutRef.current = setTimeout(() => {\n dispatch({ type: 'STOP_ANIMATION' });\n }, ANIMATION_DURATION);\n\n prevExpandRef.current = isExpand;\n }\n }, [isExpand, state.isResizing, state.isAnimating]);\n\n // 处理展开/折叠状态变化时的宽度动画和内容切换时机\n useEffect(() => {\n if (state.isAnimating) {\n // 使用 requestAnimationFrame 确保动画平滑\n const rafId = requestAnimationFrame(() => {\n if (isExpand) {\n // 展开动画:立即切换内容(先切换内容,再开始宽度动画)\n dispatch({ payload: state.expandedWidth, type: 'ANIMATE_EXPAND' });\n } else {\n // 折叠动画:延迟切换内容,在动画接近结束时才切换(300ms,略早于动画结束)\n dispatch({ payload: minWidth, type: 'ANIMATE_COLLAPSE' });\n\n if (collapseTimeoutRef.current) {\n clearTimeout(collapseTimeoutRef.current);\n }\n collapseTimeoutRef.current = setTimeout(() => {\n dispatch({ payload: false, type: 'SET_RENDER_EXPAND' });\n }, COLLAPSE_ANIMATION_DELAY);\n }\n });\n\n return () => {\n cancelAnimationFrame(rafId);\n };\n }\n }, [isExpand, state.isAnimating, minWidth, state.expandedWidth]);\n\n // 同步非动画期间的 renderExpand 状态(如拖拽)\n // 使用 ref 追踪上一次的 isResizing 状态,只在拖拽结束时同步\n const prevIsResizingRef = useRef(state.isResizing);\n useEffect(() => {\n const wasResizing = prevIsResizingRef.current;\n prevIsResizingRef.current = state.isResizing;\n\n // 只在拖拽刚结束时同步 renderExpand,避免干扰正常的展开/折叠动画\n if (wasResizing && !state.isResizing && !state.isAnimating) {\n dispatch({ payload: isExpand, type: 'SET_RENDER_EXPAND' });\n }\n }, [isExpand, state.isAnimating, state.isResizing]);\n\n // 处理外部 width prop 变化\n // width 表示展开时的宽度,实际显示宽度根据 isExpand 状态决定\n useEffect(() => {\n if (width !== undefined && !state.isResizing && !state.isAnimating) {\n // 更新展开宽度记录\n dispatch({ payload: width, type: 'SET_EXPANDED_WIDTH' });\n // 根据当前状态设置实际宽度\n if (isExpand) {\n dispatch({ payload: width, type: 'SET_WIDTH' });\n }\n // 如果是折叠状态,保持 minWidth,不改变 internalWidth\n }\n }, [width, state.isResizing, state.isAnimating, isExpand]);\n\n // 计算当前的 body 内容 - 使用 renderExpand\n const currentBody = useMemo(() => {\n return body(state.renderExpand);\n }, [body, state.renderExpand]);\n\n // 计算当前的 header(支持函数和静态值)- 使用 renderExpand\n const currentHeader = useMemo(() => {\n return typeof header === 'function' ? header(state.renderExpand) : header;\n }, [header, state.renderExpand]);\n\n // 计算当前的 footer(支持函数和静态值)- 使用 renderExpand\n const currentFooter = useMemo(() => {\n return typeof footer === 'function' ? footer(state.renderExpand) : footer;\n }, [footer, state.renderExpand]);\n\n // Handle resize - memoize to prevent recreating on every render\n const handleResize: ResizeCallback = useCallback(\n (_, __, ref, delta) => {\n const currentWidth = ref.offsetWidth;\n dispatch({ payload: currentWidth, type: 'SET_WIDTH' });\n\n onWidthDragging?.(delta, currentWidth);\n },\n [onWidthDragging],\n );\n\n const handleResizeStart = useCallback(() => {\n dispatch({ type: 'START_RESIZE' });\n }, []);\n\n const handleResizeStop: ResizeCallback = useCallback(\n (_, __, ref, delta) => {\n dispatch({ type: 'STOP_RESIZE' });\n\n const currentWidth = ref.offsetWidth;\n\n // 清除之前的动画\n if (animationTimeoutRef.current) {\n clearTimeout(animationTimeoutRef.current);\n }\n\n // 根据拖拽后的宽度决定是折叠还是展开\n if (expandable) {\n const shouldCollapse = currentWidth <= minWidth || currentWidth < collapseThreshold;\n const shouldExpand =\n !isExpand && currentWidth > minWidth && currentWidth >= collapseThreshold;\n\n if (shouldCollapse || shouldExpand) {\n // 立即设置动画状态\n dispatch({ type: 'START_ANIMATION' });\n\n if (shouldCollapse) {\n setIsExpand(false);\n dispatch({ payload: minWidth, type: 'SET_WIDTH' });\n } else {\n setIsExpand(true);\n dispatch({ payload: currentWidth, type: 'SET_EXPANDED_WIDTH' });\n dispatch({ payload: currentWidth, type: 'SET_WIDTH' });\n }\n\n animationTimeoutRef.current = setTimeout(() => {\n dispatch({ type: 'STOP_ANIMATION' });\n }, ANIMATION_DURATION);\n } else if (isExpand) {\n // 展开状态下正常拖拽,记住宽度\n dispatch({ payload: currentWidth, type: 'SET_EXPANDED_WIDTH' });\n dispatch({ payload: currentWidth, type: 'SET_WIDTH' });\n }\n } else {\n // 如果不可折叠,仅更新宽度\n dispatch({ payload: currentWidth, type: 'SET_EXPANDED_WIDTH' });\n dispatch({ payload: currentWidth, type: 'SET_WIDTH' });\n }\n\n onWidthChange?.(delta, currentWidth);\n },\n [expandable, minWidth, collapseThreshold, isExpand, onWidthChange, setIsExpand],\n );\n\n // Arrow icon based on placement and expand state\n const ArrowIcon = useMemo(() => {\n if (placement === 'left') {\n // 左侧:展开时箭头向左(折叠方向),折叠时箭头向右(展开方向)\n return ChevronLeft;\n }\n // 右侧:展开时箭头向右(折叠方向),折叠时箭头向左(展开方向)\n return ChevronRight;\n }, [placement]);\n\n // Memoize handle styles to prevent recreation\n const handleRootStyle = useMemo<CSSProperties>(\n () => ({\n display: 'flex',\n opacity: !isExpand && showHandleWhenCollapsed ? 1 : isHovering ? 1 : 0,\n transition: 'opacity 0.25s ease',\n }),\n [isExpand, showHandleWhenCollapsed, isHovering],\n );\n\n const handleCenterStyle = useMemo<CSSProperties>(\n () => ({\n ...customStyles?.handle,\n cursor: 'pointer',\n }),\n [customStyles?.handle],\n );\n\n const handleIconWrapperStyle = useMemo<CSSProperties>(\n () => ({\n marginLeft: placement === 'right' ? 4 : 0,\n marginRight: placement === 'left' ? 4 : 0,\n transform: isExpand ? 'rotate(0deg)' : 'rotate(180deg)',\n transition: `transform ${COLLAPSE_ANIMATION_DELAY} ease`,\n }),\n [placement, isExpand],\n );\n\n // Toggle handle with smooth transitions\n const handle = useMemo(\n () =>\n showHandle &&\n expandable && (\n <div\n style={handleRootStyle}\n className={cx(\n styles.toggleRoot,\n placement === 'left' ? styles.toggleLeft : styles.toggleRight,\n )}\n >\n <Center className={classNames?.handle} style={handleCenterStyle} onClick={toggleExpand}>\n <div style={handleIconWrapperStyle}>\n <Icon className={styles.handlerIcon} icon={ArrowIcon} size={16} />\n </div>\n </Center>\n </div>\n ),\n [\n showHandle,\n expandable,\n styles.toggleRoot,\n styles.toggleLeft,\n styles.toggleRight,\n styles.handlerIcon,\n placement,\n handleRootStyle,\n classNames?.handle,\n toggleExpand,\n handleCenterStyle,\n handleIconWrapperStyle,\n ArrowIcon,\n cx,\n ],\n );\n\n // Size configuration - 使用内部宽度状态\n const sizeConfig = useMemo(() => {\n return {\n maxWidth: maxWidth,\n minWidth: minWidth,\n size: { height: '100%', width: state.internalWidth },\n };\n }, [state.internalWidth, minWidth, maxWidth]);\n\n // Resize enable configuration - 始终允许拖拽\n const resizeEnable = useMemo(() => {\n if (!resizable) {\n return RESIZE_DISABLED;\n }\n return {\n bottom: false,\n bottomLeft: false,\n bottomRight: false,\n left: placement === 'right',\n right: placement === 'left',\n top: false,\n topLeft: false,\n topRight: false,\n };\n }, [resizable, placement]);\n\n // Memoize handle classes to prevent recreation\n const handleClasses = useMemo(\n () => ({\n [placement === 'left' ? 'right' : 'left']: cx(\n styles.resizeHandle,\n showHandleHighlight && styles.resizeHandleHighlight,\n placement === 'left' ? styles.resizeHandleLeft : styles.resizeHandleRight,\n ),\n }),\n [placement, styles, showHandleHighlight, cx],\n );\n\n // Memoize container style to prevent recreation\n const containerStyle = useMemo<CSSProperties>(\n () => ({\n ...customStyles?.container,\n ...rest.style,\n // 拖拽时不要动画,点击 handle 时有流畅的弹性动画\n transition: state.isResizing\n ? 'none'\n : state.isAnimating\n ? `width ${ANIMATION_DURATION}ms cubic-bezier(0.22, 1, 0.36, 1)`\n : 'none',\n }),\n [customStyles?.container, rest.style, state.isResizing, state.isAnimating],\n );\n\n // Memoize class names\n const containerClassName = useMemo(\n () => cx(styles.container, classNames?.container, className),\n [cx, styles.container, classNames?.container, className],\n );\n\n const contentClassName = useMemo(\n () =>\n cx(\n showBorder ? styles.contentContainer : styles.contentContainerNoBorder,\n styles.menuOverride,\n classNames?.content,\n ),\n [\n cx,\n styles.contentContainer,\n styles.contentContainerNoBorder,\n styles.menuOverride,\n classNames?.content,\n showBorder,\n ],\n );\n\n const headerClassName = useMemo(\n () => cx(styles.header, classNames?.header),\n [cx, styles.header, classNames?.header],\n );\n\n const bodyClassName = useMemo(\n () => cx(styles.body, classNames?.body),\n [cx, styles.body, classNames?.body],\n );\n\n const footerClassName = useMemo(\n () => cx(styles.footer, classNames?.footer),\n [cx, styles.footer, classNames?.footer],\n );\n\n // Cleanup timeouts on unmount\n useEffect(() => {\n return () => {\n if (animationTimeoutRef.current) {\n clearTimeout(animationTimeoutRef.current);\n }\n if (collapseTimeoutRef.current) {\n clearTimeout(collapseTimeoutRef.current);\n }\n };\n }, []);\n\n return (\n <aside ref={ref}>\n <Resizable\n {...sizeConfig}\n className={containerClassName}\n enable={resizeEnable}\n handleClasses={handleClasses}\n style={containerStyle}\n onResize={handleResize}\n onResizeStart={handleResizeStart}\n onResizeStop={handleResizeStop}\n >\n {handle}\n <Flexbox\n className={contentClassName}\n style={{\n ...cssVariables,\n ...customStyles?.content,\n }}\n >\n {currentHeader && (\n <div className={headerClassName} style={customStyles?.header}>\n {currentHeader}\n </div>\n )}\n <div className={bodyClassName} style={customStyles?.body}>\n {currentBody}\n </div>\n {currentFooter && (\n <div className={footerClassName} style={customStyles?.footer}>\n {currentFooter}\n </div>\n )}\n </Flexbox>\n </Resizable>\n </aside>\n );\n },\n);\n\nDraggableSideNav.displayName = 'DraggableSideNav';\n\nexport default DraggableSideNav;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DraggableSideNav.mjs","names":["Flexbox"],"sources":["../../src/DraggableSideNav/DraggableSideNav.tsx"],"sourcesContent":["'use client';\n\nimport { useHover } from 'ahooks';\nimport { cx } from 'antd-style';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\nimport { Resizable, type ResizeCallback } from 're-resizable';\nimport {\n type CSSProperties,\n memo,\n useCallback,\n useEffect,\n useMemo,\n useReducer,\n useRef,\n} from 'react';\nimport useControlledState from 'use-merge-value';\n\nimport { Center, Flexbox } from '@/Flex';\nimport Icon from '@/Icon';\n\nimport { styles } from './style';\nimport type { DraggableSideNavProps } from './type';\n\nconst DEFAULT_MIN_WIDTH = 64; // 最小宽度即折叠宽度\nconst DEFAULT_EXPAND = true;\nconst DEFAULT_EXPANDED_WIDTH = 280;\nconst ANIMATION_DURATION = 300;\nconst COLLAPSE_ANIMATION_DELAY = 200;\n\n// Pre-define static objects to avoid recreating\nconst RESIZE_DISABLED = {\n bottom: false,\n bottomLeft: false,\n bottomRight: false,\n left: false,\n right: false,\n top: false,\n topLeft: false,\n topRight: false,\n};\n\n// State reducer for better state management\ninterface SideNavState {\n expandedWidth: number;\n internalWidth: number;\n isAnimating: boolean;\n isResizing: boolean;\n renderExpand: boolean;\n}\n\ntype SideNavAction =\n | { type: 'START_RESIZE' }\n | { type: 'STOP_RESIZE' }\n | { type: 'START_ANIMATION' }\n | { type: 'STOP_ANIMATION' }\n | { payload: number; type: 'SET_WIDTH' }\n | { payload: number; type: 'SET_EXPANDED_WIDTH' }\n | { payload: boolean; type: 'SET_RENDER_EXPAND' }\n | { payload: number; type: 'ANIMATE_EXPAND' }\n | { payload: number; type: 'ANIMATE_COLLAPSE' };\n\nfunction sideNavReducer(state: SideNavState, action: SideNavAction): SideNavState {\n switch (action.type) {\n case 'START_RESIZE': {\n return { ...state, isResizing: true };\n }\n case 'STOP_RESIZE': {\n return { ...state, isResizing: false };\n }\n case 'START_ANIMATION': {\n return { ...state, isAnimating: true };\n }\n case 'STOP_ANIMATION': {\n return { ...state, isAnimating: false };\n }\n case 'SET_WIDTH': {\n return { ...state, internalWidth: action.payload };\n }\n case 'SET_EXPANDED_WIDTH': {\n return { ...state, expandedWidth: action.payload };\n }\n case 'SET_RENDER_EXPAND': {\n return { ...state, renderExpand: action.payload };\n }\n case 'ANIMATE_EXPAND': {\n return { ...state, internalWidth: action.payload, renderExpand: true };\n }\n case 'ANIMATE_COLLAPSE': {\n return { ...state, internalWidth: action.payload };\n }\n default: {\n return state;\n }\n }\n}\n\nconst DraggableSideNav = memo<DraggableSideNavProps>(\n ({\n body,\n className,\n classNames,\n defaultExpand = DEFAULT_EXPAND,\n defaultWidth,\n expand,\n expandable = true,\n footer,\n header,\n maxWidth,\n minWidth = DEFAULT_MIN_WIDTH,\n onExpandChange,\n onWidthChange,\n onWidthDragging,\n placement = 'left',\n resizable = true,\n showBorder = true,\n showHandle = true,\n showHandleWhenCollapsed = false,\n showHandleHighlight = false,\n backgroundColor,\n styles: customStyles,\n width,\n ...rest\n }) => {\n const cssVariables = useMemo<Record<string, string>>(\n () => ({\n '--draggable-side-nav-bg': backgroundColor || '',\n }),\n [backgroundColor],\n );\n const ref = useRef<HTMLDivElement>(null);\n const isHovering = useHover(ref);\n\n // Expand state management\n const [isExpand, setIsExpand] = useControlledState(defaultExpand, {\n onChange: onExpandChange,\n value: expand,\n });\n\n // Use refs for animation timeouts to avoid memory leaks\n const animationTimeoutRef = useRef<any>(undefined);\n const collapseTimeoutRef = useRef<any>(undefined);\n\n // Compute default expanded width - memoize to avoid recalculation\n const computedDefaultExpandedWidth = useMemo(\n () => defaultWidth || DEFAULT_EXPANDED_WIDTH,\n [defaultWidth],\n );\n\n // Initialize state with useReducer for better performance\n const initialState: SideNavState = {\n expandedWidth: width ?? computedDefaultExpandedWidth,\n internalWidth: isExpand ? (width ?? computedDefaultExpandedWidth) : minWidth,\n isAnimating: false,\n isResizing: false,\n renderExpand: isExpand,\n };\n\n const [state, dispatch] = useReducer(sideNavReducer, initialState);\n\n // 计算折叠阈值:展开最小宽度和折叠宽度的中间值\n const collapseThreshold = useMemo(() => {\n return minWidth + (state.expandedWidth - minWidth) / 3;\n }, [minWidth, state.expandedWidth]);\n\n // Toggle expand state with smooth animation\n const toggleExpand = useCallback(() => {\n if (!expandable) return;\n\n // 在动画或拖拽期间阻止新的切换操作,避免状态混乱\n if (state.isAnimating || state.isResizing) return;\n\n // 清除之前的动画\n if (animationTimeoutRef.current) {\n clearTimeout(animationTimeoutRef.current);\n }\n\n // 立即设置动画状态,避免其他 useEffect 干扰\n dispatch({ type: 'START_ANIMATION' });\n setIsExpand(!isExpand);\n\n // 动画完成后重置状态 - 与宽度动画时长一致\n animationTimeoutRef.current = setTimeout(() => {\n dispatch({ type: 'STOP_ANIMATION' });\n }, ANIMATION_DURATION);\n }, [expandable, isExpand, setIsExpand, state.isAnimating, state.isResizing]);\n\n // 用于跟踪上一次的 expand 状态,以检测外部变化\n const prevExpandRef = useRef(isExpand);\n\n // 监听外部 expand prop 变化,触发动画\n useEffect(() => {\n // 检测到 expand 状态变化,且不在拖拽和动画中\n if (prevExpandRef.current !== isExpand && !state.isResizing && !state.isAnimating) {\n if (animationTimeoutRef.current) {\n clearTimeout(animationTimeoutRef.current);\n }\n\n // 立即设置动画状态,避免其他 useEffect 干扰\n dispatch({ type: 'START_ANIMATION' });\n\n animationTimeoutRef.current = setTimeout(() => {\n dispatch({ type: 'STOP_ANIMATION' });\n }, ANIMATION_DURATION);\n\n prevExpandRef.current = isExpand;\n }\n }, [isExpand, state.isResizing, state.isAnimating]);\n\n // 处理展开/折叠状态变化时的宽度动画和内容切换时机\n useEffect(() => {\n if (state.isAnimating) {\n // 使用 requestAnimationFrame 确保动画平滑\n const rafId = requestAnimationFrame(() => {\n if (isExpand) {\n // 展开动画:立即切换内容(先切换内容,再开始宽度动画)\n dispatch({ payload: state.expandedWidth, type: 'ANIMATE_EXPAND' });\n } else {\n // 折叠动画:延迟切换内容,在动画接近结束时才切换(300ms,略早于动画结束)\n dispatch({ payload: minWidth, type: 'ANIMATE_COLLAPSE' });\n\n if (collapseTimeoutRef.current) {\n clearTimeout(collapseTimeoutRef.current);\n }\n collapseTimeoutRef.current = setTimeout(() => {\n dispatch({ payload: false, type: 'SET_RENDER_EXPAND' });\n }, COLLAPSE_ANIMATION_DELAY);\n }\n });\n\n return () => {\n cancelAnimationFrame(rafId);\n };\n }\n }, [isExpand, state.isAnimating, minWidth, state.expandedWidth]);\n\n // 同步非动画期间的 renderExpand 状态(如拖拽)\n // 使用 ref 追踪上一次的 isResizing 状态,只在拖拽结束时同步\n const prevIsResizingRef = useRef(state.isResizing);\n useEffect(() => {\n const wasResizing = prevIsResizingRef.current;\n prevIsResizingRef.current = state.isResizing;\n\n // 只在拖拽刚结束时同步 renderExpand,避免干扰正常的展开/折叠动画\n if (wasResizing && !state.isResizing && !state.isAnimating) {\n dispatch({ payload: isExpand, type: 'SET_RENDER_EXPAND' });\n }\n }, [isExpand, state.isAnimating, state.isResizing]);\n\n // 处理外部 width prop 变化\n // width 表示展开时的宽度,实际显示宽度根据 isExpand 状态决定\n useEffect(() => {\n if (width !== undefined && !state.isResizing && !state.isAnimating) {\n // 更新展开宽度记录\n dispatch({ payload: width, type: 'SET_EXPANDED_WIDTH' });\n // 根据当前状态设置实际宽度\n if (isExpand) {\n dispatch({ payload: width, type: 'SET_WIDTH' });\n }\n // 如果是折叠状态,保持 minWidth,不改变 internalWidth\n }\n }, [width, state.isResizing, state.isAnimating, isExpand]);\n\n // 计算当前的 body 内容 - 使用 renderExpand\n const currentBody = useMemo(() => {\n return body(state.renderExpand);\n }, [body, state.renderExpand]);\n\n // 计算当前的 header(支持函数和静态值)- 使用 renderExpand\n const currentHeader = useMemo(() => {\n return typeof header === 'function' ? header(state.renderExpand) : header;\n }, [header, state.renderExpand]);\n\n // 计算当前的 footer(支持函数和静态值)- 使用 renderExpand\n const currentFooter = useMemo(() => {\n return typeof footer === 'function' ? footer(state.renderExpand) : footer;\n }, [footer, state.renderExpand]);\n\n // Handle resize - memoize to prevent recreating on every render\n const handleResize: ResizeCallback = useCallback(\n (_, __, ref, delta) => {\n const currentWidth = ref.offsetWidth;\n dispatch({ payload: currentWidth, type: 'SET_WIDTH' });\n\n onWidthDragging?.(delta, currentWidth);\n },\n [onWidthDragging],\n );\n\n const handleResizeStart = useCallback(() => {\n dispatch({ type: 'START_RESIZE' });\n }, []);\n\n const handleResizeStop: ResizeCallback = useCallback(\n (_, __, ref, delta) => {\n dispatch({ type: 'STOP_RESIZE' });\n\n const currentWidth = ref.offsetWidth;\n\n // 清除之前的动画\n if (animationTimeoutRef.current) {\n clearTimeout(animationTimeoutRef.current);\n }\n\n // 根据拖拽后的宽度决定是折叠还是展开\n if (expandable) {\n const shouldCollapse = currentWidth <= minWidth || currentWidth < collapseThreshold;\n const shouldExpand =\n !isExpand && currentWidth > minWidth && currentWidth >= collapseThreshold;\n\n if (shouldCollapse || shouldExpand) {\n // 立即设置动画状态\n dispatch({ type: 'START_ANIMATION' });\n\n if (shouldCollapse) {\n setIsExpand(false);\n dispatch({ payload: minWidth, type: 'SET_WIDTH' });\n } else {\n setIsExpand(true);\n dispatch({ payload: currentWidth, type: 'SET_EXPANDED_WIDTH' });\n dispatch({ payload: currentWidth, type: 'SET_WIDTH' });\n }\n\n animationTimeoutRef.current = setTimeout(() => {\n dispatch({ type: 'STOP_ANIMATION' });\n }, ANIMATION_DURATION);\n } else if (isExpand) {\n // 展开状态下正常拖拽,记住宽度\n dispatch({ payload: currentWidth, type: 'SET_EXPANDED_WIDTH' });\n dispatch({ payload: currentWidth, type: 'SET_WIDTH' });\n }\n } else {\n // 如果不可折叠,仅更新宽度\n dispatch({ payload: currentWidth, type: 'SET_EXPANDED_WIDTH' });\n dispatch({ payload: currentWidth, type: 'SET_WIDTH' });\n }\n\n onWidthChange?.(delta, currentWidth);\n },\n [expandable, minWidth, collapseThreshold, isExpand, onWidthChange, setIsExpand],\n );\n\n // Arrow icon based on placement and expand state\n const ArrowIcon = useMemo(() => {\n if (placement === 'left') {\n // 左侧:展开时箭头向左(折叠方向),折叠时箭头向右(展开方向)\n return ChevronLeft;\n }\n // 右侧:展开时箭头向右(折叠方向),折叠时箭头向左(展开方向)\n return ChevronRight;\n }, [placement]);\n\n // Memoize handle styles to prevent recreation\n const handleRootStyle = useMemo<CSSProperties>(\n () => ({\n display: 'flex',\n opacity: !isExpand && showHandleWhenCollapsed ? 1 : isHovering ? 1 : 0,\n transition: 'opacity 0.25s ease',\n }),\n [isExpand, showHandleWhenCollapsed, isHovering],\n );\n\n const handleCenterStyle = useMemo<CSSProperties>(\n () => ({\n ...customStyles?.handle,\n cursor: 'pointer',\n }),\n [customStyles?.handle],\n );\n\n const handleIconWrapperStyle = useMemo<CSSProperties>(\n () => ({\n marginLeft: placement === 'right' ? 4 : 0,\n marginRight: placement === 'left' ? 4 : 0,\n transform: isExpand ? 'rotate(0deg)' : 'rotate(180deg)',\n transition: `transform ${COLLAPSE_ANIMATION_DELAY} ease`,\n }),\n [placement, isExpand],\n );\n\n // Toggle handle with smooth transitions\n const handle = useMemo(\n () =>\n showHandle &&\n expandable && (\n <div\n style={handleRootStyle}\n className={cx(\n styles.toggleRoot,\n placement === 'left' ? styles.toggleLeft : styles.toggleRight,\n )}\n >\n <Center className={classNames?.handle} style={handleCenterStyle} onClick={toggleExpand}>\n <div style={handleIconWrapperStyle}>\n <Icon className={styles.handlerIcon} icon={ArrowIcon} size={16} />\n </div>\n </Center>\n </div>\n ),\n [\n showHandle,\n expandable,\n styles.toggleRoot,\n styles.toggleLeft,\n styles.toggleRight,\n styles.handlerIcon,\n placement,\n handleRootStyle,\n classNames?.handle,\n toggleExpand,\n handleCenterStyle,\n handleIconWrapperStyle,\n ArrowIcon,\n cx,\n ],\n );\n\n // Size configuration - 使用内部宽度状态\n const sizeConfig = useMemo(() => {\n return {\n maxWidth: maxWidth,\n minWidth: minWidth,\n size: { height: '100%', width: state.internalWidth },\n };\n }, [state.internalWidth, minWidth, maxWidth]);\n\n // Resize enable configuration - 始终允许拖拽\n const resizeEnable = useMemo(() => {\n if (!resizable) {\n return RESIZE_DISABLED;\n }\n return {\n bottom: false,\n bottomLeft: false,\n bottomRight: false,\n left: placement === 'right',\n right: placement === 'left',\n top: false,\n topLeft: false,\n topRight: false,\n };\n }, [resizable, placement]);\n\n // Memoize handle classes to prevent recreation\n const handleClasses = useMemo(\n () => ({\n [placement === 'left' ? 'right' : 'left']: cx(\n styles.resizeHandle,\n showHandleHighlight && styles.resizeHandleHighlight,\n placement === 'left' ? styles.resizeHandleLeft : styles.resizeHandleRight,\n ),\n }),\n [placement, styles, showHandleHighlight, cx],\n );\n\n // Memoize container style to prevent recreation\n const containerStyle = useMemo<CSSProperties>(\n () => ({\n ...customStyles?.container,\n ...rest.style,\n // 拖拽时不要动画,点击 handle 时有流畅的弹性动画\n transition: state.isResizing\n ? 'none'\n : state.isAnimating\n ? `width ${ANIMATION_DURATION}ms cubic-bezier(0.22, 1, 0.36, 1)`\n : 'none',\n }),\n [customStyles?.container, rest.style, state.isResizing, state.isAnimating],\n );\n\n // Memoize class names\n const containerClassName = useMemo(\n () => cx(styles.container, classNames?.container, className),\n [cx, styles.container, classNames?.container, className],\n );\n\n const contentClassName = useMemo(\n () =>\n cx(\n showBorder ? styles.contentContainer : styles.contentContainerNoBorder,\n styles.menuOverride,\n classNames?.content,\n ),\n [\n cx,\n styles.contentContainer,\n styles.contentContainerNoBorder,\n styles.menuOverride,\n classNames?.content,\n showBorder,\n ],\n );\n\n const headerClassName = useMemo(\n () => cx(styles.header, classNames?.header),\n [cx, styles.header, classNames?.header],\n );\n\n const bodyClassName = useMemo(\n () => cx(styles.body, classNames?.body),\n [cx, styles.body, classNames?.body],\n );\n\n const footerClassName = useMemo(\n () => cx(styles.footer, classNames?.footer),\n [cx, styles.footer, classNames?.footer],\n );\n\n // Cleanup timeouts on unmount\n useEffect(() => {\n return () => {\n if (animationTimeoutRef.current) {\n clearTimeout(animationTimeoutRef.current);\n }\n if (collapseTimeoutRef.current) {\n clearTimeout(collapseTimeoutRef.current);\n }\n };\n }, []);\n\n return (\n <aside ref={ref}>\n <Resizable\n {...sizeConfig}\n className={containerClassName}\n enable={resizeEnable}\n handleClasses={handleClasses}\n style={containerStyle}\n onResize={handleResize}\n onResizeStart={handleResizeStart}\n onResizeStop={handleResizeStop}\n >\n {handle}\n <Flexbox\n className={contentClassName}\n style={{\n ...cssVariables,\n ...customStyles?.content,\n }}\n >\n {currentHeader && (\n <div className={headerClassName} style={customStyles?.header}>\n {currentHeader}\n </div>\n )}\n <div className={bodyClassName} style={customStyles?.body}>\n {currentBody}\n </div>\n {currentFooter && (\n <div className={footerClassName} style={customStyles?.footer}>\n {currentFooter}\n </div>\n )}\n </Flexbox>\n </Resizable>\n </aside>\n );\n },\n);\n\nDraggableSideNav.displayName = 'DraggableSideNav';\n\nexport default DraggableSideNav;\n"],"mappings":"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"}
|
|
@@ -5,12 +5,12 @@ import ControlInput from "./ControlInput.mjs";
|
|
|
5
5
|
import { memo, useMemo } from "react";
|
|
6
6
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { cx } from "antd-style";
|
|
8
|
-
import
|
|
8
|
+
import useControlledState from "use-merge-value";
|
|
9
9
|
import { Edit3 } from "lucide-react";
|
|
10
10
|
import { useHotkeys } from "react-hotkeys-hook";
|
|
11
11
|
//#region src/EditableText/EditableText.tsx
|
|
12
12
|
const EditableText = memo(({ value, showEditIcon = true, onChange, editing, onEditingChange, onChangeEnd, onFocus, onBlur, className, inputProps, onValueChanging, gap = 8, style, size = "small", styles, classNames, variant = "borderless", ...rest }) => {
|
|
13
|
-
const [edited, setEdited] =
|
|
13
|
+
const [edited, setEdited] = useControlledState(false, {
|
|
14
14
|
onChange: onEditingChange,
|
|
15
15
|
value: editing
|
|
16
16
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditableText.mjs","names":["
|
|
1
|
+
{"version":3,"file":"EditableText.mjs","names":["Flexbox"],"sources":["../../src/EditableText/EditableText.tsx"],"sourcesContent":["'use client';\n\nimport { cx } from 'antd-style';\nimport { Edit3 } from 'lucide-react';\nimport { memo, useMemo } from 'react';\nimport { useHotkeys } from 'react-hotkeys-hook';\nimport useControlledState from 'use-merge-value';\n\nimport ActionIcon from '@/ActionIcon';\nimport { Flexbox } from '@/Flex';\n\nimport ControlInput from './ControlInput';\nimport type { EditableTextProps } from './type';\n\nconst EditableText = memo<EditableTextProps>(\n ({\n value,\n showEditIcon = true,\n onChange,\n editing,\n onEditingChange,\n onChangeEnd,\n onFocus,\n onBlur,\n className,\n inputProps,\n onValueChanging,\n gap = 8,\n style,\n size = 'small',\n styles,\n classNames,\n variant = 'borderless',\n ...rest\n }) => {\n const [edited, setEdited] = useControlledState(false, {\n onChange: onEditingChange,\n value: editing,\n });\n\n useHotkeys(\n 'esc',\n () => {\n setEdited(false);\n },\n {\n enableOnFormTags: true,\n enabled: edited,\n preventDefault: true,\n },\n );\n\n const height = useMemo(() => {\n if (!size) return 32;\n switch (size) {\n case 'large': {\n return 40;\n }\n case 'middle': {\n return 32;\n }\n case 'small': {\n return 24;\n }\n }\n }, [size]);\n\n const input = (\n <ControlInput\n className={cx(className, classNames?.input)}\n size={size}\n value={value as string}\n variant={variant}\n style={{\n height,\n ...style,\n ...styles?.input,\n }}\n onBlur={onBlur}\n onChange={onChange}\n onFocus={onFocus}\n onValueChanging={onValueChanging}\n onChangeEnd={(v) => {\n onChangeEnd?.(v);\n setEdited(false);\n }}\n {...inputProps}\n />\n );\n\n const content = (\n <>\n <span>{value}</span>\n {showEditIcon && (\n <ActionIcon\n icon={Edit3}\n size=\"small\"\n title={'Edit'}\n onClick={() => {\n setEdited(!edited);\n }}\n />\n )}\n </>\n );\n\n return (\n <Flexbox\n horizontal\n align={'center'}\n className={cx(className, classNames?.container)}\n gap={gap}\n style={{\n height,\n width: '100%',\n ...style,\n ...styles?.container,\n }}\n {...rest}\n >\n {edited ? input : content}\n </Flexbox>\n );\n },\n);\n\nEditableText.displayName = 'EditableText';\n\nexport default EditableText;\n"],"mappings":";;;;;;;;;;;AAcA,MAAM,eAAe,MAClB,EACC,OACA,eAAe,MACf,UACA,SACA,iBACA,aACA,SACA,QACA,WACA,YACA,iBACA,MAAM,GACN,OACA,OAAO,SACP,QACA,YACA,UAAU,cACV,GAAG,WACC;CACJ,MAAM,CAAC,QAAQ,aAAa,mBAAmB,OAAO;EACpD,UAAU;EACV,OAAO;EACR,CAAC;AAEF,YACE,aACM;AACJ,YAAU,MAAM;IAElB;EACE,kBAAkB;EAClB,SAAS;EACT,gBAAgB;EACjB,CACF;CAED,MAAM,SAAS,cAAc;AAC3B,MAAI,CAAC,KAAM,QAAO;AAClB,UAAQ,MAAR;GACE,KAAK,QACH,QAAO;GAET,KAAK,SACH,QAAO;GAET,KAAK,QACH,QAAO;;IAGV,CAAC,KAAK,CAAC;CAEV,MAAM,QACJ,oBAAC,cAAD;EACE,WAAW,GAAG,WAAW,YAAY,MAAM;EACrC;EACC;EACE;EACT,OAAO;GACL;GACA,GAAG;GACH,GAAG,QAAQ;GACZ;EACO;EACE;EACD;EACQ;EACjB,cAAc,MAAM;AAClB,iBAAc,EAAE;AAChB,aAAU,MAAM;;EAElB,GAAI;EACJ,CAAA;CAGJ,MAAM,UACJ,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,QAAD,EAAA,UAAO,OAAa,CAAA,EACnB,gBACC,oBAAC,YAAD;EACE,MAAM;EACN,MAAK;EACL,OAAO;EACP,eAAe;AACb,aAAU,CAAC,OAAO;;EAEpB,CAAA,CAEH,EAAA,CAAA;AAGL,QACE,oBAACA,mBAAD;EACE,YAAA;EACA,OAAO;EACP,WAAW,GAAG,WAAW,YAAY,UAAU;EAC1C;EACL,OAAO;GACL;GACA,OAAO;GACP,GAAG;GACH,GAAG,QAAQ;GACZ;EACD,GAAI;YAEH,SAAS,QAAQ;EACV,CAAA;EAGf;AAED,aAAa,cAAc"}
|
|
@@ -13,7 +13,7 @@ import { styles } from "./style.mjs";
|
|
|
13
13
|
import { memo, useMemo, useRef, useState } from "react";
|
|
14
14
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
15
15
|
import { cx, useTheme } from "antd-style";
|
|
16
|
-
import
|
|
16
|
+
import useControlledState from "use-merge-value";
|
|
17
17
|
import { SmileIcon, TrashIcon, UploadIcon } from "lucide-react";
|
|
18
18
|
import chroma from "chroma-js";
|
|
19
19
|
import data from "@emoji-mart/data";
|
|
@@ -25,7 +25,7 @@ const DEFAULT_AVATAR = "🤖";
|
|
|
25
25
|
const EmojiPicker = memo(({ value, defaultAvatar = DEFAULT_AVATAR, onChange, locale = "en-US", allowUpload, allowDelete, allowModelAvatar, texts, onDelete, compressSize = 256, customEmojis, className, loading, onUpload, customTabs = [], popupClassName, popupStyle, customRender, open, defaultOpen = false, onOpenChange, popupProps, shape, contentProps, ...rest }) => {
|
|
26
26
|
const ref = useRef(null);
|
|
27
27
|
const { t } = useTranslation(emojiPicker_default);
|
|
28
|
-
const [visible, setVisible] =
|
|
28
|
+
const [visible, setVisible] = useControlledState(defaultOpen, {
|
|
29
29
|
defaultValue: defaultOpen,
|
|
30
30
|
onChange: onOpenChange,
|
|
31
31
|
value: open
|
|
@@ -40,7 +40,7 @@ const EmojiPicker = memo(({ value, defaultAvatar = DEFAULT_AVATAR, onChange, loc
|
|
|
40
40
|
revalidateOnFocus: false,
|
|
41
41
|
revalidateOnMount: false
|
|
42
42
|
});
|
|
43
|
-
const [ava, setAva] =
|
|
43
|
+
const [ava, setAva] = useControlledState(defaultAvatar, {
|
|
44
44
|
defaultValue: defaultAvatar,
|
|
45
45
|
onChange,
|
|
46
46
|
value
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmojiPicker.mjs","names":["emojiPickerMessages","Flexbox"],"sources":["../../src/EmojiPicker/EmojiPicker.tsx"],"sourcesContent":["'use client';\n\nimport data from '@emoji-mart/data';\nimport Picker from '@emoji-mart/react';\nimport { getLobeIconCDN, toc } from '@lobehub/icons';\nimport { cx, useTheme } from 'antd-style';\nimport chroma from 'chroma-js';\nimport { SmileIcon, TrashIcon, UploadIcon } from 'lucide-react';\nimport { memo, useMemo, useRef, useState } from 'react';\nimport useSWR from 'swr';\nimport useMergeState from 'use-merge-value';\n\nimport ActionIcon from '@/ActionIcon';\nimport Avatar from '@/Avatar';\nimport { Flexbox } from '@/Flex';\nimport emojiPickerMessages from '@/i18n/resources/en/emojiPicker';\nimport { useTranslation } from '@/i18n/useTranslation';\nimport Icon from '@/Icon';\nimport Popover from '@/Popover';\nimport Tabs, { type TabsProps } from '@/Tabs';\nimport Tooltip from '@/Tooltip';\n\nimport AvatarUploader from './AvatarUploader';\nimport { styles } from './style';\nimport { type EmojiPickerProps } from './type';\n\nconst DEFAULT_AVATAR = '🤖';\n\nconst EmojiPicker = memo<EmojiPickerProps>(\n ({\n value,\n defaultAvatar = DEFAULT_AVATAR,\n onChange,\n locale = 'en-US',\n allowUpload,\n allowDelete,\n allowModelAvatar,\n texts,\n onDelete,\n compressSize = 256,\n customEmojis,\n className,\n loading,\n onUpload,\n customTabs = [],\n popupClassName,\n popupStyle,\n customRender,\n open,\n defaultOpen = false,\n onOpenChange,\n popupProps,\n shape,\n contentProps,\n ...rest\n }) => {\n const ref = useRef<HTMLDivElement>(null);\n const { t } = useTranslation(emojiPickerMessages);\n const [visible, setVisible] = useMergeState(defaultOpen, {\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n value: open,\n });\n const [tab, setTab] = useState<'emoji' | 'upload'>('emoji');\n\n const theme = useTheme();\n const pickerCssVariables = useMemo<Record<string, string>>(\n () => ({\n '--emoji-picker-rgb-accent': chroma(theme.colorPrimary).rgb().join(', '),\n '--emoji-picker-rgb-background': chroma(theme.colorBgElevated).rgb().join(', '),\n }),\n [theme.colorPrimary, theme.colorBgElevated],\n );\n\n const { data: i18n } = useSWR(\n locale,\n async () => await import(`@emoji-mart/data/i18n/${locale.split('-')[0]}.json`),\n { revalidateOnFocus: false, revalidateOnMount: false },\n );\n\n const [ava, setAva] = useMergeState(defaultAvatar, {\n defaultValue: defaultAvatar,\n onChange,\n value,\n });\n\n const handleAvatarChange = (emoji: string) => {\n setAva(emoji);\n setVisible(false);\n };\n\n // Generate model avatars from @lobehub/icons when allowModelAvatar is enabled\n const mergedCustomEmojis = useMemo(() => {\n if (!allowModelAvatar) return customEmojis;\n\n const modelIcons = toc.filter((item) => item.group !== 'application');\n\n const modelEmojisCategory = {\n id: 'models',\n name: 'Models / Providers',\n emojis: modelIcons.map((item) => ({\n id: item.id.toLowerCase(),\n name: item.title,\n keywords: [item.title, item.id, item.fullTitle],\n skins: [{ src: getLobeIconCDN(item.id, { format: 'avatar', cdn: 'aliyun' }) }],\n })),\n };\n\n return customEmojis ? [modelEmojisCategory, ...customEmojis] : [modelEmojisCategory];\n }, [allowModelAvatar, customEmojis]);\n\n const emojiText = texts?.emoji ?? t('emojiPicker.emoji');\n const uploadText = texts?.upload ?? t('emojiPicker.upload');\n const deleteText = texts?.delete ?? t('emojiPicker.delete');\n\n const hideEmojiTab = typeof allowUpload === 'object' && allowUpload?.enableEmoji === false;\n\n const items: TabsProps['items'] = [\n !hideEmojiTab && {\n key: 'emoji',\n label: (\n <Tooltip title={emojiText}>\n <Icon icon={SmileIcon} size={{ size: 20, strokeWidth: 2.5 }} />\n </Tooltip>\n ),\n },\n allowUpload && {\n key: 'upload',\n label: (\n <Tooltip title={uploadText}>\n <Icon icon={UploadIcon} size={{ size: 20, strokeWidth: 2.5 }} />\n </Tooltip>\n ),\n },\n ...customTabs.map((tab) => ({ key: tab.value, label: tab.label })),\n ].filter(Boolean) as TabsProps['items'];\n\n const showTabs = allowDelete || (items && items.length > 1);\n\n const content = (\n <Flexbox\n className={cx(styles.picker, popupClassName)}\n ref={ref}\n style={{\n minWidth: 310,\n paddingTop: showTabs ? 4 : 0,\n ...pickerCssVariables,\n ...popupStyle,\n }}\n {...contentProps}\n >\n {showTabs && (\n <Flexbox\n horizontal\n align={'center'}\n className={styles.tabs}\n justify={'space-between'}\n paddingInline={10}\n >\n <Tabs\n compact\n activeKey={tab}\n items={items}\n size={'small'}\n onChange={(key) => setTab(key as any)}\n />\n {allowDelete && (\n <ActionIcon\n icon={TrashIcon}\n title={deleteText}\n size={{\n blockSize: 32,\n size: 18,\n }}\n onClick={() => {\n handleAvatarChange(defaultAvatar);\n onDelete?.();\n }}\n />\n )}\n </Flexbox>\n )}\n {tab === 'emoji' && (\n <Picker\n custom={mergedCustomEmojis}\n data={data}\n i18n={i18n}\n icons={'outline'}\n locale={locale.split('-')[0]}\n navPosition={showTabs ? 'bottom' : 'top'}\n previewPosition={'none'}\n skinTonePosition={'none'}\n theme={theme.isDarkMode ? 'dark' : 'light'}\n onEmojiSelect={(e: any) => handleAvatarChange(e.src || e.native)}\n />\n )}\n {tab === 'upload' && (\n <AvatarUploader\n compressSize={compressSize}\n shape={shape}\n texts={texts}\n onChange={handleAvatarChange}\n onUpload={onUpload}\n />\n )}\n {customTabs.map(\n (item) =>\n tab === item.value && (\n <Flexbox key={item.value} padding={10}>\n {item.render(handleAvatarChange)}\n </Flexbox>\n ),\n )}\n </Flexbox>\n );\n\n return (\n <Popover\n className={cx(styles.popover)}\n content={content}\n defaultOpen={defaultOpen}\n open={visible}\n placement={'bottom'}\n trigger={'click'}\n classNames={{\n content: styles.popover,\n root: styles.positioner,\n }}\n onOpenChange={(v) => {\n if (loading) return;\n setVisible(v);\n }}\n {...popupProps}\n >\n {customRender ? (\n customRender(ava)\n ) : (\n <Avatar\n avatar={ava}\n className={cx(styles.root, className)}\n loading={loading}\n shape={shape}\n {...rest}\n />\n )}\n </Popover>\n );\n },\n);\n\nEmojiPicker.displayName = 'EmojiPicker';\n\nexport default EmojiPicker;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EmojiPicker.mjs","names":["emojiPickerMessages","useMergeState","Flexbox"],"sources":["../../src/EmojiPicker/EmojiPicker.tsx"],"sourcesContent":["'use client';\n\nimport data from '@emoji-mart/data';\nimport Picker from '@emoji-mart/react';\nimport { getLobeIconCDN, toc } from '@lobehub/icons';\nimport { cx, useTheme } from 'antd-style';\nimport chroma from 'chroma-js';\nimport { SmileIcon, TrashIcon, UploadIcon } from 'lucide-react';\nimport { memo, useMemo, useRef, useState } from 'react';\nimport useSWR from 'swr';\nimport useMergeState from 'use-merge-value';\n\nimport ActionIcon from '@/ActionIcon';\nimport Avatar from '@/Avatar';\nimport { Flexbox } from '@/Flex';\nimport emojiPickerMessages from '@/i18n/resources/en/emojiPicker';\nimport { useTranslation } from '@/i18n/useTranslation';\nimport Icon from '@/Icon';\nimport Popover from '@/Popover';\nimport Tabs, { type TabsProps } from '@/Tabs';\nimport Tooltip from '@/Tooltip';\n\nimport AvatarUploader from './AvatarUploader';\nimport { styles } from './style';\nimport { type EmojiPickerProps } from './type';\n\nconst DEFAULT_AVATAR = '🤖';\n\nconst EmojiPicker = memo<EmojiPickerProps>(\n ({\n value,\n defaultAvatar = DEFAULT_AVATAR,\n onChange,\n locale = 'en-US',\n allowUpload,\n allowDelete,\n allowModelAvatar,\n texts,\n onDelete,\n compressSize = 256,\n customEmojis,\n className,\n loading,\n onUpload,\n customTabs = [],\n popupClassName,\n popupStyle,\n customRender,\n open,\n defaultOpen = false,\n onOpenChange,\n popupProps,\n shape,\n contentProps,\n ...rest\n }) => {\n const ref = useRef<HTMLDivElement>(null);\n const { t } = useTranslation(emojiPickerMessages);\n const [visible, setVisible] = useMergeState(defaultOpen, {\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n value: open,\n });\n const [tab, setTab] = useState<'emoji' | 'upload'>('emoji');\n\n const theme = useTheme();\n const pickerCssVariables = useMemo<Record<string, string>>(\n () => ({\n '--emoji-picker-rgb-accent': chroma(theme.colorPrimary).rgb().join(', '),\n '--emoji-picker-rgb-background': chroma(theme.colorBgElevated).rgb().join(', '),\n }),\n [theme.colorPrimary, theme.colorBgElevated],\n );\n\n const { data: i18n } = useSWR(\n locale,\n async () => await import(`@emoji-mart/data/i18n/${locale.split('-')[0]}.json`),\n { revalidateOnFocus: false, revalidateOnMount: false },\n );\n\n const [ava, setAva] = useMergeState(defaultAvatar, {\n defaultValue: defaultAvatar,\n onChange,\n value,\n });\n\n const handleAvatarChange = (emoji: string) => {\n setAva(emoji);\n setVisible(false);\n };\n\n // Generate model avatars from @lobehub/icons when allowModelAvatar is enabled\n const mergedCustomEmojis = useMemo(() => {\n if (!allowModelAvatar) return customEmojis;\n\n const modelIcons = toc.filter((item) => item.group !== 'application');\n\n const modelEmojisCategory = {\n id: 'models',\n name: 'Models / Providers',\n emojis: modelIcons.map((item) => ({\n id: item.id.toLowerCase(),\n name: item.title,\n keywords: [item.title, item.id, item.fullTitle],\n skins: [{ src: getLobeIconCDN(item.id, { format: 'avatar', cdn: 'aliyun' }) }],\n })),\n };\n\n return customEmojis ? [modelEmojisCategory, ...customEmojis] : [modelEmojisCategory];\n }, [allowModelAvatar, customEmojis]);\n\n const emojiText = texts?.emoji ?? t('emojiPicker.emoji');\n const uploadText = texts?.upload ?? t('emojiPicker.upload');\n const deleteText = texts?.delete ?? t('emojiPicker.delete');\n\n const hideEmojiTab = typeof allowUpload === 'object' && allowUpload?.enableEmoji === false;\n\n const items: TabsProps['items'] = [\n !hideEmojiTab && {\n key: 'emoji',\n label: (\n <Tooltip title={emojiText}>\n <Icon icon={SmileIcon} size={{ size: 20, strokeWidth: 2.5 }} />\n </Tooltip>\n ),\n },\n allowUpload && {\n key: 'upload',\n label: (\n <Tooltip title={uploadText}>\n <Icon icon={UploadIcon} size={{ size: 20, strokeWidth: 2.5 }} />\n </Tooltip>\n ),\n },\n ...customTabs.map((tab) => ({ key: tab.value, label: tab.label })),\n ].filter(Boolean) as TabsProps['items'];\n\n const showTabs = allowDelete || (items && items.length > 1);\n\n const content = (\n <Flexbox\n className={cx(styles.picker, popupClassName)}\n ref={ref}\n style={{\n minWidth: 310,\n paddingTop: showTabs ? 4 : 0,\n ...pickerCssVariables,\n ...popupStyle,\n }}\n {...contentProps}\n >\n {showTabs && (\n <Flexbox\n horizontal\n align={'center'}\n className={styles.tabs}\n justify={'space-between'}\n paddingInline={10}\n >\n <Tabs\n compact\n activeKey={tab}\n items={items}\n size={'small'}\n onChange={(key) => setTab(key as any)}\n />\n {allowDelete && (\n <ActionIcon\n icon={TrashIcon}\n title={deleteText}\n size={{\n blockSize: 32,\n size: 18,\n }}\n onClick={() => {\n handleAvatarChange(defaultAvatar);\n onDelete?.();\n }}\n />\n )}\n </Flexbox>\n )}\n {tab === 'emoji' && (\n <Picker\n custom={mergedCustomEmojis}\n data={data}\n i18n={i18n}\n icons={'outline'}\n locale={locale.split('-')[0]}\n navPosition={showTabs ? 'bottom' : 'top'}\n previewPosition={'none'}\n skinTonePosition={'none'}\n theme={theme.isDarkMode ? 'dark' : 'light'}\n onEmojiSelect={(e: any) => handleAvatarChange(e.src || e.native)}\n />\n )}\n {tab === 'upload' && (\n <AvatarUploader\n compressSize={compressSize}\n shape={shape}\n texts={texts}\n onChange={handleAvatarChange}\n onUpload={onUpload}\n />\n )}\n {customTabs.map(\n (item) =>\n tab === item.value && (\n <Flexbox key={item.value} padding={10}>\n {item.render(handleAvatarChange)}\n </Flexbox>\n ),\n )}\n </Flexbox>\n );\n\n return (\n <Popover\n className={cx(styles.popover)}\n content={content}\n defaultOpen={defaultOpen}\n open={visible}\n placement={'bottom'}\n trigger={'click'}\n classNames={{\n content: styles.popover,\n root: styles.positioner,\n }}\n onOpenChange={(v) => {\n if (loading) return;\n setVisible(v);\n }}\n {...popupProps}\n >\n {customRender ? (\n customRender(ava)\n ) : (\n <Avatar\n avatar={ava}\n className={cx(styles.root, className)}\n loading={loading}\n shape={shape}\n {...rest}\n />\n )}\n </Popover>\n );\n },\n);\n\nEmojiPicker.displayName = 'EmojiPicker';\n\nexport default EmojiPicker;\n"],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ import { styles, variants } from "./style.mjs";
|
|
|
9
9
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
10
10
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
11
|
import { cx, useThemeMode } from "antd-style";
|
|
12
|
-
import
|
|
12
|
+
import useControlledState from "use-merge-value";
|
|
13
13
|
import { isEqual } from "es-toolkit/compat";
|
|
14
14
|
import { Undo2Icon, XIcon } from "lucide-react";
|
|
15
15
|
import { useHotkeys, useRecordHotkeys } from "react-hotkeys-hook";
|
|
@@ -22,7 +22,7 @@ const HotkeyInput = memo(({ value = "", defaultValue = "", resetValue = "", onCh
|
|
|
22
22
|
const { isDarkMode } = useThemeMode();
|
|
23
23
|
const { t } = useTranslation(hotkey_default);
|
|
24
24
|
const isAppleDevice = useMemo(() => checkIsAppleDevice(isApple), [isApple]);
|
|
25
|
-
const [hotkeyValue, setHotkeyValue] =
|
|
25
|
+
const [hotkeyValue, setHotkeyValue] = useControlledState(defaultValue, {
|
|
26
26
|
defaultValue,
|
|
27
27
|
onChange,
|
|
28
28
|
value
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HotkeyInput.mjs","names":["hotkeyMessages","useControlledState","Flexbox"],"sources":["../../src/HotkeyInput/HotkeyInput.tsx"],"sourcesContent":["'use client';\n\nimport { type InputRef } from 'antd';\nimport { cx, useThemeMode } from 'antd-style';\nimport { isEqual } from 'es-toolkit/compat';\nimport { Undo2Icon, XIcon } from 'lucide-react';\nimport {\n type FocusEvent,\n memo,\n type MouseEvent,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useHotkeys, useRecordHotkeys } from 'react-hotkeys-hook';\nimport useControlledState from 'use-merge-value';\n\nimport ActionIcon from '@/ActionIcon';\nimport { Flexbox } from '@/Flex';\nimport Hotkey from '@/Hotkey';\nimport { checkIsAppleDevice, NORMATIVE_MODIFIER, splitKeysByPlus } from '@/Hotkey/utils';\nimport hotkeyMessages from '@/i18n/resources/en/hotkey';\nimport { useTranslation } from '@/i18n/useTranslation';\n\nimport { styles, variants } from './style';\nimport { type HotkeyInputProps } from './type';\n\nconst HotkeyInput = memo<HotkeyInputProps>(\n ({\n value = '',\n defaultValue = '',\n resetValue = '',\n onChange,\n onClear,\n onConflict,\n placeholder,\n disabled,\n shadow,\n allowClear,\n allowReset = true,\n style,\n className,\n hotkeyConflicts = [],\n variant,\n texts,\n isApple,\n onBlur,\n onReset,\n onFocus,\n }) => {\n const [isFocused, setIsFocused] = useState(false);\n const [hasConflict, setHasConflict] = useState(false);\n const [hasInvalidCombination, setHasInvalidCombination] = useState(false);\n const inputRef = useRef<InputRef>(null);\n const { isDarkMode } = useThemeMode();\n const { t } = useTranslation(hotkeyMessages);\n const isAppleDevice = useMemo(() => checkIsAppleDevice(isApple), [isApple]);\n const [hotkeyValue, setHotkeyValue] = useControlledState(defaultValue, {\n defaultValue,\n onChange,\n value,\n });\n\n // 使用 useRecordHotkeys 处理快捷键录入\n const [recordedKeys, { start, stop, isRecording, resetKeys }] = useRecordHotkeys();\n\n useHotkeys(\n '*',\n () => {\n inputRef.current?.blur();\n },\n {\n enableOnContentEditable: true,\n enableOnFormTags: true,\n enabled: isRecording && !disabled,\n keydown: false,\n keyup: true,\n preventDefault: true,\n },\n );\n\n // 处理按键,保证格式正确:修饰键在前,最多一个非修饰键在后\n const formatKeys = useCallback((keysSet: Set<string>) => {\n const modifiers: string[] = [];\n const normalKeys: string[] = [];\n\n for (const key of keysSet) {\n // 处理不同表示的修饰键\n const normalizedKey: any = key.toLowerCase();\n if (NORMATIVE_MODIFIER.includes(normalizedKey)) {\n // 统一修饰键表示\n if (\n (!isAppleDevice && normalizedKey === 'ctrl') ||\n (isAppleDevice && normalizedKey === 'meta')\n ) {\n if (!modifiers.includes('mod')) modifiers.push('mod');\n } else if (!modifiers.includes(normalizedKey)) {\n modifiers.push(normalizedKey);\n }\n } else {\n normalKeys.push(key);\n }\n }\n\n // 至少需要一个修饰键\n if (modifiers.length === 0 && normalKeys.length > 0) {\n return { isValid: false, keys: [] };\n }\n\n // 只允许一个非修饰键,如果有多个,只保留最后一个\n const finalKey = normalKeys.length > 0 ? [normalKeys.at(-1)] : [];\n const shortcuts = [modifiers, finalKey];\n\n return {\n // 组合必须包含至少一个按键\n isValid: shortcuts.every((k) => k.length > 0),\n keys: shortcuts.flat(),\n };\n }, []);\n\n // 获取格式化后的按键字符串\n const { isValid, keys } = formatKeys(recordedKeys);\n const keysString = keys.join('+');\n\n // 检查快捷键冲突\n const checkHotkeyConflict = useCallback(\n (newHotkey: string): boolean => {\n return hotkeyConflicts\n .filter((conflictKey) => conflictKey !== resetValue)\n .some((conflictKey) => {\n const newKeys = splitKeysByPlus(newHotkey);\n const conflictKeys = splitKeysByPlus(conflictKey);\n return isEqual(newKeys, conflictKeys);\n });\n },\n [hotkeyConflicts],\n );\n\n // 当按键组合完成时处理结果\n useEffect(() => {\n if (recordedKeys.size > 0 && !isRecording) {\n if (!isValid) {\n setHasInvalidCombination(true);\n setHasConflict(false);\n return;\n }\n\n setHasInvalidCombination(false);\n const newKeysString = keysString;\n\n // 检查冲突\n const conflict = checkHotkeyConflict(newKeysString);\n if (conflict) {\n setHasConflict(true);\n onConflict?.(newKeysString);\n } else {\n setHasConflict(false);\n setHotkeyValue?.(newKeysString);\n }\n }\n }, [\n recordedKeys,\n isRecording,\n isValid,\n keysString,\n checkHotkeyConflict,\n setHotkeyValue,\n onConflict,\n ]);\n\n // 处理输入框焦点\n const handleFocus = (e: FocusEvent<HTMLInputElement>) => {\n if (disabled) return;\n setIsFocused(true);\n setHasConflict(false);\n setHasInvalidCombination(false);\n start(); // 开始记录\n onFocus?.(e);\n };\n\n const handleBlur = (e: FocusEvent<HTMLInputElement>) => {\n setIsFocused(false);\n stop(); // 停止记录\n onBlur?.(e);\n };\n\n const handleClear = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setHotkeyValue?.('');\n resetKeys();\n setHasConflict(false);\n setHasInvalidCombination(false);\n setIsFocused(false);\n stop();\n onClear?.(hotkeyValue);\n };\n\n // 重置功能\n const handleReset = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setHotkeyValue?.(resetValue);\n resetKeys();\n setHasConflict(false);\n setHasInvalidCombination(false);\n setIsFocused(false);\n stop(); // 停止记录\n onReset?.(hotkeyValue, resetValue);\n };\n\n const handleClick = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (disabled || isFocused) return;\n inputRef.current?.focus();\n };\n\n const placeholderText = placeholder ?? t('hotkey.placeholder');\n const resetTitle = texts?.reset ?? t('hotkey.reset');\n const clearTitle = texts?.clear ?? t('hotkey.clear');\n const conflictText = texts?.conflicts ?? t('hotkey.conflict');\n const invalidText = texts?.invalidCombination ?? t('hotkey.invalidCombination');\n\n return (\n <Flexbox\n className={className}\n gap={8}\n style={{\n position: 'relative',\n ...style,\n }}\n >\n <Flexbox\n horizontal\n align={'center'}\n justify={'space-between'}\n className={cx(\n variants({\n disabled,\n error: hasConflict || hasInvalidCombination,\n focused: isFocused,\n shadow,\n variant: variant || (isDarkMode ? 'filled' : 'outlined'),\n }),\n )}\n onClick={handleClick}\n >\n <div style={{ pointerEvents: 'none' }}>\n {isRecording ? (\n <span className={styles.placeholder}>\n {keys.length > 0 ? <Hotkey keys={keysString} /> : placeholderText}\n </span>\n ) : hotkeyValue ? (\n <Hotkey keys={hotkeyValue} />\n ) : (\n <span className={styles.placeholder}>{placeholderText}</span>\n )}\n </div>\n\n {/* 隐藏的输入框,用于接收焦点 */}\n <input\n readOnly\n className={styles.hiddenInput}\n disabled={disabled}\n ref={inputRef as any}\n style={{ pointerEvents: 'none' }}\n onBlur={handleBlur}\n onFocus={handleFocus}\n />\n\n {!isFocused && hotkeyValue && !disabled && (allowReset || allowClear) && (\n <Flexbox horizontal gap={4}>\n {allowReset && hotkeyValue !== resetValue && (\n <ActionIcon\n icon={Undo2Icon}\n size={'small'}\n title={resetTitle}\n variant={'filled'}\n onClick={handleReset}\n />\n )}\n {allowClear && (\n <ActionIcon\n icon={XIcon}\n size={'small'}\n title={clearTitle}\n variant={'filled'}\n onClick={handleClear}\n />\n )}\n </Flexbox>\n )}\n </Flexbox>\n {hasConflict && <div className={styles.errorText}>{conflictText}</div>}\n {hasInvalidCombination && <div className={styles.errorText}>{invalidText}</div>}\n </Flexbox>\n );\n },\n);\n\nHotkeyInput.displayName = 'HotkeyInput';\n\nexport default HotkeyInput;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HotkeyInput.mjs","names":["hotkeyMessages","Flexbox"],"sources":["../../src/HotkeyInput/HotkeyInput.tsx"],"sourcesContent":["'use client';\n\nimport { type InputRef } from 'antd';\nimport { cx, useThemeMode } from 'antd-style';\nimport { isEqual } from 'es-toolkit/compat';\nimport { Undo2Icon, XIcon } from 'lucide-react';\nimport {\n type FocusEvent,\n memo,\n type MouseEvent,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useHotkeys, useRecordHotkeys } from 'react-hotkeys-hook';\nimport useControlledState from 'use-merge-value';\n\nimport ActionIcon from '@/ActionIcon';\nimport { Flexbox } from '@/Flex';\nimport Hotkey from '@/Hotkey';\nimport { checkIsAppleDevice, NORMATIVE_MODIFIER, splitKeysByPlus } from '@/Hotkey/utils';\nimport hotkeyMessages from '@/i18n/resources/en/hotkey';\nimport { useTranslation } from '@/i18n/useTranslation';\n\nimport { styles, variants } from './style';\nimport { type HotkeyInputProps } from './type';\n\nconst HotkeyInput = memo<HotkeyInputProps>(\n ({\n value = '',\n defaultValue = '',\n resetValue = '',\n onChange,\n onClear,\n onConflict,\n placeholder,\n disabled,\n shadow,\n allowClear,\n allowReset = true,\n style,\n className,\n hotkeyConflicts = [],\n variant,\n texts,\n isApple,\n onBlur,\n onReset,\n onFocus,\n }) => {\n const [isFocused, setIsFocused] = useState(false);\n const [hasConflict, setHasConflict] = useState(false);\n const [hasInvalidCombination, setHasInvalidCombination] = useState(false);\n const inputRef = useRef<InputRef>(null);\n const { isDarkMode } = useThemeMode();\n const { t } = useTranslation(hotkeyMessages);\n const isAppleDevice = useMemo(() => checkIsAppleDevice(isApple), [isApple]);\n const [hotkeyValue, setHotkeyValue] = useControlledState(defaultValue, {\n defaultValue,\n onChange,\n value,\n });\n\n // 使用 useRecordHotkeys 处理快捷键录入\n const [recordedKeys, { start, stop, isRecording, resetKeys }] = useRecordHotkeys();\n\n useHotkeys(\n '*',\n () => {\n inputRef.current?.blur();\n },\n {\n enableOnContentEditable: true,\n enableOnFormTags: true,\n enabled: isRecording && !disabled,\n keydown: false,\n keyup: true,\n preventDefault: true,\n },\n );\n\n // 处理按键,保证格式正确:修饰键在前,最多一个非修饰键在后\n const formatKeys = useCallback((keysSet: Set<string>) => {\n const modifiers: string[] = [];\n const normalKeys: string[] = [];\n\n for (const key of keysSet) {\n // 处理不同表示的修饰键\n const normalizedKey: any = key.toLowerCase();\n if (NORMATIVE_MODIFIER.includes(normalizedKey)) {\n // 统一修饰键表示\n if (\n (!isAppleDevice && normalizedKey === 'ctrl') ||\n (isAppleDevice && normalizedKey === 'meta')\n ) {\n if (!modifiers.includes('mod')) modifiers.push('mod');\n } else if (!modifiers.includes(normalizedKey)) {\n modifiers.push(normalizedKey);\n }\n } else {\n normalKeys.push(key);\n }\n }\n\n // 至少需要一个修饰键\n if (modifiers.length === 0 && normalKeys.length > 0) {\n return { isValid: false, keys: [] };\n }\n\n // 只允许一个非修饰键,如果有多个,只保留最后一个\n const finalKey = normalKeys.length > 0 ? [normalKeys.at(-1)] : [];\n const shortcuts = [modifiers, finalKey];\n\n return {\n // 组合必须包含至少一个按键\n isValid: shortcuts.every((k) => k.length > 0),\n keys: shortcuts.flat(),\n };\n }, []);\n\n // 获取格式化后的按键字符串\n const { isValid, keys } = formatKeys(recordedKeys);\n const keysString = keys.join('+');\n\n // 检查快捷键冲突\n const checkHotkeyConflict = useCallback(\n (newHotkey: string): boolean => {\n return hotkeyConflicts\n .filter((conflictKey) => conflictKey !== resetValue)\n .some((conflictKey) => {\n const newKeys = splitKeysByPlus(newHotkey);\n const conflictKeys = splitKeysByPlus(conflictKey);\n return isEqual(newKeys, conflictKeys);\n });\n },\n [hotkeyConflicts],\n );\n\n // 当按键组合完成时处理结果\n useEffect(() => {\n if (recordedKeys.size > 0 && !isRecording) {\n if (!isValid) {\n setHasInvalidCombination(true);\n setHasConflict(false);\n return;\n }\n\n setHasInvalidCombination(false);\n const newKeysString = keysString;\n\n // 检查冲突\n const conflict = checkHotkeyConflict(newKeysString);\n if (conflict) {\n setHasConflict(true);\n onConflict?.(newKeysString);\n } else {\n setHasConflict(false);\n setHotkeyValue?.(newKeysString);\n }\n }\n }, [\n recordedKeys,\n isRecording,\n isValid,\n keysString,\n checkHotkeyConflict,\n setHotkeyValue,\n onConflict,\n ]);\n\n // 处理输入框焦点\n const handleFocus = (e: FocusEvent<HTMLInputElement>) => {\n if (disabled) return;\n setIsFocused(true);\n setHasConflict(false);\n setHasInvalidCombination(false);\n start(); // 开始记录\n onFocus?.(e);\n };\n\n const handleBlur = (e: FocusEvent<HTMLInputElement>) => {\n setIsFocused(false);\n stop(); // 停止记录\n onBlur?.(e);\n };\n\n const handleClear = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setHotkeyValue?.('');\n resetKeys();\n setHasConflict(false);\n setHasInvalidCombination(false);\n setIsFocused(false);\n stop();\n onClear?.(hotkeyValue);\n };\n\n // 重置功能\n const handleReset = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setHotkeyValue?.(resetValue);\n resetKeys();\n setHasConflict(false);\n setHasInvalidCombination(false);\n setIsFocused(false);\n stop(); // 停止记录\n onReset?.(hotkeyValue, resetValue);\n };\n\n const handleClick = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (disabled || isFocused) return;\n inputRef.current?.focus();\n };\n\n const placeholderText = placeholder ?? t('hotkey.placeholder');\n const resetTitle = texts?.reset ?? t('hotkey.reset');\n const clearTitle = texts?.clear ?? t('hotkey.clear');\n const conflictText = texts?.conflicts ?? t('hotkey.conflict');\n const invalidText = texts?.invalidCombination ?? t('hotkey.invalidCombination');\n\n return (\n <Flexbox\n className={className}\n gap={8}\n style={{\n position: 'relative',\n ...style,\n }}\n >\n <Flexbox\n horizontal\n align={'center'}\n justify={'space-between'}\n className={cx(\n variants({\n disabled,\n error: hasConflict || hasInvalidCombination,\n focused: isFocused,\n shadow,\n variant: variant || (isDarkMode ? 'filled' : 'outlined'),\n }),\n )}\n onClick={handleClick}\n >\n <div style={{ pointerEvents: 'none' }}>\n {isRecording ? (\n <span className={styles.placeholder}>\n {keys.length > 0 ? <Hotkey keys={keysString} /> : placeholderText}\n </span>\n ) : hotkeyValue ? (\n <Hotkey keys={hotkeyValue} />\n ) : (\n <span className={styles.placeholder}>{placeholderText}</span>\n )}\n </div>\n\n {/* 隐藏的输入框,用于接收焦点 */}\n <input\n readOnly\n className={styles.hiddenInput}\n disabled={disabled}\n ref={inputRef as any}\n style={{ pointerEvents: 'none' }}\n onBlur={handleBlur}\n onFocus={handleFocus}\n />\n\n {!isFocused && hotkeyValue && !disabled && (allowReset || allowClear) && (\n <Flexbox horizontal gap={4}>\n {allowReset && hotkeyValue !== resetValue && (\n <ActionIcon\n icon={Undo2Icon}\n size={'small'}\n title={resetTitle}\n variant={'filled'}\n onClick={handleReset}\n />\n )}\n {allowClear && (\n <ActionIcon\n icon={XIcon}\n size={'small'}\n title={clearTitle}\n variant={'filled'}\n onClick={handleClear}\n />\n )}\n </Flexbox>\n )}\n </Flexbox>\n {hasConflict && <div className={styles.errorText}>{conflictText}</div>}\n {hasInvalidCombination && <div className={styles.errorText}>{invalidText}</div>}\n </Flexbox>\n );\n },\n);\n\nHotkeyInput.displayName = 'HotkeyInput';\n\nexport default HotkeyInput;\n"],"mappings":"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"}
|
|
@@ -6,10 +6,10 @@ import { styles } from "./styles.mjs";
|
|
|
6
6
|
import { memo } from "react";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import { cx } from "antd-style";
|
|
9
|
-
import
|
|
9
|
+
import useControlledState from "use-merge-value";
|
|
10
10
|
//#region src/ImageSelect/ImageSelect.tsx
|
|
11
11
|
const ImageSelect = memo(({ ref, className, style, value, defaultValue, onChange, options, width = 144, height = 86, styles: customStyles, classNames, unoptimized, ...rest }) => {
|
|
12
|
-
const [currentValue, setCurrentValue] =
|
|
12
|
+
const [currentValue, setCurrentValue] = useControlledState(defaultValue, {
|
|
13
13
|
defaultValue,
|
|
14
14
|
onChange,
|
|
15
15
|
value
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageSelect.mjs","names":["
|
|
1
|
+
{"version":3,"file":"ImageSelect.mjs","names":["Flexbox"],"sources":["../../src/ImageSelect/ImageSelect.tsx"],"sourcesContent":["'use client';\n\nimport { cx } from 'antd-style';\nimport { memo } from 'react';\nimport useControlledState from 'use-merge-value';\n\nimport { Flexbox } from '@/Flex';\nimport Icon from '@/Icon';\nimport Img from '@/Img';\n\nimport { styles } from './styles';\nimport type { ImageSelectProps } from './type';\n\nconst ImageSelect = memo<ImageSelectProps>(\n ({\n ref,\n className,\n style,\n value,\n defaultValue,\n onChange,\n options,\n width = 144,\n height = 86,\n styles: customStyles,\n classNames,\n unoptimized,\n ...rest\n }) => {\n const [currentValue, setCurrentValue] = useControlledState<string>(defaultValue, {\n defaultValue,\n onChange,\n value,\n });\n\n return (\n <Flexbox horizontal className={className} gap={16} ref={ref} style={style} {...rest}>\n {options?.map((item) => {\n const isActive = item.value === currentValue;\n return (\n <Flexbox\n align={'center'}\n className={cx(styles.container, isActive && styles.active)}\n gap={4}\n key={item.value}\n onClick={() => setCurrentValue(item.value)}\n >\n <Img\n alt={item.alt || item.value}\n className={cx(styles.img, isActive && styles.imgActive, classNames?.img)}\n height={height}\n src={item.img}\n unoptimized={unoptimized}\n width={width}\n style={{\n ...customStyles?.img,\n height,\n width,\n }}\n />\n\n <Flexbox horizontal align={'center'} gap={4}>\n {item.icon && <Icon icon={item.icon} />}\n {item.label}\n </Flexbox>\n </Flexbox>\n );\n })}\n </Flexbox>\n );\n },\n);\n\nImageSelect.displayName = 'ImageSelect';\n\nexport default ImageSelect;\n"],"mappings":";;;;;;;;;;AAaA,MAAM,cAAc,MACjB,EACC,KACA,WACA,OACA,OACA,cACA,UACA,SACA,QAAQ,KACR,SAAS,IACT,QAAQ,cACR,YACA,aACA,GAAG,WACC;CACJ,MAAM,CAAC,cAAc,mBAAmB,mBAA2B,cAAc;EAC/E;EACA;EACA;EACD,CAAC;AAEF,QACE,oBAACA,mBAAD;EAAS,YAAA;EAAsB;EAAW,KAAK;EAAS;EAAY;EAAO,GAAI;YAC5E,SAAS,KAAK,SAAS;GACtB,MAAM,WAAW,KAAK,UAAU;AAChC,UACE,qBAACA,mBAAD;IACE,OAAO;IACP,WAAW,GAAG,OAAO,WAAW,YAAY,OAAO,OAAO;IAC1D,KAAK;IAEL,eAAe,gBAAgB,KAAK,MAAM;cAL5C,CAOE,oBAAC,KAAD;KACE,KAAK,KAAK,OAAO,KAAK;KACtB,WAAW,GAAG,OAAO,KAAK,YAAY,OAAO,WAAW,YAAY,IAAI;KAChE;KACR,KAAK,KAAK;KACG;KACN;KACP,OAAO;MACL,GAAG,cAAc;MACjB;MACA;MACD;KACD,CAAA,EAEF,qBAACA,mBAAD;KAAS,YAAA;KAAW,OAAO;KAAU,KAAK;eAA1C,CACG,KAAK,QAAQ,oBAAC,MAAD,EAAM,MAAM,KAAK,MAAQ,CAAA,EACtC,KAAK,MACE;OACF;MArBH,KAAK,MAqBF;IAEZ;EACM,CAAA;EAGf;AAED,YAAY,cAAc"}
|
|
@@ -7,12 +7,12 @@ import { styles } from "./style.mjs";
|
|
|
7
7
|
import { memo, useMemo, useRef, useState } from "react";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import { cx } from "antd-style";
|
|
10
|
-
import
|
|
10
|
+
import useControlledState from "use-merge-value";
|
|
11
11
|
import { LucideLoader2, Search } from "lucide-react";
|
|
12
12
|
import { useHotkeys } from "react-hotkeys-hook";
|
|
13
13
|
//#region src/SearchBar/SearchBar.tsx
|
|
14
14
|
const SearchBar = memo(({ defaultValue = "", spotlight, className, value, onInputChange, placeholder, enableShortKey, shortKey = "mod+k", onSearch, loading, style, onChange, onBlur, onPressEnter, onFocus, styles: { input: inputStyle, shortKey: shortKeyStyle } = {}, classNames: { input: inputClassName, shortKey: shortKeyClassName } = {}, ...rest }) => {
|
|
15
|
-
const [inputValue, setInputValue] =
|
|
15
|
+
const [inputValue, setInputValue] = useControlledState(defaultValue, {
|
|
16
16
|
defaultValue,
|
|
17
17
|
onChange: onInputChange,
|
|
18
18
|
value
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchBar.mjs","names":[
|
|
1
|
+
{"version":3,"file":"SearchBar.mjs","names":[],"sources":["../../src/SearchBar/SearchBar.tsx"],"sourcesContent":["'use client';\n\nimport { cx } from 'antd-style';\nimport { LucideLoader2, Search } from 'lucide-react';\nimport { memo, useMemo, useRef, useState } from 'react';\nimport { useHotkeys } from 'react-hotkeys-hook';\nimport useControlledState from 'use-merge-value';\n\nimport Spotlight from '@/awesome/Spotlight';\nimport Hotkey from '@/Hotkey';\nimport Icon from '@/Icon';\nimport Input from '@/Input';\n\nimport { styles } from './style';\nimport { type SearchBarProps } from './type';\n\nconst SearchBar = memo<SearchBarProps>(\n ({\n defaultValue = '',\n spotlight,\n className,\n value,\n onInputChange,\n placeholder,\n enableShortKey,\n shortKey = 'mod+k',\n onSearch,\n loading,\n style,\n onChange,\n onBlur,\n onPressEnter,\n onFocus,\n styles: { input: inputStyle, shortKey: shortKeyStyle } = {},\n classNames: { input: inputClassName, shortKey: shortKeyClassName } = {},\n ...rest\n }) => {\n const [inputValue, setInputValue] = useControlledState(defaultValue, {\n defaultValue,\n onChange: onInputChange,\n value,\n });\n\n const [showTag, setShowTag] = useState<boolean>(true);\n const inputReference: any = useRef<HTMLInputElement>(null);\n const hotkey = useMemo(\n () => (shortKey.includes('+') ? shortKey : `mod+${shortKey}`),\n [shortKey],\n );\n\n useHotkeys(\n hotkey,\n () => {\n if (!enableShortKey) return;\n inputReference.current?.focus();\n },\n {\n enableOnFormTags: true,\n enabled: !!enableShortKey && !!shortKey,\n preventDefault: true,\n },\n );\n\n return (\n <div className={cx(styles.search, className)} style={style}>\n {spotlight && <Spotlight />}\n <Input\n allowClear\n className={inputClassName}\n placeholder={placeholder ?? 'Type keywords...'}\n ref={inputReference}\n style={inputStyle}\n value={inputValue}\n prefix={\n <Icon\n className={styles.icon}\n icon={loading ? LucideLoader2 : Search}\n size=\"small\"\n spin={loading}\n style={{ marginRight: 4 }}\n />\n }\n onBlur={(e) => {\n onBlur?.(e);\n setInputValue(e.target.value);\n setShowTag(true);\n }}\n onChange={(e) => {\n setInputValue(e.target.value);\n onChange?.(e);\n }}\n onFocus={(e) => {\n onFocus?.(e);\n setShowTag(false);\n }}\n onPressEnter={(e) => {\n onPressEnter?.(e);\n onSearch?.(inputValue);\n }}\n {...rest}\n />\n {enableShortKey && showTag && !inputValue && (\n <Hotkey\n compact\n className={cx(styles.tag, shortKeyClassName)}\n keys={hotkey}\n style={shortKeyStyle}\n />\n )}\n </div>\n );\n },\n);\n\nSearchBar.displayName = 'SearchBar';\n\nexport default SearchBar;\n"],"mappings":";;;;;;;;;;;;;AAgBA,MAAM,YAAY,MACf,EACC,eAAe,IACf,WACA,WACA,OACA,eACA,aACA,gBACA,WAAW,SACX,UACA,SACA,OACA,UACA,QACA,cACA,SACA,QAAQ,EAAE,OAAO,YAAY,UAAU,kBAAkB,EAAE,EAC3D,YAAY,EAAE,OAAO,gBAAgB,UAAU,sBAAsB,EAAE,EACvE,GAAG,WACC;CACJ,MAAM,CAAC,YAAY,iBAAiB,mBAAmB,cAAc;EACnE;EACA,UAAU;EACV;EACD,CAAC;CAEF,MAAM,CAAC,SAAS,cAAc,SAAkB,KAAK;CACrD,MAAM,iBAAsB,OAAyB,KAAK;CAC1D,MAAM,SAAS,cACN,SAAS,SAAS,IAAI,GAAG,WAAW,OAAO,YAClD,CAAC,SAAS,CACX;AAED,YACE,cACM;AACJ,MAAI,CAAC,eAAgB;AACrB,iBAAe,SAAS,OAAO;IAEjC;EACE,kBAAkB;EAClB,SAAS,CAAC,CAAC,kBAAkB,CAAC,CAAC;EAC/B,gBAAgB;EACjB,CACF;AAED,QACE,qBAAC,OAAD;EAAK,WAAW,GAAG,OAAO,QAAQ,UAAU;EAAS;YAArD;GACG,aAAa,oBAAC,WAAD,EAAa,CAAA;GAC3B,oBAAC,OAAD;IACE,YAAA;IACA,WAAW;IACX,aAAa,eAAe;IAC5B,KAAK;IACL,OAAO;IACP,OAAO;IACP,QACE,oBAAC,MAAD;KACE,WAAW,OAAO;KAClB,MAAM,UAAU,gBAAgB;KAChC,MAAK;KACL,MAAM;KACN,OAAO,EAAE,aAAa,GAAG;KACzB,CAAA;IAEJ,SAAS,MAAM;AACb,cAAS,EAAE;AACX,mBAAc,EAAE,OAAO,MAAM;AAC7B,gBAAW,KAAK;;IAElB,WAAW,MAAM;AACf,mBAAc,EAAE,OAAO,MAAM;AAC7B,gBAAW,EAAE;;IAEf,UAAU,MAAM;AACd,eAAU,EAAE;AACZ,gBAAW,MAAM;;IAEnB,eAAe,MAAM;AACnB,oBAAe,EAAE;AACjB,gBAAW,WAAW;;IAExB,GAAI;IACJ,CAAA;GACD,kBAAkB,WAAW,CAAC,cAC7B,oBAAC,QAAD;IACE,SAAA;IACA,WAAW,GAAG,OAAO,KAAK,kBAAkB;IAC5C,MAAM;IACN,OAAO;IACP,CAAA;GAEA;;EAGX;AAED,UAAU,cAAc"}
|
package/es/Toc/TocMobile.mjs
CHANGED
|
@@ -5,11 +5,11 @@ import { mapItems } from "./utils.mjs";
|
|
|
5
5
|
import { memo, useMemo } from "react";
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
7
|
import { Anchor, Collapse, ConfigProvider } from "antd";
|
|
8
|
-
import
|
|
8
|
+
import useControlledState from "use-merge-value";
|
|
9
9
|
import { PanelTopClose, PanelTopOpen } from "lucide-react";
|
|
10
10
|
//#region src/Toc/TocMobile.tsx
|
|
11
11
|
const TocMobile = memo(({ items, activeKey, onChange, getContainer, headerHeight = 64, tocWidth = 176 }) => {
|
|
12
|
-
const [activeLink, setActiveLink] =
|
|
12
|
+
const [activeLink, setActiveLink] = useControlledState("", {
|
|
13
13
|
onChange,
|
|
14
14
|
value: activeKey
|
|
15
15
|
});
|
package/es/Toc/TocMobile.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TocMobile.mjs","names":[
|
|
1
|
+
{"version":3,"file":"TocMobile.mjs","names":[],"sources":["../../src/Toc/TocMobile.tsx"],"sourcesContent":["'use client';\n\nimport { Anchor, Collapse, ConfigProvider } from 'antd';\nimport { PanelTopClose, PanelTopOpen } from 'lucide-react';\nimport { memo, useMemo } from 'react';\nimport useControlledState from 'use-merge-value';\n\nimport ActionIcon from '@/ActionIcon';\n\nimport { styles } from './style';\nimport type { TocMobileProps } from './type';\nimport { mapItems } from './utils';\n\nconst TocMobile = memo<TocMobileProps>(\n ({ items, activeKey, onChange, getContainer, headerHeight = 64, tocWidth = 176 }) => {\n // tocWidth is part of the interface but not used in this component\n void tocWidth;\n const [activeLink, setActiveLink] = useControlledState<string>('', {\n onChange,\n value: activeKey,\n });\n\n const activeAnchor = items.find((item) => item.id === activeLink);\n\n const tocItems = useMemo(() => mapItems(items), [items]);\n\n return (\n <ConfigProvider theme={{ token: { fontSize: 12, sizeStep: 3 } }}>\n <section className={styles.mobileCtn}>\n <Collapse\n ghost\n className={styles.expand}\n expandIconPlacement={'end'}\n expandIcon={({ isActive }) => (\n <ActionIcon icon={isActive ? PanelTopClose : PanelTopOpen} size={'small'} />\n )}\n >\n <Collapse.Panel\n forceRender\n header={activeAnchor ? activeAnchor.title : 'TOC'}\n key={'toc'}\n >\n <ConfigProvider theme={{ token: { fontSize: 14, sizeStep: 4 } }}>\n <Anchor\n getContainer={getContainer}\n items={tocItems}\n targetOffset={headerHeight + 48}\n onChange={(currentLink) => {\n setActiveLink(currentLink.replace('#', ''));\n }}\n />\n </ConfigProvider>\n </Collapse.Panel>\n </Collapse>\n </section>\n </ConfigProvider>\n );\n },\n);\n\nTocMobile.displayName = 'TocMobile';\n\nexport default TocMobile;\n"],"mappings":";;;;;;;;;;AAaA,MAAM,YAAY,MACf,EAAE,OAAO,WAAW,UAAU,cAAc,eAAe,IAAI,WAAW,UAAU;CAGnF,MAAM,CAAC,YAAY,iBAAiB,mBAA2B,IAAI;EACjE;EACA,OAAO;EACR,CAAC;CAEF,MAAM,eAAe,MAAM,MAAM,SAAS,KAAK,OAAO,WAAW;CAEjE,MAAM,WAAW,cAAc,SAAS,MAAM,EAAE,CAAC,MAAM,CAAC;AAExD,QACE,oBAAC,gBAAD;EAAgB,OAAO,EAAE,OAAO;GAAE,UAAU;GAAI,UAAU;GAAG,EAAE;YAC7D,oBAAC,WAAD;GAAS,WAAW,OAAO;aACzB,oBAAC,UAAD;IACE,OAAA;IACA,WAAW,OAAO;IAClB,qBAAqB;IACrB,aAAa,EAAE,eACb,oBAAC,YAAD;KAAY,MAAM,WAAW,gBAAgB;KAAc,MAAM;KAAW,CAAA;cAG9E,oBAAC,SAAS,OAAV;KACE,aAAA;KACA,QAAQ,eAAe,aAAa,QAAQ;eAG5C,oBAAC,gBAAD;MAAgB,OAAO,EAAE,OAAO;OAAE,UAAU;OAAI,UAAU;OAAG,EAAE;gBAC7D,oBAAC,QAAD;OACgB;OACd,OAAO;OACP,cAAc,eAAe;OAC7B,WAAW,gBAAgB;AACzB,sBAAc,YAAY,QAAQ,KAAK,GAAG,CAAC;;OAE7C,CAAA;MACa,CAAA;KACF,EAZV,MAYU;IACR,CAAA;GACH,CAAA;EACK,CAAA;EAGtB;AAED,UAAU,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style.mjs","names":[],"sources":["../../../src/base-ui/Modal/style.ts"],"sourcesContent":["import { createStaticStyles } from 'antd-style';\n\nexport const styles = createStaticStyles(({ css, cssVar }) => ({\n backdrop: css`\n position: fixed;\n z-index: 1000;\n inset: 0;\n\n background: color-mix(in srgb, ${cssVar.colorBgContainer} 60%, transparent);\n\n transition: opacity 150ms ease-out;\n\n &[data-starting-style],\n &[data-ending-style] {\n opacity: 0;\n }\n `,\n\n close: css`\n cursor: pointer;\n\n position: absolute;\n inset-block-start: 12px;\n inset-inline-end: 12px;\n\n display: flex;\n align-items: center;\n justify-content: center;\n\n width: 32px;\n height: 32px;\n padding: 0;\n border: none;\n border-radius: ${cssVar.borderRadiusSM};\n\n color: ${cssVar.colorTextTertiary};\n\n background: transparent;\n\n transition: all 150ms ease-out;\n\n &:hover {\n color: ${cssVar.colorText};\n background: ${cssVar.colorFillSecondary};\n }\n\n &:focus-visible {\n outline: 2px solid ${cssVar.colorPrimaryBorder};\n outline-offset: 1px;\n }\n `,\n\n closeInline: css`\n cursor: pointer;\n\n display: flex;\n align-items: center;\n justify-content: center;\n\n width: 32px;\n height: 32px;\n padding: 0;\n border: none;\n border-radius: ${cssVar.borderRadiusSM};\n\n color: ${cssVar.colorTextTertiary};\n\n background: transparent;\n\n transition: all 150ms ease-out;\n\n &:hover {\n color: ${cssVar.colorText};\n background: ${cssVar.colorFillSecondary};\n }\n\n &:focus-visible {\n outline: 2px solid ${cssVar.colorPrimaryBorder};\n outline-offset: 1px;\n }\n `,\n\n content: css`\n overflow: hidden auto;\n padding-block: 12px;\n padding-inline: 16px;\n `,\n\n footer: css`\n display: flex;\n gap: 8px;\n align-items: center;\n justify-content: flex-end;\n\n padding-block: 12px;\n padding-inline: 16px;\n border-block-start: 1px solid ${cssVar.colorBorderSecondary};\n `,\n\n header: css`\n display: flex;\n align-items: center;\n justify-content: space-between;\n\n padding-block: 12px;\n padding-inline: 16px;\n border-block-end: 1px solid ${cssVar.colorBorderSecondary};\n `,\n\n headerDraggable: css`\n cursor: default;\n user-select: none;\n `,\n\n popup: css`\n pointer-events: none;\n\n position: fixed;\n z-index: 1001;\n inset: 0;\n\n display: flex;\n align-items: center;\n justify-content: center;\n `,\n\n popupInner: css`\n pointer-events: auto;\n\n position: relative;\n\n display: flex;\n flex-direction: column;\n\n box-sizing: border-box;\n width: calc(100% - 32px);\n max-width: 520px;\n max-height: calc(100dvh - 64px);\n border: 1px solid ${cssVar.colorBorderSecondary};\n border-radius: 12px;\n\n background: ${cssVar.colorBgContainer};\n box-shadow: ${cssVar.boxShadow};\n\n transition:\n transform 150ms cubic-bezier(0.22, 1, 0.36, 1),\n opacity 150ms ease-out;\n\n &[data-starting-style],\n &[data-ending-style] {\n transform: scale(0.96) translateY(4px);\n opacity: 0;\n }\n `,\n\n title: css`\n margin: 0;\n\n font-size: 16px;\n font-weight: 600;\n line-height: 1.5;\n color: ${cssVar.colorText};\n `,\n\n buttonBase: css`\n cursor: pointer;\n\n display: inline-flex;\n gap: 6px;\n align-items: center;\n justify-content: center;\n\n height: 36px;\n padding-block: 0;\n padding-inline: 16px;\n border: 1px solid ${cssVar.colorBorder};\n border-radius: ${cssVar.borderRadiusSM};\n\n font-size: 14px;\n font-weight: 500;\n line-height: 1;\n\n transition: all 150ms ease-out;\n\n &:focus-visible {\n outline: 2px solid ${cssVar.colorPrimaryBorder};\n outline-offset: 1px;\n }\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n }\n `,\n\n cancelButton: css`\n color: ${cssVar.colorText};\n background: ${cssVar.colorBgContainer};\n\n &:hover:not(:disabled) {\n border-color: ${cssVar.colorPrimaryBorder};\n color: ${cssVar.colorPrimaryText};\n }\n `,\n\n loadingSpinner: css`\n @keyframes modal-spin {\n to {\n transform: rotate(360deg);\n }\n }\n\n display: inline-block;\n\n width: 14px;\n height: 14px;\n border: 2px solid currentcolor;\n border-block-start-color: transparent;\n border-radius: 50%;\n\n animation: modal-spin 0.6s linear infinite;\n `,\n\n dangerOkButton: css`\n border-color: ${cssVar.colorError};\n color: #fff;\n background: ${cssVar.colorError};\n\n &:hover:not(:disabled) {\n border-color: ${cssVar.colorErrorHover};\n background: ${cssVar.colorErrorHover};\n }\n\n &:active:not(:disabled) {\n border-color: ${cssVar.colorErrorActive};\n background: ${cssVar.colorErrorActive};\n }\n `,\n\n fullscreenPopupInner: css`\n width: 100% !important;\n max-width: 100% !important;\n height: 100dvh !important;\n max-height: 100dvh !important;\n border: none;\n border-radius: 0;\n `,\n\n fullscreenToggle: css`\n cursor: pointer;\n\n display: flex;\n align-items: center;\n justify-content: center;\n\n width: 28px;\n height: 28px;\n padding: 0;\n border: none;\n border-radius: ${cssVar.borderRadiusSM};\n\n color: ${cssVar.colorTextTertiary};\n\n background: transparent;\n\n transition: all 150ms ease-out;\n\n &:hover {\n color: ${cssVar.colorText};\n background: ${cssVar.colorFillSecondary};\n }\n\n &:focus-visible {\n outline: 2px solid ${cssVar.colorPrimaryBorder};\n outline-offset: 1px;\n }\n `,\n\n headerActions: css`\n display: flex;\n gap: 4px;\n align-items: center;\n margin-inline-end: -4px;\n `,\n\n okButton: css`\n border-color: ${cssVar.colorPrimary};\n color: #fff;\n background: ${cssVar.colorPrimary};\n\n &:hover:not(:disabled) {\n border-color: ${cssVar.colorPrimaryHover};\n background: ${cssVar.colorPrimaryHover};\n }\n\n &:active:not(:disabled) {\n border-color: ${cssVar.colorPrimaryActive};\n background: ${cssVar.colorPrimaryActive};\n }\n `,\n\n denyAnimation: css`\n @keyframes modal-deny {\n 0% {\n outline-color: ${cssVar.colorPrimary};\n outline-offset: 0;\n }\n\n 100% {\n outline-color: transparent;\n outline-offset: 4px;\n }\n }\n\n outline: 2px solid transparent;\n animation: modal-deny 400ms ease-out;\n `,\n\n viewport: css`\n position: fixed;\n z-index: 1000;\n inset: 0;\n overflow: auto;\n `,\n}));\n"],"mappings":";;AAEA,MAAa,SAAS,oBAAoB,EAAE,KAAK,cAAc;CAC7D,UAAU,GAAG;;;;;qCAKsB,OAAO,iBAAiB;;;;;;;;;CAU3D,OAAO,GAAG;;;;;;;;;;;;;;;qBAeS,OAAO,eAAe;;aAE9B,OAAO,kBAAkB;;;;;;;eAOvB,OAAO,UAAU;oBACZ,OAAO,mBAAmB;;;;2BAInB,OAAO,mBAAmB;;;;CAKnD,aAAa,GAAG;;;;;;;;;;;qBAWG,OAAO,eAAe;;aAE9B,OAAO,kBAAkB;;;;;;;eAOvB,OAAO,UAAU;oBACZ,OAAO,mBAAmB;;;;2BAInB,OAAO,mBAAmB;;;;CAKnD,SAAS,GAAG;;;;;CAMZ,QAAQ,GAAG;;;;;;;;oCAQuB,OAAO,qBAAqB;;CAG9D,QAAQ,GAAG;;;;;;;kCAOqB,OAAO,qBAAqB;;CAG5D,iBAAiB,GAAG;;;;CAKpB,OAAO,GAAG;;;;;;;;;;;CAYV,YAAY,GAAG
|
|
1
|
+
{"version":3,"file":"style.mjs","names":[],"sources":["../../../src/base-ui/Modal/style.ts"],"sourcesContent":["import { createStaticStyles } from 'antd-style';\n\nexport const styles = createStaticStyles(({ css, cssVar }) => ({\n backdrop: css`\n position: fixed;\n z-index: 1000;\n inset: 0;\n\n background: color-mix(in srgb, ${cssVar.colorBgContainer} 60%, transparent);\n\n transition: opacity 150ms ease-out;\n\n &[data-starting-style],\n &[data-ending-style] {\n opacity: 0;\n }\n `,\n\n close: css`\n cursor: pointer;\n\n position: absolute;\n inset-block-start: 12px;\n inset-inline-end: 12px;\n\n display: flex;\n align-items: center;\n justify-content: center;\n\n width: 32px;\n height: 32px;\n padding: 0;\n border: none;\n border-radius: ${cssVar.borderRadiusSM};\n\n color: ${cssVar.colorTextTertiary};\n\n background: transparent;\n\n transition: all 150ms ease-out;\n\n &:hover {\n color: ${cssVar.colorText};\n background: ${cssVar.colorFillSecondary};\n }\n\n &:focus-visible {\n outline: 2px solid ${cssVar.colorPrimaryBorder};\n outline-offset: 1px;\n }\n `,\n\n closeInline: css`\n cursor: pointer;\n\n display: flex;\n align-items: center;\n justify-content: center;\n\n width: 32px;\n height: 32px;\n padding: 0;\n border: none;\n border-radius: ${cssVar.borderRadiusSM};\n\n color: ${cssVar.colorTextTertiary};\n\n background: transparent;\n\n transition: all 150ms ease-out;\n\n &:hover {\n color: ${cssVar.colorText};\n background: ${cssVar.colorFillSecondary};\n }\n\n &:focus-visible {\n outline: 2px solid ${cssVar.colorPrimaryBorder};\n outline-offset: 1px;\n }\n `,\n\n content: css`\n overflow: hidden auto;\n padding-block: 12px;\n padding-inline: 16px;\n `,\n\n footer: css`\n display: flex;\n gap: 8px;\n align-items: center;\n justify-content: flex-end;\n\n padding-block: 12px;\n padding-inline: 16px;\n border-block-start: 1px solid ${cssVar.colorBorderSecondary};\n `,\n\n header: css`\n display: flex;\n align-items: center;\n justify-content: space-between;\n\n padding-block: 12px;\n padding-inline: 16px;\n border-block-end: 1px solid ${cssVar.colorBorderSecondary};\n `,\n\n headerDraggable: css`\n cursor: default;\n user-select: none;\n `,\n\n popup: css`\n pointer-events: none;\n\n position: fixed;\n z-index: 1001;\n inset: 0;\n\n display: flex;\n align-items: center;\n justify-content: center;\n `,\n\n popupInner: css`\n pointer-events: auto;\n\n position: relative;\n\n overflow: hidden;\n display: flex;\n flex-direction: column;\n\n box-sizing: border-box;\n width: calc(100% - 32px);\n max-width: 520px;\n max-height: calc(100dvh - 64px);\n border: 1px solid ${cssVar.colorBorderSecondary};\n border-radius: 12px;\n\n background: ${cssVar.colorBgContainer};\n box-shadow: ${cssVar.boxShadow};\n\n transition:\n transform 150ms cubic-bezier(0.22, 1, 0.36, 1),\n opacity 150ms ease-out;\n\n &[data-starting-style],\n &[data-ending-style] {\n transform: scale(0.96) translateY(4px);\n opacity: 0;\n }\n `,\n\n title: css`\n margin: 0;\n\n font-size: 16px;\n font-weight: 600;\n line-height: 1.5;\n color: ${cssVar.colorText};\n `,\n\n buttonBase: css`\n cursor: pointer;\n\n display: inline-flex;\n gap: 6px;\n align-items: center;\n justify-content: center;\n\n height: 36px;\n padding-block: 0;\n padding-inline: 16px;\n border: 1px solid ${cssVar.colorBorder};\n border-radius: ${cssVar.borderRadiusSM};\n\n font-size: 14px;\n font-weight: 500;\n line-height: 1;\n\n transition: all 150ms ease-out;\n\n &:focus-visible {\n outline: 2px solid ${cssVar.colorPrimaryBorder};\n outline-offset: 1px;\n }\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n }\n `,\n\n cancelButton: css`\n color: ${cssVar.colorText};\n background: ${cssVar.colorBgContainer};\n\n &:hover:not(:disabled) {\n border-color: ${cssVar.colorPrimaryBorder};\n color: ${cssVar.colorPrimaryText};\n }\n `,\n\n loadingSpinner: css`\n @keyframes modal-spin {\n to {\n transform: rotate(360deg);\n }\n }\n\n display: inline-block;\n\n width: 14px;\n height: 14px;\n border: 2px solid currentcolor;\n border-block-start-color: transparent;\n border-radius: 50%;\n\n animation: modal-spin 0.6s linear infinite;\n `,\n\n dangerOkButton: css`\n border-color: ${cssVar.colorError};\n color: #fff;\n background: ${cssVar.colorError};\n\n &:hover:not(:disabled) {\n border-color: ${cssVar.colorErrorHover};\n background: ${cssVar.colorErrorHover};\n }\n\n &:active:not(:disabled) {\n border-color: ${cssVar.colorErrorActive};\n background: ${cssVar.colorErrorActive};\n }\n `,\n\n fullscreenPopupInner: css`\n width: 100% !important;\n max-width: 100% !important;\n height: 100dvh !important;\n max-height: 100dvh !important;\n border: none;\n border-radius: 0;\n `,\n\n fullscreenToggle: css`\n cursor: pointer;\n\n display: flex;\n align-items: center;\n justify-content: center;\n\n width: 28px;\n height: 28px;\n padding: 0;\n border: none;\n border-radius: ${cssVar.borderRadiusSM};\n\n color: ${cssVar.colorTextTertiary};\n\n background: transparent;\n\n transition: all 150ms ease-out;\n\n &:hover {\n color: ${cssVar.colorText};\n background: ${cssVar.colorFillSecondary};\n }\n\n &:focus-visible {\n outline: 2px solid ${cssVar.colorPrimaryBorder};\n outline-offset: 1px;\n }\n `,\n\n headerActions: css`\n display: flex;\n gap: 4px;\n align-items: center;\n margin-inline-end: -4px;\n `,\n\n okButton: css`\n border-color: ${cssVar.colorPrimary};\n color: #fff;\n background: ${cssVar.colorPrimary};\n\n &:hover:not(:disabled) {\n border-color: ${cssVar.colorPrimaryHover};\n background: ${cssVar.colorPrimaryHover};\n }\n\n &:active:not(:disabled) {\n border-color: ${cssVar.colorPrimaryActive};\n background: ${cssVar.colorPrimaryActive};\n }\n `,\n\n denyAnimation: css`\n @keyframes modal-deny {\n 0% {\n outline-color: ${cssVar.colorPrimary};\n outline-offset: 0;\n }\n\n 100% {\n outline-color: transparent;\n outline-offset: 4px;\n }\n }\n\n outline: 2px solid transparent;\n animation: modal-deny 400ms ease-out;\n `,\n\n viewport: css`\n position: fixed;\n z-index: 1000;\n inset: 0;\n overflow: auto;\n `,\n}));\n"],"mappings":";;AAEA,MAAa,SAAS,oBAAoB,EAAE,KAAK,cAAc;CAC7D,UAAU,GAAG;;;;;qCAKsB,OAAO,iBAAiB;;;;;;;;;CAU3D,OAAO,GAAG;;;;;;;;;;;;;;;qBAeS,OAAO,eAAe;;aAE9B,OAAO,kBAAkB;;;;;;;eAOvB,OAAO,UAAU;oBACZ,OAAO,mBAAmB;;;;2BAInB,OAAO,mBAAmB;;;;CAKnD,aAAa,GAAG;;;;;;;;;;;qBAWG,OAAO,eAAe;;aAE9B,OAAO,kBAAkB;;;;;;;eAOvB,OAAO,UAAU;oBACZ,OAAO,mBAAmB;;;;2BAInB,OAAO,mBAAmB;;;;CAKnD,SAAS,GAAG;;;;;CAMZ,QAAQ,GAAG;;;;;;;;oCAQuB,OAAO,qBAAqB;;CAG9D,QAAQ,GAAG;;;;;;;kCAOqB,OAAO,qBAAqB;;CAG5D,iBAAiB,GAAG;;;;CAKpB,OAAO,GAAG;;;;;;;;;;;CAYV,YAAY,GAAG;;;;;;;;;;;;;wBAaO,OAAO,qBAAqB;;;kBAGlC,OAAO,iBAAiB;kBACxB,OAAO,UAAU;;;;;;;;;;;;CAajC,OAAO,GAAG;;;;;;aAMC,OAAO,UAAU;;CAG5B,YAAY,GAAG;;;;;;;;;;;wBAWO,OAAO,YAAY;qBACtB,OAAO,eAAe;;;;;;;;;2BAShB,OAAO,mBAAmB;;;;;;;;;CAUnD,cAAc,GAAG;aACN,OAAO,UAAU;kBACZ,OAAO,iBAAiB;;;sBAGpB,OAAO,mBAAmB;eACjC,OAAO,iBAAiB;;;CAIrC,gBAAgB,GAAG;;;;;;;;;;;;;;;;;CAkBnB,gBAAgB,GAAG;oBACD,OAAO,WAAW;;kBAEpB,OAAO,WAAW;;;sBAGd,OAAO,gBAAgB;oBACzB,OAAO,gBAAgB;;;;sBAIrB,OAAO,iBAAiB;oBAC1B,OAAO,iBAAiB;;;CAI1C,sBAAsB,GAAG;;;;;;;;CASzB,kBAAkB,GAAG;;;;;;;;;;;qBAWF,OAAO,eAAe;;aAE9B,OAAO,kBAAkB;;;;;;;eAOvB,OAAO,UAAU;oBACZ,OAAO,mBAAmB;;;;2BAInB,OAAO,mBAAmB;;;;CAKnD,eAAe,GAAG;;;;;;CAOlB,UAAU,GAAG;oBACK,OAAO,aAAa;;kBAEtB,OAAO,aAAa;;;sBAGhB,OAAO,kBAAkB;oBAC3B,OAAO,kBAAkB;;;;sBAIvB,OAAO,mBAAmB;oBAC5B,OAAO,mBAAmB;;;CAI5C,eAAe,GAAG;;;yBAGK,OAAO,aAAa;;;;;;;;;;;;;CAc3C,UAAU,GAAG;;;;;;CAMd,EAAE"}
|
|
@@ -4,7 +4,7 @@ import { rootVariants, styles, thumbVariants } from "./style.mjs";
|
|
|
4
4
|
import { createContext, use, useMemo, useRef, useState } from "react";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
import { cx } from "antd-style";
|
|
7
|
-
import
|
|
7
|
+
import useControlledState from "use-merge-value";
|
|
8
8
|
import { Switch } from "@base-ui/react/switch";
|
|
9
9
|
//#region src/base-ui/Switch/atoms.tsx
|
|
10
10
|
const SwitchContext = createContext(null);
|
|
@@ -17,7 +17,7 @@ const SwitchRoot = ({ checked, className, defaultChecked, onCheckedChange, onCli
|
|
|
17
17
|
const Motion = useMotionComponent();
|
|
18
18
|
const [isPressed, setIsPressed] = useState(false);
|
|
19
19
|
const lastEventRef = useRef(null);
|
|
20
|
-
const [isChecked, setIsChecked] =
|
|
20
|
+
const [isChecked, setIsChecked] = useControlledState(defaultChecked ?? false, {
|
|
21
21
|
defaultValue: defaultChecked,
|
|
22
22
|
onChange: (value) => {
|
|
23
23
|
if (lastEventRef.current) onCheckedChange?.(value, lastEventRef.current);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"atoms.mjs","names":[
|
|
1
|
+
{"version":3,"file":"atoms.mjs","names":[],"sources":["../../../src/base-ui/Switch/atoms.tsx"],"sourcesContent":["'use client';\n\nimport { Switch } from '@base-ui/react/switch';\nimport { cx } from 'antd-style';\nimport type { KeyboardEvent, MouseEvent } from 'react';\nimport { createContext, use, useMemo, useRef, useState } from 'react';\nimport useControlledState from 'use-merge-value';\n\nimport { useMotionComponent } from '@/MotionProvider';\n\nimport { rootVariants, styles, thumbVariants } from './style';\nimport type {\n SwitchChangeEventHandler,\n SwitchContextType,\n SwitchIconPosition,\n SwitchIconProps,\n SwitchRootProps,\n SwitchThumbProps,\n} from './type';\n\nconst SwitchContext = createContext<SwitchContextType | null>(null);\n\nexport const useSwitchContext = () => {\n const context = use(SwitchContext);\n if (!context) {\n throw new Error('useSwitchContext must be used within a SwitchRoot');\n }\n return context;\n};\n\ntype SwitchRootInternalProps = Omit<SwitchRootProps, 'onCheckedChange' | 'onClick'> & {\n onCheckedChange?: SwitchChangeEventHandler;\n onClick?: SwitchChangeEventHandler;\n};\n\nexport const SwitchRoot = ({\n checked,\n className,\n defaultChecked,\n onCheckedChange,\n onClick,\n size = 'default',\n children,\n disabled,\n readOnly,\n required,\n inputRef,\n id,\n name,\n ...rest\n}: SwitchRootInternalProps) => {\n const Motion = useMotionComponent();\n const [isPressed, setIsPressed] = useState(false);\n const lastEventRef = useRef<MouseEvent<HTMLButtonElement> | KeyboardEvent<HTMLButtonElement>>(\n null,\n );\n\n const [isChecked, setIsChecked] = useControlledState(defaultChecked ?? false, {\n defaultValue: defaultChecked,\n onChange: (value: boolean) => {\n if (lastEventRef.current) {\n onCheckedChange?.(value, lastEventRef.current);\n }\n },\n value: checked,\n });\n\n const baseClassName = rootVariants({ size });\n\n const contextValue = useMemo(\n () => ({\n isChecked: Boolean(isChecked),\n isPressed,\n setIsChecked: (value: boolean) => setIsChecked(value),\n setIsPressed,\n }),\n [isChecked, isPressed, setIsChecked],\n );\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>) => {\n lastEventRef.current = event;\n onClick?.(!isChecked, event);\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {\n if (event.key === 'Enter' || event.key === ' ') {\n lastEventRef.current = event;\n }\n (rest as any).onKeyDown?.(event);\n };\n\n return (\n <SwitchContext value={contextValue}>\n <Switch.Root\n checked={isChecked}\n defaultChecked={defaultChecked}\n disabled={disabled}\n id={id}\n inputRef={inputRef}\n name={name}\n readOnly={readOnly}\n required={required}\n render={\n <Motion.button\n {...rest}\n className={cx(baseClassName, className)}\n initial={false}\n whileTap=\"tap\"\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n onTap={() => setIsPressed(false)}\n onTapCancel={() => setIsPressed(false)}\n onTapStart={() => setIsPressed(true)}\n />\n }\n onCheckedChange={setIsChecked}\n >\n {children}\n </Switch.Root>\n </SwitchContext>\n );\n};\n\nSwitchRoot.displayName = 'SwitchRoot';\n\nexport const SwitchThumb = ({\n className,\n pressedAnimation,\n size = 'default',\n transition = { damping: 25, stiffness: 300, type: 'spring' },\n children,\n ...rest\n}: SwitchThumbProps) => {\n const Motion = useMotionComponent();\n const { isPressed } = useSwitchContext();\n const baseClassName = thumbVariants({ size });\n\n const defaultPressedAnimation = {\n width: size === 'small' ? 16 : 22,\n };\n\n return (\n <Switch.Thumb\n render={\n <Motion.span\n layout\n animate={isPressed ? pressedAnimation || defaultPressedAnimation : undefined}\n className={cx(baseClassName, className)}\n transition={transition}\n {...rest}\n >\n {children}\n </Motion.span>\n }\n />\n );\n};\n\nSwitchThumb.displayName = 'SwitchThumb';\n\nconst getIconPositionClass = (position: SwitchIconPosition, size: 'default' | 'small') => {\n if (position === 'thumb') return styles.iconThumb;\n if (position === 'left') return size === 'small' ? styles.iconLeftSmall : styles.iconLeft;\n return size === 'small' ? styles.iconRightSmall : styles.iconRight;\n};\n\nexport const SwitchIcon = ({\n children,\n className,\n position,\n transition = { bounce: 0, type: 'spring' },\n ...rest\n}: SwitchIconProps & { children?: React.ReactNode; size?: 'default' | 'small' }) => {\n const Motion = useMotionComponent();\n const { isChecked } = useSwitchContext();\n const size = (rest as any).size || 'default';\n\n const isAnimated = useMemo(() => {\n if (position === 'right') return !isChecked;\n if (position === 'left') return isChecked;\n if (position === 'thumb') return true;\n return false;\n }, [position, isChecked]);\n\n const positionClass = getIconPositionClass(position, size);\n\n return (\n <Motion.span\n animate={isAnimated ? { opacity: 1, scale: 1 } : { opacity: 0, scale: 0 }}\n className={cx(styles.icon, positionClass, className)}\n transition={transition}\n {...rest}\n >\n {children}\n </Motion.span>\n );\n};\n\nSwitchIcon.displayName = 'SwitchIcon';\n\nexport { styles as switchStyles } from './style';\n"],"mappings":"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"}
|
|
@@ -5,14 +5,14 @@ import MessageModal from "../MessageModal/MessageModal.mjs";
|
|
|
5
5
|
import { memo } from "react";
|
|
6
6
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { cx } from "antd-style";
|
|
8
|
-
import
|
|
8
|
+
import useControlledState from "use-merge-value";
|
|
9
9
|
//#region src/chat/EditableMessage/EditableMessage.tsx
|
|
10
10
|
const EditableMessage = memo(({ value, onChange, classNames = {}, onEditingChange, editing, openModal, onOpenChange, placeholder, showEditWhenEmpty = false, styles: customStyles, className, style, height, variant, editButtonSize, text, fullFeaturedCodeBlock, model, fontSize, language = "markdown", markdownProps }) => {
|
|
11
|
-
const [isEdit, setTyping] =
|
|
11
|
+
const [isEdit, setTyping] = useControlledState(false, {
|
|
12
12
|
onChange: onEditingChange,
|
|
13
13
|
value: editing
|
|
14
14
|
});
|
|
15
|
-
const [expand, setExpand] =
|
|
15
|
+
const [expand, setExpand] = useControlledState(false, {
|
|
16
16
|
onChange: onOpenChange,
|
|
17
17
|
value: openModal
|
|
18
18
|
});
|