@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.js
CHANGED
|
@@ -2570,15 +2570,7 @@ function Dropdown({
|
|
|
2570
2570
|
hasError && /* @__PURE__ */ jsx("div", { id: errorId, className: "text-status-error text-xs mt-1", children: errorMessage })
|
|
2571
2571
|
] });
|
|
2572
2572
|
}
|
|
2573
|
-
var SHIMMER =
|
|
2574
|
-
"relative overflow-hidden rounded-sm bg-surface-raised",
|
|
2575
|
-
'before:absolute before:inset-0 before:content-[""]',
|
|
2576
|
-
"before:bg-gradient-to-r before:from-transparent before:via-white/30 before:to-transparent",
|
|
2577
|
-
"before:animate-[shimmer_1.6s_linear_infinite]",
|
|
2578
|
-
// Respect prefers-reduced-motion — the resting bg-surface-raised is still
|
|
2579
|
-
// a perfectly legible placeholder for users who have animations off.
|
|
2580
|
-
"motion-reduce:before:hidden"
|
|
2581
|
-
].join(" ");
|
|
2573
|
+
var SHIMMER = "oxy-skeleton rounded-sm bg-surface-raised";
|
|
2582
2574
|
function SkeletonBox({ width, height = 16, radius, className = "", style }) {
|
|
2583
2575
|
return /* @__PURE__ */ jsx(
|
|
2584
2576
|
"span",
|
|
@@ -6246,7 +6238,7 @@ function useForm(options = {}) {
|
|
|
6246
6238
|
isValid: store.isValid,
|
|
6247
6239
|
getValue: store.getValue,
|
|
6248
6240
|
getValues: store.getValues,
|
|
6249
|
-
setValue: (name, value) => store.setValue(name, value),
|
|
6241
|
+
setValue: (name, value, opts) => store.setValue(name, value, opts),
|
|
6250
6242
|
setValues: (patch) => store.setValues(patch),
|
|
6251
6243
|
setError: store.setError,
|
|
6252
6244
|
validateField: (name) => store.validateField(name),
|
|
@@ -6356,6 +6348,208 @@ function useFieldArray(name) {
|
|
|
6356
6348
|
};
|
|
6357
6349
|
}
|
|
6358
6350
|
|
|
6359
|
-
|
|
6351
|
+
// src/components/forms/creditCard.ts
|
|
6352
|
+
var CARD_BRANDS = [
|
|
6353
|
+
{ id: "amex", label: "American Express", short: "AMEX", color: "#1F72CD", pattern: /^3[47]/, lengths: [15], cvv: 4, gaps: [4, 10] },
|
|
6354
|
+
{ id: "visa", label: "Visa", short: "VISA", color: "#1A1F71", pattern: /^4/, lengths: [16, 18, 19], cvv: 3, gaps: [4, 8, 12, 16] },
|
|
6355
|
+
{ id: "mastercard", label: "Mastercard", short: "MC", color: "#EB001B", pattern: /^(5[1-5]|2[2-7])/, lengths: [16], cvv: 3, gaps: [4, 8, 12] },
|
|
6356
|
+
{ id: "discover", label: "Discover", short: "DISC", color: "#FF6000", pattern: /^(6011|64[4-9]|65)/, lengths: [16, 19], cvv: 3, gaps: [4, 8, 12] },
|
|
6357
|
+
{ id: "diners", label: "Diners Club", short: "DINERS", color: "#0079BE", pattern: /^(36|38|30[0-5])/, lengths: [14, 16, 19], cvv: 3, gaps: [4, 10] },
|
|
6358
|
+
{ id: "jcb", label: "JCB", short: "JCB", color: "#0B4EA2", pattern: /^35/, lengths: [16, 17, 18, 19], cvv: 3, gaps: [4, 8, 12] }
|
|
6359
|
+
];
|
|
6360
|
+
var onlyDigits = (s) => (s || "").replace(/\D/g, "");
|
|
6361
|
+
function detectBrand(value) {
|
|
6362
|
+
const d = onlyDigits(value);
|
|
6363
|
+
if (!d) return null;
|
|
6364
|
+
return CARD_BRANDS.find((b) => b.pattern.test(d)) ?? null;
|
|
6365
|
+
}
|
|
6366
|
+
function maxCardLength(value) {
|
|
6367
|
+
const b = detectBrand(value);
|
|
6368
|
+
return b ? Math.max(...b.lengths) : 19;
|
|
6369
|
+
}
|
|
6370
|
+
function luhnValid(value) {
|
|
6371
|
+
const s = onlyDigits(value);
|
|
6372
|
+
if (s.length < 12) return false;
|
|
6373
|
+
let sum = 0;
|
|
6374
|
+
let double = false;
|
|
6375
|
+
for (let i = s.length - 1; i >= 0; i--) {
|
|
6376
|
+
let n = s.charCodeAt(i) - 48;
|
|
6377
|
+
if (double) {
|
|
6378
|
+
n *= 2;
|
|
6379
|
+
if (n > 9) n -= 9;
|
|
6380
|
+
}
|
|
6381
|
+
sum += n;
|
|
6382
|
+
double = !double;
|
|
6383
|
+
}
|
|
6384
|
+
return sum % 10 === 0;
|
|
6385
|
+
}
|
|
6386
|
+
function formatCardNumber(value) {
|
|
6387
|
+
const brand = detectBrand(value);
|
|
6388
|
+
const digits = onlyDigits(value).slice(0, maxCardLength(value));
|
|
6389
|
+
const gaps = brand?.gaps ?? [4, 8, 12, 16];
|
|
6390
|
+
let out = "";
|
|
6391
|
+
for (let i = 0; i < digits.length; i++) {
|
|
6392
|
+
if (gaps.includes(i)) out += " ";
|
|
6393
|
+
out += digits[i];
|
|
6394
|
+
}
|
|
6395
|
+
return out;
|
|
6396
|
+
}
|
|
6397
|
+
function cardNumberError(value) {
|
|
6398
|
+
const d = onlyDigits(value);
|
|
6399
|
+
if (!d) return "Card number is required";
|
|
6400
|
+
const brand = detectBrand(d);
|
|
6401
|
+
if (!brand) return "Unsupported card type";
|
|
6402
|
+
if (!brand.lengths.includes(d.length)) return "Card number is incomplete";
|
|
6403
|
+
if (!luhnValid(d)) return "Card number looks invalid";
|
|
6404
|
+
return void 0;
|
|
6405
|
+
}
|
|
6406
|
+
function formatExpiry(value) {
|
|
6407
|
+
let d = onlyDigits(value).slice(0, 4);
|
|
6408
|
+
if (d.length === 1 && d > "1") d = "0" + d;
|
|
6409
|
+
if (d.length <= 2) return d;
|
|
6410
|
+
return `${d.slice(0, 2)}/${d.slice(2)}`;
|
|
6411
|
+
}
|
|
6412
|
+
function expiryError(value, now = /* @__PURE__ */ new Date()) {
|
|
6413
|
+
if (!value) return "Expiry is required";
|
|
6414
|
+
const m = value.match(/^(\d{2})\/(\d{2})$/);
|
|
6415
|
+
if (!m) return "Use MM/YY";
|
|
6416
|
+
const mm = Number(m[1]);
|
|
6417
|
+
const yy = Number(m[2]);
|
|
6418
|
+
if (mm < 1 || mm > 12) return "Invalid month";
|
|
6419
|
+
const endOfMonth = new Date(2e3 + yy, mm, 0, 23, 59, 59, 999);
|
|
6420
|
+
if (endOfMonth < now) return "Card has expired";
|
|
6421
|
+
return void 0;
|
|
6422
|
+
}
|
|
6423
|
+
function cvvError(value, cardNumber) {
|
|
6424
|
+
const need = detectBrand(cardNumber)?.cvv ?? 3;
|
|
6425
|
+
const d = onlyDigits(value);
|
|
6426
|
+
if (!d) return "CVV is required";
|
|
6427
|
+
if (d.length !== need) return `CVV must be ${need} digits`;
|
|
6428
|
+
return void 0;
|
|
6429
|
+
}
|
|
6430
|
+
var toCard = (vals) => {
|
|
6431
|
+
const number = String(vals.number ?? "");
|
|
6432
|
+
return {
|
|
6433
|
+
number: onlyDigits(number),
|
|
6434
|
+
name: String(vals.name ?? ""),
|
|
6435
|
+
expiry: String(vals.expiry ?? ""),
|
|
6436
|
+
cvv: onlyDigits(String(vals.cvv ?? "")),
|
|
6437
|
+
brand: detectBrand(number)?.id ?? null
|
|
6438
|
+
};
|
|
6439
|
+
};
|
|
6440
|
+
function BrandMark({ brand }) {
|
|
6441
|
+
return /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", "aria-live": "polite", children: [
|
|
6442
|
+
/* @__PURE__ */ jsxs("svg", { width: "28", height: "18", viewBox: "0 0 28 18", fill: "none", "aria-hidden": "true", children: [
|
|
6443
|
+
/* @__PURE__ */ jsx("rect", { x: "0.5", y: "0.5", width: "27", height: "17", rx: "3", fill: "var(--color-surface-raised)", stroke: "var(--color-border)" }),
|
|
6444
|
+
/* @__PURE__ */ jsx("rect", { x: "0.5", y: "3.75", width: "27", height: "3.5", fill: brand ? brand.color : "var(--color-border-strong)" })
|
|
6445
|
+
] }),
|
|
6446
|
+
brand && /* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold uppercase tracking-wide", style: { color: brand.color }, children: brand.short }),
|
|
6447
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: brand ? brand.label : "Unknown card type" })
|
|
6448
|
+
] });
|
|
6449
|
+
}
|
|
6450
|
+
function CreditCardForm({
|
|
6451
|
+
onSubmit,
|
|
6452
|
+
onChange,
|
|
6453
|
+
defaultValue,
|
|
6454
|
+
size = "md",
|
|
6455
|
+
disabled,
|
|
6456
|
+
requireName = true,
|
|
6457
|
+
hideSubmit = false,
|
|
6458
|
+
submitLabel = "Pay",
|
|
6459
|
+
className = "",
|
|
6460
|
+
style
|
|
6461
|
+
}) {
|
|
6462
|
+
const initial = useRef({
|
|
6463
|
+
number: formatCardNumber(defaultValue?.number ?? ""),
|
|
6464
|
+
name: defaultValue?.name ?? "",
|
|
6465
|
+
expiry: formatExpiry(defaultValue?.expiry ?? ""),
|
|
6466
|
+
cvv: onlyDigits(defaultValue?.cvv ?? "")
|
|
6467
|
+
}).current;
|
|
6468
|
+
const form = useForm({ initialValues: initial });
|
|
6469
|
+
const numberStr = String(form.values.number ?? "");
|
|
6470
|
+
const brand = detectBrand(numberStr);
|
|
6471
|
+
useEffect(() => {
|
|
6472
|
+
onChange?.(toCard(form.values));
|
|
6473
|
+
}, [form.values.number, form.values.name, form.values.expiry, form.values.cvv]);
|
|
6474
|
+
const numberBind = form.fieldNative("number", {
|
|
6475
|
+
required: "Card number is required",
|
|
6476
|
+
validate: (v) => cardNumberError(String(v))
|
|
6477
|
+
});
|
|
6478
|
+
const nameBind = form.fieldNative("name", requireName ? { required: "Cardholder name is required" } : void 0);
|
|
6479
|
+
const expiryBind = form.fieldNative("expiry", {
|
|
6480
|
+
required: "Expiry is required",
|
|
6481
|
+
validate: (v) => expiryError(String(v))
|
|
6482
|
+
});
|
|
6483
|
+
const cvvBind = form.fieldNative("cvv", {
|
|
6484
|
+
required: "CVV is required",
|
|
6485
|
+
validate: (v) => cvvError(String(v), numberStr)
|
|
6486
|
+
});
|
|
6487
|
+
const cvvLen = brand?.cvv ?? 3;
|
|
6488
|
+
return /* @__PURE__ */ jsxs(
|
|
6489
|
+
Form,
|
|
6490
|
+
{
|
|
6491
|
+
form,
|
|
6492
|
+
onFinish: (vals) => onSubmit?.(toCard(vals)),
|
|
6493
|
+
className: `flex flex-col gap-4 ${className}`.trim(),
|
|
6494
|
+
style,
|
|
6495
|
+
children: [
|
|
6496
|
+
/* @__PURE__ */ jsx(
|
|
6497
|
+
TextInput,
|
|
6498
|
+
{
|
|
6499
|
+
...numberBind,
|
|
6500
|
+
label: "Card number",
|
|
6501
|
+
placeholder: "1234 5678 9012 3456",
|
|
6502
|
+
size,
|
|
6503
|
+
disabled,
|
|
6504
|
+
value: numberStr,
|
|
6505
|
+
onChange: (e) => form.setValue("number", formatCardNumber(e.target.value), { touch: true }),
|
|
6506
|
+
suffix: /* @__PURE__ */ jsx(BrandMark, { brand })
|
|
6507
|
+
}
|
|
6508
|
+
),
|
|
6509
|
+
/* @__PURE__ */ jsx(
|
|
6510
|
+
TextInput,
|
|
6511
|
+
{
|
|
6512
|
+
...nameBind,
|
|
6513
|
+
label: "Cardholder name",
|
|
6514
|
+
placeholder: "Jane Appleseed",
|
|
6515
|
+
size,
|
|
6516
|
+
disabled,
|
|
6517
|
+
value: String(form.values.name ?? ""),
|
|
6518
|
+
onChange: (e) => form.setValue("name", e.target.value, { touch: true })
|
|
6519
|
+
}
|
|
6520
|
+
),
|
|
6521
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
6522
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
|
|
6523
|
+
TextInput,
|
|
6524
|
+
{
|
|
6525
|
+
...expiryBind,
|
|
6526
|
+
label: "Expiry",
|
|
6527
|
+
placeholder: "MM/YY",
|
|
6528
|
+
size,
|
|
6529
|
+
disabled,
|
|
6530
|
+
value: String(form.values.expiry ?? ""),
|
|
6531
|
+
onChange: (e) => form.setValue("expiry", formatExpiry(e.target.value), { touch: true })
|
|
6532
|
+
}
|
|
6533
|
+
) }),
|
|
6534
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
|
|
6535
|
+
TextInput,
|
|
6536
|
+
{
|
|
6537
|
+
...cvvBind,
|
|
6538
|
+
label: "CVV",
|
|
6539
|
+
placeholder: cvvLen === 4 ? "1234" : "123",
|
|
6540
|
+
size,
|
|
6541
|
+
disabled,
|
|
6542
|
+
value: String(form.values.cvv ?? ""),
|
|
6543
|
+
onChange: (e) => form.setValue("cvv", onlyDigits(e.target.value).slice(0, cvvLen), { touch: true })
|
|
6544
|
+
}
|
|
6545
|
+
) })
|
|
6546
|
+
] }),
|
|
6547
|
+
!hideSubmit && /* @__PURE__ */ jsx(Button, { content: submitLabel, buttonType: "submit", variant: "primary", disabled })
|
|
6548
|
+
]
|
|
6549
|
+
}
|
|
6550
|
+
);
|
|
6551
|
+
}
|
|
6552
|
+
|
|
6553
|
+
export { AppShell, AutoComplete, Avatar, Box, Button, CARD_BRANDS, Catalog, CatalogCarousel, CatalogGrid, Checkbox, ColorPicker, ContextMenu, CreditCardForm, DateRangePicker, Drawer, Dropdown, FadingBase, Field, FieldHelpIcon, FieldLabel, FileInput, Flex, Form, FormContext, FormField, FormStore, Grid2 as Grid, GridCard, icons_default as Icon, IconButton, List2 as List, LoadingSpinner, Modal, NotificationProvider, NumberInput, OpaqueGridCard, OtpInput, Password, Portal, RadioGroup, Rating, ScalableContainer, SearchInput_default as SearchInput, SegmentedControl, Sidebar, SkeletonBox, SkeletonCard, SkeletonCircle, SkeletonText, Slider, Switch, Table, Tabs, TagsInput, DatePicker as Temporal, TextArea, TextInput, ThemeProvider, ThemeSwitch, TimePicker, Tooltip, TooltipProvider, TopBar, Tree, TreeSelect, Typography, Wizard, cardNumberError, cvvError, detectBrand, expiryError, fieldShell, formatCardNumber, formatExpiry, isRequired, luhnValid, onlyDigits, patterns, runFieldRules, useFieldArray, useForm, useFormField, useFormStore, useNotification };
|
|
6360
6554
|
//# sourceMappingURL=index.js.map
|
|
6361
6555
|
//# sourceMappingURL=index.js.map
|