From 6177b96e86e4e4cda2eca3a5fcaeee50e4618db8 Mon Sep 17 00:00:00 2001 From: 07akioni <07akioni2@gmail.com> Date: Fri, 11 Jun 2021 16:22:06 +0800 Subject: [PATCH] feat(button): unstable rtl (#74) --- src/_mixins/use-rtl.ts | 51 +++++++++++++++++++ src/button/demos/zhCN/index.demo-entry.md | 1 + src/button/demos/zhCN/rtl-debug.demo.md | 24 +++++++++ src/button/src/Button.tsx | 10 +++- src/button/src/styles/button-rtl.cssr.ts | 7 +++ src/button/styles/index.ts | 1 + src/button/styles/rtl.ts | 6 +++ src/config-provider/src/ConfigProvider.ts | 30 ++++++++--- src/config-provider/src/internal-interface.ts | 12 +++++ src/styles.ts | 2 +- 10 files changed, 136 insertions(+), 8 deletions(-) create mode 100644 src/_mixins/use-rtl.ts create mode 100644 src/button/demos/zhCN/rtl-debug.demo.md create mode 100644 src/button/src/styles/button-rtl.cssr.ts create mode 100644 src/button/styles/rtl.ts 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<RtlEnabledState | undefined> | undefined, + clsPrefixRef: Ref<string> +): Ref<RtlItem | undefined> | 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 +<n-space vertical> + <n-space><n-switch v-model:value="rtlEnabled" />Rtl</n-space> + <n-config-provider :rtl="rtlEnabled ? rtlStyles : undefined"> + <n-button>Rtl Test</n-button> + </n-config-provider> +</n-space> +``` + +```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<ButtonTheme>), @@ -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<ConfigProviderInjection> = Symbol('configProviderInjection') export const configProviderProps = { - abstract: { - type: Boolean, - default: false - }, + abstract: Boolean, bordered: { type: Boolean as PropType<boolean | undefined>, default: undefined @@ -37,6 +40,7 @@ export const configProviderProps = { locale: Object as PropType<NLocale | null>, dateLocale: Object as PropType<NDateLocale | null>, namespace: String, + rtl: Array as PropType<RtlProp>, tag: { type: String, default: 'div' @@ -160,7 +164,21 @@ export default defineComponent({ const { clsPrefix } = props return NConfigProvider?.mergedClsPrefixRef.value ?? clsPrefix }) + const mergedRtlRef: ComputedRef<RtlEnabledState | undefined> = 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<keyof GlobalThemeWithoutCommon, RtlItem> +> + export interface ConfigProviderInjection { mergedClsPrefixRef: Ref<string | undefined> mergedBorderedRef: Ref<boolean | undefined> @@ -211,6 +222,7 @@ export interface ConfigProviderInjection { mergedIconsRef: Ref<GlobalIconConfig | undefined> mergedThemeRef: Ref<GlobalTheme | undefined> mergedThemeOverridesRef: Ref<GlobalThemeOverrides | undefined> + mergedRtlRef: Ref<RtlEnabledState | undefined> // deprecated /** @deprecated */ mergedLegacyThemeRef: Ref<string | undefined> 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'