From ac9d3d4bd93be91f785a06d844e9e2ccfe0ef156 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Apr 2026 05:51:18 +0000 Subject: [PATCH 1/3] Initial plan From fdf293650dc701d397220dd42f936e8eca8d7b61 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Apr 2026 05:53:15 +0000 Subject: [PATCH 2/3] chore: organize npm package repository structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add MIT LICENSE file - Expand .gitignore (dist/, logs, .env, .DS_Store) - Add package.json metadata: license, repository, bugs, homepage, engines - Fix README version (1.0.8 → 1.0.11), expand Contributing section - Add GitHub Actions CI workflow (Node 18/20/22 matrix) - Remove dist/ from git tracking (build artifact) Agent-Logs-Url: https://github.com/mrashed21/bd-number-validator/sessions/be134569-daf1-442f-addc-fa02087c547c Co-authored-by: mrashed21 <143103102+mrashed21@users.noreply.github.com> --- .github/workflows/ci.yml | 31 ++++++ .gitignore | 9 +- LICENSE | 21 ++++ README.md | 24 +++- dist/chunk-DVWXJMTU.js | 202 ---------------------------------- dist/index.cjs | 232 --------------------------------------- dist/index.d.cts | 14 --- dist/index.d.ts | 14 --- dist/index.js | 10 -- dist/react.cjs | 230 -------------------------------------- dist/react.css | 46 -------- dist/react.d.cts | 33 ------ dist/react.d.ts | 33 ------ dist/react.js | 8 -- package.json | 14 ++- 15 files changed, 96 insertions(+), 825 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 LICENSE delete mode 100644 dist/chunk-DVWXJMTU.js delete mode 100644 dist/index.cjs delete mode 100644 dist/index.d.cts delete mode 100644 dist/index.d.ts delete mode 100644 dist/index.js delete mode 100644 dist/react.cjs delete mode 100644 dist/react.css delete mode 100644 dist/react.d.cts delete mode 100644 dist/react.d.ts delete mode 100644 dist/react.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..869cc0c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + name: Build (Node ${{ matrix.node-version }}) + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18.x, 20.x, 22.x] + + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build diff --git a/.gitignore b/.gitignore index b512c09..ff1d2bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,8 @@ -node_modules \ No newline at end of file +node_modules +dist +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.env +.DS_Store \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5cab005 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Muhammad Rashed + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 0ddbb1c..c1bd830 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A lightweight, production-ready validator for Bangladesh mobile numbers with nor [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) **Author:** Muhammad Rashed -**Version:** 1.0.8 +**Version:** 1.0.11 --- @@ -597,6 +597,28 @@ Pull requests, issues, and feedback are welcome! **GitHub Repository:** [https://github.com/mrashed21/bd-number-validator](https://github.com/mrashed21/bd-number-validator) +### Development + +```bash +# Clone the repository +git clone https://github.com/mrashed21/bd-number-validator.git +cd bd-number-validator + +# Install dependencies +npm install + +# Build the package +npm run build +``` + +### Submitting Changes + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/my-feature`) +3. Commit your changes (`git commit -m 'Add my feature'`) +4. Push to the branch (`git push origin feature/my-feature`) +5. Open a Pull Request + --- ## 📧 Support diff --git a/dist/chunk-DVWXJMTU.js b/dist/chunk-DVWXJMTU.js deleted file mode 100644 index 6ba472b..0000000 --- a/dist/chunk-DVWXJMTU.js +++ /dev/null @@ -1,202 +0,0 @@ -// src/validate.ts -function validatePhoneNumber(input) { - if (!input || input.trim() === "") { - return { isValid: true }; - } - let value = input.replace(/[^0-9]/g, ""); - let raw = ""; - if (value.startsWith("8801")) { - raw = value.slice(2); - } else if (value.startsWith("880")) { - raw = "0" + value.slice(3); - } else if (value.startsWith("801")) { - raw = value.slice(1); - } else if (value.startsWith("80")) { - raw = "0" + value.slice(2); - } else if (value.startsWith("0")) { - raw = value; - } else { - raw = "0" + value; - } - const operators = { - Grameenphone: ["017", "013"], - Banglalink: ["019", "014"], - Robi: ["018"], - Airtel: ["016"], - Teletalk: ["015"] - }; - let operator = "Unknown"; - if (raw.length >= 3) { - const prefix = raw.slice(0, 3); - for (const [name, prefixList] of Object.entries(operators)) { - if (prefixList.includes(prefix)) { - operator = name; - break; - } - } - if (operator === "Unknown") { - return { isValid: false, error: "Invalid operator" }; - } - } - if (raw.length < 3) { - return { isValid: true }; - } - if (raw.length === 3) { - return { isValid: true, operator }; - } - if (raw.length > 3 && raw.length < 11) { - return { isValid: false, error: "Invalid number" }; - } - if (raw.length > 11) { - return { isValid: false, error: "Invalid number" }; - } - if (raw.length === 11) { - const normalized = "+880" + raw.slice(1); - return { - isValid: true, - operator, - normalized - }; - } - return { isValid: false, error: "Invalid number" }; -} - -// src/react/useBDPhone.ts -import { useState } from "react"; -function useBDPhone(initial = "") { - const [value, setValue] = useState(initial); - const onChange = (v) => { - const cleaned = v.replace(/[^0-9]/g, ""); - setValue(cleaned); - }; - const validation = validatePhoneNumber(value); - return { - raw: value, - normalized: validation.normalized, - operator: validation.operator, - error: validation.error, - isValid: validation.isValid, - onChange - }; -} - -// src/react/BDPhoneInput.tsx -import { useEffect, useRef } from "react"; - -// src/react/formatBDPhoneUI.ts -function formatBDPhoneUI(raw) { - if (!raw) return ""; - const digits = raw.replace(/\D/g, ""); - if (digits.length <= 3) return digits; - if (digits.length <= 7) return `${digits.slice(0, 3)} ${digits.slice(3)}`; - return `${digits.slice(0, 3)} ${digits.slice(3, 7)} ${digits.slice(7, 13)}`; -} - -// src/react/BDPhoneInput.tsx -import { jsx, jsxs } from "react/jsx-runtime"; -function BDPhoneInput({ - value, - onValueChange, - showError = true, - label = "Phone Number", - showLabel = true, - labelClass = "bdp-label", - renderLabel, - containerClass = "bdp-wrapper", - wrapperClass = "bdp-input-box", - flagClass = "bdp-flag", - prefixClass = "bdp-prefix", - inputClass = "bdp-input", - errorClass = "bdp-error-text", - renderFlag, - renderPrefix, - renderError -}) { - const { raw, onChange, error, isValid, normalized } = useBDPhone(value != null ? value : ""); - const inputRef = useRef(null); - const cursorPositionRef = useRef(0); - const handleChange = (e) => { - var _a; - const input = e.target; - const cursorPos = (_a = input.selectionStart) != null ? _a : 0; - const newValue = e.target.value; - const newDigits = newValue.replace(/\D/g, ""); - let maxLength; - if (newDigits.startsWith("8801")) maxLength = 15; - else if (newDigits.startsWith("880")) maxLength = 15; - else if (newDigits.startsWith("801")) maxLength = 15; - else if (newDigits.startsWith("80")) maxLength = 15; - else if (newDigits.startsWith("0")) maxLength = 11; - else maxLength = 10; - const limitedDigits = newDigits.slice(0, maxLength); - let digitsBeforeCursor = 0; - for (let i = 0; i < cursorPos && i < newValue.length; i++) { - if (/\d/.test(newValue[i])) digitsBeforeCursor++; - } - onChange(limitedDigits); - const newFormatted = formatBDPhoneUI(limitedDigits); - let newCursorPos = 0; - let digitCount = 0; - for (let i = 0; i < newFormatted.length; i++) { - if (/\d/.test(newFormatted[i])) { - digitCount++; - if (digitCount >= digitsBeforeCursor) { - newCursorPos = i + 1; - break; - } - } - } - if (digitCount < digitsBeforeCursor) { - newCursorPos = newFormatted.length; - } - cursorPositionRef.current = newCursorPos; - }; - useEffect(() => { - const input = inputRef.current; - if (input && document.activeElement === input) { - input.setSelectionRange( - cursorPositionRef.current, - cursorPositionRef.current - ); - } - }); - useEffect(() => { - onValueChange == null ? void 0 : onValueChange(isValid ? normalized : void 0); - }, [raw, isValid, normalized, onValueChange]); - return /* @__PURE__ */ jsxs("div", { className: containerClass, children: [ - showLabel && label && (renderLabel ? renderLabel(label) : /* @__PURE__ */ jsx("label", { className: labelClass, children: label })), - /* @__PURE__ */ jsxs( - "div", - { - className: wrapperClass, - style: { - borderColor: !isValid && raw.length >= 3 ? "#ef4444" : void 0 - }, - children: [ - renderFlag ? renderFlag() : /* @__PURE__ */ jsx("div", { className: flagClass, children: /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 30 20", children: [ - /* @__PURE__ */ jsx("rect", { width: "30", height: "20", fill: "#006a4e" }), - /* @__PURE__ */ jsx("circle", { cx: "12.5", cy: "10", r: "5", fill: "#f42a41" }) - ] }) }), - renderPrefix ? renderPrefix() : /* @__PURE__ */ jsx("span", { className: prefixClass, children: "+880" }), - /* @__PURE__ */ jsx( - "input", - { - ref: inputRef, - className: inputClass, - placeholder: "1XX XXXX XXXX", - value: formatBDPhoneUI(raw), - onChange: handleChange - } - ) - ] - } - ), - showError && raw.length >= 3 && error && (renderError ? renderError(error) : /* @__PURE__ */ jsx("span", { className: errorClass, children: error })) - ] }); -} - -export { - validatePhoneNumber, - useBDPhone, - BDPhoneInput -}; diff --git a/dist/index.cjs b/dist/index.cjs deleted file mode 100644 index e193855..0000000 --- a/dist/index.cjs +++ /dev/null @@ -1,232 +0,0 @@ -"use strict"; -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); -}; -var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; -}; -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); - -// src/index.ts -var src_exports = {}; -__export(src_exports, { - BDPhoneInput: () => BDPhoneInput, - useBDPhone: () => useBDPhone, - validatePhoneNumber: () => validatePhoneNumber -}); -module.exports = __toCommonJS(src_exports); - -// src/react/BDPhoneInput.tsx -var import_react2 = require("react"); - -// src/react/formatBDPhoneUI.ts -function formatBDPhoneUI(raw) { - if (!raw) return ""; - const digits = raw.replace(/\D/g, ""); - if (digits.length <= 3) return digits; - if (digits.length <= 7) return `${digits.slice(0, 3)} ${digits.slice(3)}`; - return `${digits.slice(0, 3)} ${digits.slice(3, 7)} ${digits.slice(7, 13)}`; -} - -// src/react/useBDPhone.ts -var import_react = require("react"); - -// src/validate.ts -function validatePhoneNumber(input) { - if (!input || input.trim() === "") { - return { isValid: true }; - } - let value = input.replace(/[^0-9]/g, ""); - let raw = ""; - if (value.startsWith("8801")) { - raw = value.slice(2); - } else if (value.startsWith("880")) { - raw = "0" + value.slice(3); - } else if (value.startsWith("801")) { - raw = value.slice(1); - } else if (value.startsWith("80")) { - raw = "0" + value.slice(2); - } else if (value.startsWith("0")) { - raw = value; - } else { - raw = "0" + value; - } - const operators = { - Grameenphone: ["017", "013"], - Banglalink: ["019", "014"], - Robi: ["018"], - Airtel: ["016"], - Teletalk: ["015"] - }; - let operator = "Unknown"; - if (raw.length >= 3) { - const prefix = raw.slice(0, 3); - for (const [name, prefixList] of Object.entries(operators)) { - if (prefixList.includes(prefix)) { - operator = name; - break; - } - } - if (operator === "Unknown") { - return { isValid: false, error: "Invalid operator" }; - } - } - if (raw.length < 3) { - return { isValid: true }; - } - if (raw.length === 3) { - return { isValid: true, operator }; - } - if (raw.length > 3 && raw.length < 11) { - return { isValid: false, error: "Invalid number" }; - } - if (raw.length > 11) { - return { isValid: false, error: "Invalid number" }; - } - if (raw.length === 11) { - const normalized = "+880" + raw.slice(1); - return { - isValid: true, - operator, - normalized - }; - } - return { isValid: false, error: "Invalid number" }; -} - -// src/react/useBDPhone.ts -function useBDPhone(initial = "") { - const [value, setValue] = (0, import_react.useState)(initial); - const onChange = (v) => { - const cleaned = v.replace(/[^0-9]/g, ""); - setValue(cleaned); - }; - const validation = validatePhoneNumber(value); - return { - raw: value, - normalized: validation.normalized, - operator: validation.operator, - error: validation.error, - isValid: validation.isValid, - onChange - }; -} - -// src/react/BDPhoneInput.tsx -var import_jsx_runtime = require("react/jsx-runtime"); -function BDPhoneInput({ - value, - onValueChange, - showError = true, - label = "Phone Number", - showLabel = true, - labelClass = "bdp-label", - renderLabel, - containerClass = "bdp-wrapper", - wrapperClass = "bdp-input-box", - flagClass = "bdp-flag", - prefixClass = "bdp-prefix", - inputClass = "bdp-input", - errorClass = "bdp-error-text", - renderFlag, - renderPrefix, - renderError -}) { - const { raw, onChange, error, isValid, normalized } = useBDPhone(value != null ? value : ""); - const inputRef = (0, import_react2.useRef)(null); - const cursorPositionRef = (0, import_react2.useRef)(0); - const handleChange = (e) => { - var _a; - const input = e.target; - const cursorPos = (_a = input.selectionStart) != null ? _a : 0; - const newValue = e.target.value; - const newDigits = newValue.replace(/\D/g, ""); - let maxLength; - if (newDigits.startsWith("8801")) maxLength = 15; - else if (newDigits.startsWith("880")) maxLength = 15; - else if (newDigits.startsWith("801")) maxLength = 15; - else if (newDigits.startsWith("80")) maxLength = 15; - else if (newDigits.startsWith("0")) maxLength = 11; - else maxLength = 10; - const limitedDigits = newDigits.slice(0, maxLength); - let digitsBeforeCursor = 0; - for (let i = 0; i < cursorPos && i < newValue.length; i++) { - if (/\d/.test(newValue[i])) digitsBeforeCursor++; - } - onChange(limitedDigits); - const newFormatted = formatBDPhoneUI(limitedDigits); - let newCursorPos = 0; - let digitCount = 0; - for (let i = 0; i < newFormatted.length; i++) { - if (/\d/.test(newFormatted[i])) { - digitCount++; - if (digitCount >= digitsBeforeCursor) { - newCursorPos = i + 1; - break; - } - } - } - if (digitCount < digitsBeforeCursor) { - newCursorPos = newFormatted.length; - } - cursorPositionRef.current = newCursorPos; - }; - (0, import_react2.useEffect)(() => { - const input = inputRef.current; - if (input && document.activeElement === input) { - input.setSelectionRange( - cursorPositionRef.current, - cursorPositionRef.current - ); - } - }); - (0, import_react2.useEffect)(() => { - onValueChange == null ? void 0 : onValueChange(isValid ? normalized : void 0); - }, [raw, isValid, normalized, onValueChange]); - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: containerClass, children: [ - showLabel && label && (renderLabel ? renderLabel(label) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: labelClass, children: label })), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( - "div", - { - className: wrapperClass, - style: { - borderColor: !isValid && raw.length >= 3 ? "#ef4444" : void 0 - }, - children: [ - renderFlag ? renderFlag() : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: flagClass, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 30 20", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: "30", height: "20", fill: "#006a4e" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12.5", cy: "10", r: "5", fill: "#f42a41" }) - ] }) }), - renderPrefix ? renderPrefix() : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: prefixClass, children: "+880" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)( - "input", - { - ref: inputRef, - className: inputClass, - placeholder: "1XX XXXX XXXX", - value: formatBDPhoneUI(raw), - onChange: handleChange - } - ) - ] - } - ), - showError && raw.length >= 3 && error && (renderError ? renderError(error) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: errorClass, children: error })) - ] }); -} -// Annotate the CommonJS export names for ESM import in node: -0 && (module.exports = { - BDPhoneInput, - useBDPhone, - validatePhoneNumber -}); diff --git a/dist/index.d.cts b/dist/index.d.cts deleted file mode 100644 index c02fc6b..0000000 --- a/dist/index.d.cts +++ /dev/null @@ -1,14 +0,0 @@ -export { BDPhoneInput, useBDPhone } from './react.cjs'; -import 'react/jsx-runtime'; -import 'react'; - -type Operator = "Grameenphone" | "Banglalink" | "Robi" | "Airtel" | "Teletalk" | "Unknown"; -interface PhoneValidationResult { - isValid: boolean; - normalized?: string; - operator?: Operator; - error?: string; -} -declare function validatePhoneNumber(input: string): PhoneValidationResult; - -export { validatePhoneNumber }; diff --git a/dist/index.d.ts b/dist/index.d.ts deleted file mode 100644 index acc66d9..0000000 --- a/dist/index.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -export { BDPhoneInput, useBDPhone } from './react.js'; -import 'react/jsx-runtime'; -import 'react'; - -type Operator = "Grameenphone" | "Banglalink" | "Robi" | "Airtel" | "Teletalk" | "Unknown"; -interface PhoneValidationResult { - isValid: boolean; - normalized?: string; - operator?: Operator; - error?: string; -} -declare function validatePhoneNumber(input: string): PhoneValidationResult; - -export { validatePhoneNumber }; diff --git a/dist/index.js b/dist/index.js deleted file mode 100644 index 29114b1..0000000 --- a/dist/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import { - BDPhoneInput, - useBDPhone, - validatePhoneNumber -} from "./chunk-DVWXJMTU.js"; -export { - BDPhoneInput, - useBDPhone, - validatePhoneNumber -}; diff --git a/dist/react.cjs b/dist/react.cjs deleted file mode 100644 index 337ece4..0000000 --- a/dist/react.cjs +++ /dev/null @@ -1,230 +0,0 @@ -"use strict"; -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); -}; -var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; -}; -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); - -// src/react/index.ts -var react_exports = {}; -__export(react_exports, { - BDPhoneInput: () => BDPhoneInput, - useBDPhone: () => useBDPhone -}); -module.exports = __toCommonJS(react_exports); - -// src/react/BDPhoneInput.tsx -var import_react2 = require("react"); - -// src/react/formatBDPhoneUI.ts -function formatBDPhoneUI(raw) { - if (!raw) return ""; - const digits = raw.replace(/\D/g, ""); - if (digits.length <= 3) return digits; - if (digits.length <= 7) return `${digits.slice(0, 3)} ${digits.slice(3)}`; - return `${digits.slice(0, 3)} ${digits.slice(3, 7)} ${digits.slice(7, 13)}`; -} - -// src/react/useBDPhone.ts -var import_react = require("react"); - -// src/validate.ts -function validatePhoneNumber(input) { - if (!input || input.trim() === "") { - return { isValid: true }; - } - let value = input.replace(/[^0-9]/g, ""); - let raw = ""; - if (value.startsWith("8801")) { - raw = value.slice(2); - } else if (value.startsWith("880")) { - raw = "0" + value.slice(3); - } else if (value.startsWith("801")) { - raw = value.slice(1); - } else if (value.startsWith("80")) { - raw = "0" + value.slice(2); - } else if (value.startsWith("0")) { - raw = value; - } else { - raw = "0" + value; - } - const operators = { - Grameenphone: ["017", "013"], - Banglalink: ["019", "014"], - Robi: ["018"], - Airtel: ["016"], - Teletalk: ["015"] - }; - let operator = "Unknown"; - if (raw.length >= 3) { - const prefix = raw.slice(0, 3); - for (const [name, prefixList] of Object.entries(operators)) { - if (prefixList.includes(prefix)) { - operator = name; - break; - } - } - if (operator === "Unknown") { - return { isValid: false, error: "Invalid operator" }; - } - } - if (raw.length < 3) { - return { isValid: true }; - } - if (raw.length === 3) { - return { isValid: true, operator }; - } - if (raw.length > 3 && raw.length < 11) { - return { isValid: false, error: "Invalid number" }; - } - if (raw.length > 11) { - return { isValid: false, error: "Invalid number" }; - } - if (raw.length === 11) { - const normalized = "+880" + raw.slice(1); - return { - isValid: true, - operator, - normalized - }; - } - return { isValid: false, error: "Invalid number" }; -} - -// src/react/useBDPhone.ts -function useBDPhone(initial = "") { - const [value, setValue] = (0, import_react.useState)(initial); - const onChange = (v) => { - const cleaned = v.replace(/[^0-9]/g, ""); - setValue(cleaned); - }; - const validation = validatePhoneNumber(value); - return { - raw: value, - normalized: validation.normalized, - operator: validation.operator, - error: validation.error, - isValid: validation.isValid, - onChange - }; -} - -// src/react/BDPhoneInput.tsx -var import_jsx_runtime = require("react/jsx-runtime"); -function BDPhoneInput({ - value, - onValueChange, - showError = true, - label = "Phone Number", - showLabel = true, - labelClass = "bdp-label", - renderLabel, - containerClass = "bdp-wrapper", - wrapperClass = "bdp-input-box", - flagClass = "bdp-flag", - prefixClass = "bdp-prefix", - inputClass = "bdp-input", - errorClass = "bdp-error-text", - renderFlag, - renderPrefix, - renderError -}) { - const { raw, onChange, error, isValid, normalized } = useBDPhone(value != null ? value : ""); - const inputRef = (0, import_react2.useRef)(null); - const cursorPositionRef = (0, import_react2.useRef)(0); - const handleChange = (e) => { - var _a; - const input = e.target; - const cursorPos = (_a = input.selectionStart) != null ? _a : 0; - const newValue = e.target.value; - const newDigits = newValue.replace(/\D/g, ""); - let maxLength; - if (newDigits.startsWith("8801")) maxLength = 15; - else if (newDigits.startsWith("880")) maxLength = 15; - else if (newDigits.startsWith("801")) maxLength = 15; - else if (newDigits.startsWith("80")) maxLength = 15; - else if (newDigits.startsWith("0")) maxLength = 11; - else maxLength = 10; - const limitedDigits = newDigits.slice(0, maxLength); - let digitsBeforeCursor = 0; - for (let i = 0; i < cursorPos && i < newValue.length; i++) { - if (/\d/.test(newValue[i])) digitsBeforeCursor++; - } - onChange(limitedDigits); - const newFormatted = formatBDPhoneUI(limitedDigits); - let newCursorPos = 0; - let digitCount = 0; - for (let i = 0; i < newFormatted.length; i++) { - if (/\d/.test(newFormatted[i])) { - digitCount++; - if (digitCount >= digitsBeforeCursor) { - newCursorPos = i + 1; - break; - } - } - } - if (digitCount < digitsBeforeCursor) { - newCursorPos = newFormatted.length; - } - cursorPositionRef.current = newCursorPos; - }; - (0, import_react2.useEffect)(() => { - const input = inputRef.current; - if (input && document.activeElement === input) { - input.setSelectionRange( - cursorPositionRef.current, - cursorPositionRef.current - ); - } - }); - (0, import_react2.useEffect)(() => { - onValueChange == null ? void 0 : onValueChange(isValid ? normalized : void 0); - }, [raw, isValid, normalized, onValueChange]); - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: containerClass, children: [ - showLabel && label && (renderLabel ? renderLabel(label) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: labelClass, children: label })), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( - "div", - { - className: wrapperClass, - style: { - borderColor: !isValid && raw.length >= 3 ? "#ef4444" : void 0 - }, - children: [ - renderFlag ? renderFlag() : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: flagClass, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 30 20", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: "30", height: "20", fill: "#006a4e" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12.5", cy: "10", r: "5", fill: "#f42a41" }) - ] }) }), - renderPrefix ? renderPrefix() : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: prefixClass, children: "+880" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)( - "input", - { - ref: inputRef, - className: inputClass, - placeholder: "1XX XXXX XXXX", - value: formatBDPhoneUI(raw), - onChange: handleChange - } - ) - ] - } - ), - showError && raw.length >= 3 && error && (renderError ? renderError(error) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: errorClass, children: error })) - ] }); -} -// Annotate the CommonJS export names for ESM import in node: -0 && (module.exports = { - BDPhoneInput, - useBDPhone -}); diff --git a/dist/react.css b/dist/react.css deleted file mode 100644 index 80d916b..0000000 --- a/dist/react.css +++ /dev/null @@ -1,46 +0,0 @@ -/* src/react/bd-phone.css */ -.bdp-wrapper { - display: flex; - flex-direction: column; - gap: 4px; - width: 100%; -} -.bdp-label { - font-size: 14px; - font-weight: 500; - color: #374151; -} -.bdp-input-box { - display: flex; - align-items: center; - gap: 8px; - background: #ffffff; - border: 1px solid #d1d5db; - padding: 12px; - border-radius: 8px; - transition: 0.2s border ease; -} -.bdp-error-border { - border-color: #ef4444 !important; -} -.bdp-flag { - width: 30px; - height: 25px; - border-radius: 3px; -} -.bdp-prefix { - font-weight: 700; - color: #1f2937; -} -.bdp-input { - flex: 1; - border: none; - outline: none; - background: transparent; - font-size: 16px; - color: #111827; -} -.bdp-error-text { - font-size: 14px; - color: #dc2626; -} diff --git a/dist/react.d.cts b/dist/react.d.cts deleted file mode 100644 index b341134..0000000 --- a/dist/react.d.cts +++ /dev/null @@ -1,33 +0,0 @@ -import * as react_jsx_runtime from 'react/jsx-runtime'; -import { ReactNode } from 'react'; - -interface BDPhoneInputProps { - value?: string; - onValueChange?: (v?: string) => void; - showError?: boolean; - label?: string; - showLabel?: boolean; - labelClass?: string; - renderLabel?: (label: string) => ReactNode; - containerClass?: string; - wrapperClass?: string; - flagClass?: string; - prefixClass?: string; - inputClass?: string; - errorClass?: string; - renderFlag?: () => ReactNode; - renderPrefix?: () => ReactNode; - renderError?: (error: string) => ReactNode; -} -declare function BDPhoneInput({ value, onValueChange, showError, label, showLabel, labelClass, renderLabel, containerClass, wrapperClass, flagClass, prefixClass, inputClass, errorClass, renderFlag, renderPrefix, renderError, }: BDPhoneInputProps): react_jsx_runtime.JSX.Element; - -declare function useBDPhone(initial?: string): { - raw: string; - normalized: string | undefined; - operator: ("Grameenphone" | "Banglalink" | "Robi" | "Airtel" | "Teletalk" | "Unknown") | undefined; - error: string | undefined; - isValid: boolean; - onChange: (v: string) => void; -}; - -export { BDPhoneInput, useBDPhone }; diff --git a/dist/react.d.ts b/dist/react.d.ts deleted file mode 100644 index b341134..0000000 --- a/dist/react.d.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as react_jsx_runtime from 'react/jsx-runtime'; -import { ReactNode } from 'react'; - -interface BDPhoneInputProps { - value?: string; - onValueChange?: (v?: string) => void; - showError?: boolean; - label?: string; - showLabel?: boolean; - labelClass?: string; - renderLabel?: (label: string) => ReactNode; - containerClass?: string; - wrapperClass?: string; - flagClass?: string; - prefixClass?: string; - inputClass?: string; - errorClass?: string; - renderFlag?: () => ReactNode; - renderPrefix?: () => ReactNode; - renderError?: (error: string) => ReactNode; -} -declare function BDPhoneInput({ value, onValueChange, showError, label, showLabel, labelClass, renderLabel, containerClass, wrapperClass, flagClass, prefixClass, inputClass, errorClass, renderFlag, renderPrefix, renderError, }: BDPhoneInputProps): react_jsx_runtime.JSX.Element; - -declare function useBDPhone(initial?: string): { - raw: string; - normalized: string | undefined; - operator: ("Grameenphone" | "Banglalink" | "Robi" | "Airtel" | "Teletalk" | "Unknown") | undefined; - error: string | undefined; - isValid: boolean; - onChange: (v: string) => void; -}; - -export { BDPhoneInput, useBDPhone }; diff --git a/dist/react.js b/dist/react.js deleted file mode 100644 index 4e0b6de..0000000 --- a/dist/react.js +++ /dev/null @@ -1,8 +0,0 @@ -import { - BDPhoneInput, - useBDPhone -} from "./chunk-DVWXJMTU.js"; -export { - BDPhoneInput, - useBDPhone -}; diff --git a/package.json b/package.json index 1f1ac09..9d00a03 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,19 @@ "name": "bd-number-validator", "version": "1.0.11", "description": "Bangladesh phone validator with Operator detection. React component and hook for BD phone input with validation and formatting.", - "author": "Muhammad Rashed", + "author": "Muhammad Rashed ", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/mrashed21/bd-number-validator.git" + }, + "bugs": { + "url": "https://github.com/mrashed21/bd-number-validator/issues" + }, + "homepage": "https://github.com/mrashed21/bd-number-validator#readme", + "engines": { + "node": ">=16" + }, "main": "dist/index.cjs", "module": "dist/index.js", "types": "dist/index.d.ts", From 08d3a120edc916345fdb455fd21834bb5a97ef13 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Apr 2026 05:53:54 +0000 Subject: [PATCH 3/3] ci: set explicit GITHUB_TOKEN permissions (contents: read) Agent-Logs-Url: https://github.com/mrashed21/bd-number-validator/sessions/be134569-daf1-442f-addc-fa02087c547c Co-authored-by: mrashed21 <143103102+mrashed21@users.noreply.github.com> --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 869cc0c..dd63ff0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,8 @@ jobs: build: name: Build (Node ${{ matrix.node-version }}) runs-on: ubuntu-latest + permissions: + contents: read strategy: matrix: