@steveesamson/microform 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ import type { FormOptions, FormAction, FormValues, FormErrors } from './types.js';
2
+ import type { Params } from './internal.svelte.js';
3
+ export declare const formAction: (values: FormValues, errors: FormErrors, unfits: FormErrors, reportDirty: (clean: boolean) => void, options: FormOptions, validationMap: Params) => FormAction;
@@ -1,13 +1,12 @@
1
- import { get } from "svelte/store";
2
- import { useValidator } from "./form-validators.js";
3
- import { getEditableContent } from "./utils.js";
1
+ import { useValidator } from './form-validators.js';
2
+ import { getEditableContent } from './utils.js';
4
3
  const isField = (node) => {
5
- return node instanceof HTMLSelectElement ||
4
+ return (node instanceof HTMLSelectElement ||
6
5
  node instanceof HTMLInputElement ||
7
- node instanceof HTMLTextAreaElement;
6
+ node instanceof HTMLTextAreaElement);
8
7
  };
9
8
  const isExcluded = (node) => {
10
- return node instanceof HTMLInputElement && ['radio', 'checkbox'].includes(node.type.toLowerCase());
9
+ return (node instanceof HTMLInputElement && ['radio', 'checkbox'].includes(node.type.toLowerCase()));
11
10
  };
12
11
  const isCheckbox = (node) => {
13
12
  return node instanceof HTMLInputElement && ['checkbox'].includes(node.type.toLowerCase());
@@ -16,12 +15,11 @@ const isRadio = (node) => {
16
15
  return node instanceof HTMLInputElement && ['radio'].includes(node.type.toLowerCase());
17
16
  };
18
17
  const checkFormFitness = (values, validationMap, validate) => {
19
- const _values = get(values);
20
18
  for (const [name, { validations }] of Object.entries(validationMap)) {
21
- validate({ name, value: _values[name], validations, });
19
+ validate({ name, value: values[name], validations });
22
20
  }
23
21
  };
24
- export const formAction = (values, errors, unfits, isdirty, options, validationMap) => {
22
+ export const formAction = (values, errors, unfits, reportDirty, options, validationMap) => {
25
23
  const { validators: customValidators } = options;
26
24
  const { validate, validators } = useValidator(errors, values);
27
25
  // override
@@ -30,12 +28,13 @@ export const formAction = (values, errors, unfits, isdirty, options, validationM
30
28
  validators[key] = val;
31
29
  }
32
30
  }
31
+ const hasError = (next) => !!next;
33
32
  return (node, eventProps) => {
34
33
  const nodeName = isField(node) ? node.name : '';
35
34
  const { name: dsname = nodeName } = node.dataset || {};
36
- const { name = dsname, validations = [], validateEvent = options.validateEvent = 'blur', html = false } = eventProps || {};
35
+ const { name = dsname, validations = [], validateEvent = (options.validateEvent = 'blur'), html = false } = eventProps || {};
37
36
  validationMap[name] = { validations, html, nodeRef: node };
38
- const storedValue = get(values)[name] || '';
37
+ const storedValue = values[name] || '';
39
38
  let defValue = storedValue;
40
39
  if (isField(node) && !isExcluded(node)) {
41
40
  defValue = node.value || storedValue;
@@ -45,58 +44,50 @@ export const formAction = (values, errors, unfits, isdirty, options, validationM
45
44
  defValue = node.innerHTML || storedValue;
46
45
  node.innerHTML = defValue;
47
46
  }
48
- values.update((data) => {
49
- return { ...data, [name]: defValue };
50
- });
51
- let unsubscribe;
47
+ values[name] = defValue;
48
+ let eventBound = $state(false);
52
49
  const updateNode = (e) => {
53
- if (!unsubscribe) {
54
- unsubscribe = values.subscribe((data) => {
55
- validate({ name, value: data[name], validations, node });
56
- });
50
+ if (!eventBound) {
51
+ eventBound = true;
57
52
  }
58
53
  if (isField(node) && !isExcluded(node)) {
59
54
  const value = e.target.value || '';
60
- values.update((data) => {
61
- return { ...data, [name]: value };
62
- });
55
+ values[name] = value;
63
56
  }
64
57
  else if (node.isContentEditable) {
65
58
  const { value: htm, text } = getEditableContent({ target: node }, html);
66
- values.update((data) => {
67
- return { ...data, [name]: htm, [`${name}-text`]: text };
68
- });
59
+ values[name] = htm;
60
+ values[`${name}-text`] = text;
69
61
  }
70
62
  else if (isCheckbox(node)) {
71
63
  const { checked, value: val } = node;
72
- const { [name]: fieldValue } = get(values);
73
- let current = fieldValue.split(',');
64
+ const fieldValue = String(values[name] ?? '');
65
+ let current = fieldValue?.split(',');
74
66
  if (checked) {
75
67
  current.push(val);
76
68
  }
77
69
  else {
78
70
  current = current.filter((next) => next !== val);
79
71
  }
80
- values.update((data) => {
81
- return { ...data, [name]: [...new Set(current)].join(',') };
82
- });
72
+ values[name] = [...new Set(current)].join(',');
83
73
  }
84
74
  else if (isRadio(node)) {
85
75
  const { value: fvalue } = node;
86
- values.update((data) => {
87
- return { ...data, [name]: fvalue };
88
- });
76
+ values[name] = fvalue;
89
77
  }
90
78
  const { validate: validateUnfit } = useValidator(unfits, values, validators);
91
79
  checkFormFitness(values, validationMap, validateUnfit);
92
- isdirty.set(true);
80
+ validate({ name, value: values[name], validations, node });
81
+ const withErrors = Object.values(errors).some(hasError);
82
+ const withUnfits = Object.values(unfits).some(hasError);
83
+ const clean = !withErrors && !withUnfits;
84
+ reportDirty(clean);
93
85
  };
94
- node.addEventListener(validateEvent, updateNode);
95
- return {
96
- destroy() {
97
- unsubscribe?.();
86
+ $effect(() => {
87
+ node.addEventListener(validateEvent, updateNode);
88
+ return () => {
98
89
  node.removeEventListener(validateEvent, updateNode);
99
- }
100
- };
90
+ };
91
+ });
101
92
  };
102
93
  };
@@ -1,6 +1,4 @@
1
- import { type Writable } from "svelte/store";
2
- import type { ValidateArgs, ValidatorType, ValidatorMap } from "./types.js";
3
- import type { Params } from "./internal.js";
1
+ import type { ValidateArgs, ValidatorType, ValidatorMap, FormErrors, FormValues } from './types.js';
4
2
  export declare const IS_REQUIRED = "required";
5
3
  export declare const IS_EMAIL = "email";
6
4
  export declare const IS_URL = "url";
@@ -16,7 +14,7 @@ export declare const IS_MIN = "min";
16
14
  export declare const IS_MAX = "max";
17
15
  export declare const IT_MATCHES = "match";
18
16
  export declare const IS_FILE_SIZE_MB = "file-size-mb";
19
- export declare const useValidator: (errors: Writable<Params>, values: Writable<Params>, validators?: ValidatorMap<ValidatorType>) => {
17
+ export declare const useValidator: (errors: FormErrors, values: FormValues, validators?: ValidatorMap<ValidatorType>) => {
20
18
  validate: ({ name, value, validations, node }: ValidateArgs) => Promise<void>;
21
19
  validators: ValidatorMap<ValidatorType>;
22
20
  };
@@ -1,5 +1,4 @@
1
- import { get } from "svelte/store";
2
- import { makeName, isValidFileSize } from "./utils.js";
1
+ import { makeName, isValidFileSize } from './utils.js';
3
2
  const regexes = {
4
3
  number: /^[-+]?[0-9]+(\.[0-9]+)?$/g,
5
4
  alpha: /^[A-Z\s]+$/gi,
@@ -7,22 +6,22 @@ const regexes = {
7
6
  integer: /^[-+]?\d+$/g,
8
7
  email: /^[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$/gi,
9
8
  url: /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i,
10
- ip: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/g,
9
+ ip: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/g
11
10
  };
12
- export const IS_REQUIRED = "required";
13
- export const IS_EMAIL = "email";
14
- export const IS_URL = "url";
15
- export const IS_IP = "ip";
16
- export const IS_INTEGER = "integer";
17
- export const IS_NUMBER = "number";
18
- export const IS_ALPHA = "alpha";
19
- export const IS_ALPHANUM = "alphanum";
20
- export const IS_MIN_LEN = "minlen";
21
- export const IS_MAX_LEN = "maxlen";
22
- export const IS_LEN = "len";
23
- export const IS_MIN = "min";
24
- export const IS_MAX = "max";
25
- export const IT_MATCHES = "match";
11
+ export const IS_REQUIRED = 'required';
12
+ export const IS_EMAIL = 'email';
13
+ export const IS_URL = 'url';
14
+ export const IS_IP = 'ip';
15
+ export const IS_INTEGER = 'integer';
16
+ export const IS_NUMBER = 'number';
17
+ export const IS_ALPHA = 'alpha';
18
+ export const IS_ALPHANUM = 'alphanum';
19
+ export const IS_MIN_LEN = 'minlen';
20
+ export const IS_MAX_LEN = 'maxlen';
21
+ export const IS_LEN = 'len';
22
+ export const IS_MIN = 'min';
23
+ export const IS_MAX = 'max';
24
+ export const IT_MATCHES = 'match';
26
25
  export const IS_FILE_SIZE_MB = 'file-size-mb';
27
26
  const getDefaultValidators = () => {
28
27
  const validators = {
@@ -30,78 +29,94 @@ const getDefaultValidators = () => {
30
29
  if (!value || (Array.isArray(value) && !value.length)) {
31
30
  return `${label} is mandatory.`;
32
31
  }
33
- return "";
32
+ return '';
34
33
  },
35
34
  [IS_EMAIL]: ({ value, label }) => {
36
- return (!!value && !value.match(regexes['email'])) ? `${label} should be a valid email.` : '';
35
+ return !!value && !value.match(regexes['email']) ? `${label} should be a valid email.` : '';
37
36
  },
38
37
  [IS_URL]: ({ value, label }) => {
39
- return !!value && !value.match(regexes['url']) ? `${label} should be a valid URL.` : "";
38
+ return !!value && !value.match(regexes['url']) ? `${label} should be a valid URL.` : '';
40
39
  },
41
40
  [IS_IP]: ({ value, label }) => {
42
- return !!value && !value.match(regexes['ip']) ? `${label} should be a valid IP.` : "";
41
+ return !!value && !value.match(regexes['ip']) ? `${label} should be a valid IP.` : '';
43
42
  },
44
43
  [IS_INTEGER]: ({ value, label }) => {
45
- return !!value && !value.match(regexes['integer']) ? `${label} must be an integer number.` : "";
44
+ return !!value && !value.match(regexes['integer'])
45
+ ? `${label} must be an integer number.`
46
+ : '';
46
47
  },
47
48
  [IS_NUMBER]: ({ value, label }) => {
48
- return !!value && !value.match(regexes['number']) ? `${label} must be a number.` : "";
49
+ return !!value && !value.match(regexes['number']) ? `${label} must be a number.` : '';
49
50
  },
50
51
  [IS_ALPHA]: ({ value, label }) => {
51
- return !!value && !value.match(regexes['alpha']) ? `${label} should be a string of alphabets.` : "";
52
+ return !!value && !value.match(regexes['alpha'])
53
+ ? `${label} should be a string of alphabets.`
54
+ : '';
52
55
  },
53
56
  [IS_ALPHANUM]: ({ value, label }) => {
54
- return !!value && !value.match(regexes['alphanum']) ? `${label} should be a string of alphanumerics starting with alphabets.` : "";
57
+ return !!value && !value.match(regexes['alphanum'])
58
+ ? `${label} should be a string of alphanumerics starting with alphabets.`
59
+ : '';
55
60
  },
56
61
  [IS_MIN_LEN]: ({ value, label, parts }) => {
57
- if (!!value) {
62
+ if (value) {
58
63
  if (!parts || parts.length < 2) {
59
64
  return `${label}: min-length validation requires minimum length.`;
60
65
  }
61
66
  const extra = parts[1].trim();
62
- return value.length >= parseInt(parts[1], 10) ? "" : `${label} must be at least ${extra} characters long.`;
67
+ return value.length >= parseInt(parts[1], 10)
68
+ ? ''
69
+ : `${label} must be at least ${extra} characters long.`;
63
70
  }
64
- return "";
71
+ return '';
65
72
  },
66
73
  [IS_MAX_LEN]: ({ value, label, parts }) => {
67
- if (!!value) {
74
+ if (value) {
68
75
  if (!parts || parts.length < 2) {
69
76
  return `${label}: max-length validation requires maximum length.`;
70
77
  }
71
78
  const extra = parts[1].trim();
72
- return value.length <= parseInt(parts[1], 10) ? "" : `${label} must be at most ${extra} characters long.`;
79
+ return value.length <= parseInt(parts[1], 10)
80
+ ? ''
81
+ : `${label} must be at most ${extra} characters long.`;
73
82
  }
74
- return "";
83
+ return '';
75
84
  },
76
85
  [IS_LEN]: ({ value, label, parts }) => {
77
- if (!!value) {
86
+ if (value) {
78
87
  if (!parts || parts.length < 2) {
79
88
  return `${label}: length validation requires length.`;
80
89
  }
81
90
  const extra = parts[1].trim();
82
- return value.length === parseInt(parts[1], 10) ? "" : `${label} must exactly be ${extra} characters long.`;
91
+ return value.length === parseInt(parts[1], 10)
92
+ ? ''
93
+ : `${label} must exactly be ${extra} characters long.`;
83
94
  }
84
- return "";
95
+ return '';
85
96
  },
86
97
  [IS_MAX]: ({ value, label, parts }) => {
87
- if (!!value) {
98
+ if (value) {
88
99
  if (!parts || parts.length < 2) {
89
100
  return `${label}: max validation requires the maximum value.`;
90
101
  }
91
102
  const extra = parts[1].trim();
92
- return parseInt(value, 10) <= parseInt(parts[1], 10) ? "" : `${label} must not be greater than ${extra}.`;
103
+ return parseInt(value, 10) <= parseInt(parts[1], 10)
104
+ ? ''
105
+ : `${label} must not be greater than ${extra}.`;
93
106
  }
94
- return "";
107
+ return '';
95
108
  },
96
109
  [IS_MIN]: ({ value, label, parts }) => {
97
- if (!!value) {
110
+ if (value) {
98
111
  if (!parts || parts.length < 2) {
99
112
  return `${label}: min validation requires the minimum value.`;
100
113
  }
101
114
  const extra = parts[1].trim();
102
- return parseInt(value, 10) >= parseInt(parts[1], 10) ? "" : `${label} must not be less than ${extra}.`;
115
+ return parseInt(value, 10) >= parseInt(parts[1], 10)
116
+ ? ''
117
+ : `${label} must not be less than ${extra}.`;
103
118
  }
104
- return "";
119
+ return '';
105
120
  },
106
121
  [IT_MATCHES]: ({ values, value, label, parts }) => {
107
122
  if (value) {
@@ -110,40 +125,46 @@ const getDefaultValidators = () => {
110
125
  }
111
126
  const partyName = parts[1].trim();
112
127
  const partyValue = values[partyName];
113
- return value === partyValue ? "" : `${label} does not match ${makeName(partyName)}.`;
128
+ return value === partyValue ? '' : `${label} does not match ${makeName(partyName)}.`;
114
129
  }
115
- return "";
130
+ return '';
116
131
  },
117
132
  [IS_FILE_SIZE_MB]: ({ value, node, label, parts }) => {
118
- if (!!value) {
133
+ if (value) {
119
134
  if (!parts || parts.length < 2) {
120
135
  return `${label}: max file size in MB validation requires maximum file size of mb value.`;
121
- ;
122
136
  }
123
137
  const extra = parts[1].trim();
124
138
  return isValidFileSize(node, parseInt(extra, 10));
125
139
  }
126
- return "";
140
+ return '';
127
141
  }
128
142
  };
129
143
  return validators;
130
144
  };
131
145
  export const useValidator = (errors, values, validators = getDefaultValidators()) => {
132
146
  const setError = (name, error) => {
133
- errors.update((prev) => {
134
- return { ...prev, [name]: error };
135
- });
147
+ errors[name] = error ?? '';
136
148
  };
137
149
  return {
138
150
  validate: async ({ name, value, validations = [], node = undefined }) => {
139
151
  if (validations.length) {
140
- const _validations = validations.includes('required') ? ['required', ...validations.filter((val) => val !== 'required')] : [...validations];
152
+ const _validations = validations.includes('required')
153
+ ? ['required', ...validations.filter((val) => val !== 'required')]
154
+ : [...validations];
141
155
  for (let i = 0; i < _validations.length; ++i) {
142
156
  const validation = _validations[i], parts = validation.split(':'), type = parts[0].trim();
143
157
  const validator = validators[type];
144
158
  if (!validator)
145
159
  return;
146
- const error = validator({ name, label: makeName(name), value, values: get(values), node, parts });
160
+ const error = validator({
161
+ name,
162
+ label: makeName(name),
163
+ value,
164
+ values,
165
+ node,
166
+ parts
167
+ });
147
168
  setError(name, error ? error : '');
148
169
  if (error) {
149
170
  break;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from "./types.js";
2
- export { IS_EMAIL, IS_ALPHA, IS_ALPHANUM, IS_INTEGER, IS_IP, IS_LEN, IT_MATCHES as IS_MATCH_FOR, IS_MAX, IS_FILE_SIZE_MB as IS_MAX_FILE_SIZE, IS_MAX_LEN, IS_MIN, IS_MIN_LEN, IS_NUMBER, IS_REQUIRED, IS_URL } from "./form-validators.js";
3
- import microform from "./index.svelte.js";
1
+ export * from './types.js';
2
+ export { IS_EMAIL, IS_ALPHA, IS_ALPHANUM, IS_INTEGER, IS_IP, IS_LEN, IT_MATCHES as IS_MATCH_FOR, IS_MAX, IS_FILE_SIZE_MB as IS_MAX_FILE_SIZE, IS_MAX_LEN, IS_MIN, IS_MIN_LEN, IS_NUMBER, IS_REQUIRED, IS_URL } from './form-validators.js';
3
+ import microform from './index.svelte.js';
4
4
  export default microform;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export * from "./types.js";
2
- export { IS_EMAIL, IS_ALPHA, IS_ALPHANUM, IS_INTEGER, IS_IP, IS_LEN, IT_MATCHES as IS_MATCH_FOR, IS_MAX, IS_FILE_SIZE_MB as IS_MAX_FILE_SIZE, IS_MAX_LEN, IS_MIN, IS_MIN_LEN, IS_NUMBER, IS_REQUIRED, IS_URL } from "./form-validators.js";
3
- import microform from "./index.svelte.js";
1
+ export * from './types.js';
2
+ export { IS_EMAIL, IS_ALPHA, IS_ALPHANUM, IS_INTEGER, IS_IP, IS_LEN, IT_MATCHES as IS_MATCH_FOR, IS_MAX, IS_FILE_SIZE_MB as IS_MAX_FILE_SIZE, IS_MAX_LEN, IS_MIN, IS_MIN_LEN, IS_NUMBER, IS_REQUIRED, IS_URL } from './form-validators.js';
3
+ import microform from './index.svelte.js';
4
4
  export default microform;
@@ -1,36 +1,24 @@
1
- import { writable, derived, get } from 'svelte/store';
2
- import { formAction } from './form-action.js';
3
- import { bindStateToStore } from "./utils.js";
1
+ import { formAction } from './form-action.svelte.js';
2
+ import { formState } from './internal.svelte.js';
4
3
  const microform = (props) => {
5
4
  // form default values
6
5
  const data = props?.data || {};
7
- // form values
8
- const _values = writable({ ...data });
9
- // internal checks
10
- const unfits = writable({});
11
- // external form errors
12
- const _errors = writable({});
13
- const isdirty = writable(false);
14
- const isclean = derived(([_errors, unfits]), ([$errors, $unfits]) => {
15
- const errVals = Object.values($errors);
16
- const unfitVals = Object.values($unfits);
17
- return (errVals.length === 0 || errVals.reduce((comm, next) => comm && !next, true))
18
- && (unfitVals.length === 0 || unfitVals.reduce((comm, next) => comm && !next, true));
19
- });
20
- const _valid = derived(([isclean, isdirty]), ([$isclean, $isdirty]) => {
21
- return $isclean && $isdirty;
22
- });
6
+ // form state
7
+ const state = formState(data);
23
8
  const validationMap = {};
24
9
  const { options = {
25
10
  validateEvent: 'blur',
26
11
  validators: {}
27
12
  } } = props || {};
28
- const form = formAction(_values, _errors, unfits, isdirty, options, validationMap);
13
+ const updateSanity = (isOk) => {
14
+ state.sanity.ok = isOk;
15
+ };
16
+ const form = formAction(state.values, state.errors, state.unfits, updateSanity, options, validationMap);
29
17
  const handleSubmit = (e, handler) => {
30
18
  e.preventDefault();
31
- if (!get(_valid))
19
+ if (!state.sanity.ok)
32
20
  return;
33
- handler({ ...get(_values) });
21
+ handler({ ...state.values });
34
22
  };
35
23
  const onsubmit = (handler) => {
36
24
  const onSubmit = async (e) => {
@@ -44,37 +32,37 @@ const microform = (props) => {
44
32
  });
45
33
  };
46
34
  const reset = () => {
47
- _errors.set({});
48
- unfits.set({});
49
- _values.set({ ...data });
50
- isdirty.set(false);
35
+ const defaultKeys = Object.keys({ ...data });
36
+ for (const [key,] of Object.entries(state.values)) {
37
+ if (defaultKeys.includes(key)) {
38
+ state.values[key] = data[key];
39
+ }
40
+ else {
41
+ delete state.values[key];
42
+ }
43
+ }
44
+ state.errors = {};
45
+ state.unfits = {};
46
+ state.sanity.ok = false;
51
47
  for (const [name, { nodeRef, html }] of Object.entries(validationMap).filter(([, { nodeRef }]) => !!nodeRef)) {
52
48
  if (nodeRef) {
53
49
  if (nodeRef.isContentEditable) {
54
- nodeRef[html ? "innerHTML" : 'textContent'] = data[name] || '';
50
+ nodeRef[html ? 'innerHTML' : 'textContent'] = data[name] || '';
55
51
  }
56
52
  else {
57
- nodeRef["value"] = data[name] || '';
53
+ nodeRef['value'] = data[name] || '';
58
54
  }
59
55
  }
60
56
  }
61
57
  };
62
- const values = $state({ ...data });
63
- const errors = $state({});
64
- const sanity = $state({ ok: get(_valid) });
65
- bindStateToStore(values, _values);
66
- bindStateToStore(errors, _errors);
67
- _valid.subscribe((changes) => {
68
- sanity.ok = changes;
69
- });
70
58
  return {
71
- values,
72
- errors,
73
- sanity,
59
+ values: state.values,
60
+ errors: state.errors,
61
+ sanity: state.sanity,
74
62
  form,
75
63
  submit,
76
64
  onsubmit,
77
- reset,
65
+ reset
78
66
  };
79
67
  };
80
68
  export default microform;
@@ -0,0 +1,12 @@
1
+ import type { FormValues, FormErrors, FormSanity } from "./types.js";
2
+ export type Params<T = any> = {
3
+ [key: string | number | symbol]: T;
4
+ };
5
+ export type Primitive = string | number | boolean | string[] | number[] | boolean[];
6
+ export type FormState = {
7
+ values: FormValues;
8
+ errors: FormErrors;
9
+ sanity: FormSanity;
10
+ unfits: FormErrors;
11
+ };
12
+ export declare const formState: (data: Params) => FormState;
@@ -0,0 +1,15 @@
1
+ export const formState = (data) => {
2
+ // form values
3
+ const values = $state({ ...data });
4
+ // external form errors
5
+ const errors = $state({});
6
+ const sanity = $state({ ok: false });
7
+ // internal checks
8
+ const unfits = $state({});
9
+ return {
10
+ values,
11
+ errors,
12
+ sanity,
13
+ unfits,
14
+ };
15
+ };
package/dist/types.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { type Writable } from "svelte/store";
2
- import type { Params } from "./internal.js";
1
+ import type { Params } from './internal.svelte.js';
3
2
  export type Validator = `max:${number}` | `min:${number}` | `len:${number}` | `minlen:${number}` | `maxlen:${number}` | `file-size-mb:${number}` | `match:${string}` | 'required' | 'email' | 'integer' | 'number' | 'alpha' | 'alphanum' | 'url' | 'ip';
4
3
  export type ValidatorKey = 'required' | 'email' | 'integer' | 'number' | 'alpha' | 'alphanum' | 'url' | 'ip' | `max` | `min` | `len` | `minlen` | `maxlen` | `file-size-mb` | `match`;
5
4
  export type FieldProps = {
@@ -26,10 +25,10 @@ export interface ValidateArgs {
26
25
  export type FormReturn = {
27
26
  destroy: () => void;
28
27
  };
29
- export type ValidateEvent = 'input' | 'change' | 'keyup' | 'blur';
28
+ export type ValidateEvent = 'input' | 'change' | 'keyup' | 'blur' | 'keydown';
30
29
  export type FormValues = Params;
31
30
  export type FormErrors = Params;
32
- export type Dirty = Writable<boolean>;
31
+ export type Dirty = boolean;
33
32
  export type ActionOptions = {
34
33
  validateEvent?: ValidateEvent;
35
34
  name?: string;
@@ -37,7 +36,7 @@ export type ActionOptions = {
37
36
  node?: HTMLElement;
38
37
  html?: boolean;
39
38
  };
40
- export type FormAction = (node: HTMLElement, eventProps?: ActionOptions) => FormReturn;
39
+ export type FormAction = (node: HTMLElement, eventProps?: ActionOptions) => void;
41
40
  export type FormSubmitEvent = SubmitEvent & {
42
41
  currentTarget: EventTarget & HTMLFormElement;
43
42
  };
@@ -57,7 +56,7 @@ export type MicroFormReturn = {
57
56
  values: FormValues;
58
57
  errors: FormErrors;
59
58
  sanity: FormSanity;
60
- form: (node: HTMLElement, eventProps?: ActionOptions) => FormReturn;
59
+ form: (node: HTMLElement, eventProps?: ActionOptions) => void;
61
60
  submit: (formNode: HTMLFormElement, handler: FormSubmit) => void;
62
61
  onsubmit: (handler: FormSubmit) => (e: Event) => Promise<void>;
63
62
  reset: () => void;
package/dist/types.js CHANGED
@@ -1 +1 @@
1
- import {} from "svelte/store";
1
+ export {};
package/dist/utils.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- import type { Writable } from "svelte/store";
2
- import type { Params } from "./internal.js";
3
1
  type TEvent = {
4
2
  target: HTMLElement;
5
3
  };
@@ -9,5 +7,4 @@ export declare const getEditableContent: (e: TEvent, isHtml: boolean) => {
9
7
  };
10
8
  export declare const makeName: (str: string) => string;
11
9
  export declare const isValidFileSize: (node: HTMLInputElement | undefined, maxFileSizeInMB: number) => string;
12
- export declare const bindStateToStore: (state: Params, store: Writable<Params>) => void;
13
10
  export {};
package/dist/utils.js CHANGED
@@ -1,21 +1,20 @@
1
1
  export const getEditableContent = (e, isHtml) => {
2
2
  const el = e.target;
3
- const text = el.textContent?.trim() || "";
3
+ const text = el.textContent?.trim() || '';
4
4
  if (!isHtml) {
5
5
  return { text, value: text };
6
6
  }
7
+ if (!text) {
8
+ return { value: '', text };
9
+ }
7
10
  let htm = el.innerHTML;
8
11
  htm = htm.trim();
9
- htm = htm.replace(/<br>/g, "");
10
- htm = htm.replace(/<div><\/div>/g, "");
11
- htm = htm.replace(/<p><\/p>/g, "");
12
- htm = htm.replace(/<div>/g, "<p>");
13
- htm = htm.replace(/<\/div>/g, "</p>");
14
- htm = htm.trim()
15
- ? htm.indexOf("<p>") === -1
16
- ? `<p>${htm}</p>`
17
- : htm
18
- : htm;
12
+ htm = htm.replace(/<br>/g, '');
13
+ htm = htm.replace(/<div><\/div>/g, '');
14
+ htm = htm.replace(/<p><\/p>/g, '');
15
+ htm = htm.replace(/<div>/g, '<p>');
16
+ htm = htm.replace(/<\/div>/g, '</p>');
17
+ htm = htm.trim() ? (htm.indexOf('<p>') === -1 ? `<p>${htm}</p>` : htm) : htm;
19
18
  return { value: htm.trim(), text };
20
19
  };
21
20
  export const makeName = function (str) {
@@ -44,10 +43,10 @@ export const isValidFileSize = (node, maxFileSizeInMB) => {
44
43
  }
45
44
  return '';
46
45
  };
47
- export const bindStateToStore = (state, store) => {
48
- store.subscribe((changes) => {
49
- for (const [k, v] of Object.entries(changes)) {
50
- state[k] = v;
51
- }
52
- });
53
- };
46
+ // export const bindStateToStore = (state: Params, store: Writable<Params>): void => {
47
+ // store.subscribe((changes: Params) => {
48
+ // for (const [k, v] of Object.entries(changes)) {
49
+ // state[k] = v;
50
+ // }
51
+ // });
52
+ // };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steveesamson/microform",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "postbuild": "touch ./docs/.nojekyll",
@@ -1,4 +0,0 @@
1
- import { type Writable } from "svelte/store";
2
- import type { FormOptions, FormAction } from "./types.js";
3
- import type { Params } from "./internal.js";
4
- export declare const formAction: (values: Writable<Params>, errors: Writable<Params>, unfits: Writable<Params>, isdirty: Writable<boolean>, options: FormOptions, validationMap: Params) => FormAction;
@@ -1,4 +0,0 @@
1
- export type Params<T = any> = {
2
- [key: string | number | symbol]: T;
3
- };
4
- export type Primitive = string | number | boolean | string[] | number[] | boolean[];
package/dist/internal.js DELETED
@@ -1 +0,0 @@
1
- export {};