@geomak/ui 5.7.2 → 5.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +214 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +296 -2
- package/dist/index.d.ts +296 -2
- package/dist/index.js +205 -11
- package/dist/index.js.map +1 -1
- package/dist/styles.css +30 -52
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2605,15 +2605,7 @@ function Dropdown({
|
|
|
2605
2605
|
hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { id: errorId, className: "text-status-error text-xs mt-1", children: errorMessage })
|
|
2606
2606
|
] });
|
|
2607
2607
|
}
|
|
2608
|
-
var SHIMMER =
|
|
2609
|
-
"relative overflow-hidden rounded-sm bg-surface-raised",
|
|
2610
|
-
'before:absolute before:inset-0 before:content-[""]',
|
|
2611
|
-
"before:bg-gradient-to-r before:from-transparent before:via-white/30 before:to-transparent",
|
|
2612
|
-
"before:animate-[shimmer_1.6s_linear_infinite]",
|
|
2613
|
-
// Respect prefers-reduced-motion — the resting bg-surface-raised is still
|
|
2614
|
-
// a perfectly legible placeholder for users who have animations off.
|
|
2615
|
-
"motion-reduce:before:hidden"
|
|
2616
|
-
].join(" ");
|
|
2608
|
+
var SHIMMER = "oxy-skeleton rounded-sm bg-surface-raised";
|
|
2617
2609
|
function SkeletonBox({ width, height = 16, radius, className = "", style }) {
|
|
2618
2610
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2619
2611
|
"span",
|
|
@@ -6281,7 +6273,7 @@ function useForm(options = {}) {
|
|
|
6281
6273
|
isValid: store.isValid,
|
|
6282
6274
|
getValue: store.getValue,
|
|
6283
6275
|
getValues: store.getValues,
|
|
6284
|
-
setValue: (name, value) => store.setValue(name, value),
|
|
6276
|
+
setValue: (name, value, opts) => store.setValue(name, value, opts),
|
|
6285
6277
|
setValues: (patch) => store.setValues(patch),
|
|
6286
6278
|
setError: store.setError,
|
|
6287
6279
|
validateField: (name) => store.validateField(name),
|
|
@@ -6391,6 +6383,208 @@ function useFieldArray(name) {
|
|
|
6391
6383
|
};
|
|
6392
6384
|
}
|
|
6393
6385
|
|
|
6386
|
+
// src/components/forms/creditCard.ts
|
|
6387
|
+
var CARD_BRANDS = [
|
|
6388
|
+
{ id: "amex", label: "American Express", short: "AMEX", color: "#1F72CD", pattern: /^3[47]/, lengths: [15], cvv: 4, gaps: [4, 10] },
|
|
6389
|
+
{ id: "visa", label: "Visa", short: "VISA", color: "#1A1F71", pattern: /^4/, lengths: [16, 18, 19], cvv: 3, gaps: [4, 8, 12, 16] },
|
|
6390
|
+
{ id: "mastercard", label: "Mastercard", short: "MC", color: "#EB001B", pattern: /^(5[1-5]|2[2-7])/, lengths: [16], cvv: 3, gaps: [4, 8, 12] },
|
|
6391
|
+
{ id: "discover", label: "Discover", short: "DISC", color: "#FF6000", pattern: /^(6011|64[4-9]|65)/, lengths: [16, 19], cvv: 3, gaps: [4, 8, 12] },
|
|
6392
|
+
{ id: "diners", label: "Diners Club", short: "DINERS", color: "#0079BE", pattern: /^(36|38|30[0-5])/, lengths: [14, 16, 19], cvv: 3, gaps: [4, 10] },
|
|
6393
|
+
{ id: "jcb", label: "JCB", short: "JCB", color: "#0B4EA2", pattern: /^35/, lengths: [16, 17, 18, 19], cvv: 3, gaps: [4, 8, 12] }
|
|
6394
|
+
];
|
|
6395
|
+
var onlyDigits = (s) => (s || "").replace(/\D/g, "");
|
|
6396
|
+
function detectBrand(value) {
|
|
6397
|
+
const d = onlyDigits(value);
|
|
6398
|
+
if (!d) return null;
|
|
6399
|
+
return CARD_BRANDS.find((b) => b.pattern.test(d)) ?? null;
|
|
6400
|
+
}
|
|
6401
|
+
function maxCardLength(value) {
|
|
6402
|
+
const b = detectBrand(value);
|
|
6403
|
+
return b ? Math.max(...b.lengths) : 19;
|
|
6404
|
+
}
|
|
6405
|
+
function luhnValid(value) {
|
|
6406
|
+
const s = onlyDigits(value);
|
|
6407
|
+
if (s.length < 12) return false;
|
|
6408
|
+
let sum = 0;
|
|
6409
|
+
let double = false;
|
|
6410
|
+
for (let i = s.length - 1; i >= 0; i--) {
|
|
6411
|
+
let n = s.charCodeAt(i) - 48;
|
|
6412
|
+
if (double) {
|
|
6413
|
+
n *= 2;
|
|
6414
|
+
if (n > 9) n -= 9;
|
|
6415
|
+
}
|
|
6416
|
+
sum += n;
|
|
6417
|
+
double = !double;
|
|
6418
|
+
}
|
|
6419
|
+
return sum % 10 === 0;
|
|
6420
|
+
}
|
|
6421
|
+
function formatCardNumber(value) {
|
|
6422
|
+
const brand = detectBrand(value);
|
|
6423
|
+
const digits = onlyDigits(value).slice(0, maxCardLength(value));
|
|
6424
|
+
const gaps = brand?.gaps ?? [4, 8, 12, 16];
|
|
6425
|
+
let out = "";
|
|
6426
|
+
for (let i = 0; i < digits.length; i++) {
|
|
6427
|
+
if (gaps.includes(i)) out += " ";
|
|
6428
|
+
out += digits[i];
|
|
6429
|
+
}
|
|
6430
|
+
return out;
|
|
6431
|
+
}
|
|
6432
|
+
function cardNumberError(value) {
|
|
6433
|
+
const d = onlyDigits(value);
|
|
6434
|
+
if (!d) return "Card number is required";
|
|
6435
|
+
const brand = detectBrand(d);
|
|
6436
|
+
if (!brand) return "Unsupported card type";
|
|
6437
|
+
if (!brand.lengths.includes(d.length)) return "Card number is incomplete";
|
|
6438
|
+
if (!luhnValid(d)) return "Card number looks invalid";
|
|
6439
|
+
return void 0;
|
|
6440
|
+
}
|
|
6441
|
+
function formatExpiry(value) {
|
|
6442
|
+
let d = onlyDigits(value).slice(0, 4);
|
|
6443
|
+
if (d.length === 1 && d > "1") d = "0" + d;
|
|
6444
|
+
if (d.length <= 2) return d;
|
|
6445
|
+
return `${d.slice(0, 2)}/${d.slice(2)}`;
|
|
6446
|
+
}
|
|
6447
|
+
function expiryError(value, now = /* @__PURE__ */ new Date()) {
|
|
6448
|
+
if (!value) return "Expiry is required";
|
|
6449
|
+
const m = value.match(/^(\d{2})\/(\d{2})$/);
|
|
6450
|
+
if (!m) return "Use MM/YY";
|
|
6451
|
+
const mm = Number(m[1]);
|
|
6452
|
+
const yy = Number(m[2]);
|
|
6453
|
+
if (mm < 1 || mm > 12) return "Invalid month";
|
|
6454
|
+
const endOfMonth = new Date(2e3 + yy, mm, 0, 23, 59, 59, 999);
|
|
6455
|
+
if (endOfMonth < now) return "Card has expired";
|
|
6456
|
+
return void 0;
|
|
6457
|
+
}
|
|
6458
|
+
function cvvError(value, cardNumber) {
|
|
6459
|
+
const need = detectBrand(cardNumber)?.cvv ?? 3;
|
|
6460
|
+
const d = onlyDigits(value);
|
|
6461
|
+
if (!d) return "CVV is required";
|
|
6462
|
+
if (d.length !== need) return `CVV must be ${need} digits`;
|
|
6463
|
+
return void 0;
|
|
6464
|
+
}
|
|
6465
|
+
var toCard = (vals) => {
|
|
6466
|
+
const number = String(vals.number ?? "");
|
|
6467
|
+
return {
|
|
6468
|
+
number: onlyDigits(number),
|
|
6469
|
+
name: String(vals.name ?? ""),
|
|
6470
|
+
expiry: String(vals.expiry ?? ""),
|
|
6471
|
+
cvv: onlyDigits(String(vals.cvv ?? "")),
|
|
6472
|
+
brand: detectBrand(number)?.id ?? null
|
|
6473
|
+
};
|
|
6474
|
+
};
|
|
6475
|
+
function BrandMark({ brand }) {
|
|
6476
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1.5", "aria-live": "polite", children: [
|
|
6477
|
+
/* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "28", height: "18", viewBox: "0 0 28 18", fill: "none", "aria-hidden": "true", children: [
|
|
6478
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "0.5", y: "0.5", width: "27", height: "17", rx: "3", fill: "var(--color-surface-raised)", stroke: "var(--color-border)" }),
|
|
6479
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "0.5", y: "3.75", width: "27", height: "3.5", fill: brand ? brand.color : "var(--color-border-strong)" })
|
|
6480
|
+
] }),
|
|
6481
|
+
brand && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-bold uppercase tracking-wide", style: { color: brand.color }, children: brand.short }),
|
|
6482
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: brand ? brand.label : "Unknown card type" })
|
|
6483
|
+
] });
|
|
6484
|
+
}
|
|
6485
|
+
function CreditCardForm({
|
|
6486
|
+
onSubmit,
|
|
6487
|
+
onChange,
|
|
6488
|
+
defaultValue,
|
|
6489
|
+
size = "md",
|
|
6490
|
+
disabled,
|
|
6491
|
+
requireName = true,
|
|
6492
|
+
hideSubmit = false,
|
|
6493
|
+
submitLabel = "Pay",
|
|
6494
|
+
className = "",
|
|
6495
|
+
style
|
|
6496
|
+
}) {
|
|
6497
|
+
const initial = React8.useRef({
|
|
6498
|
+
number: formatCardNumber(defaultValue?.number ?? ""),
|
|
6499
|
+
name: defaultValue?.name ?? "",
|
|
6500
|
+
expiry: formatExpiry(defaultValue?.expiry ?? ""),
|
|
6501
|
+
cvv: onlyDigits(defaultValue?.cvv ?? "")
|
|
6502
|
+
}).current;
|
|
6503
|
+
const form = useForm({ initialValues: initial });
|
|
6504
|
+
const numberStr = String(form.values.number ?? "");
|
|
6505
|
+
const brand = detectBrand(numberStr);
|
|
6506
|
+
React8.useEffect(() => {
|
|
6507
|
+
onChange?.(toCard(form.values));
|
|
6508
|
+
}, [form.values.number, form.values.name, form.values.expiry, form.values.cvv]);
|
|
6509
|
+
const numberBind = form.fieldNative("number", {
|
|
6510
|
+
required: "Card number is required",
|
|
6511
|
+
validate: (v) => cardNumberError(String(v))
|
|
6512
|
+
});
|
|
6513
|
+
const nameBind = form.fieldNative("name", requireName ? { required: "Cardholder name is required" } : void 0);
|
|
6514
|
+
const expiryBind = form.fieldNative("expiry", {
|
|
6515
|
+
required: "Expiry is required",
|
|
6516
|
+
validate: (v) => expiryError(String(v))
|
|
6517
|
+
});
|
|
6518
|
+
const cvvBind = form.fieldNative("cvv", {
|
|
6519
|
+
required: "CVV is required",
|
|
6520
|
+
validate: (v) => cvvError(String(v), numberStr)
|
|
6521
|
+
});
|
|
6522
|
+
const cvvLen = brand?.cvv ?? 3;
|
|
6523
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6524
|
+
Form,
|
|
6525
|
+
{
|
|
6526
|
+
form,
|
|
6527
|
+
onFinish: (vals) => onSubmit?.(toCard(vals)),
|
|
6528
|
+
className: `flex flex-col gap-4 ${className}`.trim(),
|
|
6529
|
+
style,
|
|
6530
|
+
children: [
|
|
6531
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6532
|
+
TextInput,
|
|
6533
|
+
{
|
|
6534
|
+
...numberBind,
|
|
6535
|
+
label: "Card number",
|
|
6536
|
+
placeholder: "1234 5678 9012 3456",
|
|
6537
|
+
size,
|
|
6538
|
+
disabled,
|
|
6539
|
+
value: numberStr,
|
|
6540
|
+
onChange: (e) => form.setValue("number", formatCardNumber(e.target.value), { touch: true }),
|
|
6541
|
+
suffix: /* @__PURE__ */ jsxRuntime.jsx(BrandMark, { brand })
|
|
6542
|
+
}
|
|
6543
|
+
),
|
|
6544
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6545
|
+
TextInput,
|
|
6546
|
+
{
|
|
6547
|
+
...nameBind,
|
|
6548
|
+
label: "Cardholder name",
|
|
6549
|
+
placeholder: "Jane Appleseed",
|
|
6550
|
+
size,
|
|
6551
|
+
disabled,
|
|
6552
|
+
value: String(form.values.name ?? ""),
|
|
6553
|
+
onChange: (e) => form.setValue("name", e.target.value, { touch: true })
|
|
6554
|
+
}
|
|
6555
|
+
),
|
|
6556
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
|
|
6557
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6558
|
+
TextInput,
|
|
6559
|
+
{
|
|
6560
|
+
...expiryBind,
|
|
6561
|
+
label: "Expiry",
|
|
6562
|
+
placeholder: "MM/YY",
|
|
6563
|
+
size,
|
|
6564
|
+
disabled,
|
|
6565
|
+
value: String(form.values.expiry ?? ""),
|
|
6566
|
+
onChange: (e) => form.setValue("expiry", formatExpiry(e.target.value), { touch: true })
|
|
6567
|
+
}
|
|
6568
|
+
) }),
|
|
6569
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6570
|
+
TextInput,
|
|
6571
|
+
{
|
|
6572
|
+
...cvvBind,
|
|
6573
|
+
label: "CVV",
|
|
6574
|
+
placeholder: cvvLen === 4 ? "1234" : "123",
|
|
6575
|
+
size,
|
|
6576
|
+
disabled,
|
|
6577
|
+
value: String(form.values.cvv ?? ""),
|
|
6578
|
+
onChange: (e) => form.setValue("cvv", onlyDigits(e.target.value).slice(0, cvvLen), { touch: true })
|
|
6579
|
+
}
|
|
6580
|
+
) })
|
|
6581
|
+
] }),
|
|
6582
|
+
!hideSubmit && /* @__PURE__ */ jsxRuntime.jsx(Button, { content: submitLabel, buttonType: "submit", variant: "primary", disabled })
|
|
6583
|
+
]
|
|
6584
|
+
}
|
|
6585
|
+
);
|
|
6586
|
+
}
|
|
6587
|
+
|
|
6394
6588
|
Object.defineProperty(exports, "COLORS", {
|
|
6395
6589
|
enumerable: true,
|
|
6396
6590
|
get: function () { return chunk255PCZIW_cjs.colors_default; }
|
|
@@ -6412,12 +6606,14 @@ exports.AutoComplete = AutoComplete;
|
|
|
6412
6606
|
exports.Avatar = Avatar;
|
|
6413
6607
|
exports.Box = Box;
|
|
6414
6608
|
exports.Button = Button;
|
|
6609
|
+
exports.CARD_BRANDS = CARD_BRANDS;
|
|
6415
6610
|
exports.Catalog = Catalog;
|
|
6416
6611
|
exports.CatalogCarousel = CatalogCarousel;
|
|
6417
6612
|
exports.CatalogGrid = CatalogGrid;
|
|
6418
6613
|
exports.Checkbox = Checkbox;
|
|
6419
6614
|
exports.ColorPicker = ColorPicker;
|
|
6420
6615
|
exports.ContextMenu = ContextMenu;
|
|
6616
|
+
exports.CreditCardForm = CreditCardForm;
|
|
6421
6617
|
exports.DateRangePicker = DateRangePicker;
|
|
6422
6618
|
exports.Drawer = Drawer;
|
|
6423
6619
|
exports.Dropdown = Dropdown;
|
|
@@ -6472,8 +6668,16 @@ exports.Tree = Tree;
|
|
|
6472
6668
|
exports.TreeSelect = TreeSelect;
|
|
6473
6669
|
exports.Typography = Typography;
|
|
6474
6670
|
exports.Wizard = Wizard;
|
|
6671
|
+
exports.cardNumberError = cardNumberError;
|
|
6672
|
+
exports.cvvError = cvvError;
|
|
6673
|
+
exports.detectBrand = detectBrand;
|
|
6674
|
+
exports.expiryError = expiryError;
|
|
6475
6675
|
exports.fieldShell = fieldShell;
|
|
6676
|
+
exports.formatCardNumber = formatCardNumber;
|
|
6677
|
+
exports.formatExpiry = formatExpiry;
|
|
6476
6678
|
exports.isRequired = isRequired;
|
|
6679
|
+
exports.luhnValid = luhnValid;
|
|
6680
|
+
exports.onlyDigits = onlyDigits;
|
|
6477
6681
|
exports.patterns = patterns;
|
|
6478
6682
|
exports.runFieldRules = runFieldRules;
|
|
6479
6683
|
exports.useFieldArray = useFieldArray;
|