refactor: mentions (#6255)

* refactor: mentions

* refactor: mentions menu provider
This commit is contained in:
Zev Zhu 2023-02-14 14:09:23 +08:00 committed by GitHub
parent 44e5d09f22
commit 92795a828f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 263 additions and 218 deletions

View File

@ -12,6 +12,9 @@ import { optionProps } from '../vc-mentions/src/Option';
import type { KeyboardEventHandler } from '../_util/EventInterface';
import type { InputStatus } from '../_util/statusUtils';
import { getStatusClassNames, getMergedStatus } from '../_util/statusUtils';
import useStyle from './style';
import { useProvideOverride } from '../menu/src/OverrideContext';
import warning from '../_util/warning';
interface MentionsConfig {
prefix?: string | string[];
@ -98,12 +101,27 @@ const Mentions = defineComponent({
slots: ['notFoundContent', 'option'],
setup(props, { slots, emit, attrs, expose }) {
const { prefixCls, renderEmpty, direction } = useConfigInject('mentions', props);
const [wrapSSR, hashId] = useStyle(prefixCls);
const focused = ref(false);
const vcMentions = ref(null);
const value = ref(props.value ?? props.defaultValue ?? '');
const formItemContext = useInjectFormItemContext();
const formItemInputContext = FormItemInputContext.useInject();
const mergedStatus = computed(() => getMergedStatus(formItemInputContext.status, props.status));
useProvideOverride({
prefixCls: computed(() => `${prefixCls.value}-menu`),
mode: computed(() => 'vertical'),
selectable: computed(() => false),
onClick: () => {},
validator: ({ mode }) => {
// Warning if use other mode
warning(
!mode || mode === 'vertical',
'Mentions',
`mode="${mode}" is not supported for Mentions's Menu.`,
);
},
});
watch(
() => props.value,
val => {
@ -182,6 +200,7 @@ const Mentions = defineComponent({
},
getStatusClassNames(prefixCls.value, mergedStatus.value),
!hasFeedback && className,
hashId.value,
);
const mentionsProps = {
@ -206,11 +225,12 @@ const Mentions = defineComponent({
const mentions = (
<VcMentions
{...mentionsProps}
dropdownClassName={hashId.value}
v-slots={{ notFoundContent: getNotFoundContent, option: slots.option }}
></VcMentions>
);
if (hasFeedback) {
return (
return wrapSSR(
<div
class={classNames(
`${prefixCls.value}-affix-wrapper`,
@ -220,14 +240,15 @@ const Mentions = defineComponent({
hasFeedback,
),
className,
hashId.value,
)}
>
{mentions}
<span class={`${prefixCls.value}-suffix`}>{feedbackIcon}</span>
</div>
</div>,
);
}
return mentions;
return wrapSSR(mentions);
};
},
});

View File

@ -1,179 +0,0 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@import '../../input/style/mixin';
@import './status';
@mention-prefix-cls: ~'@{ant-prefix}-mentions';
.@{mention-prefix-cls} {
.reset-component();
.input();
position: relative;
display: inline-block;
height: auto;
padding: 0;
overflow: hidden;
line-height: @line-height-base;
white-space: pre-wrap;
vertical-align: bottom;
// =================== Status ===================
&-disabled {
> textarea {
.disabled();
}
}
&-focused {
.active();
}
// ================= Input Area =================
> textarea,
&-measure {
min-height: @input-height-base - 2px;
margin: 0;
padding: @input-padding-vertical-base @input-padding-horizontal-base;
overflow: inherit;
overflow-x: hidden;
overflow-y: auto;
/* stylelint-disable declaration-block-no-redundant-longhand-properties */
font-weight: inherit;
font-size: inherit;
font-family: inherit;
font-style: inherit;
font-variant: inherit;
font-size-adjust: inherit;
font-stretch: inherit;
line-height: inherit;
/* stylelint-enable declaration-block-no-redundant-longhand-properties */
direction: inherit;
letter-spacing: inherit;
white-space: inherit;
text-align: inherit;
vertical-align: top;
word-wrap: break-word;
word-break: inherit;
tab-size: inherit;
}
> textarea {
width: 100%;
border: none;
outline: none;
resize: none;
& when (@theme = dark) {
background-color: transparent;
}
.placeholder();
}
&-measure {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
color: transparent;
pointer-events: none;
> span {
display: inline-block;
min-height: 1em;
}
}
// ================== Dropdown ==================
&-dropdown {
// Ref select dropdown style
.reset-component();
position: absolute;
top: -9999px;
left: -9999px;
z-index: @zindex-dropdown;
box-sizing: border-box;
font-size: @font-size-base;
font-variant: initial;
background-color: @mentions-dropdown-bg;
border-radius: @border-radius-base;
outline: none;
box-shadow: @box-shadow-base;
&-hidden {
display: none;
}
&-menu {
max-height: 250px;
margin-bottom: 0;
padding-left: 0; // Override default ul/ol
overflow: auto;
list-style: none;
outline: none;
&-item {
position: relative;
display: block;
min-width: 100px;
padding: 5px @control-padding-horizontal;
overflow: hidden;
color: @text-color;
font-weight: normal;
line-height: @line-height-base;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
transition: background 0.3s ease;
&:hover {
background-color: @item-hover-bg;
}
&:first-child {
border-radius: @border-radius-base @border-radius-base 0 0;
}
&:last-child {
border-radius: 0 0 @border-radius-base @border-radius-base;
}
&-disabled {
color: @disabled-color;
cursor: not-allowed;
&:hover {
color: @disabled-color;
background-color: @mentions-dropdown-menu-item-hover-bg;
cursor: not-allowed;
}
}
&-selected {
color: @text-color;
font-weight: @select-item-selected-font-weight;
background-color: @background-color-light;
}
&-active {
background-color: @item-hover-bg;
}
}
}
}
&-suffix {
position: absolute;
top: 0;
right: @input-padding-horizontal-base;
bottom: 0;
z-index: 1;
display: inline-flex;
align-items: center;
margin: auto;
}
}
@import './rtl';

View File

@ -0,0 +1,232 @@
import type { InputToken } from '../../input/style';
import {
genActiveStyle,
genBasicInputStyle,
genDisabledStyle,
genPlaceholderStyle,
genStatusStyle,
initInputToken,
} from '../../input/style';
import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook } from '../../theme/internal';
import { resetComponent, textEllipsis } from '../../_style';
export interface ComponentToken {
zIndexPopup: number;
dropdownHeight: number;
controlItemWidth: number;
}
type MentionsToken = InputToken<FullToken<'Mentions'>>;
const genMentionsStyle: GenerateStyle<MentionsToken> = token => {
const {
componentCls,
colorTextDisabled,
controlItemBgHover,
controlPaddingHorizontal,
colorText,
motionDurationSlow,
lineHeight,
controlHeight,
inputPaddingHorizontal,
inputPaddingVertical,
fontSize,
colorBgElevated,
borderRadiusLG,
boxShadowSecondary,
} = token;
const itemPaddingVertical = Math.round(
(token.controlHeight - token.fontSize * token.lineHeight) / 2,
);
return {
[componentCls]: {
...resetComponent(token),
...genBasicInputStyle(token),
position: 'relative',
display: 'inline-block',
height: 'auto',
padding: 0,
overflow: 'hidden',
lineHeight,
whiteSpace: 'pre-wrap',
verticalAlign: 'bottom',
...genStatusStyle(token, componentCls),
'&-disabled': {
'> textarea': {
...genDisabledStyle(token),
},
},
'&-focused': {
...genActiveStyle(token),
},
[`&-affix-wrapper ${componentCls}-suffix`]: {
position: 'absolute',
top: 0,
insetInlineEnd: inputPaddingHorizontal,
bottom: 0,
zIndex: 1,
display: 'inline-flex',
alignItems: 'center',
margin: 'auto',
},
// ================= Input Area =================
[`> textarea, ${componentCls}-measure`]: {
color: colorText,
boxSizing: 'border-box',
minHeight: controlHeight - 2,
margin: 0,
padding: `${inputPaddingVertical}px ${inputPaddingHorizontal}px`,
overflow: 'inherit',
overflowX: 'hidden',
overflowY: 'auto',
fontWeight: 'inherit',
fontSize: 'inherit',
fontFamily: 'inherit',
fontStyle: 'inherit',
fontVariant: 'inherit',
fontSizeAdjust: 'inherit',
fontStretch: 'inherit',
lineHeight: 'inherit',
direction: 'inherit',
letterSpacing: 'inherit',
whiteSpace: 'inherit',
textAlign: 'inherit',
verticalAlign: 'top',
wordWrap: 'break-word',
wordBreak: 'inherit',
tabSize: 'inherit',
},
'> textarea': {
width: '100%',
border: 'none',
outline: 'none',
resize: 'none',
backgroundColor: 'inherit',
...genPlaceholderStyle(token.colorTextPlaceholder),
},
[`${componentCls}-measure`]: {
position: 'absolute',
top: 0,
insetInlineEnd: 0,
bottom: 0,
insetInlineStart: 0,
zIndex: -1,
color: 'transparent',
pointerEvents: 'none',
'> span': {
display: 'inline-block',
minHeight: '1em',
},
},
// ================== Dropdown ==================
'&-dropdown': {
// Ref select dropdown style
...resetComponent(token),
position: 'absolute',
top: -9999,
insetInlineStart: -9999,
zIndex: token.zIndexPopup,
boxSizing: 'border-box',
fontSize,
fontVariant: 'initial',
backgroundColor: colorBgElevated,
borderRadius: borderRadiusLG,
outline: 'none',
boxShadow: boxShadowSecondary,
'&-hidden': {
display: 'none',
},
[`${componentCls}-dropdown-menu`]: {
maxHeight: token.dropdownHeight,
marginBottom: 0,
paddingInlineStart: 0, // Override default ul/ol
overflow: 'auto',
listStyle: 'none',
outline: 'none',
'&-item': {
...textEllipsis,
position: 'relative',
display: 'block',
minWidth: token.controlItemWidth,
padding: `${itemPaddingVertical}px ${controlPaddingHorizontal}px`,
color: colorText,
fontWeight: 'normal',
lineHeight,
cursor: 'pointer',
transition: `background ${motionDurationSlow} ease`,
'&:hover': {
backgroundColor: controlItemBgHover,
},
'&:first-child': {
borderStartStartRadius: borderRadiusLG,
borderStartEndRadius: borderRadiusLG,
borderEndStartRadius: 0,
borderEndEndRadius: 0,
},
'&:last-child': {
borderStartStartRadius: 0,
borderStartEndRadius: 0,
borderEndStartRadius: borderRadiusLG,
borderEndEndRadius: borderRadiusLG,
},
'&-disabled': {
color: colorTextDisabled,
cursor: 'not-allowed',
'&:hover': {
color: colorTextDisabled,
backgroundColor: controlItemBgHover,
cursor: 'not-allowed',
},
},
'&-selected': {
color: colorText,
fontWeight: token.fontWeightStrong,
backgroundColor: controlItemBgHover,
},
'&-active': {
backgroundColor: controlItemBgHover,
},
},
},
},
},
};
};
// ============================== Export ==============================
export default genComponentStyleHook(
'Mentions',
token => {
const mentionsToken = initInputToken<FullToken<'Mentions'>>(token);
return [genMentionsStyle(mentionsToken)];
},
token => ({
dropdownHeight: 250,
controlItemWidth: 100,
zIndexPopup: token.zIndexPopupBase + 50,
}),
);

View File

@ -1,7 +0,0 @@
import './index.less';
// style dependencies
import '../../empty/style';
import '../../spin/style';
// deps-lint-skip: form

View File

@ -1,10 +0,0 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@mention-prefix-cls: ~'@{ant-prefix}-mentions';
.@{mention-prefix-cls} {
&-rtl {
direction: rtl;
}
}

View File

@ -1,16 +0,0 @@
@import '../../input/style/mixin';
@mention-prefix-cls: ~'@{ant-prefix}-mentions';
@input-prefix-cls: ~'@{ant-prefix}-input';
.@{mention-prefix-cls} {
&-status-error {
.status-color(@mention-prefix-cls, @error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline);
.status-color-common(@input-prefix-cls, @error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline);
}
&-status-warning {
.status-color(@mention-prefix-cls, @warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline);
.status-color-common(@input-prefix-cls, @warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline);
}
}

View File

@ -14,7 +14,7 @@ import './tabs/style';
// import './popover/style';
// import './popconfirm/style';
// import './menu/style';
import './mentions/style';
// import './mentions/style';
// import './dropdown/style';
// import './divider/style';
// import './card/style';

View File

@ -19,7 +19,7 @@ import type { ComponentToken as EmptyComponentToken } from '../../empty/style';
// import type { ComponentToken as InputNumberComponentToken } from '../../input-number/style';
import type { ComponentToken as LayoutComponentToken } from '../../layout/style';
import type { ComponentToken as ListComponentToken } from '../../list/style';
// import type { ComponentToken as MentionsComponentToken } from '../../mentions/style';
import type { ComponentToken as MentionsComponentToken } from '../../mentions/style';
import type { ComponentToken as MenuComponentToken } from '../../menu/style';
import type { ComponentToken as MessageComponentToken } from '../../message/style';
import type { ComponentToken as ModalComponentToken } from '../../modal/style';
@ -79,7 +79,7 @@ export interface ComponentTokenMap {
// InputNumber?: InputNumberComponentToken;
Layout?: LayoutComponentToken;
List?: ListComponentToken;
// Mentions?: MentionsComponentToken;
Mentions?: MentionsComponentToken;
Notification?: NotificationComponentToken;
PageHeader?: {};
Pagination?: {};

View File

@ -54,6 +54,7 @@ export default defineComponent({
transitionName: String,
getPopupContainer: Function,
direction: String,
dropdownClassName: String,
},
slots: ['notFoundContent', 'option'],
setup(props, { slots }) {
@ -88,6 +89,7 @@ export default defineComponent({
prefixCls={getDropdownPrefix()}
popupVisible={visible}
popup={getDropdownElement()}
popupClassName={props.dropdownClassName}
popupPlacement={popupPlacement.value}
popupTransitionName={transitionName}
builtinPlacements={BUILT_IN_PLACEMENTS}

View File

@ -287,6 +287,7 @@ export default defineComponent({
<KeywordTrigger
prefixCls={prefixCls}
transitionName={transitionName}
dropdownClassName={props.dropdownClassName}
placement={placement}
options={measuring ? options.value : []}
visible

View File

@ -40,6 +40,7 @@ export const mentionsProps = {
export const vcMentionsProps = {
...mentionsProps,
dropdownClassName: String,
};
export const defaultProps = {