@cimplify/sdk 0.11.0 → 0.12.1

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.
@@ -9,7 +9,7 @@
9
9
  "files": [
10
10
  {
11
11
  "path": "variant-selector.tsx",
12
- "content": "\"use client\";\n\nimport React, { useState, useEffect, useRef, useId } from \"react\";\nimport { RadioGroup } from \"@base-ui/react/radio-group\";\nimport { Radio } from \"@base-ui/react/radio\";\nimport type { ProductVariant, VariantAxisWithValues } from \"@cimplify/sdk\";\nimport type { Money } from \"@cimplify/sdk\";\nimport { parsePrice } from \"@cimplify/sdk\";\nimport { getVariantDisplayName } from \"@cimplify/sdk\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface VariantSelectorClassNames {\n root?: string;\n axisLabel?: string;\n axisOptions?: string;\n option?: string;\n optionSelected?: string;\n listLabel?: string;\n list?: string;\n name?: string;\n pricing?: string;\n adjustment?: string;\n}\n\nexport interface VariantSelectorProps {\n variants: ProductVariant[];\n variantAxes?: VariantAxisWithValues[];\n basePrice?: Money;\n selectedVariantId?: string;\n onVariantChange: (variantId: string | undefined, variant: ProductVariant | undefined) => void;\n productName?: string;\n className?: string;\n classNames?: VariantSelectorClassNames;\n}\n\nexport function VariantSelector({\n variants,\n variantAxes,\n basePrice,\n selectedVariantId,\n onVariantChange,\n productName,\n className,\n classNames,\n}: VariantSelectorProps): React.ReactElement | null {\n const [axisSelections, setAxisSelections] = useState<Record<string, string>>({});\n const initialized = useRef(false);\n const idPrefix = useId();\n\n useEffect(() => {\n initialized.current = false;\n }, [variants]);\n\n useEffect(() => {\n if (initialized.current) return;\n if (!variants || variants.length === 0) return;\n\n const defaultVariant = variants.find((v) => v.is_default) || variants[0];\n if (!defaultVariant) return;\n\n initialized.current = true;\n onVariantChange(defaultVariant.id, defaultVariant);\n\n if (defaultVariant.display_attributes) {\n const initial: Record<string, string> = {};\n for (const attr of defaultVariant.display_attributes) {\n initial[attr.axis_id] = attr.value_id;\n }\n setAxisSelections(initial);\n }\n }, [variants, onVariantChange]);\n\n useEffect(() => {\n if (!initialized.current) return;\n if (!variantAxes || variantAxes.length === 0) return;\n\n const match = variants.find((v) => {\n if (!v.display_attributes) return false;\n return v.display_attributes.every(\n (attr) => axisSelections[attr.axis_id] === attr.value_id,\n );\n });\n\n if (match && match.id !== selectedVariantId) {\n onVariantChange(match.id, match);\n }\n }, [axisSelections, variants, variantAxes, selectedVariantId, onVariantChange]);\n\n if (!variants || variants.length <= 1) {\n return null;\n }\n\n const basePriceNum = basePrice != null ? parsePrice(basePrice) : 0;\n\n if (variantAxes && variantAxes.length > 0) {\n return (\n <div data-cimplify-variant-selector className={cn(\"space-y-5\", className, classNames?.root)}>\n {variantAxes.map((axis) => {\n const labelId = `${idPrefix}-axis-${axis.id}`;\n return (\n <div key={axis.id} data-cimplify-variant-axis>\n <label\n id={labelId}\n data-cimplify-variant-axis-label\n className={cn(\"block text-xs font-medium uppercase tracking-wider text-muted-foreground mb-3\", classNames?.axisLabel)}\n >\n {axis.name}\n </label>\n <RadioGroup\n aria-labelledby={labelId}\n value={axisSelections[axis.id] ?? \"\"}\n onValueChange={(value) => {\n setAxisSelections((prev) => ({\n ...prev,\n [axis.id]: value,\n }));\n }}\n data-cimplify-variant-axis-options\n className={cn(\"flex flex-wrap gap-2\", classNames?.axisOptions)}\n >\n {axis.values.map((value) => {\n const isSelected = axisSelections[axis.id] === value.id;\n return (\n <Radio.Root\n key={value.id}\n value={value.id}\n render={<button type=\"button\" />}\n data-cimplify-variant-option\n data-selected={isSelected || undefined}\n className={cn(\n \"px-4 py-2 border text-sm font-medium transition-colors border-border hover:border-primary/50\",\n isSelected && \"bg-primary text-primary-foreground border-primary\",\n isSelected ? classNames?.optionSelected : classNames?.option,\n )}\n >\n {value.name}\n </Radio.Root>\n );\n })}\n </RadioGroup>\n </div>\n );\n })}\n </div>\n );\n }\n\n const listLabelId = `${idPrefix}-variant-list`;\n\n return (\n <div data-cimplify-variant-selector className={cn(\"space-y-5\", className, classNames?.root)}>\n <label\n id={listLabelId}\n data-cimplify-variant-list-label\n className={cn(\"block text-xs font-medium uppercase tracking-wider text-muted-foreground mb-3\", classNames?.listLabel)}\n >\n Options\n </label>\n <RadioGroup\n aria-labelledby={listLabelId}\n value={selectedVariantId ?? \"\"}\n onValueChange={(value) => {\n const variant = variants.find((v) => v.id === value);\n onVariantChange(variant?.id, variant);\n }}\n data-cimplify-variant-list\n className={cn(\"space-y-2\", classNames?.list)}\n >\n {variants.map((variant) => {\n const isSelected = selectedVariantId === variant.id;\n const adjustment = parsePrice(variant.price_adjustment);\n const effectivePrice = basePriceNum + adjustment;\n\n return (\n <Radio.Root\n key={variant.id}\n value={variant.id}\n render={<button type=\"button\" />}\n data-cimplify-variant-option\n data-selected={isSelected || undefined}\n className={cn(\n \"w-full flex items-center justify-between px-4 py-3 border transition-colors border-border hover:border-primary/50\",\n isSelected && \"bg-primary/5 border-primary\",\n isSelected ? classNames?.optionSelected : classNames?.option,\n )}\n >\n <span\n data-cimplify-variant-name\n className={cn(\"font-medium\", isSelected && \"text-primary\", classNames?.name)}\n >\n {getVariantDisplayName(variant, productName)}\n </span>\n <span data-cimplify-variant-pricing className={cn(\"text-sm\", classNames?.pricing)}>\n {adjustment !== 0 && (\n <span\n data-cimplify-variant-adjustment\n className={cn(\n adjustment > 0 ? \"text-muted-foreground\" : \"text-green-600\",\n classNames?.adjustment,\n )}\n >\n {adjustment > 0 ? \"+\" : \"\"}\n <Price amount={variant.price_adjustment} />\n </span>\n )}\n <Price amount={effectivePrice} className=\"text-muted-foreground\" />\n </span>\n </Radio.Root>\n );\n })}\n </RadioGroup>\n </div>\n );\n}\n"
12
+ "content": "\"use client\";\n\nimport React, { useState, useEffect, useRef, useId } from \"react\";\nimport { RadioGroup } from \"@base-ui/react/radio-group\";\nimport { Radio } from \"@base-ui/react/radio\";\nimport type { ProductVariant, VariantAxisWithValues } from \"@cimplify/sdk\";\nimport type { Money } from \"@cimplify/sdk\";\nimport { parsePrice } from \"@cimplify/sdk\";\nimport { getVariantDisplayName } from \"@cimplify/sdk\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface VariantSelectorClassNames {\n root?: string;\n axisLabel?: string;\n axisOptions?: string;\n option?: string;\n optionSelected?: string;\n listLabel?: string;\n list?: string;\n name?: string;\n pricing?: string;\n adjustment?: string;\n}\n\nexport interface VariantSelectorProps {\n variants: ProductVariant[];\n variantAxes?: VariantAxisWithValues[];\n basePrice?: Money;\n selectedVariantId?: string;\n onVariantChange: (variantId: string | undefined, variant: ProductVariant | undefined) => void;\n productName?: string;\n className?: string;\n classNames?: VariantSelectorClassNames;\n}\n\nexport function VariantSelector({\n variants,\n variantAxes,\n basePrice,\n selectedVariantId,\n onVariantChange,\n productName,\n className,\n classNames,\n}: VariantSelectorProps): React.ReactElement | null {\n const [axisSelections, setAxisSelections] = useState<Record<string, string>>({});\n const initialized = useRef(false);\n const idPrefix = useId();\n\n useEffect(() => {\n initialized.current = false;\n }, [variants]);\n\n useEffect(() => {\n if (initialized.current) return;\n if (!variants || variants.length === 0) return;\n\n const defaultVariant = variants.find((v) => v.is_default) || variants[0];\n if (!defaultVariant) return;\n\n initialized.current = true;\n onVariantChange(defaultVariant.id, defaultVariant);\n\n if (defaultVariant.display_attributes) {\n const initial: Record<string, string> = {};\n for (const attr of defaultVariant.display_attributes) {\n initial[attr.axis_id] = attr.value_id;\n }\n setAxisSelections(initial);\n }\n }, [variants, onVariantChange]);\n\n useEffect(() => {\n if (!initialized.current) return;\n if (!variantAxes || variantAxes.length === 0) return;\n\n const match = variants.find((v) => {\n if (!v.display_attributes) return false;\n return v.display_attributes.every(\n (attr) => axisSelections[attr.axis_id] === attr.value_id,\n );\n });\n\n if (match && match.id !== selectedVariantId) {\n onVariantChange(match.id, match);\n }\n }, [axisSelections, variants, variantAxes, selectedVariantId, onVariantChange]);\n\n if (!variants || variants.length <= 1) {\n return null;\n }\n\n const basePriceNum = basePrice != null ? parsePrice(basePrice) : 0;\n\n if (variantAxes && variantAxes.length > 0) {\n return (\n <div data-cimplify-variant-selector className={cn(\"space-y-5\", className, classNames?.root)}>\n {variantAxes.map((axis) => {\n const labelId = `${idPrefix}-axis-${axis.id}`;\n return (\n <div key={axis.id} data-cimplify-variant-axis>\n <label\n id={labelId}\n data-cimplify-variant-axis-label\n className={cn(\"block text-xs font-medium uppercase tracking-wider text-muted-foreground mb-3\", classNames?.axisLabel)}\n >\n {axis.name}\n </label>\n <RadioGroup\n aria-labelledby={labelId}\n value={axisSelections[axis.id] ?? \"\"}\n onValueChange={(value) => {\n setAxisSelections((prev) => ({\n ...prev,\n [axis.id]: value,\n }));\n }}\n data-cimplify-variant-axis-options\n className={cn(\"flex flex-wrap gap-2\", classNames?.axisOptions)}\n >\n {axis.values.map((value) => {\n const isSelected = axisSelections[axis.id] === value.id;\n return (\n <Radio.Root\n key={value.id}\n value={value.id}\n data-cimplify-variant-option\n data-selected={isSelected || undefined}\n className={cn(\n \"px-4 py-2 border text-sm font-medium transition-colors border-border hover:border-primary/50\",\n isSelected && \"bg-primary text-primary-foreground border-primary\",\n isSelected ? classNames?.optionSelected : classNames?.option,\n )}\n >\n {value.name}\n </Radio.Root>\n );\n })}\n </RadioGroup>\n </div>\n );\n })}\n </div>\n );\n }\n\n const listLabelId = `${idPrefix}-variant-list`;\n\n return (\n <div data-cimplify-variant-selector className={cn(\"space-y-5\", className, classNames?.root)}>\n <label\n id={listLabelId}\n data-cimplify-variant-list-label\n className={cn(\"block text-xs font-medium uppercase tracking-wider text-muted-foreground mb-3\", classNames?.listLabel)}\n >\n Options\n </label>\n <RadioGroup\n aria-labelledby={listLabelId}\n value={selectedVariantId ?? \"\"}\n onValueChange={(value) => {\n const variant = variants.find((v) => v.id === value);\n onVariantChange(variant?.id, variant);\n }}\n data-cimplify-variant-list\n className={cn(\"space-y-2\", classNames?.list)}\n >\n {variants.map((variant) => {\n const isSelected = selectedVariantId === variant.id;\n const adjustment = parsePrice(variant.price_adjustment);\n const effectivePrice = basePriceNum + adjustment;\n\n return (\n <Radio.Root\n key={variant.id}\n value={variant.id}\n data-cimplify-variant-option\n data-selected={isSelected || undefined}\n className={cn(\n \"w-full flex items-center justify-between px-4 py-3 border transition-colors border-border hover:border-primary/50\",\n isSelected && \"bg-primary/5 border-primary\",\n isSelected ? classNames?.optionSelected : classNames?.option,\n )}\n >\n <span\n data-cimplify-variant-name\n className={cn(\"font-medium\", isSelected && \"text-primary\", classNames?.name)}\n >\n {getVariantDisplayName(variant, productName)}\n </span>\n <span data-cimplify-variant-pricing className={cn(\"text-sm\", classNames?.pricing)}>\n {adjustment !== 0 && (\n <span\n data-cimplify-variant-adjustment\n className={cn(\n adjustment > 0 ? \"text-muted-foreground\" : \"text-green-600\",\n classNames?.adjustment,\n )}\n >\n {adjustment > 0 ? \"+\" : \"\"}\n <Price amount={variant.price_adjustment} />\n </span>\n )}\n <Price amount={effectivePrice} className=\"text-muted-foreground\" />\n </span>\n </Radio.Root>\n );\n })}\n </RadioGroup>\n </div>\n );\n}\n"
13
13
  }
14
14
  ]
15
15
  }