@lockerverse/react 0.2.163 → 0.2.165

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.
package/MIGRATIONS.md CHANGED
@@ -36,6 +36,20 @@ If it cannot be found, report that limit and use the baseline review below. Do n
36
36
  claim that every historical migration was applied. Read this file even if the
37
37
  version already matches: a previous upgrade may have missed host adoption steps.
38
38
 
39
+ ## 0.2.165 — multi-select custom fields
40
+
41
+ - **Affected:** React signup and payment forms with `multi_select` custom fields.
42
+ The field now opens a dropdown with multiple choices instead of a native listbox.
43
+ Accessible dropdown support adds Base UI Select to the signup bundle. The packed
44
+ signup consumer measures 111.80 kB brotli, about 32 kB above its former 80 kB
45
+ budget; hosts should account for this increase in initial JavaScript.
46
+ - **Required:** upgrade the matching SDK/React packages and deploy the host with
47
+ its existing signup or payment stylesheet. No host code or saved field changes
48
+ are required. Selected option IDs remain arrays; single-select fields are unchanged.
49
+ - **Check:** open the dropdown, select and remove multiple options, and confirm
50
+ keyboard navigation, Escape, and required-field feedback. Do not submit a live
51
+ signup or payment for this check.
52
+
39
53
  ## Existing integration baseline through 0.2.161
40
54
 
41
55
  This guide starts with the current integration baseline. It is not a complete
@@ -1 +1 @@
1
- {"version":3,"file":"custom-fields.d.ts","names":[],"sources":["../src/custom-fields.tsx"],"mappings":";;KAqBY,oBAAoB;iBAEhB,wBACd,SAAS,oBACT,gBAAgB,4BAAyB;;iBAW3B,wBACd,QAAQ,0BACR,gBAAe,oBACd;iBAca,2BACd,QAAQ,0BACR,QAAQ,oBAAiB;iBA2DX,wBACd,QAAQ,0BACR,QAAQ,oBACP;iBAgBa,qBACd,QAAQ,0BACR,QAAQ,oBAAiB;iBA6BX,eACd,iBACA,gBACA,QACA,QACA,UACA,UACA;EAEA;EACA;EACA,SAAS;EACT,QAAQ;EACR;EACA,WAAW,QAAQ;EACnB,QAAQ;oBACT,IAAA;KAeW;EACV;EACA;EACA;EACA,OAAO;EACP;EACA,WAAW;EACX;;iBAGc,cACd,iBACA,mBACG,SACF,mCAAgB,IAAA"}
1
+ {"version":3,"file":"custom-fields.d.ts","names":[],"sources":["../src/custom-fields.tsx"],"mappings":";;KAsBY,oBAAoB;iBAEhB,wBACd,SAAS,oBACT,gBAAgB,4BAAyB;;iBAW3B,wBACd,QAAQ,0BACR,gBAAe,oBACd;iBAca,2BACd,QAAQ,0BACR,QAAQ,oBAAiB;iBA2DX,wBACd,QAAQ,0BACR,QAAQ,oBACP;iBAgBa,qBACd,QAAQ,0BACR,QAAQ,oBAAiB;iBA6BX,eACd,iBACA,gBACA,QACA,QACA,UACA,UACA;EAEA;EACA;EACA,SAAS;EACT,QAAQ;EACR;EACA,WAAW,QAAQ;EACnB,QAAQ;oBACT,IAAA;KAeW;EACV;EACA;EACA;EACA,OAAO;EACP;EACA,WAAW;EACX;;iBAGc,cACd,iBACA,mBACG,SACF,mCAAgB,IAAA"}
@@ -1,2 +1,2 @@
1
- import{f as e,m as t,p as n,u as r}from"./phone-field.js";import{useId as i}from"react";import{jsx as a,jsxs as o}from"react/jsx-runtime";import{array as s,minLength as c,nonEmpty as l,pipe as u,safeParse as d,string as f,trim as p}from"valibot";function m(e,t){let n=new Set(t.map(e=>e.productId));return e.widget.customFields.filter(e=>!e.paymentOptionIds?.length||e.paymentOptionIds.some(e=>n.has(e)))}function h(e,t={}){return Object.fromEntries(e.map(e=>[e.id,_(e,Object.hasOwn(t,e.id)?t[e.id]:void 0)]))}function g(e,t){let n={};for(let r of e){let e=t[r.id];n[r.id]=_(r,e)}return Object.keys(t).length===e.length&&e.every(e=>{let r=t[e.id],i=n[e.id];return Object.hasOwn(t,e.id)&&v(r,i)})?t:n}function _(e,t){if(e.type===`checkbox`)return t===!0;if(e.type===`checkboxes`||e.type===`multi_select`){let n=new Set(e.options.map(e=>e.id));return Array.isArray(t)?t.filter(e=>n.has(e)):[]}let n=y(t);return e.type===`radio`||e.type===`select`?e.options.some(e=>e.id===n)?n:``:n}function v(e,t){return Array.isArray(e)&&Array.isArray(t)?e.length===t.length&&e.every((e,n)=>e===t[n]):e===t}function y(e){let t=d(f(),e);return t.success?t.output:``}function b(e,t){return e.map(e=>{let n=t[e.id];return e.type===`checkbox`?{fieldId:e.id,value:n===!0}:e.type===`checkboxes`||e.type===`multi_select`?{fieldId:e.id,value:Array.isArray(n)?n:[]}:{fieldId:e.id,value:y(n)}})}function x(e,t){let n={};for(let r of e){if(!r.required)continue;let e=`${r.label} is required.`,i=t[r.id];if(r.type===`checkbox`){i!==!0&&(n[r.id]=e);continue}let a=r.type===`checkboxes`||r.type===`multi_select`?d(u(s(f()),c(1,e)),i??[]):d(u(f(),p(),l(e)),y(i));a.success||(n[r.id]=a.issues[0]?.message??e)}return n}function S({choiceClassName:e=`lockerverse-payment__check-row`,errorClassName:t=`lockerverse-payment__field-message`,errors:n={},fields:r,idPrefix:i,onChange:o,values:s}){return r.map(r=>a(C,{choiceClassName:e,error:n[r.id],errorClassName:t,field:r,idPrefix:i,onChange:e=>o({...s,[r.id]:e}),value:s[r.id]},r.id))}function C({choiceClassName:e=`lockerverse-payment__check-row`,errorClassName:t=`lockerverse-payment__field-message`,...n}){let r=i(),{field:o,idPrefix:s=`lockerverse-${r}`}=n,c=`${s}-custom-${o.id}`,l=`${c}-error`,u={...n,choiceClassName:e,controlId:c,errorClassName:t,errorId:l};switch(o.type){case`checkbox`:return a(w,{...u,field:o});case`text`:case`textarea`:return a(T,{...u,field:o});case`select`:case`multi_select`:return a(E,{...u,field:o});case`radio`:case`checkboxes`:return a(D,{...u,field:o});default:return null}}function w({choiceClassName:t,controlId:n,error:i,errorClassName:s,errorId:c,field:l,onChange:u,value:d}){return o(`label`,{className:t,htmlFor:n,children:[a(r,{"aria-describedby":i?c:void 0,"aria-invalid":i?`true`:void 0,checked:d===!0,id:n,name:`custom:${l.id}`,onChange:e=>u(e.currentTarget.checked),required:l.required}),a(`span`,{children:l.label}),a(e,{className:s,error:i,id:c})]})}function T({controlId:e,error:r,errorId:i,field:o,onChange:s,value:c}){let l={"aria-describedby":r?i:void 0,"aria-invalid":r?`true`:void 0,id:e,name:`custom:${o.id}`,onChange:e=>s(e.currentTarget.value),required:o.required,value:y(c)};return o.type===`textarea`?a(n,{error:r,errorId:i,id:e,label:o.label,children:a(`textarea`,{...l,className:`lockerverse-form__control`})}):a(t,{...l,error:r,errorId:i,label:o.label})}function E({controlId:e,error:t,errorId:r,field:i,onChange:s,value:c}){return a(n,{error:t,errorId:r,id:e,label:i.label,children:o(`select`,{"aria-describedby":t?r:void 0,"aria-invalid":t?`true`:void 0,className:`lockerverse-form__control`,id:e,multiple:i.type===`multi_select`,name:`custom:${i.id}`,onChange:e=>s(i.type===`multi_select`?[...e.currentTarget.selectedOptions].map(e=>e.value):e.currentTarget.value),required:i.required,value:c===!0||c===!1?``:c??(i.type===`multi_select`?[]:``),children:[i.type===`select`?a(`option`,{value:``,children:`Select…`}):null,i.options.map(e=>a(`option`,{value:e.id,children:e.label},e.id))]})})}function D({choiceClassName:t,controlId:n,error:r,errorClassName:i,errorId:s,field:c,onChange:l,value:u}){let d=Array.isArray(u)?u:[];return o(`fieldset`,{"aria-describedby":r?s:void 0,"aria-invalid":r?`true`:void 0,children:[a(`legend`,{children:c.label}),c.options.map((e,r)=>o(`label`,{className:t,children:[a(`input`,{checked:Array.isArray(u)?u.includes(e.id):u===e.id,className:c.type===`checkboxes`?`lockerverse-form__checkbox`:void 0,id:`${n}-${r}`,name:`custom:${c.id}`,onChange:t=>{if(c.type===`radio`){l(e.id);return}l(t.currentTarget.checked?[...d,e.id]:d.filter(t=>t!==e.id))},required:c.required&&c.type===`radio`,type:c.type===`radio`?`radio`:`checkbox`,value:e.id}),a(`span`,{children:e.label})]},e.id)),a(e,{className:i,error:r,id:s})]})}export{m as a,h as i,S as n,g as o,b as r,x as s,C as t};
1
+ import{f as e,m as t,p as n,u as r}from"./phone-field.js";import{useId as i}from"react";import{jsx as a,jsxs as o}from"react/jsx-runtime";import{Select as s}from"@base-ui/react/select";import{array as c,minLength as l,nonEmpty as u,pipe as d,safeParse as f,string as p,trim as m}from"valibot";function h(e,t){let n=new Set(t.map(e=>e.productId));return e.widget.customFields.filter(e=>!e.paymentOptionIds?.length||e.paymentOptionIds.some(e=>n.has(e)))}function g(e,t={}){return Object.fromEntries(e.map(e=>[e.id,v(e,Object.hasOwn(t,e.id)?t[e.id]:void 0)]))}function _(e,t){let n={};for(let r of e){let e=t[r.id];n[r.id]=v(r,e)}return Object.keys(t).length===e.length&&e.every(e=>{let r=t[e.id],i=n[e.id];return Object.hasOwn(t,e.id)&&y(r,i)})?t:n}function v(e,t){if(e.type===`checkbox`)return t===!0;if(e.type===`checkboxes`||e.type===`multi_select`){let n=new Set(e.options.map(e=>e.id));return Array.isArray(t)?t.filter(e=>n.has(e)):[]}let n=b(t);return e.type===`radio`||e.type===`select`?e.options.some(e=>e.id===n)?n:``:n}function y(e,t){return Array.isArray(e)&&Array.isArray(t)?e.length===t.length&&e.every((e,n)=>e===t[n]):e===t}function b(e){let t=f(p(),e);return t.success?t.output:``}function x(e,t){return e.map(e=>{let n=t[e.id];return e.type===`checkbox`?{fieldId:e.id,value:n===!0}:e.type===`checkboxes`||e.type===`multi_select`?{fieldId:e.id,value:Array.isArray(n)?n:[]}:{fieldId:e.id,value:b(n)}})}function S(e,t){let n={};for(let r of e){if(!r.required)continue;let e=`${r.label} is required.`,i=t[r.id];if(r.type===`checkbox`){i!==!0&&(n[r.id]=e);continue}let a=r.type===`checkboxes`||r.type===`multi_select`?f(d(c(p()),l(1,e)),i??[]):f(d(p(),m(),u(e)),b(i));a.success||(n[r.id]=a.issues[0]?.message??e)}return n}function C({choiceClassName:e=`lockerverse-payment__check-row`,errorClassName:t=`lockerverse-payment__field-message`,errors:n={},fields:r,idPrefix:i,onChange:o,values:s}){return r.map(r=>a(w,{choiceClassName:e,error:n[r.id],errorClassName:t,field:r,idPrefix:i,onChange:e=>o({...s,[r.id]:e}),value:s[r.id]},r.id))}function w({choiceClassName:e=`lockerverse-payment__check-row`,errorClassName:t=`lockerverse-payment__field-message`,...n}){let r=i(),{field:o,idPrefix:s=`lockerverse-${r}`}=n,c=`${s}-custom-${o.id}`,l=`${c}-error`,u={...n,choiceClassName:e,controlId:c,errorClassName:t,errorId:l};switch(o.type){case`checkbox`:return a(T,{...u,field:o});case`text`:case`textarea`:return a(E,{...u,field:o});case`select`:return a(O,{...u,field:o});case`multi_select`:return a(D,{...u,field:o});case`radio`:case`checkboxes`:return a(k,{...u,field:o});default:return null}}function T({choiceClassName:t,controlId:n,error:i,errorClassName:s,errorId:c,field:l,onChange:u,value:d}){return o(`label`,{className:t,htmlFor:n,children:[a(r,{"aria-describedby":i?c:void 0,"aria-invalid":i?`true`:void 0,checked:d===!0,id:n,name:`custom:${l.id}`,onChange:e=>u(e.currentTarget.checked),required:l.required}),a(`span`,{children:l.label}),a(e,{className:s,error:i,id:c})]})}function E({controlId:e,error:r,errorId:i,field:o,onChange:s,value:c}){let l={"aria-describedby":r?i:void 0,"aria-invalid":r?`true`:void 0,id:e,name:`custom:${o.id}`,onChange:e=>s(e.currentTarget.value),required:o.required,value:b(c)};return o.type===`textarea`?a(n,{error:r,errorId:i,id:e,label:o.label,children:a(`textarea`,{...l,className:`lockerverse-form__control`})}):a(t,{...l,error:r,errorId:i,label:o.label})}function D({controlId:e,error:t,errorId:r,field:i,onChange:c,value:l}){return a(n,{error:t,errorId:r,id:e,label:i.label,children:o(s.Root,{id:e,items:i.options.map(e=>({label:e.label,value:e.id})),modal:!1,multiple:!0,name:`custom:${i.id}`,onValueChange:c,required:i.required,value:Array.isArray(l)?l:[],children:[o(s.Trigger,{"aria-describedby":t?r:void 0,"aria-invalid":t?`true`:void 0,className:`lockerverse-form__control lockerverse-multi-select__trigger`,id:e,name:`custom:${i.id}`,type:`button`,children:[a(s.Value,{placeholder:`Select…`}),a(s.Icon,{className:`lockerverse-multi-select__icon`,children:null})]}),a(s.Positioner,{align:`start`,alignItemWithTrigger:!1,className:`lockerverse-multi-select__positioner`,sideOffset:6,children:a(s.Popup,{className:`lockerverse-multi-select__popup`,children:a(s.List,{className:`lockerverse-multi-select__list`,children:i.options.map(e=>o(s.Item,{className:`lockerverse-multi-select__item`,value:e.id,children:[a(`span`,{"aria-hidden":`true`,className:`lockerverse-multi-select__check`,children:a(s.ItemIndicator,{children:`✓`})}),a(s.ItemText,{children:e.label})]},e.id))})})})]})})}function O({controlId:e,error:t,errorId:r,field:i,onChange:s,value:c}){return a(n,{error:t,errorId:r,id:e,label:i.label,children:o(`select`,{"aria-describedby":t?r:void 0,"aria-invalid":t?`true`:void 0,className:`lockerverse-form__control`,id:e,name:`custom:${i.id}`,onChange:e=>s(e.currentTarget.value),required:i.required,value:b(c),children:[a(`option`,{value:``,children:`Select…`}),i.options.map(e=>a(`option`,{value:e.id,children:e.label},e.id))]})})}function k({choiceClassName:t,controlId:n,error:r,errorClassName:i,errorId:s,field:c,onChange:l,value:u}){let d=Array.isArray(u)?u:[];return o(`fieldset`,{"aria-describedby":r?s:void 0,"aria-invalid":r?`true`:void 0,children:[a(`legend`,{children:c.label}),c.options.map((e,r)=>o(`label`,{className:t,children:[a(`input`,{checked:Array.isArray(u)?u.includes(e.id):u===e.id,className:c.type===`checkboxes`?`lockerverse-form__checkbox`:void 0,id:`${n}-${r}`,name:`custom:${c.id}`,onChange:t=>{if(c.type===`radio`){l(e.id);return}l(t.currentTarget.checked?[...d,e.id]:d.filter(t=>t!==e.id))},required:c.required&&c.type===`radio`,type:c.type===`radio`?`radio`:`checkbox`,value:e.id}),a(`span`,{children:e.label})]},e.id)),a(e,{className:i,error:r,id:s})]})}export{h as a,g as i,C as n,_ as o,x as r,S as s,w as t};
2
2
  //# sourceMappingURL=custom-fields.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"custom-fields.js","names":[],"sources":["../src/custom-fields.tsx"],"sourcesContent":["import type {\n LockerverseBooleanCustomField,\n LockerverseCatalog,\n LockerverseCheckoutItem,\n LockerverseCustomField,\n LockerverseCustomFieldAnswer,\n LockerverseMultiOptionCustomField,\n LockerverseSingleOptionCustomField,\n} from \"@lockerverse/sdk/checkout\";\nimport { useId } from \"react\";\nimport {\n array,\n minLength,\n nonEmpty,\n pipe,\n safeParse,\n string,\n trim,\n} from \"valibot\";\nimport { Checkbox, FieldError, FormField, TextField } from \"./form-field.tsx\";\n\nexport type CustomFieldValues = Record<string, boolean | string | string[]>;\n\nexport function getCheckoutCustomFields(\n catalog: LockerverseCatalog,\n items: readonly LockerverseCheckoutItem[]\n) {\n const selectedOptionIds = new Set(items.map((item) => item.productId));\n return catalog.widget.customFields.filter(\n (field) =>\n !field.paymentOptionIds?.length ||\n field.paymentOptionIds.some((id) => selectedOptionIds.has(id))\n );\n}\n\n/** Normalize initial values against configured fields; unknown fields and options are discarded. */\nexport function createCustomFieldValues(\n fields: LockerverseCustomField[],\n initialValues: CustomFieldValues = {}\n): CustomFieldValues {\n return Object.fromEntries(\n fields.map((field) => [\n field.id,\n reconcileCustomFieldValue(\n field,\n Object.hasOwn(initialValues, field.id)\n ? initialValues[field.id]\n : undefined\n ),\n ])\n );\n}\n\nexport function reconcileCustomFieldValues(\n fields: LockerverseCustomField[],\n values: CustomFieldValues\n) {\n const next: CustomFieldValues = {};\n for (const field of fields) {\n const value = values[field.id];\n next[field.id] = reconcileCustomFieldValue(field, value);\n }\n const oldKeys = Object.keys(values);\n if (\n oldKeys.length === fields.length &&\n fields.every((field) => {\n const before = values[field.id];\n const after = next[field.id];\n return (\n Object.hasOwn(values, field.id) && sameCustomFieldValue(before, after)\n );\n })\n ) {\n return values;\n }\n return next;\n}\n\nfunction reconcileCustomFieldValue(\n field: LockerverseCustomField,\n value: CustomFieldValues[string] | undefined\n) {\n if (field.type === \"checkbox\") {\n return value === true;\n }\n if (field.type === \"checkboxes\" || field.type === \"multi_select\") {\n const optionIds = new Set(field.options.map((option) => option.id));\n return Array.isArray(value) ? value.filter((id) => optionIds.has(id)) : [];\n }\n const text = getStringValue(value);\n if (field.type === \"radio\" || field.type === \"select\") {\n return field.options.some((option) => option.id === text) ? text : \"\";\n }\n return text;\n}\n\nfunction sameCustomFieldValue(\n before: CustomFieldValues[string] | undefined,\n after: CustomFieldValues[string] | undefined\n) {\n if (Array.isArray(before) && Array.isArray(after)) {\n return (\n before.length === after.length &&\n before.every((id, index) => id === after[index])\n );\n }\n return before === after;\n}\n\nfunction getStringValue(value: CustomFieldValues[string] | undefined) {\n const result = safeParse(string(), value);\n return result.success ? result.output : \"\";\n}\n\nexport function buildCustomFieldAnswers(\n fields: LockerverseCustomField[],\n values: CustomFieldValues\n): LockerverseCustomFieldAnswer[] {\n return fields.map((field): LockerverseCustomFieldAnswer => {\n const value = values[field.id];\n if (field.type === \"checkbox\") {\n return { fieldId: field.id, value: value === true };\n }\n if (field.type === \"checkboxes\" || field.type === \"multi_select\") {\n return { fieldId: field.id, value: Array.isArray(value) ? value : [] };\n }\n return {\n fieldId: field.id,\n value: getStringValue(value),\n };\n });\n}\n\nexport function validateCustomFields(\n fields: LockerverseCustomField[],\n values: CustomFieldValues\n) {\n const errors: Record<string, string> = {};\n for (const field of fields) {\n if (!field.required) {\n continue;\n }\n const message = `${field.label} is required.`;\n const value = values[field.id];\n if (field.type === \"checkbox\") {\n if (value !== true) {\n errors[field.id] = message;\n }\n continue;\n }\n const result =\n field.type === \"checkboxes\" || field.type === \"multi_select\"\n ? safeParse(pipe(array(string()), minLength(1, message)), value ?? [])\n : safeParse(\n pipe(string(), trim(), nonEmpty(message)),\n getStringValue(value)\n );\n if (!result.success) {\n errors[field.id] = result.issues[0]?.message ?? message;\n }\n }\n return errors;\n}\n\nexport function CustomFields({\n choiceClassName = \"lockerverse-payment__check-row\",\n errorClassName = \"lockerverse-payment__field-message\",\n errors = {},\n fields,\n idPrefix,\n onChange,\n values,\n}: {\n choiceClassName?: string;\n errorClassName?: string;\n errors?: Record<string, string>;\n fields: LockerverseCustomField[];\n idPrefix?: string | undefined;\n onChange: (values: CustomFieldValues) => void;\n values: CustomFieldValues;\n}) {\n return fields.map((field) => (\n <CustomField\n choiceClassName={choiceClassName}\n error={errors[field.id]}\n errorClassName={errorClassName}\n field={field}\n idPrefix={idPrefix}\n key={field.id}\n onChange={(value) => onChange({ ...values, [field.id]: value })}\n value={values[field.id]}\n />\n ));\n}\n\nexport type CustomFieldProps = {\n choiceClassName?: string;\n error?: string | undefined;\n errorClassName?: string;\n field: LockerverseCustomField;\n idPrefix?: string | undefined;\n onChange: (value: boolean | string | string[]) => void;\n value: boolean | string | string[] | undefined;\n};\n\nexport function CustomField({\n choiceClassName = \"lockerverse-payment__check-row\",\n errorClassName = \"lockerverse-payment__field-message\",\n ...props\n}: CustomFieldProps) {\n const generatedId = useId();\n const { field, idPrefix = `lockerverse-${generatedId}` } = props;\n const controlId = `${idPrefix}-custom-${field.id}`;\n const errorId = `${controlId}-error`;\n const controlProps = {\n ...props,\n choiceClassName,\n controlId,\n errorClassName,\n errorId,\n };\n switch (field.type) {\n case \"checkbox\":\n return <BooleanCustomField {...controlProps} field={field} />;\n case \"text\":\n case \"textarea\":\n return <TextCustomField {...controlProps} field={field} />;\n case \"select\":\n case \"multi_select\":\n return <SelectCustomField {...controlProps} field={field} />;\n case \"radio\":\n case \"checkboxes\":\n return <ChoiceCustomField {...controlProps} field={field} />;\n default:\n return null;\n }\n}\n\nfunction BooleanCustomField({\n choiceClassName,\n controlId,\n error,\n errorClassName,\n errorId,\n field,\n onChange,\n value,\n}: ControlProps & { field: LockerverseBooleanCustomField }) {\n return (\n <label className={choiceClassName} htmlFor={controlId}>\n <Checkbox\n aria-describedby={error ? errorId : undefined}\n aria-invalid={error ? \"true\" : undefined}\n checked={value === true}\n id={controlId}\n name={`custom:${field.id}`}\n onChange={(event) => onChange(event.currentTarget.checked)}\n required={field.required}\n />\n <span>{field.label}</span>\n <FieldError className={errorClassName} error={error} id={errorId} />\n </label>\n );\n}\n\ntype ControlProps = CustomFieldProps & {\n choiceClassName: string;\n errorClassName: string;\n controlId: string;\n errorId: string;\n};\n\nfunction TextCustomField({\n controlId,\n error,\n errorId,\n field,\n onChange,\n value,\n}: ControlProps & {\n field: Extract<LockerverseCustomField, { type: \"text\" | \"textarea\" }>;\n}) {\n const commonProps = {\n \"aria-describedby\": error ? errorId : undefined,\n \"aria-invalid\": error ? (\"true\" as const) : undefined,\n id: controlId,\n name: `custom:${field.id}`,\n onChange: (\n event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>\n ) => onChange(event.currentTarget.value),\n required: field.required,\n value: getStringValue(value),\n };\n return field.type === \"textarea\" ? (\n <FormField\n error={error}\n errorId={errorId}\n id={controlId}\n label={field.label}\n >\n <textarea {...commonProps} className=\"lockerverse-form__control\" />\n </FormField>\n ) : (\n <TextField\n {...commonProps}\n error={error}\n errorId={errorId}\n label={field.label}\n />\n );\n}\n\nfunction SelectCustomField({\n controlId,\n error,\n errorId,\n field,\n onChange,\n value,\n}: ControlProps & {\n field: LockerverseMultiOptionCustomField | LockerverseSingleOptionCustomField;\n}) {\n return (\n <FormField\n error={error}\n errorId={errorId}\n id={controlId}\n label={field.label}\n >\n <select\n aria-describedby={error ? errorId : undefined}\n aria-invalid={error ? \"true\" : undefined}\n className=\"lockerverse-form__control\"\n id={controlId}\n multiple={field.type === \"multi_select\"}\n name={`custom:${field.id}`}\n onChange={(event) =>\n onChange(\n field.type === \"multi_select\"\n ? [...event.currentTarget.selectedOptions].map(\n (option) => option.value\n )\n : event.currentTarget.value\n )\n }\n required={field.required}\n value={\n value === true || value === false\n ? \"\"\n : (value ?? (field.type === \"multi_select\" ? [] : \"\"))\n }\n >\n {field.type === \"select\" ? <option value=\"\">Select…</option> : null}\n {field.options.map((option) => (\n <option key={option.id} value={option.id}>\n {option.label}\n </option>\n ))}\n </select>\n </FormField>\n );\n}\n\nfunction ChoiceCustomField({\n choiceClassName,\n controlId,\n error,\n errorClassName,\n errorId,\n field,\n onChange,\n value,\n}: ControlProps & {\n field: LockerverseMultiOptionCustomField | LockerverseSingleOptionCustomField;\n}) {\n const selectedValues = Array.isArray(value) ? value : [];\n return (\n <fieldset\n aria-describedby={error ? errorId : undefined}\n aria-invalid={error ? \"true\" : undefined}\n >\n <legend>{field.label}</legend>\n {field.options.map((option, index) => (\n <label className={choiceClassName} key={option.id}>\n <input\n checked={\n Array.isArray(value)\n ? value.includes(option.id)\n : value === option.id\n }\n className={\n field.type === \"checkboxes\"\n ? \"lockerverse-form__checkbox\"\n : undefined\n }\n id={`${controlId}-${index}`}\n name={`custom:${field.id}`}\n onChange={(event) => {\n if (field.type === \"radio\") {\n onChange(option.id);\n return;\n }\n onChange(\n event.currentTarget.checked\n ? [...selectedValues, option.id]\n : selectedValues.filter((id) => id !== option.id)\n );\n }}\n required={field.required && field.type === \"radio\"}\n type={field.type === \"radio\" ? \"radio\" : \"checkbox\"}\n value={option.id}\n />\n <span>{option.label}</span>\n </label>\n ))}\n <FieldError className={errorClassName} error={error} id={errorId} />\n </fieldset>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"custom-fields.js","names":[],"sources":["../src/custom-fields.tsx"],"sourcesContent":["import { Select } from \"@base-ui/react/select\";\nimport type {\n LockerverseBooleanCustomField,\n LockerverseCatalog,\n LockerverseCheckoutItem,\n LockerverseCustomField,\n LockerverseCustomFieldAnswer,\n LockerverseMultiOptionCustomField,\n LockerverseSingleOptionCustomField,\n} from \"@lockerverse/sdk/checkout\";\nimport { useId } from \"react\";\nimport {\n array,\n minLength,\n nonEmpty,\n pipe,\n safeParse,\n string,\n trim,\n} from \"valibot\";\nimport { Checkbox, FieldError, FormField, TextField } from \"./form-field.tsx\";\n\nexport type CustomFieldValues = Record<string, boolean | string | string[]>;\n\nexport function getCheckoutCustomFields(\n catalog: LockerverseCatalog,\n items: readonly LockerverseCheckoutItem[]\n) {\n const selectedOptionIds = new Set(items.map((item) => item.productId));\n return catalog.widget.customFields.filter(\n (field) =>\n !field.paymentOptionIds?.length ||\n field.paymentOptionIds.some((id) => selectedOptionIds.has(id))\n );\n}\n\n/** Normalize initial values against configured fields; unknown fields and options are discarded. */\nexport function createCustomFieldValues(\n fields: LockerverseCustomField[],\n initialValues: CustomFieldValues = {}\n): CustomFieldValues {\n return Object.fromEntries(\n fields.map((field) => [\n field.id,\n reconcileCustomFieldValue(\n field,\n Object.hasOwn(initialValues, field.id)\n ? initialValues[field.id]\n : undefined\n ),\n ])\n );\n}\n\nexport function reconcileCustomFieldValues(\n fields: LockerverseCustomField[],\n values: CustomFieldValues\n) {\n const next: CustomFieldValues = {};\n for (const field of fields) {\n const value = values[field.id];\n next[field.id] = reconcileCustomFieldValue(field, value);\n }\n const oldKeys = Object.keys(values);\n if (\n oldKeys.length === fields.length &&\n fields.every((field) => {\n const before = values[field.id];\n const after = next[field.id];\n return (\n Object.hasOwn(values, field.id) && sameCustomFieldValue(before, after)\n );\n })\n ) {\n return values;\n }\n return next;\n}\n\nfunction reconcileCustomFieldValue(\n field: LockerverseCustomField,\n value: CustomFieldValues[string] | undefined\n) {\n if (field.type === \"checkbox\") {\n return value === true;\n }\n if (field.type === \"checkboxes\" || field.type === \"multi_select\") {\n const optionIds = new Set(field.options.map((option) => option.id));\n return Array.isArray(value) ? value.filter((id) => optionIds.has(id)) : [];\n }\n const text = getStringValue(value);\n if (field.type === \"radio\" || field.type === \"select\") {\n return field.options.some((option) => option.id === text) ? text : \"\";\n }\n return text;\n}\n\nfunction sameCustomFieldValue(\n before: CustomFieldValues[string] | undefined,\n after: CustomFieldValues[string] | undefined\n) {\n if (Array.isArray(before) && Array.isArray(after)) {\n return (\n before.length === after.length &&\n before.every((id, index) => id === after[index])\n );\n }\n return before === after;\n}\n\nfunction getStringValue(value: CustomFieldValues[string] | undefined) {\n const result = safeParse(string(), value);\n return result.success ? result.output : \"\";\n}\n\nexport function buildCustomFieldAnswers(\n fields: LockerverseCustomField[],\n values: CustomFieldValues\n): LockerverseCustomFieldAnswer[] {\n return fields.map((field): LockerverseCustomFieldAnswer => {\n const value = values[field.id];\n if (field.type === \"checkbox\") {\n return { fieldId: field.id, value: value === true };\n }\n if (field.type === \"checkboxes\" || field.type === \"multi_select\") {\n return { fieldId: field.id, value: Array.isArray(value) ? value : [] };\n }\n return {\n fieldId: field.id,\n value: getStringValue(value),\n };\n });\n}\n\nexport function validateCustomFields(\n fields: LockerverseCustomField[],\n values: CustomFieldValues\n) {\n const errors: Record<string, string> = {};\n for (const field of fields) {\n if (!field.required) {\n continue;\n }\n const message = `${field.label} is required.`;\n const value = values[field.id];\n if (field.type === \"checkbox\") {\n if (value !== true) {\n errors[field.id] = message;\n }\n continue;\n }\n const result =\n field.type === \"checkboxes\" || field.type === \"multi_select\"\n ? safeParse(pipe(array(string()), minLength(1, message)), value ?? [])\n : safeParse(\n pipe(string(), trim(), nonEmpty(message)),\n getStringValue(value)\n );\n if (!result.success) {\n errors[field.id] = result.issues[0]?.message ?? message;\n }\n }\n return errors;\n}\n\nexport function CustomFields({\n choiceClassName = \"lockerverse-payment__check-row\",\n errorClassName = \"lockerverse-payment__field-message\",\n errors = {},\n fields,\n idPrefix,\n onChange,\n values,\n}: {\n choiceClassName?: string;\n errorClassName?: string;\n errors?: Record<string, string>;\n fields: LockerverseCustomField[];\n idPrefix?: string | undefined;\n onChange: (values: CustomFieldValues) => void;\n values: CustomFieldValues;\n}) {\n return fields.map((field) => (\n <CustomField\n choiceClassName={choiceClassName}\n error={errors[field.id]}\n errorClassName={errorClassName}\n field={field}\n idPrefix={idPrefix}\n key={field.id}\n onChange={(value) => onChange({ ...values, [field.id]: value })}\n value={values[field.id]}\n />\n ));\n}\n\nexport type CustomFieldProps = {\n choiceClassName?: string;\n error?: string | undefined;\n errorClassName?: string;\n field: LockerverseCustomField;\n idPrefix?: string | undefined;\n onChange: (value: boolean | string | string[]) => void;\n value: boolean | string | string[] | undefined;\n};\n\nexport function CustomField({\n choiceClassName = \"lockerverse-payment__check-row\",\n errorClassName = \"lockerverse-payment__field-message\",\n ...props\n}: CustomFieldProps) {\n const generatedId = useId();\n const { field, idPrefix = `lockerverse-${generatedId}` } = props;\n const controlId = `${idPrefix}-custom-${field.id}`;\n const errorId = `${controlId}-error`;\n const controlProps = {\n ...props,\n choiceClassName,\n controlId,\n errorClassName,\n errorId,\n };\n switch (field.type) {\n case \"checkbox\":\n return <BooleanCustomField {...controlProps} field={field} />;\n case \"text\":\n case \"textarea\":\n return <TextCustomField {...controlProps} field={field} />;\n case \"select\":\n return <SelectCustomField {...controlProps} field={field} />;\n case \"multi_select\":\n return <MultiSelectCustomField {...controlProps} field={field} />;\n case \"radio\":\n case \"checkboxes\":\n return <ChoiceCustomField {...controlProps} field={field} />;\n default:\n return null;\n }\n}\n\nfunction BooleanCustomField({\n choiceClassName,\n controlId,\n error,\n errorClassName,\n errorId,\n field,\n onChange,\n value,\n}: ControlProps & { field: LockerverseBooleanCustomField }) {\n return (\n <label className={choiceClassName} htmlFor={controlId}>\n <Checkbox\n aria-describedby={error ? errorId : undefined}\n aria-invalid={error ? \"true\" : undefined}\n checked={value === true}\n id={controlId}\n name={`custom:${field.id}`}\n onChange={(event) => onChange(event.currentTarget.checked)}\n required={field.required}\n />\n <span>{field.label}</span>\n <FieldError className={errorClassName} error={error} id={errorId} />\n </label>\n );\n}\n\ntype ControlProps = CustomFieldProps & {\n choiceClassName: string;\n errorClassName: string;\n controlId: string;\n errorId: string;\n};\n\nfunction TextCustomField({\n controlId,\n error,\n errorId,\n field,\n onChange,\n value,\n}: ControlProps & {\n field: Extract<LockerverseCustomField, { type: \"text\" | \"textarea\" }>;\n}) {\n const commonProps = {\n \"aria-describedby\": error ? errorId : undefined,\n \"aria-invalid\": error ? (\"true\" as const) : undefined,\n id: controlId,\n name: `custom:${field.id}`,\n onChange: (\n event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>\n ) => onChange(event.currentTarget.value),\n required: field.required,\n value: getStringValue(value),\n };\n return field.type === \"textarea\" ? (\n <FormField\n error={error}\n errorId={errorId}\n id={controlId}\n label={field.label}\n >\n <textarea {...commonProps} className=\"lockerverse-form__control\" />\n </FormField>\n ) : (\n <TextField\n {...commonProps}\n error={error}\n errorId={errorId}\n label={field.label}\n />\n );\n}\n\nfunction MultiSelectCustomField({\n controlId,\n error,\n errorId,\n field,\n onChange,\n value,\n}: ControlProps & { field: LockerverseMultiOptionCustomField }) {\n return (\n <FormField\n error={error}\n errorId={errorId}\n id={controlId}\n label={field.label}\n >\n <Select.Root\n id={controlId}\n items={field.options.map((option) => ({\n label: option.label,\n value: option.id,\n }))}\n modal={false}\n multiple\n name={`custom:${field.id}`}\n onValueChange={onChange}\n required={field.required}\n value={Array.isArray(value) ? value : []}\n >\n <Select.Trigger\n aria-describedby={error ? errorId : undefined}\n aria-invalid={error ? \"true\" : undefined}\n className=\"lockerverse-form__control lockerverse-multi-select__trigger\"\n id={controlId}\n name={`custom:${field.id}`}\n type=\"button\"\n >\n <Select.Value placeholder=\"Select…\" />\n <Select.Icon className=\"lockerverse-multi-select__icon\">\n {null}\n </Select.Icon>\n </Select.Trigger>\n <Select.Positioner\n align=\"start\"\n alignItemWithTrigger={false}\n className=\"lockerverse-multi-select__positioner\"\n sideOffset={6}\n >\n <Select.Popup className=\"lockerverse-multi-select__popup\">\n <Select.List className=\"lockerverse-multi-select__list\">\n {field.options.map((option) => (\n <Select.Item\n className=\"lockerverse-multi-select__item\"\n key={option.id}\n value={option.id}\n >\n <span\n aria-hidden=\"true\"\n className=\"lockerverse-multi-select__check\"\n >\n <Select.ItemIndicator>✓</Select.ItemIndicator>\n </span>\n <Select.ItemText>{option.label}</Select.ItemText>\n </Select.Item>\n ))}\n </Select.List>\n </Select.Popup>\n </Select.Positioner>\n </Select.Root>\n </FormField>\n );\n}\n\nfunction SelectCustomField({\n controlId,\n error,\n errorId,\n field,\n onChange,\n value,\n}: ControlProps & {\n field: LockerverseSingleOptionCustomField;\n}) {\n return (\n <FormField\n error={error}\n errorId={errorId}\n id={controlId}\n label={field.label}\n >\n <select\n aria-describedby={error ? errorId : undefined}\n aria-invalid={error ? \"true\" : undefined}\n className=\"lockerverse-form__control\"\n id={controlId}\n name={`custom:${field.id}`}\n onChange={(event) => onChange(event.currentTarget.value)}\n required={field.required}\n value={getStringValue(value)}\n >\n <option value=\"\">Select…</option>\n {field.options.map((option) => (\n <option key={option.id} value={option.id}>\n {option.label}\n </option>\n ))}\n </select>\n </FormField>\n );\n}\n\nfunction ChoiceCustomField({\n choiceClassName,\n controlId,\n error,\n errorClassName,\n errorId,\n field,\n onChange,\n value,\n}: ControlProps & {\n field: LockerverseMultiOptionCustomField | LockerverseSingleOptionCustomField;\n}) {\n const selectedValues = Array.isArray(value) ? value : [];\n return (\n <fieldset\n aria-describedby={error ? errorId : undefined}\n aria-invalid={error ? \"true\" : undefined}\n >\n <legend>{field.label}</legend>\n {field.options.map((option, index) => (\n <label className={choiceClassName} key={option.id}>\n <input\n checked={\n Array.isArray(value)\n ? value.includes(option.id)\n : value === option.id\n }\n className={\n field.type === \"checkboxes\"\n ? \"lockerverse-form__checkbox\"\n : undefined\n }\n id={`${controlId}-${index}`}\n name={`custom:${field.id}`}\n onChange={(event) => {\n if (field.type === \"radio\") {\n onChange(option.id);\n return;\n }\n onChange(\n event.currentTarget.checked\n ? [...selectedValues, option.id]\n : selectedValues.filter((id) => id !== option.id)\n );\n }}\n required={field.required && field.type === \"radio\"}\n type={field.type === \"radio\" ? \"radio\" : \"checkbox\"}\n value={option.id}\n />\n <span>{option.label}</span>\n </label>\n ))}\n <FieldError className={errorClassName} error={error} id={errorId} />\n </fieldset>\n );\n}\n"],"mappings":"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"}
@@ -16,3 +16,71 @@ select.lockerverse-form__control:not([multiple]) {
16
16
  select.lockerverse-form__control[multiple] {
17
17
  background-image: none;
18
18
  }
19
+
20
+ .lockerverse-multi-select__trigger {
21
+ display: flex;
22
+ gap: 12px;
23
+ align-items: center;
24
+ justify-content: space-between;
25
+ text-align: left;
26
+ cursor: pointer;
27
+ }
28
+ .lockerverse-multi-select__icon {
29
+ flex: 0 0 auto;
30
+ width: 8px;
31
+ height: 8px;
32
+ margin: -4px 3px 0;
33
+ border-right: 2px solid currentcolor;
34
+ border-bottom: 2px solid currentcolor;
35
+ transform: rotate(45deg);
36
+ }
37
+ .lockerverse-multi-select__positioner {
38
+ z-index: 100;
39
+ width: var(--anchor-width);
40
+ max-width: var(--available-width);
41
+ }
42
+ .lockerverse-multi-select__popup {
43
+ overflow: hidden;
44
+ color: var(--lockerverse-text);
45
+ background: var(--lockerverse-control, var(--lockerverse-bg));
46
+ border: 1px solid var(--lockerverse-border);
47
+ border-radius: var(--lockerverse-radius, 10px);
48
+ box-shadow: 0 8px 24px #0003;
49
+ }
50
+ .lockerverse-multi-select__list {
51
+ box-sizing: border-box;
52
+ max-height: min(280px, var(--available-height));
53
+ padding: 4px;
54
+ overflow-y: auto;
55
+ outline: none;
56
+ }
57
+ .lockerverse-multi-select__item {
58
+ display: flex;
59
+ gap: 10px;
60
+ align-items: center;
61
+ min-height: 44px;
62
+ padding: 8px 10px;
63
+ font: inherit;
64
+ cursor: pointer;
65
+ user-select: none;
66
+ outline: none;
67
+ border-radius: calc(var(--lockerverse-radius, 10px) / 2);
68
+ }
69
+ .lockerverse-multi-select__item[data-highlighted] {
70
+ background: color-mix(in srgb, var(--lockerverse-text) 10%, transparent);
71
+ }
72
+ .lockerverse-multi-select__check {
73
+ display: grid;
74
+ flex: 0 0 auto;
75
+ place-items: center;
76
+ width: 18px;
77
+ height: 18px;
78
+ border: 1px solid var(--lockerverse-border);
79
+ border-radius: 4px;
80
+ }
81
+ .lockerverse-multi-select__item[data-selected]
82
+ .lockerverse-multi-select__check {
83
+ color: var(--lockerverse-accent-contrast, #000);
84
+ background: var(--lockerverse-accent);
85
+ border-color: var(--lockerverse-accent);
86
+ }
package/dist/signup.css CHANGED
@@ -134,10 +134,6 @@
134
134
  color-mix(in srgb, var(--lockerverse-text) 8%, transparent);
135
135
  }
136
136
 
137
- .lockerverse-signup select[multiple] {
138
- min-height: 128px;
139
- }
140
-
141
137
  .lockerverse-signup [aria-invalid="true"] {
142
138
  border-color: var(--lockerverse-danger);
143
139
  }
@@ -1,6 +1,6 @@
1
1
  # Lockerverse auctions guide
2
2
 
3
- Package: @lockerverse/react@0.2.163
3
+ Package: @lockerverse/react@0.2.165
4
4
 
5
5
  Generated from this release's maintained documentation. Do not edit this copy.
6
6
 
package/guides/events.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Lockerverse events guide
2
2
 
3
- Package: @lockerverse/react@0.2.163
3
+ Package: @lockerverse/react@0.2.165
4
4
 
5
5
  Generated from this release's maintained documentation. Do not edit this copy.
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Lockerverse integration guide
2
2
 
3
- Package: @lockerverse/react@0.2.163
3
+ Package: @lockerverse/react@0.2.165
4
4
 
5
5
  Generated from this release's maintained documentation. Do not edit this copy.
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Lockerverse payment-widgets guide
2
2
 
3
- Package: @lockerverse/react@0.2.163
3
+ Package: @lockerverse/react@0.2.165
4
4
 
5
5
  Generated from this release's maintained documentation. Do not edit this copy.
6
6
 
package/guides/signup.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Lockerverse signup guide
2
2
 
3
- Package: @lockerverse/react@0.2.163
3
+ Package: @lockerverse/react@0.2.165
4
4
 
5
5
  Generated from this release's maintained documentation. Do not edit this copy.
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lockerverse/react",
3
- "version": "0.2.163",
3
+ "version": "0.2.165",
4
4
  "description": "Tree-shakable React payment and signup UI for Lockerverse",
5
5
  "repository": {
6
6
  "type": "git",
@@ -136,7 +136,7 @@
136
136
  "embla-carousel-react": "8.6.0",
137
137
  "lucide-react": "0.536.0",
138
138
  "valibot": "1.4.2",
139
- "@lockerverse/sdk": "0.2.163"
139
+ "@lockerverse/sdk": "0.2.165"
140
140
  },
141
141
  "devDependencies": {
142
142
  "@size-limit/file": "13.0.3",