From ddda5c84bece61268aa37e32866468e8b4b4d708 Mon Sep 17 00:00:00 2001 From: 07akioni <07akioni2@gmail.com> Date: Sat, 23 Jan 2021 16:45:01 +0800 Subject: [PATCH] refactor(layout): ts --- src/layout/index.js | 6 - src/layout/index.ts | 6 + src/layout/src/Layout.tsx | 134 ++++++++ src/layout/src/Layout.vue | 134 -------- src/layout/src/LayoutFooter.tsx | 56 ++++ src/layout/src/LayoutFooter.vue | 53 ---- .../{LayoutHeader.vue => LayoutHeader.tsx} | 43 +-- src/layout/src/LayoutSider.tsx | 296 ++++++++++++++++++ src/layout/src/LayoutSider.vue | 284 ----------------- src/layout/src/ToggleBar.tsx | 25 ++ src/layout/src/ToggleBar.vue | 22 -- src/layout/src/ToggleButton.tsx | 17 + src/layout/src/ToggleButton.vue | 10 - src/layout/src/interface.ts | 6 + src/layout/src/layoutModeMixin.js | 15 - ...t-footer.cssr.js => layout-footer.cssr.ts} | 0 ...t-header.cssr.js => layout-header.cssr.ts} | 0 ...out-sider.cssr.js => layout-sider.cssr.ts} | 10 +- .../styles/{layout.cssr.js => layout.cssr.ts} | 0 src/layout/styles/{dark.js => dark.ts} | 5 +- src/layout/styles/index.js | 2 - src/layout/styles/index.ts | 3 + src/layout/styles/light.js | 33 -- src/layout/styles/light.ts | 42 +++ 24 files changed, 613 insertions(+), 589 deletions(-) delete mode 100644 src/layout/index.js create mode 100644 src/layout/index.ts create mode 100644 src/layout/src/Layout.tsx delete mode 100644 src/layout/src/Layout.vue create mode 100644 src/layout/src/LayoutFooter.tsx delete mode 100644 src/layout/src/LayoutFooter.vue rename src/layout/src/{LayoutHeader.vue => LayoutHeader.tsx} (51%) create mode 100644 src/layout/src/LayoutSider.tsx delete mode 100644 src/layout/src/LayoutSider.vue create mode 100644 src/layout/src/ToggleBar.tsx delete mode 100644 src/layout/src/ToggleBar.vue create mode 100644 src/layout/src/ToggleButton.tsx delete mode 100644 src/layout/src/ToggleButton.vue create mode 100644 src/layout/src/interface.ts delete mode 100644 src/layout/src/layoutModeMixin.js rename src/layout/src/styles/{layout-footer.cssr.js => layout-footer.cssr.ts} (100%) rename src/layout/src/styles/{layout-header.cssr.js => layout-header.cssr.ts} (100%) rename src/layout/src/styles/{layout-sider.cssr.js => layout-sider.cssr.ts} (95%) rename src/layout/src/styles/{layout.cssr.js => layout.cssr.ts} (100%) rename src/layout/styles/{dark.js => dark.ts} (89%) delete mode 100644 src/layout/styles/index.js create mode 100644 src/layout/styles/index.ts delete mode 100644 src/layout/styles/light.js create mode 100644 src/layout/styles/light.ts diff --git a/src/layout/index.js b/src/layout/index.js deleted file mode 100644 index 5b40718c7..000000000 --- a/src/layout/index.js +++ /dev/null @@ -1,6 +0,0 @@ -/* istanbul ignore file */ -export { default as NLayout } from './src/Layout.vue' -export { default as NLayoutContent } from './src/Layout.vue' -export { default as NLayoutHeader } from './src/LayoutHeader.vue' -export { default as NLayoutFooter } from './src/LayoutFooter.vue' -export { default as NLayoutSider } from './src/LayoutSider.vue' diff --git a/src/layout/index.ts b/src/layout/index.ts new file mode 100644 index 000000000..f8d95874d --- /dev/null +++ b/src/layout/index.ts @@ -0,0 +1,6 @@ +/* istanbul ignore file */ +export { default as NLayout } from './src/Layout' +export { default as NLayoutContent } from './src/Layout' +export { default as NLayoutHeader } from './src/LayoutHeader' +export { default as NLayoutFooter } from './src/LayoutFooter' +export { default as NLayoutSider } from './src/LayoutSider' diff --git a/src/layout/src/Layout.tsx b/src/layout/src/Layout.tsx new file mode 100644 index 000000000..f5322bf69 --- /dev/null +++ b/src/layout/src/Layout.tsx @@ -0,0 +1,134 @@ +import { + h, + defineComponent, + computed, + PropType, + reactive, + inject, + provide, + ref +} from 'vue' +import { NScrollbar } from '../../scrollbar' +import type { ScrollbarProps, ScrollbarRef } from '../../scrollbar' +import { useTheme } from '../../_mixins' +import type { ThemeProps } from '../../_mixins' +import { layoutLight } from '../styles' +import type { LayoutTheme } from '../styles' +import style from './styles/layout.cssr' +import { positionProp } from './interface' + +export interface LayoutInjection { + hasSider: boolean + siderWidth: number | null + siderCollapsedWidth: number | null + siderCollapseMode: 'width' | 'transform' | null + siderPosition: 'absolute' | 'static' | null + siderCollapsed: boolean | null +} + +export default defineComponent({ + name: 'Layout', + alias: ['LayoutContent'], + props: { + ...(useTheme.props as ThemeProps), + position: positionProp, + nativeScrollbar: { + type: Boolean, + default: true + }, + scrollbarProps: Object as PropType> + }, + setup (props) { + const selfRef = ref(null) + const scrollbarRef = ref(null) + const themeRef = useTheme('Layout', 'Layout', style, layoutLight, props) + const state = reactive({ + hasSider: false, + siderWidth: null, + siderCollapsedWidth: null, + siderCollapseMode: null, + siderPosition: null, + siderCollapsed: null + }) + const NLayout = inject('NLayout', null) + const styleMarginLeftRef = computed(() => { + if (NLayout?.hasSider) { + if ( + NLayout.siderPosition === 'absolute' && + props.position === 'absolute' + ) { + if (NLayout.siderCollapsed) { + return `${NLayout.siderCollapsedWidth as number}px` + } else { + return `${NLayout.siderWidth as number}px` + } + } + } + return '' + }) + const mergedLayoutStyleRef = computed(() => { + return { + marginLeft: styleMarginLeftRef.value + } + }) + function scrollTo (options: ScrollToOptions): void + function scrollTo (x: number, y: number): void + function scrollTo (options: ScrollToOptions | number, y?: number): void { + if (scrollbarRef.value) { + scrollbarRef.value.scrollTo(options as any, y as any) + } else if (selfRef.value) { + selfRef.value.scrollTo(options as any, y as any) + } + } + provide('NLayout', state) + return { + selfRef, + scrollbarRef, + state, + mergedLayoutStyle: mergedLayoutStyleRef, + scrollTo, + mergedTheme: themeRef, + cssVars: computed(() => { + const { + common: { cubicBezierEaseInOut }, + self: { color, textColor } + } = themeRef.value + return { + '--bezier': cubicBezierEaseInOut, + '--color': color, + '--text-color': textColor + } + }) + } + }, + render () { + return ( +
+ {!this.nativeScrollbar ? ( + + {this.$slots} + + ) : ( + this.$slots + )} +
+ ) + } +}) diff --git a/src/layout/src/Layout.vue b/src/layout/src/Layout.vue deleted file mode 100644 index 90dee0fc3..000000000 --- a/src/layout/src/Layout.vue +++ /dev/null @@ -1,134 +0,0 @@ - - - diff --git a/src/layout/src/LayoutFooter.tsx b/src/layout/src/LayoutFooter.tsx new file mode 100644 index 000000000..9d04553ef --- /dev/null +++ b/src/layout/src/LayoutFooter.tsx @@ -0,0 +1,56 @@ +import { h, computed, defineComponent, renderSlot, CSSProperties } from 'vue' +import { useTheme } from '../../_mixins' +import type { ThemeProps } from '../../_mixins' +import { layoutLight } from '../styles' +import type { LayoutTheme } from '../styles' +import { positionProp } from './interface' +import style from './styles/layout-footer.cssr' + +export default defineComponent({ + name: 'LayoutFooter', + props: { + ...(useTheme.props as ThemeProps), + position: positionProp, + bordered: { + type: Boolean, + default: false + } + }, + setup (props) { + const themeRef = useTheme( + 'Layout', + 'LayoutFooter', + style, + layoutLight, + props + ) + return { + cssVars: computed(() => { + const { + common: { cubicBezierEaseInOut }, + self: { footerBorderColor } + } = themeRef.value + return { + '--bezier': cubicBezierEaseInOut, + '--footer-border-color': footerBorderColor + } + }) + } + }, + render () { + return ( +
+ {renderSlot(this.$slots, 'default')} +
+ ) + } +}) diff --git a/src/layout/src/LayoutFooter.vue b/src/layout/src/LayoutFooter.vue deleted file mode 100644 index 4e7c4998d..000000000 --- a/src/layout/src/LayoutFooter.vue +++ /dev/null @@ -1,53 +0,0 @@ - - - diff --git a/src/layout/src/LayoutHeader.vue b/src/layout/src/LayoutHeader.tsx similarity index 51% rename from src/layout/src/LayoutHeader.vue rename to src/layout/src/LayoutHeader.tsx index bb31e7683..13870042e 100644 --- a/src/layout/src/LayoutHeader.vue +++ b/src/layout/src/LayoutHeader.tsx @@ -1,28 +1,16 @@ - - - diff --git a/src/layout/src/LayoutSider.tsx b/src/layout/src/LayoutSider.tsx new file mode 100644 index 000000000..5ace04674 --- /dev/null +++ b/src/layout/src/LayoutSider.tsx @@ -0,0 +1,296 @@ +import { + h, + defineComponent, + computed, + nextTick, + PropType, + ref, + watch, + CSSProperties, + toRef, + inject, + onBeforeUnmount +} from 'vue' +import { useTheme } from '../../_mixins' +import type { ThemeProps } from '../../_mixins' +import type { MaybeArray } from '../../_utils' +import { call } from '../../_utils' +import { NScrollbar } from '../../scrollbar' +import type { ScrollbarProps, ScrollbarRef } from '../../scrollbar' +import { layoutLight } from '../styles' +import type { LayoutTheme } from '../styles' +import style from './styles/layout-sider.cssr' +import type { LayoutInjection } from './Layout' +import ToggleButton from './ToggleButton' +import ToggleBar from './ToggleBar' +import { positionProp } from './interface' + +export default defineComponent({ + name: 'LayoutSider', + props: { + ...(useTheme.props as ThemeProps), + position: positionProp, + bordered: { + type: Boolean, + default: false + }, + collapsedWidth: { + type: Number, + default: 48 + }, + width: { + type: Number, + default: 272 + }, + collapseMode: { + type: String as PropType<'width' | 'transform'>, + default: 'transform' + }, + collapsed: { + type: Boolean, + default: false + }, + showContent: { + type: Boolean, + default: true + }, + showTrigger: { + type: [Boolean, String], + default: false + }, + nativeScrollbar: { + type: Boolean, + default: true + }, + duration: { + type: Number, + default: 300 + }, + scrollbarProps: Object as PropType>, + triggerStyle: Object as PropType, + // eslint-disable-next-line vue/prop-name-casing + 'onUpdate:collapsed': Function as PropType< + MaybeArray<(value: boolean) => void> + >, + // deprecated + onExpand: Function as PropType void>>, + onCollapse: Function as PropType void>> + }, + setup (props) { + const selfRef = ref(null) + const scrollbarRef = ref(null) + let collapseTimerId: number | null = null + const styleWidthRef = ref(null) + const styleMaxWidthRef = ref(null) + const NLayout = inject('NLayout', null) + const styleTransformRef = computed(() => { + if (props.collapseMode === 'transform') { + if (!props.collapsed) return 'translateX(0)' + else return `translateX(-${props.width - props.collapsedWidth}px)` + } + return '' + }) + function scrollTo (options: ScrollToOptions): void + function scrollTo (x: number, y: number): void + function scrollTo (options: ScrollToOptions | number, y?: number): void { + if (scrollbarRef.value) { + scrollbarRef.value.scrollTo(options as any, y as any) + } else if (selfRef.value) { + if (y === undefined) { + selfRef.value.scrollTo(options as any) + } else { + selfRef.value.scrollTo(options as any, y as any) + } + } + } + function handleTriggerClick (): void { + const { + 'onUpdate:collapsed': onUpdateCollapsed, + collapsed, + // deprecated + onExpand, + onCollapse + } = props + if (onUpdateCollapsed) { + call(onUpdateCollapsed, !collapsed) + } + if (collapsed) { + if (onExpand) call(onExpand) + } else { + if (onCollapse) call(onCollapse) + } + } + watch(toRef(props, 'width'), (value) => { + if (NLayout) { + NLayout.siderWidth = value + } + }) + watch(toRef(props, 'collapsedWidth'), (value) => { + if (NLayout) { + NLayout.siderCollapsedWidth = value + } + }) + watch(toRef(props, 'collapseMode'), (value) => { + if (NLayout) { + NLayout.siderCollapseMode = value + } + }) + watch(toRef(props, 'position'), (value) => { + if (NLayout) { + NLayout.siderPosition = value + } + }) + watch(toRef(props, 'collapsed'), (value) => { + if (props.collapseMode === 'width') { + if (collapseTimerId) { + window.clearTimeout(collapseTimerId) + } + if (value) { + styleMaxWidthRef.value = `${props.width}px` + void nextTick(() => { + void selfRef.value?.offsetWidth + styleMaxWidthRef.value = `${props.collapsedWidth}px` + }) + collapseTimerId = window.setTimeout(() => { + styleWidthRef.value = `${props.collapsedWidth}px` + styleMaxWidthRef.value = null + }, props.duration) + } else { + styleMaxWidthRef.value = `${props.collapsedWidth}px` + styleWidthRef.value = `${props.width}px` + void nextTick(() => { + void selfRef.value?.offsetWidth + styleMaxWidthRef.value = `${props.width}px` + }) + collapseTimerId = window.setTimeout(() => { + styleMaxWidthRef.value = null + }, props.duration) + } + } + if (NLayout) { + NLayout.siderCollapsed = value + } + }) + // onCreated, init + if (props.collapseMode === 'width') { + if (props.collapsed) { + styleWidthRef.value = `${props.collapsedWidth}px` + } else { + styleWidthRef.value = `${props.width}px` + } + } else { + styleWidthRef.value = `${props.width}px` + } + if (NLayout) { + NLayout.hasSider = true + NLayout.siderWidth = props.width + NLayout.siderCollapsedWidth = props.collapsedWidth + NLayout.siderCollapseMode = props.collapseMode + NLayout.siderPosition = props.position + NLayout.siderCollapsed = props.collapsed + } + onBeforeUnmount(() => { + if (NLayout) { + NLayout.hasSider = false + NLayout.siderWidth = null + NLayout.siderCollapsedWidth = null + NLayout.siderCollapseMode = null + NLayout.siderPosition = null + NLayout.siderCollapsed = null + } + }) + const themeRef = useTheme( + 'Layout', + 'LayoutSider', + style, + layoutLight, + props + ) + return { + selfRef, + scrollbarRef, + mergedTheme: themeRef, + styleTransform: styleTransformRef, + styleMaxWidth: styleMaxWidthRef, + styleWidth: styleWidthRef, + scrollTo, + handleTriggerClick, + cssVars: computed(() => { + const { + common: { cubicBezierEaseInOut }, + self: { + siderColor, + siderToggleButtonColor, + siderBorderColor, + siderToggleBarColor, + siderToggleBarColorHover + } + } = themeRef.value + return { + '--bezier': cubicBezierEaseInOut, + '--sider-color': siderColor, + '--sider-border-color': siderBorderColor, + '--sider-toggle-button-color': siderToggleButtonColor, + '--sider-toggle-bar-color': siderToggleBarColor, + '--sider-toggle-bar-color-hover': siderToggleBarColorHover + } + }) + } + }, + render () { + return ( + + ) + } +}) diff --git a/src/layout/src/LayoutSider.vue b/src/layout/src/LayoutSider.vue deleted file mode 100644 index e9f1c323c..000000000 --- a/src/layout/src/LayoutSider.vue +++ /dev/null @@ -1,284 +0,0 @@ - - - diff --git a/src/layout/src/ToggleBar.tsx b/src/layout/src/ToggleBar.tsx new file mode 100644 index 000000000..0ba8fb290 --- /dev/null +++ b/src/layout/src/ToggleBar.tsx @@ -0,0 +1,25 @@ +import { h, defineComponent } from 'vue' + +export default defineComponent({ + props: { + collapsed: { + type: Boolean, + default: false + } + }, + render () { + return ( +
+
+
+
+ ) + } +}) diff --git a/src/layout/src/ToggleBar.vue b/src/layout/src/ToggleBar.vue deleted file mode 100644 index 8637d6631..000000000 --- a/src/layout/src/ToggleBar.vue +++ /dev/null @@ -1,22 +0,0 @@ - - - diff --git a/src/layout/src/ToggleButton.tsx b/src/layout/src/ToggleButton.tsx new file mode 100644 index 000000000..3577761b6 --- /dev/null +++ b/src/layout/src/ToggleButton.tsx @@ -0,0 +1,17 @@ +import { h, defineComponent } from 'vue' + +export default defineComponent({ + name: 'LayoutToggleButton', + render () { + return ( +
+ + + +
+ ) + } +}) diff --git a/src/layout/src/ToggleButton.vue b/src/layout/src/ToggleButton.vue deleted file mode 100644 index 099bee527..000000000 --- a/src/layout/src/ToggleButton.vue +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/src/layout/src/interface.ts b/src/layout/src/interface.ts new file mode 100644 index 000000000..9ccf17666 --- /dev/null +++ b/src/layout/src/interface.ts @@ -0,0 +1,6 @@ +import { PropType } from 'vue' + +export const positionProp = { + type: String as PropType<'static' | 'absolute'>, + default: 'static' +} diff --git a/src/layout/src/layoutModeMixin.js b/src/layout/src/layoutModeMixin.js deleted file mode 100644 index 27d237fc6..000000000 --- a/src/layout/src/layoutModeMixin.js +++ /dev/null @@ -1,15 +0,0 @@ -export default { - inject: { - NLayout: { - default: null - } - }, - props: { - position: { - default: 'static', - validator (value) { - return ['static', 'absolute'].includes(value) - } - } - } -} diff --git a/src/layout/src/styles/layout-footer.cssr.js b/src/layout/src/styles/layout-footer.cssr.ts similarity index 100% rename from src/layout/src/styles/layout-footer.cssr.js rename to src/layout/src/styles/layout-footer.cssr.ts diff --git a/src/layout/src/styles/layout-header.cssr.js b/src/layout/src/styles/layout-header.cssr.ts similarity index 100% rename from src/layout/src/styles/layout-header.cssr.js rename to src/layout/src/styles/layout-header.cssr.ts diff --git a/src/layout/src/styles/layout-sider.cssr.js b/src/layout/src/styles/layout-sider.cssr.ts similarity index 95% rename from src/layout/src/styles/layout-sider.cssr.js rename to src/layout/src/styles/layout-sider.cssr.ts index 75aae22a9..26c59c19c 100644 --- a/src/layout/src/styles/layout-sider.cssr.js +++ b/src/layout/src/styles/layout-sider.cssr.ts @@ -108,15 +108,7 @@ export default cB('layout-sider', ` left: 0; top: 0; bottom: 0; - `, [ - cE('content', ` - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; - `) - ]), + `), cM('bordered', [ cE('border', { backgroundColor: 'var(--sider-border-color)' diff --git a/src/layout/src/styles/layout.cssr.js b/src/layout/src/styles/layout.cssr.ts similarity index 100% rename from src/layout/src/styles/layout.cssr.js rename to src/layout/src/styles/layout.cssr.ts diff --git a/src/layout/styles/dark.js b/src/layout/styles/dark.ts similarity index 89% rename from src/layout/styles/dark.js rename to src/layout/styles/dark.ts index 7bc942880..c04443f47 100644 --- a/src/layout/styles/dark.js +++ b/src/layout/styles/dark.ts @@ -1,8 +1,9 @@ import { composite } from 'seemly' import { commonDark } from '../../_styles/new-common' import { scrollbarDark } from '../../scrollbar/styles' +import type { LayoutTheme } from './light' -export default { +const layoutDark: LayoutTheme = { name: 'Layout', common: commonDark, peers: { @@ -31,3 +32,5 @@ export default { } } } + +export default layoutDark diff --git a/src/layout/styles/index.js b/src/layout/styles/index.js deleted file mode 100644 index 02ad332c8..000000000 --- a/src/layout/styles/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default as layoutDark } from './dark.js' -export { default as layoutLight } from './light.js' diff --git a/src/layout/styles/index.ts b/src/layout/styles/index.ts new file mode 100644 index 000000000..0c077c4a8 --- /dev/null +++ b/src/layout/styles/index.ts @@ -0,0 +1,3 @@ +export { default as layoutDark } from './dark' +export { default as layoutLight } from './light' +export type { LayoutTheme, LayoutThemeVars } from './light' diff --git a/src/layout/styles/light.js b/src/layout/styles/light.js deleted file mode 100644 index 3acdc5c66..000000000 --- a/src/layout/styles/light.js +++ /dev/null @@ -1,33 +0,0 @@ -import { composite } from 'seemly' -import { commonLight } from '../../_styles/new-common' -import { scrollbarLight } from '../../scrollbar/styles' - -export default { - name: 'Layout', - common: commonLight, - peers: { - Scrollbar: scrollbarLight - }, - self (vars) { - const { - textColor2, - bodyColor, - cardColor, - dividerColorOverlay, - scrollbarColorOverlay, - scrollbarColorHoverOverlay - } = vars - return { - textColor: textColor2, - color: bodyColor, - headerColor: cardColor, - headerBorderColor: dividerColorOverlay, - footerBorderColor: dividerColorOverlay, - siderBorderColor: dividerColorOverlay, - siderColor: cardColor, - siderToggleButtonColor: 'rgba(0, 0, 0, .15)', - siderToggleBarColor: composite(bodyColor, scrollbarColorOverlay), - siderToggleBarColorHover: composite(bodyColor, scrollbarColorHoverOverlay) - } - } -} diff --git a/src/layout/styles/light.ts b/src/layout/styles/light.ts new file mode 100644 index 000000000..6d783b64a --- /dev/null +++ b/src/layout/styles/light.ts @@ -0,0 +1,42 @@ +import { composite } from 'seemly' +import { commonLight } from '../../_styles/new-common' +import type { ThemeCommonVars } from '../../_styles/new-common' +import { scrollbarLight } from '../../scrollbar/styles' +import { createTheme } from '../../_mixins' + +const self = (vars: ThemeCommonVars) => { + const { + textColor2, + bodyColor, + cardColor, + dividerColorOverlay, + scrollbarColorOverlay, + scrollbarColorHoverOverlay + } = vars + return { + textColor: textColor2, + color: bodyColor, + headerColor: cardColor, + headerBorderColor: dividerColorOverlay, + footerBorderColor: dividerColorOverlay, + siderBorderColor: dividerColorOverlay, + siderColor: cardColor, + siderToggleButtonColor: 'rgba(0, 0, 0, .15)', + siderToggleBarColor: composite(bodyColor, scrollbarColorOverlay), + siderToggleBarColorHover: composite(bodyColor, scrollbarColorHoverOverlay) + } +} + +export type LayoutThemeVars = ReturnType + +const layoutLight = createTheme({ + name: 'Layout', + common: commonLight, + peers: { + Scrollbar: scrollbarLight + }, + self +}) + +export default layoutLight +export type LayoutTheme = typeof layoutLight