mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-01-18 12:34:25 +08:00
refactor(layout): ts
This commit is contained in:
parent
62242cf442
commit
ddda5c84be
@ -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'
|
6
src/layout/index.ts
Normal file
6
src/layout/index.ts
Normal file
@ -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'
|
134
src/layout/src/Layout.tsx
Normal file
134
src/layout/src/Layout.tsx
Normal file
@ -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<LayoutTheme>),
|
||||
position: positionProp,
|
||||
nativeScrollbar: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
scrollbarProps: Object as PropType<Partial<ScrollbarProps>>
|
||||
},
|
||||
setup (props) {
|
||||
const selfRef = ref<HTMLElement | null>(null)
|
||||
const scrollbarRef = ref<ScrollbarRef | null>(null)
|
||||
const themeRef = useTheme('Layout', 'Layout', style, layoutLight, props)
|
||||
const state = reactive<LayoutInjection>({
|
||||
hasSider: false,
|
||||
siderWidth: null,
|
||||
siderCollapsedWidth: null,
|
||||
siderCollapseMode: null,
|
||||
siderPosition: null,
|
||||
siderCollapsed: null
|
||||
})
|
||||
const NLayout = inject<LayoutInjection | null>('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<LayoutInjection>('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 (
|
||||
<div
|
||||
ref="selfRef"
|
||||
class={[
|
||||
'n-layout',
|
||||
`n-layout--${this.position}-positioned`,
|
||||
this.state.siderCollapseMode &&
|
||||
`n-layout--${this.state.siderCollapseMode}-collapse-mode`,
|
||||
{
|
||||
'n-layout--has-sider': this.state.hasSider
|
||||
}
|
||||
]}
|
||||
style={[this.mergedLayoutStyle, this.cssVars] as any}
|
||||
>
|
||||
{!this.nativeScrollbar ? (
|
||||
<NScrollbar
|
||||
{...this.scrollbarProps}
|
||||
ref="scrollbarRef"
|
||||
unstableTheme={this.mergedTheme.peers.Scrollbar}
|
||||
unstableThemeOverrides={this.mergedTheme.overrides.Scrollbar}
|
||||
>
|
||||
{this.$slots}
|
||||
</NScrollbar>
|
||||
) : (
|
||||
this.$slots
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
@ -1,134 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="n-layout"
|
||||
:class="{
|
||||
[`n-layout--${position}-positioned`]: true,
|
||||
'n-layout--has-sider': hasSider,
|
||||
[`n-layout--${siderCollapseMode}-collapse-mode`]: siderCollapseMode
|
||||
}"
|
||||
:style="mergedLayoutStyle"
|
||||
>
|
||||
<n-scrollbar
|
||||
v-if="!nativeScrollbar"
|
||||
ref="scrollbar"
|
||||
:unstable-theme="mergedTheme.peers.Scrollbar"
|
||||
:unstable-theme-overrides="mergedTheme.overrides.Scrollbar"
|
||||
v-bind="scrollbarProps"
|
||||
>
|
||||
<slot />
|
||||
</n-scrollbar>
|
||||
<slot v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { nextTick, defineComponent, computed } from 'vue'
|
||||
import { NScrollbar } from '../../scrollbar'
|
||||
import { useTheme } from '../../_mixins'
|
||||
import { layoutLight } from '../styles'
|
||||
import layoutModeMixin from './layoutModeMixin'
|
||||
import style from './styles/layout.cssr.js'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Layout',
|
||||
alias: ['LayoutContent'],
|
||||
components: {
|
||||
NScrollbar
|
||||
},
|
||||
mixins: [layoutModeMixin],
|
||||
provide () {
|
||||
return {
|
||||
NLayout: this
|
||||
}
|
||||
},
|
||||
props: {
|
||||
...useTheme.props,
|
||||
nativeScrollbar: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
scrollbarProps: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
const themeRef = useTheme('Layout', 'Layout', style, layoutLight, props)
|
||||
return {
|
||||
mergedTheme: themeRef,
|
||||
cssVars: computed(() => {
|
||||
const {
|
||||
common: { cubicBezierEaseInOut },
|
||||
self: { color, textColor }
|
||||
} = themeRef.value
|
||||
return {
|
||||
'--bezier': cubicBezierEaseInOut,
|
||||
'--color': color,
|
||||
'--text-color': textColor
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
hasSider: false,
|
||||
siderWidth: null,
|
||||
collapsedSiderWidth: null,
|
||||
siderCollapseMode: null,
|
||||
siderPosition: null,
|
||||
siderCollapsed: null,
|
||||
childLayoutTransitionDisabled: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
styleMarginLeft () {
|
||||
const { NLayout } = this
|
||||
if (NLayout && NLayout.hasSider) {
|
||||
if (
|
||||
NLayout.siderPosition === 'absolute' &&
|
||||
this.position === 'absolute'
|
||||
) {
|
||||
if (NLayout.siderCollapsed) {
|
||||
return `${NLayout.collapsedSiderWidth}px`
|
||||
} else {
|
||||
return `${NLayout.siderWidth}px`
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
mergedLayoutStyle () {
|
||||
return Object.assign(
|
||||
{
|
||||
marginLeft: this.styleMarginLeft,
|
||||
transition: this.transitionDisabled ? 'none' : null
|
||||
},
|
||||
this.cssVars
|
||||
)
|
||||
},
|
||||
transitionDisabled () {
|
||||
const { NLayout } = this
|
||||
if (NLayout && NLayout.childLayoutTransitionDisabled) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
scrollTo (...args) {
|
||||
if (this.$refs.scrollbar) {
|
||||
this.$refs.scrollbar.scrollTo(...args)
|
||||
} else {
|
||||
this.$el.scrollTo(...args)
|
||||
}
|
||||
},
|
||||
blockChildLayoutTransitionOneTick () {
|
||||
this.childLayoutTransitionDisabled = true
|
||||
nextTick(() => {
|
||||
this.childLayoutTransitionDisabled = false
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
56
src/layout/src/LayoutFooter.tsx
Normal file
56
src/layout/src/LayoutFooter.tsx
Normal file
@ -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<LayoutTheme>),
|
||||
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 (
|
||||
<div
|
||||
class={[
|
||||
'n-layout-footer',
|
||||
{
|
||||
[`n-layout-footer--${this.position}-positioned`]: this.position,
|
||||
'n-layout-footer--bordered': this.bordered
|
||||
}
|
||||
]}
|
||||
style={this.cssVars as CSSProperties}
|
||||
>
|
||||
{renderSlot(this.$slots, 'default')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
@ -1,53 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="n-layout-footer"
|
||||
:class="{
|
||||
[`n-layout-footer--${position}-positioned`]: position,
|
||||
[`n-layout-footer--bordered`]: bordered
|
||||
}"
|
||||
:style="cssVars"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import layoutModeMixin from './layoutModeMixin'
|
||||
import { useTheme } from '../../_mixins'
|
||||
import { layoutLight } from '../styles'
|
||||
import style from './styles/layout-footer.cssr.js'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutFooter',
|
||||
mixins: [layoutModeMixin],
|
||||
props: {
|
||||
...useTheme.props,
|
||||
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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
@ -1,28 +1,16 @@
|
||||
<template>
|
||||
<div
|
||||
class="n-layout-header"
|
||||
:class="{
|
||||
[`n-layout-header--${position}-positioned`]: position,
|
||||
[`n-layout-header--bordered`]: bordered
|
||||
}"
|
||||
:style="cssVars"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, computed } from 'vue'
|
||||
import { h, defineComponent, computed, CSSProperties } from 'vue'
|
||||
import { useTheme } from '../../_mixins'
|
||||
import layoutModeMixin from './layoutModeMixin'
|
||||
import type { ThemeProps } from '../../_mixins'
|
||||
import { layoutLight } from '../styles'
|
||||
import style from './styles/layout-header.cssr.js'
|
||||
import type { LayoutTheme } from '../styles'
|
||||
import { positionProp } from './interface'
|
||||
import style from './styles/layout-header.cssr'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutHeader',
|
||||
mixins: [layoutModeMixin],
|
||||
props: {
|
||||
...useTheme.props,
|
||||
...(useTheme.props as ThemeProps<LayoutTheme>),
|
||||
position: positionProp,
|
||||
bordered: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@ -49,6 +37,21 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
render () {
|
||||
return (
|
||||
<div
|
||||
class={[
|
||||
'n-layout-header',
|
||||
this.position && `n-layout-header--${this.position}-positioned`,
|
||||
{
|
||||
'n-layout-header--bordered': this.bordered
|
||||
}
|
||||
]}
|
||||
style={this.cssVars as CSSProperties}
|
||||
>
|
||||
{this.$slots}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
</script>
|
296
src/layout/src/LayoutSider.tsx
Normal file
296
src/layout/src/LayoutSider.tsx
Normal file
@ -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<LayoutTheme>),
|
||||
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<Partial<ScrollbarProps>>,
|
||||
triggerStyle: Object as PropType<CSSProperties>,
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:collapsed': Function as PropType<
|
||||
MaybeArray<(value: boolean) => void>
|
||||
>,
|
||||
// deprecated
|
||||
onExpand: Function as PropType<MaybeArray<() => void>>,
|
||||
onCollapse: Function as PropType<MaybeArray<() => void>>
|
||||
},
|
||||
setup (props) {
|
||||
const selfRef = ref<HTMLElement | null>(null)
|
||||
const scrollbarRef = ref<ScrollbarRef | null>(null)
|
||||
let collapseTimerId: number | null = null
|
||||
const styleWidthRef = ref<string | null>(null)
|
||||
const styleMaxWidthRef = ref<string | null>(null)
|
||||
const NLayout = inject<LayoutInjection | null>('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 (
|
||||
<aside
|
||||
ref="selfRef"
|
||||
class={[
|
||||
'n-layout-sider',
|
||||
this.position && [`n-layout-sider--${this.position}-positioned`],
|
||||
{
|
||||
'n-layout-sider--bordered': this.bordered,
|
||||
'n-layout-sider--collapsed': this.collapsed,
|
||||
'n-layout-sider--show-content': this.showContent
|
||||
}
|
||||
]}
|
||||
style={
|
||||
[
|
||||
this.cssVars,
|
||||
{
|
||||
transform: this.styleTransform,
|
||||
maxWidth: this.styleMaxWidth,
|
||||
width: this.styleWidth
|
||||
}
|
||||
] as any
|
||||
}
|
||||
>
|
||||
{!this.nativeScrollbar ? (
|
||||
<NScrollbar
|
||||
ref="scrollbarRef"
|
||||
class="n-layout-sider__content"
|
||||
{...this.scrollbarProps}
|
||||
unstableTheme={this.mergedTheme.peers.Scrollbar}
|
||||
unstableThemeOverrides={this.mergedTheme.overrides.Scrollbar}
|
||||
>
|
||||
{this.$slots}
|
||||
</NScrollbar>
|
||||
) : (
|
||||
<div class="n-layout-sider__content">{this.$slots}</div>
|
||||
)}
|
||||
|
||||
{this.bordered ? <div class="n-layout-sider__border" /> : null}
|
||||
{this.showTrigger ? (
|
||||
this.showTrigger === 'arrow-circle' ? (
|
||||
<ToggleButton
|
||||
style={this.triggerStyle}
|
||||
onClick={this.handleTriggerClick}
|
||||
/>
|
||||
) : (
|
||||
<ToggleBar
|
||||
collapsed={this.collapsed}
|
||||
style={this.triggerStyle}
|
||||
onClick={this.handleTriggerClick}
|
||||
/>
|
||||
)
|
||||
) : null}
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
})
|
@ -1,284 +0,0 @@
|
||||
<template>
|
||||
<aside
|
||||
class="n-layout-sider"
|
||||
:class="{
|
||||
[`n-layout-sider--${position}-positioned`]: position,
|
||||
[`n-layout-sider--bordered`]: bordered,
|
||||
[`n-layout-sider--collapsed`]: collapsed,
|
||||
[`n-layout-sider--show-content`]: showContent
|
||||
}"
|
||||
:style="{
|
||||
...cssVars,
|
||||
transform: styleTransform,
|
||||
maxWidth: styleMaxWidth,
|
||||
width: styleWidth
|
||||
}"
|
||||
>
|
||||
<n-scrollbar
|
||||
v-if="!nativeScrollbar"
|
||||
ref="scrollbar"
|
||||
class="n-layout-sider__content"
|
||||
:unstable-theme="NLayout.mergedTheme.peers.Scrollbar"
|
||||
:unstable-theme-overrides="NLayout.mergedTheme.overrides.Scrollbar"
|
||||
v-bind="scrollbarProps"
|
||||
>
|
||||
<slot />
|
||||
</n-scrollbar>
|
||||
<div v-else class="n-layout-sider__content">
|
||||
<slot />
|
||||
</div>
|
||||
<div v-if="bordered" class="n-layout-sider__border" />
|
||||
<template v-if="showTrigger">
|
||||
<toggle-button
|
||||
v-if="showTrigger === 'arrow-circle'"
|
||||
:style="triggerStyle"
|
||||
@click="handleTriggerClick"
|
||||
/>
|
||||
<toggle-bar
|
||||
v-else
|
||||
:collapsed="collapsed"
|
||||
:style="triggerStyle"
|
||||
@click="handleTriggerClick"
|
||||
/>
|
||||
</template>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, computed, nextTick } from 'vue'
|
||||
import { useTheme } from '../../_mixins'
|
||||
import { NScrollbar } from '../../scrollbar'
|
||||
import { layoutLight } from '../styles'
|
||||
import style from './styles/layout-sider.cssr.js'
|
||||
import layoutModeMixin from './layoutModeMixin'
|
||||
import ToggleButton from './ToggleButton.vue'
|
||||
import ToggleBar from './ToggleBar.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutSider',
|
||||
components: {
|
||||
ToggleButton,
|
||||
ToggleBar,
|
||||
NScrollbar
|
||||
},
|
||||
mixins: [layoutModeMixin],
|
||||
props: {
|
||||
...useTheme.props,
|
||||
bordered: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
collapsedWidth: {
|
||||
type: Number,
|
||||
default: 48
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: 272
|
||||
},
|
||||
collapseMode: {
|
||||
validator (value) {
|
||||
return ['width', 'transform'].includes(value)
|
||||
},
|
||||
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: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
},
|
||||
triggerStyle: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
},
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:collapsed': {
|
||||
type: Function,
|
||||
default: undefined
|
||||
},
|
||||
// deprecated
|
||||
onExpand: {
|
||||
type: Function,
|
||||
default: undefined
|
||||
},
|
||||
onCollapse: {
|
||||
type: Function,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
const themeRef = useTheme(
|
||||
'Layout',
|
||||
'LayoutSider',
|
||||
style,
|
||||
layoutLight,
|
||||
props
|
||||
)
|
||||
return {
|
||||
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
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
collapseTimerId: null,
|
||||
styleWidth: null,
|
||||
styleMaxWidth: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
styleTransform () {
|
||||
if (this.collapseMode === 'transform') {
|
||||
if (!this.collapsed) return 'translateX(0)'
|
||||
else return `translateX(-${this.width - this.collapsedWidth}px)`
|
||||
}
|
||||
return null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
width (value) {
|
||||
if (this.NLayout) {
|
||||
this.NLayout.siderWidth = value
|
||||
}
|
||||
},
|
||||
collapsedWidth (value) {
|
||||
if (this.NLayout) {
|
||||
this.NLayout.collapsedWidth = value
|
||||
}
|
||||
},
|
||||
collapseMode (value) {
|
||||
if (this.NLayout) {
|
||||
this.NLayout.siderCollapseMode = value
|
||||
}
|
||||
},
|
||||
position (value) {
|
||||
if (this.NLayout) {
|
||||
this.NLayout.siderPosition = value
|
||||
}
|
||||
},
|
||||
collapsed (value) {
|
||||
if (this.collapseMode === 'width') {
|
||||
if (this.collapseTimerId) {
|
||||
window.clearTimeout(this.collapseTimerId)
|
||||
}
|
||||
if (value) {
|
||||
this.styleMaxWidth = `${this.width}px`
|
||||
nextTick(() => {
|
||||
this.$el.getBoundingClientRect()
|
||||
this.styleMaxWidth = `${this.collapsedWidth}px`
|
||||
})
|
||||
this.collapseTimerId = window.setTimeout(() => {
|
||||
this.styleWidth = `${this.collapsedWidth}px`
|
||||
this.styleMaxWidth = null
|
||||
}, this.duration)
|
||||
} else {
|
||||
this.styleMaxWidth = `${this.collapsedWidth}px`
|
||||
this.styleWidth = `${this.width}px`
|
||||
nextTick(() => {
|
||||
this.$el.getBoundingClientRect()
|
||||
this.styleMaxWidth = `${this.width}px`
|
||||
})
|
||||
this.collapseTimerId = window.setTimeout(() => {
|
||||
this.styleMaxWidth = null
|
||||
}, this.duration)
|
||||
}
|
||||
}
|
||||
if (this.NLayout) {
|
||||
this.NLayout.siderCollapsed = value
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (this.collapseMode === 'width') {
|
||||
if (this.collapsed) {
|
||||
this.styleWidth = `${this.collapsedWidth}px`
|
||||
} else {
|
||||
this.styleWidth = `${this.width}px`
|
||||
}
|
||||
} else {
|
||||
this.styleWidth = `${this.width}px`
|
||||
}
|
||||
const NLayout = this.NLayout
|
||||
if (NLayout) {
|
||||
NLayout.hasSider = true
|
||||
NLayout.siderWidth = this.width
|
||||
NLayout.collapsedSiderWidth = this.collapsedWidth
|
||||
NLayout.siderCollapseMode = this.collapseMode
|
||||
NLayout.siderPosition = this.position
|
||||
NLayout.siderCollapsed = this.collapsed
|
||||
}
|
||||
},
|
||||
beforeUnmount () {
|
||||
const NLayout = this.NLayout
|
||||
if (NLayout) {
|
||||
NLayout.hasSider = false
|
||||
NLayout.siderWidth = null
|
||||
NLayout.collapsedSiderWidth = null
|
||||
NLayout.siderCollapseMode = null
|
||||
NLayout.siderPosition = null
|
||||
NLayout.siderCollapsed = null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
scrollTo (...args) {
|
||||
if (this.$refs.scrollbar) {
|
||||
this.$refs.scrollbar.scrollTo(...args)
|
||||
} else {
|
||||
this.$el.scrollTo(...args)
|
||||
}
|
||||
},
|
||||
handleTriggerClick () {
|
||||
const {
|
||||
'onUpdate:collapsed': onUpdateCollapsed,
|
||||
collapsed,
|
||||
// deprecated
|
||||
onExpand,
|
||||
onCollapse
|
||||
} = this
|
||||
if (onUpdateCollapsed) onUpdateCollapsed(!collapsed)
|
||||
if (collapsed) {
|
||||
if (onExpand) onExpand()
|
||||
} else {
|
||||
if (onCollapse) onCollapse()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
25
src/layout/src/ToggleBar.tsx
Normal file
25
src/layout/src/ToggleBar.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { h, defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
render () {
|
||||
return (
|
||||
<div
|
||||
class={[
|
||||
'n-layout-toggle-bar',
|
||||
{
|
||||
'n-layout-toggle-bar--collapsed': this.collapsed
|
||||
}
|
||||
]}
|
||||
>
|
||||
<div class="n-layout-toggle-bar__top" />
|
||||
<div class="n-layout-toggle-bar__bottom" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
@ -1,22 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="n-layout-toggle-bar"
|
||||
:class="{
|
||||
'n-layout-toggle-bar--collapsed': collapsed
|
||||
}"
|
||||
>
|
||||
<div class="n-layout-toggle-bar__top" />
|
||||
<div class="n-layout-toggle-bar__bottom" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
17
src/layout/src/ToggleButton.tsx
Normal file
17
src/layout/src/ToggleButton.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { h, defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutToggleButton',
|
||||
render () {
|
||||
return (
|
||||
<div class="n-layout-toggle-button">
|
||||
<svg viewBox="0 0 56.06 56.06">
|
||||
<path
|
||||
d="M50,22A28,28,0,1,0,78,50,28.06,28.06,0,0,0,50,22ZM65.09,52.16h-25l7.1,7.1a2.16,2.16,0,0,1-3.05,3.05L33.38,51.52a2.15,2.15,0,0,1,0-3L44.16,37.69a2.16,2.16,0,0,1,3.05,3.05l-7.1,7.1h25a2.16,2.16,0,0,1,0,4.32Z"
|
||||
transform="translate(-21.97 -21.97)"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
@ -1,10 +0,0 @@
|
||||
<template>
|
||||
<div class="n-layout-toggle-button">
|
||||
<svg viewBox="0 0 56.06 56.06">
|
||||
<path
|
||||
d="M50,22A28,28,0,1,0,78,50,28.06,28.06,0,0,0,50,22ZM65.09,52.16h-25l7.1,7.1a2.16,2.16,0,0,1-3.05,3.05L33.38,51.52a2.15,2.15,0,0,1,0-3L44.16,37.69a2.16,2.16,0,0,1,3.05,3.05l-7.1,7.1h25a2.16,2.16,0,0,1,0,4.32Z"
|
||||
transform="translate(-21.97 -21.97)"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
6
src/layout/src/interface.ts
Normal file
6
src/layout/src/interface.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { PropType } from 'vue'
|
||||
|
||||
export const positionProp = {
|
||||
type: String as PropType<'static' | 'absolute'>,
|
||||
default: 'static'
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
export default {
|
||||
inject: {
|
||||
NLayout: {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
props: {
|
||||
position: {
|
||||
default: 'static',
|
||||
validator (value) {
|
||||
return ['static', 'absolute'].includes(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)'
|
@ -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
|
@ -1,2 +0,0 @@
|
||||
export { default as layoutDark } from './dark.js'
|
||||
export { default as layoutLight } from './light.js'
|
3
src/layout/styles/index.ts
Normal file
3
src/layout/styles/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { default as layoutDark } from './dark'
|
||||
export { default as layoutLight } from './light'
|
||||
export type { LayoutTheme, LayoutThemeVars } from './light'
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
42
src/layout/styles/light.ts
Normal file
42
src/layout/styles/light.ts
Normal file
@ -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<typeof self>
|
||||
|
||||
const layoutLight = createTheme({
|
||||
name: 'Layout',
|
||||
common: commonLight,
|
||||
peers: {
|
||||
Scrollbar: scrollbarLight
|
||||
},
|
||||
self
|
||||
})
|
||||
|
||||
export default layoutLight
|
||||
export type LayoutTheme = typeof layoutLight
|
Loading…
Reference in New Issue
Block a user