diff --git a/docs/custom-formkit-input/README.md b/docs/custom-formkit-input/README.md index e4455119a..6a46d7b3f 100644 --- a/docs/custom-formkit-input/README.md +++ b/docs/custom-formkit-input/README.md @@ -13,6 +13,7 @@ 1. language: 目前支持 `yaml`, `html`, `css`, `javascript`, `json` 2. height: 编辑器高度,如:`100px` - `attachment`: 附件选择 +- `repeater`: 定义一个对象集合,可以让使用者可视化的操作集合。 - `menuCheckbox`:选择一组菜单 - `menuRadio`:选择一个菜单 - `menuItemSelect`:选择菜单项 @@ -48,3 +49,68 @@ const postName = ref("") name: menus label: 底部菜单组 ``` + +### Repeater + +Repeater 是一个集合类型的输入组件,可以让使用者可视化的操作集合。 + +在 Vue SFC 中以组件形式使用: + +```vue + + + +``` + +在 FormKit Schema 中使用: + +```yaml +- $formkit: repeater + name: users + label: Users + items: + - $formkit: text + name: full_name + label: Full Name + validation: required + - $formkit: email + name: email + label: Email + validation: required|email +``` + +最终得到的数据类似于: + +```json +[ + { + "full_name": "Jack", + "email": "jack@example.com" + }, + { + "full_name": "John", + "email": "john@example.com" + } +] +``` diff --git a/package.json b/package.json index c8d6310f2..f74df6370 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@formkit/inputs": "^1.0.0-beta.11", "@formkit/themes": "^1.0.0-beta.11", "@formkit/vue": "^1.0.0-beta.11", + "@formkit/utils": "^1.0.0-beta.11", "@halo-dev/api-client": "^0.0.46", "@halo-dev/components": "workspace:*", "@halo-dev/console-shared": "workspace:*", diff --git a/packages/components/src/components/button/Button.vue b/packages/components/src/components/button/Button.vue index 7945c8ef0..db9937da0 100644 --- a/packages/components/src/components/button/Button.vue +++ b/packages/components/src/components/button/Button.vue @@ -3,6 +3,7 @@ :class="classes" :disabled="disabled" class="btn" + type="button" @click="handleClick" > @@ -128,11 +129,11 @@ function handleClick() { } .btn-primary { - @apply text-white bg-primary; + @apply text-white bg-primary #{!important}; } .btn-secondary { - @apply text-white bg-secondary; + @apply text-white bg-secondary #{!important}; } .btn-danger { diff --git a/packages/components/src/components/button/__tests__/__snapshots__/Button.spec.ts.snap b/packages/components/src/components/button/__tests__/__snapshots__/Button.spec.ts.snap index b1fb19239..e39fdbec3 100644 --- a/packages/components/src/components/button/__tests__/__snapshots__/Button.spec.ts.snap +++ b/packages/components/src/components/button/__tests__/__snapshots__/Button.spec.ts.snap @@ -1,61 +1,61 @@ // Vitest Snapshot v1 exports[`Button > should render 1`] = ` -"" `; exports[`Button > should work with block prop 1`] = ` -"" `; exports[`Button > should work with circle prop 1`] = ` -"" `; exports[`Button > should work with disabled prop 1`] = ` -"" `; exports[`Button > should work with size prop 1`] = ` -"" `; exports[`Button > should work with size prop 2`] = ` -"" `; exports[`Button > should work with size prop 3`] = ` -"" `; exports[`Button > should work with type prop 1`] = ` -"" `; exports[`Button > should work with type prop 2`] = ` -"" `; exports[`Button > should work with type prop 3`] = ` -"" `; diff --git a/packages/components/src/components/empty/__tests__/__snapshots__/Empty.spec.ts.snap b/packages/components/src/components/empty/__tests__/__snapshots__/Empty.spec.ts.snap index 47d580dda..d6bd2dfef 100644 --- a/packages/components/src/components/empty/__tests__/__snapshots__/Empty.spec.ts.snap +++ b/packages/components/src/components/empty/__tests__/__snapshots__/Empty.spec.ts.snap @@ -5,7 +5,7 @@ exports[`Empty > should match snapshot 1`] = `
\\"Empty\\"
Not found
No posts found
-
" diff --git a/packages/components/src/icons/icons.ts b/packages/components/src/icons/icons.ts index 8dbe498c0..d22cff3e2 100644 --- a/packages/components/src/icons/icons.ts +++ b/packages/components/src/icons/icons.ts @@ -54,6 +54,8 @@ import IconRefreshLine from "~icons/ri/refresh-line"; import IconWindowLine from "~icons/ri/window-line"; import IconSendPlaneFill from "~icons/ri/send-plane-fill"; import IconRocketLine from "~icons/ri/rocket-line"; +import IconArrowUpCircleLine from "~icons/ri/arrow-up-circle-line"; +import IconArrowDownCircleLine from "~icons/ri/arrow-down-circle-line"; export { IconDashboard, @@ -112,4 +114,6 @@ export { IconWindowLine, IconSendPlaneFill, IconRocketLine, + IconArrowUpCircleLine, + IconArrowDownCircleLine, }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c76eb854f..3beaa3394 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,6 +10,7 @@ importers: '@formkit/i18n': ^1.0.0-beta.11 '@formkit/inputs': ^1.0.0-beta.11 '@formkit/themes': ^1.0.0-beta.11 + '@formkit/utils': ^1.0.0-beta.11 '@formkit/vue': ^1.0.0-beta.11 '@halo-dev/api-client': ^0.0.46 '@halo-dev/components': workspace:* @@ -99,11 +100,12 @@ importers: yaml: ^2.1.3 dependencies: '@emoji-mart/data': 1.0.6 - '@formkit/core': 1.0.0-beta.11 - '@formkit/i18n': 1.0.0-beta.11 - '@formkit/inputs': 1.0.0-beta.11 - '@formkit/themes': 1.0.0-beta.11_tailwindcss@3.2.1 - '@formkit/vue': 1.0.0-beta.11_vjnbgdptsk6bkj7ab5a6mk2cwm + '@formkit/core': 1.0.0-beta.12-e579559 + '@formkit/i18n': 1.0.0-beta.12-e579559 + '@formkit/inputs': 1.0.0-beta.12-e579559 + '@formkit/themes': 1.0.0-beta.12-e579559_tailwindcss@3.2.1 + '@formkit/utils': 1.0.0-beta.12-e579559 + '@formkit/vue': 1.0.0-beta.12-e579559_vjnbgdptsk6bkj7ab5a6mk2cwm '@halo-dev/api-client': 0.0.46 '@halo-dev/components': link:packages/components '@halo-dev/console-shared': link:packages/shared @@ -1845,50 +1847,50 @@ packages: '@floating-ui/core': 0.3.1 dev: false - /@formkit/core/1.0.0-beta.11: - resolution: {integrity: sha512-1yIQUicY1ZVGkeT0AEVp3ahx5FdJg+snyc8yaitsNmQeCSq1Ju5o1nCRcpcZ7j2ezMbGUiL7nTscGGejC15N2Q==} + /@formkit/core/1.0.0-beta.12-e579559: + resolution: {integrity: sha512-y90ubMcFr6WtAjZqUOLgA3p4jm024f6R7iDRVKHsmdwSKm1/GZl4D+Yo2k8DL40xM7NbZhIvK40MiEC0pNZ3ig==} dependencies: - '@formkit/utils': 1.0.0-beta.11 + '@formkit/utils': 1.0.0-beta.12-e579559 dev: false - /@formkit/dev/1.0.0-beta.11: - resolution: {integrity: sha512-YuPfE4ZoIAOQfawKlnCxXdnNOktckGNUhtCirpgsyBuHVu15rX1E5mYRAyEBhDGeEPV8IOFNpooSKA/y9W7dtw==} + /@formkit/dev/1.0.0-beta.12-e579559: + resolution: {integrity: sha512-jH5o9VwQWVuyO8cIr3BiZFXai0BtQe8mL+EUECEoRYtp6vkzgk6ErezejX7jqnu4dW9NS6chMeLIF+OAjh6zIg==} dependencies: - '@formkit/core': 1.0.0-beta.11 - '@formkit/utils': 1.0.0-beta.11 + '@formkit/core': 1.0.0-beta.12-e579559 + '@formkit/utils': 1.0.0-beta.12-e579559 dev: false - /@formkit/i18n/1.0.0-beta.11: - resolution: {integrity: sha512-ID4y9r5NoKy+rIiv41UGCtrg8iR3Lor30oBEUJlEDPAh34vlwh4g2jorXgysdSVMRCuLnqGyGspLnrPdxgSszw==} + /@formkit/i18n/1.0.0-beta.12-e579559: + resolution: {integrity: sha512-IKfhVR+LWSoIAd60OFHjLbt4y/owd7uQfI1uO8sjXs2iz+7VqmE33R7DcI3cu5+WuC9JE/letRteL9nSSJ6kBA==} dependencies: - '@formkit/core': 1.0.0-beta.11 - '@formkit/utils': 1.0.0-beta.11 - '@formkit/validation': 1.0.0-beta.11 + '@formkit/core': 1.0.0-beta.12-e579559 + '@formkit/utils': 1.0.0-beta.12-e579559 + '@formkit/validation': 1.0.0-beta.12-e579559 dev: false - /@formkit/inputs/1.0.0-beta.11: - resolution: {integrity: sha512-DF+olkewjAE/q+alsG1t4GFX7JHzVvTCukOOEuqp8AzT+MGyZFzxikSXd8qA5zX+1BvRCgQwzWZMBaQLUo3IMA==} + /@formkit/inputs/1.0.0-beta.12-e579559: + resolution: {integrity: sha512-igRXzW9edZ4wMHkX7XFricPsVNU99BoMWKwDOzg+erjLKwPw+h4tVKWmZFe2xTUUDSCBoxkLS5TgdsUZ84vMng==} dependencies: - '@formkit/core': 1.0.0-beta.11 + '@formkit/core': 1.0.0-beta.12-e579559 dev: false - /@formkit/observer/1.0.0-beta.11: - resolution: {integrity: sha512-jpOqOJdpydl/2eOL0yXVZjXYdk9tY0EsAf9e5PTxXcWOZpBskSwPdjNMnx8y9ycrpj6lKPcbMfs2bjnQQue8IQ==} + /@formkit/observer/1.0.0-beta.12-e579559: + resolution: {integrity: sha512-6Nki4VmUN3OyU70N6AIttvn/ScZXq5mwv11BIYLgfsfiMI5zoiflmNHrD7yf4KTUVvz16ThJB5nRGIXlnUnH3Q==} dependencies: - '@formkit/core': 1.0.0-beta.11 - '@formkit/utils': 1.0.0-beta.11 + '@formkit/core': 1.0.0-beta.12-e579559 + '@formkit/utils': 1.0.0-beta.12-e579559 dev: false - /@formkit/rules/1.0.0-beta.11: - resolution: {integrity: sha512-6SQ+fGXTx9MLMUk832x9oR/423U4/7TpDxyx/cjz1xaQjAKzorwNGbjiiRszp4f3cSYcpyi/SZ36n0UktLaCVA==} + /@formkit/rules/1.0.0-beta.12-e579559: + resolution: {integrity: sha512-xu3VRPTob2GWA54MQafekVX+fUWYJbcMrkcDEo8yB4ZgKdlW5Er1C3gYa/bByuMNpY0/lklwdpKP/8aoIKfmxw==} dependencies: - '@formkit/core': 1.0.0-beta.11 - '@formkit/utils': 1.0.0-beta.11 - '@formkit/validation': 1.0.0-beta.11 + '@formkit/core': 1.0.0-beta.12-e579559 + '@formkit/utils': 1.0.0-beta.12-e579559 + '@formkit/validation': 1.0.0-beta.12-e579559 dev: false - /@formkit/themes/1.0.0-beta.11_tailwindcss@3.2.1: - resolution: {integrity: sha512-OmefvFoLHZLnmbuT+Mqbqr681rumc62Q08Y1g82ZxG6aXWculwQYVQfZJeJFrqrJPpccsfoSflWeAuAMWiP/Yw==} + /@formkit/themes/1.0.0-beta.12-e579559_tailwindcss@3.2.1: + resolution: {integrity: sha512-ceFbta+xDO87Y96zAGqe3qCvSVyqEgmpsDCjk3BZTghydhIJupmgKSyr6TmmyJ5WT8zpcs8wnc2kF1YbUVt8Yw==} peerDependencies: tailwindcss: ^3.0.0 unocss: ^0.31.0 @@ -1901,35 +1903,35 @@ packages: windicss: optional: true dependencies: - '@formkit/core': 1.0.0-beta.11 + '@formkit/core': 1.0.0-beta.12-e579559 tailwindcss: 3.2.1_postcss@8.4.18 dev: false - /@formkit/utils/1.0.0-beta.11: - resolution: {integrity: sha512-ajeB6A0WJXaErwIgdHGnVZotghvWOZ+JvFKK6fM/rYQza2O4YFiPAVKqJRQjMbW5Jxia4sT7PQhPUFvZQFbuKQ==} + /@formkit/utils/1.0.0-beta.12-e579559: + resolution: {integrity: sha512-4yTz4IRzPX3G08aAO+ZfEuBYfy6OSgk3csx3TBhOcL6NzyIbJkTc2ZsxaUYXgY6FOVclazLGMKM5+jBitMOL5Q==} dev: false - /@formkit/validation/1.0.0-beta.11: - resolution: {integrity: sha512-7HePIek0Y2MsH45lHDyCekJfENNdIr8ZIkJQx4VwAlfbIyZElZx5VsxInCSLf/03aOu4PhabOxpNUt38AP2hAg==} + /@formkit/validation/1.0.0-beta.12-e579559: + resolution: {integrity: sha512-f/S0LefikeBZRxKkjx1fD7RODXMHglg6+B1NUYEKsEKc3ySjH/V9/XroaPszXfkupAnVBSTjZXG7T9hmn39VVA==} dependencies: - '@formkit/core': 1.0.0-beta.11 - '@formkit/observer': 1.0.0-beta.11 + '@formkit/core': 1.0.0-beta.12-e579559 + '@formkit/observer': 1.0.0-beta.12-e579559 dev: false - /@formkit/vue/1.0.0-beta.11_vjnbgdptsk6bkj7ab5a6mk2cwm: - resolution: {integrity: sha512-0tVy8JZ/j4QbmYlVPJyFGZOukWgRBTV+DhyGo82FZvvb2+YrRAbDhqJGLIjLyOO0pNTwk7arSgDiiKd/OK8LtA==} + /@formkit/vue/1.0.0-beta.12-e579559_vjnbgdptsk6bkj7ab5a6mk2cwm: + resolution: {integrity: sha512-z281m0+6alG7RyztFBDFvhVB2iYqaVk5o3Flfrro9FkzGDNCFjMyk0LwSTVQd8DhOVYeXPGImtKqp+mFoxrH4Q==} peerDependencies: vue: ^3.2.1 dependencies: - '@formkit/core': 1.0.0-beta.11 - '@formkit/dev': 1.0.0-beta.11 - '@formkit/i18n': 1.0.0-beta.11 - '@formkit/inputs': 1.0.0-beta.11 - '@formkit/observer': 1.0.0-beta.11 - '@formkit/rules': 1.0.0-beta.11 - '@formkit/themes': 1.0.0-beta.11_tailwindcss@3.2.1 - '@formkit/utils': 1.0.0-beta.11 - '@formkit/validation': 1.0.0-beta.11 + '@formkit/core': 1.0.0-beta.12-e579559 + '@formkit/dev': 1.0.0-beta.12-e579559 + '@formkit/i18n': 1.0.0-beta.12-e579559 + '@formkit/inputs': 1.0.0-beta.12-e579559 + '@formkit/observer': 1.0.0-beta.12-e579559 + '@formkit/rules': 1.0.0-beta.12-e579559 + '@formkit/themes': 1.0.0-beta.12-e579559_tailwindcss@3.2.1 + '@formkit/utils': 1.0.0-beta.12-e579559 + '@formkit/validation': 1.0.0-beta.12-e579559 vue: 3.2.41 transitivePeerDependencies: - tailwindcss diff --git a/src/formkit/formkit.config.ts b/src/formkit/formkit.config.ts index 15dab8dee..1bbd0159e 100644 --- a/src/formkit/formkit.config.ts +++ b/src/formkit/formkit.config.ts @@ -5,6 +5,7 @@ import type { DefaultConfigOptions } from "@formkit/vue"; import { form } from "./inputs/form"; import { attachment } from "./inputs/attachment"; import { code } from "./inputs/code"; +import { repeater } from "./inputs/repeater"; import { menuCheckbox } from "./inputs/menu-checkbox"; import { menuRadio } from "./inputs/menu-radio"; import { menuItemSelect } from "./inputs/menu-item-select"; @@ -15,14 +16,18 @@ import { tagSelect } from "./inputs/tag-select"; import { categoryCheckbox } from "./inputs/category-checkbox"; import { tagCheckbox } from "./inputs/tag-checkbox"; +import radioAlt from "./plugins/radio-alt"; + const config: DefaultConfigOptions = { config: { classes: generateClasses(theme), }, + plugins: [radioAlt], inputs: { form, attachment, code, + repeater, menuCheckbox, menuRadio, menuItemSelect, diff --git a/src/formkit/inputs/repeater/Repeater.vue b/src/formkit/inputs/repeater/Repeater.vue new file mode 100644 index 000000000..203694582 --- /dev/null +++ b/src/formkit/inputs/repeater/Repeater.vue @@ -0,0 +1,117 @@ + + + diff --git a/src/formkit/inputs/repeater/index.ts b/src/formkit/inputs/repeater/index.ts new file mode 100644 index 000000000..216ad8c31 --- /dev/null +++ b/src/formkit/inputs/repeater/index.ts @@ -0,0 +1,29 @@ +import type { FormKitTypeDefinition } from "@formkit/core"; +import { + fieldset, + help, + inner, + legend, + message, + messages, + outer, + prefix, + suffix, +} from "@formkit/inputs"; +import { repeaterItems } from "./sections"; +import Repeater from "./Repeater.vue"; + +export const repeater: FormKitTypeDefinition = { + schema: outer( + fieldset( + legend("$label"), + help("$help"), + inner(prefix(), repeaterItems("$slots.default"), suffix()) + ), + messages(message("$message.value")) + ), + type: "list", + library: { + Repeater: Repeater, + }, +}; diff --git a/src/formkit/inputs/repeater/sections/index.ts b/src/formkit/inputs/repeater/sections/index.ts new file mode 100644 index 000000000..a79b201a7 --- /dev/null +++ b/src/formkit/inputs/repeater/sections/index.ts @@ -0,0 +1,8 @@ +import { createSection } from "@formkit/inputs"; + +export const repeaterItems = createSection("repeaterItems", () => ({ + $cmp: "Repeater", + props: { + context: "$node.context", + }, +})); diff --git a/src/formkit/plugins/radio-alt.ts b/src/formkit/plugins/radio-alt.ts new file mode 100644 index 000000000..7ce37f0a5 --- /dev/null +++ b/src/formkit/plugins/radio-alt.ts @@ -0,0 +1,9 @@ +import type { FormKitNode } from "@formkit/core"; + +let i = 0; + +export default function radioAlt(node: FormKitNode) { + if (node.props.type === "radio") { + node.props.altName = `radio_${node.name}_${++i}`; + } +} diff --git a/src/formkit/theme.ts b/src/formkit/theme.ts index a63a6abe1..d53887c03 100644 --- a/src/formkit/theme.ts +++ b/src/formkit/theme.ts @@ -14,7 +14,7 @@ const boxClassification = { "group border border-gray-300 rounded-base px-2 py-2 focus-within:border-primary max-w-lg", wrapper: "flex items-center mb-1 cursor-pointer group-[.formkit-fieldset]:px-2", - help: "mb-2 mt-0", + help: "mb-2 mt-0 px-2", input: "form-check-input h-4 w-4 mr-2 border border-gray-500 rounded-sm bg-white checked:bg-primary focus:outline-none focus:ring-0 transition duration-200", inner: "flex items-center", @@ -79,6 +79,18 @@ const theme: Record> = { input: textClassification.input.replace("resize-none", "resize-y") + " py-2", }, + repeater: { + label: textClassification.label, + legend: `${textClassification.label} px-2`, + fieldset: boxClassification.fieldset, + wrapper: boxClassification.wrapper, + help: boxClassification.wrapper, + inner: "flex flex-col gap-4", + items: "flex flex-col w-full gap-2", + item: "border rounded-base grid grid-cols-12 focus-within:border-primary transition-all overflow-hidden focus-within:shadow-sm", + content: "flex-1 p-2 col-span-11", + controls: "bg-gray-200 col-span-1 flex items-center justify-center", + }, }; export default theme; diff --git a/src/modules/contents/attachments/components/AttachmentPolicyEditingModal.vue b/src/modules/contents/attachments/components/AttachmentPolicyEditingModal.vue index 82b090f96..a44a75c08 100644 --- a/src/modules/contents/attachments/components/AttachmentPolicyEditingModal.vue +++ b/src/modules/contents/attachments/components/AttachmentPolicyEditingModal.vue @@ -208,7 +208,10 @@ const onVisibleChange = (visible: boolean) => { name="displayName" validation="required" > - +