mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-04-12 14:40:47 +08:00
feat(collapse-transition): new component, closes #829
This commit is contained in:
parent
c31442bdf7
commit
94693bb64a
@ -17,6 +17,7 @@
|
||||
- `n-switch` add `unchecked-value` prop, closes [#1234](https://github.com/TuSimple/naive-ui/issues/1234).
|
||||
- `n-checkbox` add `checked-value` prop, closes [#1234](https://github.com/TuSimple/naive-ui/issues/1234).
|
||||
- `n-checkbox` add `unchecked-value` prop, closes [#1234](https://github.com/TuSimple/naive-ui/issues/1234).
|
||||
- Add `n-collapse-transition` component, closes [#829](https://github.com/TuSimple/naive-ui/issues/829).
|
||||
|
||||
### Fixes
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
- `n-switch` 新增 `unchecked-value` 属性,关闭 [#1234](https://github.com/TuSimple/naive-ui/issues/1234)
|
||||
- `n-checkbox` 新增 `checked-value` 属性,关闭 [#1234](https://github.com/TuSimple/naive-ui/issues/1234)
|
||||
- `n-checkbox` 新增 `unchecked-value` 属性,关闭 [#1234](https://github.com/TuSimple/naive-ui/issues/1234)
|
||||
- 新增 `n-collapse-transition` 组件,关闭 [#829](https://github.com/TuSimple/naive-ui/issues/829).
|
||||
|
||||
### Fixes
|
||||
|
||||
|
@ -462,6 +462,11 @@ export const enComponentRoutes = [
|
||||
{
|
||||
path: 'carousel',
|
||||
component: () => import('../../src/carousel/demos/enUS/index.demo-entry.md')
|
||||
},
|
||||
{
|
||||
path: 'collapse-transition',
|
||||
component: () =>
|
||||
import('../../src/collapse-transition/demos/enUS/index.demo-entry.md')
|
||||
}
|
||||
]
|
||||
|
||||
@ -801,6 +806,11 @@ export const zhComponentRoutes = [
|
||||
{
|
||||
path: 'carousel',
|
||||
component: () => import('../../src/carousel/demos/zhCN/index.demo-entry.md')
|
||||
},
|
||||
{
|
||||
path: 'collapse-transition',
|
||||
component: () =>
|
||||
import('../../src/collapse-transition/demos/zhCN/index.demo-entry.md')
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -654,6 +654,19 @@ export function createComponentMenuOptions ({ lang, theme, mode }) {
|
||||
}
|
||||
]
|
||||
}),
|
||||
appendCounts({
|
||||
zh: '工具组件',
|
||||
en: 'Utility Components',
|
||||
type: 'group',
|
||||
children: [
|
||||
{
|
||||
en: 'Collapse Transition',
|
||||
zh: '折叠动画',
|
||||
enSuffix: true,
|
||||
path: '/collapse-transition'
|
||||
}
|
||||
]
|
||||
}),
|
||||
appendCounts({
|
||||
zh: '配置组件',
|
||||
en: 'Config Components',
|
||||
|
27
src/collapse-transition/demos/enUS/basic.demo.md
Normal file
27
src/collapse-transition/demos/enUS/basic.demo.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Basic
|
||||
|
||||
```html
|
||||
<n-space vertical>
|
||||
<n-switch v-model:value="collapsed">
|
||||
<template #checked>Collapsed</template>
|
||||
<template #unchecked>Expanded</template>
|
||||
</n-switch>
|
||||
<n-collapse-transition :collapsed="collapsed">
|
||||
"There is no single development, in either technology or management
|
||||
technique, which by itself promises even one order of magnitude [tenfold]
|
||||
improvement within a decade in productivity, in reliability, in simplicity."
|
||||
</n-collapse-transition>
|
||||
</n-space>
|
||||
```
|
||||
|
||||
```js
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
return {
|
||||
collapsed: ref(true)
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
23
src/collapse-transition/demos/enUS/index.demo-entry.md
Normal file
23
src/collapse-transition/demos/enUS/index.demo-entry.md
Normal file
@ -0,0 +1,23 @@
|
||||
# Collapse Transition
|
||||
|
||||
A collapse item without any form of encapsulation.
|
||||
|
||||
## Demos
|
||||
|
||||
```demo
|
||||
basic
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### CollapseTransition Props
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| ------ | --------- | ------- | ------------------------------------------- |
|
||||
| appear | `boolean` | `false` | Whether to play animation on first mounted. |
|
||||
|
||||
### CollapseTransition Slots
|
||||
|
||||
| Name | Parameters | Description |
|
||||
| ------- | ---------- | ---------------------------------- |
|
||||
| default | `()` | The content inside the transition. |
|
25
src/collapse-transition/demos/zhCN/basic.demo.md
Normal file
25
src/collapse-transition/demos/zhCN/basic.demo.md
Normal file
@ -0,0 +1,25 @@
|
||||
# 基本用法
|
||||
|
||||
```html
|
||||
<n-space vertical>
|
||||
<n-switch v-model:value="collapsed">
|
||||
<template #checked>展开</template>
|
||||
<template #unchecked>折叠</template>
|
||||
</n-switch>
|
||||
<n-collapse-transition :collapsed="collapsed">
|
||||
感知度,方法论,组合拳,引爆点,点线面,精细化,差异化,平台化,结构化,影响力,耦合性,便捷性,一致性,端到端,短平快,护城河,体验感,颗粒度
|
||||
</n-collapse-transition>
|
||||
</n-space>
|
||||
```
|
||||
|
||||
```js
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
return {
|
||||
collapsed: ref(true)
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
23
src/collapse-transition/demos/zhCN/index.demo-entry.md
Normal file
23
src/collapse-transition/demos/zhCN/index.demo-entry.md
Normal file
@ -0,0 +1,23 @@
|
||||
# 折叠渐变 Collapse Transition
|
||||
|
||||
一个没什么封装的 collapse item。
|
||||
|
||||
## 演示
|
||||
|
||||
```demo
|
||||
basic
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### CollapseTransition Props
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| ------ | --------- | ------- | ------------------------ |
|
||||
| appear | `boolean` | `false` | 是否在首次出现时播放动画 |
|
||||
|
||||
### CollapseTransition Slots
|
||||
|
||||
| 名称 | 参数 | 说明 |
|
||||
| ------- | ---- | ---------- |
|
||||
| default | `()` | 渐变的内容 |
|
1
src/collapse-transition/index.ts
Normal file
1
src/collapse-transition/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as NCollapseTransition } from './src/CollapseTransition'
|
65
src/collapse-transition/src/CollapseTransition.tsx
Normal file
65
src/collapse-transition/src/CollapseTransition.tsx
Normal file
@ -0,0 +1,65 @@
|
||||
import { computed, h, defineComponent, mergeProps } from 'vue'
|
||||
import { useConfig, useTheme } from '../../_mixins'
|
||||
import type { ThemeProps } from '../../_mixins'
|
||||
import { ExtractPublicPropTypes } from '../../_utils'
|
||||
import type { CollapseTransitionTheme } from '../styles'
|
||||
import style from './styles/index.cssr'
|
||||
import { collapseTransitionLight } from '../styles'
|
||||
import { NFadeInExpandTransition } from '../../_internal'
|
||||
|
||||
const collapseProps = {
|
||||
...(useTheme.props as ThemeProps<CollapseTransitionTheme>),
|
||||
collapsed: Boolean,
|
||||
appear: Boolean
|
||||
} as const
|
||||
|
||||
export type CollapseProps = ExtractPublicPropTypes<typeof collapseProps>
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CollapseTransition',
|
||||
props: collapseProps,
|
||||
inheritAttrs: false,
|
||||
setup (props) {
|
||||
const { mergedClsPrefixRef } = useConfig(props)
|
||||
const mergedThemeRef = useTheme(
|
||||
'CollapseTransition',
|
||||
'CollapseTransition',
|
||||
style,
|
||||
collapseTransitionLight,
|
||||
props
|
||||
)
|
||||
return {
|
||||
mergedClsPrefix: mergedClsPrefixRef,
|
||||
cssVars: computed(() => {
|
||||
const {
|
||||
self: { bezier }
|
||||
} = mergedThemeRef.value
|
||||
return {
|
||||
'--bezier': bezier
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
render () {
|
||||
return (
|
||||
<NFadeInExpandTransition appear={this.appear}>
|
||||
{{
|
||||
default: () =>
|
||||
this.collapsed
|
||||
? h(
|
||||
'div', // Don't use jsx since it would cause useless spread in each rendering
|
||||
mergeProps(
|
||||
{
|
||||
class: `${this.mergedClsPrefix}-collapse-transition`,
|
||||
style: this.cssVars
|
||||
},
|
||||
this.$attrs
|
||||
),
|
||||
this.$slots
|
||||
)
|
||||
: null
|
||||
}}
|
||||
</NFadeInExpandTransition>
|
||||
)
|
||||
}
|
||||
})
|
8
src/collapse-transition/src/styles/index.cssr.ts
Normal file
8
src/collapse-transition/src/styles/index.cssr.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { cB } from '../../../_utils/cssr'
|
||||
import fadeInHeightExpandTransition from '../../../_styles/transitions/fade-in-height-expand.cssr'
|
||||
|
||||
export default cB('collapse-transition', {
|
||||
width: '100%'
|
||||
}, [
|
||||
fadeInHeightExpandTransition()
|
||||
])
|
11
src/collapse-transition/styles/dark.ts
Normal file
11
src/collapse-transition/styles/dark.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { commonDark } from '../../_styles/common'
|
||||
import type { CollapseTransitionTheme } from './light'
|
||||
import { self } from './light'
|
||||
|
||||
const collapseTransitionDark: CollapseTransitionTheme = {
|
||||
name: 'CollapseTransition',
|
||||
common: commonDark,
|
||||
self
|
||||
}
|
||||
|
||||
export default collapseTransitionDark
|
6
src/collapse-transition/styles/index.ts
Normal file
6
src/collapse-transition/styles/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export { default as collapseTransitionDark } from './dark'
|
||||
export { default as collapseTransitionLight } from './light'
|
||||
export type {
|
||||
CollapseTransitionTheme,
|
||||
CollapseTransitionThemeVars
|
||||
} from './light'
|
24
src/collapse-transition/styles/light.ts
Normal file
24
src/collapse-transition/styles/light.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { commonLight } from '../../_styles/common'
|
||||
import type { ThemeCommonVars } from '../../_styles/common'
|
||||
import { Theme } from '../../_mixins/use-theme'
|
||||
|
||||
export const self = (vars: ThemeCommonVars) => {
|
||||
const { cubicBezierEaseInOut } = vars
|
||||
return {
|
||||
bezier: cubicBezierEaseInOut
|
||||
}
|
||||
}
|
||||
|
||||
export type CollapseTransitionThemeVars = ReturnType<typeof self>
|
||||
|
||||
const collapseTransitionLight: Theme<
|
||||
'CollapseTransition',
|
||||
CollapseTransitionThemeVars
|
||||
> = {
|
||||
name: 'CollapseTransition',
|
||||
common: commonLight,
|
||||
self
|
||||
}
|
||||
|
||||
export default collapseTransitionLight
|
||||
export type CollapseTransitionTheme = typeof collapseTransitionLight
|
152
src/collapse-transition/tests/CollapseTransition.spec.tsx
Normal file
152
src/collapse-transition/tests/CollapseTransition.spec.tsx
Normal file
@ -0,0 +1,152 @@
|
||||
import { h } from 'vue'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { NCollapse, NCollapseItem } from '../index'
|
||||
|
||||
describe('n-collapse', () => {
|
||||
it('should work with import on demand', () => {
|
||||
mount(NCollapse)
|
||||
})
|
||||
|
||||
it('can customize icon', () => {
|
||||
const wrapper = mount(() => {
|
||||
return (
|
||||
<NCollapse>
|
||||
{{
|
||||
arrow: () => <div class="my-icon"></div>,
|
||||
default: () => <NCollapseItem name="1"></NCollapseItem>
|
||||
}}
|
||||
</NCollapse>
|
||||
)
|
||||
})
|
||||
expect(wrapper.find('.my-icon').exists()).toEqual(true)
|
||||
})
|
||||
|
||||
it('should work with `arrow-placement` prop', async () => {
|
||||
const wrapper = mount(NCollapse, {
|
||||
slots: {
|
||||
default: () => <NCollapseItem name="1"></NCollapseItem>
|
||||
}
|
||||
})
|
||||
expect(wrapper.find('.n-collapse-item').classes()).toContain(
|
||||
'n-collapse-item--left-arrow-placement'
|
||||
)
|
||||
|
||||
await wrapper.setProps({ arrowPlacement: 'right' })
|
||||
expect(wrapper.find('.n-collapse-item').classes()).toContain(
|
||||
'n-collapse-item--right-arrow-placement'
|
||||
)
|
||||
})
|
||||
|
||||
it('should work with nested structure', async () => {
|
||||
mount(NCollapse, {
|
||||
slots: {
|
||||
default: () =>
|
||||
h(
|
||||
NCollapseItem,
|
||||
{ name: '1', title: 'test1' },
|
||||
{
|
||||
default: () =>
|
||||
h(NCollapse, null, {
|
||||
default: () => h(NCollapseItem, { name: '2', title: 'test2' })
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
// todo: test display-directive
|
||||
// I wanted to test this function, but I was bothered by the <transition-stub>
|
||||
})
|
||||
|
||||
it('should work with `display-directive` prop', async () => {
|
||||
mount(NCollapse, {
|
||||
props: {
|
||||
displayDirective: 'show'
|
||||
},
|
||||
slots: {
|
||||
default: () =>
|
||||
h(
|
||||
NCollapseItem,
|
||||
{ name: '1', title: 'test' },
|
||||
{ default: () => h('div', null, { default: () => 'test' }) }
|
||||
)
|
||||
}
|
||||
// todo: test display-directive
|
||||
// I wanted to test this function, but I was bothered by the <transition-stub>
|
||||
})
|
||||
})
|
||||
|
||||
it('should work with `on-item-header-click` prop', async () => {
|
||||
const onClick = jest.fn()
|
||||
const wrapper = mount(NCollapse, {
|
||||
props: {
|
||||
onItemHeaderClick: onClick
|
||||
},
|
||||
slots: {
|
||||
default: () => <NCollapseItem name="1"></NCollapseItem>
|
||||
}
|
||||
})
|
||||
const triggerNodeWrapper = wrapper.find('.n-collapse-item__header-main')
|
||||
await triggerNodeWrapper.trigger('click')
|
||||
expect(onClick).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should work with `slots` ', async () => {
|
||||
const wrapper = mount(NCollapse, {
|
||||
slots: {
|
||||
header: () => 'header',
|
||||
'header-extra': () => 'header-extra',
|
||||
default: () => <NCollapseItem name="1"></NCollapseItem>,
|
||||
arrow: () => 'arrow'
|
||||
}
|
||||
})
|
||||
expect(wrapper.find('.n-collapse-item__header-main').exists()).toBe(true)
|
||||
expect(wrapper.find('.n-collapse-item__header-main').text()).toBe('arrow')
|
||||
|
||||
expect(wrapper.find('.n-collapse-item__header-extra').exists()).toBe(true)
|
||||
expect(wrapper.find('.n-collapse-item__header-extra').text()).toBe(
|
||||
'header-extra'
|
||||
)
|
||||
|
||||
expect(wrapper.find('.n-collapse-item-arrow').exists()).toBe(true)
|
||||
expect(wrapper.find('.n-collapse-item-arrow').text()).toBe('arrow')
|
||||
})
|
||||
|
||||
it('props.defaultExpandedNames', async () => {
|
||||
let wrapper = mount(NCollapse, {
|
||||
props: {
|
||||
defaultExpandedNames: ['1']
|
||||
},
|
||||
slots: {
|
||||
default: () => [
|
||||
<NCollapseItem name="1">
|
||||
{{ default: () => <div class="ci1"></div> }}
|
||||
</NCollapseItem>,
|
||||
<NCollapseItem name="2">
|
||||
{{ default: () => <div class="ci2"></div> }}
|
||||
</NCollapseItem>
|
||||
]
|
||||
}
|
||||
})
|
||||
expect(wrapper.find('.ci1').isVisible()).toEqual(true)
|
||||
expect(wrapper.find('.ci2').exists()).toEqual(false)
|
||||
wrapper = mount(NCollapse, {
|
||||
props: {
|
||||
accordion: true,
|
||||
defaultExpandedNames: '1'
|
||||
},
|
||||
slots: {
|
||||
default: () => [
|
||||
<NCollapseItem name="1">
|
||||
{{ default: () => <div class="ci1"></div> }}
|
||||
</NCollapseItem>,
|
||||
<NCollapseItem name="2">
|
||||
{{ default: () => <div class="ci2"></div> }}
|
||||
</NCollapseItem>
|
||||
]
|
||||
}
|
||||
})
|
||||
expect(wrapper.find('.ci1').isVisible()).toEqual(true)
|
||||
expect(wrapper.find('.ci2').exists()).toEqual(false)
|
||||
})
|
||||
})
|
19
src/collapse-transition/tests/server.spec.tsx
Normal file
19
src/collapse-transition/tests/server.spec.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @jest-environment node
|
||||
*/
|
||||
import { h, createSSRApp } from 'vue'
|
||||
import { renderToString } from '@vue/server-renderer'
|
||||
import { setup } from '@css-render/vue3-ssr'
|
||||
import { NCollapse } from '../..'
|
||||
|
||||
describe('SSR', () => {
|
||||
it('works', async () => {
|
||||
const app = createSSRApp(() => <NCollapse />)
|
||||
setup(app)
|
||||
try {
|
||||
await renderToString(app)
|
||||
} catch (e) {
|
||||
expect(e).not.toBeTruthy()
|
||||
}
|
||||
})
|
||||
})
|
@ -15,6 +15,7 @@ export * from './cascader'
|
||||
export * from './checkbox'
|
||||
export * from './code'
|
||||
export * from './collapse'
|
||||
export * from './collapse-transition'
|
||||
export * from './config-provider'
|
||||
export * from './data-table'
|
||||
export * from './date-picker'
|
||||
|
@ -85,6 +85,7 @@ import { DataTableRenderFilter, DataTableRenderSorter } from '../../data-table'
|
||||
import { IconPlacement } from '../../dialog/src/interface'
|
||||
import type { GlobalTheme, GlobalThemeOverrides } from './interface'
|
||||
import type { EmptyProps } from '../../empty'
|
||||
import { CollapseTransitionTheme } from '../../collapse-transition/styles'
|
||||
|
||||
export interface GlobalThemeWithoutCommon {
|
||||
Alert?: AlertTheme
|
||||
@ -102,6 +103,7 @@ export interface GlobalThemeWithoutCommon {
|
||||
Checkbox?: CheckboxTheme
|
||||
Code?: CodeTheme
|
||||
Collapse?: CollapseTheme
|
||||
CollapseTransition?: CollapseTransitionTheme
|
||||
ColorPicker?: ColorPickerTheme
|
||||
DataTable?: DataTableTheme
|
||||
DatePicker?: DatePickerTheme
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 自定义字段
|
||||
# Customizing Field
|
||||
|
||||
后端会传来各种各样的数据,你可以自定义 `key`、`label` 和 `children` 的字段。
|
||||
Various data would come from backend, you can customize `key`, `label` and `children`'s field name.
|
||||
|
||||
```html
|
||||
<n-layout has-sider>
|
||||
@ -43,73 +43,73 @@ function renderIcon (icon) {
|
||||
|
||||
const menuOptions = [
|
||||
{
|
||||
whateverLabel: '且听风吟',
|
||||
whateverLabel: 'Hear the Wind Sing',
|
||||
whateverKey: 'hear-the-wind-sing',
|
||||
icon: renderIcon(BookIcon)
|
||||
},
|
||||
{
|
||||
whateverLabel: '1973年的弹珠玩具',
|
||||
whateverLabel: 'Pinball 1973',
|
||||
whateverKey: 'pinball-1973',
|
||||
icon: renderIcon(BookIcon),
|
||||
disabled: true,
|
||||
whateverChildren: [
|
||||
{
|
||||
whateverLabel: '鼠',
|
||||
whateverLabel: 'Rat',
|
||||
whateverKey: 'rat'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
whateverLabel: '寻羊冒险记',
|
||||
whateverLabel: 'A Wild Sheep Chase',
|
||||
whateverKey: 'a-wild-sheep-chase',
|
||||
disabled: true,
|
||||
icon: renderIcon(BookIcon)
|
||||
},
|
||||
{
|
||||
whateverLabel: '舞,舞,舞',
|
||||
whateverKey: 'dance-dance-dance',
|
||||
whateverLabel: 'Dance Dance Dance',
|
||||
whateverKey: 'Dance Dance Dance',
|
||||
icon: renderIcon(BookIcon),
|
||||
whateverChildren: [
|
||||
{
|
||||
type: 'group',
|
||||
whateverLabel: '人物',
|
||||
whateverLabel: 'People',
|
||||
whateverKey: 'people',
|
||||
whateverChildren: [
|
||||
{
|
||||
whateverLabel: '叙事者',
|
||||
whateverLabel: 'Narrator',
|
||||
whateverKey: 'narrator',
|
||||
icon: renderIcon(PersonIcon)
|
||||
},
|
||||
{
|
||||
whateverLabel: '羊男',
|
||||
whateverLabel: 'Sheep Man',
|
||||
whateverKey: 'sheep-man',
|
||||
icon: renderIcon(PersonIcon)
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
whateverLabel: '饮品',
|
||||
whateverLabel: 'Beverage',
|
||||
whateverKey: 'beverage',
|
||||
icon: renderIcon(WineIcon),
|
||||
whateverChildren: [
|
||||
{
|
||||
whateverLabel: '威士忌',
|
||||
whateverLabel: 'Whisky',
|
||||
whateverKey: 'whisky'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
whateverLabel: '食物',
|
||||
whateverLabel: 'Food',
|
||||
whateverKey: 'food',
|
||||
whateverChildren: [
|
||||
{
|
||||
whateverLabel: '三明治',
|
||||
whateverLabel: 'Sandwich',
|
||||
whateverKey: 'sandwich'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
whateverLabel: '过去增多,未来减少',
|
||||
whateverLabel: 'The past increases. The future recedes.',
|
||||
whateverKey: 'the-past-increases-the-future-recedes'
|
||||
}
|
||||
]
|
||||
|
@ -16,6 +16,7 @@ collapse
|
||||
inverted
|
||||
long-label
|
||||
accordion
|
||||
customize-field
|
||||
```
|
||||
|
||||
## API
|
||||
|
@ -68,6 +68,7 @@ export default cB('switch', `
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 1;
|
||||
`),
|
||||
cE('checked', `
|
||||
right: 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user