@astro-utils/forms
Version:
Server component for Astro (call server functions from client side with validation and state management)
144 lines (143 loc) • 5.24 kB
JavaScript
import { getFormValue } from '../form-tools/post.js';
import AboutFormName from './form-utils/about-form-name.js';
import { parseCheckbox, parseColor, parseDate, parseEmail, parseEmptyFiles, parseFiles, parseJSON, parseNumber, parseURL } from './form-utils/parse.js';
import { validateFunc, validateRequire, validateStringPatters } from './form-utils/validate.js';
import { getProperty } from 'dot-prop';
import { ZodType } from 'zod';
const OK_NOT_STRING_VALUE = ['checkbox', 'file'];
const OK_INPUT_VALUE_NULL = ['checkbox'];
const DAY_IN_MS = 86400000;
export async function getInputValue(astro, bindId, bind) {
const { value, name, readonly } = astro.props;
if (readonly) {
return getProperty(bind, name, value);
}
return await getFormValue(astro.request, bindId + name);
}
export async function validateFormInput(astro, bind, bindId) {
const { type, value: originalValue, minlength, maxlength, pattern, required, name, errorMessage, validate } = astro.props;
const parseValue = await getInputValue(astro, bindId, bind);
const aboutInput = new AboutFormName(bind, name, parseValue, errorMessage);
if (!OK_INPUT_VALUE_NULL.includes(type) && !validateRequire(aboutInput, required)) {
if (type === 'file') {
parseEmptyFiles(aboutInput, astro);
}
aboutInput.setValue();
return;
}
const checkStringPatterns = originalValue != null && !OK_NOT_STRING_VALUE.includes(type);
if (checkStringPatterns && !validateStringPatters(aboutInput, minlength, maxlength, pattern)) {
return;
}
await validateByInputType(astro, aboutInput, bind);
if (!aboutInput.hadError) {
if (typeof validate == 'function') {
await validateFunc(aboutInput, validate);
}
else if (validate instanceof ZodType) {
aboutInput.catchParse(validate);
}
}
aboutInput.setValue();
}
async function validateByInputType(astro, aboutInput, bind) {
const { type, min, max, value: originalValue, multiple, readonly } = astro.props;
switch (type) {
case 'checkbox':
parseCheckbox(aboutInput, originalValue);
break;
case 'color':
parseColor(aboutInput);
break;
case 'date':
case 'datetime-local':
case 'month':
case 'week':
case 'time':
parseDate(aboutInput, type, min, max);
break;
case 'email':
parseEmail(aboutInput);
break;
case 'number':
case 'range':
case 'int':
parseNumber(aboutInput, type, min, max);
break;
case 'radio':
const plugin = bind.getPlugin('HTMLInputRadioPlugin');
plugin.addNewValue(aboutInput, originalValue);
break;
case 'url':
parseURL(aboutInput);
break;
case 'json':
parseJSON(aboutInput);
break;
case 'file':
await parseFiles(aboutInput, astro, multiple, readonly);
break;
}
}
function toDateTimeLocal(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day}T${hours}:${minutes}`;
}
function stringifyCustomValue(date, type) {
if (typeof date === 'string' || !date) {
return date;
}
switch (type) {
case 'date':
return toDateTimeLocal(date).slice(0, 10);
case 'datetime-local':
return toDateTimeLocal(date).slice(0, 16);
case 'time':
return date.toTimeString().slice(0, 5);
case 'month':
return toDateTimeLocal(date).slice(0, 7);
case 'week':
return formatToDateWeek(date);
case 'json':
return JSON.stringify(date);
}
return date;
}
export function inputReturnValueAttr(astro, bind) {
const value = stringifyCustomValue(getProperty(bind, astro.props.name, astro.props.value), astro.props.type);
const min = stringifyCustomValue(astro.props.min, astro.props.type);
const max = stringifyCustomValue(astro.props.max, astro.props.type);
switch (astro.props.type) {
case 'checkbox':
return { checked: value ?? astro.props.checked };
case 'file':
return {};
}
return { value, min, max };
}
function formatToDateWeek(date) {
const year = date.getFullYear();
const firstDayOfYear = new Date(year, 0, 1);
const daysSinceStartOfYear = (date.getTime() - firstDayOfYear.getTime()) / DAY_IN_MS;
const weekNumber = Math.ceil((daysSinceStartOfYear + firstDayOfYear.getDay() + 1) / 7);
return `${year}-W${weekNumber.toString().padStart(2, '0')}`;
}
export function caseTypes(type) {
if (type == 'int') {
return {
type: 'number',
pattern: '\\d+',
step: '1'
};
}
else if (type == 'json') {
return {
type: 'text'
};
}
return { type };
}