@conform-to/dom
Advanced tools
Comparing version 0.5.0-pre.0 to 0.5.0
export declare type Primitive = null | undefined | string | number | boolean | Date; | ||
export declare type FieldElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement; | ||
export interface FieldConfig<Schema = unknown> extends FieldConstraint<Schema> { | ||
id?: string; | ||
name: string; | ||
@@ -8,2 +9,3 @@ defaultValue?: FieldValue<Schema>; | ||
form?: string; | ||
errorId?: string; | ||
} | ||
@@ -32,2 +34,7 @@ export declare type FieldValue<Schema> = Schema extends Primitive ? string : Schema extends File ? File : Schema extends Array<infer InnerType> ? Array<FieldValue<InnerType>> : Schema extends Record<string, any> ? { | ||
}; | ||
export interface CommandButtonProps<Name extends string = string> { | ||
name: `conform/${Name}`; | ||
value: string; | ||
formNoValidate?: boolean; | ||
} | ||
export declare function isFieldElement(element: unknown): element is FieldElement; | ||
@@ -38,16 +45,26 @@ export declare function getFormElements(form: HTMLFormElement): FieldElement[]; | ||
export declare function getName(paths: Array<string | number>): string; | ||
export declare function shouldValidate(submission: Submission, name?: string): boolean; | ||
export declare function shouldValidate(submission: Submission, name: string): boolean; | ||
export declare function hasError(error: Array<[string, string]>, name?: string): boolean; | ||
export declare function reportSubmission(form: HTMLFormElement, submission: Submission): void; | ||
export declare function setValue<T>(target: any, paths: Array<string | number>, valueFn: (prev?: T) => T): void; | ||
/** | ||
* The ponyfill of `HTMLFormElement.requestSubmit()` | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/requestSubmit | ||
* @see https://caniuse.com/?search=requestSubmit | ||
*/ | ||
export declare function requestSubmit(form: HTMLFormElement, submitter?: HTMLButtonElement | HTMLInputElement): void; | ||
export declare function requestValidate(form: HTMLFormElement, field?: string): void; | ||
/** | ||
* Creates a command button on demand and trigger a form submit by clicking it. | ||
*/ | ||
export declare function requestCommand(form: HTMLFormElement | undefined, buttonProps: CommandButtonProps): void; | ||
/** | ||
* Returns the properties required to configure a command button for validation | ||
* | ||
* @see https://conform.guide/api/react#validate | ||
*/ | ||
export declare function validate(field?: string): CommandButtonProps<'validate'>; | ||
export declare function getFormElement(element: HTMLFormElement | HTMLFieldSetElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement | null): HTMLFormElement | null; | ||
export declare function focusFirstInvalidField(form: HTMLFormElement): void; | ||
export declare function focus(field: FieldElement): void; | ||
export declare function getSubmissionType(name: string): string | null; | ||
export declare function parse<Schema extends Record<string, any>>(payload: FormData | URLSearchParams): Submission<Schema>; | ||
export declare type Command = { | ||
name: string; | ||
value: string; | ||
}; | ||
export declare type ListCommand<Schema = unknown> = { | ||
@@ -89,1 +106,26 @@ type: 'prepend'; | ||
export declare function handleList<Schema>(submission: Submission<Schema>): Submission<Schema>; | ||
export interface ListCommandButtonBuilder { | ||
append<Schema>(name: string, payload?: { | ||
defaultValue: Schema; | ||
}): CommandButtonProps<'list'>; | ||
prepend<Schema>(name: string, payload?: { | ||
defaultValue: Schema; | ||
}): CommandButtonProps<'list'>; | ||
replace<Schema>(name: string, payload: { | ||
defaultValue: Schema; | ||
index: number; | ||
}): CommandButtonProps<'list'>; | ||
remove(name: string, payload: { | ||
index: number; | ||
}): CommandButtonProps<'list'>; | ||
reorder(name: string, payload: { | ||
from: number; | ||
to: number; | ||
}): CommandButtonProps<'list'>; | ||
} | ||
/** | ||
* Helpers to configure a command button for modifying a list | ||
* | ||
* @see https://conform.guide/api/react#list | ||
*/ | ||
export declare const list: ListCommandButtonBuilder; |
138
index.js
@@ -48,3 +48,3 @@ 'use strict'; | ||
function shouldValidate(submission, name) { | ||
return submission.type === 'submit' || submission.type === 'validate' && (typeof name === 'undefined' || submission.intent === name); | ||
return submission.type === 'submit' || submission.type === 'validate' && (submission.intent === '' || submission.intent === name); | ||
} | ||
@@ -59,14 +59,17 @@ function hasError(error, name) { | ||
var messageByName = new Map(); | ||
var nameByInput = new Map(); | ||
for (var [name, message] of submission.error) { | ||
if (!messageByName.has(name)) { | ||
for (var [_name, message] of submission.error) { | ||
if (!messageByName.has(_name)) { | ||
// Only keep the first error message (for now) | ||
messageByName.set(name, message); | ||
messageByName.set(_name, message); | ||
// We can't use empty string as button name | ||
// As `form.element.namedItem('')` will always returns null | ||
var elementName = name ? name : '__form__'; | ||
var elementName = _name ? _name : '__form__'; | ||
var item = form.elements.namedItem(elementName); | ||
if (item instanceof RadioNodeList) { | ||
throw new Error('Repeated field name is not supported'); | ||
for (var field of item) { | ||
if (field.type !== 'radio') { | ||
throw new Error('Repeated field name is not supported'); | ||
} | ||
} | ||
} | ||
@@ -82,3 +85,2 @@ if (item === null) { | ||
} | ||
nameByInput.set(item, name); | ||
} | ||
@@ -88,6 +90,9 @@ } | ||
if (isFieldElement(element) && element.willValidate) { | ||
var _nameByInput$get; | ||
var _name = (_nameByInput$get = nameByInput.get(element)) !== null && _nameByInput$get !== void 0 ? _nameByInput$get : element.name; | ||
var _message = messageByName.get(_name); | ||
if (typeof _message !== 'undefined' || shouldValidate(submission, _name)) { | ||
var _elementName = element.name !== '__form__' ? element.name : ''; | ||
var _message = messageByName.get(_elementName); | ||
var elementShouldValidate = shouldValidate(submission, _elementName); | ||
if (elementShouldValidate) { | ||
element.dataset.conformTouched = 'true'; | ||
} | ||
if (typeof _message !== 'undefined' || elementShouldValidate) { | ||
var invalidEvent = new Event('invalid', { | ||
@@ -99,5 +104,7 @@ cancelable: true | ||
} | ||
if (elementShouldValidate && !element.validity.valid) { | ||
focus(element); | ||
} | ||
} | ||
} | ||
focusFirstInvalidField(form); | ||
} | ||
@@ -118,2 +125,8 @@ function setValue(target, paths, valueFn) { | ||
} | ||
/** | ||
* The ponyfill of `HTMLFormElement.requestSubmit()` | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/requestSubmit | ||
* @see https://caniuse.com/?search=requestSubmit | ||
*/ | ||
function requestSubmit(form, submitter) { | ||
@@ -127,12 +140,35 @@ var submitEvent = new SubmitEvent('submit', { | ||
} | ||
function requestValidate(form, field) { | ||
/** | ||
* Creates a command button on demand and trigger a form submit by clicking it. | ||
*/ | ||
function requestCommand(form, buttonProps) { | ||
if (!form) { | ||
console.warn('No form element is provided'); | ||
return; | ||
} | ||
var button = document.createElement('button'); | ||
button.name = 'conform/validate'; | ||
button.value = field !== null && field !== void 0 ? field : ''; | ||
button.formNoValidate = true; | ||
button.name = buttonProps.name; | ||
button.value = buttonProps.value; | ||
button.hidden = true; | ||
if (buttonProps.formNoValidate) { | ||
button.formNoValidate = true; | ||
} | ||
form.appendChild(button); | ||
requestSubmit(form, button); | ||
button.click(); | ||
form.removeChild(button); | ||
} | ||
/** | ||
* Returns the properties required to configure a command button for validation | ||
* | ||
* @see https://conform.guide/api/react#validate | ||
*/ | ||
function validate(field) { | ||
return { | ||
name: 'conform/validate', | ||
value: field !== null && field !== void 0 ? field : '', | ||
formNoValidate: true | ||
}; | ||
} | ||
function getFormElement(element) { | ||
@@ -145,16 +181,8 @@ var form = element instanceof HTMLFormElement ? element : element === null || element === void 0 ? void 0 : element.form; | ||
} | ||
function focusFirstInvalidField(form) { | ||
function focus(field) { | ||
var currentFocus = document.activeElement; | ||
if (!isFieldElement(currentFocus) || currentFocus.tagName !== 'BUTTON' || currentFocus.form !== form) { | ||
if (!isFieldElement(currentFocus) || currentFocus.tagName !== 'BUTTON' || currentFocus.form !== field.form) { | ||
return; | ||
} | ||
for (var field of form.elements) { | ||
if (isFieldElement(field)) { | ||
// Focus on the first non button field | ||
if (!field.validity.valid && field.dataset.conformTouched && field.tagName !== 'BUTTON') { | ||
field.focus(); | ||
break; | ||
} | ||
} | ||
} | ||
field.focus(); | ||
} | ||
@@ -176,4 +204,4 @@ function getSubmissionType(name) { | ||
try { | ||
var _loop = function _loop(name, value) { | ||
var submissionType = getSubmissionType(name); | ||
var _loop = function _loop(value, _name2) { | ||
var submissionType = getSubmissionType(_name2); | ||
if (submissionType) { | ||
@@ -192,3 +220,3 @@ if (typeof value !== 'string') { | ||
} else { | ||
var paths = getPaths(name); | ||
var paths = getPaths(_name2); | ||
setValue(submission.value, paths, prev => { | ||
@@ -205,4 +233,4 @@ if (!prev) { | ||
}; | ||
for (var [name, value] of payload.entries()) { | ||
_loop(name, value); | ||
for (var [_name2, value] of payload.entries()) { | ||
_loop(value, _name2); | ||
} | ||
@@ -222,4 +250,4 @@ switch (submission.type) { | ||
var command = JSON.parse(data); | ||
if (typeof command.type !== 'string' || !['prepend', 'append', 'replace', 'remove', 'reorder'].includes(command.type)) { | ||
throw new Error('Unsupported list command type'); | ||
if (typeof command.type !== 'string' || !['prepend', 'append', 'replace', 'remove', 'reorder', 'combine'].includes(command.type)) { | ||
throw new Error("Unknown list command received: ".concat(command.type)); | ||
} | ||
@@ -267,11 +295,39 @@ return command; | ||
setValue(submission.value, paths, list => { | ||
if (!Array.isArray(list)) { | ||
if (typeof list !== 'undefined' && !Array.isArray(list)) { | ||
throw new Error('The list command can only be applied to a list'); | ||
} | ||
return updateList(list, command); | ||
return updateList(list !== null && list !== void 0 ? list : [], command); | ||
}); | ||
return submission; | ||
} | ||
/** | ||
* Helpers to configure a command button for modifying a list | ||
* | ||
* @see https://conform.guide/api/react#list | ||
*/ | ||
var list = new Proxy({}, { | ||
get(_target, type) { | ||
switch (type) { | ||
case 'append': | ||
case 'prepend': | ||
case 'replace': | ||
case 'remove': | ||
case 'reorder': | ||
return function (scope) { | ||
var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
return { | ||
name: 'conform/list', | ||
value: JSON.stringify({ | ||
type, | ||
scope, | ||
payload | ||
}), | ||
formNoValidate: true | ||
}; | ||
}; | ||
} | ||
} | ||
}); | ||
exports.focusFirstInvalidField = focusFirstInvalidField; | ||
exports.focus = focus; | ||
exports.getFormData = getFormData; | ||
@@ -286,9 +342,11 @@ exports.getFormElement = getFormElement; | ||
exports.isFieldElement = isFieldElement; | ||
exports.list = list; | ||
exports.parse = parse; | ||
exports.parseListCommand = parseListCommand; | ||
exports.reportSubmission = reportSubmission; | ||
exports.requestCommand = requestCommand; | ||
exports.requestSubmit = requestSubmit; | ||
exports.requestValidate = requestValidate; | ||
exports.setValue = setValue; | ||
exports.shouldValidate = shouldValidate; | ||
exports.updateList = updateList; | ||
exports.validate = validate; |
@@ -44,3 +44,3 @@ import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.js'; | ||
function shouldValidate(submission, name) { | ||
return submission.type === 'submit' || submission.type === 'validate' && (typeof name === 'undefined' || submission.intent === name); | ||
return submission.type === 'submit' || submission.type === 'validate' && (submission.intent === '' || submission.intent === name); | ||
} | ||
@@ -55,14 +55,17 @@ function hasError(error, name) { | ||
var messageByName = new Map(); | ||
var nameByInput = new Map(); | ||
for (var [name, message] of submission.error) { | ||
if (!messageByName.has(name)) { | ||
for (var [_name, message] of submission.error) { | ||
if (!messageByName.has(_name)) { | ||
// Only keep the first error message (for now) | ||
messageByName.set(name, message); | ||
messageByName.set(_name, message); | ||
// We can't use empty string as button name | ||
// As `form.element.namedItem('')` will always returns null | ||
var elementName = name ? name : '__form__'; | ||
var elementName = _name ? _name : '__form__'; | ||
var item = form.elements.namedItem(elementName); | ||
if (item instanceof RadioNodeList) { | ||
throw new Error('Repeated field name is not supported'); | ||
for (var field of item) { | ||
if (field.type !== 'radio') { | ||
throw new Error('Repeated field name is not supported'); | ||
} | ||
} | ||
} | ||
@@ -78,3 +81,2 @@ if (item === null) { | ||
} | ||
nameByInput.set(item, name); | ||
} | ||
@@ -84,6 +86,9 @@ } | ||
if (isFieldElement(element) && element.willValidate) { | ||
var _nameByInput$get; | ||
var _name = (_nameByInput$get = nameByInput.get(element)) !== null && _nameByInput$get !== void 0 ? _nameByInput$get : element.name; | ||
var _message = messageByName.get(_name); | ||
if (typeof _message !== 'undefined' || shouldValidate(submission, _name)) { | ||
var _elementName = element.name !== '__form__' ? element.name : ''; | ||
var _message = messageByName.get(_elementName); | ||
var elementShouldValidate = shouldValidate(submission, _elementName); | ||
if (elementShouldValidate) { | ||
element.dataset.conformTouched = 'true'; | ||
} | ||
if (typeof _message !== 'undefined' || elementShouldValidate) { | ||
var invalidEvent = new Event('invalid', { | ||
@@ -95,5 +100,7 @@ cancelable: true | ||
} | ||
if (elementShouldValidate && !element.validity.valid) { | ||
focus(element); | ||
} | ||
} | ||
} | ||
focusFirstInvalidField(form); | ||
} | ||
@@ -114,2 +121,8 @@ function setValue(target, paths, valueFn) { | ||
} | ||
/** | ||
* The ponyfill of `HTMLFormElement.requestSubmit()` | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/requestSubmit | ||
* @see https://caniuse.com/?search=requestSubmit | ||
*/ | ||
function requestSubmit(form, submitter) { | ||
@@ -123,12 +136,35 @@ var submitEvent = new SubmitEvent('submit', { | ||
} | ||
function requestValidate(form, field) { | ||
/** | ||
* Creates a command button on demand and trigger a form submit by clicking it. | ||
*/ | ||
function requestCommand(form, buttonProps) { | ||
if (!form) { | ||
console.warn('No form element is provided'); | ||
return; | ||
} | ||
var button = document.createElement('button'); | ||
button.name = 'conform/validate'; | ||
button.value = field !== null && field !== void 0 ? field : ''; | ||
button.formNoValidate = true; | ||
button.name = buttonProps.name; | ||
button.value = buttonProps.value; | ||
button.hidden = true; | ||
if (buttonProps.formNoValidate) { | ||
button.formNoValidate = true; | ||
} | ||
form.appendChild(button); | ||
requestSubmit(form, button); | ||
button.click(); | ||
form.removeChild(button); | ||
} | ||
/** | ||
* Returns the properties required to configure a command button for validation | ||
* | ||
* @see https://conform.guide/api/react#validate | ||
*/ | ||
function validate(field) { | ||
return { | ||
name: 'conform/validate', | ||
value: field !== null && field !== void 0 ? field : '', | ||
formNoValidate: true | ||
}; | ||
} | ||
function getFormElement(element) { | ||
@@ -141,16 +177,8 @@ var form = element instanceof HTMLFormElement ? element : element === null || element === void 0 ? void 0 : element.form; | ||
} | ||
function focusFirstInvalidField(form) { | ||
function focus(field) { | ||
var currentFocus = document.activeElement; | ||
if (!isFieldElement(currentFocus) || currentFocus.tagName !== 'BUTTON' || currentFocus.form !== form) { | ||
if (!isFieldElement(currentFocus) || currentFocus.tagName !== 'BUTTON' || currentFocus.form !== field.form) { | ||
return; | ||
} | ||
for (var field of form.elements) { | ||
if (isFieldElement(field)) { | ||
// Focus on the first non button field | ||
if (!field.validity.valid && field.dataset.conformTouched && field.tagName !== 'BUTTON') { | ||
field.focus(); | ||
break; | ||
} | ||
} | ||
} | ||
field.focus(); | ||
} | ||
@@ -172,4 +200,4 @@ function getSubmissionType(name) { | ||
try { | ||
var _loop = function _loop(name, value) { | ||
var submissionType = getSubmissionType(name); | ||
var _loop = function _loop(value, _name2) { | ||
var submissionType = getSubmissionType(_name2); | ||
if (submissionType) { | ||
@@ -188,3 +216,3 @@ if (typeof value !== 'string') { | ||
} else { | ||
var paths = getPaths(name); | ||
var paths = getPaths(_name2); | ||
setValue(submission.value, paths, prev => { | ||
@@ -201,4 +229,4 @@ if (!prev) { | ||
}; | ||
for (var [name, value] of payload.entries()) { | ||
_loop(name, value); | ||
for (var [_name2, value] of payload.entries()) { | ||
_loop(value, _name2); | ||
} | ||
@@ -218,4 +246,4 @@ switch (submission.type) { | ||
var command = JSON.parse(data); | ||
if (typeof command.type !== 'string' || !['prepend', 'append', 'replace', 'remove', 'reorder'].includes(command.type)) { | ||
throw new Error('Unsupported list command type'); | ||
if (typeof command.type !== 'string' || !['prepend', 'append', 'replace', 'remove', 'reorder', 'combine'].includes(command.type)) { | ||
throw new Error("Unknown list command received: ".concat(command.type)); | ||
} | ||
@@ -263,10 +291,38 @@ return command; | ||
setValue(submission.value, paths, list => { | ||
if (!Array.isArray(list)) { | ||
if (typeof list !== 'undefined' && !Array.isArray(list)) { | ||
throw new Error('The list command can only be applied to a list'); | ||
} | ||
return updateList(list, command); | ||
return updateList(list !== null && list !== void 0 ? list : [], command); | ||
}); | ||
return submission; | ||
} | ||
/** | ||
* Helpers to configure a command button for modifying a list | ||
* | ||
* @see https://conform.guide/api/react#list | ||
*/ | ||
var list = new Proxy({}, { | ||
get(_target, type) { | ||
switch (type) { | ||
case 'append': | ||
case 'prepend': | ||
case 'replace': | ||
case 'remove': | ||
case 'reorder': | ||
return function (scope) { | ||
var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
return { | ||
name: 'conform/list', | ||
value: JSON.stringify({ | ||
type, | ||
scope, | ||
payload | ||
}), | ||
formNoValidate: true | ||
}; | ||
}; | ||
} | ||
} | ||
}); | ||
export { focusFirstInvalidField, getFormData, getFormElement, getFormElements, getName, getPaths, getSubmissionType, handleList, hasError, isFieldElement, parse, parseListCommand, reportSubmission, requestSubmit, requestValidate, setValue, shouldValidate, updateList }; | ||
export { focus, getFormData, getFormElement, getFormElements, getName, getPaths, getSubmissionType, handleList, hasError, isFieldElement, list, parse, parseListCommand, reportSubmission, requestCommand, requestSubmit, setValue, shouldValidate, updateList, validate }; |
@@ -5,3 +5,3 @@ { | ||
"license": "MIT", | ||
"version": "0.5.0-pre.0", | ||
"version": "0.5.0", | ||
"main": "index.js", | ||
@@ -8,0 +8,0 @@ "module": "module/index.js", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
30911
839