-
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add dynamic list input (#6146)
#### What type of PR is this? /kind feature /area ui /milestone 2.17.x #### What this PR does / why we need it: 为 formkit 增加动态列表的 input。 使用方式: ``` - $formkit: list name: users label: Users addLabel: Add User min: 1 max: 3 itemType: string children: - $formkit: text index: "$index" validation: required ``` > [!NOTE] > `list` 组件有且只有一个子节点,并且必须为子节点传递 `index` 属性。若想提供多个字段,则建议使用 `group` 组件包裹。 #### How to test it? 测试动态数组是否正常可用。保存的结果是否为 `{users: ["", ""]}` #### Which issue(s) this PR fixes: Fixes #6098 #### Does this PR introduce a user-facing change? ```release-note 为 Formkit 增加动态列表的 input 组件 list ```
- Loading branch information
Showing
8 changed files
with
560 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<script lang="ts" setup> | ||
import { VButton, IconAddCircle } from "@halo-dev/components"; | ||
import type { FormKitFrameworkContext } from "@formkit/core"; | ||
import type { PropType } from "vue"; | ||
const props = defineProps({ | ||
context: { | ||
type: Object as PropType<FormKitFrameworkContext>, | ||
required: true, | ||
}, | ||
disabled: { | ||
type: Boolean, | ||
required: false, | ||
}, | ||
onClick: { | ||
type: Function as PropType<() => void>, | ||
required: true, | ||
}, | ||
}); | ||
const handleAppendClick = () => { | ||
if (!props.disabled && props.onClick) { | ||
props.onClick(); | ||
} | ||
}; | ||
</script> | ||
|
||
<template> | ||
<div :class="context.classes.add" @click="handleAppendClick"> | ||
<VButton :disabled="disabled" type="secondary"> | ||
<template #icon> | ||
<IconAddCircle class="h-full w-full" /> | ||
</template> | ||
{{ context.addLabel || $t("core.common.buttons.add") }} | ||
</VButton> | ||
</div> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import type { FormKitNode } from "@formkit/core"; | ||
import { undefine } from "@formkit/utils"; | ||
|
||
export const lists = function (node: FormKitNode) { | ||
node._c.sync = true; | ||
node.on("created", listFeature.bind(null, node)); | ||
}; | ||
|
||
const fn = (node: FormKitNode): object | string | boolean | number => { | ||
switch (node.props.itemType.toLocaleLowerCase()) { | ||
case "object": | ||
return {}; | ||
case "boolean": | ||
return false; | ||
case "number": | ||
return 0; | ||
default: | ||
return ""; | ||
} | ||
}; | ||
|
||
function createValue(num: number, node: FormKitNode) { | ||
return new Array(num).fill("").map(() => fn(node)); | ||
} | ||
|
||
function listFeature(node: FormKitNode) { | ||
node.props.removeControl = node.props.removeControl ?? true; | ||
node.props.upControl = node.props.upControl ?? true; | ||
node.props.downControl = node.props.downControl ?? true; | ||
node.props.insertControl = node.props.insertControl ?? true; | ||
node.props.addButton = node.props.addButton ?? true; | ||
node.props.addLabel = node.props.addLabel ?? false; | ||
node.props.addAttrs = node.props.addAttrs ?? {}; | ||
node.props.min = node.props.min ? Number(node.props.min) : 0; | ||
node.props.max = node.props.max ? Number(node.props.max) : Infinity; | ||
node.props.itemType = node.props.itemType ?? "string"; | ||
if (node.props.min > node.props.max) { | ||
throw Error("list: min must be less than max"); | ||
} | ||
|
||
if ("disabled" in node.props) { | ||
node.props.disabled = undefine(node.props.disabled); | ||
} | ||
|
||
if (Array.isArray(node.value)) { | ||
if (node.value.length < node.props.min) { | ||
const value = createValue(node.props.min - node.value.length, node); | ||
node.input(node.value.concat(value), false); | ||
} else { | ||
if (node.value.length > node.props.max) { | ||
node.input(node.value.slice(0, node.props.max), false); | ||
} | ||
} | ||
} else { | ||
node.input(createValue(node.props.min, node), false); | ||
} | ||
|
||
if (node.context) { | ||
const fns = node.context.fns; | ||
fns.createShift = (index: number, offset: number) => () => { | ||
const value = node._value as unknown[]; | ||
value.splice(index + offset, 0, value.splice(index, 1)[0]), | ||
node.input(value, false); | ||
}; | ||
fns.createInsert = (index: number) => () => { | ||
const value = node._value as unknown[]; | ||
value.splice(index + 1, 0, fn(node)), node.input(value, false); | ||
}; | ||
fns.createAppend = () => () => { | ||
const value = node._value as unknown[]; | ||
console.log(fn(node)); | ||
value.push(fn(node)), node.input(value, false); | ||
}; | ||
fns.createRemover = (index: number) => () => { | ||
const value = node._value as unknown[]; | ||
value.splice(index, 1), node.input(value, false); | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import type { FormKitTypeDefinition } from "@formkit/core"; | ||
|
||
import { | ||
disablesChildren, | ||
renamesRadios, | ||
fieldset, | ||
messages, | ||
message, | ||
outer, | ||
legend, | ||
help, | ||
inner, | ||
prefix, | ||
$if, | ||
suffix, | ||
} from "@formkit/inputs"; | ||
import { | ||
addButton, | ||
content, | ||
controls, | ||
down, | ||
downControl, | ||
downIcon, | ||
empty, | ||
insert, | ||
insertControl, | ||
insertIcon, | ||
item, | ||
items, | ||
remove, | ||
removeControl, | ||
removeIcon, | ||
up, | ||
upControl, | ||
upIcon, | ||
} from "./sections"; | ||
import { i18n } from "@/locales"; | ||
import { | ||
IconAddCircle, | ||
IconArrowDownCircleLine, | ||
IconArrowUpCircleLine, | ||
IconCloseCircle, | ||
} from "@halo-dev/components"; | ||
import AddButton from "./AddButton.vue"; | ||
import { lists } from "./features/lists"; | ||
|
||
/** | ||
* Input definition for a dynamic list input. | ||
* @public | ||
*/ | ||
export const list: FormKitTypeDefinition = { | ||
/** | ||
* The actual schema of the input, or a function that returns the schema. | ||
*/ | ||
schema: outer( | ||
fieldset( | ||
legend("$label"), | ||
help("$help"), | ||
inner( | ||
prefix(), | ||
$if( | ||
"$value.length === 0", | ||
$if("$slots.empty", empty()), | ||
$if( | ||
"$slots.default", | ||
items( | ||
item( | ||
content("$slots.default"), | ||
controls( | ||
up(upControl(upIcon())), | ||
remove(removeControl(removeIcon())), | ||
insert(insertControl(insertIcon())), | ||
down(downControl(downIcon())) | ||
) | ||
) | ||
), | ||
suffix() | ||
) | ||
), | ||
suffix(), | ||
addButton(`$addLabel || (${i18n.global.t("core.common.buttons.add")})`) | ||
) | ||
), | ||
messages(message("$message.value")) | ||
), | ||
/** | ||
* The type of node, can be a list, group, or input. | ||
*/ | ||
type: "list", | ||
/** | ||
* An array of extra props to accept for this input. | ||
*/ | ||
props: [ | ||
"min", | ||
"max", | ||
"upControl", | ||
"downControl", | ||
"removeControl", | ||
"insertControl", | ||
"addLabel", | ||
"addButton", | ||
"itemType", | ||
], | ||
/** | ||
* Additional features that should be added to your input | ||
*/ | ||
features: [lists, disablesChildren, renamesRadios], | ||
|
||
library: { | ||
IconAddCircle, | ||
IconCloseCircle, | ||
IconArrowUpCircleLine, | ||
IconArrowDownCircleLine, | ||
AddButton, | ||
}, | ||
}; |
Oops, something went wrong.