@lockerverse/react 0.2.162 → 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 +16 -0
- package/SDK_RULES.md +38 -0
- package/dist/custom-fields.d.ts.map +1 -1
- package/dist/custom-fields.js +1 -1
- package/dist/custom-fields.js.map +1 -1
- package/dist/select-control.css +68 -0
- package/dist/signup.css +0 -4
- package/guides/auctions.md +1 -1
- package/guides/events.md +1 -1
- package/guides/integration.md +4 -1
- package/guides/payment-widgets.md +1 -1
- package/guides/signup.md +1 -1
- package/package.json +2 -2
package/MIGRATIONS.md
CHANGED
|
@@ -15,6 +15,8 @@ script. Installing a package does not edit host code or guarantee these steps ra
|
|
|
15
15
|
and update the real lockfile. Follow peer requirements in the target manifests.
|
|
16
16
|
3. After installation, read this target file and SDK_RULES.md. React consumers
|
|
17
17
|
must also read guides/integration.md and each applicable resource guide.
|
|
18
|
+
Follow SDK_RULES.md to create or merge the short host-root AGENTS.md section
|
|
19
|
+
so future upgrades use this procedure even without a new integration prompt.
|
|
18
20
|
4. For each affected package, apply all entries where old version < entry version
|
|
19
21
|
<= target version, from oldest to newest. Include releases skipped by the
|
|
20
22
|
upgrade. Use semantic version comparison, not string comparison. An Unreleased
|
|
@@ -34,6 +36,20 @@ If it cannot be found, report that limit and use the baseline review below. Do n
|
|
|
34
36
|
claim that every historical migration was applied. Read this file even if the
|
|
35
37
|
version already matches: a previous upgrade may have missed host adoption steps.
|
|
36
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
|
+
|
|
37
53
|
## Existing integration baseline through 0.2.161
|
|
38
54
|
|
|
39
55
|
This guide starts with the current integration baseline. It is not a complete
|
package/SDK_RULES.md
CHANGED
|
@@ -4,6 +4,44 @@ These rules apply to SDK maintenance and host integration. Read the installed
|
|
|
4
4
|
package README, public types, resource guide, and MIGRATIONS.md with this file.
|
|
5
5
|
Use the instructions from the target release. Do not copy APIs from newer docs.
|
|
6
6
|
|
|
7
|
+
## Keep the host instructions available
|
|
8
|
+
|
|
9
|
+
On the first integration or upgrade, create or merge a short `Lockerverse SDK
|
|
10
|
+
maintenance` section in the host project's root `AGENTS.md`. Preserve unrelated
|
|
11
|
+
instructions and update an existing equivalent section instead of duplicating it.
|
|
12
|
+
Use the block below. Do not copy this entire guide or pin a release in that file.
|
|
13
|
+
For a monorepo, keep this pointer in the root and name the affected app directories;
|
|
14
|
+
resolve node_modules and lockfiles from the relevant package-manager workspace.
|
|
15
|
+
|
|
16
|
+
```markdown
|
|
17
|
+
## Lockerverse SDK maintenance
|
|
18
|
+
|
|
19
|
+
For any install, update, or integration edit involving @lockerverse/sdk or
|
|
20
|
+
@lockerverse/react:
|
|
21
|
+
- Record the old resolved package versions before installation. Use the existing
|
|
22
|
+
package manager; keep React/Core matched when both are used, without downgrades.
|
|
23
|
+
- Read SDK_RULES.md and MIGRATIONS.md in the target installed package under
|
|
24
|
+
node_modules/@lockerverse/react/ (or @lockerverse/sdk/ for Core-only hosts).
|
|
25
|
+
React users must also read guides/integration.md and the relevant resource guides.
|
|
26
|
+
- Apply all applicable migrations, including skipped releases, and review the
|
|
27
|
+
existing-integration baseline even if the version already matches. New
|
|
28
|
+
integrations use the current guides and baseline.
|
|
29
|
+
- If files are missing, select a published matching stable release that includes
|
|
30
|
+
them without downgrading, or report the blocked step. Do not invent instructions.
|
|
31
|
+
- If a tool already updated dependencies, recover the old versions from the prior
|
|
32
|
+
lockfile or Git history. Report old/new versions, applied steps, skipped steps
|
|
33
|
+
with reasons, and checks. A version bump alone does not complete a migration.
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
For Lovable, this short pointer can also be kept in workspace knowledge, scoped
|
|
37
|
+
to tasks that use Lockerverse. Do not install the SDK in unrelated projects.
|
|
38
|
+
Workspace knowledge covers projects that have not yet received their root file;
|
|
39
|
+
the root file keeps the rule with the project when it moves to another workspace
|
|
40
|
+
or agent. Do not assume files inside node_modules are read automatically.
|
|
41
|
+
See [Lovable's knowledge documentation](https://docs.lovable.dev/features/knowledge).
|
|
42
|
+
If the host file cannot be edited, report that persistence is incomplete.
|
|
43
|
+
A package installation does not run these instructions or modify the host file.
|
|
44
|
+
|
|
7
45
|
## Public content and search
|
|
8
46
|
|
|
9
47
|
- In an existing server-rendered host, load public auction data in its server
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom-fields.d.ts","names":[],"sources":["../src/custom-fields.tsx"],"mappings":";;
|
|
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"}
|
package/dist/custom-fields.js
CHANGED
|
@@ -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
|
|
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":"sPAuBA,SAAgB,EACd,EACA,EACA,CACA,IAAM,EAAoB,IAAI,IAAI,EAAM,IAAK,GAAS,EAAK,SAAS,CAAC,EACrE,OAAO,EAAQ,OAAO,aAAa,OAChC,GACC,CAAC,EAAM,kBAAkB,QACzB,EAAM,iBAAiB,KAAM,GAAO,EAAkB,IAAI,CAAE,CAAC,CACjE,CACF,CAGA,SAAgB,EACd,EACA,EAAmC,CAAC,EACjB,CACnB,OAAO,OAAO,YACZ,EAAO,IAAK,GAAU,CACpB,EAAM,GACN,EACE,EACA,OAAO,OAAO,EAAe,EAAM,EAAE,EACjC,EAAc,EAAM,IACpB,IAAA,EACN,CACF,CAAC,CACH,CACF,CAEA,SAAgB,EACd,EACA,EACA,CACA,IAAM,EAA0B,CAAC,EACjC,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAQ,EAAO,EAAM,IAC3B,EAAK,EAAM,IAAM,EAA0B,EAAO,CAAK,CACzD,CAcA,OAbgB,OAAO,KAAK,CAEpB,CAAC,CAAC,SAAW,EAAO,QAC1B,EAAO,MAAO,GAAU,CACtB,IAAM,EAAS,EAAO,EAAM,IACtB,EAAQ,EAAK,EAAM,IACzB,OACE,OAAO,OAAO,EAAQ,EAAM,EAAE,GAAK,EAAqB,EAAQ,CAAK,CAEzE,CAAC,EAEM,EAEF,CACT,CAEA,SAAS,EACP,EACA,EACA,CACA,GAAI,EAAM,OAAS,WACjB,OAAO,IAAU,GAEnB,GAAI,EAAM,OAAS,cAAgB,EAAM,OAAS,eAAgB,CAChE,IAAM,EAAY,IAAI,IAAI,EAAM,QAAQ,IAAK,GAAW,EAAO,EAAE,CAAC,EAClE,OAAO,MAAM,QAAQ,CAAK,EAAI,EAAM,OAAQ,GAAO,EAAU,IAAI,CAAE,CAAC,EAAI,CAAC,CAC3E,CACA,IAAM,EAAO,EAAe,CAAK,EAIjC,OAHI,EAAM,OAAS,SAAW,EAAM,OAAS,SACpC,EAAM,QAAQ,KAAM,GAAW,EAAO,KAAO,CAAI,EAAI,EAAO,GAE9D,CACT,CAEA,SAAS,EACP,EACA,EACA,CAOA,OANI,MAAM,QAAQ,CAAM,GAAK,MAAM,QAAQ,CAAK,EAE5C,EAAO,SAAW,EAAM,QACxB,EAAO,OAAO,EAAI,IAAU,IAAO,EAAM,EAAM,EAG5C,IAAW,CACpB,CAEA,SAAS,EAAe,EAA8C,CACpE,IAAM,EAAS,EAAU,EAAO,EAAG,CAAK,EACxC,OAAO,EAAO,QAAU,EAAO,OAAS,EAC1C,CAEA,SAAgB,EACd,EACA,EACgC,CAChC,OAAO,EAAO,IAAK,GAAwC,CACzD,IAAM,EAAQ,EAAO,EAAM,IAO3B,OANI,EAAM,OAAS,WACV,CAAE,QAAS,EAAM,GAAI,MAAO,IAAU,EAAK,EAEhD,EAAM,OAAS,cAAgB,EAAM,OAAS,eACzC,CAAE,QAAS,EAAM,GAAI,MAAO,MAAM,QAAQ,CAAK,EAAI,EAAQ,CAAC,CAAE,EAEhE,CACL,QAAS,EAAM,GACf,MAAO,EAAe,CAAK,CAC7B,CACF,CAAC,CACH,CAEA,SAAgB,EACd,EACA,EACA,CACA,IAAM,EAAiC,CAAC,EACxC,IAAK,IAAM,KAAS,EAAQ,CAC1B,GAAI,CAAC,EAAM,SACT,SAEF,IAAM,EAAU,GAAG,EAAM,MAAM,eACzB,EAAQ,EAAO,EAAM,IAC3B,GAAI,EAAM,OAAS,WAAY,CACzB,IAAU,KACZ,EAAO,EAAM,IAAM,GAErB,QACF,CACA,IAAM,EACJ,EAAM,OAAS,cAAgB,EAAM,OAAS,eAC1C,EAAU,EAAK,EAAM,EAAO,CAAC,EAAG,EAAU,EAAG,CAAO,CAAC,EAAG,GAAS,CAAC,CAAC,EACnE,EACE,EAAK,EAAO,EAAG,EAAK,EAAG,EAAS,CAAO,CAAC,EACxC,EAAe,CAAK,CACtB,EACD,EAAO,UACV,EAAO,EAAM,IAAM,EAAO,OAAO,EAAE,EAAE,SAAW,EAEpD,CACA,OAAO,CACT,CAEA,SAAgB,EAAa,CAC3B,kBAAkB,iCAClB,iBAAiB,qCACjB,SAAS,CAAC,EACV,SACA,WACA,WACA,UASC,CACD,OAAO,EAAO,IAAK,GACjB,EAAC,EAAD,CACmB,kBACjB,MAAO,EAAO,EAAM,IACJ,iBACT,QACG,WAEV,SAAW,GAAU,EAAS,CAAE,GAAG,GAAS,EAAM,IAAK,CAAM,CAAC,EAC9D,MAAO,EAAO,EAAM,GACrB,EAHM,EAAM,EAGZ,CACF,CACH,CAYA,SAAgB,EAAY,CAC1B,kBAAkB,iCAClB,iBAAiB,qCACjB,GAAG,GACgB,CACnB,IAAM,EAAc,EAAM,EACpB,CAAE,QAAO,WAAW,eAAe,KAAkB,EACrD,EAAY,GAAG,EAAS,UAAU,EAAM,KACxC,EAAU,GAAG,EAAU,QACvB,EAAe,CACnB,GAAG,EACH,kBACA,YACA,iBACA,SACF,EACA,OAAQ,EAAM,KAAd,CACE,IAAK,WACH,OAAO,EAAC,EAAD,CAAoB,GAAI,EAAqB,OAAQ,CAAA,EAC9D,IAAK,OACL,IAAK,WACH,OAAO,EAAC,EAAD,CAAiB,GAAI,EAAqB,OAAQ,CAAA,EAC3D,IAAK,SACL,IAAK,eACH,OAAO,EAAC,EAAD,CAAmB,GAAI,EAAqB,OAAQ,CAAA,EAC7D,IAAK,QACL,IAAK,aACH,OAAO,EAAC,EAAD,CAAmB,GAAI,EAAqB,OAAQ,CAAA,EAC7D,QACE,OAAO,IACX,CACF,CAEA,SAAS,EAAmB,CAC1B,kBACA,YACA,QACA,iBACA,UACA,QACA,WACA,SAC0D,CAC1D,OACE,EAAC,QAAD,CAAO,UAAW,EAAiB,QAAS,EAA5C,SAAA,CACE,EAAC,EAAD,CACE,mBAAkB,EAAQ,EAAU,IAAA,GACpC,eAAc,EAAQ,OAAS,IAAA,GAC/B,QAAS,IAAU,GACnB,GAAI,EACJ,KAAM,UAAU,EAAM,KACtB,SAAW,GAAU,EAAS,EAAM,cAAc,OAAO,EACzD,SAAU,EAAM,QACjB,CAAA,EACD,EAAC,OAAD,CAAA,SAAO,EAAM,KAAY,CAAA,EACzB,EAAC,EAAD,CAAY,UAAW,EAAuB,QAAO,GAAI,CAAU,CAAA,CAC9D,GAEX,CASA,SAAS,EAAgB,CACvB,YACA,QACA,UACA,QACA,WACA,SAGC,CACD,IAAM,EAAc,CAClB,mBAAoB,EAAQ,EAAU,IAAA,GACtC,eAAgB,EAAS,OAAmB,IAAA,GAC5C,GAAI,EACJ,KAAM,UAAU,EAAM,KACtB,SACE,GACG,EAAS,EAAM,cAAc,KAAK,EACvC,SAAU,EAAM,SAChB,MAAO,EAAe,CAAK,CAC7B,EACA,OAAO,EAAM,OAAS,WACpB,EAAC,EAAD,CACS,QACE,UACT,GAAI,EACJ,MAAO,EAAM,MAEb,SAAA,EAAC,WAAD,CAAU,GAAI,EAAa,UAAU,2BAA6B,CAAA,CACzD,CAAA,EAEX,EAAC,EAAD,CACE,GAAI,EACG,QACE,UACT,MAAO,EAAM,KACd,CAAA,CAEL,CAEA,SAAS,EAAkB,CACzB,YACA,QACA,UACA,QACA,WACA,SAGC,CACD,OACE,EAAC,EAAD,CACS,QACE,UACT,GAAI,EACJ,MAAO,EAAM,MAEb,SAAA,EAAC,SAAD,CACE,mBAAkB,EAAQ,EAAU,IAAA,GACpC,eAAc,EAAQ,OAAS,IAAA,GAC/B,UAAU,4BACV,GAAI,EACJ,SAAU,EAAM,OAAS,eACzB,KAAM,UAAU,EAAM,KACtB,SAAW,GACT,EACE,EAAM,OAAS,eACX,CAAC,GAAG,EAAM,cAAc,eAAe,CAAC,CAAC,IACtC,GAAW,EAAO,KACrB,EACA,EAAM,cAAc,KAC1B,EAEF,SAAU,EAAM,SAChB,MACE,IAAU,IAAQ,IAAU,GACxB,GACC,IAAU,EAAM,OAAS,eAAiB,CAAC,EAAI,IApBxD,SAAA,CAuBG,EAAM,OAAS,SAAW,EAAC,SAAD,CAAQ,MAAM,GAAG,SAAA,SAAe,CAAA,EAAI,KAC9D,EAAM,QAAQ,IAAK,GAClB,EAAC,SAAD,CAAwB,MAAO,EAAO,GACnC,SAAA,EAAO,KACF,EAFK,EAAO,EAEZ,CACT,CACK,GACC,CAAA,CAEf,CAEA,SAAS,EAAkB,CACzB,kBACA,YACA,QACA,iBACA,UACA,QACA,WACA,SAGC,CACD,IAAM,EAAiB,MAAM,QAAQ,CAAK,EAAI,EAAQ,CAAC,EACvD,OACE,EAAC,WAAD,CACE,mBAAkB,EAAQ,EAAU,IAAA,GACpC,eAAc,EAAQ,OAAS,IAAA,GAFjC,SAAA,CAIE,EAAC,SAAD,CAAA,SAAS,EAAM,KAAc,CAAA,EAC5B,EAAM,QAAQ,KAAK,EAAQ,IAC1B,EAAC,QAAD,CAAO,UAAW,EAAlB,SAAA,CACE,EAAC,QAAD,CACE,QACE,MAAM,QAAQ,CAAK,EACf,EAAM,SAAS,EAAO,EAAE,EACxB,IAAU,EAAO,GAEvB,UACE,EAAM,OAAS,aACX,6BACA,IAAA,GAEN,GAAI,GAAG,EAAU,GAAG,IACpB,KAAM,UAAU,EAAM,KACtB,SAAW,GAAU,CACnB,GAAI,EAAM,OAAS,QAAS,CAC1B,EAAS,EAAO,EAAE,EAClB,MACF,CACA,EACE,EAAM,cAAc,QAChB,CAAC,GAAG,EAAgB,EAAO,EAAE,EAC7B,EAAe,OAAQ,GAAO,IAAO,EAAO,EAAE,CACpD,CACF,EACA,SAAU,EAAM,UAAY,EAAM,OAAS,QAC3C,KAAM,EAAM,OAAS,QAAU,QAAU,WACzC,MAAO,EAAO,EACf,CAAA,EACD,EAAC,OAAD,CAAA,SAAO,EAAO,KAAY,CAAA,CACrB,CA9BiC,EAAA,EAAO,EA8BxC,CACR,EACD,EAAC,EAAD,CAAY,UAAW,EAAuB,QAAO,GAAI,CAAU,CAAA,CAC3D,GAEd"}
|
|
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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"}
|
package/dist/select-control.css
CHANGED
|
@@ -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
|
}
|
package/guides/auctions.md
CHANGED
package/guides/events.md
CHANGED
package/guides/integration.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Lockerverse integration guide
|
|
2
2
|
|
|
3
|
-
Package: @lockerverse/react@0.2.
|
|
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
|
|
|
@@ -9,6 +9,9 @@ Generated from this release's maintained documentation. Do not edit this copy.
|
|
|
9
9
|
## Required quality and upgrade instructions
|
|
10
10
|
|
|
11
11
|
Read `node_modules/@lockerverse/react/SDK_RULES.md` before implementation.
|
|
12
|
+
Follow its host-instruction section to create or merge the short SDK maintenance
|
|
13
|
+
rule in the host root `AGENTS.md`, preserving unrelated instructions. This is
|
|
14
|
+
required for both new integrations and updates; report if it cannot be saved.
|
|
12
15
|
Before an upgrade, record the resolved installed versions. After installing the
|
|
13
16
|
target pair, read `node_modules/@lockerverse/react/MIGRATIONS.md` and apply all
|
|
14
17
|
applicable entries between the old and target versions in order. Review the
|
package/guides/signup.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lockerverse/react",
|
|
3
|
-
"version": "0.2.
|
|
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.
|
|
139
|
+
"@lockerverse/sdk": "0.2.165"
|
|
140
140
|
},
|
|
141
141
|
"devDependencies": {
|
|
142
142
|
"@size-limit/file": "13.0.3",
|