diff --git a/src/_mixins/use-rtl.ts b/src/_mixins/use-rtl.ts new file mode 100644 index 000000000..475beccbb --- /dev/null +++ b/src/_mixins/use-rtl.ts @@ -0,0 +1,51 @@ +import { Ref, onBeforeMount, inject, watchEffect, computed } from 'vue' +import { ssrInjectionKey } from '../ssr/context' +import { + RtlEnabledState, + RtlItem +} from '../config-provider/src/internal-interface' + +// The current implemention will take extra perf & memory usage. I just want to +// make it work now. If we can determine whether the style is already mounted, +// we won't need to watch effect. However, we need to make css-render support +// it. We need to refactor ssrAdapter and expose a exists function +export default function useRtl ( + mountId: string, + rtlStateRef: Ref | undefined, + clsPrefixRef: Ref +): Ref | undefined { + if (!rtlStateRef) return undefined + const ssrAdapter = inject(ssrInjectionKey, undefined) + const componentRtlStateRef = computed(() => { + const { value: rtlState } = rtlStateRef + if (!rtlState) { + return undefined + } + const componentRtlState = rtlState[mountId as keyof RtlEnabledState] + if (!componentRtlState) { + return undefined + } + return componentRtlState + }) + const mountStyle = (): void => { + watchEffect(() => { + const { value: clsPrefix } = clsPrefixRef + const { value: componentRtlState } = componentRtlStateRef + if (!componentRtlState) return + componentRtlState.style.mount({ + id: `${clsPrefix}${mountId}Rtl`, + head: true, + props: { + bPrefix: clsPrefix ? `.${clsPrefix}-` : undefined + }, + ssr: ssrAdapter + }) + }) + } + if (ssrAdapter) { + mountStyle() + } else { + onBeforeMount(mountStyle) + } + return componentRtlStateRef +} diff --git a/src/button/demos/zhCN/index.demo-entry.md b/src/button/demos/zhCN/index.demo-entry.md index 6bf35bed2..e4ae7b6dc 100644 --- a/src/button/demos/zhCN/index.demo-entry.md +++ b/src/button/demos/zhCN/index.demo-entry.md @@ -18,6 +18,7 @@ ghost loading color group +rtl-debug debug ``` diff --git a/src/button/demos/zhCN/rtl-debug.demo.md b/src/button/demos/zhCN/rtl-debug.demo.md new file mode 100644 index 000000000..7742d09ac --- /dev/null +++ b/src/button/demos/zhCN/rtl-debug.demo.md @@ -0,0 +1,24 @@ +# Rtl Debug + +```html + + Rtl + + Rtl Test + + +``` + +```js +import { defineComponent, ref } from 'vue' +import { unstableButtonRtl } from 'naive-ui' + +export default defineComponent({ + setup () { + return { + rtlEnabled: ref(false), + rtlStyles: [unstableButtonRtl] + } + } +}) +``` diff --git a/src/button/src/Button.tsx b/src/button/src/Button.tsx index 8ad312534..624c3ac1e 100644 --- a/src/button/src/Button.tsx +++ b/src/button/src/Button.tsx @@ -28,6 +28,7 @@ import type { ButtonTheme } from '../styles' import { buttonGroupInjectionKey } from './ButtonGroup' import type { Type, Size } from './interface' import style from './styles/button.cssr' +import useRtl from '../../_mixins/use-rtl' const buttonProps = { ...(useTheme.props as ThemeProps), @@ -157,7 +158,7 @@ const Button = defineComponent({ const handleBlur = (): void => { enterPressedRef.value = false } - const { mergedClsPrefixRef } = useConfig(props) + const { mergedClsPrefixRef, NConfigProvider } = useConfig(props) const themeRef = useTheme( 'Button', 'Button', @@ -166,6 +167,11 @@ const Button = defineComponent({ props, mergedClsPrefixRef ) + const rtlEnabledRef = useRtl( + 'Button', + NConfigProvider?.mergedRtlRef, + mergedClsPrefixRef + ) return { selfRef, waveRef, @@ -174,6 +180,7 @@ const Button = defineComponent({ mergedSize: mergedSizeRef, showBorder: showBorderRef, enterPressed: enterPressedRef, + rtlEnabled: rtlEnabledRef, handleMouseDown, handleKeyDown, handleBlur, @@ -390,6 +397,7 @@ const Button = defineComponent({ `${mergedClsPrefix}-button`, `${mergedClsPrefix}-button--${this.type}-type`, { + [`${mergedClsPrefix}-button--rtl`]: this.rtlEnabled, [`${mergedClsPrefix}-button--disabled`]: this.disabled, [`${mergedClsPrefix}-button--block`]: this.block, [`${mergedClsPrefix}-button--pressed`]: this.enterPressed, diff --git a/src/button/src/styles/button-rtl.cssr.ts b/src/button/src/styles/button-rtl.cssr.ts new file mode 100644 index 000000000..fe4e06e55 --- /dev/null +++ b/src/button/src/styles/button-rtl.cssr.ts @@ -0,0 +1,7 @@ +import { cB, cM } from '../../../_utils/cssr' + +export default cB('button', [ + cM('rtl', ` + direction: rtl; + `) +]) diff --git a/src/button/styles/index.ts b/src/button/styles/index.ts index adaf8f71d..3acbdc8bc 100644 --- a/src/button/styles/index.ts +++ b/src/button/styles/index.ts @@ -1,3 +1,4 @@ export { default as buttonDark } from './dark' export { default as buttonLight } from './light' +export { default as buttonRtl } from './rtl' export type { ButtonThemeVars, ButtonTheme } from './light' diff --git a/src/button/styles/rtl.ts b/src/button/styles/rtl.ts new file mode 100644 index 000000000..9660f1f5b --- /dev/null +++ b/src/button/styles/rtl.ts @@ -0,0 +1,6 @@ +import rtlStyle from '../src/styles/button-rtl.cssr' + +export default { + name: 'Button', + style: rtlStyle +} diff --git a/src/config-provider/src/ConfigProvider.ts b/src/config-provider/src/ConfigProvider.ts index 466886b72..f316b1bc9 100644 --- a/src/config-provider/src/ConfigProvider.ts +++ b/src/config-provider/src/ConfigProvider.ts @@ -6,7 +6,9 @@ import { PropType, provide, InjectionKey, - renderSlot + renderSlot, + ComputedRef, + markRaw } from 'vue' import { useMemo } from 'vooks' import { merge } from 'lodash-es' @@ -18,17 +20,18 @@ import type { GlobalComponentConfig, GlobalIconConfig } from './interface' -import type { ConfigProviderInjection } from './internal-interface' +import type { + ConfigProviderInjection, + RtlProp, + RtlEnabledState +} from './internal-interface' import { NDateLocale, NLocale } from '../../locales' export const configProviderInjectionKey: InjectionKey = Symbol('configProviderInjection') export const configProviderProps = { - abstract: { - type: Boolean, - default: false - }, + abstract: Boolean, bordered: { type: Boolean as PropType, default: undefined @@ -37,6 +40,7 @@ export const configProviderProps = { locale: Object as PropType, dateLocale: Object as PropType, namespace: String, + rtl: Array as PropType, tag: { type: String, default: 'div' @@ -160,7 +164,21 @@ export default defineComponent({ const { clsPrefix } = props return NConfigProvider?.mergedClsPrefixRef.value ?? clsPrefix }) + const mergedRtlRef: ComputedRef = computed( + () => { + const { rtl } = props + if (rtl === undefined) { + return NConfigProvider?.mergedRtlRef.value + } + const rtlEnabledState: RtlEnabledState = {} + for (const rtlInfo of rtl) { + rtlEnabledState[rtlInfo.name] = markRaw(rtlInfo) + } + return rtlEnabledState + } + ) provide(configProviderInjectionKey, { + mergedRtlRef, mergedIconsRef, mergedComponentPropsRef, mergedBorderedRef, diff --git a/src/config-provider/src/internal-interface.ts b/src/config-provider/src/internal-interface.ts index 01250029d..1e02663df 100644 --- a/src/config-provider/src/internal-interface.ts +++ b/src/config-provider/src/internal-interface.ts @@ -1,4 +1,5 @@ import { VNodeChild, Ref } from 'vue' +import { CNode } from 'css-render' import type { AlertTheme } from '../../alert/styles' import type { AnchorTheme } from '../../anchor/styles' import type { AutoCompleteTheme } from '../../auto-complete/styles' @@ -200,6 +201,16 @@ export interface GlobalIconConfig { zoomOut?: () => VNodeChild } +export interface RtlItem { + name: keyof GlobalThemeWithoutCommon + style: CNode +} +export type RtlProp = RtlItem[] + +export type RtlEnabledState = Partial< +Record +> + export interface ConfigProviderInjection { mergedClsPrefixRef: Ref mergedBorderedRef: Ref @@ -211,6 +222,7 @@ export interface ConfigProviderInjection { mergedIconsRef: Ref mergedThemeRef: Ref mergedThemeOverridesRef: Ref + mergedRtlRef: Ref // deprecated /** @deprecated */ mergedLegacyThemeRef: Ref diff --git a/src/styles.ts b/src/styles.ts index b7336ec90..d599203e2 100644 --- a/src/styles.ts +++ b/src/styles.ts @@ -6,7 +6,7 @@ export { avatarDark } from './avatar/styles' export { backTopDark } from './back-top/styles' export { badgeDark } from './badge/styles' export { breadcrumbDark } from './breadcrumb/styles' -export { buttonDark } from './button/styles' +export { buttonDark, buttonRtl as unstableButtonRtl } from './button/styles' export { cardDark } from './card/styles' export { cascaderDark } from './cascader/styles' export { checkboxDark } from './checkbox/styles'