Skip to content

Commit

Permalink
feat(pro:layout): add logo prop and update style (#1307)
Browse files Browse the repository at this point in the history
* feat(pro:layout): add logo prop and update style

* style(comp:layout): css varibale update
  • Loading branch information
danranVm committed Nov 28, 2022
1 parent 23aeeed commit 1d47a0c
Show file tree
Hide file tree
Showing 64 changed files with 1,150 additions and 1,218 deletions.
18 changes: 11 additions & 7 deletions packages/cdk/utils/src/composable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,32 +50,36 @@ export function useState<T>(defaultOrFactory: T | (() => T), shallow = true): [C
return [computed(() => state.value) as ComputedRef<T>, setState]
}

export function useControlledProp<T, K extends keyof T>(props: T, key: K): [ComputedRef<T[K]>, (value: T[K]) => void]
export function useControlledProp<T, K extends keyof T>(
props: T,
key: K,
): [ComputedRef<T[K]>, (value: T[K], ...args: unknown[]) => void]
export function useControlledProp<T, K extends keyof T>(
props: T,
key: K,
defaultOrFactory: Exclude<T[K], undefined> | (() => Exclude<T[K], undefined>),
): [ComputedRef<Exclude<T[K], undefined>>, (value: Exclude<T[K], undefined>) => void]
): [ComputedRef<Exclude<T[K], undefined>>, (value: Exclude<T[K], undefined>, ...args: unknown[]) => void]
export function useControlledProp<T, K extends keyof T>(
props: T,
key: K,
defaultOrFactory?: Exclude<T[K], undefined> | (() => Exclude<T[K], undefined>),
): [ComputedRef<T[K]>, (value: T[K]) => void] {
const defaultValue = (isFunction(defaultOrFactory) ? defaultOrFactory() : defaultOrFactory)!
): [ComputedRef<T[K]>, (value: T[K], ...args: unknown[]) => void] {
const tempProp = shallowRef(props[key])

watch(
() => props[key],
value => (tempProp.value = value),
)

const state = computed(() => props[key] ?? tempProp.value ?? defaultValue)
const state = computed(
() => props[key] ?? tempProp.value ?? (isFunction(defaultOrFactory) ? defaultOrFactory() : defaultOrFactory)!,
)

const setState = (value: T[K]) => {
const setState = (value: T[K], ...args: unknown[]) => {
if (value !== toRaw(state.value)) {
tempProp.value = value
// eslint-disable-next-line @typescript-eslint/no-explicit-any
callEmit((props as any)[`onUpdate:${key as string}`], value)
callEmit((props as any)[`onUpdate:${key as string}`], value, ...args)
}
}

Expand Down
4 changes: 4 additions & 0 deletions packages/components/button/docs/Api.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@
| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
| --- | --- | --- | --- | --- | --- |
| `block` | 将按钮宽度调整为自适应其父元素的宽度 | `boolean` | - | - |- |
| `danger` | 设置按钮组危险状态 | `boolean` | - | - |- |
| `disabled` | 设置按钮组禁用状态 | `boolean` | - | - |- |
| `gap` | 设置按钮组的 gap 配置 | `number \| string` | `0` | - | 也就是 `Space``size` |
| `ghost` | 设置按钮组为幽灵状态 | `boolean` | - | - |- |
| `mode` | 设置组内按钮种类 | `'primary' \| 'default' \| 'dashed' \| 'text' \| 'link'` | - | - |- |
| `shape` | 设置组内按钮形状 | `'circle' \| 'round'` | - | - |- |
| `size` | 设置组内按钮大小 | `'lg' \| 'md' \| 'sm'` | - | - |- |
16 changes: 12 additions & 4 deletions packages/components/button/src/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { FORM_TOKEN } from '@idux/components/form'
import { IxIcon } from '@idux/components/icon'

import { buttonToken } from './token'
import { buttonProps } from './types'
import { type ButtonGroupProps, buttonProps } from './types'

const aProps = ['href', 'target', 'rel', 'download', 'hreflang', 'ping']

Expand All @@ -24,14 +24,22 @@ export default defineComponent({
const common = useGlobalConfig('common')
const mergedPrefixCls = computed(() => `${common.prefixCls}-button`)
const config = useGlobalConfig('button')
const groupProps = inject(buttonToken, {})
const groupProps = inject(buttonToken, {} as ButtonGroupProps)
const formContext = inject(FORM_TOKEN, null)

const mode = computed(() => props.mode ?? groupProps.mode ?? 'default')
const size = computed(() => props.size ?? groupProps.size ?? formContext?.size.value ?? config.size)

const classes = computed(() => {
const { block, danger, disabled, ghost, loading, icon, shape = groupProps.shape } = props
const {
block = groupProps.block,
danger = groupProps.danger,
disabled = groupProps.disabled,
ghost = groupProps.ghost,
loading,
icon,
shape = groupProps.shape,
} = props
const prefixCls = mergedPrefixCls.value
return normalizeClass({
[prefixCls]: true,
Expand All @@ -40,7 +48,7 @@ export default defineComponent({
[`${prefixCls}-disabled`]: disabled,
[`${prefixCls}-ghost`]: ghost,
[`${prefixCls}-loading`]: loading,
[`${prefixCls}-icon-only`]: !slots.default && (icon || loading),
[`${prefixCls}-icon-only`]: !slots.default && (icon || loading || slots.icon),
[`${prefixCls}-${mode.value}`]: mode.value,
[`${prefixCls}-${shape}`]: !!shape,
[`${prefixCls}-${size.value}`]: true,
Expand Down
2 changes: 1 addition & 1 deletion packages/components/button/src/ButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default defineComponent({
provide(buttonToken, props)

return () => (
<IxSpace class={classes.value} size={props.gap}>
<IxSpace class={classes.value} block={props.block} size={props.gap}>
{slots.default && slots.default()}
</IxSpace>
)
Expand Down
4 changes: 4 additions & 0 deletions packages/components/button/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ export type ButtonComponent = DefineComponent<
export type ButtonInstance = InstanceType<DefineComponent<ButtonProps>>

export const buttonGroupProps = {
block: { type: Boolean, default: undefined },
danger: { type: Boolean, default: undefined },
disabled: { type: Boolean, default: undefined },
gap: { type: [Number, String] as PropType<number | string>, default: 0 },
ghost: { type: Boolean, default: undefined },
mode: String as PropType<ButtonMode>,
size: String as PropType<ButtonSize>,
shape: String as PropType<ButtonShape>,
Expand Down
10 changes: 9 additions & 1 deletion packages/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,14 @@ import { IxIcon } from '@idux/components/icon'
import { IxImage, IxImageViewer } from '@idux/components/image'
import { IxInput } from '@idux/components/input'
import { IxInputNumber } from '@idux/components/input-number'
import { IxLayout, IxLayoutContent, IxLayoutFooter, IxLayoutHeader, IxLayoutSider } from '@idux/components/layout'
import {
IxLayout,
IxLayoutContent,
IxLayoutFooter,
IxLayoutHeader,
IxLayoutSider,
IxLayoutSiderTrigger,
} from '@idux/components/layout'
import { IxList, IxListItem } from '@idux/components/list'
import { IxLoadingBarProvider } from '@idux/components/loading-bar'
import { IxMenu, IxMenuDivider, IxMenuItem, IxMenuItemGroup, IxMenuSub } from '@idux/components/menu'
Expand Down Expand Up @@ -116,6 +123,7 @@ const components = [
IxLayoutFooter,
IxLayoutHeader,
IxLayoutSider,
IxLayoutSiderTrigger,
IxList,
IxListItem,
IxLoadingBarProvider,
Expand Down
56 changes: 46 additions & 10 deletions packages/components/layout/__tests__/layout.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mount } from '@vue/test-utils'
import { MountingOptions, mount } from '@vue/test-utils'
import { h } from 'vue'

import { renderWork } from '@tests'
Expand All @@ -8,6 +8,7 @@ import LayoutContent from '../src/LayoutContent'
import LayoutFooter from '../src/LayoutFooter'
import LayoutHeader from '../src/LayoutHeader'
import LayoutSider from '../src/LayoutSider'
import LayoutSiderTrigger from '../src/LayoutSiderTrigger'
import { LayoutProps } from '../src/types'

const defaultSlots = [
Expand All @@ -18,21 +19,56 @@ const defaultSlots = [
]

describe('Layout', () => {
const LayoutMount = (options?: MountingOptions<Partial<LayoutProps>>) => {
return mount(Layout, options)
}

renderWork<LayoutProps>(Layout, {
slots: {
default: () => defaultSlots,
},
})

describe('LayoutSider', () => {
test('collapsed work', async () => {
const wrapper = mount(Layout, {
slots: {
default: () => [h(LayoutSider, { collapsed: true }, { default: () => 'sider' })],
},
})

expect(wrapper.find('.ix-layout-sider').classes()).toContain('ix-layout-sider-collapsed')
test('collapsed work', async () => {
const onUpdateCollapsed = vi.fn()
const wrapper = mount(Layout, {
slots: {
default: () => [
h(
LayoutSider,
{ collapsed: true, 'onUpdate:collapsed': onUpdateCollapsed },
{ default: () => h(LayoutSiderTrigger) },
),
],
},
})

expect(wrapper.find('.ix-layout-sider').classes()).toContain('ix-layout-sider-collapsed')

await wrapper.findComponent(LayoutSiderTrigger).find('button').trigger('click')

expect(onUpdateCollapsed).toBeCalledWith(false, 'trigger')
})

test('fixed work', async () => {
const wrapper = LayoutMount({ props: { fixed: true } })

expect(wrapper.classes()).toContain('ix-layout-fixed-header')
expect(wrapper.classes()).toContain('ix-layout-fixed-sider')

await wrapper.setProps({ fixed: false })

expect(wrapper.classes()).not.toContain('ix-layout-fixed-header')
expect(wrapper.classes()).not.toContain('ix-layout-fixed-sider')

await wrapper.setProps({ fixed: { header: true, sider: false } })

expect(wrapper.classes()).toContain('ix-layout-fixed-header')
expect(wrapper.classes()).not.toContain('ix-layout-fixed-sider')

await wrapper.setProps({ fixed: { header: false, sider: true } })

expect(wrapper.classes()).not.toContain('ix-layout-fixed-header')
expect(wrapper.classes()).toContain('ix-layout-fixed-sider')
})
})
29 changes: 28 additions & 1 deletion packages/components/layout/docs/Api.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@

布局容器, 其内可放置 `IxLayoutContent`, `IxLayoutFooter`, `IxLayoutHeader``IxLayoutSider` 等组件。

#### LayoutProps

| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
| --- | --- | --- | --- | --- | --- |
| `fixed` | 顶部栏和侧边栏是否固定 | `boolean \| { sider: boolean, header: boolean }` | `false` | - | - |
| `floatSider` | 漂浮的侧边栏 | `boolean` | `false` | - |`true` 时,侧边栏展开不挤压 `Content` 的位置 |

### IxLayoutContent

内容部分,自带默认样式及基本功能,其下可嵌套任何元素,只能放在 `IxLayout` 中。
Expand All @@ -24,4 +31,24 @@
| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
| --- | --- | --- | --- | --- | --- |
| `v-model:collapsed` | 是否折叠状态 | `boolean` | - | - | - |
| `breakpoint` | 触发响应式布局的断点 | `xs`, `sm`, `md`, `lg`, `xl` | - | - | - |
| `breakpoint` | 触发折叠状态的断点 | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | - | - | - |
| `pointer` | 通过 `pointer` 进入和离开触发折叠状态的改变 | `boolean` | `false` | - | - |
| `pointerDelay` | 触发叠状态的改变的延迟时间 | `number \| [number, number]` | `[0, 100]` | - | 为数组时,第一个元素是延迟显示的时间,第二个元素是延迟隐藏的时间 |
| `onUpdate:collapsed` | 折叠状态改变后的回调 | `(collapsed: boolean, changeType: 'breakpoint' \| 'pointer' \| 'trigger') => void` | - | - | - |

### IxLayoutSiderTrigger

侧边栏折叠状态触发器,可以控制侧边栏的折叠状态,必须在 `IxLayoutSider` 中使用。

#### LayoutSiderTriggerProps

| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
| --- | --- | --- | --- | --- | --- |
| `icon` | 自定义折叠图标 | `string \| VNode \| Array<string \| VNode>` | - | - | 默认为 `[menu-fold, menu-unfold]` |

#### LayoutSiderTriggerSlots

| 名称 | 说明 | 参数类型 | 备注 |
| --- | --- | --- | --- |
| `default` | 自定义内容 | - | - |
| `icon` | 自定义图标 | `{ collapsed: boolean }` | - |
6 changes: 2 additions & 4 deletions packages/components/layout/docs/Theme.zh.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
| 名称 | default | seer | 备注 |
| --- | --- | --- | --- |
| `@layout-header-height` | `48px` | `44px` | - |
| `@layout-header-padding` | `0 16px` | - | - |
| `@layout-sider-width` | `224px` | - | - |
| `@layout-sider-collapsed-width` | `48px` | `44px` | - |
| `@layout-footer-padding` | `16px 32px` | - | - |
| `@layout-sider-collapsed-width` | `@menu-collapsed-width` | `44px` | - |
| `@layout-header-height` | - | `44px` | - |
10 changes: 9 additions & 1 deletion packages/components/layout/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,24 @@ import type {
LayoutFooterComponent,
LayoutHeaderComponent,
LayoutSiderComponent,
LayoutSiderTriggerComponent,
} from './src/types'

import Layout from './src/Layout'
import LayoutContent from './src/LayoutContent'
import LayoutFooter from './src/LayoutFooter'
import LayoutHeader from './src/LayoutHeader'
import LayoutSider from './src/LayoutSider'
import LayoutSiderTrigger from './src/LayoutSiderTrigger'

const IxLayout = Layout as unknown as LayoutComponent
const IxLayoutContent = LayoutContent as unknown as LayoutContentComponent
const IxLayoutFooter = LayoutFooter as unknown as LayoutFooterComponent
const IxLayoutHeader = LayoutHeader as unknown as LayoutHeaderComponent
const IxLayoutSider = LayoutSider as unknown as LayoutSiderComponent
const IxLayoutSiderTrigger = LayoutSiderTrigger as unknown as LayoutSiderTriggerComponent

export { IxLayout, IxLayoutContent, IxLayoutFooter, IxLayoutHeader, IxLayoutSider }
export { IxLayout, IxLayoutContent, IxLayoutFooter, IxLayoutHeader, IxLayoutSider, IxLayoutSiderTrigger }

export type {
LayoutInstance,
Expand All @@ -43,4 +46,9 @@ export type {
LayoutSiderInstance,
LayoutSiderComponent,
LayoutSiderPublicProps as LayoutSiderProps,
LayoutSiderTriggerInstance,
LayoutSiderTriggerComponent,
LayoutSiderTriggerPublicProps as LayoutSiderTriggerProps,
LayoutFixedType,
LayoutCollapseChangeType,
} from './src/types'
26 changes: 23 additions & 3 deletions packages/components/layout/src/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import { computed, defineComponent } from 'vue'
import { computed, defineComponent, normalizeClass } from 'vue'

import { isObject } from 'lodash-es'

import { useGlobalConfig } from '@idux/components/config'

Expand All @@ -14,12 +16,30 @@ import { layoutProps } from './types'
export default defineComponent({
name: 'IxLayout',
props: layoutProps,
setup(_, { slots }) {
setup(props, { slots }) {
const common = useGlobalConfig('common')
const mergedPrefixCls = computed(() => `${common.prefixCls}-layout`)
const mergedFixed = computed(() => {
const { fixed } = props
if (isObject(fixed)) {
const { header = false, sider = false } = fixed
return { header, sider }
}
return { header: fixed, sider: fixed }
})
const classes = computed(() => {
const prefixCls = mergedPrefixCls.value
const { header, sider } = mergedFixed.value
return normalizeClass({
[prefixCls]: true,
[`${prefixCls}-fixed-header`]: header,
[`${prefixCls}-fixed-sider`]: sider,
[`${prefixCls}-float-sider`]: props.floatSider,
})
})

return () => {
return <section class={mergedPrefixCls.value}>{slots.default?.()}</section>
return <section class={classes.value}>{slots.default?.()}</section>
}
},
})

0 comments on commit 1d47a0c

Please sign in to comment.