@barcodesdk/ui 0.1.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/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ Copyright (c) 2026 Vijay Reddy
2
+
3
+ All rights reserved.
4
+
5
+ This software is proprietary beta software. Unauthorized copying,
6
+ modification, distribution, or use is prohibited except under a written
7
+ license agreement with the copyright holder.
8
+
9
+ Third-party notices for bundled dependencies are in NOTICES.md.
@@ -0,0 +1,8 @@
1
+ export { objectFitRect } from "./object-fit.js";
2
+ export { ScanOverlay } from "./scan-overlay.js";
3
+ export type { ScanOverlayProps } from "./scan-overlay.js";
4
+ export { ScanGuide } from "./scan-guide.js";
5
+ export type { ScanGuideProps } from "./scan-guide.js";
6
+ export { ScannerStatus } from "./scanner-status.js";
7
+ export type { ScannerStatusProps } from "./scanner-status.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,147 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ function objectFitRect(containerWidth, containerHeight, sourceWidth, sourceHeight, fit = "contain") {
4
+ if (containerWidth <= 0 || containerHeight <= 0 || sourceWidth <= 0 || sourceHeight <= 0) {
5
+ return null;
6
+ }
7
+ const scale = fit === "cover" ? Math.max(containerWidth / sourceWidth, containerHeight / sourceHeight) : Math.min(containerWidth / sourceWidth, containerHeight / sourceHeight);
8
+ const width = sourceWidth * scale;
9
+ const height = sourceHeight * scale;
10
+ return {
11
+ left: (containerWidth - width) / 2,
12
+ top: (containerHeight - height) / 2,
13
+ width,
14
+ height
15
+ };
16
+ }
17
+ function ScanOverlay({
18
+ results,
19
+ sourceWidth,
20
+ sourceHeight,
21
+ containerWidth,
22
+ containerHeight,
23
+ objectFit = "contain",
24
+ fill = "rgba(34,197,94,0.18)",
25
+ stroke = "#22c55e",
26
+ className
27
+ }) {
28
+ const fitted = objectFitRect(
29
+ containerWidth,
30
+ containerHeight,
31
+ sourceWidth,
32
+ sourceHeight,
33
+ objectFit
34
+ );
35
+ if (!fitted || results.length === 0) {
36
+ return null;
37
+ }
38
+ const strokeWidth = Math.max(1.5, Math.min(sourceWidth, sourceHeight) / 240);
39
+ return /* @__PURE__ */ jsx(
40
+ "svg",
41
+ {
42
+ className,
43
+ style: {
44
+ pointerEvents: "none",
45
+ position: "absolute",
46
+ left: fitted.left,
47
+ top: fitted.top,
48
+ width: fitted.width,
49
+ height: fitted.height
50
+ },
51
+ viewBox: `0 0 ${sourceWidth} ${sourceHeight}`,
52
+ preserveAspectRatio: "none",
53
+ children: results.map((result) => {
54
+ const points = result.cornerPoints.map((point) => `${point.x},${point.y}`).join(" ");
55
+ return /* @__PURE__ */ jsx(
56
+ "polygon",
57
+ {
58
+ points,
59
+ fill,
60
+ stroke,
61
+ strokeWidth,
62
+ strokeLinejoin: "round"
63
+ },
64
+ result.id
65
+ );
66
+ })
67
+ }
68
+ );
69
+ }
70
+ function ScanGuide({
71
+ className,
72
+ label = "Align barcode inside the frame"
73
+ }) {
74
+ return /* @__PURE__ */ jsx(
75
+ "div",
76
+ {
77
+ className,
78
+ role: "img",
79
+ "aria-label": label,
80
+ style: {
81
+ pointerEvents: "none",
82
+ position: "absolute",
83
+ inset: "18%",
84
+ border: "2px solid rgba(255,255,255,0.85)",
85
+ borderRadius: 12,
86
+ boxShadow: "0 0 0 9999px rgba(0,0,0,0.28)",
87
+ display: "flex",
88
+ alignItems: "flex-end",
89
+ justifyContent: "center",
90
+ paddingBottom: 12
91
+ },
92
+ children: /* @__PURE__ */ jsx(
93
+ "span",
94
+ {
95
+ "aria-hidden": "true",
96
+ style: {
97
+ color: "#fff",
98
+ fontSize: 12,
99
+ fontWeight: 500,
100
+ textShadow: "0 1px 2px rgba(0,0,0,0.6)"
101
+ },
102
+ children: label
103
+ }
104
+ )
105
+ }
106
+ );
107
+ }
108
+ const LABELS = {
109
+ idle: "Idle",
110
+ initializing: "Initializing",
111
+ ready: "Ready to scan",
112
+ scanning: "Scanning",
113
+ paused: "Paused",
114
+ error: "Scanner error",
115
+ destroyed: "Scanner closed"
116
+ };
117
+ const LIVE_LABELS = {
118
+ idle: "Scanner idle",
119
+ initializing: "Scanner initializing",
120
+ ready: "Scanner ready",
121
+ scanning: "Scanner actively scanning",
122
+ paused: "Scanner paused",
123
+ error: "Scanner encountered an error",
124
+ destroyed: "Scanner closed"
125
+ };
126
+ function ScannerStatus({ state, className, label }) {
127
+ const visible = label ?? LABELS[state];
128
+ return /* @__PURE__ */ jsx(
129
+ "span",
130
+ {
131
+ className,
132
+ "data-state": state,
133
+ role: "status",
134
+ "aria-live": "polite",
135
+ "aria-atomic": "true",
136
+ "aria-label": LIVE_LABELS[state],
137
+ children: visible
138
+ }
139
+ );
140
+ }
141
+ export {
142
+ ScanGuide,
143
+ ScanOverlay,
144
+ ScannerStatus,
145
+ objectFitRect
146
+ };
147
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/object-fit.ts","../src/scan-overlay.tsx","../src/scan-guide.tsx","../src/scanner-status.tsx"],"sourcesContent":["export function objectFitRect(\n containerWidth: number,\n containerHeight: number,\n sourceWidth: number,\n sourceHeight: number,\n fit: \"contain\" | \"cover\" = \"contain\",\n) {\n if (\n containerWidth <= 0 ||\n containerHeight <= 0 ||\n sourceWidth <= 0 ||\n sourceHeight <= 0\n ) {\n return null;\n }\n\n const scale =\n fit === \"cover\"\n ? Math.max(containerWidth / sourceWidth, containerHeight / sourceHeight)\n : Math.min(containerWidth / sourceWidth, containerHeight / sourceHeight);\n\n const width = sourceWidth * scale;\n const height = sourceHeight * scale;\n\n return {\n left: (containerWidth - width) / 2,\n top: (containerHeight - height) / 2,\n width,\n height,\n };\n}\n","\"use client\";\n\nimport type { BarcodeResult } from \"@barcodesdk/core\";\nimport { objectFitRect } from \"./object-fit.js\";\n\nexport interface ScanOverlayProps {\n results: BarcodeResult[];\n sourceWidth: number;\n sourceHeight: number;\n containerWidth: number;\n containerHeight: number;\n objectFit?: \"contain\" | \"cover\";\n fill?: string;\n stroke?: string;\n className?: string;\n}\n\n/** SVG polygons mapped from barcode corner points into the fitted media box. */\nexport function ScanOverlay({\n results,\n sourceWidth,\n sourceHeight,\n containerWidth,\n containerHeight,\n objectFit = \"contain\",\n fill = \"rgba(34,197,94,0.18)\",\n stroke = \"#22c55e\",\n className,\n}: ScanOverlayProps) {\n const fitted = objectFitRect(\n containerWidth,\n containerHeight,\n sourceWidth,\n sourceHeight,\n objectFit,\n );\n\n if (!fitted || results.length === 0) {\n return null;\n }\n\n const strokeWidth = Math.max(1.5, Math.min(sourceWidth, sourceHeight) / 240);\n\n return (\n <svg\n className={className}\n style={{\n pointerEvents: \"none\",\n position: \"absolute\",\n left: fitted.left,\n top: fitted.top,\n width: fitted.width,\n height: fitted.height,\n }}\n viewBox={`0 0 ${sourceWidth} ${sourceHeight}`}\n preserveAspectRatio=\"none\"\n >\n {results.map((result) => {\n const points = result.cornerPoints\n .map((point) => `${point.x},${point.y}`)\n .join(\" \");\n return (\n <polygon\n key={result.id}\n points={points}\n fill={fill}\n stroke={stroke}\n strokeWidth={strokeWidth}\n strokeLinejoin=\"round\"\n />\n );\n })}\n </svg>\n );\n}\n","\"use client\";\n\nexport interface ScanGuideProps {\n className?: string;\n label?: string;\n}\n\n/** Simple center viewfinder frame for camera guidance. */\nexport function ScanGuide({\n className,\n label = \"Align barcode inside the frame\",\n}: ScanGuideProps) {\n return (\n <div\n className={className}\n role=\"img\"\n aria-label={label}\n style={{\n pointerEvents: \"none\",\n position: \"absolute\",\n inset: \"18%\",\n border: \"2px solid rgba(255,255,255,0.85)\",\n borderRadius: 12,\n boxShadow: \"0 0 0 9999px rgba(0,0,0,0.28)\",\n display: \"flex\",\n alignItems: \"flex-end\",\n justifyContent: \"center\",\n paddingBottom: 12,\n }}\n >\n <span\n aria-hidden=\"true\"\n style={{\n color: \"#fff\",\n fontSize: 12,\n fontWeight: 500,\n textShadow: \"0 1px 2px rgba(0,0,0,0.6)\",\n }}\n >\n {label}\n </span>\n </div>\n );\n}\n","\"use client\";\n\nimport type { ScannerState } from \"@barcodesdk/core\";\n\nexport interface ScannerStatusProps {\n state: ScannerState;\n className?: string;\n /** Override visible label; screen readers always get a full phrase. */\n label?: string;\n}\n\nconst LABELS: Record<ScannerState, string> = {\n idle: \"Idle\",\n initializing: \"Initializing\",\n ready: \"Ready to scan\",\n scanning: \"Scanning\",\n paused: \"Paused\",\n error: \"Scanner error\",\n destroyed: \"Scanner closed\",\n};\n\nconst LIVE_LABELS: Record<ScannerState, string> = {\n idle: \"Scanner idle\",\n initializing: \"Scanner initializing\",\n ready: \"Scanner ready\",\n scanning: \"Scanner actively scanning\",\n paused: \"Scanner paused\",\n error: \"Scanner encountered an error\",\n destroyed: \"Scanner closed\",\n};\n\nexport function ScannerStatus({ state, className, label }: ScannerStatusProps) {\n const visible = label ?? LABELS[state];\n return (\n <span\n className={className}\n data-state={state}\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n aria-label={LIVE_LABELS[state]}\n >\n {visible}\n </span>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export declare function objectFitRect(containerWidth: number, containerHeight: number, sourceWidth: number, sourceHeight: number, fit?: "contain" | "cover"): {
2
+ left: number;
3
+ top: number;
4
+ width: number;
5
+ height: number;
6
+ } | null;
7
+ //# sourceMappingURL=object-fit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-fit.d.ts","sourceRoot":"","sources":["../src/object-fit.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,CAC3B,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,GAAG,GAAE,SAAS,GAAG,OAAmB;;;;;SAyBrC"}
@@ -0,0 +1,7 @@
1
+ export interface ScanGuideProps {
2
+ className?: string;
3
+ label?: string;
4
+ }
5
+ /** Simple center viewfinder frame for camera guidance. */
6
+ export declare function ScanGuide({ className, label, }: ScanGuideProps): import("react").JSX.Element;
7
+ //# sourceMappingURL=scan-guide.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-guide.d.ts","sourceRoot":"","sources":["../src/scan-guide.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,0DAA0D;AAC1D,wBAAgB,SAAS,CAAC,EACxB,SAAS,EACT,KAAwC,GACzC,EAAE,cAAc,+BAgChB"}
@@ -0,0 +1,15 @@
1
+ import type { BarcodeResult } from "@barcodesdk/core";
2
+ export interface ScanOverlayProps {
3
+ results: BarcodeResult[];
4
+ sourceWidth: number;
5
+ sourceHeight: number;
6
+ containerWidth: number;
7
+ containerHeight: number;
8
+ objectFit?: "contain" | "cover";
9
+ fill?: string;
10
+ stroke?: string;
11
+ className?: string;
12
+ }
13
+ /** SVG polygons mapped from barcode corner points into the fitted media box. */
14
+ export declare function ScanOverlay({ results, sourceWidth, sourceHeight, containerWidth, containerHeight, objectFit, fill, stroke, className, }: ScanOverlayProps): import("react").JSX.Element | null;
15
+ //# sourceMappingURL=scan-overlay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-overlay.d.ts","sourceRoot":"","sources":["../src/scan-overlay.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGtD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,gFAAgF;AAChF,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,WAAW,EACX,YAAY,EACZ,cAAc,EACd,eAAe,EACf,SAAqB,EACrB,IAA6B,EAC7B,MAAkB,EAClB,SAAS,GACV,EAAE,gBAAgB,sCA8ClB"}
@@ -0,0 +1,9 @@
1
+ import type { ScannerState } from "@barcodesdk/core";
2
+ export interface ScannerStatusProps {
3
+ state: ScannerState;
4
+ className?: string;
5
+ /** Override visible label; screen readers always get a full phrase. */
6
+ label?: string;
7
+ }
8
+ export declare function ScannerStatus({ state, className, label }: ScannerStatusProps): import("react").JSX.Element;
9
+ //# sourceMappingURL=scanner-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner-status.d.ts","sourceRoot":"","sources":["../src/scanner-status.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,YAAY,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAsBD,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,kBAAkB,+BAc5E"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@barcodesdk/ui",
3
+ "version": "0.1.0",
4
+ "description": "Optional overlay primitives for Barcode WebSDK",
5
+ "license": "SEE LICENSE IN LICENSE",
6
+ "author": "Vijay Reddy",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/vjiayreddy/barcodesdk.git",
10
+ "directory": "packages/ui"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public",
14
+ "tag": "beta"
15
+ },
16
+ "type": "module",
17
+ "sideEffects": false,
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "main": "./dist/index.js",
22
+ "module": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/index.js"
28
+ }
29
+ },
30
+ "peerDependencies": {
31
+ "react": "^18.0.0 || ^19.0.0",
32
+ "react-dom": "^18.0.0 || ^19.0.0"
33
+ },
34
+ "dependencies": {
35
+ "@barcodesdk/core": "0.1.0"
36
+ },
37
+ "devDependencies": {
38
+ "@types/react": "^19",
39
+ "@types/react-dom": "^19",
40
+ "react": "19.2.8",
41
+ "react-dom": "19.2.8",
42
+ "typescript": "^5",
43
+ "vite": "^6",
44
+ "vitest": "^3"
45
+ },
46
+ "scripts": {
47
+ "build": "vite build && tsc --emitDeclarationOnly -p tsconfig.build.json",
48
+ "typecheck": "tsc --noEmit",
49
+ "test": "vitest run"
50
+ }
51
+ }