@verma-consulting/design-library 0.1.47 → 0.1.48
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/README.md +5 -0
- package/dist/index.d.mts +22 -2
- package/dist/index.d.ts +22 -2
- package/dist/index.js +174 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +178 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Shared MUI-based React components for Verma Consulting applications. Use this package for consistent UI patterns, forms, and design tokens across projects.
|
|
4
4
|
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- **Node.js** `>=24.4.1` (see `engines` in `package.json`). For local development, use **24.4.1** via `.nvmrc`, `.node-version`, `.mise.toml`, or Volta (`volta` field in `package.json`).
|
|
8
|
+
|
|
5
9
|
## Installation
|
|
6
10
|
|
|
7
11
|
```bash
|
|
@@ -60,6 +64,7 @@ The library re-exports **all of `@mui/material`**, so you can use MUI components
|
|
|
60
64
|
| [FormSnackBar](./docs/FormSnackBar.md) | Snackbar for form success/error messages. |
|
|
61
65
|
| [SearchableSelect](./docs/SearchableSelect.md) | Searchable single/multi select with view/edit modes. |
|
|
62
66
|
| [PhoneNumberField](./docs/PhoneNumberField.md) | Phone input with country code and E.164 output. |
|
|
67
|
+
| [OTPField](./docs/OTPField.md) | One-time code input (single-character cells, paste, mask). |
|
|
63
68
|
| [InputFileUpload](./docs/InputFileUpload.md) | Button that opens a file picker. |
|
|
64
69
|
| [ImageUploadAvatar](./docs/ImageUploadAvatar.md) | Avatar-style image upload with preview and clear. |
|
|
65
70
|
| [TabPanel](./docs/TabPanel.md) | Panel content for MUI Tabs (by index). |
|
package/dist/index.d.mts
CHANGED
|
@@ -2,7 +2,7 @@ import { DialogProps, DrawerProps } from '@mui/material';
|
|
|
2
2
|
export * from '@mui/material';
|
|
3
3
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
4
|
import * as React from 'react';
|
|
5
|
-
import React__default, { FC, KeyboardEvent } from 'react';
|
|
5
|
+
import React__default, { FC, KeyboardEvent, CSSProperties } from 'react';
|
|
6
6
|
import MuiTablePagination, { LabelDisplayedRowsArgs } from '@mui/material/TablePagination';
|
|
7
7
|
import { SxProps, Theme } from '@mui/material/styles';
|
|
8
8
|
export { ThemeProvider, createTheme, styled, useTheme } from '@mui/material/styles';
|
|
@@ -210,4 +210,24 @@ interface CountrySelectProps {
|
|
|
210
210
|
}
|
|
211
211
|
declare const CountrySelect: React__default.FC<CountrySelectProps>;
|
|
212
212
|
|
|
213
|
-
|
|
213
|
+
type OTPFieldProps = {
|
|
214
|
+
length: number;
|
|
215
|
+
initialValue?: string;
|
|
216
|
+
onChange: (value: string, index?: number) => void;
|
|
217
|
+
onComplete?: (value: string, index?: number) => void;
|
|
218
|
+
/** Use password inputs (no brief reveal; React 19–safe replacement for `react-pin-input` `secret`). */
|
|
219
|
+
secret?: boolean;
|
|
220
|
+
autoSelect?: boolean;
|
|
221
|
+
disabled?: boolean;
|
|
222
|
+
/** Applied to the focused cell’s outline (`fieldset`). */
|
|
223
|
+
inputFocusStyle?: CSSProperties;
|
|
224
|
+
/** Applied to the outer `Box` (MUI `sx`). */
|
|
225
|
+
sx?: SxProps<Theme>;
|
|
226
|
+
type?: string;
|
|
227
|
+
inputMode?: React__default.HTMLAttributes<HTMLInputElement>["inputMode"];
|
|
228
|
+
/** Optional per-character filter (same idea as `react-pin-input` `regexCriteria`). */
|
|
229
|
+
regexCriteria?: RegExp;
|
|
230
|
+
};
|
|
231
|
+
declare const OTPField: React__default.FC<OTPFieldProps>;
|
|
232
|
+
|
|
233
|
+
export { CountrySelect, type CountryType, EmptyState, FormDialog, FormDrawer, FormPopover, FormSnackBar, IOSSwitch, ImageUploadAvatar, InputFileUpload, Loader, Logo, OTPField, type OTPFieldProps, PhoneNumberField, Pill, SearchableSelect, SkeletonBar, StatusPill, TabPanel, TablePagination, type TablePaginationDesignProps };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { DialogProps, DrawerProps } from '@mui/material';
|
|
|
2
2
|
export * from '@mui/material';
|
|
3
3
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
4
|
import * as React from 'react';
|
|
5
|
-
import React__default, { FC, KeyboardEvent } from 'react';
|
|
5
|
+
import React__default, { FC, KeyboardEvent, CSSProperties } from 'react';
|
|
6
6
|
import MuiTablePagination, { LabelDisplayedRowsArgs } from '@mui/material/TablePagination';
|
|
7
7
|
import { SxProps, Theme } from '@mui/material/styles';
|
|
8
8
|
export { ThemeProvider, createTheme, styled, useTheme } from '@mui/material/styles';
|
|
@@ -210,4 +210,24 @@ interface CountrySelectProps {
|
|
|
210
210
|
}
|
|
211
211
|
declare const CountrySelect: React__default.FC<CountrySelectProps>;
|
|
212
212
|
|
|
213
|
-
|
|
213
|
+
type OTPFieldProps = {
|
|
214
|
+
length: number;
|
|
215
|
+
initialValue?: string;
|
|
216
|
+
onChange: (value: string, index?: number) => void;
|
|
217
|
+
onComplete?: (value: string, index?: number) => void;
|
|
218
|
+
/** Use password inputs (no brief reveal; React 19–safe replacement for `react-pin-input` `secret`). */
|
|
219
|
+
secret?: boolean;
|
|
220
|
+
autoSelect?: boolean;
|
|
221
|
+
disabled?: boolean;
|
|
222
|
+
/** Applied to the focused cell’s outline (`fieldset`). */
|
|
223
|
+
inputFocusStyle?: CSSProperties;
|
|
224
|
+
/** Applied to the outer `Box` (MUI `sx`). */
|
|
225
|
+
sx?: SxProps<Theme>;
|
|
226
|
+
type?: string;
|
|
227
|
+
inputMode?: React__default.HTMLAttributes<HTMLInputElement>["inputMode"];
|
|
228
|
+
/** Optional per-character filter (same idea as `react-pin-input` `regexCriteria`). */
|
|
229
|
+
regexCriteria?: RegExp;
|
|
230
|
+
};
|
|
231
|
+
declare const OTPField: React__default.FC<OTPFieldProps>;
|
|
232
|
+
|
|
233
|
+
export { CountrySelect, type CountryType, EmptyState, FormDialog, FormDrawer, FormPopover, FormSnackBar, IOSSwitch, ImageUploadAvatar, InputFileUpload, Loader, Logo, OTPField, type OTPFieldProps, PhoneNumberField, Pill, SearchableSelect, SkeletonBar, StatusPill, TabPanel, TablePagination, type TablePaginationDesignProps };
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,7 @@ __export(index_exports, {
|
|
|
42
42
|
InputFileUpload: () => InputFileUpload_default,
|
|
43
43
|
Loader: () => Loader_default,
|
|
44
44
|
Logo: () => Logo_default,
|
|
45
|
+
OTPField: () => OTPField_default,
|
|
45
46
|
PhoneNumberField: () => PhoneNumberField_default,
|
|
46
47
|
Pill: () => Pill_default,
|
|
47
48
|
SearchableSelect: () => SearchableSelect_default,
|
|
@@ -2274,6 +2275,178 @@ var CountrySelect = ({
|
|
|
2274
2275
|
);
|
|
2275
2276
|
};
|
|
2276
2277
|
var CountrySelect_default = CountrySelect;
|
|
2278
|
+
|
|
2279
|
+
// src/OTPField.tsx
|
|
2280
|
+
var import_react11 = require("react");
|
|
2281
|
+
var import_material17 = require("@mui/material");
|
|
2282
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
2283
|
+
function toCells(s, len) {
|
|
2284
|
+
const slice = (s || "").slice(0, len);
|
|
2285
|
+
return Array.from({ length: len }, (_, i) => {
|
|
2286
|
+
var _a2;
|
|
2287
|
+
return (_a2 = slice[i]) != null ? _a2 : "";
|
|
2288
|
+
});
|
|
2289
|
+
}
|
|
2290
|
+
var OTPField = ({
|
|
2291
|
+
length,
|
|
2292
|
+
initialValue = "",
|
|
2293
|
+
onChange,
|
|
2294
|
+
onComplete,
|
|
2295
|
+
secret = false,
|
|
2296
|
+
autoSelect = false,
|
|
2297
|
+
disabled = false,
|
|
2298
|
+
inputFocusStyle,
|
|
2299
|
+
sx,
|
|
2300
|
+
type = "text",
|
|
2301
|
+
inputMode = "text",
|
|
2302
|
+
regexCriteria
|
|
2303
|
+
}) => {
|
|
2304
|
+
const [cells, setCells] = (0, import_react11.useState)(() => toCells(initialValue, length));
|
|
2305
|
+
const cellsRef = (0, import_react11.useRef)(cells);
|
|
2306
|
+
cellsRef.current = cells;
|
|
2307
|
+
const refs = (0, import_react11.useRef)([]);
|
|
2308
|
+
(0, import_react11.useEffect)(() => {
|
|
2309
|
+
setCells(toCells(initialValue, length));
|
|
2310
|
+
}, [initialValue, length]);
|
|
2311
|
+
const emit = (0, import_react11.useCallback)(
|
|
2312
|
+
(next, editedIndex) => {
|
|
2313
|
+
setCells(next);
|
|
2314
|
+
const joined = next.join("");
|
|
2315
|
+
onChange(joined, editedIndex);
|
|
2316
|
+
if (onComplete && joined.length === length && next.every(Boolean)) {
|
|
2317
|
+
onComplete(joined, length - 1);
|
|
2318
|
+
}
|
|
2319
|
+
},
|
|
2320
|
+
[length, onChange, onComplete]
|
|
2321
|
+
);
|
|
2322
|
+
(0, import_react11.useEffect)(() => {
|
|
2323
|
+
if (!autoSelect) return;
|
|
2324
|
+
const id = requestAnimationFrame(() => {
|
|
2325
|
+
var _a2;
|
|
2326
|
+
return (_a2 = refs.current[0]) == null ? void 0 : _a2.focus();
|
|
2327
|
+
});
|
|
2328
|
+
return () => cancelAnimationFrame(id);
|
|
2329
|
+
}, [autoSelect, length]);
|
|
2330
|
+
const charAllowed = (0, import_react11.useCallback)(
|
|
2331
|
+
(ch) => {
|
|
2332
|
+
if (!ch) return true;
|
|
2333
|
+
if (type === "numeric" && /\D/.test(ch)) return false;
|
|
2334
|
+
if (regexCriteria && !regexCriteria.test(ch)) return false;
|
|
2335
|
+
return true;
|
|
2336
|
+
},
|
|
2337
|
+
[regexCriteria, type]
|
|
2338
|
+
);
|
|
2339
|
+
const applyMany = (0, import_react11.useCallback)(
|
|
2340
|
+
(startIdx, raw) => {
|
|
2341
|
+
var _a2;
|
|
2342
|
+
const incoming = (type === "numeric" ? raw.replace(/\D/g, "") : raw).split("");
|
|
2343
|
+
const next = [...cellsRef.current];
|
|
2344
|
+
let i = startIdx;
|
|
2345
|
+
for (const ch of incoming) {
|
|
2346
|
+
if (i >= length) break;
|
|
2347
|
+
if (!charAllowed(ch)) return;
|
|
2348
|
+
next[i] = ch;
|
|
2349
|
+
i += 1;
|
|
2350
|
+
}
|
|
2351
|
+
const focusAt = Math.min(Math.max(i - 1, 0), length - 1);
|
|
2352
|
+
emit(next, focusAt);
|
|
2353
|
+
(_a2 = refs.current[focusAt]) == null ? void 0 : _a2.focus();
|
|
2354
|
+
},
|
|
2355
|
+
[charAllowed, emit, length, type]
|
|
2356
|
+
);
|
|
2357
|
+
const onFieldChange = (idx, e) => {
|
|
2358
|
+
var _a2;
|
|
2359
|
+
const raw = e.target.value;
|
|
2360
|
+
if (raw.length > 1) {
|
|
2361
|
+
applyMany(idx, raw);
|
|
2362
|
+
return;
|
|
2363
|
+
}
|
|
2364
|
+
if (raw && !charAllowed(raw)) return;
|
|
2365
|
+
const next = [...cellsRef.current];
|
|
2366
|
+
next[idx] = raw;
|
|
2367
|
+
emit(next, idx);
|
|
2368
|
+
if (raw && idx < length - 1) {
|
|
2369
|
+
(_a2 = refs.current[idx + 1]) == null ? void 0 : _a2.focus();
|
|
2370
|
+
}
|
|
2371
|
+
};
|
|
2372
|
+
const onKeyDown = (idx, e) => {
|
|
2373
|
+
var _a2, _b, _c;
|
|
2374
|
+
if (e.key === "Backspace") {
|
|
2375
|
+
const next = [...cellsRef.current];
|
|
2376
|
+
if (next[idx]) {
|
|
2377
|
+
next[idx] = "";
|
|
2378
|
+
emit(next, idx);
|
|
2379
|
+
} else if (idx > 0) {
|
|
2380
|
+
next[idx - 1] = "";
|
|
2381
|
+
emit(next, idx - 1);
|
|
2382
|
+
(_a2 = refs.current[idx - 1]) == null ? void 0 : _a2.focus();
|
|
2383
|
+
}
|
|
2384
|
+
e.preventDefault();
|
|
2385
|
+
} else if (e.key === "ArrowLeft" && idx > 0) {
|
|
2386
|
+
(_b = refs.current[idx - 1]) == null ? void 0 : _b.focus();
|
|
2387
|
+
e.preventDefault();
|
|
2388
|
+
} else if (e.key === "ArrowRight" && idx < length - 1) {
|
|
2389
|
+
(_c = refs.current[idx + 1]) == null ? void 0 : _c.focus();
|
|
2390
|
+
e.preventDefault();
|
|
2391
|
+
}
|
|
2392
|
+
};
|
|
2393
|
+
const onPasteFirst = (e) => {
|
|
2394
|
+
e.preventDefault();
|
|
2395
|
+
const text = e.clipboardData.getData("text").replace(/\s/g, "");
|
|
2396
|
+
if (!text) return;
|
|
2397
|
+
applyMany(0, text);
|
|
2398
|
+
};
|
|
2399
|
+
const inputType = secret ? "password" : type === "numeric" ? "tel" : "text";
|
|
2400
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
2401
|
+
import_material17.Box,
|
|
2402
|
+
{
|
|
2403
|
+
sx: [
|
|
2404
|
+
{
|
|
2405
|
+
display: "flex",
|
|
2406
|
+
gap: 1,
|
|
2407
|
+
flexWrap: "nowrap",
|
|
2408
|
+
justifyContent: "center",
|
|
2409
|
+
alignItems: "center"
|
|
2410
|
+
},
|
|
2411
|
+
...sx == null ? [] : Array.isArray(sx) ? sx : [sx]
|
|
2412
|
+
],
|
|
2413
|
+
children: Array.from({ length }).map((_, idx) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
2414
|
+
import_material17.TextField,
|
|
2415
|
+
{
|
|
2416
|
+
inputRef: (el) => {
|
|
2417
|
+
refs.current[idx] = el;
|
|
2418
|
+
},
|
|
2419
|
+
value: cells[idx],
|
|
2420
|
+
onChange: (e) => onFieldChange(idx, e),
|
|
2421
|
+
onKeyDown: (e) => onKeyDown(idx, e),
|
|
2422
|
+
onPaste: idx === 0 ? onPasteFirst : void 0,
|
|
2423
|
+
disabled,
|
|
2424
|
+
size: "small",
|
|
2425
|
+
type: inputType,
|
|
2426
|
+
sx: {
|
|
2427
|
+
width: 44,
|
|
2428
|
+
"& .MuiOutlinedInput-input": {
|
|
2429
|
+
textAlign: "center",
|
|
2430
|
+
py: 1,
|
|
2431
|
+
px: 0.5
|
|
2432
|
+
},
|
|
2433
|
+
...inputFocusStyle ? {
|
|
2434
|
+
"& .MuiOutlinedInput-root.Mui-focused fieldset": inputFocusStyle
|
|
2435
|
+
} : {}
|
|
2436
|
+
},
|
|
2437
|
+
inputProps: {
|
|
2438
|
+
maxLength: 1,
|
|
2439
|
+
inputMode: inputMode != null ? inputMode : void 0,
|
|
2440
|
+
"aria-label": `Code character ${idx + 1} of ${length}`,
|
|
2441
|
+
autoComplete: "one-time-code"
|
|
2442
|
+
}
|
|
2443
|
+
},
|
|
2444
|
+
idx
|
|
2445
|
+
))
|
|
2446
|
+
}
|
|
2447
|
+
);
|
|
2448
|
+
};
|
|
2449
|
+
var OTPField_default = OTPField;
|
|
2277
2450
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2278
2451
|
0 && (module.exports = {
|
|
2279
2452
|
CountrySelect,
|
|
@@ -2287,6 +2460,7 @@ var CountrySelect_default = CountrySelect;
|
|
|
2287
2460
|
InputFileUpload,
|
|
2288
2461
|
Loader,
|
|
2289
2462
|
Logo,
|
|
2463
|
+
OTPField,
|
|
2290
2464
|
PhoneNumberField,
|
|
2291
2465
|
Pill,
|
|
2292
2466
|
SearchableSelect,
|