@ponchopay/pp-browser 1.2.0 → 1.3.0

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.
@@ -12,6 +12,8 @@ export declare abstract class PpForm extends HTMLElement {
12
12
  disconnectedCallback(): void;
13
13
  attributeChangedCallback(): void;
14
14
  submit(): void;
15
+ private validateField;
16
+ checkArrayIsValid(attribute: string, field: Field): boolean;
15
17
  checkValidity(): boolean;
16
18
  static get observedAttributes(): string[];
17
19
  }
@@ -1,4 +1,4 @@
1
- import { formatName, joinPaths, split } from './utils.js';
1
+ import { createInputsForField, joinPaths } from './utils.js';
2
2
  import { mandatory, single } from './validation.js';
3
3
  const DEFAULT_BASE = 'https://pay.ponchopay.com/';
4
4
  export class PpForm extends HTMLElement {
@@ -22,22 +22,23 @@ export class PpForm extends HTMLElement {
22
22
  const base = this.getAttributeWithFallback('base', DEFAULT_BASE);
23
23
  form.action = joinPaths(base, this.path);
24
24
  form.replaceChildren();
25
- Object.entries(this.fields).forEach(([attribute, { collection }]) => {
25
+ Object.entries(this.fields).forEach(([attribute, field]) => {
26
+ if (field.type === 'array') {
27
+ const matchingAttributes = Array.from(this.attributes).filter(attr => attr.name.startsWith(`${attribute}.`));
28
+ matchingAttributes.forEach(attr => {
29
+ var _a;
30
+ const parts = attr.name.split('.');
31
+ const fieldName = parts[parts.length - 1];
32
+ const subField = (_a = field.schema) === null || _a === void 0 ? void 0 : _a[fieldName];
33
+ if (!subField)
34
+ return;
35
+ createInputsForField(form, attr.name, attr.value, subField.type);
36
+ });
37
+ return;
38
+ }
26
39
  if (this.hasAttribute(attribute)) {
27
40
  const attr = this.getAttribute(attribute);
28
- let name = formatName(attribute);
29
- let values = attr.length > 0 ? [attr] : [];
30
- if (collection) {
31
- name = `${name}[]`;
32
- values = split(attr, ',');
33
- }
34
- values.forEach(value => {
35
- const input = document.createElement('input');
36
- input.type = 'hidden';
37
- input.name = name;
38
- input.value = value;
39
- form.appendChild(input);
40
- });
41
+ createInputsForField(form, attribute, attr, field.type);
41
42
  }
42
43
  });
43
44
  const slot = document.createElement('slot');
@@ -121,19 +122,49 @@ export class PpForm extends HTMLElement {
121
122
  var _a;
122
123
  (_a = this.shadowRoot.querySelector('form')) === null || _a === void 0 ? void 0 : _a.requestSubmit();
123
124
  }
124
- checkValidity() {
125
- return Object.entries(this.fields).every(([attribute, { required }]) => {
126
- if (required === true) {
127
- return this.isAttributeSet(attribute);
128
- }
129
- if (Array.isArray(required)) {
130
- const [dependant, value] = required;
131
- if ((value === true || this.getAttribute(dependant) === value) &&
132
- this.isAttributeSet(dependant)) {
133
- return this.isAttributeSet(attribute);
134
- }
125
+ validateField(fieldPath, field) {
126
+ if (field.required === true) {
127
+ return this.isAttributeSet(fieldPath);
128
+ }
129
+ if (Array.isArray(field.required)) {
130
+ const [dependant, value] = field.required;
131
+ if ((value === true || this.getAttribute(dependant) === value) &&
132
+ this.isAttributeSet(dependant)) {
133
+ return this.isAttributeSet(fieldPath);
135
134
  }
135
+ }
136
+ return true;
137
+ }
138
+ checkArrayIsValid(attribute, field) {
139
+ const matchingAttributes = Array.from(this.attributes).filter(attr => attr.name.startsWith(`${attribute}.`));
140
+ const indexes = matchingAttributes.map(attr => {
141
+ const match = attr.name.match(new RegExp(`^${attribute}\\.(\\d+)\\.`));
142
+ return match ? parseInt(match[1], 10) : null;
143
+ });
144
+ const uniqueIndexes = Array.from(new Set(indexes));
145
+ // If array is required and no items exist fail validation
146
+ if (field.required === true && uniqueIndexes.length === 0) {
147
+ return false;
148
+ }
149
+ // If no items exist but array is optional, validation passes
150
+ if (uniqueIndexes.length === 0) {
136
151
  return true;
152
+ }
153
+ return uniqueIndexes.every(index => Object.entries(field.schema).every(([key, subField]) => {
154
+ const fullPath = `${attribute}.${index}.${key}`;
155
+ return this.validateField(fullPath, subField);
156
+ }));
157
+ }
158
+ checkValidity() {
159
+ return Object.entries(this.fields).every(([attribute, { type, schema, required }]) => {
160
+ if (type === 'array') {
161
+ return this.checkArrayIsValid(attribute, {
162
+ type,
163
+ schema,
164
+ required,
165
+ });
166
+ }
167
+ return this.validateField(attribute, { type, required, schema });
137
168
  });
138
169
  }
139
170
  static get observedAttributes() {
@@ -1 +1 @@
1
- {"version":3,"file":"PpForm.js","sourceRoot":"","sources":["../../src/PpForm.ts"],"names":[],"mappings":"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","sourcesContent":["import { formatName, joinPaths, split } from './utils.js';\nimport { Field, mandatory, single } from './validation.js';\n\nconst DEFAULT_BASE = 'https://pay.ponchopay.com/';\n\nexport abstract class PpForm extends HTMLElement {\n private readonly path: string;\n private readonly label: string;\n private readonly fields: Record<string, Field>;\n\n protected constructor(\n path: string,\n label: string,\n fields: Record<string, Field>\n ) {\n super();\n this.path = path;\n this.label = label;\n this.fields = { ...fields, token: mandatory(single()) };\n this.attachShadow({ mode: 'open' });\n }\n\n private getAttributeWithFallback(name: string, fallback: string): string {\n return this.getAttribute(name) ?? fallback;\n }\n\n private isAttributeSet(name: string): boolean {\n return this.getAttributeWithFallback(name, '').length > 0;\n }\n\n private syncAttributes(): void {\n const form = this.shadowRoot!.querySelector('form');\n if (form) {\n const base = this.getAttributeWithFallback('base', DEFAULT_BASE);\n\n form.action = joinPaths(base, this.path);\n form.replaceChildren();\n\n Object.entries(this.fields).forEach(([attribute, { collection }]) => {\n if (this.hasAttribute(attribute)) {\n const attr = this.getAttribute(attribute)!;\n\n let name = formatName(attribute);\n let values = attr.length > 0 ? [attr] : [];\n if (collection) {\n name = `${name}[]`;\n values = split(attr, ',');\n }\n\n values.forEach(value => {\n const input = document.createElement('input');\n input.type = 'hidden';\n input.name = name;\n input.value = value;\n form.appendChild(input);\n });\n }\n });\n\n const slot = document.createElement('slot');\n slot.innerHTML = this.label;\n\n const button = document.createElement('button');\n button.setAttribute('part', 'button');\n button.type = 'submit';\n button.appendChild(slot);\n form.appendChild(button);\n }\n }\n\n private showErrorMessage(): void {\n const form = this.shadowRoot!.querySelector('form');\n if (form) {\n form.querySelector('div')?.remove();\n\n const div = document.createElement('div');\n div.setAttribute('part', 'error');\n div.className = 'error';\n div.innerHTML = 'Some attributes are incorrect. Please, review them.';\n form.appendChild(div);\n }\n }\n\n public connectedCallback(): void {\n const base = this.getAttributeWithFallback('base', DEFAULT_BASE);\n\n const style = document.createElement('style');\n style.textContent = `\n form {\n display: contents;\n }\n\n button {\n width: 100%;\n background-color: #02C2A0;\n white-space: nowrap;\n text-decoration-line: none;\n border-radius: .25rem;\n box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\n text-align: center;\n font-size: 1.25rem;\n line-height: 1.75rem;\n color: white;\n margin-top: .5rem;\n margin-bottom: .75rem;\n padding: .75rem 1rem;\n display: flex;\n flex-wrap: nowrap;\n align-items: center;\n justify-content: space-evenly;\n cursor: pointer;\n text-transform: none;\n box-sizing: border-box;\n border-width: 0;\n border-style: solid;\n border-color: currentColor;\n }\n\n div.error {\n color: #EB3B50;\n font-size: 0.875rem;\n }`;\n\n const form = document.createElement('form');\n form.action = joinPaths(base, this.path);\n form.method = 'post';\n form.onsubmit = event => {\n if (!this.checkValidity()) {\n event.preventDefault();\n this.showErrorMessage();\n }\n };\n this.shadowRoot!.append(style, form);\n\n this.syncAttributes();\n }\n\n public disconnectedCallback(): void {\n this.shadowRoot!.replaceChildren();\n }\n\n public attributeChangedCallback(): void {\n this.syncAttributes();\n }\n\n public submit(): void {\n this.shadowRoot!.querySelector('form')?.requestSubmit();\n }\n\n public checkValidity(): boolean {\n return Object.entries(this.fields).every(([attribute, { required }]) => {\n if (required === true) {\n return this.isAttributeSet(attribute);\n }\n\n if (Array.isArray(required)) {\n const [dependant, value] = required;\n\n if (\n (value === true || this.getAttribute(dependant) === value) &&\n this.isAttributeSet(dependant)\n ) {\n return this.isAttributeSet(attribute);\n }\n }\n\n return true;\n });\n }\n\n public static get observedAttributes() {\n return ['base', 'token'];\n }\n}\n"]}
1
+ {"version":3,"file":"PpForm.js","sourceRoot":"","sources":["../../src/PpForm.ts"],"names":[],"mappings":"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","sourcesContent":["import { createInputsForField, joinPaths } from './utils.js';\nimport { Field, mandatory, single } from './validation.js';\n\nconst DEFAULT_BASE = 'https://pay.ponchopay.com/';\n\nexport abstract class PpForm extends HTMLElement {\n private readonly path: string;\n private readonly label: string;\n private readonly fields: Record<string, Field>;\n\n protected constructor(\n path: string,\n label: string,\n fields: Record<string, Field>\n ) {\n super();\n this.path = path;\n this.label = label;\n this.fields = { ...fields, token: mandatory(single()) };\n this.attachShadow({ mode: 'open' });\n }\n\n private getAttributeWithFallback(name: string, fallback: string): string {\n return this.getAttribute(name) ?? fallback;\n }\n\n private isAttributeSet(name: string): boolean {\n return this.getAttributeWithFallback(name, '').length > 0;\n }\n\n private syncAttributes(): void {\n const form = this.shadowRoot!.querySelector('form');\n if (form) {\n const base = this.getAttributeWithFallback('base', DEFAULT_BASE);\n\n form.action = joinPaths(base, this.path);\n form.replaceChildren();\n\n Object.entries(this.fields).forEach(([attribute, field]) => {\n if (field.type === 'array') {\n const matchingAttributes = Array.from(this.attributes).filter(attr =>\n attr.name.startsWith(`${attribute}.`)\n );\n\n matchingAttributes.forEach(attr => {\n const parts = attr.name.split('.');\n const fieldName = parts[parts.length - 1];\n\n const subField = field.schema?.[fieldName];\n if (!subField) return;\n\n createInputsForField(form, attr.name, attr.value, subField.type);\n });\n\n return;\n }\n\n if (this.hasAttribute(attribute)) {\n const attr = this.getAttribute(attribute)!;\n createInputsForField(form, attribute, attr, field.type);\n }\n });\n\n const slot = document.createElement('slot');\n slot.innerHTML = this.label;\n\n const button = document.createElement('button');\n button.setAttribute('part', 'button');\n button.type = 'submit';\n button.appendChild(slot);\n form.appendChild(button);\n }\n }\n\n private showErrorMessage(): void {\n const form = this.shadowRoot!.querySelector('form');\n if (form) {\n form.querySelector('div')?.remove();\n\n const div = document.createElement('div');\n div.setAttribute('part', 'error');\n div.className = 'error';\n div.innerHTML = 'Some attributes are incorrect. Please, review them.';\n form.appendChild(div);\n }\n }\n\n public connectedCallback(): void {\n const base = this.getAttributeWithFallback('base', DEFAULT_BASE);\n\n const style = document.createElement('style');\n style.textContent = `\n form {\n display: contents;\n }\n\n button {\n width: 100%;\n background-color: #02C2A0;\n white-space: nowrap;\n text-decoration-line: none;\n border-radius: .25rem;\n box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\n text-align: center;\n font-size: 1.25rem;\n line-height: 1.75rem;\n color: white;\n margin-top: .5rem;\n margin-bottom: .75rem;\n padding: .75rem 1rem;\n display: flex;\n flex-wrap: nowrap;\n align-items: center;\n justify-content: space-evenly;\n cursor: pointer;\n text-transform: none;\n box-sizing: border-box;\n border-width: 0;\n border-style: solid;\n border-color: currentColor;\n }\n\n div.error {\n color: #EB3B50;\n font-size: 0.875rem;\n }`;\n\n const form = document.createElement('form');\n form.action = joinPaths(base, this.path);\n form.method = 'post';\n form.onsubmit = event => {\n if (!this.checkValidity()) {\n event.preventDefault();\n this.showErrorMessage();\n }\n };\n this.shadowRoot!.append(style, form);\n\n this.syncAttributes();\n }\n\n public disconnectedCallback(): void {\n this.shadowRoot!.replaceChildren();\n }\n\n public attributeChangedCallback(): void {\n this.syncAttributes();\n }\n\n public submit(): void {\n this.shadowRoot!.querySelector('form')?.requestSubmit();\n }\n\n private validateField(fieldPath: string, field: Field): boolean {\n if (field.required === true) {\n return this.isAttributeSet(fieldPath);\n }\n\n if (Array.isArray(field.required)) {\n const [dependant, value] = field.required;\n\n if (\n (value === true || this.getAttribute(dependant) === value) &&\n this.isAttributeSet(dependant)\n ) {\n return this.isAttributeSet(fieldPath);\n }\n }\n\n return true;\n }\n\n public checkArrayIsValid(attribute: string, field: Field): boolean {\n const matchingAttributes = Array.from(this.attributes).filter(attr =>\n attr.name.startsWith(`${attribute}.`)\n );\n\n const indexes = matchingAttributes.map(attr => {\n const match = attr.name.match(new RegExp(`^${attribute}\\\\.(\\\\d+)\\\\.`));\n return match ? parseInt(match[1], 10) : null;\n });\n\n const uniqueIndexes = Array.from(new Set(indexes));\n\n // If array is required and no items exist fail validation\n if (field.required === true && uniqueIndexes.length === 0) {\n return false;\n }\n\n // If no items exist but array is optional, validation passes\n if (uniqueIndexes.length === 0) {\n return true;\n }\n\n return uniqueIndexes.every(index =>\n Object.entries(field.schema!).every(([key, subField]) => {\n const fullPath = `${attribute}.${index}.${key}`;\n return this.validateField(fullPath, subField);\n })\n );\n }\n\n public checkValidity(): boolean {\n return Object.entries(this.fields).every(\n ([attribute, { type, schema, required }]) => {\n if (type === 'array') {\n return this.checkArrayIsValid(attribute, {\n type,\n schema,\n required,\n });\n }\n\n return this.validateField(attribute, { type, required, schema });\n }\n );\n }\n\n public static get observedAttributes() {\n return ['base', 'token'];\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { PpForm } from './PpForm.js';
2
- import { mandatory, optional, single } from './validation.js';
2
+ import { array, mandatory, optional, single } from './validation.js';
3
3
  const fields = {
4
4
  amount: mandatory(single()),
5
5
  metadata: mandatory(single()),
@@ -8,6 +8,11 @@ const fields = {
8
8
  note: optional(single()),
9
9
  expiry: optional(single()),
10
10
  'constraints.minimum_card_amount': optional(single()),
11
+ line_items: optional(array({
12
+ quantity: mandatory(single()),
13
+ amount: mandatory(single()),
14
+ description: mandatory(single()),
15
+ })),
11
16
  };
12
17
  export class PpPayment extends PpForm {
13
18
  constructor() {
@@ -1 +1 @@
1
- {"version":3,"file":"PpPayment.js","sourceRoot":"","sources":["../../src/PpPayment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9D,MAAM,MAAM,GAAG;IACb,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;IAC3B,QAAQ,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;IAC7B,GAAG,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;IACxB,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;IAC1B,IAAI,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IAC1B,iCAAiC,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;CACtD,CAAC;AAEF,MAAM,OAAO,SAAU,SAAQ,MAAM;IACnC;QACE,KAAK,CAAC,mCAAmC,EAAE,oBAAoB,EAAE,MAAM,CAAC,CAAC;IAC3E,CAAC;IAEM,MAAM,KAAK,kBAAkB;QAClC,OAAO,CAAC,GAAG,MAAM,CAAC,kBAAkB,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAChE,CAAC;CACF","sourcesContent":["import { PpForm } from './PpForm.js';\nimport { mandatory, optional, single } from './validation.js';\n\nconst fields = {\n amount: mandatory(single()),\n metadata: mandatory(single()),\n urn: mandatory(single()),\n email: mandatory(single()),\n note: optional(single()),\n expiry: optional(single()),\n 'constraints.minimum_card_amount': optional(single()),\n};\n\nexport class PpPayment extends PpForm {\n public constructor() {\n super('/api/integration/generic/initiate', 'Pay with PonchoPay', fields);\n }\n\n public static get observedAttributes() {\n return [...PpForm.observedAttributes, ...Object.keys(fields)];\n }\n}\n"]}
1
+ {"version":3,"file":"PpPayment.js","sourceRoot":"","sources":["../../src/PpPayment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAErE,MAAM,MAAM,GAAG;IACb,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;IAC3B,QAAQ,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;IAC7B,GAAG,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;IACxB,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;IAC1B,IAAI,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IAC1B,iCAAiC,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IACrD,UAAU,EAAE,QAAQ,CAClB,KAAK,CAAC;QACJ,QAAQ,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;QAC7B,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;QAC3B,WAAW,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;KACjC,CAAC,CACH;CACF,CAAC;AAEF,MAAM,OAAO,SAAU,SAAQ,MAAM;IACnC;QACE,KAAK,CAAC,mCAAmC,EAAE,oBAAoB,EAAE,MAAM,CAAC,CAAC;IAC3E,CAAC;IAEM,MAAM,KAAK,kBAAkB;QAClC,OAAO,CAAC,GAAG,MAAM,CAAC,kBAAkB,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAChE,CAAC;CACF","sourcesContent":["import { PpForm } from './PpForm.js';\nimport { array, mandatory, optional, single } from './validation.js';\n\nconst fields = {\n amount: mandatory(single()),\n metadata: mandatory(single()),\n urn: mandatory(single()),\n email: mandatory(single()),\n note: optional(single()),\n expiry: optional(single()),\n 'constraints.minimum_card_amount': optional(single()),\n line_items: optional(\n array({\n quantity: mandatory(single()),\n amount: mandatory(single()),\n description: mandatory(single()),\n })\n ),\n};\n\nexport class PpPayment extends PpForm {\n public constructor() {\n super('/api/integration/generic/initiate', 'Pay with PonchoPay', fields);\n }\n\n public static get observedAttributes() {\n return [...PpForm.observedAttributes, ...Object.keys(fields)];\n }\n}\n"]}
@@ -11,3 +11,7 @@ export declare function formatName(name: string): string;
11
11
  * Splits a haystack by the needle. If the haystack is an empty string, it will return an empty array
12
12
  */
13
13
  export declare function split(haystack: string, needle: string): string[];
14
+ /**
15
+ * Creates hidden input elements for a field and appends them to the form
16
+ */
17
+ export declare function createInputsForField(form: HTMLFormElement, attributeName: string, attributeValue: string, fieldType: 'single' | 'multiple' | 'array'): void;
package/dist/src/utils.js CHANGED
@@ -38,4 +38,22 @@ export function split(haystack, needle) {
38
38
  }
39
39
  return haystack.split(needle);
40
40
  }
41
+ /**
42
+ * Creates hidden input elements for a field and appends them to the form
43
+ */
44
+ export function createInputsForField(form, attributeName, attributeValue, fieldType) {
45
+ let name = formatName(attributeName);
46
+ let values = attributeValue.length > 0 ? [attributeValue] : [];
47
+ if (fieldType === 'multiple') {
48
+ name = `${name}[]`;
49
+ values = split(attributeValue, ',');
50
+ }
51
+ values.forEach(value => {
52
+ const input = document.createElement('input');
53
+ input.type = 'hidden';
54
+ input.name = name;
55
+ input.value = value;
56
+ form.appendChild(input);
57
+ });
58
+ }
41
59
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,SAAS,SAAS,CAAC,QAAgB,EAAE,MAAc;IACjD,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC;IAC1C,OAAO,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,SAAS,OAAO,CAAC,QAAgB,EAAE,MAAc;IAC/C,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,MAAM,KAAK,CAAC,CAAC;IAC1C,OAAO,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,KAAa;IACnD,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,UAAU,GAAG,EAAE,CAAC;IACpB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;QACnB,UAAU,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;KACrC;IAED,OAAO,GAAG,IAAI,GAAG,UAAU,EAAE,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,KAAK,CAAC,QAAgB,EAAE,MAAc;IACpD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;QACzB,OAAO,EAAE,CAAC;KACX;IAED,OAAO,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC","sourcesContent":["export type Maybe<T> = T | undefined;\n\n/**\n * Clears the start of the string from any matching character\n */\nfunction trimStart(haystack: string, needle: string): string {\n const regex = new RegExp(`^[${needle}]+`);\n return haystack.replace(regex, '');\n}\n\n/**\n * Clears the end of the string from any matching character\n */\nfunction trimEnd(haystack: string, needle: string): string {\n const regex = new RegExp(`[${needle}]+$`);\n return haystack.replace(regex, '');\n}\n\n/**\n * Joins two paths with a single forward slash in the middle\n */\nexport function joinPaths(left: string, right: string): string {\n return `${trimEnd(left, '/')}/${trimStart(right, '/')}`;\n}\n\n/**\n * Prepares the property name for the API's format\n */\nexport function formatName(name: string): string {\n const [root, ...path] = name.split('.');\n let properties = '';\n if (path.length > 0) {\n properties = `[${path.join('][')}]`;\n }\n\n return `${root}${properties}`;\n}\n\n/**\n * Splits a haystack by the needle. If the haystack is an empty string, it will return an empty array\n */\nexport function split(haystack: string, needle: string): string[] {\n if (haystack.length === 0) {\n return [];\n }\n\n return haystack.split(needle);\n}\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,SAAS,SAAS,CAAC,QAAgB,EAAE,MAAc;IACjD,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC;IAC1C,OAAO,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,SAAS,OAAO,CAAC,QAAgB,EAAE,MAAc;IAC/C,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,MAAM,KAAK,CAAC,CAAC;IAC1C,OAAO,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,KAAa;IACnD,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,UAAU,GAAG,EAAE,CAAC;IACpB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;QACnB,UAAU,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;KACrC;IAED,OAAO,GAAG,IAAI,GAAG,UAAU,EAAE,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,KAAK,CAAC,QAAgB,EAAE,MAAc;IACpD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;QACzB,OAAO,EAAE,CAAC;KACX;IAED,OAAO,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAClC,IAAqB,EACrB,aAAqB,EACrB,cAAsB,EACtB,SAA0C;IAE1C,IAAI,IAAI,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC;IACrC,IAAI,MAAM,GAAG,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE/D,IAAI,SAAS,KAAK,UAAU,EAAE;QAC5B,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC;QACnB,MAAM,GAAG,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;KACrC;IAED,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;QACrB,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC9C,KAAK,CAAC,IAAI,GAAG,QAAQ,CAAC;QACtB,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;QAClB,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["export type Maybe<T> = T | undefined;\n\n/**\n * Clears the start of the string from any matching character\n */\nfunction trimStart(haystack: string, needle: string): string {\n const regex = new RegExp(`^[${needle}]+`);\n return haystack.replace(regex, '');\n}\n\n/**\n * Clears the end of the string from any matching character\n */\nfunction trimEnd(haystack: string, needle: string): string {\n const regex = new RegExp(`[${needle}]+$`);\n return haystack.replace(regex, '');\n}\n\n/**\n * Joins two paths with a single forward slash in the middle\n */\nexport function joinPaths(left: string, right: string): string {\n return `${trimEnd(left, '/')}/${trimStart(right, '/')}`;\n}\n\n/**\n * Prepares the property name for the API's format\n */\nexport function formatName(name: string): string {\n const [root, ...path] = name.split('.');\n let properties = '';\n if (path.length > 0) {\n properties = `[${path.join('][')}]`;\n }\n\n return `${root}${properties}`;\n}\n\n/**\n * Splits a haystack by the needle. If the haystack is an empty string, it will return an empty array\n */\nexport function split(haystack: string, needle: string): string[] {\n if (haystack.length === 0) {\n return [];\n }\n\n return haystack.split(needle);\n}\n\n/**\n * Creates hidden input elements for a field and appends them to the form\n */\nexport function createInputsForField(\n form: HTMLFormElement,\n attributeName: string,\n attributeValue: string,\n fieldType: 'single' | 'multiple' | 'array'\n): void {\n let name = formatName(attributeName);\n let values = attributeValue.length > 0 ? [attributeValue] : [];\n\n if (fieldType === 'multiple') {\n name = `${name}[]`;\n values = split(attributeValue, ',');\n }\n\n values.forEach(value => {\n const input = document.createElement('input');\n input.type = 'hidden';\n input.name = name;\n input.value = value;\n form.appendChild(input);\n });\n}\n"]}
@@ -10,8 +10,9 @@
10
10
  * is a string, it also needs to match that value. Optional otherwise.
11
11
  */
12
12
  export type Field = {
13
- collection: boolean;
13
+ type: 'single' | 'multiple' | 'array';
14
14
  required: boolean | [string, string | true];
15
+ schema?: Record<string, Field>;
15
16
  };
16
17
  /**
17
18
  * Marks the attribute as representing a single value.
@@ -35,3 +36,4 @@ export declare function optional(attribute: Field): Field;
35
36
  * Marks the attribute's requirement as dependant on another attribute's existance.
36
37
  */
37
38
  export declare function dependant(name: string, value: string | true, attribute: Field): Field;
39
+ export declare function array(itemSchema: Record<string, Field>): Field;
@@ -3,14 +3,14 @@
3
3
  * Note: By default it makes the attribute optional. Use the other functions to change this.
4
4
  */
5
5
  export function single() {
6
- return { required: false, collection: false };
6
+ return { type: 'single', required: false };
7
7
  }
8
8
  /**
9
9
  * Marks the attribute as representing multiple values.
10
10
  * Note: By default it makes the attribute optional. Use the other functions to change this.
11
11
  */
12
12
  export function multiple() {
13
- return { required: false, collection: true };
13
+ return { type: 'multiple', required: false };
14
14
  }
15
15
  /**
16
16
  * Marks the attribute as mandatory.
@@ -30,4 +30,7 @@ export function optional(attribute) {
30
30
  export function dependant(name, value, attribute) {
31
31
  return { ...attribute, required: [name, value] };
32
32
  }
33
+ export function array(itemSchema) {
34
+ return { type: 'array', required: false, schema: itemSchema };
35
+ }
33
36
  //# sourceMappingURL=validation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/validation.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AACH,MAAM,UAAU,MAAM;IACpB,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AAChD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ;IACtB,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;AAC/C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,SAAgB;IACxC,OAAO,EAAE,GAAG,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAC1C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,SAAgB;IACvC,OAAO,EAAE,GAAG,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CACvB,IAAY,EACZ,KAAoB,EACpB,SAAgB;IAEhB,OAAO,EAAE,GAAG,SAAS,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;AACnD,CAAC","sourcesContent":["/**\n * Collection: Whether the attribute is expected to have comma-separated values ot nor.\n * - false: Single\n * - true: Multiple\n *\n * Required: Whether the attribute is required to submit the form or not.\n * - false: Optional\n * - true: Mandatory\n * - [string, string | true]: Mandatory if the attribute in the first index is set. If the second\n * is a string, it also needs to match that value. Optional otherwise.\n */\nexport type Field = {\n collection: boolean;\n required: boolean | [string, string | true];\n};\n\n/**\n * Marks the attribute as representing a single value.\n * Note: By default it makes the attribute optional. Use the other functions to change this.\n */\nexport function single(): Field {\n return { required: false, collection: false };\n}\n\n/**\n * Marks the attribute as representing multiple values.\n * Note: By default it makes the attribute optional. Use the other functions to change this.\n */\nexport function multiple(): Field {\n return { required: false, collection: true };\n}\n\n/**\n * Marks the attribute as mandatory.\n */\nexport function mandatory(attribute: Field): Field {\n return { ...attribute, required: true };\n}\n\n/**\n * Marks the attribute as optional.\n */\nexport function optional(attribute: Field): Field {\n return { ...attribute, required: false };\n}\n\n/**\n * Marks the attribute's requirement as dependant on another attribute's existance.\n */\nexport function dependant(\n name: string,\n value: string | true,\n attribute: Field\n): Field {\n return { ...attribute, required: [name, value] };\n}\n"]}
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/validation.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,MAAM,UAAU,MAAM;IACpB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAC7C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ;IACtB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAC/C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,SAAgB;IACxC,OAAO,EAAE,GAAG,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAC1C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,SAAgB;IACvC,OAAO,EAAE,GAAG,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CACvB,IAAY,EACZ,KAAoB,EACpB,SAAgB;IAEhB,OAAO,EAAE,GAAG,SAAS,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,UAAiC;IACrD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAChE,CAAC","sourcesContent":["/**\n * Collection: Whether the attribute is expected to have comma-separated values ot nor.\n * - false: Single\n * - true: Multiple\n *\n * Required: Whether the attribute is required to submit the form or not.\n * - false: Optional\n * - true: Mandatory\n * - [string, string | true]: Mandatory if the attribute in the first index is set. If the second\n * is a string, it also needs to match that value. Optional otherwise.\n */\nexport type Field = {\n type: 'single' | 'multiple' | 'array';\n required: boolean | [string, string | true];\n schema?: Record<string, Field>;\n};\n\n/**\n * Marks the attribute as representing a single value.\n * Note: By default it makes the attribute optional. Use the other functions to change this.\n */\nexport function single(): Field {\n return { type: 'single', required: false };\n}\n\n/**\n * Marks the attribute as representing multiple values.\n * Note: By default it makes the attribute optional. Use the other functions to change this.\n */\nexport function multiple(): Field {\n return { type: 'multiple', required: false };\n}\n\n/**\n * Marks the attribute as mandatory.\n */\nexport function mandatory(attribute: Field): Field {\n return { ...attribute, required: true };\n}\n\n/**\n * Marks the attribute as optional.\n */\nexport function optional(attribute: Field): Field {\n return { ...attribute, required: false };\n}\n\n/**\n * Marks the attribute's requirement as dependant on another attribute's existance.\n */\nexport function dependant(\n name: string,\n value: string | true,\n attribute: Field\n): Field {\n return { ...attribute, required: [name, value] };\n}\n\nexport function array(itemSchema: Record<string, Field>): Field {\n return { type: 'array', required: false, schema: itemSchema };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ponchopay/pp-browser",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Tools to integrate PonchoPay on the browser",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",