mirror of
https://github.com/dream-num/univer.git
synced 2026-09-24 15:32:18 +08:00
feat(design): input support slot usage (#5095)
This commit is contained in:
@@ -67,7 +67,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/docs": "workspace:*",
|
||||
"@univerjs/docs-ui": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import type { Meta } from '@storybook/react';
|
||||
|
||||
import { Pager } from '../pager/Pager';
|
||||
import { Input } from './Input';
|
||||
|
||||
const meta: Meta<typeof Input> = {
|
||||
@@ -62,3 +63,24 @@ export const InputDisabled = {
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const Clear = {
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Input allowClear />
|
||||
<Input value="content" allowClear />
|
||||
</>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const ClearWithPage = {
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Input value="with some very long content to push content offset" allowClear slot={<Pager value={1} total={10} />} />
|
||||
</>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import type { VariantProps } from 'class-variance-authority';
|
||||
import { CloseSingle } from '@univerjs/icons';
|
||||
import { cva } from 'class-variance-authority';
|
||||
import { forwardRef } from 'react';
|
||||
import React, { forwardRef, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { clsx } from '../../helper/clsx';
|
||||
|
||||
type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
|
||||
@@ -26,7 +26,7 @@ export const inputVariants = cva(
|
||||
`
|
||||
univer-box-border univer-w-full univer-rounded-md univer-border univer-border-solid univer-border-gray-200
|
||||
univer-bg-white univer-transition-colors univer-duration-200
|
||||
dark:univer-bg-gray-700 dark:univer-border-gray-600 dark:univer-text-white
|
||||
dark:univer-border-gray-600 dark:univer-bg-gray-700 dark:univer-text-white
|
||||
focus:univer-border-primary-500 focus:univer-outline-none focus:univer-ring-2 focus:univer-ring-primary-500/20
|
||||
placeholder:univer-text-gray-400
|
||||
`,
|
||||
@@ -59,6 +59,7 @@ export interface IInputProps extends Pick<InputProps, 'onFocus' | 'onBlur'>,
|
||||
onChange?: (value: string) => void;
|
||||
inputClass?: string;
|
||||
inputStyle?: React.CSSProperties;
|
||||
slot?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const Input = forwardRef<HTMLInputElement, IInputProps>(
|
||||
@@ -77,6 +78,7 @@ export const Input = forwardRef<HTMLInputElement, IInputProps>(
|
||||
onChange,
|
||||
onFocus,
|
||||
onBlur,
|
||||
slot,
|
||||
inputClass,
|
||||
inputStyle,
|
||||
...props
|
||||
@@ -90,6 +92,30 @@ export const Input = forwardRef<HTMLInputElement, IInputProps>(
|
||||
onChange?.(e.target.value);
|
||||
};
|
||||
|
||||
const hasSlotContent = useMemo(() => {
|
||||
return (allowClear && value && !disabled) || slot;
|
||||
}, [allowClear, disabled, slot, value]);
|
||||
|
||||
const [paddingRight, setPaddingRight] = useState(0);
|
||||
const slotRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let observer: MutationObserver | null = null;
|
||||
if (slot && slotRef.current) {
|
||||
observer = new MutationObserver(() => {
|
||||
if (slotRef.current) {
|
||||
setPaddingRight(slotRef.current.offsetWidth + 4 * 2);
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(slotRef.current, { childList: true, subtree: true });
|
||||
// eslint-disable-next-line react-hooks-extra/no-direct-set-state-in-use-effect
|
||||
setPaddingRight(slotRef.current.offsetWidth + 4 * 2);
|
||||
}
|
||||
|
||||
return () => observer?.disconnect();
|
||||
}, [slotRef.current]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
@@ -105,10 +131,10 @@ export const Input = forwardRef<HTMLInputElement, IInputProps>(
|
||||
className={clsx(
|
||||
inputVariants({ size }),
|
||||
disabled && `
|
||||
univer-cursor-not-allowed univer-text-gray-400 univer-bg-gray-50
|
||||
univer-cursor-not-allowed univer-bg-gray-50 univer-text-gray-400
|
||||
dark:univer-text-gray-500
|
||||
`,
|
||||
allowClear && 'univer-pr-8',
|
||||
(allowClear && !slot) && 'univer-pr-8',
|
||||
inputClass
|
||||
)}
|
||||
placeholder={placeholder}
|
||||
@@ -120,23 +146,34 @@ export const Input = forwardRef<HTMLInputElement, IInputProps>(
|
||||
onChange={handleChange}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
style={inputStyle}
|
||||
style={{ ...inputStyle, paddingRight }}
|
||||
{...props}
|
||||
/>
|
||||
{allowClear && value && !disabled && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClear}
|
||||
{hasSlotContent && (
|
||||
<div
|
||||
className={`
|
||||
univer-absolute univer-right-2 univer-flex univer-items-center univer-rounded-full
|
||||
univer-border-none univer-bg-transparent univer-p-1 univer-text-gray-400
|
||||
univer-transition-colors univer-duration-200
|
||||
focus:univer-outline-none
|
||||
hover:univer-text-gray-500
|
||||
univer-absolute univer-right-2 univer-flex univer-items-center univer-gap-1
|
||||
univer-rounded-full
|
||||
`}
|
||||
ref={slotRef}
|
||||
>
|
||||
<CloseSingle className="univer-size-4" />
|
||||
</button>
|
||||
{slot}
|
||||
{allowClear && value && !disabled && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClear}
|
||||
className={`
|
||||
univer-flex univer-size-4 univer-cursor-pointer univer-rounded-full univer-border-none
|
||||
univer-bg-transparent univer-p-1 univer-text-gray-400 univer-transition-colors
|
||||
univer-duration-200
|
||||
focus:univer-outline-none
|
||||
hover:univer-text-gray-500
|
||||
`}
|
||||
>
|
||||
<CloseSingle className="univer-size-3" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -38,7 +38,7 @@ function useFindInputFocus(findReplaceService: IFindReplaceService, ref: Forward
|
||||
}, []);
|
||||
|
||||
const selectHasFocus = useCallback(() => {
|
||||
const allInputs = document.querySelectorAll('.univer-find-replace-dialog-container .univer-select-selection-search-input');
|
||||
const allInputs = document.querySelectorAll('[data-u-comp="find-replace-dialog"] [data-u-comp="search-input"]');
|
||||
return Array.from(allInputs).some((input) => input === document.activeElement);
|
||||
}, []);
|
||||
|
||||
@@ -95,7 +95,7 @@ export const FindDialog = forwardRef(function FindDialogImpl(_props, ref) {
|
||||
);
|
||||
});
|
||||
|
||||
export const ReplaceDialog = forwardRef(function ReplaceDIalogImpl(_props, ref) {
|
||||
export const ReplaceDialog = forwardRef(function ReplaceDialogImpl(_props, ref) {
|
||||
const findReplaceService = useDependency(IFindReplaceService);
|
||||
const localeService = useDependency(LocaleService);
|
||||
const commandService = useDependency(ICommandService);
|
||||
@@ -297,7 +297,7 @@ export function FindReplaceDialog() {
|
||||
}, [setDialogContainerFocus, setDialogInputFocus]);
|
||||
|
||||
return (
|
||||
<div ref={dialogContainerRef}>
|
||||
<div ref={dialogContainerRef} data-u-comp="find-replace-dialog">
|
||||
{!state.replaceRevealed ? <FindDialog ref={focusRef} /> : <ReplaceDialog ref={focusRef} />}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -40,30 +40,32 @@ export function SearchInput(props: ISearchInputProps) {
|
||||
return (
|
||||
<div className="univer-relative univer-flex univer-items-center univer-gap-2" onDrag={(e) => e.stopPropagation()}>
|
||||
<Input
|
||||
data-u-comp="search-input"
|
||||
autoFocus
|
||||
placeholder={localeService.t('find-replace.dialog.find-placeholder')}
|
||||
value={findString}
|
||||
onChange={(value) => onChange?.(value)}
|
||||
slot={(
|
||||
<Pager
|
||||
loop
|
||||
text={text}
|
||||
value={matchesPosition}
|
||||
total={matchesCount}
|
||||
onChange={(newIndex) => {
|
||||
if (matchesPosition === matchesCount && newIndex === 1) {
|
||||
findReplaceService.moveToNextMatch();
|
||||
} else if (matchesPosition === 1 && newIndex === matchesCount) {
|
||||
findReplaceService.moveToPreviousMatch();
|
||||
} else if (newIndex < matchesPosition) {
|
||||
findReplaceService.moveToPreviousMatch();
|
||||
} else {
|
||||
findReplaceService.moveToNextMatch();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{...rest}
|
||||
/>
|
||||
|
||||
<Pager
|
||||
loop
|
||||
text={text}
|
||||
value={matchesPosition}
|
||||
total={matchesCount}
|
||||
onChange={(newIndex) => {
|
||||
if (matchesPosition === matchesCount && newIndex === 1) {
|
||||
findReplaceService.moveToNextMatch();
|
||||
} else if (matchesPosition === 1 && newIndex === matchesCount) {
|
||||
findReplaceService.moveToPreviousMatch();
|
||||
} else if (newIndex < matchesPosition) {
|
||||
findReplaceService.moveToPreviousMatch();
|
||||
} else {
|
||||
findReplaceService.moveToNextMatch();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Generated
-3
@@ -804,9 +804,6 @@ importers:
|
||||
'@univerjs/core':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/core
|
||||
'@univerjs/design':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/design
|
||||
'@univerjs/docs':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/docs
|
||||
|
||||
Reference in New Issue
Block a user