@tiny-codes/react-easy 1.7.5 → 1.7.6
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/CHANGELOG.md +16 -0
- package/es/components/Animation/Pulse/index.d.ts +18 -2
- package/es/components/Animation/Pulse/index.js +1 -1
- package/es/components/Animation/Pulse/index.js.map +1 -1
- package/es/components/BreakLines/index.d.ts +7 -7
- package/es/components/BreakLines/index.js.map +1 -1
- package/es/components/ColumnSetting/index.d.ts +7 -2
- package/es/components/ColumnSetting/index.js +27 -16
- package/es/components/ColumnSetting/index.js.map +1 -1
- package/es/components/ConfigProvider/context.d.ts +2 -0
- package/es/components/ConfigProvider/context.js +1 -1
- package/es/components/ConfigProvider/context.js.map +1 -1
- package/es/components/ConfigProvider/index.d.ts +17 -1
- package/es/components/ConfigProvider/index.js +15 -8
- package/es/components/ConfigProvider/index.js.map +1 -1
- package/es/components/ContextMenu/index.d.ts +16 -0
- package/es/components/ContextMenu/index.js.map +1 -1
- package/es/components/EditableText/form.d.ts +4 -0
- package/es/components/EditableText/form.js.map +1 -1
- package/es/components/EditableText/index.d.ts +6 -2
- package/es/components/EditableText/index.js.map +1 -1
- package/es/components/FloatDrawer/index.d.ts +10 -0
- package/es/components/FloatDrawer/index.js.map +1 -1
- package/es/components/OverflowTags/index.d.ts +2 -0
- package/es/components/OverflowTags/index.js.map +1 -1
- package/es/hooks/index.d.ts +2 -0
- package/es/hooks/index.js +2 -0
- package/es/hooks/index.js.map +1 -1
- package/es/hooks/useLocalStorage.d.ts +16 -0
- package/es/hooks/useLocalStorage.js +115 -0
- package/es/hooks/useLocalStorage.js.map +1 -0
- package/es/hooks/useMovable.js +2 -2
- package/es/hooks/useMovable.js.map +1 -1
- package/es/locales/index.js +1 -1
- package/es/locales/index.js.map +1 -1
- package/es/utils/math.d.ts +11 -3
- package/es/utils/math.js +62 -9
- package/es/utils/math.js.map +1 -1
- package/lib/components/Animation/Pulse/index.d.ts +18 -2
- package/lib/components/Animation/Pulse/index.js +0 -5
- package/lib/components/Animation/Pulse/index.js.map +2 -2
- package/lib/components/BreakLines/index.d.ts +7 -7
- package/lib/components/BreakLines/index.js.map +1 -1
- package/lib/components/ColumnSetting/index.d.ts +7 -2
- package/lib/components/ColumnSetting/index.js +18 -14
- package/lib/components/ColumnSetting/index.js.map +3 -3
- package/lib/components/ConfigProvider/context.d.ts +2 -0
- package/lib/components/ConfigProvider/context.js +1 -1
- package/lib/components/ConfigProvider/context.js.map +2 -2
- package/lib/components/ConfigProvider/index.d.ts +17 -1
- package/lib/components/ConfigProvider/index.js +13 -6
- package/lib/components/ConfigProvider/index.js.map +2 -2
- package/lib/components/ContextMenu/index.d.ts +16 -0
- package/lib/components/ContextMenu/index.js.map +2 -2
- package/lib/components/EditableText/form.d.ts +4 -0
- package/lib/components/EditableText/form.js.map +2 -2
- package/lib/components/EditableText/index.d.ts +6 -2
- package/lib/components/EditableText/index.js.map +2 -2
- package/lib/components/FloatDrawer/index.d.ts +10 -0
- package/lib/components/FloatDrawer/index.js.map +2 -2
- package/lib/components/OverflowTags/index.d.ts +2 -0
- package/lib/components/OverflowTags/index.js.map +2 -2
- package/lib/hooks/index.d.ts +2 -0
- package/lib/hooks/index.js +5 -0
- package/lib/hooks/index.js.map +2 -2
- package/lib/hooks/useLocalStorage.d.ts +16 -0
- package/lib/hooks/useLocalStorage.js +120 -0
- package/lib/hooks/useLocalStorage.js.map +7 -0
- package/lib/hooks/useMovable.js +2 -2
- package/lib/hooks/useMovable.js.map +3 -3
- package/lib/locales/index.js +1 -1
- package/lib/locales/index.js.map +1 -1
- package/lib/utils/math.d.ts +11 -3
- package/lib/utils/math.js +43 -8
- package/lib/utils/math.js.map +3 -3
- package/package.json +14 -7
|
@@ -2,6 +2,10 @@ import type { CSSProperties, ReactElement } from 'react';
|
|
|
2
2
|
import type { ButtonProps, FormItemProps, FormProps, InputProps, SpaceProps } from 'antd';
|
|
3
3
|
import type { TextAreaProps } from 'antd/es/input';
|
|
4
4
|
export interface EditableFormProps<V, IT extends 'Input' | 'TextArea' | RenderInputInterface> {
|
|
5
|
+
/**
|
|
6
|
+
* - **EN:** Custom prefix for the component's CSS class.
|
|
7
|
+
* - **CN:** 组件的自定义 CSS 类前缀。
|
|
8
|
+
*/
|
|
5
9
|
prefixCls?: string;
|
|
6
10
|
/**
|
|
7
11
|
* - **EN:** The value to edit
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/EditableText/form.tsx"],
|
|
4
|
-
"sourcesContent": ["import type { CSSProperties, ReactElement } from 'react';\nimport { useContext, useEffect, useRef, useState } from 'react';\nimport type { ButtonProps, FormItemProps, FormProps, InputProps, SpaceProps } from 'antd';\nimport { Button, ConfigProvider, Form, Input, Space } from 'antd';\nimport type { InputRef, TextAreaProps } from 'antd/es/input';\nimport names from 'classnames';\nimport { CheckSquareFilled, CloseSquareFilled } from '@ant-design/icons';\nimport useT from '../../hooks/useT';\n\nconst defaultInputActionGap = 8;\n// eslint-disable-next-line @typescript-eslint/ban-types\nexport interface EditableFormProps<V, IT extends 'Input' | 'TextArea' | RenderInputInterface> {\n prefixCls?: string;\n /**\n * - **EN:** The value to edit\n * - **CN:** 编辑的值\n */\n value: V | undefined;\n /**\n * - **EN:** Whether the text is required when editing, default is `true`, if `formItemProps.rules`\n * is set, it will override this value\n * - **CN:** 编辑文本时是否必填,默认值为`true`,如果设置了`formItemProps.rules`,将会覆盖此值\n *\n * @default true\n */\n required?: boolean;\n /**\n * - **EN:** Form properties\n * - **CN:** 表单属性\n */\n formProps?: FormProps;\n /**\n * - **EN:** Form item properties\n * - **CN:** 表单项属性\n */\n formItemProps?: FormItemProps;\n /**\n * - **EN:** Custom component type for rendering the input box\n * - **CN:** 自定义设置渲染输入框的组件类型\n *\n * @default 'Input'\n */\n inputComp?: IT;\n /**\n * - **EN:** Input component properties\n * - **CN:** 输入框组件属性\n */\n inputProps?: IT extends 'TextArea' ? TextAreaProps : IT extends 'Input' ? InputProps : never;\n /**\n * - **EN:** Whether to display as a block-level element, with width 100%\n * - **CN:** 是否显示为块级元素,宽度100%\n *\n * @default false\n */\n block?: boolean;\n /**\n * - **EN:** Semantic class names\n * - **CN:** 语义化类名\n */\n classNames?: {\n /**\n * - **EN:** Edit mode parent container class name\n * - **CN:** 编辑模式父容器类名\n */\n wrapper?: string;\n /**\n * - **EN:** Submit button class name\n * - **CN:** 提交按钮类名\n */\n submitButton?: string;\n /**\n * - **EN:** Cancel button class name\n * - **CN:** 取消按钮类名\n */\n cancelButton?: string;\n /**\n * - **EN:** Submit button icon class name\n * - **CN:** 提交按钮图标类名\n */\n submitIcon?: string;\n /**\n * - **EN:** Cancel button icon class name\n * - **CN:** 取消按钮图标类名\n */\n cancelIcon?: string;\n };\n /**\n * - **EN:** Semantic styles\n * - **CN:** 语义化样式\n */\n styles?: {\n /**\n * - **EN:** Edit mode parent container style\n * - **CN:** 编辑模式父容器样式\n */\n wrapper?: CSSProperties;\n /**\n * - **EN:** Submit button style\n * - **CN:** 提交按钮样式\n */\n submitButton?: CSSProperties;\n /**\n * - **EN:** Cancel button style\n * - **CN:** 取消按钮样式\n */\n cancelButton?: CSSProperties;\n /**\n * - **EN:** Submit button icon style\n * - **CN:** 提交按钮图标样式\n */\n submitIcon?: CSSProperties;\n /**\n * - **EN:** Cancel button icon style\n * - **CN:** 取消按钮图标样式\n */\n cancelIcon?: CSSProperties;\n };\n /**\n * **EN:** Submit and cancel action button alignment\n *\n * **CN:** 提交、取消操作按钮的对齐方式\n *\n * - `start` - align to the top | 顶部对齐\n * - `center` - align to the center | 居中对齐\n * - `end` - align to the bottom | 底部对齐\n *\n * @default 'center'\n */\n actionAlign?: SpaceProps['align'];\n /**\n * - **EN:** Submit button properties\n * - **CN:** 提交操作按钮属性\n */\n submitProps?: ButtonProps;\n /**\n * - **EN:** Cancel button properties\n * - **CN:** 取消操作按钮属性\n */\n cancelProps?: ButtonProps;\n /**\n * - **EN:** Confirm button click event, supports asynchronous operations\n * - **CN:** 确认按钮点击事件,支持异步操作\n */\n onOk: (value: V | undefined) => void | Promise<void>;\n /**\n * - **EN:** Cancel button click event, supports asynchronous operations\n * - **CN:** 取消按钮点击事件,支持异步操作\n */\n onCancel?: () => void | Promise<void>;\n}\n\n// eslint-disable-next-line @typescript-eslint/ban-types\nconst EditableTextForm = <V, IT extends 'Input' | 'TextArea' | RenderInputInterface>(\n props: EditableFormProps<V, IT>\n) => {\n const {\n prefixCls,\n value,\n required = true,\n classNames,\n styles: styleNames,\n inputComp = 'Input' as IT,\n block: blockInProps = false,\n formProps,\n formItemProps,\n inputProps,\n actionAlign = 'center',\n submitProps,\n cancelProps,\n onOk,\n onCancel,\n } = props;\n const t = useT();\n const { getPrefixCls: getAntPrefixCls } = useContext(ConfigProvider.ConfigContext);\n const [form] = Form.useForm<{ value: V | undefined }>();\n const wrapperRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<InputRef>(null);\n const supportAutoScale = inputComp === 'Input' || inputComp === 'TextArea';\n const detectorRef = useRef<HTMLSpanElement>(null);\n const actionRef = useRef<HTMLDivElement>(null);\n const [inputWidth, setInputWidth] = useState<number>();\n const [visibility, setVisibility] = useState<CSSProperties['visibility']>('hidden');\n const [forceBlock, setForceBlock] = useState(false);\n const [saving, setSaving] = useState(false);\n const inputActionGapRef = useRef<number>(0);\n inputActionGapRef.current =\n pxToNumber(formItemProps?.style?.marginRight ?? formItemProps?.style?.marginInlineEnd) || defaultInputActionGap;\n const block = blockInProps || forceBlock;\n\n // Update form value when `value` prop changes\n useEffect(() => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n form.setFieldsValue({ value: value as any });\n }, [form, value]);\n\n // Detect the width of the text and dynamically scale the input width until it reaches the max width of the container.\n // Only built-in Input and TextArea components are supported.\n useEffect(() => {\n let contentWidth = 0;\n const inputElement = inputRef.current?.input;\n if (detectorRef.current && inputElement && supportAutoScale) {\n const inputStyle = getComputedStyle(inputElement);\n contentWidth =\n detectorRef.current.offsetWidth +\n pxToNumber(inputStyle.paddingLeft) +\n pxToNumber(inputStyle.paddingRight) +\n pxToNumber(inputStyle.borderLeftWidth) +\n pxToNumber(inputStyle.borderRightWidth);\n setInputWidth(contentWidth);\n }\n const actionWidth = actionRef.current?.offsetWidth || 0; // Width of action buttons\n const gap = inputActionGapRef.current; // Gap between input box and action buttons\n setVisibility('visible');\n // Listen to the width of the wrapper container, if the input box width is greater than the wrapper container width,\n const observer = new ResizeObserver(([entity]) => {\n const wrapperWidth = entity.contentRect.width;\n // Width deviation greater than 1px is considered overflow, to avoid misjudgment due to pixel density or scaling\n if (contentWidth + actionWidth + gap - wrapperWidth > 1) {\n setForceBlock(true);\n } else {\n setForceBlock(false);\n }\n });\n if (wrapperRef.current) {\n observer.observe(wrapperRef.current);\n }\n return () => observer.disconnect();\n }, [supportAutoScale]);\n\n // Cancel editing\n const handleCancel = async () => {\n form.resetFields();\n try {\n await onCancel?.();\n } catch (error) {\n console.error(error);\n }\n };\n // Pressing the Escape key to cancel editing\n const handleEscape: InputProps['onKeyUp'] = async (e) => {\n if ((inputComp === 'Input' || inputComp === 'TextArea') && e.key === 'Escape') {\n await handleCancel();\n cancelProps?.onClick?.(e as unknown as React.MouseEvent<HTMLElement>);\n }\n };\n // Submit editing\n const handleSubmit = async (values: { value: V | undefined }) => {\n const { value } = values;\n try {\n setSaving(true);\n await onOk?.(value);\n } catch (error) {\n console.error(error);\n } finally {\n setSaving(false);\n }\n };\n\n const renderInput = () => {\n if (inputComp === 'Input' || inputComp === 'TextArea') {\n const COMP = inputComp === 'Input' ? Input : Input.TextArea;\n return (\n <COMP\n ref={inputRef}\n placeholder={t('components.EditableText.placeholder')}\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n {...(inputProps as any)}\n style={{\n width: block ? undefined : inputWidth, // Width is 100% in block mode, no need to set width\n ...(inputProps && 'style' in inputProps ? inputProps?.style : {}),\n }}\n onPressEnter={() => {\n if (inputComp === 'Input') {\n form.submit();\n }\n }}\n onKeyUp={handleEscape}\n />\n );\n } else {\n return (\n <CustomInput<V>\n {...props}\n render={inputComp}\n submit={async () => {\n const values = await form.validateFields();\n await handleSubmit(values);\n }}\n cancel={handleCancel}\n />\n );\n }\n };\n\n return (\n <div ref={wrapperRef} className={names(`${prefixCls}-form`, classNames?.wrapper)} style={styleNames?.wrapper}>\n <Form\n component=\"div\" // Do not use form tag to avoid conflicts with external form, which may be nested in other forms\n layout=\"inline\"\n form={form}\n initialValues={{ value }}\n autoComplete=\"off\"\n onFinish={handleSubmit}\n {...formProps}\n style={{\n visibility,\n ...formProps?.style,\n }}\n >\n {/* Input box */}\n <Form.Item\n rules={[{ required, message: t('components.EditableText.requiredMsg') }]}\n {...formItemProps}\n className={names(formItemProps?.className, { [`${getAntPrefixCls('form-item-block')}`]: block })}\n style={{\n marginInlineEnd: defaultInputActionGap,\n ...formItemProps?.style,\n }}\n name=\"value\"\n >\n {renderInput()}\n </Form.Item>\n {/* Used to calculate the width of the text content */}\n {supportAutoScale && (\n <span\n ref={detectorRef}\n style={{\n position: 'absolute',\n visibility: 'hidden',\n height: 0,\n whiteSpace: 'pre',\n }}\n >\n {value?.toString()}\n </span>\n )}\n\n {/* Action buttons */}\n <Space ref={actionRef} className={`${prefixCls}-form-btns`} align={actionAlign} size={4}>\n <Button\n type=\"text\"\n disabled={saving}\n loading={saving}\n title={t('components.EditableText.save')}\n icon={<CheckSquareFilled className={classNames?.submitIcon} style={styleNames?.submitIcon} />}\n style={styleNames?.submitButton}\n onClick={() => form.submit()}\n {...submitProps}\n className={names(\n `${prefixCls}-form-btn`,\n `${prefixCls}-form-btn-save`,\n classNames?.submitButton,\n submitProps?.className\n )}\n />\n <Button\n type=\"text\"\n style={styleNames?.cancelButton}\n className={names(`${prefixCls}-form-btn`, `${prefixCls}-form-btn-close`, classNames?.cancelButton)}\n title={t('components.EditableText.cancel')}\n icon={<CloseSquareFilled className={classNames?.cancelIcon} style={styleNames?.cancelIcon} />}\n onClick={async (e) => {\n await handleCancel();\n cancelProps?.onClick?.(e);\n }}\n {...cancelProps}\n />\n </Space>\n </Form>\n </div>\n );\n};\n\nfunction CustomInput<V>(\n props: RenderInputProps<V> & {\n render: RenderInputInterface;\n }\n): ReactElement | null {\n const { render, value, onChange, ...restProps } = props;\n const useInput = render;\n return useInput({ ...restProps, value, onChange });\n}\n\nfunction pxToNumber(px: string | number | null | undefined) {\n return px ? parseFloat(px.toString().replace('px', '')) : 0;\n}\n\nexport type RenderInputInterface = <V>(props: RenderInputProps<V>) => ReactElement | null;\nexport interface RenderInputProps<V>\n extends Omit<EditableFormProps<V, 'Input'>, 'inputComp' | 'inputProps' | 'onOk' | 'onCancel'> {\n onChange?: (value: V | undefined) => void;\n submit(): Promise<void>;\n cancel(): Promise<void>;\n}\n\nexport default EditableTextForm;\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAAwD;AAExD,kBAA2D;AAE3D,wBAAkB;AAClB,mBAAqD;AACrD,kBAAiB;AAEjB,IAAM,wBAAwB;
|
|
4
|
+
"sourcesContent": ["import type { CSSProperties, ReactElement } from 'react';\nimport { useContext, useEffect, useRef, useState } from 'react';\nimport type { ButtonProps, FormItemProps, FormProps, InputProps, SpaceProps } from 'antd';\nimport { Button, ConfigProvider, Form, Input, Space } from 'antd';\nimport type { InputRef, TextAreaProps } from 'antd/es/input';\nimport names from 'classnames';\nimport { CheckSquareFilled, CloseSquareFilled } from '@ant-design/icons';\nimport useT from '../../hooks/useT';\n\nconst defaultInputActionGap = 8;\n// eslint-disable-next-line @typescript-eslint/ban-types\nexport interface EditableFormProps<V, IT extends 'Input' | 'TextArea' | RenderInputInterface> {\n /**\n * - **EN:** Custom prefix for the component's CSS class.\n * - **CN:** 组件的自定义 CSS 类前缀。\n */\n prefixCls?: string;\n /**\n * - **EN:** The value to edit\n * - **CN:** 编辑的值\n */\n value: V | undefined;\n /**\n * - **EN:** Whether the text is required when editing, default is `true`, if `formItemProps.rules`\n * is set, it will override this value\n * - **CN:** 编辑文本时是否必填,默认值为`true`,如果设置了`formItemProps.rules`,将会覆盖此值\n *\n * @default true\n */\n required?: boolean;\n /**\n * - **EN:** Form properties\n * - **CN:** 表单属性\n */\n formProps?: FormProps;\n /**\n * - **EN:** Form item properties\n * - **CN:** 表单项属性\n */\n formItemProps?: FormItemProps;\n /**\n * - **EN:** Custom component type for rendering the input box\n * - **CN:** 自定义设置渲染输入框的组件类型\n *\n * @default 'Input'\n */\n inputComp?: IT;\n /**\n * - **EN:** Input component properties\n * - **CN:** 输入框组件属性\n */\n inputProps?: IT extends 'TextArea' ? TextAreaProps : IT extends 'Input' ? InputProps : never;\n /**\n * - **EN:** Whether to display as a block-level element, with width 100%\n * - **CN:** 是否显示为块级元素,宽度100%\n *\n * @default false\n */\n block?: boolean;\n /**\n * - **EN:** Semantic class names\n * - **CN:** 语义化类名\n */\n classNames?: {\n /**\n * - **EN:** Edit mode parent container class name\n * - **CN:** 编辑模式父容器类名\n */\n wrapper?: string;\n /**\n * - **EN:** Submit button class name\n * - **CN:** 提交按钮类名\n */\n submitButton?: string;\n /**\n * - **EN:** Cancel button class name\n * - **CN:** 取消按钮类名\n */\n cancelButton?: string;\n /**\n * - **EN:** Submit button icon class name\n * - **CN:** 提交按钮图标类名\n */\n submitIcon?: string;\n /**\n * - **EN:** Cancel button icon class name\n * - **CN:** 取消按钮图标类名\n */\n cancelIcon?: string;\n };\n /**\n * - **EN:** Semantic styles\n * - **CN:** 语义化样式\n */\n styles?: {\n /**\n * - **EN:** Edit mode parent container style\n * - **CN:** 编辑模式父容器样式\n */\n wrapper?: CSSProperties;\n /**\n * - **EN:** Submit button style\n * - **CN:** 提交按钮样式\n */\n submitButton?: CSSProperties;\n /**\n * - **EN:** Cancel button style\n * - **CN:** 取消按钮样式\n */\n cancelButton?: CSSProperties;\n /**\n * - **EN:** Submit button icon style\n * - **CN:** 提交按钮图标样式\n */\n submitIcon?: CSSProperties;\n /**\n * - **EN:** Cancel button icon style\n * - **CN:** 取消按钮图标样式\n */\n cancelIcon?: CSSProperties;\n };\n /**\n * **EN:** Submit and cancel action button alignment\n *\n * **CN:** 提交、取消操作按钮的对齐方式\n *\n * - `start` - align to the top | 顶部对齐\n * - `center` - align to the center | 居中对齐\n * - `end` - align to the bottom | 底部对齐\n *\n * @default 'center'\n */\n actionAlign?: SpaceProps['align'];\n /**\n * - **EN:** Submit button properties\n * - **CN:** 提交操作按钮属性\n */\n submitProps?: ButtonProps;\n /**\n * - **EN:** Cancel button properties\n * - **CN:** 取消操作按钮属性\n */\n cancelProps?: ButtonProps;\n /**\n * - **EN:** Confirm button click event, supports asynchronous operations\n * - **CN:** 确认按钮点击事件,支持异步操作\n */\n onOk: (value: V | undefined) => void | Promise<void>;\n /**\n * - **EN:** Cancel button click event, supports asynchronous operations\n * - **CN:** 取消按钮点击事件,支持异步操作\n */\n onCancel?: () => void | Promise<void>;\n}\n\n// eslint-disable-next-line @typescript-eslint/ban-types\nconst EditableTextForm = <V, IT extends 'Input' | 'TextArea' | RenderInputInterface>(\n props: EditableFormProps<V, IT>\n) => {\n const {\n prefixCls,\n value,\n required = true,\n classNames,\n styles: styleNames,\n inputComp = 'Input' as IT,\n block: blockInProps = false,\n formProps,\n formItemProps,\n inputProps,\n actionAlign = 'center',\n submitProps,\n cancelProps,\n onOk,\n onCancel,\n } = props;\n const t = useT();\n const { getPrefixCls: getAntPrefixCls } = useContext(ConfigProvider.ConfigContext);\n const [form] = Form.useForm<{ value: V | undefined }>();\n const wrapperRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<InputRef>(null);\n const supportAutoScale = inputComp === 'Input' || inputComp === 'TextArea';\n const detectorRef = useRef<HTMLSpanElement>(null);\n const actionRef = useRef<HTMLDivElement>(null);\n const [inputWidth, setInputWidth] = useState<number>();\n const [visibility, setVisibility] = useState<CSSProperties['visibility']>('hidden');\n const [forceBlock, setForceBlock] = useState(false);\n const [saving, setSaving] = useState(false);\n const inputActionGapRef = useRef<number>(0);\n inputActionGapRef.current =\n pxToNumber(formItemProps?.style?.marginRight ?? formItemProps?.style?.marginInlineEnd) || defaultInputActionGap;\n const block = blockInProps || forceBlock;\n\n // Update form value when `value` prop changes\n useEffect(() => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n form.setFieldsValue({ value: value as any });\n }, [form, value]);\n\n // Detect the width of the text and dynamically scale the input width until it reaches the max width of the container.\n // Only built-in Input and TextArea components are supported.\n useEffect(() => {\n let contentWidth = 0;\n const inputElement = inputRef.current?.input;\n if (detectorRef.current && inputElement && supportAutoScale) {\n const inputStyle = getComputedStyle(inputElement);\n contentWidth =\n detectorRef.current.offsetWidth +\n pxToNumber(inputStyle.paddingLeft) +\n pxToNumber(inputStyle.paddingRight) +\n pxToNumber(inputStyle.borderLeftWidth) +\n pxToNumber(inputStyle.borderRightWidth);\n setInputWidth(contentWidth);\n }\n const actionWidth = actionRef.current?.offsetWidth || 0; // Width of action buttons\n const gap = inputActionGapRef.current; // Gap between input box and action buttons\n setVisibility('visible');\n // Listen to the width of the wrapper container, if the input box width is greater than the wrapper container width,\n const observer = new ResizeObserver(([entity]) => {\n const wrapperWidth = entity.contentRect.width;\n // Width deviation greater than 1px is considered overflow, to avoid misjudgment due to pixel density or scaling\n if (contentWidth + actionWidth + gap - wrapperWidth > 1) {\n setForceBlock(true);\n } else {\n setForceBlock(false);\n }\n });\n if (wrapperRef.current) {\n observer.observe(wrapperRef.current);\n }\n return () => observer.disconnect();\n }, [supportAutoScale]);\n\n // Cancel editing\n const handleCancel = async () => {\n form.resetFields();\n try {\n await onCancel?.();\n } catch (error) {\n console.error(error);\n }\n };\n // Pressing the Escape key to cancel editing\n const handleEscape: InputProps['onKeyUp'] = async (e) => {\n if ((inputComp === 'Input' || inputComp === 'TextArea') && e.key === 'Escape') {\n await handleCancel();\n cancelProps?.onClick?.(e as unknown as React.MouseEvent<HTMLElement>);\n }\n };\n // Submit editing\n const handleSubmit = async (values: { value: V | undefined }) => {\n const { value } = values;\n try {\n setSaving(true);\n await onOk?.(value);\n } catch (error) {\n console.error(error);\n } finally {\n setSaving(false);\n }\n };\n\n const renderInput = () => {\n if (inputComp === 'Input' || inputComp === 'TextArea') {\n const COMP = inputComp === 'Input' ? Input : Input.TextArea;\n return (\n <COMP\n ref={inputRef}\n placeholder={t('components.EditableText.placeholder')}\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n {...(inputProps as any)}\n style={{\n width: block ? undefined : inputWidth, // Width is 100% in block mode, no need to set width\n ...(inputProps && 'style' in inputProps ? inputProps?.style : {}),\n }}\n onPressEnter={() => {\n if (inputComp === 'Input') {\n form.submit();\n }\n }}\n onKeyUp={handleEscape}\n />\n );\n } else {\n return (\n <CustomInput<V>\n {...props}\n render={inputComp}\n submit={async () => {\n const values = await form.validateFields();\n await handleSubmit(values);\n }}\n cancel={handleCancel}\n />\n );\n }\n };\n\n return (\n <div ref={wrapperRef} className={names(`${prefixCls}-form`, classNames?.wrapper)} style={styleNames?.wrapper}>\n <Form\n component=\"div\" // Do not use form tag to avoid conflicts with external form, which may be nested in other forms\n layout=\"inline\"\n form={form}\n initialValues={{ value }}\n autoComplete=\"off\"\n onFinish={handleSubmit}\n {...formProps}\n style={{\n visibility,\n ...formProps?.style,\n }}\n >\n {/* Input box */}\n <Form.Item\n rules={[{ required, message: t('components.EditableText.requiredMsg') }]}\n {...formItemProps}\n className={names(formItemProps?.className, { [`${getAntPrefixCls('form-item-block')}`]: block })}\n style={{\n marginInlineEnd: defaultInputActionGap,\n ...formItemProps?.style,\n }}\n name=\"value\"\n >\n {renderInput()}\n </Form.Item>\n {/* Used to calculate the width of the text content */}\n {supportAutoScale && (\n <span\n ref={detectorRef}\n style={{\n position: 'absolute',\n visibility: 'hidden',\n height: 0,\n whiteSpace: 'pre',\n }}\n >\n {value?.toString()}\n </span>\n )}\n\n {/* Action buttons */}\n <Space ref={actionRef} className={`${prefixCls}-form-btns`} align={actionAlign} size={4}>\n <Button\n type=\"text\"\n disabled={saving}\n loading={saving}\n title={t('components.EditableText.save')}\n icon={<CheckSquareFilled className={classNames?.submitIcon} style={styleNames?.submitIcon} />}\n style={styleNames?.submitButton}\n onClick={() => form.submit()}\n {...submitProps}\n className={names(\n `${prefixCls}-form-btn`,\n `${prefixCls}-form-btn-save`,\n classNames?.submitButton,\n submitProps?.className\n )}\n />\n <Button\n type=\"text\"\n style={styleNames?.cancelButton}\n className={names(`${prefixCls}-form-btn`, `${prefixCls}-form-btn-close`, classNames?.cancelButton)}\n title={t('components.EditableText.cancel')}\n icon={<CloseSquareFilled className={classNames?.cancelIcon} style={styleNames?.cancelIcon} />}\n onClick={async (e) => {\n await handleCancel();\n cancelProps?.onClick?.(e);\n }}\n {...cancelProps}\n />\n </Space>\n </Form>\n </div>\n );\n};\n\nfunction CustomInput<V>(\n props: RenderInputProps<V> & {\n render: RenderInputInterface;\n }\n): ReactElement | null {\n const { render, value, onChange, ...restProps } = props;\n const useInput = render;\n return useInput({ ...restProps, value, onChange });\n}\n\nfunction pxToNumber(px: string | number | null | undefined) {\n return px ? parseFloat(px.toString().replace('px', '')) : 0;\n}\n\nexport type RenderInputInterface = <V>(props: RenderInputProps<V>) => ReactElement | null;\nexport interface RenderInputProps<V>\n extends Omit<EditableFormProps<V, 'Input'>, 'inputComp' | 'inputProps' | 'onOk' | 'onCancel'> {\n onChange?: (value: V | undefined) => void;\n submit(): Promise<void>;\n cancel(): Promise<void>;\n}\n\nexport default EditableTextForm;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["useT", "_a", "_b", "value", "names"]
|
|
7
7
|
}
|
|
@@ -8,6 +8,10 @@ import type { TitleProps } from 'antd/es/typography/Title';
|
|
|
8
8
|
import { type EditableFormProps, type RenderInputInterface } from './form';
|
|
9
9
|
declare const getEllipsisConfig: (content: ReactNode | undefined) => EllipsisConfig;
|
|
10
10
|
export interface EditableTextProps<V = string, TT extends 'Text' | 'Paragraph' | 'Title' | 'Link' = 'Text', IT extends 'Input' | 'TextArea' | RenderInputInterface = 'Input'> extends Omit<EditableFormProps<V, IT>, 'block'> {
|
|
11
|
+
/**
|
|
12
|
+
* - **EN:** Custom prefix for the component's CSS class.
|
|
13
|
+
* - **CN:** 组件的自定义 CSS 类前缀。
|
|
14
|
+
*/
|
|
11
15
|
prefixCls?: string;
|
|
12
16
|
/**
|
|
13
17
|
* - **EN:** Custom read-only display text, replacing `value` display
|
|
@@ -15,7 +19,7 @@ export interface EditableTextProps<V = string, TT extends 'Text' | 'Paragraph' |
|
|
|
15
19
|
*
|
|
16
20
|
* @default true
|
|
17
21
|
*/
|
|
18
|
-
displayText?: ReactNode | ((value: V | undefined) => ReactNode);
|
|
22
|
+
displayText?: boolean | ReactNode | ((value: V | undefined) => ReactNode);
|
|
19
23
|
/**
|
|
20
24
|
* - **EN:** Another way to customize read-only display text, with higher priority than
|
|
21
25
|
* `displayText`. This method does not support text truncation.
|
|
@@ -114,7 +118,7 @@ export interface EditableTextProps<V = string, TT extends 'Text' | 'Paragraph' |
|
|
|
114
118
|
* - **EN:** Custom component type for rendering the text
|
|
115
119
|
* - **CN:** 自定义设置渲染文本组件的组件类型
|
|
116
120
|
*
|
|
117
|
-
* @default
|
|
121
|
+
* @default Typography.Text
|
|
118
122
|
*/
|
|
119
123
|
textComp?: TT;
|
|
120
124
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/EditableText/index.tsx"],
|
|
4
|
-
"sourcesContent": ["import type { CSSProperties, ReactNode } from 'react';\nimport React, { useContext, useEffect, useMemo, useState } from 'react';\nimport { ConfigProvider, Flex, Typography } from 'antd';\nimport type { EllipsisConfig } from 'antd/es/typography/Base';\nimport type { LinkProps } from 'antd/es/typography/Link';\nimport type { ParagraphProps } from 'antd/es/typography/Paragraph';\nimport type { TextProps } from 'antd/es/typography/Text';\nimport type { TitleProps } from 'antd/es/typography/Title';\nimport names from 'classnames';\nimport { EditOutlined } from '@ant-design/icons';\nimport useT from '../../hooks/useT';\nimport EditableTextForm, { type EditableFormProps, type RenderInputInterface } from './form';\nimport useStyle from './style';\n\nconst getEllipsisConfig = (content: ReactNode | undefined): EllipsisConfig => ({\n tooltip: {\n title: content,\n overlayStyle: { maxWidth: 500 },\n },\n});\n\nexport interface EditableTextProps<\n V = string,\n TT extends 'Text' | 'Paragraph' | 'Title' | 'Link' = 'Text',\n IT extends 'Input' | 'TextArea' | RenderInputInterface = 'Input',\n> extends Omit<EditableFormProps<V, IT>, 'block'> {\n prefixCls?: string;\n /**\n * - **EN:** Custom read-only display text, replacing `value` display\n * - **CN:** 自定义只读显示文本,替代`value`显示\n *\n * @default true\n */\n displayText?: ReactNode | ((value: V | undefined) => ReactNode);\n /**\n * - **EN:** Another way to customize read-only display text, with higher priority than\n * `displayText`. This method does not support text truncation.\n * - **CN:** 另外一种方式自定义只读显示文本,优先级高于`displayText`,这种方式无文本省略效果\n */\n children?: ReactNode;\n /**\n * - **EN:** Whether to allow editing. If set to `false`, the edit button will not be displayed.\n * - **CN:** 是否允许编辑,如果设置为`false`,则不显示编辑按钮\n *\n * @default true\n */\n editable?: boolean;\n /**\n * - **EN:** Whether to open edit mode.\n * - **CN:** 是否打开编辑模式\n *\n * @default false\n */\n editing?: boolean;\n /**\n * - **EN:** Whether to display as a block-level element, with width 100%\n * - **CN:** 是否显示为块级元素,宽度100%\n *\n * @default false\n */\n block?:\n | boolean\n | {\n /**\n * - **EN:** Whether to display as a block-level element in view mode, with width 100%\n * - **CN:** 只读模式是否显示为块级元素,宽度100%\n *\n * @default false\n */\n view?: boolean;\n /**\n * - **EN:** Whether to display as a block-level element in edit mode, with width 100%\n * - **CN:** 编辑模式是否显示为块级元素,宽度100%\n *\n * @default false\n */\n editing?: boolean;\n };\n /**\n * - **EN:** Component container class name.\n * - **CN:** 组件容器类名\n */\n className?: string;\n /**\n * - **EN:** Component container style.\n * - **CN:** 组件容器样式\n */\n style?: CSSProperties;\n /**\n * - **EN:** Semantic class names.\n * - **CN:** 语义化类名\n */\n classNames?: {\n /**\n * - **EN:** Read-only text class name\n * - **CN:** 只读文本类名\n */\n text?: string;\n /**\n * - **EN:** Edit button class name\n * - **CN:** 编辑按钮类名\n */\n editIcon?: string;\n } & EditableFormProps<V, IT>['classNames'];\n /**\n * - **EN:** After saving with `onOk`, the modified value is passed out through `onChange`\n * - **CN:** 在`onOk`保存后,通过`onChange`把修改后的值传递出去\n */\n onChange?: (value: V | undefined) => void;\n /**\n * - **EN:** Event triggered when the editing state changes\n * - **CN:** 编辑状态改变事件\n */\n onEditingChange?: (editing: boolean) => void;\n /**\n * - **EN:** Whether to display as a block-level element, with width 100%\n * - **CN:** 语义化样式\n */\n styles?: {\n /**\n * - **EN:** Read-only text class name\n * - **CN:** 只读文本类名\n */\n text?: CSSProperties;\n /**\n * - **EN:** Edit button style\n * - **CN:** 编辑按钮样式\n */\n editIcon?: CSSProperties;\n } & EditableFormProps<V, IT>['styles'];\n\n /**\n * - **EN:** Custom component type for rendering the text\n * - **CN:** 自定义设置渲染文本组件的组件类型\n *\n * @default 'Text'\n */\n textComp?: TT;\n /**\n * - **EN:** Text component props\n * - **CN:** 文本组件属性\n */\n textProps?: TT extends 'Text'\n ? TextProps\n : TT extends 'Paragraph'\n ? ParagraphProps\n : TT extends 'Title'\n ? TitleProps\n : TT extends 'Link'\n ? LinkProps\n : never;\n /**\n * - **EN:** Custom edit icon\n * - **CN:** 自定义编辑图标\n */\n editIcon?: ReactNode;\n /**\n * **EN:** Edit button vertical alignment\n *\n * **CN:** 编辑按钮垂直对齐方式\n *\n * - `start` - align to the top | 顶部对齐\n * - `center` - align to the center | 居中对齐\n * - `end` - align to the bottom | 底部对齐\n * - `baseline` - align to the text baseline, useful when the text has line height set |\n * 与文本基线对齐,在文本设置了行高时会非常有用\n *\n * @default 'center'\n */\n editIconAlign?: CSSProperties['alignItems'];\n}\n\n/**\n * - **EN:** Editable text component, providing a read-only display and an edit mode.\n * - **CN:** 可编辑文本组件,提供只读显示和编辑模式。\n *\n * @example\n * <EditableText value=\"Editable Text\" onOk={(value) => console.log('Saved value:', value)} />;\n */\nconst EditableText = <\n V,\n TT extends 'Text' | 'Paragraph' | 'Title' | 'Link',\n IT extends 'Input' | 'TextArea' | RenderInputInterface,\n>(\n props: EditableTextProps<V, TT, IT>\n) => {\n const {\n prefixCls: prefixClsInProps,\n value: valueInProps,\n displayText: displayTextInProps,\n editable = true,\n editing = false,\n className = '',\n style = {},\n classNames,\n styles: styleNames,\n block: blockInProps,\n textComp = 'Text',\n textProps,\n editIcon,\n editIconAlign = 'center',\n children,\n formProps,\n formItemProps,\n inputComp: inputCompInProps,\n inputProps,\n required,\n actionAlign,\n submitProps,\n cancelProps,\n onOk,\n onCancel,\n onChange,\n onEditingChange,\n } = props;\n\n const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);\n const prefixCls = getPrefixCls('easy-editable-text', prefixClsInProps);\n const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);\n const t = useT();\n const [isEditing, setIsEditing] = useState<boolean>(editing);\n const TypographyComponent = Typography[textComp];\n const [value, setValue] = useState(valueInProps);\n const inputComp = inputCompInProps ?? (textComp === 'Paragraph' ? 'TextArea' : 'Input');\n const viewBlock = typeof blockInProps === 'boolean' ? blockInProps : blockInProps?.view;\n const editingBlock = typeof blockInProps === 'boolean' ? blockInProps : blockInProps?.editing;\n const editableRef = React.useRef(editable);\n editableRef.current = editable;\n const displayText = useMemo(() => {\n if (typeof displayTextInProps === 'function') {\n return displayTextInProps(value);\n }\n return displayTextInProps ?? value?.toString();\n }, [displayTextInProps, value]);\n\n // value受控\n useEffect(() => {\n setValue(valueInProps);\n }, [valueInProps]);\n // editing受控\n useEffect(() => {\n if (editableRef.current) {\n setIsEditing(editing);\n }\n }, [editing]);\n\n // 编辑状态改变\n const handleEditingChange = (editing: boolean) => {\n setIsEditing(editing);\n onEditingChange?.(editing);\n };\n // 提交编辑\n const handleOk = async (val: V | undefined) => {\n try {\n await onOk?.(val);\n onChange?.(val);\n setValue(val);\n handleEditingChange(false);\n } catch (error) {\n console.error(error);\n }\n };\n // 取消编辑\n const handleCancel = async () => {\n handleEditingChange(false);\n await onCancel?.();\n };\n\n return wrapCSSVar(\n <div className={names(prefixCls, hashId, cssVarCls, className)} style={style}>\n {isEditing ? (\n <div className={`${prefixCls}-edit-mode`}>\n <EditableTextForm\n prefixCls={prefixCls}\n value={value}\n required={required}\n formProps={formProps}\n formItemProps={formItemProps}\n inputComp={inputComp}\n inputProps={inputProps}\n block={editingBlock}\n classNames={classNames}\n styles={styleNames}\n actionAlign={actionAlign}\n submitProps={submitProps}\n cancelProps={cancelProps}\n onOk={handleOk}\n onCancel={handleCancel}\n />\n </div>\n ) : (\n <Flex\n className={names(`${prefixCls}-view-mode`, {\n [`${prefixCls}-single-line`]: textComp !== 'Paragraph',\n [`${prefixCls}-has-children`]: !!children,\n [`${prefixCls}-view-mode-block`]: viewBlock,\n })}\n align={editIconAlign}\n >\n <div className={`${prefixCls}-text-content`}>\n {children ?? (\n <TypographyComponent\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n {...(textProps as any)}\n className={names(classNames?.text, classNames?.text, textProps?.className)}\n style={{\n marginBottom: textComp === 'Title' ? 0 : undefined, // 去掉标题组件的默认下边距\n ...(textProps?.style ?? styleNames?.text),\n }}\n ellipsis={textProps?.ellipsis ?? getEllipsisConfig(displayText)}\n >\n {displayText}\n </TypographyComponent>\n )}\n </div>\n {/* 编辑按钮 */}\n {editable && (\n <div\n className={names(`${prefixCls}-edit-icon`, classNames?.editIcon)}\n style={styleNames?.editIcon}\n title={t('components.EditableText.edit')}\n onClick={() => handleEditingChange(true)}\n >\n {editIcon ?? <EditOutlined />}\n </div>\n )}\n </Flex>\n )}\n </div>\n );\n};\n\ntype EditableTextInterface = typeof EditableText & {\n getEllipsisConfig: typeof getEllipsisConfig;\n};\n\nEditableText.getEllipsisConfig = getEllipsisConfig;\n\nexport default EditableText as EditableTextInterface;\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAAgE;AAChE,kBAAiD;AAMjD,wBAAkB;AAClB,mBAA6B;AAC7B,kBAAiB;AACjB,kBAAoF;AACpF,mBAAqB;AAErB,IAAM,oBAAoB,CAAC,aAAoD;AAAA,EAC7E,SAAS;AAAA,IACP,OAAO;AAAA,IACP,cAAc,EAAE,UAAU,IAAI;AAAA,EAChC;AACF;
|
|
4
|
+
"sourcesContent": ["import type { CSSProperties, ReactNode } from 'react';\nimport React, { useContext, useEffect, useMemo, useState } from 'react';\nimport { ConfigProvider, Flex, Typography } from 'antd';\nimport type { EllipsisConfig } from 'antd/es/typography/Base';\nimport type { LinkProps } from 'antd/es/typography/Link';\nimport type { ParagraphProps } from 'antd/es/typography/Paragraph';\nimport type { TextProps } from 'antd/es/typography/Text';\nimport type { TitleProps } from 'antd/es/typography/Title';\nimport names from 'classnames';\nimport { EditOutlined } from '@ant-design/icons';\nimport useT from '../../hooks/useT';\nimport EditableTextForm, { type EditableFormProps, type RenderInputInterface } from './form';\nimport useStyle from './style';\n\nconst getEllipsisConfig = (content: ReactNode | undefined): EllipsisConfig => ({\n tooltip: {\n title: content,\n overlayStyle: { maxWidth: 500 },\n },\n});\n\nexport interface EditableTextProps<\n V = string,\n TT extends 'Text' | 'Paragraph' | 'Title' | 'Link' = 'Text',\n IT extends 'Input' | 'TextArea' | RenderInputInterface = 'Input',\n> extends Omit<EditableFormProps<V, IT>, 'block'> {\n /**\n * - **EN:** Custom prefix for the component's CSS class.\n * - **CN:** 组件的自定义 CSS 类前缀。\n */\n prefixCls?: string;\n /**\n * - **EN:** Custom read-only display text, replacing `value` display\n * - **CN:** 自定义只读显示文本,替代`value`显示\n *\n * @default true\n */\n displayText?: boolean | ReactNode | ((value: V | undefined) => ReactNode);\n /**\n * - **EN:** Another way to customize read-only display text, with higher priority than\n * `displayText`. This method does not support text truncation.\n * - **CN:** 另外一种方式自定义只读显示文本,优先级高于`displayText`,这种方式无文本省略效果\n */\n children?: ReactNode;\n /**\n * - **EN:** Whether to allow editing. If set to `false`, the edit button will not be displayed.\n * - **CN:** 是否允许编辑,如果设置为`false`,则不显示编辑按钮\n *\n * @default true\n */\n editable?: boolean;\n /**\n * - **EN:** Whether to open edit mode.\n * - **CN:** 是否打开编辑模式\n *\n * @default false\n */\n editing?: boolean;\n /**\n * - **EN:** Whether to display as a block-level element, with width 100%\n * - **CN:** 是否显示为块级元素,宽度100%\n *\n * @default false\n */\n block?:\n | boolean\n | {\n /**\n * - **EN:** Whether to display as a block-level element in view mode, with width 100%\n * - **CN:** 只读模式是否显示为块级元素,宽度100%\n *\n * @default false\n */\n view?: boolean;\n /**\n * - **EN:** Whether to display as a block-level element in edit mode, with width 100%\n * - **CN:** 编辑模式是否显示为块级元素,宽度100%\n *\n * @default false\n */\n editing?: boolean;\n };\n /**\n * - **EN:** Component container class name.\n * - **CN:** 组件容器类名\n */\n className?: string;\n /**\n * - **EN:** Component container style.\n * - **CN:** 组件容器样式\n */\n style?: CSSProperties;\n /**\n * - **EN:** Semantic class names.\n * - **CN:** 语义化类名\n */\n classNames?: {\n /**\n * - **EN:** Read-only text class name\n * - **CN:** 只读文本类名\n */\n text?: string;\n /**\n * - **EN:** Edit button class name\n * - **CN:** 编辑按钮类名\n */\n editIcon?: string;\n } & EditableFormProps<V, IT>['classNames'];\n /**\n * - **EN:** After saving with `onOk`, the modified value is passed out through `onChange`\n * - **CN:** 在`onOk`保存后,通过`onChange`把修改后的值传递出去\n */\n onChange?: (value: V | undefined) => void;\n /**\n * - **EN:** Event triggered when the editing state changes\n * - **CN:** 编辑状态改变事件\n */\n onEditingChange?: (editing: boolean) => void;\n /**\n * - **EN:** Whether to display as a block-level element, with width 100%\n * - **CN:** 语义化样式\n */\n styles?: {\n /**\n * - **EN:** Read-only text class name\n * - **CN:** 只读文本类名\n */\n text?: CSSProperties;\n /**\n * - **EN:** Edit button style\n * - **CN:** 编辑按钮样式\n */\n editIcon?: CSSProperties;\n } & EditableFormProps<V, IT>['styles'];\n\n /**\n * - **EN:** Custom component type for rendering the text\n * - **CN:** 自定义设置渲染文本组件的组件类型\n *\n * @default Typography.Text\n */\n textComp?: TT;\n /**\n * - **EN:** Text component props\n * - **CN:** 文本组件属性\n */\n textProps?: TT extends 'Text'\n ? TextProps\n : TT extends 'Paragraph'\n ? ParagraphProps\n : TT extends 'Title'\n ? TitleProps\n : TT extends 'Link'\n ? LinkProps\n : never;\n /**\n * - **EN:** Custom edit icon\n * - **CN:** 自定义编辑图标\n */\n editIcon?: ReactNode;\n /**\n * **EN:** Edit button vertical alignment\n *\n * **CN:** 编辑按钮垂直对齐方式\n *\n * - `start` - align to the top | 顶部对齐\n * - `center` - align to the center | 居中对齐\n * - `end` - align to the bottom | 底部对齐\n * - `baseline` - align to the text baseline, useful when the text has line height set |\n * 与文本基线对齐,在文本设置了行高时会非常有用\n *\n * @default 'center'\n */\n editIconAlign?: CSSProperties['alignItems'];\n}\n\n/**\n * - **EN:** Editable text component, providing a read-only display and an edit mode.\n * - **CN:** 可编辑文本组件,提供只读显示和编辑模式。\n *\n * @example\n * <EditableText value=\"Editable Text\" onOk={(value) => console.log('Saved value:', value)} />;\n */\nconst EditableText = <\n V,\n TT extends 'Text' | 'Paragraph' | 'Title' | 'Link',\n IT extends 'Input' | 'TextArea' | RenderInputInterface,\n>(\n props: EditableTextProps<V, TT, IT>\n) => {\n const {\n prefixCls: prefixClsInProps,\n value: valueInProps,\n displayText: displayTextInProps,\n editable = true,\n editing = false,\n className = '',\n style = {},\n classNames,\n styles: styleNames,\n block: blockInProps,\n textComp = 'Text',\n textProps,\n editIcon,\n editIconAlign = 'center',\n children,\n formProps,\n formItemProps,\n inputComp: inputCompInProps,\n inputProps,\n required,\n actionAlign,\n submitProps,\n cancelProps,\n onOk,\n onCancel,\n onChange,\n onEditingChange,\n } = props;\n\n const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);\n const prefixCls = getPrefixCls('easy-editable-text', prefixClsInProps);\n const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);\n const t = useT();\n const [isEditing, setIsEditing] = useState<boolean>(editing);\n const TypographyComponent = Typography[textComp];\n const [value, setValue] = useState(valueInProps);\n const inputComp = inputCompInProps ?? (textComp === 'Paragraph' ? 'TextArea' : 'Input');\n const viewBlock = typeof blockInProps === 'boolean' ? blockInProps : blockInProps?.view;\n const editingBlock = typeof blockInProps === 'boolean' ? blockInProps : blockInProps?.editing;\n const editableRef = React.useRef(editable);\n editableRef.current = editable;\n const displayText = useMemo(() => {\n if (typeof displayTextInProps === 'function') {\n return displayTextInProps(value);\n }\n return displayTextInProps ?? value?.toString();\n }, [displayTextInProps, value]);\n\n // value受控\n useEffect(() => {\n setValue(valueInProps);\n }, [valueInProps]);\n // editing受控\n useEffect(() => {\n if (editableRef.current) {\n setIsEditing(editing);\n }\n }, [editing]);\n\n // 编辑状态改变\n const handleEditingChange = (editing: boolean) => {\n setIsEditing(editing);\n onEditingChange?.(editing);\n };\n // 提交编辑\n const handleOk = async (val: V | undefined) => {\n try {\n await onOk?.(val);\n onChange?.(val);\n setValue(val);\n handleEditingChange(false);\n } catch (error) {\n console.error(error);\n }\n };\n // 取消编辑\n const handleCancel = async () => {\n handleEditingChange(false);\n await onCancel?.();\n };\n\n return wrapCSSVar(\n <div className={names(prefixCls, hashId, cssVarCls, className)} style={style}>\n {isEditing ? (\n <div className={`${prefixCls}-edit-mode`}>\n <EditableTextForm\n prefixCls={prefixCls}\n value={value}\n required={required}\n formProps={formProps}\n formItemProps={formItemProps}\n inputComp={inputComp}\n inputProps={inputProps}\n block={editingBlock}\n classNames={classNames}\n styles={styleNames}\n actionAlign={actionAlign}\n submitProps={submitProps}\n cancelProps={cancelProps}\n onOk={handleOk}\n onCancel={handleCancel}\n />\n </div>\n ) : (\n <Flex\n className={names(`${prefixCls}-view-mode`, {\n [`${prefixCls}-single-line`]: textComp !== 'Paragraph',\n [`${prefixCls}-has-children`]: !!children,\n [`${prefixCls}-view-mode-block`]: viewBlock,\n })}\n align={editIconAlign}\n >\n <div className={`${prefixCls}-text-content`}>\n {children ?? (\n <TypographyComponent\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n {...(textProps as any)}\n className={names(classNames?.text, classNames?.text, textProps?.className)}\n style={{\n marginBottom: textComp === 'Title' ? 0 : undefined, // 去掉标题组件的默认下边距\n ...(textProps?.style ?? styleNames?.text),\n }}\n ellipsis={textProps?.ellipsis ?? getEllipsisConfig(displayText)}\n >\n {displayText}\n </TypographyComponent>\n )}\n </div>\n {/* 编辑按钮 */}\n {editable && (\n <div\n className={names(`${prefixCls}-edit-icon`, classNames?.editIcon)}\n style={styleNames?.editIcon}\n title={t('components.EditableText.edit')}\n onClick={() => handleEditingChange(true)}\n >\n {editIcon ?? <EditOutlined />}\n </div>\n )}\n </Flex>\n )}\n </div>\n );\n};\n\ntype EditableTextInterface = typeof EditableText & {\n getEllipsisConfig: typeof getEllipsisConfig;\n};\n\nEditableText.getEllipsisConfig = getEllipsisConfig;\n\nexport default EditableText as EditableTextInterface;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["useStyle", "useT", "React", "editing", "names", "EditableTextForm"]
|
|
7
7
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { CSSProperties, FC, ReactNode } from 'react';
|
|
2
2
|
import type { CardProps } from 'antd';
|
|
3
3
|
export interface FloatDrawerProps {
|
|
4
|
+
/**
|
|
5
|
+
* - **EN:** Custom prefix for the component's CSS class.
|
|
6
|
+
* - **CN:** 组件的自定义 CSS 类前缀。
|
|
7
|
+
*/
|
|
4
8
|
prefixCls?: string;
|
|
5
9
|
/**
|
|
6
10
|
* - **EN:** Whether the drawer is open
|
|
@@ -53,16 +57,22 @@ export interface FloatDrawerProps {
|
|
|
53
57
|
/**
|
|
54
58
|
* - **EN:** Whether to show the toggle button, default is `true`.
|
|
55
59
|
* - **CN:** 是否显示展开/收起按钮,默认`true`。
|
|
60
|
+
*
|
|
61
|
+
* @default true
|
|
56
62
|
*/
|
|
57
63
|
showToggle?: boolean;
|
|
58
64
|
/**
|
|
59
65
|
* - **EN:** Whether the drawer is resizable, default is `true`.
|
|
60
66
|
* - **CN:** 抽屉是否可调整大小, 默认`true`。
|
|
67
|
+
*
|
|
68
|
+
* @default true
|
|
61
69
|
*/
|
|
62
70
|
resizable?: boolean;
|
|
63
71
|
/**
|
|
64
72
|
* - **EN:** Whether to destroy the drawer content when closed, default is `false`.
|
|
65
73
|
* - **CN:** 抽屉关闭时是否销毁内容,默认`false`。
|
|
74
|
+
*
|
|
75
|
+
* @default false
|
|
66
76
|
*/
|
|
67
77
|
destroyOnClose?: boolean;
|
|
68
78
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/FloatDrawer/index.tsx"],
|
|
4
|
-
"sourcesContent": ["import type { CSSProperties, FC, ReactNode } from 'react';\nimport { useContext, useEffect, useMemo, useRef, useState } from 'react';\nimport type { CardProps } from 'antd';\nimport { Card, ConfigProvider } from 'antd';\nimport classNames from 'classnames';\nimport { DownOutlined, LeftOutlined, RightOutlined, UpOutlined } from '@ant-design/icons';\nimport useRefFunction from '../../hooks/useRefFunction';\nimport useStyle from './style';\n\nexport interface FloatDrawerProps {\n prefixCls?: string;\n /**\n * - **EN:** Whether the drawer is open\n * - **CN:** 抽屉是否打开\n */\n open?: boolean;\n /**\n * - **EN:** Position of the drawer\n * - **CN:** 抽屉的位置\n *\n * @default 'right'\n */\n position?: 'left' | 'right' | 'top' | 'bottom';\n /**\n * - **EN:** Default size of the drawer. If the drawer is placed on the left or right, this is the\n * width, otherwise it is the height. If not set, the drawer will adapt to the content size.\n * - **CN:** 抽屉的默认宽度。如果抽屉放在左侧或右侧,则为宽度,否则为高度。不设置时,抽屉会根据内容自适应大小。\n */\n defaultSize?: number;\n /**\n * - **EN:** Minimum size of the drawer\n * - **CN:** 抽屉的最小宽度\n *\n * @default 0\n */\n minSize?: number;\n /**\n * - **EN:** Maximum size of the drawer\n * - **CN:** 抽屉的最大宽度\n *\n * @default Infinity\n */\n maxSize?: number;\n /**\n * - **EN:** Offset of the drawer from the edge of the screen, supports all CSS length units,\n * default is `0`.\n *\n * > The drawer will be hidden outside the parent container's edge, but since the parent container\n * > may have padding or border, the drawer may not be completely hidden. You can adjust the\n * > `edgeOffset` to ensure the drawer is completely hidden outside the parent container.\n *\n * - **CN:** 抽屉距离屏幕边缘的偏移量,支持所有的CSS长度单位, 默认`0`。\n *\n * > 抽屉会隐藏在父容器的外边缘, 但由于父容器可能有padding或border, 导致抽屉不能完全隐藏, 因此可以通过设置 `edgeOffset` 来调整抽屉的偏移量,\n * > 以确保抽屉完全隐藏在父容器之外。\n *\n * @default 0\n */\n edgeOffset?: CSSProperties['width'];\n /**\n * - **EN:** Whether to show the toggle button, default is `true`.\n * - **CN:** 是否显示展开/收起按钮,默认`true`。\n */\n showToggle?: boolean;\n /**\n * - **EN:** Whether the drawer is resizable, default is `true`.\n * - **CN:** 抽屉是否可调整大小, 默认`true`。\n */\n resizable?: boolean;\n /**\n * - **EN:** Whether to destroy the drawer content when closed, default is `false`.\n * - **CN:** 抽屉关闭时是否销毁内容,默认`false`。\n */\n destroyOnClose?: boolean;\n /**\n * - **EN:** Cache key for storing the drawer size in localStorage. If not set, the size will not be\n * cached.\n * - **CN:** 指定一个localStorage缓存键,用于记忆抽屉宽度。如果不设置,则不使用缓存。\n */\n cacheKey?: string;\n /**\n * - **EN:** Custom class name for the root element\n * - **CN:** 根元素的自定义类名\n */\n className?: string;\n /**\n * - **EN:** Custom class names for specific elements\n * - **CN:** 特定元素的自定义类名\n */\n classNames?: {\n /**\n * - **EN:** Class name for the drawer element\n * - **CN:** 抽屉元素的类名\n */\n drawer?: string;\n /**\n * - **EN:** Class name for the expand handle\n * - **CN:** 展开手柄的类名\n */\n toggle?: string;\n /**\n * - **EN:** Class name for the resize handle\n * - **CN:** 调整大小手柄的类名\n */\n resizeHandle?: string;\n /**\n * - **EN:** Class name for the handle icon\n * - **CN:** 手柄图标的类名\n */\n handleIcon?: string;\n /**\n * - **EN:** Class name for the content area\n * - **CN:** 内容区域的类名\n */\n content?: string;\n /**\n * - **EN:** Class name for the card element\n * - **CN:** 卡片元素的类名\n */\n card?: string;\n };\n /**\n * - **EN:** Custom styles for the root element\n * - **CN:** 根元素的自定义样式\n */\n style?: CSSProperties;\n /**\n * - **EN:** Custom styles for specific elements\n * - **CN:** 特定元素的自定义样式\n */\n styles?: {\n /**\n * - **EN:** Styles for the drawer element\n * - **CN:** 抽屉元素的样式\n */\n drawer?: CSSProperties;\n /**\n * - **EN:** Styles for the expand handle\n * - **CN:** 展开手柄的样式\n */\n toggle?: CSSProperties;\n /**\n * - **EN:** Styles for the resize handle\n * - **CN:** 调整大小手柄的样式\n */\n resizeHandle?: CSSProperties;\n /**\n * - **EN:** Styles for the handle icon\n * - **CN:** 手柄图标的样式\n */\n handleIcon?: CSSProperties;\n /**\n * - **EN:** Styles for the content area\n * - **CN:** 内容区域的样式\n */\n content?: CSSProperties;\n /**\n * - **EN:** Styles for the card element\n * - **CN:** 卡片元素的样式\n */\n card?: CSSProperties;\n };\n /**\n * - **EN:** Custom properties for the card element\n * - **CN:** 卡片元素的自定义属性\n */\n cardProps?: Omit<CardProps, 'children'>;\n /**\n * - **EN:** Content to be rendered inside the drawer\n * - **CN:** 抽屉内容\n */\n children?: ReactNode;\n /**\n * - **EN:** Callback function when the open state changes\n * - **CN:** 打开状态变化时的回调函数\n */\n onOpenChange?: (open: boolean) => void;\n /**\n * - **EN:** Callback function when the drawer is resized\n * - **CN:** 抽屉调整大小时的回调函数\n */\n onResize?: (size: number) => void;\n /**\n * - **EN:** Event handler for click events on the drawer container and its internal elements\n * - **CN:** 点击抽屉容器及内部元素时的事件\n */\n onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;\n}\n\n/**\n * - **EN:** FloatDrawer component for creating a draggable, resizable drawer\n * - **CN:** FloatDrawer组件,用于创建可拖动、可调整大小的抽屉\n */\nconst FloatDrawer: FC<FloatDrawerProps> = (props) => {\n const {\n prefixCls: prefixClsInProps,\n open,\n position = 'right',\n cardProps,\n children,\n className,\n classNames: classNamesInProps,\n style,\n styles,\n cacheKey,\n defaultSize,\n minSize = 0,\n maxSize = +Infinity,\n edgeOffset,\n showToggle = true,\n resizable = true,\n destroyOnClose = false,\n onOpenChange,\n onResize,\n onClick,\n } = props;\n const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);\n const prefixCls = getPrefixCls('easy-float-drawer', prefixClsInProps);\n const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);\n const [drawerRef, setDrawerRef] = useState<HTMLDivElement | null>(null);\n const [size, setSize] = useState(\n cacheKey && localStorage.getItem(cacheKey) ? Number(localStorage.getItem(cacheKey)) || defaultSize : defaultSize\n );\n const sizeMap = useMemo(() => {\n const type = position === 'left' || position === 'right' ? 'width' : 'height';\n return {\n [type]: size,\n };\n }, [position, size]);\n const [isOpen, setIsOpen] = useState<boolean>();\n const [isDragging, setIsDragging] = useState(false);\n const dragStartPos = useRef<number>(0);\n const dragStartSize = useRef<number>(size);\n const classString = classNames(\n prefixCls,\n className,\n {\n [`${prefixCls}-open`]: isOpen,\n [`${prefixCls}-closed`]: !isOpen,\n [`${prefixCls}-${position}`]: position,\n },\n hashId,\n cssVarCls\n );\n const closeIcon = useMemo(() => {\n return position === 'left' ? (\n <RightOutlined />\n ) : position === 'top' ? (\n <DownOutlined />\n ) : position === 'bottom' ? (\n <UpOutlined />\n ) : (\n <LeftOutlined />\n );\n }, [position]);\n const openIcon = useMemo(() => {\n return position === 'left' ? (\n <LeftOutlined />\n ) : position === 'top' ? (\n <UpOutlined />\n ) : position === 'bottom' ? (\n <DownOutlined />\n ) : (\n <RightOutlined />\n );\n }, [position]);\n\n // Handle drawer visibility\n const toggleDrawer = () => {\n setIsOpen(!isOpen);\n onOpenChange?.(!isOpen);\n };\n\n // Handle resize events\n const handleResizeStart = useRefFunction((e: React.MouseEvent) => {\n setIsDragging(true);\n if (position === 'top' || position === 'bottom') {\n dragStartPos.current = e.clientY;\n } else {\n dragStartPos.current = e.clientX;\n }\n dragStartSize.current = size;\n e.preventDefault();\n });\n const handleResize = useRefFunction((e: MouseEvent) => {\n if (isDragging && dragStartSize.current) {\n let newSize: number;\n if (position === 'left') {\n newSize = dragStartSize.current - (dragStartPos.current - e.clientX);\n } else if (position === 'top') {\n newSize = dragStartSize.current - (dragStartPos.current - e.clientY);\n } else if (position === 'bottom') {\n newSize = dragStartSize.current - (e.clientY - dragStartPos.current);\n } else {\n newSize = dragStartSize.current - (e.clientX - dragStartPos.current);\n }\n if (newSize >= minSize && newSize <= maxSize) {\n setSize(newSize);\n if (cacheKey) {\n localStorage.setItem(cacheKey, String(newSize));\n }\n onResize?.(newSize);\n }\n }\n });\n const handleResizeEnd = useRefFunction(() => {\n setIsDragging(false);\n });\n\n // Monitor drawer ref, calculate size if not set\n // This is useful for initial rendering when size is not provided\n useEffect(() => {\n if (drawerRef && size === undefined) {\n const rect = drawerRef.getBoundingClientRect();\n setSize(position === 'left' || position === 'right' ? rect.width : rect.height);\n }\n }, [drawerRef, position, size]);\n\n // Controlled open state\n useEffect(() => {\n setIsOpen(open);\n }, [open]);\n\n // Handle global events\n useEffect(() => {\n if (isDragging) {\n window.addEventListener('mousemove', handleResize);\n window.addEventListener('mouseup', handleResizeEnd);\n } else {\n window.removeEventListener('mousemove', handleResize);\n window.removeEventListener('mouseup', handleResizeEnd);\n }\n return () => {\n window.removeEventListener('mousemove', handleResize);\n window.removeEventListener('mouseup', handleResizeEnd);\n };\n }, [isDragging]);\n\n return wrapCSSVar(\n <div\n className={classString}\n style={{\n // @ts-expect-error: because of dynamic CSS variables\n '--edge-offset': typeof edgeOffset === 'number' ? `${edgeOffset}px` : edgeOffset,\n ...style,\n }}\n onClick={onClick}\n >\n <div\n ref={setDrawerRef}\n className={classNames(`${prefixCls}-drawer`, classNamesInProps?.drawer)}\n style={{ ...sizeMap, ...styles?.drawer }}\n >\n {resizable && (\n <div\n className={classNames(\n `${prefixCls}-resize-handle`,\n isDragging && `${prefixCls}-resize-handle-dragging`,\n classNamesInProps?.resizeHandle\n )}\n style={styles?.resizeHandle}\n onMouseDown={handleResizeStart}\n />\n )}\n <div className={classNames(`${prefixCls}-content`, classNamesInProps?.content)} style={styles?.content}>\n <Card\n bordered={false}\n variant=\"borderless\"\n className={classNames(`${prefixCls}-card`, classNamesInProps?.card)}\n style={styles?.card}\n {...cardProps}\n >\n {!destroyOnClose || isOpen ? children : null}\n </Card>\n </div>\n {showToggle && (\n <div\n className={classNames(`${prefixCls}-toggle`, classNamesInProps?.toggle)}\n style={styles?.toggle}\n onClick={toggleDrawer}\n >\n <div\n className={classNames(`${prefixCls}-handle-icon`, classNamesInProps?.handleIcon)}\n style={styles?.handleIcon}\n >\n {isOpen ? openIcon : closeIcon}\n </div>\n </div>\n )}\n </div>\n </div>\n );\n};\n\nexport default FloatDrawer;\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAAiE;AAEjE,kBAAqC;AACrC,wBAAuB;AACvB,mBAAsE;AACtE,4BAA2B;AAC3B,mBAAqB;
|
|
4
|
+
"sourcesContent": ["import type { CSSProperties, FC, ReactNode } from 'react';\nimport { useContext, useEffect, useMemo, useRef, useState } from 'react';\nimport type { CardProps } from 'antd';\nimport { Card, ConfigProvider } from 'antd';\nimport classNames from 'classnames';\nimport { DownOutlined, LeftOutlined, RightOutlined, UpOutlined } from '@ant-design/icons';\nimport useRefFunction from '../../hooks/useRefFunction';\nimport useStyle from './style';\n\nexport interface FloatDrawerProps {\n /**\n * - **EN:** Custom prefix for the component's CSS class.\n * - **CN:** 组件的自定义 CSS 类前缀。\n */\n prefixCls?: string;\n /**\n * - **EN:** Whether the drawer is open\n * - **CN:** 抽屉是否打开\n */\n open?: boolean;\n /**\n * - **EN:** Position of the drawer\n * - **CN:** 抽屉的位置\n *\n * @default 'right'\n */\n position?: 'left' | 'right' | 'top' | 'bottom';\n /**\n * - **EN:** Default size of the drawer. If the drawer is placed on the left or right, this is the\n * width, otherwise it is the height. If not set, the drawer will adapt to the content size.\n * - **CN:** 抽屉的默认宽度。如果抽屉放在左侧或右侧,则为宽度,否则为高度。不设置时,抽屉会根据内容自适应大小。\n */\n defaultSize?: number;\n /**\n * - **EN:** Minimum size of the drawer\n * - **CN:** 抽屉的最小宽度\n *\n * @default 0\n */\n minSize?: number;\n /**\n * - **EN:** Maximum size of the drawer\n * - **CN:** 抽屉的最大宽度\n *\n * @default Infinity\n */\n maxSize?: number;\n /**\n * - **EN:** Offset of the drawer from the edge of the screen, supports all CSS length units,\n * default is `0`.\n *\n * > The drawer will be hidden outside the parent container's edge, but since the parent container\n * > may have padding or border, the drawer may not be completely hidden. You can adjust the\n * > `edgeOffset` to ensure the drawer is completely hidden outside the parent container.\n *\n * - **CN:** 抽屉距离屏幕边缘的偏移量,支持所有的CSS长度单位, 默认`0`。\n *\n * > 抽屉会隐藏在父容器的外边缘, 但由于父容器可能有padding或border, 导致抽屉不能完全隐藏, 因此可以通过设置 `edgeOffset` 来调整抽屉的偏移量,\n * > 以确保抽屉完全隐藏在父容器之外。\n *\n * @default 0\n */\n edgeOffset?: CSSProperties['width'];\n /**\n * - **EN:** Whether to show the toggle button, default is `true`.\n * - **CN:** 是否显示展开/收起按钮,默认`true`。\n *\n * @default true\n */\n showToggle?: boolean;\n /**\n * - **EN:** Whether the drawer is resizable, default is `true`.\n * - **CN:** 抽屉是否可调整大小, 默认`true`。\n *\n * @default true\n */\n resizable?: boolean;\n /**\n * - **EN:** Whether to destroy the drawer content when closed, default is `false`.\n * - **CN:** 抽屉关闭时是否销毁内容,默认`false`。\n *\n * @default false\n */\n destroyOnClose?: boolean;\n /**\n * - **EN:** Cache key for storing the drawer size in localStorage. If not set, the size will not be\n * cached.\n * - **CN:** 指定一个localStorage缓存键,用于记忆抽屉宽度。如果不设置,则不使用缓存。\n */\n cacheKey?: string;\n /**\n * - **EN:** Custom class name for the root element\n * - **CN:** 根元素的自定义类名\n */\n className?: string;\n /**\n * - **EN:** Custom class names for specific elements\n * - **CN:** 特定元素的自定义类名\n */\n classNames?: {\n /**\n * - **EN:** Class name for the drawer element\n * - **CN:** 抽屉元素的类名\n */\n drawer?: string;\n /**\n * - **EN:** Class name for the expand handle\n * - **CN:** 展开手柄的类名\n */\n toggle?: string;\n /**\n * - **EN:** Class name for the resize handle\n * - **CN:** 调整大小手柄的类名\n */\n resizeHandle?: string;\n /**\n * - **EN:** Class name for the handle icon\n * - **CN:** 手柄图标的类名\n */\n handleIcon?: string;\n /**\n * - **EN:** Class name for the content area\n * - **CN:** 内容区域的类名\n */\n content?: string;\n /**\n * - **EN:** Class name for the card element\n * - **CN:** 卡片元素的类名\n */\n card?: string;\n };\n /**\n * - **EN:** Custom styles for the root element\n * - **CN:** 根元素的自定义样式\n */\n style?: CSSProperties;\n /**\n * - **EN:** Custom styles for specific elements\n * - **CN:** 特定元素的自定义样式\n */\n styles?: {\n /**\n * - **EN:** Styles for the drawer element\n * - **CN:** 抽屉元素的样式\n */\n drawer?: CSSProperties;\n /**\n * - **EN:** Styles for the expand handle\n * - **CN:** 展开手柄的样式\n */\n toggle?: CSSProperties;\n /**\n * - **EN:** Styles for the resize handle\n * - **CN:** 调整大小手柄的样式\n */\n resizeHandle?: CSSProperties;\n /**\n * - **EN:** Styles for the handle icon\n * - **CN:** 手柄图标的样式\n */\n handleIcon?: CSSProperties;\n /**\n * - **EN:** Styles for the content area\n * - **CN:** 内容区域的样式\n */\n content?: CSSProperties;\n /**\n * - **EN:** Styles for the card element\n * - **CN:** 卡片元素的样式\n */\n card?: CSSProperties;\n };\n /**\n * - **EN:** Custom properties for the card element\n * - **CN:** 卡片元素的自定义属性\n */\n cardProps?: Omit<CardProps, 'children'>;\n /**\n * - **EN:** Content to be rendered inside the drawer\n * - **CN:** 抽屉内容\n */\n children?: ReactNode;\n /**\n * - **EN:** Callback function when the open state changes\n * - **CN:** 打开状态变化时的回调函数\n */\n onOpenChange?: (open: boolean) => void;\n /**\n * - **EN:** Callback function when the drawer is resized\n * - **CN:** 抽屉调整大小时的回调函数\n */\n onResize?: (size: number) => void;\n /**\n * - **EN:** Event handler for click events on the drawer container and its internal elements\n * - **CN:** 点击抽屉容器及内部元素时的事件\n */\n onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;\n}\n\n/**\n * - **EN:** FloatDrawer component for creating a draggable, resizable drawer\n * - **CN:** FloatDrawer组件,用于创建可拖动、可调整大小的抽屉\n */\nconst FloatDrawer: FC<FloatDrawerProps> = (props) => {\n const {\n prefixCls: prefixClsInProps,\n open,\n position = 'right',\n cardProps,\n children,\n className,\n classNames: classNamesInProps,\n style,\n styles,\n cacheKey,\n defaultSize,\n minSize = 0,\n maxSize = +Infinity,\n edgeOffset,\n showToggle = true,\n resizable = true,\n destroyOnClose = false,\n onOpenChange,\n onResize,\n onClick,\n } = props;\n const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);\n const prefixCls = getPrefixCls('easy-float-drawer', prefixClsInProps);\n const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);\n const [drawerRef, setDrawerRef] = useState<HTMLDivElement | null>(null);\n const [size, setSize] = useState(\n cacheKey && localStorage.getItem(cacheKey) ? Number(localStorage.getItem(cacheKey)) || defaultSize : defaultSize\n );\n const sizeMap = useMemo(() => {\n const type = position === 'left' || position === 'right' ? 'width' : 'height';\n return {\n [type]: size,\n };\n }, [position, size]);\n const [isOpen, setIsOpen] = useState<boolean>();\n const [isDragging, setIsDragging] = useState(false);\n const dragStartPos = useRef<number>(0);\n const dragStartSize = useRef<number>(size);\n const classString = classNames(\n prefixCls,\n className,\n {\n [`${prefixCls}-open`]: isOpen,\n [`${prefixCls}-closed`]: !isOpen,\n [`${prefixCls}-${position}`]: position,\n },\n hashId,\n cssVarCls\n );\n const closeIcon = useMemo(() => {\n return position === 'left' ? (\n <RightOutlined />\n ) : position === 'top' ? (\n <DownOutlined />\n ) : position === 'bottom' ? (\n <UpOutlined />\n ) : (\n <LeftOutlined />\n );\n }, [position]);\n const openIcon = useMemo(() => {\n return position === 'left' ? (\n <LeftOutlined />\n ) : position === 'top' ? (\n <UpOutlined />\n ) : position === 'bottom' ? (\n <DownOutlined />\n ) : (\n <RightOutlined />\n );\n }, [position]);\n\n // Handle drawer visibility\n const toggleDrawer = () => {\n setIsOpen(!isOpen);\n onOpenChange?.(!isOpen);\n };\n\n // Handle resize events\n const handleResizeStart = useRefFunction((e: React.MouseEvent) => {\n setIsDragging(true);\n if (position === 'top' || position === 'bottom') {\n dragStartPos.current = e.clientY;\n } else {\n dragStartPos.current = e.clientX;\n }\n dragStartSize.current = size;\n e.preventDefault();\n });\n const handleResize = useRefFunction((e: MouseEvent) => {\n if (isDragging && dragStartSize.current) {\n let newSize: number;\n if (position === 'left') {\n newSize = dragStartSize.current - (dragStartPos.current - e.clientX);\n } else if (position === 'top') {\n newSize = dragStartSize.current - (dragStartPos.current - e.clientY);\n } else if (position === 'bottom') {\n newSize = dragStartSize.current - (e.clientY - dragStartPos.current);\n } else {\n newSize = dragStartSize.current - (e.clientX - dragStartPos.current);\n }\n if (newSize >= minSize && newSize <= maxSize) {\n setSize(newSize);\n if (cacheKey) {\n localStorage.setItem(cacheKey, String(newSize));\n }\n onResize?.(newSize);\n }\n }\n });\n const handleResizeEnd = useRefFunction(() => {\n setIsDragging(false);\n });\n\n // Monitor drawer ref, calculate size if not set\n // This is useful for initial rendering when size is not provided\n useEffect(() => {\n if (drawerRef && size === undefined) {\n const rect = drawerRef.getBoundingClientRect();\n setSize(position === 'left' || position === 'right' ? rect.width : rect.height);\n }\n }, [drawerRef, position, size]);\n\n // Controlled open state\n useEffect(() => {\n setIsOpen(open);\n }, [open]);\n\n // Handle global events\n useEffect(() => {\n if (isDragging) {\n window.addEventListener('mousemove', handleResize);\n window.addEventListener('mouseup', handleResizeEnd);\n } else {\n window.removeEventListener('mousemove', handleResize);\n window.removeEventListener('mouseup', handleResizeEnd);\n }\n return () => {\n window.removeEventListener('mousemove', handleResize);\n window.removeEventListener('mouseup', handleResizeEnd);\n };\n }, [isDragging]);\n\n return wrapCSSVar(\n <div\n className={classString}\n style={{\n // @ts-expect-error: because of dynamic CSS variables\n '--edge-offset': typeof edgeOffset === 'number' ? `${edgeOffset}px` : edgeOffset,\n ...style,\n }}\n onClick={onClick}\n >\n <div\n ref={setDrawerRef}\n className={classNames(`${prefixCls}-drawer`, classNamesInProps?.drawer)}\n style={{ ...sizeMap, ...styles?.drawer }}\n >\n {resizable && (\n <div\n className={classNames(\n `${prefixCls}-resize-handle`,\n isDragging && `${prefixCls}-resize-handle-dragging`,\n classNamesInProps?.resizeHandle\n )}\n style={styles?.resizeHandle}\n onMouseDown={handleResizeStart}\n />\n )}\n <div className={classNames(`${prefixCls}-content`, classNamesInProps?.content)} style={styles?.content}>\n <Card\n bordered={false}\n variant=\"borderless\"\n className={classNames(`${prefixCls}-card`, classNamesInProps?.card)}\n style={styles?.card}\n {...cardProps}\n >\n {!destroyOnClose || isOpen ? children : null}\n </Card>\n </div>\n {showToggle && (\n <div\n className={classNames(`${prefixCls}-toggle`, classNamesInProps?.toggle)}\n style={styles?.toggle}\n onClick={toggleDrawer}\n >\n <div\n className={classNames(`${prefixCls}-handle-icon`, classNamesInProps?.handleIcon)}\n style={styles?.handleIcon}\n >\n {isOpen ? openIcon : closeIcon}\n </div>\n </div>\n )}\n </div>\n </div>\n );\n};\n\nexport default FloatDrawer;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["useStyle", "classNames", "useRefFunction"]
|
|
7
7
|
}
|
|
@@ -55,6 +55,8 @@ export interface OverflowTagsProps<T> extends Omit<OverflowProps<T>, 'renderItem
|
|
|
55
55
|
* contain a `color` property to specify the color, and the latter takes precedence.
|
|
56
56
|
*
|
|
57
57
|
* **CN**: 是否使用随机颜色,默认`false`。注意,tag对象还可以包含`color`属性来指定颜色,而且后者优先级更高。
|
|
58
|
+
*
|
|
59
|
+
* @default false
|
|
58
60
|
*/
|
|
59
61
|
randomColors?: boolean;
|
|
60
62
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/OverflowTags/index.tsx"],
|
|
4
|
-
"sourcesContent": ["import type { ReactNode } from 'react';\nimport { useCallback, useContext, useMemo } from 'react';\nimport type { DropdownProps, TagProps } from 'antd';\nimport { ConfigProvider, Dropdown, Tag, theme } from 'antd';\nimport { PresetColors } from 'antd/es/theme/internal';\nimport classNames from 'classnames';\nimport type { OverflowProps } from 'rc-overflow';\nimport Overflow from 'rc-overflow';\nimport { random } from '../../utils/math';\nimport useStyle from './style';\n\nexport interface OverflowTagsProps<T> extends Omit<OverflowProps<T>, 'renderItem'> {\n /**\n * **CN**: 标签集合的数据\n *\n * **EN**: Data collection of tags\n */\n tags: T[] | undefined;\n /**\n * **EN**: Function to get the tag name, default is `tag.label` or `tag.name`\n *\n * **CN**: 获取标签名称的函数,默认取`tag.label`或`tag.name`\n */\n getTagName?: (tag: T) => ReactNode;\n /**\n * **EN**: Function to get the unique identifier of the tag, default is `tag.value` or `tag.id`\n *\n * **CN**: 获取标签唯一标识的函数,默认取`tag.value`或`tag.id`\n */\n getTagKey?: (tag: T) => React.Key;\n\n /**\n * **EN**: Custom tag rendering function\n *\n * **CN**: 标签渲染函数\n */\n renderTag?: OverflowProps<T>['renderItem'];\n /**\n * **EN**: Custom properties for the tag component\n *\n * **CN**: 自定义标签的组件属性\n */\n tagProps?: TagProps | ((tag: T, options: { tags: T[] }) => TagProps);\n /**\n * **EN**: When the number of tags exceeds the maximum display count, an ellipsis tag will be\n * shown. This property is used to set the style of the ellipsis tag.\n *\n * **CN**: 当标签数量超过最大显示数量时,会显示省略号的标签,此属性用于设置省略号标签的样式\n */\n ellipsisTagProps?: TagProps | ((tag: T, options: { omittedItems: T[]; allTags: T[] }) => TagProps);\n /**\n * **EN**: Custom properties for the dropdown component when tags are overflowed\n *\n * **CN**: 当标签溢出时,下拉菜单的自定义属性\n */\n ellipsisDropdownProps?: DropdownProps;\n /**\n * **EN**: Whether to use random colors, default is `false`. Note that the tag object can also\n * contain a `color` property to specify the color, and the latter takes precedence.\n *\n * **CN**: 是否使用随机颜色,默认`false`。注意,tag对象还可以包含`color`属性来指定颜色,而且后者优先级更高。\n */\n randomColors?: boolean;\n}\n\n/**\n * - **EN:** Overflow tags component, used to display a collection of tags that can overflow and be\n * truncated. It supports displaying tags in a dropdown when the number of tags exceeds the\n * maximum display count. It also supports custom tag rendering and properties.\n * - **CN:** 溢出标签组件,用于显示一组可以溢出和截断的标签集合。当标签数量超过最大显示数量时,支持在下拉菜单中显示标签。还支持自定义标签渲染和属性。\n *\n * @example\n * <OverflowTags\n * tags={[\n * { value: 1, label: 'Tag1', icon: <Icon1 /> },\n * { value: 2, label: 'Tag2', icon: <Icon2 /> },\n * ]}\n * tagProps={{ color: 'blue' }}\n * ellipsisTagProps={{ color: 'grey' }}\n * />;\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst OverflowTags = <T,>(props: OverflowTagsProps<T>) => {\n const {\n tags = [],\n randomColors,\n getTagName: getTagNameInProps,\n getTagKey,\n tagProps,\n ellipsisTagProps,\n ellipsisDropdownProps,\n className,\n prefixCls: prefixClsInProps,\n ...restProps\n } = props;\n const { token } = theme.useToken();\n const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);\n const prefixCls = getPrefixCls('easy-overflow-tags', prefixClsInProps);\n const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);\n\n const colors = useMemo(\n () => PresetColors.filter((c) => !['lime', 'yellow', 'magenta'].includes(c)).map((color) => token[`${color}-3`]),\n [token]\n );\n const getValue = useCallback((tag: T | undefined, field: string) => {\n if (tag != null && typeof tag === 'object') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const record = tag as Record<string, any>;\n return record[field];\n }\n return undefined;\n }, []);\n const getTagName = useCallback(\n (tag: T) => {\n return getTagNameInProps\n ? getTagNameInProps(tag)\n : (getValue(tag, 'label') ?? getValue(tag, 'name') ?? tag?.toString());\n },\n [getTagNameInProps, getValue]\n );\n const renderTag = (item: T) => {\n return (\n <Tag\n icon={getValue(item, 'icon')}\n color={randomColors ? colors[random(0, colors.length - 1)] : getValue(item, 'color') || 'default'}\n {...(typeof tagProps === 'function' ? tagProps(item, { tags }) : tagProps)}\n >\n {getTagName(item)}\n </Tag>\n );\n };\n\n return wrapCSSVar(\n <Overflow<T>\n className={classNames(hashId, cssVarCls, prefixCls, className)}\n data={tags}\n maxCount=\"responsive\"\n renderItem={renderTag}\n renderRest={(omittedItems) => (\n <Dropdown\n {...ellipsisDropdownProps}\n menu={{\n items: omittedItems.map((tag) => ({\n type: 'item',\n key: getTagKey ? getTagKey(tag) : (getValue(tag, 'value') ?? getValue(tag, 'id') ?? getTagName(tag)),\n label: getTagName(tag),\n })),\n ...ellipsisDropdownProps?.menu,\n }}\n >\n <Tag\n {...(typeof ellipsisTagProps === 'function'\n ? ellipsisTagProps(omittedItems[0], { omittedItems, allTags: tags })\n : ellipsisTagProps)}\n >\n + {omittedItems.length}...\n </Tag>\n </Dropdown>\n )}\n {...restProps}\n />\n );\n};\n\nexport default OverflowTags;\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAAiD;AAEjD,kBAAqD;AACrD,sBAA6B;AAC7B,wBAAuB;AAEvB,yBAAqB;AACrB,kBAAuB;AACvB,mBAAqB;
|
|
4
|
+
"sourcesContent": ["import type { ReactNode } from 'react';\nimport { useCallback, useContext, useMemo } from 'react';\nimport type { DropdownProps, TagProps } from 'antd';\nimport { ConfigProvider, Dropdown, Tag, theme } from 'antd';\nimport { PresetColors } from 'antd/es/theme/internal';\nimport classNames from 'classnames';\nimport type { OverflowProps } from 'rc-overflow';\nimport Overflow from 'rc-overflow';\nimport { random } from '../../utils/math';\nimport useStyle from './style';\n\nexport interface OverflowTagsProps<T> extends Omit<OverflowProps<T>, 'renderItem'> {\n /**\n * **CN**: 标签集合的数据\n *\n * **EN**: Data collection of tags\n */\n tags: T[] | undefined;\n /**\n * **EN**: Function to get the tag name, default is `tag.label` or `tag.name`\n *\n * **CN**: 获取标签名称的函数,默认取`tag.label`或`tag.name`\n */\n getTagName?: (tag: T) => ReactNode;\n /**\n * **EN**: Function to get the unique identifier of the tag, default is `tag.value` or `tag.id`\n *\n * **CN**: 获取标签唯一标识的函数,默认取`tag.value`或`tag.id`\n */\n getTagKey?: (tag: T) => React.Key;\n\n /**\n * **EN**: Custom tag rendering function\n *\n * **CN**: 标签渲染函数\n */\n renderTag?: OverflowProps<T>['renderItem'];\n /**\n * **EN**: Custom properties for the tag component\n *\n * **CN**: 自定义标签的组件属性\n */\n tagProps?: TagProps | ((tag: T, options: { tags: T[] }) => TagProps);\n /**\n * **EN**: When the number of tags exceeds the maximum display count, an ellipsis tag will be\n * shown. This property is used to set the style of the ellipsis tag.\n *\n * **CN**: 当标签数量超过最大显示数量时,会显示省略号的标签,此属性用于设置省略号标签的样式\n */\n ellipsisTagProps?: TagProps | ((tag: T, options: { omittedItems: T[]; allTags: T[] }) => TagProps);\n /**\n * **EN**: Custom properties for the dropdown component when tags are overflowed\n *\n * **CN**: 当标签溢出时,下拉菜单的自定义属性\n */\n ellipsisDropdownProps?: DropdownProps;\n /**\n * **EN**: Whether to use random colors, default is `false`. Note that the tag object can also\n * contain a `color` property to specify the color, and the latter takes precedence.\n *\n * **CN**: 是否使用随机颜色,默认`false`。注意,tag对象还可以包含`color`属性来指定颜色,而且后者优先级更高。\n *\n * @default false\n */\n randomColors?: boolean;\n}\n\n/**\n * - **EN:** Overflow tags component, used to display a collection of tags that can overflow and be\n * truncated. It supports displaying tags in a dropdown when the number of tags exceeds the\n * maximum display count. It also supports custom tag rendering and properties.\n * - **CN:** 溢出标签组件,用于显示一组可以溢出和截断的标签集合。当标签数量超过最大显示数量时,支持在下拉菜单中显示标签。还支持自定义标签渲染和属性。\n *\n * @example\n * <OverflowTags\n * tags={[\n * { value: 1, label: 'Tag1', icon: <Icon1 /> },\n * { value: 2, label: 'Tag2', icon: <Icon2 /> },\n * ]}\n * tagProps={{ color: 'blue' }}\n * ellipsisTagProps={{ color: 'grey' }}\n * />;\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst OverflowTags = <T,>(props: OverflowTagsProps<T>) => {\n const {\n tags = [],\n randomColors,\n getTagName: getTagNameInProps,\n getTagKey,\n tagProps,\n ellipsisTagProps,\n ellipsisDropdownProps,\n className,\n prefixCls: prefixClsInProps,\n ...restProps\n } = props;\n const { token } = theme.useToken();\n const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);\n const prefixCls = getPrefixCls('easy-overflow-tags', prefixClsInProps);\n const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);\n\n const colors = useMemo(\n () => PresetColors.filter((c) => !['lime', 'yellow', 'magenta'].includes(c)).map((color) => token[`${color}-3`]),\n [token]\n );\n const getValue = useCallback((tag: T | undefined, field: string) => {\n if (tag != null && typeof tag === 'object') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const record = tag as Record<string, any>;\n return record[field];\n }\n return undefined;\n }, []);\n const getTagName = useCallback(\n (tag: T) => {\n return getTagNameInProps\n ? getTagNameInProps(tag)\n : (getValue(tag, 'label') ?? getValue(tag, 'name') ?? tag?.toString());\n },\n [getTagNameInProps, getValue]\n );\n const renderTag = (item: T) => {\n return (\n <Tag\n icon={getValue(item, 'icon')}\n color={randomColors ? colors[random(0, colors.length - 1)] : getValue(item, 'color') || 'default'}\n {...(typeof tagProps === 'function' ? tagProps(item, { tags }) : tagProps)}\n >\n {getTagName(item)}\n </Tag>\n );\n };\n\n return wrapCSSVar(\n <Overflow<T>\n className={classNames(hashId, cssVarCls, prefixCls, className)}\n data={tags}\n maxCount=\"responsive\"\n renderItem={renderTag}\n renderRest={(omittedItems) => (\n <Dropdown\n {...ellipsisDropdownProps}\n menu={{\n items: omittedItems.map((tag) => ({\n type: 'item',\n key: getTagKey ? getTagKey(tag) : (getValue(tag, 'value') ?? getValue(tag, 'id') ?? getTagName(tag)),\n label: getTagName(tag),\n })),\n ...ellipsisDropdownProps?.menu,\n }}\n >\n <Tag\n {...(typeof ellipsisTagProps === 'function'\n ? ellipsisTagProps(omittedItems[0], { omittedItems, allTags: tags })\n : ellipsisTagProps)}\n >\n + {omittedItems.length}...\n </Tag>\n </Dropdown>\n )}\n {...restProps}\n />\n );\n};\n\nexport default OverflowTags;\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAAiD;AAEjD,kBAAqD;AACrD,sBAA6B;AAC7B,wBAAuB;AAEvB,yBAAqB;AACrB,kBAAuB;AACvB,mBAAqB;AA2ErB,IAAM,eAAe,CAAK,UAAgC;AACxD,QAAM;AAAA,IACJ,OAAO,CAAC;AAAA,IACR;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,GAAG;AAAA,EACL,IAAI;AACJ,QAAM,EAAE,MAAM,IAAI,kBAAM,SAAS;AACjC,QAAM,EAAE,aAAa,QAAI,yBAAW,2BAAe,aAAa;AAChE,QAAM,YAAY,aAAa,sBAAsB,gBAAgB;AACrE,QAAM,CAAC,YAAY,QAAQ,SAAS,QAAI,aAAAA,SAAS,SAAS;AAE1D,QAAM,aAAS;AAAA,IACb,MAAM,6BAAa,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,UAAU,SAAS,EAAE,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,MAAM,GAAG,SAAS,CAAC;AAAA,IAC/G,CAAC,KAAK;AAAA,EACR;AACA,QAAM,eAAW,0BAAY,CAAC,KAAoB,UAAkB;AAClE,QAAI,OAAO,QAAQ,OAAO,QAAQ,UAAU;AAE1C,YAAM,SAAS;AACf,aAAO,OAAO,KAAK;AAAA,IACrB;AACA,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AACL,QAAM,iBAAa;AAAA,IACjB,CAAC,QAAW;AACV,aAAO,oBACH,kBAAkB,GAAG,IACpB,SAAS,KAAK,OAAO,KAAK,SAAS,KAAK,MAAM,MAAK,2BAAK;AAAA,IAC/D;AAAA,IACA,CAAC,mBAAmB,QAAQ;AAAA,EAC9B;AACA,QAAM,YAAY,CAAC,SAAY;AAC7B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,SAAS,MAAM,MAAM;AAAA,QAC3B,OAAO,eAAe,WAAO,oBAAO,GAAG,OAAO,SAAS,CAAC,CAAC,IAAI,SAAS,MAAM,OAAO,KAAK;AAAA,QACvF,GAAI,OAAO,aAAa,aAAa,SAAS,MAAM,EAAE,KAAK,CAAC,IAAI;AAAA;AAAA,MAEhE,WAAW,IAAI;AAAA,IAClB;AAAA,EAEJ;AAEA,SAAO;AAAA,IACL;AAAA,MAAC,mBAAAC;AAAA,MAAA;AAAA,QACC,eAAW,kBAAAC,SAAW,QAAQ,WAAW,WAAW,SAAS;AAAA,QAC7D,MAAM;AAAA,QACN,UAAS;AAAA,QACT,YAAY;AAAA,QACZ,YAAY,CAAC,iBACX;AAAA,UAAC;AAAA;AAAA,YACE,GAAG;AAAA,YACJ,MAAM;AAAA,cACJ,OAAO,aAAa,IAAI,CAAC,SAAS;AAAA,gBAChC,MAAM;AAAA,gBACN,KAAK,YAAY,UAAU,GAAG,IAAK,SAAS,KAAK,OAAO,KAAK,SAAS,KAAK,IAAI,KAAK,WAAW,GAAG;AAAA,gBAClG,OAAO,WAAW,GAAG;AAAA,cACvB,EAAE;AAAA,cACF,GAAG,+DAAuB;AAAA,YAC5B;AAAA;AAAA,UAEA;AAAA,YAAC;AAAA;AAAA,cACE,GAAI,OAAO,qBAAqB,aAC7B,iBAAiB,aAAa,CAAC,GAAG,EAAE,cAAc,SAAS,KAAK,CAAC,IACjE;AAAA;AAAA,YACL;AAAA,YACI,aAAa;AAAA,YAAO;AAAA,UACzB;AAAA,QACF;AAAA,QAED,GAAG;AAAA;AAAA,IACN;AAAA,EACF;AACF;AAEA,IAAO,uBAAQ;",
|
|
6
6
|
"names": ["useStyle", "Overflow", "classNames"]
|
|
7
7
|
}
|
package/lib/hooks/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { default as useAudioPlayer } from './useAudioPlayer';
|
|
2
2
|
export { default as useDebounce } from './useDebounce';
|
|
3
|
+
export * from './useLocalStorage';
|
|
4
|
+
export { default as useLocalStorage } from './useLocalStorage';
|
|
3
5
|
export * from './useMovable';
|
|
4
6
|
export { default as useMovable } from './useMovable';
|
|
5
7
|
export * from './useProcessingText';
|
package/lib/hooks/index.js
CHANGED
|
@@ -32,6 +32,7 @@ var hooks_exports = {};
|
|
|
32
32
|
__export(hooks_exports, {
|
|
33
33
|
useAudioPlayer: () => import_useAudioPlayer.default,
|
|
34
34
|
useDebounce: () => import_useDebounce.default,
|
|
35
|
+
useLocalStorage: () => import_useLocalStorage.default,
|
|
35
36
|
useMovable: () => import_useMovable.default,
|
|
36
37
|
useProcessingText: () => import_useProcessingText.default,
|
|
37
38
|
useRefFunction: () => import_useRefFunction.default,
|
|
@@ -47,6 +48,8 @@ __export(hooks_exports, {
|
|
|
47
48
|
module.exports = __toCommonJS(hooks_exports);
|
|
48
49
|
var import_useAudioPlayer = __toESM(require("./useAudioPlayer"));
|
|
49
50
|
var import_useDebounce = __toESM(require("./useDebounce"));
|
|
51
|
+
__reExport(hooks_exports, require("./useLocalStorage"), module.exports);
|
|
52
|
+
var import_useLocalStorage = __toESM(require("./useLocalStorage"));
|
|
50
53
|
__reExport(hooks_exports, require("./useMovable"), module.exports);
|
|
51
54
|
var import_useMovable = __toESM(require("./useMovable"));
|
|
52
55
|
__reExport(hooks_exports, require("./useProcessingText"), module.exports);
|
|
@@ -68,6 +71,7 @@ var import_useValidatorBuilder = __toESM(require("./useValidatorBuilder"));
|
|
|
68
71
|
0 && (module.exports = {
|
|
69
72
|
useAudioPlayer,
|
|
70
73
|
useDebounce,
|
|
74
|
+
useLocalStorage,
|
|
71
75
|
useMovable,
|
|
72
76
|
useProcessingText,
|
|
73
77
|
useRefFunction,
|
|
@@ -79,6 +83,7 @@ var import_useValidatorBuilder = __toESM(require("./useValidatorBuilder"));
|
|
|
79
83
|
useValidator,
|
|
80
84
|
useValidatorBuilder,
|
|
81
85
|
useValidators,
|
|
86
|
+
...require("./useLocalStorage"),
|
|
82
87
|
...require("./useMovable"),
|
|
83
88
|
...require("./useProcessingText"),
|
|
84
89
|
...require("./useSplitter"),
|
package/lib/hooks/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/hooks/index.ts"],
|
|
4
|
-
"sourcesContent": ["export { default as useAudioPlayer } from './useAudioPlayer';\n\nexport { default as useDebounce } from './useDebounce';\n\nexport * from './useMovable';\nexport { default as useMovable } from './useMovable';\n\nexport * from './useProcessingText';\nexport { default as useProcessingText } from './useProcessingText';\n\nexport { default as useRefValue } from './useRefValue';\n\nexport { default as useRefFunction } from './useRefFunction';\n\nexport * from './useSplitter';\nexport { default as useSplitter } from './useSplitter';\n\nexport * from './useSSE';\nexport { default as useSSE } from './useSSE';\n\nexport * from './useStompSocket';\nexport { default as useStompSocket } from './useStompSocket';\n\nexport * from './useUserMedia';\nexport { default as useUserMedia } from './useUserMedia';\n\nexport type { ValidatorRuleMap } from './useValidators';\nexport { default as useValidators } from './useValidators';\n\nexport { default as useValidator } from './useValidator';\n\nexport type { Validator, RuleRegExpFlags, ValidatorRule, BuilderOptions } from './useValidatorBuilder';\nexport { default as useValidatorBuilder } from './useValidatorBuilder';\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAA0C;AAE1C,yBAAuC;AAEvC,0BAAc,
|
|
4
|
+
"sourcesContent": ["export { default as useAudioPlayer } from './useAudioPlayer';\n\nexport { default as useDebounce } from './useDebounce';\n\nexport * from './useLocalStorage';\nexport { default as useLocalStorage } from './useLocalStorage';\n\nexport * from './useMovable';\nexport { default as useMovable } from './useMovable';\n\nexport * from './useProcessingText';\nexport { default as useProcessingText } from './useProcessingText';\n\nexport { default as useRefValue } from './useRefValue';\n\nexport { default as useRefFunction } from './useRefFunction';\n\nexport * from './useSplitter';\nexport { default as useSplitter } from './useSplitter';\n\nexport * from './useSSE';\nexport { default as useSSE } from './useSSE';\n\nexport * from './useStompSocket';\nexport { default as useStompSocket } from './useStompSocket';\n\nexport * from './useUserMedia';\nexport { default as useUserMedia } from './useUserMedia';\n\nexport type { ValidatorRuleMap } from './useValidators';\nexport { default as useValidators } from './useValidators';\n\nexport { default as useValidator } from './useValidator';\n\nexport type { Validator, RuleRegExpFlags, ValidatorRule, BuilderOptions } from './useValidatorBuilder';\nexport { default as useValidatorBuilder } from './useValidatorBuilder';\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAA0C;AAE1C,yBAAuC;AAEvC,0BAAc,8BAJd;AAKA,6BAA2C;AAE3C,0BAAc,yBAPd;AAQA,wBAAsC;AAEtC,0BAAc,gCAVd;AAWA,+BAA6C;AAE7C,yBAAuC;AAEvC,4BAA0C;AAE1C,0BAAc,0BAjBd;AAkBA,yBAAuC;AAEvC,0BAAc,qBApBd;AAqBA,oBAAkC;AAElC,0BAAc,6BAvBd;AAwBA,4BAA0C;AAE1C,0BAAc,2BA1Bd;AA2BA,0BAAwC;AAGxC,2BAAyC;AAEzC,0BAAwC;AAGxC,iCAA+C;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type SetStateAction<T> = T | ((prev: T) => T);
|
|
2
|
+
export interface UseLocalStorageOptions<T> {
|
|
3
|
+
/** When true, listen to `storage` events and keep state in sync across tabs. Defaults to true. */
|
|
4
|
+
sync?: boolean;
|
|
5
|
+
/** Custom serializer. Defaults to JSON.stringify. */
|
|
6
|
+
serialize?: (value: T) => string;
|
|
7
|
+
/** Custom deserializer. Defaults to JSON.parse. */
|
|
8
|
+
deserialize?: (raw: string) => T;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* useLocalStorage
|
|
12
|
+
*
|
|
13
|
+
* - If `key` is empty/falsy, behaves like useState and does not touch localStorage.
|
|
14
|
+
*/
|
|
15
|
+
declare function useLocalStorage<T>(key?: string | null, initialValue?: T | (() => T), options?: UseLocalStorageOptions<T>): [T, (action: SetStateAction<T>) => void, () => void];
|
|
16
|
+
export default useLocalStorage;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/hooks/useLocalStorage.ts
|
|
20
|
+
var useLocalStorage_exports = {};
|
|
21
|
+
__export(useLocalStorage_exports, {
|
|
22
|
+
default: () => useLocalStorage_default
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(useLocalStorage_exports);
|
|
25
|
+
var import_react = require("react");
|
|
26
|
+
function isBrowser() {
|
|
27
|
+
return typeof window !== "undefined" && typeof window.localStorage !== "undefined";
|
|
28
|
+
}
|
|
29
|
+
function defaultSerialize(value) {
|
|
30
|
+
return JSON.stringify(value);
|
|
31
|
+
}
|
|
32
|
+
function defaultDeserialize(raw) {
|
|
33
|
+
return JSON.parse(raw);
|
|
34
|
+
}
|
|
35
|
+
function useLocalStorage(key, initialValue, options) {
|
|
36
|
+
const storageKey = (key ?? "").trim();
|
|
37
|
+
const enabled = storageKey.length > 0;
|
|
38
|
+
const sync = (options == null ? void 0 : options.sync) ?? true;
|
|
39
|
+
const serialize = (options == null ? void 0 : options.serialize) ?? defaultSerialize;
|
|
40
|
+
const deserialize = (options == null ? void 0 : options.deserialize) ?? defaultDeserialize;
|
|
41
|
+
const initialValueRef = (0, import_react.useRef)(initialValue);
|
|
42
|
+
initialValueRef.current = initialValue;
|
|
43
|
+
const getInitial = (0, import_react.useCallback)(() => {
|
|
44
|
+
const fallback = (() => {
|
|
45
|
+
const v = initialValueRef.current;
|
|
46
|
+
return typeof v === "function" ? v() : v;
|
|
47
|
+
})();
|
|
48
|
+
if (!enabled || !isBrowser())
|
|
49
|
+
return fallback;
|
|
50
|
+
try {
|
|
51
|
+
const raw = window.localStorage.getItem(storageKey);
|
|
52
|
+
if (raw == null)
|
|
53
|
+
return fallback;
|
|
54
|
+
return deserialize(raw);
|
|
55
|
+
} catch {
|
|
56
|
+
return fallback;
|
|
57
|
+
}
|
|
58
|
+
}, [deserialize, enabled, storageKey]);
|
|
59
|
+
const [value, setValueState] = (0, import_react.useState)(getInitial);
|
|
60
|
+
const writeStorage = (0, import_react.useCallback)(
|
|
61
|
+
(nextValue) => {
|
|
62
|
+
if (!enabled || !isBrowser())
|
|
63
|
+
return;
|
|
64
|
+
try {
|
|
65
|
+
window.localStorage.setItem(storageKey, serialize(nextValue));
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
[enabled, serialize, storageKey]
|
|
70
|
+
);
|
|
71
|
+
const setValue = (0, import_react.useCallback)(
|
|
72
|
+
(action) => {
|
|
73
|
+
setValueState((prev) => {
|
|
74
|
+
const next = typeof action === "function" ? action(prev) : action;
|
|
75
|
+
writeStorage(next);
|
|
76
|
+
return next;
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
[writeStorage]
|
|
80
|
+
);
|
|
81
|
+
const remove = (0, import_react.useCallback)(() => {
|
|
82
|
+
const next = (() => {
|
|
83
|
+
const v = initialValueRef.current;
|
|
84
|
+
return typeof v === "function" ? v() : v;
|
|
85
|
+
})();
|
|
86
|
+
setValueState(next);
|
|
87
|
+
if (!enabled || !isBrowser())
|
|
88
|
+
return;
|
|
89
|
+
try {
|
|
90
|
+
window.localStorage.removeItem(storageKey);
|
|
91
|
+
} catch {
|
|
92
|
+
}
|
|
93
|
+
}, [enabled, storageKey]);
|
|
94
|
+
(0, import_react.useEffect)(() => {
|
|
95
|
+
setValueState(getInitial());
|
|
96
|
+
}, [getInitial]);
|
|
97
|
+
(0, import_react.useEffect)(() => {
|
|
98
|
+
if (!enabled || !sync || !isBrowser())
|
|
99
|
+
return;
|
|
100
|
+
const onStorage = (e) => {
|
|
101
|
+
if (e.storageArea !== window.localStorage)
|
|
102
|
+
return;
|
|
103
|
+
if (e.key !== storageKey)
|
|
104
|
+
return;
|
|
105
|
+
if (e.newValue == null) {
|
|
106
|
+
setValueState(getInitial());
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
setValueState(deserialize(e.newValue));
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
window.addEventListener("storage", onStorage);
|
|
115
|
+
return () => window.removeEventListener("storage", onStorage);
|
|
116
|
+
}, [deserialize, enabled, getInitial, storageKey, sync]);
|
|
117
|
+
return (0, import_react.useMemo)(() => [value, setValue, remove], [remove, setValue, value]);
|
|
118
|
+
}
|
|
119
|
+
var useLocalStorage_default = useLocalStorage;
|
|
120
|
+
//# sourceMappingURL=useLocalStorage.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/hooks/useLocalStorage.ts"],
|
|
4
|
+
"sourcesContent": ["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\ntype SetStateAction<T> = T | ((prev: T) => T);\n\nexport interface UseLocalStorageOptions<T> {\n /** When true, listen to `storage` events and keep state in sync across tabs. Defaults to true. */\n sync?: boolean;\n /** Custom serializer. Defaults to JSON.stringify. */\n serialize?: (value: T) => string;\n /** Custom deserializer. Defaults to JSON.parse. */\n deserialize?: (raw: string) => T;\n}\n\nfunction isBrowser() {\n return typeof window !== 'undefined' && typeof window.localStorage !== 'undefined';\n}\n\nfunction defaultSerialize<T>(value: T) {\n return JSON.stringify(value);\n}\n\nfunction defaultDeserialize<T>(raw: string) {\n return JSON.parse(raw) as T;\n}\n\n/**\n * useLocalStorage\n *\n * - If `key` is empty/falsy, behaves like useState and does not touch localStorage.\n */\nfunction useLocalStorage<T>(\n key?: string | null,\n initialValue?: T | (() => T),\n options?: UseLocalStorageOptions<T>\n): [T, (action: SetStateAction<T>) => void, () => void] {\n const storageKey = (key ?? '').trim();\n const enabled = storageKey.length > 0;\n const sync = options?.sync ?? true;\n const serialize = options?.serialize ?? defaultSerialize;\n const deserialize = options?.deserialize ?? defaultDeserialize;\n\n const initialValueRef = useRef(initialValue);\n initialValueRef.current = initialValue;\n\n const getInitial = useCallback((): T => {\n const fallback = (() => {\n const v = initialValueRef.current;\n return (typeof v === 'function' ? (v as () => T)() : (v as T)) as T;\n })();\n\n if (!enabled || !isBrowser()) return fallback;\n\n try {\n const raw = window.localStorage.getItem(storageKey);\n if (raw == null) return fallback;\n return deserialize(raw);\n } catch {\n return fallback;\n }\n }, [deserialize, enabled, storageKey]);\n\n const [value, setValueState] = useState<T>(getInitial);\n\n const writeStorage = useCallback(\n (nextValue: T) => {\n if (!enabled || !isBrowser()) return;\n try {\n window.localStorage.setItem(storageKey, serialize(nextValue));\n } catch {\n // ignore write errors (e.g., quota exceeded, blocked storage)\n }\n },\n [enabled, serialize, storageKey]\n );\n\n const setValue = useCallback(\n (action: SetStateAction<T>) => {\n setValueState((prev) => {\n const next = typeof action === 'function' ? (action as (p: T) => T)(prev) : action;\n writeStorage(next);\n return next;\n });\n },\n [writeStorage]\n );\n\n const remove = useCallback(() => {\n // If key is empty/falsy, just reset to initial value.\n const next = (() => {\n const v = initialValueRef.current;\n return (typeof v === 'function' ? (v as () => T)() : (v as T)) as T;\n })();\n\n setValueState(next);\n\n if (!enabled || !isBrowser()) return;\n try {\n window.localStorage.removeItem(storageKey);\n } catch {\n // ignore\n }\n }, [enabled, storageKey]);\n\n // Keep state updated if key changes.\n useEffect(() => {\n setValueState(getInitial());\n }, [getInitial]);\n\n // Cross-tab sync.\n useEffect(() => {\n if (!enabled || !sync || !isBrowser()) return;\n\n const onStorage = (e: StorageEvent) => {\n if (e.storageArea !== window.localStorage) return;\n if (e.key !== storageKey) return;\n\n // When removed, fall back to initial.\n if (e.newValue == null) {\n setValueState(getInitial());\n return;\n }\n\n try {\n setValueState(deserialize(e.newValue));\n } catch {\n // ignore parse errors\n }\n };\n\n window.addEventListener('storage', onStorage);\n return () => window.removeEventListener('storage', onStorage);\n }, [deserialize, enabled, getInitial, storageKey, sync]);\n\n // Stable tuple identity.\n return useMemo(() => [value, setValue, remove] as const, [remove, setValue, value]);\n}\n\nexport default useLocalStorage;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|