diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index 0257455f9..dbcecd523 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -2,6 +2,11 @@ ## Pending +### Feats + +- `n-dropdown` add `disabled` prop +- `n-card` add `:target` style + ### Fixes - Fix `n-popover` sometimes won't sync position in manual mode. diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 62cdd21f2..0060e33cd 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -2,6 +2,11 @@ ## Pending +### Feats + +- `n-dropdown` 新增 `disabled` 属性 +- `n-card` 增加 `:target` 的样式 + ### Fixes - 修复 `n-popover` 有时在手动模式不会同步位置 @@ -393,7 +398,7 @@ - `n-layout-sider` 新增 `default-collapsed` 属性 - `n-modal` 支持自定义位置 -### Fixed +### Fixes - 修正 `n-menu` 垂直折叠时 `n-menu-item` tooltip 不显示的问题 - 修正 `n-menu` `collapsed-icon-size` 不生效的问题 diff --git a/src/card/src/Card.tsx b/src/card/src/Card.tsx index 2467ad81b..0c94937be 100644 --- a/src/card/src/Card.tsx +++ b/src/card/src/Card.tsx @@ -83,6 +83,7 @@ export default defineComponent({ self: { color, colorModal, + colorTarget, textColor, titleTextColor, titleFontWeight, @@ -113,6 +114,7 @@ export default defineComponent({ '--color': color, '--color-modal': colorModal, '--color-popover': colorPopover, + '--color-target': colorTarget, '--text-color': textColor, '--line-height': lineHeight, '--action-color': actionColor, diff --git a/src/card/src/styles/index.cssr.ts b/src/card/src/styles/index.cssr.ts index de6d8f922..a52b090f9 100644 --- a/src/card/src/styles/index.cssr.ts +++ b/src/card/src/styles/index.cssr.ts @@ -137,9 +137,11 @@ export default c([ width: 100%; `) ]), - cM('bordered', { - border: '1px solid var(--border-color)' - }), + cM('bordered', ` + border: 1px solid var(--border-color); + `, [ + c('&:target', 'border-color: var(--color-target);') + ]), cM('action-segmented', [ c('>', [ cE('action', [ diff --git a/src/card/styles/light.ts b/src/card/styles/light.ts index fedc181c2..fa3f96482 100644 --- a/src/card/styles/light.ts +++ b/src/card/styles/light.ts @@ -5,6 +5,7 @@ import { Theme } from '../../_mixins' export const self = (vars: ThemeCommonVars) => { const { + primaryColor, borderRadius, lineHeight, fontSize, @@ -27,6 +28,7 @@ export const self = (vars: ThemeCommonVars) => { color: cardColor, colorModal: modalColor, colorPopover: popoverColor, + colorTarget: primaryColor, textColor: textColor2, titleTextColor: textColor1, borderColor: dividerColor, diff --git a/src/dropdown/demos/enUS/basic.demo.md b/src/dropdown/demos/enUS/basic.demo.md index fc65d87f6..4d985f8c9 100644 --- a/src/dropdown/demos/enUS/basic.demo.md +++ b/src/dropdown/demos/enUS/basic.demo.md @@ -19,7 +19,8 @@ export default defineComponent({ options: [ { label: 'Marina Bay Sands', - key: 'marina bay sands' + key: 'marina bay sands', + disabled: true }, { label: "Brown's Hotel, London", diff --git a/src/dropdown/demos/enUS/cascade.demo.md b/src/dropdown/demos/enUS/cascade.demo.md index a9147e805..bdfa17573 100644 --- a/src/dropdown/demos/enUS/cascade.demo.md +++ b/src/dropdown/demos/enUS/cascade.demo.md @@ -30,7 +30,8 @@ const options = [ default: () => h(CashIcon) }) }, - key: 'daisy buchanan' + key: 'daisy buchanan', + disabled: true }, { type: 'divider', @@ -55,6 +56,7 @@ const options = [ { label: 'Others', key: 'others2', + disabled: true, children: [ { label: 'Chicken', diff --git a/src/dropdown/demos/enUS/index.demo-entry.md b/src/dropdown/demos/enUS/index.demo-entry.md index 9c7f19841..ceb15f266 100644 --- a/src/dropdown/demos/enUS/index.demo-entry.md +++ b/src/dropdown/demos/enUS/index.demo-entry.md @@ -33,6 +33,7 @@ For other props, for example `placement`, please see [Popover Props](popover#Pro | icon? | `() => VNodeChild` | | | key | `string \| number` | Should be unique. | | label | `string` | | +| disabled | `boolean` | | ### DropdownDivider Type @@ -50,6 +51,7 @@ For other props, for example `placement`, please see [Popover Props](popover#Pro | icon? | `() => VNodeChild` | | | key | `string \| number` | Should be unique. | | children | `Array` | | +| disabled | `boolean` | | ### DropdownGroup Type diff --git a/src/dropdown/demos/enUS/trigger.demo.md b/src/dropdown/demos/enUS/trigger.demo.md index 50d01537c..a7084ae49 100644 --- a/src/dropdown/demos/enUS/trigger.demo.md +++ b/src/dropdown/demos/enUS/trigger.demo.md @@ -30,7 +30,8 @@ export default defineComponent({ options: [ { label: 'Marina Bay Sands', - key: 'Marina Bay Sands' + key: 'Marina Bay Sands', + disabled: true }, { label: "Brown's Hotel, London", diff --git a/src/dropdown/demos/zhCN/basic.demo.md b/src/dropdown/demos/zhCN/basic.demo.md index 11d1c9d74..fea799668 100644 --- a/src/dropdown/demos/zhCN/basic.demo.md +++ b/src/dropdown/demos/zhCN/basic.demo.md @@ -19,7 +19,8 @@ export default defineComponent({ options: [ { label: '滨海湾金沙,新加坡', - key: 'marina bay sands' + key: 'marina bay sands', + disabled: true }, { label: '布朗酒店,伦敦', diff --git a/src/dropdown/demos/zhCN/cascade.demo.md b/src/dropdown/demos/zhCN/cascade.demo.md index 995542a10..2aaa73982 100644 --- a/src/dropdown/demos/zhCN/cascade.demo.md +++ b/src/dropdown/demos/zhCN/cascade.demo.md @@ -30,7 +30,8 @@ const options = [ default: () => h(CashIcon) }) }, - key: 'daisy buchanan' + key: 'daisy buchanan', + disabled: true }, { type: 'divider', @@ -55,6 +56,7 @@ const options = [ { label: '其他', key: 'others2', + disabled: true, children: [ { label: '鸡肉', diff --git a/src/dropdown/demos/zhCN/index.demo-entry.md b/src/dropdown/demos/zhCN/index.demo-entry.md index f00a4cf73..853e4f26c 100644 --- a/src/dropdown/demos/zhCN/index.demo-entry.md +++ b/src/dropdown/demos/zhCN/index.demo-entry.md @@ -29,11 +29,12 @@ group-debug ### DropdownOption Type -| 属性 | 类型 | 说明 | -| ----- | ------------------ | -------- | -| icon? | `() => VNodeChild` | | -| key | `string \| number` | 需要唯一 | -| label | `string` | | +| 属性 | 类型 | 说明 | +| -------- | ------------------ | -------- | +| icon? | `() => VNodeChild` | | +| key | `string \| number` | 需要唯一 | +| label | `string` | | +| disabled | `boolean` | | ### DropdownDivider Type @@ -51,6 +52,7 @@ group-debug | icon? | `() => VNodeChild` | | | key | `string \| number` | 需要唯一 | | children | `Array` | | +| disabled | `boolean` | | ### DropdownGroup Type diff --git a/src/dropdown/demos/zhCN/trigger.demo.md b/src/dropdown/demos/zhCN/trigger.demo.md index 214a02b42..3f67c976e 100644 --- a/src/dropdown/demos/zhCN/trigger.demo.md +++ b/src/dropdown/demos/zhCN/trigger.demo.md @@ -30,7 +30,8 @@ export default defineComponent({ options: [ { label: '滨海湾金沙,新加坡', - key: 'marina bay sands' + key: 'marina bay sands', + disabled: true }, { label: '布朗酒店,伦敦', diff --git a/src/dropdown/src/Dropdown.ts b/src/dropdown/src/Dropdown.ts index 5da09cebf..d713127d5 100644 --- a/src/dropdown/src/Dropdown.ts +++ b/src/dropdown/src/Dropdown.ts @@ -307,6 +307,7 @@ export default defineComponent({ dividerColor, borderRadius, boxShadow, + optionOpacityDisabled, [createKey('optionIconSuffixWidth', size)]: optionIconSuffixWidth, [createKey('optionSuffixWidth', size)]: optionSuffixWidth, [createKey('optionIconPrefixWidth', size)]: optionIconPrefixWidth, @@ -327,7 +328,8 @@ export default defineComponent({ '--option-suffix-width': optionSuffixWidth, '--option-icon-suffix-width': optionIconSuffixWidth, '--option-icon-size': optionIconSize, - '--divider-color': dividerColor + '--divider-color': dividerColor, + '--option-opacity-disabled': optionOpacityDisabled } // writing like this is the fastest method if (inverted) { diff --git a/src/dropdown/src/DropdownOption.tsx b/src/dropdown/src/DropdownOption.tsx index bc540c1fc..b5df6fe6b 100644 --- a/src/dropdown/src/DropdownOption.tsx +++ b/src/dropdown/src/DropdownOption.tsx @@ -30,9 +30,8 @@ interface NDropdownOptionInjection { enteringSubmenuRef: Ref } -const dropdownOptionInjectionKey: InjectionKey = Symbol( - 'dropdown-option' -) +const dropdownOptionInjectionKey: InjectionKey = + Symbol('dropdown-option') export default defineComponent({ name: 'DropdownOption', @@ -75,13 +74,18 @@ export default defineComponent({ const hasSubmenuRef = computed(() => { return isSubmenuNode(props.tmNode.rawNode) }) + const mergedDisabledRef = computed(() => { + const { disabled } = props.tmNode + return disabled + }) const showSubmenuRef = computed(() => { if (!hasSubmenuRef.value) return false + const { key, disabled } = props.tmNode + if (disabled) return false const { value: hoverKey } = hoverKeyRef const { value: keyboardKey } = keyboardKeyRef const { value: lastToggledSubmenuKey } = lastToggledSubmenuKeyRef const { value: pendingKeyPath } = pendingKeyPathRef - const { key } = props.tmNode if (hoverKey !== null) return pendingKeyPath.includes(key) if (keyboardKey !== null) { return ( @@ -144,7 +148,7 @@ export default defineComponent({ if (!hasSubmenu && !tmNode.disabled) { NDropdown.doSelect( tmNode.key, - ((tmNode as unknown) as TreeNode).rawNode + (tmNode as unknown as TreeNode).rawNode ) NDropdown.doUpdateShow(false) } @@ -177,6 +181,7 @@ export default defineComponent({ if (index === -1) return false return index === activeKeyPath.length - 1 }), + mergedDisabled: mergedDisabledRef, handleClick, handleMouseMove, handleMouseEnter, @@ -209,8 +214,10 @@ export default defineComponent({ { [`${clsPrefix}-dropdown-option-body--pending`]: this.pending, [`${clsPrefix}-dropdown-option-body--active`]: this.active, - [`${clsPrefix}-dropdown-option-body--child-active`]: this - .childActive + [`${clsPrefix}-dropdown-option-body--child-active`]: + this.childActive, + [`${clsPrefix}-dropdown-option-body--disabled`]: + this.mergedDisabled } ]} onMousemove={this.handleMouseMove} diff --git a/src/dropdown/src/styles/index.cssr.ts b/src/dropdown/src/styles/index.cssr.ts index 2af32d397..8ea5ce54d 100644 --- a/src/dropdown/src/styles/index.cssr.ts +++ b/src/dropdown/src/styles/index.cssr.ts @@ -1,4 +1,4 @@ -import { cB, cM, cE } from '../../../_utils/cssr' +import { cB, cM, cE, cNotM } from '../../../_utils/cssr' import fadeInScaleUpTransition from '../../../_styles/transitions/fade-in-scale-up.cssr' // vars: @@ -23,6 +23,8 @@ import fadeInScaleUpTransition from '../../../_styles/transitions/fade-in-scale- // --prefix-color // --suffix-color // --option-icon-size +// --option-opacity-disabled + export default cB('dropdown-menu', ` transform-origin: inherit; padding: var(--padding); @@ -49,9 +51,11 @@ export default cB('dropdown-menu', ` color .3s var(--bezier); `, [ cM('pending', { - color: 'var(--option-text-color-hover)', - backgroundColor: 'var(--option-color-hover)' + color: 'var(--option-text-color-hover)' }, [ + cNotM('disabled', { + backgroundColor: 'var(--option-color-hover)' + }), cE('prefix, suffix', { color: 'var(--option-text-color-hover)' }) @@ -64,6 +68,10 @@ export default cB('dropdown-menu', ` color: 'var(--option-text-color-active)' }) ]), + cM('disabled', { + cursor: 'not-allowed', + opacity: 'var(--option-opacity-disabled)' + }), cM('child-active', { color: 'var(--option-text-color-child-active)' }, [ diff --git a/src/dropdown/styles/light.ts b/src/dropdown/styles/light.ts index 226b560a5..20c67e522 100644 --- a/src/dropdown/styles/light.ts +++ b/src/dropdown/styles/light.ts @@ -23,7 +23,8 @@ export const self = (vars: ThemeCommonVars) => { heightMedium, heightLarge, heightHuge, - textColor3 + textColor3, + opacityDisabled } = vars return { ...commonVariables, @@ -60,7 +61,8 @@ export const self = (vars: ThemeCommonVars) => { prefixColorInverted: '#BBB', optionColorHoverInverted: primaryColor, optionColorActiveInverted: primaryColor, - groupHeaderTextColorInverted: '#AAA' + groupHeaderTextColorInverted: '#AAA', + optionOpacityDisabled: opacityDisabled } } diff --git a/src/dropdown/tests/Dropdown.spec.ts b/src/dropdown/tests/Dropdown.spec.ts index 0fd12e34d..1e36ecbb6 100644 --- a/src/dropdown/tests/Dropdown.spec.ts +++ b/src/dropdown/tests/Dropdown.spec.ts @@ -9,4 +9,44 @@ describe('n-dropdown', () => { } }) }) + + it('dropdown disabled', async () => { + const onSelect = jest.fn() + + const options = [ + { + label: '滨海湾金沙,新加坡', + key: 'marina bay sands', + disabled: true + } + ] + const triggerEvent = 'click' + const wrapper = mount(NDropdown, { + attachTo: document.body, + props: { + options, + trigger: triggerEvent, + onSelect: onSelect + }, + slots: { + default: () => 'star kirby' + } + }) + + const triggerNodeWrapper = wrapper.find('span') + expect(triggerNodeWrapper.exists()).toBe(true) + await triggerNodeWrapper.trigger(triggerEvent) + + const disabledMenu = document.querySelector( + '.n-dropdown-option-body--disabled' + ) as HTMLDivElement + + expect(disabledMenu).not.toEqual(null) + + await disabledMenu.click() + + expect(onSelect).not.toHaveBeenCalledWith() + + wrapper.unmount() + }) })