UNPKG

sveltekit-superforms

Version:

Making SvelteKit validation and displaying of forms easier than ever!

226 lines (225 loc) 7.15 kB
import { derived } from 'svelte/store'; import { SuperFormError } from '../index.js'; import { traversePath } from '../traversal.js'; import { splitPath } from '../stringPath.js'; const defaultOptions = { trueStringValue: 'true', dateFormat: 'iso', emptyIfZero: true }; ///// Proxy functions /////////////////////////////////////////////// export function booleanProxy(form, path, options = { trueStringValue: 'true' }) { return _stringProxy(form, path, 'boolean', { ...defaultOptions, ...options }); } export function intProxy(form, path, options = {}) { return _stringProxy(form, path, 'int', { ...defaultOptions, ...options }); } export function numberProxy(form, path, options = {}) { return _stringProxy(form, path, 'number', { ...defaultOptions, ...options }); } export function dateProxy(form, path, options = { format: 'iso' }) { return _stringProxy(form, path, 'date', { ...defaultOptions, dateFormat: options.format, empty: options.empty }); } export function stringProxy(form, path, options) { return _stringProxy(form, path, 'string', { ...defaultOptions, empty: options.empty }); } ///// Implementation //////////////////////////////////////////////// /** * Creates a string store that will pass its value to a field in the form. * @param form The form * @param field Form field * @param type 'number' | 'int' | 'boolean' */ function _stringProxy(form, path, type, options) { function toValue(value) { if (!value && options.empty !== undefined && (value !== 0 || options.emptyIfZero)) { return options.empty === 'null' ? null : undefined; } if (typeof value === 'number') { value = value.toString(); } if (typeof value !== 'string') { throw new SuperFormError('stringProxy received a non-string value.'); } if (type == 'string') return value; else if (type == 'boolean') return !!value; else if (type == 'date') return new Date(value); const numberToConvert = options.delimiter ? value.replace(options.delimiter, '.') : value; let num; if (type == 'number') num = parseFloat(numberToConvert); else num = parseInt(numberToConvert, 10); if (options.empty !== undefined && ((num === 0 && options.emptyIfZero) || isNaN(num))) { return options.empty == 'null' ? null : undefined; } return num; } const proxy2 = fieldProxy(form, path); const proxy = derived(proxy2, (value) => { if (value === undefined || value === null) return ''; if (type == 'string') { return value; } else if (type == 'int' || type == 'number') { const num = value; return isNaN(num) ? '' : String(num); } else if (type == 'date') { const date = value; if (isNaN(date)) return ''; switch (options.dateFormat) { case 'iso': return date.toISOString(); case 'date': return date.toISOString().slice(0, 10); case 'datetime': return date.toISOString().slice(0, 16); case 'time': return date.toISOString().slice(11, 16); case 'date-utc': return UTCDate(date); case 'datetime-utc': return UTCDate(date) + 'T' + UTCTime(date); case 'time-utc': return UTCTime(date); case 'date-local': return localDate(date); case 'datetime-local': return localDate(date) + 'T' + localTime(date); case 'time-local': return localTime(date); } } else { // boolean return value ? options.trueStringValue : ''; } }); return { subscribe: proxy.subscribe, set(val) { proxy2.set(toValue(val)); }, update(updater) { proxy2.update((f) => toValue(updater(String(f)))); } }; } export function formFieldProxy(form, path) { const path2 = splitPath(path); // Filter out array indices, the constraints structure doesn't contain these. const constraintsPath = path2 .filter((p) => isNaN(parseInt(String(p)))) .join('.'); return { path, value: fieldProxy(form.form, path), errors: fieldProxy(form.errors, path), constraints: fieldProxy(form.constraints, constraintsPath) }; } export function fieldProxy(form, path) { const path2 = splitPath(path); const proxy = derived(form, ($form) => { const data = traversePath($form, path2); return data?.value; }); return { subscribe(...params) { //console.log('~ fieldproxy ~ subscribe', path); const unsub = proxy.subscribe(...params); return () => { //console.log('~ fieldproxy ~ unsubscribe', field); unsub(); }; }, //subscribe: proxy.subscribe, update(upd) { //console.log('~ fieldStore ~ update value for', path); form.update((f) => { const output = traversePath(f, path2); if (output) output.parent[output.key] = upd(output.value); //else console.log('[update] Not found:', path, 'in', f); return f; }); }, set(value) { //console.log('~ fieldStore ~ set value for', path, value); form.update((f) => { const output = traversePath(f, path2); if (output) output.parent[output.key] = value; //else console.log('[set] Not found:', path, 'in', f); return f; }); } }; } function localDate(date) { return (date.getFullYear() + '-' + String(date.getMonth() + 1).padStart(2, '0') + '-' + String(date.getDate()).padStart(2, '0')); } function localTime(date) { return (String(date.getHours()).padStart(2, '0') + ':' + String(date.getMinutes()).padStart(2, '0')); } function UTCDate(date) { return (date.getUTCFullYear() + '-' + String(date.getUTCMonth() + 1).padStart(2, '0') + '-' + String(date.getUTCDate()).padStart(2, '0')); } function UTCTime(date) { return (String(date.getUTCHours()).padStart(2, '0') + ':' + String(date.getUTCMinutes()).padStart(2, '0')); } /* function dateToUTC(date: Date) { return new Date( date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds() ); } */