✨(component): [space]新增 space 组件
This commit is contained in:
parent
c235cbb1f1
commit
cf0b74820e
26
package/component/src/component/space/Renderer.ts
Normal file
26
package/component/src/component/space/Renderer.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
|
import type { PropType } from "vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "Renderer",
|
||||||
|
props: {
|
||||||
|
renderFn: {
|
||||||
|
type: Function,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: Object as PropType<Record<string, any>>,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
return () => {
|
||||||
|
if (typeof props.renderFn !== "function") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return props.renderFn(props.data);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
42
package/component/src/component/space/index.less
Normal file
42
package/component/src/component/space/index.less
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
.layui-space {
|
||||||
|
display: inline-flex;
|
||||||
|
|
||||||
|
&-horizontal {
|
||||||
|
.layui-space-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-vertical {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-wrap {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-fill {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-align-start {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-align-center {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-align-end {
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-align-baseline {
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
width: inherit;
|
||||||
|
}
|
||||||
|
}
|
5
package/component/src/component/space/index.ts
Normal file
5
package/component/src/component/space/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { withInstall, WithInstallType } from "../../utils";
|
||||||
|
import Component from "./index.vue";
|
||||||
|
|
||||||
|
const component: WithInstallType<typeof Component> = withInstall(Component);
|
||||||
|
export default component;
|
132
package/component/src/component/space/index.vue
Normal file
132
package/component/src/component/space/index.vue
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LaySpace",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import "./index.less";
|
||||||
|
import {
|
||||||
|
computed,
|
||||||
|
h,
|
||||||
|
useSlots,
|
||||||
|
Comment,
|
||||||
|
VNode,
|
||||||
|
Fragment,
|
||||||
|
isVNode,
|
||||||
|
createTextVNode,
|
||||||
|
VNodeArrayChildren,
|
||||||
|
StyleValue,
|
||||||
|
} from "vue";
|
||||||
|
import Renderer from "./Renderer";
|
||||||
|
|
||||||
|
export type SpaceSize = "lg" | "md" | "sm" | "xs" | number | string;
|
||||||
|
|
||||||
|
export interface LaySpaceProps {
|
||||||
|
/* 对齐方式 */
|
||||||
|
align?: "start" | "end" | "center" | "baseline";
|
||||||
|
/* 间距方向 */
|
||||||
|
direction?: "horizontal" | "vertical";
|
||||||
|
/* 子元素是否填充父容器 */
|
||||||
|
fill?: boolean;
|
||||||
|
/* 间距大小 */
|
||||||
|
size?: SpaceSize | [SpaceSize, SpaceSize];
|
||||||
|
/* 是否自动折行 */
|
||||||
|
wrap?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<LaySpaceProps>(), {
|
||||||
|
align: "center",
|
||||||
|
direction: "horizontal",
|
||||||
|
size: "sm",
|
||||||
|
});
|
||||||
|
|
||||||
|
const slots = useSlots();
|
||||||
|
|
||||||
|
const extractChildren = () => {
|
||||||
|
const result: VNode[] = [];
|
||||||
|
const children = slots.default && (slots?.default() as VNodeArrayChildren);
|
||||||
|
const elementData = Array.isArray(children) ? [...children] : [];
|
||||||
|
|
||||||
|
while (elementData.length) {
|
||||||
|
const vnode = elementData.shift();
|
||||||
|
|
||||||
|
if (vnode === null) continue;
|
||||||
|
|
||||||
|
if (Array.isArray(vnode)) {
|
||||||
|
elementData.unshift(...vnode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isVNode(vnode) || vnode.type === Comment) continue;
|
||||||
|
|
||||||
|
if (vnode.type === Fragment && Array.isArray(vnode.children)) {
|
||||||
|
elementData.unshift(vnode.children);
|
||||||
|
} else if (typeof vnode === "string" || typeof vnode === "number") {
|
||||||
|
result.push(createTextVNode(vnode));
|
||||||
|
} else {
|
||||||
|
result.push(vnode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const spaceClass = computed(() => [
|
||||||
|
"layui-space",
|
||||||
|
{
|
||||||
|
[`layui-space-align-${props.align}`]: props.align,
|
||||||
|
[`layui-space-${props.direction}`]: props.direction,
|
||||||
|
[`layui-space-wrap`]: props.wrap,
|
||||||
|
[`layui-space-fill`]: props.fill,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const spaceStyle = computed(() => {
|
||||||
|
const sizeMap = { xs: "4px", sm: "8px", md: "16px", lg: "24px" };
|
||||||
|
let gap = "";
|
||||||
|
|
||||||
|
if (Array.isArray(props.size)) {
|
||||||
|
gap = props.size
|
||||||
|
.map((size) => {
|
||||||
|
if (typeof size === "number") {
|
||||||
|
return `${size}px`;
|
||||||
|
}
|
||||||
|
if (typeof size === "string") {
|
||||||
|
return sizeMap[props.size as keyof Omit<SpaceSize, number>] || size;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
})
|
||||||
|
.join(" ");
|
||||||
|
} else if (typeof props.size === "string") {
|
||||||
|
gap = sizeMap[props.size as keyof Omit<SpaceSize, string>] || props.size;
|
||||||
|
} else if (typeof props.size === "number") {
|
||||||
|
gap = `${props.size}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
gap,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const itemStyle = computed<StyleValue>(() => [
|
||||||
|
props.fill ? { flexGrow: 1, minWidth: "100%" } : {},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const children = extractChildren();
|
||||||
|
|
||||||
|
const renderSpaceItems = () =>
|
||||||
|
children.map((child, index) => {
|
||||||
|
return h(
|
||||||
|
"div",
|
||||||
|
{
|
||||||
|
class: "layui-space-item",
|
||||||
|
style: itemStyle.value,
|
||||||
|
},
|
||||||
|
h(child)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div :class="spaceClass" :style="spaceStyle">
|
||||||
|
<Renderer :renderFn="renderSpaceItems"></Renderer>
|
||||||
|
</div>
|
||||||
|
</template>
|
@ -88,6 +88,7 @@ import LayNoticeBar from "./component/noticeBar/index";
|
|||||||
import LayPageHeader from "./component/pageHeader/index";
|
import LayPageHeader from "./component/pageHeader/index";
|
||||||
import LayCascader from "./component/cascader/index";
|
import LayCascader from "./component/cascader/index";
|
||||||
import LayAffix from "./component/affix/index";
|
import LayAffix from "./component/affix/index";
|
||||||
|
import LaySpace from "./component/space/index";
|
||||||
import LayConfigProvider from "./provider";
|
import LayConfigProvider from "./provider";
|
||||||
import { InstallOptions } from "./types";
|
import { InstallOptions } from "./types";
|
||||||
|
|
||||||
@ -174,6 +175,7 @@ const components: Record<string, Plugin> = {
|
|||||||
LayPageHeader,
|
LayPageHeader,
|
||||||
LayCascader,
|
LayCascader,
|
||||||
LayAffix,
|
LayAffix,
|
||||||
|
LaySpace,
|
||||||
};
|
};
|
||||||
|
|
||||||
const install = (app: App, options?: InstallOptions): void => {
|
const install = (app: App, options?: InstallOptions): void => {
|
||||||
@ -267,6 +269,7 @@ export {
|
|||||||
LayPageHeader,
|
LayPageHeader,
|
||||||
LayCascader,
|
LayCascader,
|
||||||
LayAffix,
|
LayAffix,
|
||||||
|
LaySpace,
|
||||||
install,
|
install,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,245 @@
|
|||||||
|
::: anchor
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: title 基本介绍
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: describe 控制组件之间的间距。
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: title 基础使用
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: demo 默认横向排列,控制相邻组件的水平间距
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<lay-space>
|
||||||
|
<lay-button v-for="idx of 5" type="normal">按钮 {{idx}}</lay-button>
|
||||||
|
</lay-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
::: title 垂直方向
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: demo 通过 `direction="vertical"` 设置垂直方向
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<lay-space direction="vertical" fill wrap>
|
||||||
|
<lay-button v-for="idx of 5" type="normal" fluid="true">按钮 {{idx}}</lay-button>
|
||||||
|
</lay-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
::: title 间距大小
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: demo 通过 `size` 控制组件大小 `xs, sm, md , lg`, 分别对应 `4px, 8px, 16px, 24px` 的间距,默认`md`。`size` 也支持通过数组设置间距 `[row-gap, column-gap]`
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<lay-radio
|
||||||
|
v-for="sizeKey of sizeKeys"
|
||||||
|
v-model="sizeSelected"
|
||||||
|
name="action"
|
||||||
|
:value="sizeKey"
|
||||||
|
@change="changeSize">
|
||||||
|
{{sizeKey}}
|
||||||
|
</lay-radio>
|
||||||
|
<br><br>
|
||||||
|
<lay-space :size="spaceSize">
|
||||||
|
<lay-button v-for="idx of 5" type="normal">按钮 {{idx}}</lay-button>
|
||||||
|
</lay-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const sizeKeys = ["xs","sm","md","lg"];
|
||||||
|
const spaceSize = ref();
|
||||||
|
|
||||||
|
const sizeSelected = ref("sm");
|
||||||
|
const changeSize = function( key ) {
|
||||||
|
spaceSize.value = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sizeSelected,
|
||||||
|
changeSize,
|
||||||
|
spaceSize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
::: title 自定义间距大小
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: demo
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<lay-slider v-model="customSize"></lay-slider>
|
||||||
|
<br><br>
|
||||||
|
<lay-space :size="customSize">
|
||||||
|
<lay-button v-for="idx of 5" type="normal">按钮 {{idx}}</lay-button>
|
||||||
|
</lay-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const customSize = ref(8);
|
||||||
|
|
||||||
|
return {
|
||||||
|
customSize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
::: title 对齐方式
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: demo
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<lay-radio
|
||||||
|
v-for="alignKey of alignKeys"
|
||||||
|
v-model="alignSelected"
|
||||||
|
name="action"
|
||||||
|
:value="alignKey"
|
||||||
|
@change="changeAlign">
|
||||||
|
{{alignKey}}
|
||||||
|
</lay-radio>
|
||||||
|
<br><br>
|
||||||
|
<lay-space :align="spaceAlign" style="backgroundColor: whitesmoke;padding: 10px;">
|
||||||
|
<span>Space:</span>
|
||||||
|
<lay-button>默认按钮</lay-button>
|
||||||
|
<lay-card title="标题">
|
||||||
|
内容
|
||||||
|
</lay-card>
|
||||||
|
</lay-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const alignKeys = ["start", "center","end","baseline"];
|
||||||
|
const spaceAlign = ref();
|
||||||
|
|
||||||
|
const alignSelected = ref("center");
|
||||||
|
const changeAlign = function( key ) {
|
||||||
|
spaceAlign.value = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
alignSelected,
|
||||||
|
changeAlign,
|
||||||
|
spaceAlign,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
::: title 自动换行
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: demo
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<lay-space wrap :size="[16,24]">
|
||||||
|
<lay-button v-for="_ of 20">默认按钮{{_}}</lay-button>
|
||||||
|
</lay-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
::: title Space 属性
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: table
|
||||||
|
|
||||||
|
| 属性 | 描述 | 类型 | 默认值 | 可选值 |
|
||||||
|
| ----------- | -------- | ------ | ------ | ------ |
|
||||||
|
| align | 对齐方式 | string| `center`| `start` `end` `center` `baseline`|
|
||||||
|
| direction | 间距方向 | string | `horizontal` | `horizontal` `vertical`|
|
||||||
|
| fill | 子元素是否填充父容器 | boolean| `false`| -|
|
||||||
|
| size | 间距大小 | string | md | `lg` `md` `sm` `xs` `number` `string` `[spaceSize,spaceSize]`|
|
||||||
|
| wrap | 是否自动折行 | boolean| `false`| -|
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
>`type spaceSize = "lg" | "md" | "sm" | "xs" | number | string`
|
||||||
|
|
||||||
|
> `[row-gap, column-gap], eg: ['xs','md'] ['10px', '15px'] [10, 15]`
|
||||||
|
|
||||||
|
:::title Space 插槽
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::table
|
||||||
|
| 插槽 | 描述 | 参数 |
|
||||||
|
|------ |----------|-----------|
|
||||||
|
| default | 默认插槽 | - |
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
::: contributor space
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: previousNext space
|
||||||
|
:::
|
@ -429,6 +429,11 @@ const zhCN = [
|
|||||||
component: () => import("../document/zh-CN/components/tagInput.md"),
|
component: () => import("../document/zh-CN/components/tagInput.md"),
|
||||||
meta: { title: "标签" },
|
meta: { title: "标签" },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/zh-CN/components/space",
|
||||||
|
component: () => import("../document/zh-CN/components/space.md"),
|
||||||
|
meta: { title: "间距" },
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -88,6 +88,12 @@ const menus = [
|
|||||||
subTitle: "skeleton",
|
subTitle: "skeleton",
|
||||||
path: "/zh-CN/components/skeleton",
|
path: "/zh-CN/components/skeleton",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 12,
|
||||||
|
title: "间距",
|
||||||
|
subTitle: "space",
|
||||||
|
path: "/zh-CN/components/space",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user