From be251c08cca1a639e0c6d1f8c6511d13b5284f87 Mon Sep 17 00:00:00 2001 From: wzrove <88016243+wzrove@users.noreply.github.com> Date: Fri, 16 Dec 2022 10:24:10 +0800 Subject: [PATCH] feat: stop Implicit submission (halo-dev/console#766) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind feature /area console #### What this PR does / why we need it: 阻止表单的[隐式提交](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#implicit-submission) >For the purpose of the previous paragraph, an element is a field that blocks implicit submission of a element if it is an element whose [form owner](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-owner) is that element and whose attribute is in one of the following states: [Text](https://html.spec.whatwg.org/multipage/input.html#text-(type=text)-state-and-search-state-(type=search)), [Search](https://html.spec.whatwg.org/multipage/input.html#text-(type=text)-state-and-search-state-(type=search)), [URL](https://html.spec.whatwg.org/multipage/input.html#url-state-(type=url)), [Telephone](https://html.spec.whatwg.org/multipage/input.html#telephone-state-(type=tel)), [Email](https://html.spec.whatwg.org/multipage/input.html#email-state-(type=email)), [Password](https://html.spec.whatwg.org/multipage/input.html#password-state-(type=password)), [Date](https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date)), [Month](https://html.spec.whatwg.org/multipage/input.html#month-state-(type=month)), [Week](https://html.spec.whatwg.org/multipage/input.html#week-state-(type=week)), [Time](https://html.spec.whatwg.org/multipage/input.html#time-state-(type=time)), [Local Date and Time](https://html.spec.whatwg.org/multipage/input.html#local-date-and-time-state-(type=datetime-local)), [Number](https://html.spec.whatwg.org/multipage/input.html#number-state-(type=number)) 当form的子元素只有一个且tyep为 ` "text","search","url","email","password","date","month","week","time","datetime-local","number"`其中任一。 阻止键入`enter` 触发`submit` ,做法是在from表单上监听键盘事件,然后阻止。 #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/2893 #### Screenshots: #### Special notes for your reviewer: + 进入[测试页面](https://formkit.com/playground?fkv=1.0.0-beta.12&fileTab=Playground.vue&files=JTVCJTdCJTIybmFtZSUyMiUzQSUyMlBsYXlncm91bmQudnVlJTIyJTJDJTIyZWRpdG9yJTIyJTNBJTIyJTNDc2NyaXB0JTIwc2V0dXAlM0UlNUNuaW1wb3J0JTIwJTdCcmVmJTdEJTIwZnJvbSUyMCd2dWUnJTNCJTVDbmNvbnN0JTIwdHlwZUxpc3QlMjAlM0QlMjByZWYoJTVCJ2F1dG9jb21wbGV0ZSclMkMlMjAnYnV0dG9uJyUyQyUyMCdjaGVja2JveCclMkMlMjAnY29sb3InJTJDJTIwJ2RhdGUnJTJDJTIwJ2RhdGV0aW1lLWxvY2FsJyUyQyUyMCdkcm9wZG93biclMkMlMjAnZW1haWwnJTJDJTIwJ2ZpbGUnJTJDJTIwJTIwJ2xpc3QnJTJDJTIwJ21vbnRoJyUyQyUyMCdudW1iZXInJTJDJTIwJ3Bhc3N3b3JkJyUyQyUyMCdyYWRpbyclMkMlMjAncmFuZ2UnJTJDJTIwJ3JhdGluZyclMkMlMjAncmVwZWF0ZXInJTJDJTIwJ3NlYXJjaCclMkMlMjAnc2VsZWN0JyUyQyUyMCdzdWJtaXQnJTJDJTIwJ3RhZ2xpc3QnJTJDJTIwJ3RleHQnJTJDJTIwJ3RleHRhcmVhJyUyQyUyMCd0aW1lJyUyQyUyMCd0b2dnbGUnJTJDJTIwJ3VybCclMkMlMjAnd2VlayclNUQpJTNCJTVDbmNvbnN0JTIwaW1wbGljaVN1Ym1pc3Npb25UeXBlJTNEJTIwcmVmKCU1Qid0ZXh0JyUyQyUyMCdzZWFyY2gnJTJDJTIwJ3VybCclMkMlMjAlMjAnZW1haWwnJTJDJTIwJ3Bhc3N3b3JkJyUyQyUyMCdkYXRlJyUyQyUyMCdtb250aCclMkMlMjAnd2VlayclMkMlMjAndGltZSclMkMlMjAnZGF0ZXRpbWUtbG9jYWwnJTIwJTJDJTIwJ251bWJlciclNUQpJTVDbiUzQyUyRnNjcmlwdCUzRSU1Q24lNUNuJTNDdGVtcGxhdGUlM0UlNUNuJTIwJTNDaDIlM0VJbXBsaWNpdCUyMHN1Ym1pc3Npb24lMjB0ZXN0JTNDJTJGaDIlM0UlNUNuJTIwJTIwJTIwJTNDZGl2JTIwdi1mb3IlM0QlNUMlMjJ0eXBlJTIwaW4lMjBpbXBsaWNpU3VibWlzc2lvblR5cGUlNUMlMjIlMjAlM0FrZXklM0QlNUMlMjJ0eXBlJTVDJTIyJTIwc3R5bGUlM0QlNUMlMjJwYWRkaW5nJTNBJTIwMjBweCUyMDBweCU1QyUyMiUzRSU1Q24lMjAlMjAlMjAlMjAlM0NGb3JtS2l0JTVDbiUyMCUyMCUyMCUyMHR5cGUlM0QlNUMlMjJmb3JtJTVDJTIyJTVDbiUyMCUyMCUyMCUyMCUzQWxhYmVsJTNEJTVDJTIyJ0Zvcm1LaXQnJTIwJTJCJTIwdHlwZSU1QyUyMiU1Q24lMjAlMjAlMjAlMjBoZWxwJTNEJTVDJTIyZWRpdCUyMG1lJTIwdG8lMjBnZXQlMjBzdGFydGVkJTVDJTIyJTVDbiUyMCUyMCUyMCUyMCU0MGtleWRvd24lM0QlNUMlMjJmbiU1QyUyMiU1Q24lMjAlMjAlMjAlMjAlM0FpZCUzRCU1QyUyMnR5cGUlNUMlMjIlNUNuJTIwJTIwJTNFJTVDbiUyMCUyMCUzQ3AlMjBzdHlsZSUzRCU1QyUyMmJvcmRlci10b3AlM0ElMjA1cHglMjBzb2xpZCUzQiU1QyUyMiUzRXRlc3QlMjBmb3JtJTYwcyUyMGFjdGlvbiUyMHdoZW4lMjB0b3VjaCUyMCdlbnRlciclMjBrZXkuJTIwY2hpbGQlNjBzdHlwZSUyMCUzQSUyMCU3QiU3QnR5cGUlN0QlN0QlMjAlM0MlMkZwJTNFJTVDbiUyMCUyMCUzQ0Zvcm1LaXQlNUNuJTIwJTIwJTIwJTIwJTNBdHlwZSUzRCU1QyUyMnR5cGUlNUMlMjIlNUNuJTIwJTIwJTIwJTIwJTNBbGFiZWwlM0QlNUMlMjInbXklMjB0eXBlJTIwaXMlMjAnJTJCJTIwdHlwZSU1QyUyMiU1Q24lMjAlMjAlMjAlMjBoZWxwJTNEJTVDJTIydG91Y2glMjBlbnRlciUyMGtleSU1QyUyMiU1Q24lMjAlMjAlMjAlMjAlM0F2YWx1ZSUzRCU1QyUyMnR5cGUlNUMlMjIlNUNuJTIwJTIwJTJGJTNFJTVDbiUyMCUyMCUzQyUyRkZvcm1LaXQlM0UlNUNuJTIwJTIwJTNDJTJGZGl2JTNFJTVDbiUyMCUzQ2gyJTNFLi4uJTIwZnJvbSUyMHRlc3QlMjAlM0MlMkZoMiUzRSU1Q24lMjAlMjAlM0NkaXYlMjB2LWZvciUzRCU1QyUyMnR5cGUlMjBpbiUyMHR5cGVMaXN0JTVDJTIyJTIwJTNBa2V5JTNEJTVDJTIydHlwZSU1QyUyMiUyMHN0eWxlJTNEJTVDJTIycGFkZGluZyUzQSUyMDIwcHglMjAwcHglNUMlMjIlMjAlM0UlNUNuJTIwJTIwJTIwJTIwJTNDRm9ybUtpdCU1Q24lMjAlMjAlMjAlMjB2LWlmJTNEJTVDJTIyIWltcGxpY2lTdWJtaXNzaW9uVHlwZS5pbmNsdWRlcyh0eXBlKSU1QyUyMiU1Q24lMjAlMjAlMjAlMjB0eXBlJTNEJTVDJTIyZm9ybSU1QyUyMiU1Q24lMjAlMjAlMjAlMjAlM0FsYWJlbCUzRCU1QyUyMidGb3JtS2l0JyUyMCUyQiUyMHR5cGUlNUMlMjIlNUNuJTIwJTIwJTIwJTIwaGVscCUzRCU1QyUyMmVkaXQlMjBtZSUyMHRvJTIwZ2V0JTIwc3RhcnRlZCU1QyUyMiU1Q24lMjAlMjAlMjAlMjAlNDBrZXlkb3duJTNEJTVDJTIyZm4lNUMlMjIlNUNuJTIwJTIwJTIwJTIwJTNBaWQlM0QlNUMlMjJ0eXBlJTVDJTIyJTVDbiUyMCUyMCUzRSU1Q24lMjAlMjAlM0NwJTIwc3R5bGUlM0QlNUMlMjJib3JkZXItdG9wJTNBJTIwNXB4JTIwc29saWQlM0IlNUMlMjIlM0V0ZXN0JTIwZm9ybSU2MHMlMjBhY3Rpb24lMjB3aGVuJTIwdG91Y2glMjAnZW50ZXInJTIwa2V5LiUyMGNoaWxkJTYwc3R5cGUlMjAlM0ElMjAlN0IlN0J0eXBlJTdEJTdEJTIwJTNDJTJGcCUzRSU1Q24lMjAlMjAlM0NGb3JtS2l0JTVDbiUyMCUyMCUyMCUyMCUzQXR5cGUlM0QlNUMlMjJ0eXBlJTVDJTIyJTVDbiUyMCUyMCUyMCUyMCUzQWxhYmVsJTNEJTVDJTIyJ215JTIwdHlwZSUyMGlzJTIwJyUyQiUyMHR5cGUlNUMlMjIlNUNuJTIwJTIwJTIwJTIwaGVscCUzRCU1QyUyMnRvdWNoJTIwZW50ZXIlMjBrZXklNUMlMjIlNUNuJTIwJTIwJTIwJTIwJTNBdmFsdWUlM0QlNUMlMjJ0eXBlJTVDJTIyJTVDbiUyMCUyMCUyRiUzRSU1Q24lMjAlMjAlM0MlMkZGb3JtS2l0JTNFJTVDbiUyMCUyMCUzQyUyRmRpdiUzRSU1Q24lNUNuJTNDJTJGdGVtcGxhdGUlM0UlNUNuJTVDbiUzQ3N0eWxlJTIwc2NvcGVkJTNFJTVDbiUyRiolNUNudmFuaWxsYSUyMENTUyUyMGNhbiUyMGdvJTIwaGVyZS4lNUNuS2VlcCUyMHN0eWxlcyUyMHNjb3BlZCUyMHRvJTIwYXZvaWQlMjBtdWx0aXBsZSUyMGZpbGVzJTVDbm92ZXJ3cml0aW5nJTIwZWFjaCUyMG90aGVyJTIwaW4lMjB0aGUlMjByZW5kZXIlMjBvdXRwdXQuJTVDbiolMkYlNUNuJTNDJTJGc3R5bGUlM0UlNUNuJTIyJTJDJTIyYWRkZWQlMjIlM0F0cnVlJTdEJTJDJTdCJTIybmFtZSUyMiUzQSUyMmZvcm1raXQuY29uZmlnLmpzJTIyJTJDJTIyZWRpdG9yJTIyJTNBJTIyJTJGKiUyMEluJTIwdGhpcyUyMGZpbGUlMkMlMjBleHBvcnQlMjB5b3VyJTIwZmluYWwlMjBjb25maWcuJTVDbkl0JTIwd2lsbCUyMGF1dG9tYXRpY2FsbHklMjBiZSUyMGluamVjdGVkJTIwaW50byUyMHRoZSUyMHBsYXlncm91bmQlMjBmb3IlMjB5b3UuJTVDbkltcG9ydHMlMjBmcm9tJTIwb3RoZXIlMjBwbGF5Z3JvdW5kJTIwZmlsZXMlMjBhcmUlMjBzdXBwb3J0ZWQuJTIwKiUyRiU1Q25pbXBvcnQlMjAlN0IlMjBvbk1vdW50ZWQlMjAlN0QlMjBmcm9tJTIwJTVDJTIydnVlJTVDJTIyJTNCJTVDbmNvbnN0JTIwaW1wbGljaVN1Ym1pc3Npb25UeXBlJTIwJTNEJTIwJTVCJTVDbiUyMCUyMCU1QyUyMnRleHQlNUMlMjIlMkMlNUNuJTIwJTIwJTVDJTIyc2VhcmNoJTVDJTIyJTJDJTVDbiUyMCUyMCU1QyUyMnVybCU1QyUyMiUyQyU1Q24lMjAlMjAlNUMlMjJlbWFpbCU1QyUyMiUyQyU1Q24lMjAlMjAlNUMlMjJwYXNzd29yZCU1QyUyMiUyQyU1Q24lMjAlMjAlNUMlMjJudW1iZXIlNUMlMjIlMkMlNUNuJTVEJTNCJTVDbmZ1bmN0aW9uJTIwaGFuZGxlS2V5ZG93bihlKSUyMCU3QiU1Q24lMjAlMjBpZiUyMCglNUNuJTIwJTIwJTIwJTIwZS5rZXklMjAlM0QlM0QlMjAlNUMlMjJFbnRlciU1QyUyMiUyMCUyNiUyNiU1Q24lMjAlMjAlMjAlMjBlLmFsdEtleSUyMCUzRCUzRCUyMGZhbHNlJTIwJTI2JTI2JTVDbiUyMCUyMCUyMCUyMGUuY3RybEtleSUyMCUzRCUzRCUyMGZhbHNlJTIwJTI2JTI2JTVDbiUyMCUyMCUyMCUyMGUubWV0YUtleSUyMCUzRCUzRCUyMGZhbHNlJTVDbiUyMCUyMCklMjAlN0IlNUNuJTJGJTJGJTIwb3BlbiUyMHdpbGwlMjBzdG9wJTIwc3VibWl0JTVDbiUyRiUyRiUyMCUyMGUuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKCklM0IlNUNuJTJGJTJGJTIwJTIwZS5wcmV2ZW50RGVmYXVsdCgpJTNCJTVDbiUyRiUyRiUyMCUyMGUuc3RvcFByb3BhZ2F0aW9uKCklM0IlNUNuJTIwJTIwJTdEJTVDbiU3RCU1Q24lNUNuY29uc3QlMjBpbnB1dFByZXZlbnRGbiUyMCUzRCUyMChub2RlKSUyMCUzRCUzRSUyMCU3QiU1Q24lMjAlMjBpZiUyMChub2RlLnR5cGUlMjAlM0QlM0QlMjAlNUMlMjJncm91cCU1QyUyMiklMjAlN0IlNUNuJTIwJTIwJTIwJTIwb25Nb3VudGVkKCgpJTIwJTNEJTNFJTIwJTdCJTVDbiUyMCUyMCUyMCUyMCUyMCUyMGlmJTIwKG5vZGUuY2hpbGRyZW4ubGVuZ3RoJTIwJTNEJTNEJTIwMSUyMCUyNiUyNiUyMG5vZGUucHJvcHMudHlwZSUyMCUzRCUzRCUyMCU1QyUyMmZvcm0lNUMlMjIlMjAlMjYlMjYlMjBpbXBsaWNpU3VibWlzc2lvblR5cGUuaW5jbHVkZXMobm9kZS5jaGlsZHJlbiU1QjAlNUQucHJvcHMudHlwZSkpJTIwJTdCJTVDbiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMGNvbnN0JTIwaWQlMjAlM0QlMjBub2RlLnByb3BzLmlkJTIwJTNGJTNGJTIwJTVDJTIyJTVDJTIyJTNCJTVDbiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMGNvbnN0JTIwcm9vdEZvcm0lMjAlM0QlMjBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChpZCklM0IlNUNuJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwcm9vdEZvcm0lM0YuYWRkRXZlbnRMaXN0ZW5lciglNUMlMjJrZXlkb3duJTVDJTIyJTJDJTIwaGFuZGxlS2V5ZG93biklM0IlNUNuJTIwJTIwJTIwJTIwJTIwJTIwJTdEJTVDbiUyMCUyMCUyMCUyMCU3RCklM0IlNUNuJTIwJTIwJTdEJTVDbiU3RCUzQiU1Q24lNUNuJTVDbmV4cG9ydCUyMGRlZmF1bHQlMjAlN0IlNUNuJTIwJTIwcGx1Z2lucyUzQSUyMCU1QmlucHV0UHJldmVudEZuJTIwJTVEJTVDbiU3RCU1Q24lMjIlMkMlMjJyZW1vdmFibGUlMjIlM0F0cnVlJTJDJTIyYWRkZWQlMjIlM0F0cnVlJTdEJTVE&imports=JTdCJTIybmFtZSUyMiUzQSUyMkltcG9ydE1hcCUyMiUyQyUyMmVkaXRvciUyMiUzQSUyMiU3QiU1Q24lMjAlMjAlNUMlMjJ2dWUlNUMlMjIlM0ElMjAlNUMlMjJodHRwcyUzQSUyRiUyRmNkbi5qc2RlbGl2ci5uZXQlMkZucG0lMkZ2dWUlNDAzJTJGZGlzdCUyRnZ1ZS5lc20tYnJvd3Nlci5taW4uanMlNUMlMjIlNUNuJTdEJTVDbiUyMiU3RA) + 点击表单元素,表单元素聚焦之后,之后键入`enter`观察是否触发`submit` + 打开`formkit.config.js`tab, 放开注释,阻止事件行为。重复第二点。观察`submit`事件。 #### Does this PR introduce a user-facing change? ```release-note 禁止使用 Enter 提交,需要使用组合键(Ctrl + Enter) ``` --- src/formkit/formkit.config.ts | 3 +- .../plugins/stop-implicit-submission.ts | 80 +++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 src/formkit/plugins/stop-implicit-submission.ts diff --git a/src/formkit/formkit.config.ts b/src/formkit/formkit.config.ts index 1bbd0159e..6ddc6e8a6 100644 --- a/src/formkit/formkit.config.ts +++ b/src/formkit/formkit.config.ts @@ -17,12 +17,13 @@ import { categoryCheckbox } from "./inputs/category-checkbox"; import { tagCheckbox } from "./inputs/tag-checkbox"; import radioAlt from "./plugins/radio-alt"; +import stopImplicitSubmission from "./plugins/stop-implicit-submission"; const config: DefaultConfigOptions = { config: { classes: generateClasses(theme), }, - plugins: [radioAlt], + plugins: [radioAlt, stopImplicitSubmission], inputs: { form, attachment, diff --git a/src/formkit/plugins/stop-implicit-submission.ts b/src/formkit/plugins/stop-implicit-submission.ts new file mode 100644 index 000000000..0c5caa6b9 --- /dev/null +++ b/src/formkit/plugins/stop-implicit-submission.ts @@ -0,0 +1,80 @@ +import router from "@/router"; +import type { FormKitNode } from "@formkit/core"; +import { onMounted } from "vue"; +import { useRoute } from "vue-router"; +function handleKeydown(e: KeyboardEvent) { + if ( + e.key == "Enter" && + e.altKey == false && + e.ctrlKey == false && + e.metaKey == false + ) { + e.stopImmediatePropagation(); + e.preventDefault(); + e.stopPropagation(); + } +} + +// 以下表单键入enter引起表单提交, +//https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#implicit-submission +const implicitSubmissionType = [ + "text", + "search", + "url", + "email", + "password", + "date", + "month", + "week", + "time", + "datetime-local", + "number", +]; + +const FormKeydownEventControllerMap = new Map(); + +const clearFormKeydownEventByPath = (fullPath: string) => { + if (FormKeydownEventControllerMap.size) { + FormKeydownEventControllerMap.forEach((controller, path) => { + if (!fullPath.includes(path)) { + controller.abort(); + FormKeydownEventControllerMap.delete(path); + } + }); + } +}; + +//https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener#%E5%8F%82%E6%95%B0 +//使用AbortSignal来取消事件监听 +const inputPreventFn = (node: FormKitNode) => { + const id = node.props.id ?? ""; + if (node.type == "group") { + onMounted(() => { + const { path } = useRoute(); + let controller = FormKeydownEventControllerMap.get(path); + if ( + node.children.length == 1 && + node.props.type == "form" && + implicitSubmissionType.includes(node.children[0].props.type) + ) { + if (!controller) { + controller = new AbortController(); + FormKeydownEventControllerMap.set(path, controller); + } + const rootForm = document.getElementById(id); + rootForm?.addEventListener("keydown", handleKeydown, { + signal: controller.signal, + }); + } + }); + } +}; + +// 进入下一个页面,清除上一个页面的事件。 +// 可以做个测试后面。https://segmentfault.com/q/1010000042671015 +// map里面的key是path,这里传fullPath,当有多级路由的时候避免清掉上一级的事件。 +router.beforeEach(({ fullPath }) => { + clearFormKeydownEventByPath(fullPath); +}); + +export default inputPreventFn;