From 92795a828f4c6159e5a58b95d8549a4b37847a95 Mon Sep 17 00:00:00 2001 From: Zev Zhu <45655660+aibayanyu20@users.noreply.github.com> Date: Tue, 14 Feb 2023 14:09:23 +0800 Subject: [PATCH] refactor: mentions (#6255) * refactor: mentions * refactor: mentions menu provider --- components/mentions/index.tsx | 27 +- components/mentions/style/index.less | 179 -------------- components/mentions/style/index.ts | 232 ++++++++++++++++++ components/mentions/style/index.tsx | 7 - components/mentions/style/rtl.less | 10 - components/mentions/style/status.less | 16 -- components/style.ts | 2 +- components/theme/interface/components.ts | 4 +- components/vc-mentions/src/KeywordTrigger.tsx | 2 + components/vc-mentions/src/Mentions.tsx | 1 + components/vc-mentions/src/mentionsProps.ts | 1 + 11 files changed, 263 insertions(+), 218 deletions(-) delete mode 100644 components/mentions/style/index.less create mode 100644 components/mentions/style/index.ts delete mode 100644 components/mentions/style/index.tsx delete mode 100644 components/mentions/style/rtl.less delete mode 100644 components/mentions/style/status.less diff --git a/components/mentions/index.tsx b/components/mentions/index.tsx index b68846544..0a64dc7fb 100644 --- a/components/mentions/index.tsx +++ b/components/mentions/index.tsx @@ -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 = ( ); if (hasFeedback) { - return ( + return wrapSSR(
{mentions} {feedbackIcon} -
+ , ); } - return mentions; + return wrapSSR(mentions); }; }, }); diff --git a/components/mentions/style/index.less b/components/mentions/style/index.less deleted file mode 100644 index 06e2b1f29..000000000 --- a/components/mentions/style/index.less +++ /dev/null @@ -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'; diff --git a/components/mentions/style/index.ts b/components/mentions/style/index.ts new file mode 100644 index 000000000..746b39389 --- /dev/null +++ b/components/mentions/style/index.ts @@ -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>; + +const genMentionsStyle: GenerateStyle = 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>(token); + return [genMentionsStyle(mentionsToken)]; + }, + token => ({ + dropdownHeight: 250, + controlItemWidth: 100, + zIndexPopup: token.zIndexPopupBase + 50, + }), +); diff --git a/components/mentions/style/index.tsx b/components/mentions/style/index.tsx deleted file mode 100644 index b39860748..000000000 --- a/components/mentions/style/index.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import './index.less'; - -// style dependencies -import '../../empty/style'; -import '../../spin/style'; - -// deps-lint-skip: form diff --git a/components/mentions/style/rtl.less b/components/mentions/style/rtl.less deleted file mode 100644 index 7cd95832d..000000000 --- a/components/mentions/style/rtl.less +++ /dev/null @@ -1,10 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@mention-prefix-cls: ~'@{ant-prefix}-mentions'; - -.@{mention-prefix-cls} { - &-rtl { - direction: rtl; - } -} diff --git a/components/mentions/style/status.less b/components/mentions/style/status.less deleted file mode 100644 index 92d61e337..000000000 --- a/components/mentions/style/status.less +++ /dev/null @@ -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); - } -} diff --git a/components/style.ts b/components/style.ts index 1d9dd730d..0b00479fd 100644 --- a/components/style.ts +++ b/components/style.ts @@ -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'; diff --git a/components/theme/interface/components.ts b/components/theme/interface/components.ts index 40a4de653..a64de3b6e 100644 --- a/components/theme/interface/components.ts +++ b/components/theme/interface/components.ts @@ -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?: {}; diff --git a/components/vc-mentions/src/KeywordTrigger.tsx b/components/vc-mentions/src/KeywordTrigger.tsx index b428e8a68..7291f49e4 100644 --- a/components/vc-mentions/src/KeywordTrigger.tsx +++ b/components/vc-mentions/src/KeywordTrigger.tsx @@ -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} diff --git a/components/vc-mentions/src/Mentions.tsx b/components/vc-mentions/src/Mentions.tsx index ebfa57863..2965c3830 100644 --- a/components/vc-mentions/src/Mentions.tsx +++ b/components/vc-mentions/src/Mentions.tsx @@ -287,6 +287,7 @@ export default defineComponent({