@nuvui/barcodes 0.0.0-stage → 0.1.1
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 +21 -0
- package/README.md +60 -2
- package/dist/components/barcode/barcode.cjs +94 -0
- package/dist/components/barcode/barcode.d.cts +70 -0
- package/dist/components/barcode/barcode.d.ts +70 -0
- package/dist/components/barcode/barcode.js +94 -0
- package/dist/components/barcode/encode.cjs +86 -0
- package/dist/components/barcode/encode.d.cts +52 -0
- package/dist/components/barcode/encode.d.ts +52 -0
- package/dist/components/barcode/encode.js +85 -0
- package/dist/components/barcode/index.cjs +6 -0
- package/dist/components/barcode/index.d.cts +3 -0
- package/dist/components/barcode/index.d.ts +3 -0
- package/dist/components/barcode/index.js +3 -0
- package/dist/components/qrcode/encode.cjs +28 -0
- package/dist/components/qrcode/encode.d.cts +29 -0
- package/dist/components/qrcode/encode.d.ts +29 -0
- package/dist/components/qrcode/encode.js +28 -0
- package/dist/components/qrcode/index.cjs +6 -0
- package/dist/components/qrcode/index.d.cts +3 -0
- package/dist/components/qrcode/index.d.ts +3 -0
- package/dist/components/qrcode/index.js +3 -0
- package/dist/components/qrcode/qrcode.cjs +121 -0
- package/dist/components/qrcode/qrcode.d.cts +59 -0
- package/dist/components/qrcode/qrcode.d.ts +59 -0
- package/dist/components/qrcode/qrcode.js +120 -0
- package/dist/css/barcode.css +26 -0
- package/dist/css/qrcode.css +28 -0
- package/dist/index.cjs +15 -0
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +6 -0
- package/dist/save.cjs +120 -0
- package/dist/save.d.cts +38 -0
- package/dist/save.d.ts +38 -0
- package/dist/save.js +117 -0
- package/dist/styles.css +53 -0
- package/dist/utils/cx.cjs +6 -0
- package/dist/utils/cx.js +6 -0
- package/package.json +118 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nuvraxis
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,61 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @nuvui/barcodes
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Barcodes and QR codes for [`@nuvui/react`](https://www.npmjs.com/package/@nuvui/react), drawn as SVG.
|
|
4
|
+
|
|
5
|
+
This is early: the package is on npm as a 0.x version, and before 1.0 a minor version may rename or remove things. The [changelog](https://nuvui.nuvraxis.com/docs/changelog) says where one does, and the [repository README](https://github.com/Nuvraxis/nuvui#readme) says where things stand.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add @nuvui/barcodes @nuvui/react
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import "@nuvui/react/styles.css";
|
|
13
|
+
import "@nuvui/barcodes/styles.css";
|
|
14
|
+
import { Barcode, QRCode } from "@nuvui/barcodes";
|
|
15
|
+
|
|
16
|
+
export function Label() {
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<Barcode value="590123412345" type="ean13" aria-label="Item 5901234" />
|
|
20
|
+
<QRCode
|
|
21
|
+
value="https://example.com/orders/1042"
|
|
22
|
+
aria-label="Open order 1042"
|
|
23
|
+
/>
|
|
24
|
+
</>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## What's in it
|
|
30
|
+
|
|
31
|
+
- `Barcode`: bars for a scanner, in seventeen kinds. Code 128, GS1-128, Code 39 with and without its check character, EAN-13, EAN-8, UPC-A, UPC-E, ITF, ITF-14, MSI with and without its check digits, Codabar and Pharmacode. The value is written under the bars.
|
|
32
|
+
- `QRCode`: a QR code at one of four levels of error correction, with room in the middle for a logo.
|
|
33
|
+
- `QRCodeSwissCross`: the cross in the middle of a Swiss QR-bill's code.
|
|
34
|
+
- `encodeBarcode` and `encodeQRCode`: the bars and the squares as data, or `null` for a value that can't be encoded. They need no browser, so they check a value in a form or on a server.
|
|
35
|
+
- `saveCode`, `codeToBlob` and `codeToSvg`: a code on the page as an SVG or a PNG file.
|
|
36
|
+
|
|
37
|
+
Each component has an entry of its own, `@nuvui/barcodes/barcode` and `@nuvui/barcodes/qrcode`, so a page with only QR codes doesn't carry every barcode's tables.
|
|
38
|
+
|
|
39
|
+
## How it behaves
|
|
40
|
+
|
|
41
|
+
Both components are SVG with no state and no `"use client"`. A server component renders them as markup, and no script is sent for them.
|
|
42
|
+
|
|
43
|
+
A code is black on white in every theme, and where the browser forces colors. A scanner reads the difference between the two, and many don't read a light code on a dark ground. Two CSS variables change the colors if you have to.
|
|
44
|
+
|
|
45
|
+
A value that can't be a code of its kind, such as a letter in an EAN-13, is drawn as an empty box with a line through it, or as your `fallback`. It's never drawn as a code that reads as something else.
|
|
46
|
+
|
|
47
|
+
A code is a picture with a name. Give it `aria-label` for what it's for. Don't make a code the only way to get what it holds: put the text or a link next to it.
|
|
48
|
+
|
|
49
|
+
## Dependencies
|
|
50
|
+
|
|
51
|
+
`@nuvui/react` is a peer dependency: the codes take their colors and their font from its tokens.
|
|
52
|
+
|
|
53
|
+
The bars are worked out by [JsBarcode](https://github.com/lindell/JsBarcode) and the squares by [uqr](https://github.com/unjs/uqr). Both are MIT, both are ordinary dependencies, and neither has dependencies of its own. Only their encoders are used. The drawing is done here.
|
|
54
|
+
|
|
55
|
+
It's a package of its own so that an app which only wants buttons and dialogs doesn't have to install either.
|
|
56
|
+
|
|
57
|
+
The docs are at https://nuvui.nuvraxis.com/docs/barcodes.
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
MIT
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
const require_cx = require("../../utils/cx.cjs");
|
|
2
|
+
const require_encode = require("./encode.cjs");
|
|
3
|
+
let react = require("react");
|
|
4
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
5
|
+
//#region src/components/barcode/barcode.tsx
|
|
6
|
+
const gap = 2;
|
|
7
|
+
const emptyModules = 96;
|
|
8
|
+
/**
|
|
9
|
+
* A barcode, drawn as an SVG. It's a picture with a name: say what the
|
|
10
|
+
* code is for with `aria-label`, and without one the name is the value.
|
|
11
|
+
*
|
|
12
|
+
* It has no state and needs no browser, so it can be a server component.
|
|
13
|
+
*/
|
|
14
|
+
const Barcode = (0, react.forwardRef)(function Barcode({ value, type = "code128", barWidth = 2, height = 64, quietZone = 10, showText = true, text, textSize = 14, flat = false, fallback, className, "aria-label": label, "aria-labelledby": labelledBy, ...props }, ref) {
|
|
15
|
+
const encoding = require_encode.encodeBarcode(value, type, {
|
|
16
|
+
text,
|
|
17
|
+
flat
|
|
18
|
+
});
|
|
19
|
+
if (!encoding && fallback !== void 0) return fallback;
|
|
20
|
+
const edge = quietZone * barWidth;
|
|
21
|
+
const width = ((encoding?.modules ?? emptyModules) + quietZone * 2) * barWidth;
|
|
22
|
+
const total = showText ? height + gap + textSize * 1.25 : height;
|
|
23
|
+
const guardHeight = showText ? height + gap + textSize / 2 : height;
|
|
24
|
+
const name = labelledBy ? void 0 : label ?? encoding?.text ?? value;
|
|
25
|
+
const root = {
|
|
26
|
+
ref,
|
|
27
|
+
width,
|
|
28
|
+
height: total,
|
|
29
|
+
viewBox: `0 0 ${width} ${total}`,
|
|
30
|
+
...props
|
|
31
|
+
};
|
|
32
|
+
if (!encoding) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
33
|
+
...root,
|
|
34
|
+
role: "img",
|
|
35
|
+
"aria-label": name,
|
|
36
|
+
"aria-labelledby": labelledBy,
|
|
37
|
+
className: require_cx.cx("nuv-barcode", className),
|
|
38
|
+
"data-invalid": "",
|
|
39
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
40
|
+
className: "nuv-barcode__ground",
|
|
41
|
+
width: "100%",
|
|
42
|
+
height: "100%"
|
|
43
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
44
|
+
className: "nuv-barcode__mark",
|
|
45
|
+
d: `M0.5 0.5H${width - .5}V${total - .5}H0.5ZM0.5 ${total - .5}L${width - .5} 0.5`
|
|
46
|
+
})]
|
|
47
|
+
});
|
|
48
|
+
let bars = "";
|
|
49
|
+
const texts = [];
|
|
50
|
+
let start = edge;
|
|
51
|
+
for (const [index, part] of encoding.parts.entries()) {
|
|
52
|
+
const tall = part.guard ? guardHeight : height;
|
|
53
|
+
for (const run of part.bars.matchAll(/1+/g)) {
|
|
54
|
+
const x = start + run.index * barWidth;
|
|
55
|
+
const across = run[0].length * barWidth;
|
|
56
|
+
bars += `M${x} 0h${across}v${tall}h${-across}z`;
|
|
57
|
+
}
|
|
58
|
+
const end = start + part.bars.length * barWidth;
|
|
59
|
+
if (showText && part.text) {
|
|
60
|
+
const [x, anchor] = part.align === "start" ? [start, "start"] : part.align === "end" ? [end, "end"] : [(start + end) / 2, "middle"];
|
|
61
|
+
texts.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("text", {
|
|
62
|
+
className: "nuv-barcode__text",
|
|
63
|
+
x,
|
|
64
|
+
y: height + gap + textSize,
|
|
65
|
+
textAnchor: anchor,
|
|
66
|
+
fontSize: textSize,
|
|
67
|
+
children: part.text
|
|
68
|
+
}, index));
|
|
69
|
+
}
|
|
70
|
+
start = end;
|
|
71
|
+
}
|
|
72
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
73
|
+
...root,
|
|
74
|
+
role: "img",
|
|
75
|
+
"aria-label": name,
|
|
76
|
+
"aria-labelledby": labelledBy,
|
|
77
|
+
className: require_cx.cx("nuv-barcode", className),
|
|
78
|
+
"data-type": encoding.type,
|
|
79
|
+
children: [
|
|
80
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
81
|
+
className: "nuv-barcode__ground",
|
|
82
|
+
width: "100%",
|
|
83
|
+
height: "100%"
|
|
84
|
+
}),
|
|
85
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
86
|
+
className: "nuv-barcode__bars",
|
|
87
|
+
d: bars
|
|
88
|
+
}),
|
|
89
|
+
texts
|
|
90
|
+
]
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
//#endregion
|
|
94
|
+
exports.Barcode = Barcode;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { BarcodeType } from "./encode.cjs";
|
|
2
|
+
import { ReactNode, SVGAttributes } from "react";
|
|
3
|
+
//#region src/components/barcode/barcode.d.ts
|
|
4
|
+
export interface BarcodeOwnProps {
|
|
5
|
+
/** What the barcode holds. */
|
|
6
|
+
value: string;
|
|
7
|
+
/**
|
|
8
|
+
* The kind of barcode. Each takes its own values: an EAN-13 is twelve
|
|
9
|
+
* or thirteen digits, a Code 39 is capitals, digits and a few signs,
|
|
10
|
+
* and a Code 128 is any ASCII text.
|
|
11
|
+
* @default "code128"
|
|
12
|
+
*/
|
|
13
|
+
type?: BarcodeType;
|
|
14
|
+
/**
|
|
15
|
+
* The width of the narrowest bar, in pixels. A scanner needs it to be
|
|
16
|
+
* a whole number of the screen's or the printer's dots.
|
|
17
|
+
* @default 2
|
|
18
|
+
*/
|
|
19
|
+
barWidth?: number;
|
|
20
|
+
/**
|
|
21
|
+
* The height of the bars, in pixels. The text is under that.
|
|
22
|
+
* @default 64
|
|
23
|
+
*/
|
|
24
|
+
height?: number;
|
|
25
|
+
/**
|
|
26
|
+
* The empty space at each end, counted in narrowest bars. A scanner
|
|
27
|
+
* finds the barcode by it, and most kinds ask for ten.
|
|
28
|
+
* @default 10
|
|
29
|
+
*/
|
|
30
|
+
quietZone?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Writes the value under the bars, for someone to type when it won't
|
|
33
|
+
* scan.
|
|
34
|
+
* @default true
|
|
35
|
+
*/
|
|
36
|
+
showText?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Written under the bars in place of the value: the same number with
|
|
39
|
+
* spaces in it, say. An EAN or a UPC writes its own digits whatever
|
|
40
|
+
* this is.
|
|
41
|
+
*/
|
|
42
|
+
text?: string;
|
|
43
|
+
/**
|
|
44
|
+
* The size of the text, in pixels.
|
|
45
|
+
* @default 14
|
|
46
|
+
*/
|
|
47
|
+
textSize?: number;
|
|
48
|
+
/**
|
|
49
|
+
* An EAN or a UPC with every bar the same height and its digits in one
|
|
50
|
+
* line, in place of the usual look with taller bars at the ends and in
|
|
51
|
+
* the middle.
|
|
52
|
+
* @default false
|
|
53
|
+
*/
|
|
54
|
+
flat?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* What's drawn when the value can't be a barcode of this kind. Without
|
|
57
|
+
* it there's an empty box with a line through it, which no scanner
|
|
58
|
+
* reads as anything.
|
|
59
|
+
*/
|
|
60
|
+
fallback?: ReactNode;
|
|
61
|
+
}
|
|
62
|
+
export interface BarcodeProps extends BarcodeOwnProps, Omit<SVGAttributes<SVGSVGElement>, keyof BarcodeOwnProps> {}
|
|
63
|
+
/**
|
|
64
|
+
* A barcode, drawn as an SVG. It's a picture with a name: say what the
|
|
65
|
+
* code is for with `aria-label`, and without one the name is the value.
|
|
66
|
+
*
|
|
67
|
+
* It has no state and needs no browser, so it can be a server component.
|
|
68
|
+
*/
|
|
69
|
+
export declare const Barcode: import("react").ForwardRefExoticComponent<BarcodeProps & import("react").RefAttributes<SVGSVGElement>>;
|
|
70
|
+
//#endregion
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { BarcodeType } from "./encode.js";
|
|
2
|
+
import { ReactNode, SVGAttributes } from "react";
|
|
3
|
+
//#region src/components/barcode/barcode.d.ts
|
|
4
|
+
export interface BarcodeOwnProps {
|
|
5
|
+
/** What the barcode holds. */
|
|
6
|
+
value: string;
|
|
7
|
+
/**
|
|
8
|
+
* The kind of barcode. Each takes its own values: an EAN-13 is twelve
|
|
9
|
+
* or thirteen digits, a Code 39 is capitals, digits and a few signs,
|
|
10
|
+
* and a Code 128 is any ASCII text.
|
|
11
|
+
* @default "code128"
|
|
12
|
+
*/
|
|
13
|
+
type?: BarcodeType;
|
|
14
|
+
/**
|
|
15
|
+
* The width of the narrowest bar, in pixels. A scanner needs it to be
|
|
16
|
+
* a whole number of the screen's or the printer's dots.
|
|
17
|
+
* @default 2
|
|
18
|
+
*/
|
|
19
|
+
barWidth?: number;
|
|
20
|
+
/**
|
|
21
|
+
* The height of the bars, in pixels. The text is under that.
|
|
22
|
+
* @default 64
|
|
23
|
+
*/
|
|
24
|
+
height?: number;
|
|
25
|
+
/**
|
|
26
|
+
* The empty space at each end, counted in narrowest bars. A scanner
|
|
27
|
+
* finds the barcode by it, and most kinds ask for ten.
|
|
28
|
+
* @default 10
|
|
29
|
+
*/
|
|
30
|
+
quietZone?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Writes the value under the bars, for someone to type when it won't
|
|
33
|
+
* scan.
|
|
34
|
+
* @default true
|
|
35
|
+
*/
|
|
36
|
+
showText?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Written under the bars in place of the value: the same number with
|
|
39
|
+
* spaces in it, say. An EAN or a UPC writes its own digits whatever
|
|
40
|
+
* this is.
|
|
41
|
+
*/
|
|
42
|
+
text?: string;
|
|
43
|
+
/**
|
|
44
|
+
* The size of the text, in pixels.
|
|
45
|
+
* @default 14
|
|
46
|
+
*/
|
|
47
|
+
textSize?: number;
|
|
48
|
+
/**
|
|
49
|
+
* An EAN or a UPC with every bar the same height and its digits in one
|
|
50
|
+
* line, in place of the usual look with taller bars at the ends and in
|
|
51
|
+
* the middle.
|
|
52
|
+
* @default false
|
|
53
|
+
*/
|
|
54
|
+
flat?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* What's drawn when the value can't be a barcode of this kind. Without
|
|
57
|
+
* it there's an empty box with a line through it, which no scanner
|
|
58
|
+
* reads as anything.
|
|
59
|
+
*/
|
|
60
|
+
fallback?: ReactNode;
|
|
61
|
+
}
|
|
62
|
+
export interface BarcodeProps extends BarcodeOwnProps, Omit<SVGAttributes<SVGSVGElement>, keyof BarcodeOwnProps> {}
|
|
63
|
+
/**
|
|
64
|
+
* A barcode, drawn as an SVG. It's a picture with a name: say what the
|
|
65
|
+
* code is for with `aria-label`, and without one the name is the value.
|
|
66
|
+
*
|
|
67
|
+
* It has no state and needs no browser, so it can be a server component.
|
|
68
|
+
*/
|
|
69
|
+
export declare const Barcode: import("react").ForwardRefExoticComponent<BarcodeProps & import("react").RefAttributes<SVGSVGElement>>;
|
|
70
|
+
//#endregion
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { cx } from "../../utils/cx.js";
|
|
2
|
+
import { encodeBarcode } from "./encode.js";
|
|
3
|
+
import { forwardRef } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/barcode/barcode.tsx
|
|
6
|
+
const gap = 2;
|
|
7
|
+
const emptyModules = 96;
|
|
8
|
+
/**
|
|
9
|
+
* A barcode, drawn as an SVG. It's a picture with a name: say what the
|
|
10
|
+
* code is for with `aria-label`, and without one the name is the value.
|
|
11
|
+
*
|
|
12
|
+
* It has no state and needs no browser, so it can be a server component.
|
|
13
|
+
*/
|
|
14
|
+
const Barcode = forwardRef(function Barcode({ value, type = "code128", barWidth = 2, height = 64, quietZone = 10, showText = true, text, textSize = 14, flat = false, fallback, className, "aria-label": label, "aria-labelledby": labelledBy, ...props }, ref) {
|
|
15
|
+
const encoding = encodeBarcode(value, type, {
|
|
16
|
+
text,
|
|
17
|
+
flat
|
|
18
|
+
});
|
|
19
|
+
if (!encoding && fallback !== void 0) return fallback;
|
|
20
|
+
const edge = quietZone * barWidth;
|
|
21
|
+
const width = ((encoding?.modules ?? emptyModules) + quietZone * 2) * barWidth;
|
|
22
|
+
const total = showText ? height + gap + textSize * 1.25 : height;
|
|
23
|
+
const guardHeight = showText ? height + gap + textSize / 2 : height;
|
|
24
|
+
const name = labelledBy ? void 0 : label ?? encoding?.text ?? value;
|
|
25
|
+
const root = {
|
|
26
|
+
ref,
|
|
27
|
+
width,
|
|
28
|
+
height: total,
|
|
29
|
+
viewBox: `0 0 ${width} ${total}`,
|
|
30
|
+
...props
|
|
31
|
+
};
|
|
32
|
+
if (!encoding) return /* @__PURE__ */ jsxs("svg", {
|
|
33
|
+
...root,
|
|
34
|
+
role: "img",
|
|
35
|
+
"aria-label": name,
|
|
36
|
+
"aria-labelledby": labelledBy,
|
|
37
|
+
className: cx("nuv-barcode", className),
|
|
38
|
+
"data-invalid": "",
|
|
39
|
+
children: [/* @__PURE__ */ jsx("rect", {
|
|
40
|
+
className: "nuv-barcode__ground",
|
|
41
|
+
width: "100%",
|
|
42
|
+
height: "100%"
|
|
43
|
+
}), /* @__PURE__ */ jsx("path", {
|
|
44
|
+
className: "nuv-barcode__mark",
|
|
45
|
+
d: `M0.5 0.5H${width - .5}V${total - .5}H0.5ZM0.5 ${total - .5}L${width - .5} 0.5`
|
|
46
|
+
})]
|
|
47
|
+
});
|
|
48
|
+
let bars = "";
|
|
49
|
+
const texts = [];
|
|
50
|
+
let start = edge;
|
|
51
|
+
for (const [index, part] of encoding.parts.entries()) {
|
|
52
|
+
const tall = part.guard ? guardHeight : height;
|
|
53
|
+
for (const run of part.bars.matchAll(/1+/g)) {
|
|
54
|
+
const x = start + run.index * barWidth;
|
|
55
|
+
const across = run[0].length * barWidth;
|
|
56
|
+
bars += `M${x} 0h${across}v${tall}h${-across}z`;
|
|
57
|
+
}
|
|
58
|
+
const end = start + part.bars.length * barWidth;
|
|
59
|
+
if (showText && part.text) {
|
|
60
|
+
const [x, anchor] = part.align === "start" ? [start, "start"] : part.align === "end" ? [end, "end"] : [(start + end) / 2, "middle"];
|
|
61
|
+
texts.push(/* @__PURE__ */ jsx("text", {
|
|
62
|
+
className: "nuv-barcode__text",
|
|
63
|
+
x,
|
|
64
|
+
y: height + gap + textSize,
|
|
65
|
+
textAnchor: anchor,
|
|
66
|
+
fontSize: textSize,
|
|
67
|
+
children: part.text
|
|
68
|
+
}, index));
|
|
69
|
+
}
|
|
70
|
+
start = end;
|
|
71
|
+
}
|
|
72
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
73
|
+
...root,
|
|
74
|
+
role: "img",
|
|
75
|
+
"aria-label": name,
|
|
76
|
+
"aria-labelledby": labelledBy,
|
|
77
|
+
className: cx("nuv-barcode", className),
|
|
78
|
+
"data-type": encoding.type,
|
|
79
|
+
children: [
|
|
80
|
+
/* @__PURE__ */ jsx("rect", {
|
|
81
|
+
className: "nuv-barcode__ground",
|
|
82
|
+
width: "100%",
|
|
83
|
+
height: "100%"
|
|
84
|
+
}),
|
|
85
|
+
/* @__PURE__ */ jsx("path", {
|
|
86
|
+
className: "nuv-barcode__bars",
|
|
87
|
+
d: bars
|
|
88
|
+
}),
|
|
89
|
+
texts
|
|
90
|
+
]
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
//#endregion
|
|
94
|
+
export { Barcode };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
let jsbarcode_bin_barcodes_CODE39_index_js = require("jsbarcode/bin/barcodes/CODE39/index.js");
|
|
2
|
+
let jsbarcode_bin_barcodes_CODE128_index_js = require("jsbarcode/bin/barcodes/CODE128/index.js");
|
|
3
|
+
let jsbarcode_bin_barcodes_codabar_index_js = require("jsbarcode/bin/barcodes/codabar/index.js");
|
|
4
|
+
let jsbarcode_bin_barcodes_EAN_UPC_index_js = require("jsbarcode/bin/barcodes/EAN_UPC/index.js");
|
|
5
|
+
let jsbarcode_bin_barcodes_ITF_index_js = require("jsbarcode/bin/barcodes/ITF/index.js");
|
|
6
|
+
let jsbarcode_bin_barcodes_MSI_index_js = require("jsbarcode/bin/barcodes/MSI/index.js");
|
|
7
|
+
let jsbarcode_bin_barcodes_pharmacode_index_js = require("jsbarcode/bin/barcodes/pharmacode/index.js");
|
|
8
|
+
//#region src/components/barcode/encode.ts
|
|
9
|
+
const encoders = {
|
|
10
|
+
code128: [jsbarcode_bin_barcodes_CODE128_index_js.CODE128, {}],
|
|
11
|
+
"gs1-128": [jsbarcode_bin_barcodes_CODE128_index_js.CODE128, { ean128: true }],
|
|
12
|
+
code39: [jsbarcode_bin_barcodes_CODE39_index_js.CODE39, {}],
|
|
13
|
+
"code39-mod43": [jsbarcode_bin_barcodes_CODE39_index_js.CODE39, { mod43: true }],
|
|
14
|
+
ean13: [jsbarcode_bin_barcodes_EAN_UPC_index_js.EAN13, {}],
|
|
15
|
+
ean8: [jsbarcode_bin_barcodes_EAN_UPC_index_js.EAN8, {}],
|
|
16
|
+
upca: [jsbarcode_bin_barcodes_EAN_UPC_index_js.UPC, {}],
|
|
17
|
+
upce: [jsbarcode_bin_barcodes_EAN_UPC_index_js.UPCE, {}],
|
|
18
|
+
itf: [jsbarcode_bin_barcodes_ITF_index_js.ITF, {}],
|
|
19
|
+
itf14: [jsbarcode_bin_barcodes_ITF_index_js.ITF14, {}],
|
|
20
|
+
msi: [jsbarcode_bin_barcodes_MSI_index_js.MSI, {}],
|
|
21
|
+
"msi-mod10": [jsbarcode_bin_barcodes_MSI_index_js.MSI10, {}],
|
|
22
|
+
"msi-mod11": [jsbarcode_bin_barcodes_MSI_index_js.MSI11, {}],
|
|
23
|
+
"msi-mod1010": [jsbarcode_bin_barcodes_MSI_index_js.MSI1010, {}],
|
|
24
|
+
"msi-mod1110": [jsbarcode_bin_barcodes_MSI_index_js.MSI1110, {}],
|
|
25
|
+
codabar: [jsbarcode_bin_barcodes_codabar_index_js.codabar, {}],
|
|
26
|
+
pharmacode: [jsbarcode_bin_barcodes_pharmacode_index_js.pharmacode, {}]
|
|
27
|
+
};
|
|
28
|
+
/** Every kind of barcode there is here. */
|
|
29
|
+
const barcodeTypes = Object.keys(encoders);
|
|
30
|
+
const grouped = /* @__PURE__ */ new Set([
|
|
31
|
+
"ean13",
|
|
32
|
+
"ean8",
|
|
33
|
+
"upca",
|
|
34
|
+
"upce"
|
|
35
|
+
]);
|
|
36
|
+
const aligned = {
|
|
37
|
+
left: "start",
|
|
38
|
+
center: "center",
|
|
39
|
+
right: "end"
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* The bars for a value, or `null` if this kind of barcode can't hold it:
|
|
43
|
+
* a letter in an EAN, a check digit that's wrong, an odd number of digits
|
|
44
|
+
* in an ITF, nothing at all.
|
|
45
|
+
*
|
|
46
|
+
* It needs no browser, so it runs wherever the value is checked.
|
|
47
|
+
*/
|
|
48
|
+
function encodeBarcode(value, type = "code128", { text, flat = false } = {}) {
|
|
49
|
+
const entry = encoders[type];
|
|
50
|
+
if (!entry || typeof value !== "string" || value === "") return null;
|
|
51
|
+
const [Encoder, extra] = entry;
|
|
52
|
+
let encoded;
|
|
53
|
+
try {
|
|
54
|
+
const encoder = new Encoder(value, {
|
|
55
|
+
...extra,
|
|
56
|
+
text: grouped.has(type) ? void 0 : text,
|
|
57
|
+
flat,
|
|
58
|
+
displayValue: true,
|
|
59
|
+
fontSize: 0,
|
|
60
|
+
height: 0,
|
|
61
|
+
textMargin: 0,
|
|
62
|
+
width: 1
|
|
63
|
+
});
|
|
64
|
+
if (!encoder.valid()) return null;
|
|
65
|
+
encoded = encoder.encode();
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const parts = (Array.isArray(encoded) ? encoded : [encoded]).filter((part) => part.data.length > 0).map((part) => ({
|
|
70
|
+
bars: part.data,
|
|
71
|
+
text: part.text === void 0 ? void 0 : String(part.text),
|
|
72
|
+
guard: part.options?.height !== void 0,
|
|
73
|
+
align: aligned[part.options?.textAlign ?? "center"]
|
|
74
|
+
}));
|
|
75
|
+
const modules = parts.reduce((sum, part) => sum + part.bars.length, 0);
|
|
76
|
+
if (modules === 0) return null;
|
|
77
|
+
return {
|
|
78
|
+
type,
|
|
79
|
+
parts,
|
|
80
|
+
modules,
|
|
81
|
+
text: parts.map((part) => part.text ?? "").join("")
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
exports.barcodeTypes = barcodeTypes;
|
|
86
|
+
exports.encodeBarcode = encodeBarcode;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
//#region src/components/barcode/encode.d.ts
|
|
2
|
+
/** The kinds of barcode there are here. */
|
|
3
|
+
export type BarcodeType = "code128" | "gs1-128" | "code39" | "code39-mod43" | "ean13" | "ean8" | "upca" | "upce" | "itf" | "itf14" | "msi" | "msi-mod10" | "msi-mod11" | "msi-mod1010" | "msi-mod1110" | "codabar" | "pharmacode";
|
|
4
|
+
/** Every kind of barcode there is here. */
|
|
5
|
+
export declare const barcodeTypes: BarcodeType[];
|
|
6
|
+
export interface BarcodePart {
|
|
7
|
+
/** "1" for a bar one module wide and "0" for a space as wide. */
|
|
8
|
+
bars: string;
|
|
9
|
+
/** What's written under this part, if anything. */
|
|
10
|
+
text?: string;
|
|
11
|
+
/**
|
|
12
|
+
* A guard: bars at the two ends and in the middle of an EAN or a UPC,
|
|
13
|
+
* drawn taller than the rest.
|
|
14
|
+
*/
|
|
15
|
+
guard: boolean;
|
|
16
|
+
/** Where the text sits under the part. */
|
|
17
|
+
align: "start" | "center" | "end";
|
|
18
|
+
}
|
|
19
|
+
export interface BarcodeEncoding {
|
|
20
|
+
type: BarcodeType;
|
|
21
|
+
/** The parts from left to right. Most kinds have one. */
|
|
22
|
+
parts: BarcodePart[];
|
|
23
|
+
/** How many modules wide the bars are, all parts together. */
|
|
24
|
+
modules: number;
|
|
25
|
+
/**
|
|
26
|
+
* The whole of what's written under the bars, with a check digit the
|
|
27
|
+
* encoder worked out.
|
|
28
|
+
*/
|
|
29
|
+
text: string;
|
|
30
|
+
}
|
|
31
|
+
export interface BarcodeEncodeOptions {
|
|
32
|
+
/**
|
|
33
|
+
* Written under the bars in place of the value. An EAN or a UPC writes
|
|
34
|
+
* its own digits whatever this is.
|
|
35
|
+
*/
|
|
36
|
+
text?: string;
|
|
37
|
+
/**
|
|
38
|
+
* An EAN or a UPC in one part, with every bar the same height and no
|
|
39
|
+
* digit outside the bars.
|
|
40
|
+
* @default false
|
|
41
|
+
*/
|
|
42
|
+
flat?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The bars for a value, or `null` if this kind of barcode can't hold it:
|
|
46
|
+
* a letter in an EAN, a check digit that's wrong, an odd number of digits
|
|
47
|
+
* in an ITF, nothing at all.
|
|
48
|
+
*
|
|
49
|
+
* It needs no browser, so it runs wherever the value is checked.
|
|
50
|
+
*/
|
|
51
|
+
export declare function encodeBarcode(value: string, type?: BarcodeType, { text, flat }?: BarcodeEncodeOptions): BarcodeEncoding | null;
|
|
52
|
+
//#endregion
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
//#region src/components/barcode/encode.d.ts
|
|
2
|
+
/** The kinds of barcode there are here. */
|
|
3
|
+
export type BarcodeType = "code128" | "gs1-128" | "code39" | "code39-mod43" | "ean13" | "ean8" | "upca" | "upce" | "itf" | "itf14" | "msi" | "msi-mod10" | "msi-mod11" | "msi-mod1010" | "msi-mod1110" | "codabar" | "pharmacode";
|
|
4
|
+
/** Every kind of barcode there is here. */
|
|
5
|
+
export declare const barcodeTypes: BarcodeType[];
|
|
6
|
+
export interface BarcodePart {
|
|
7
|
+
/** "1" for a bar one module wide and "0" for a space as wide. */
|
|
8
|
+
bars: string;
|
|
9
|
+
/** What's written under this part, if anything. */
|
|
10
|
+
text?: string;
|
|
11
|
+
/**
|
|
12
|
+
* A guard: bars at the two ends and in the middle of an EAN or a UPC,
|
|
13
|
+
* drawn taller than the rest.
|
|
14
|
+
*/
|
|
15
|
+
guard: boolean;
|
|
16
|
+
/** Where the text sits under the part. */
|
|
17
|
+
align: "start" | "center" | "end";
|
|
18
|
+
}
|
|
19
|
+
export interface BarcodeEncoding {
|
|
20
|
+
type: BarcodeType;
|
|
21
|
+
/** The parts from left to right. Most kinds have one. */
|
|
22
|
+
parts: BarcodePart[];
|
|
23
|
+
/** How many modules wide the bars are, all parts together. */
|
|
24
|
+
modules: number;
|
|
25
|
+
/**
|
|
26
|
+
* The whole of what's written under the bars, with a check digit the
|
|
27
|
+
* encoder worked out.
|
|
28
|
+
*/
|
|
29
|
+
text: string;
|
|
30
|
+
}
|
|
31
|
+
export interface BarcodeEncodeOptions {
|
|
32
|
+
/**
|
|
33
|
+
* Written under the bars in place of the value. An EAN or a UPC writes
|
|
34
|
+
* its own digits whatever this is.
|
|
35
|
+
*/
|
|
36
|
+
text?: string;
|
|
37
|
+
/**
|
|
38
|
+
* An EAN or a UPC in one part, with every bar the same height and no
|
|
39
|
+
* digit outside the bars.
|
|
40
|
+
* @default false
|
|
41
|
+
*/
|
|
42
|
+
flat?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The bars for a value, or `null` if this kind of barcode can't hold it:
|
|
46
|
+
* a letter in an EAN, a check digit that's wrong, an odd number of digits
|
|
47
|
+
* in an ITF, nothing at all.
|
|
48
|
+
*
|
|
49
|
+
* It needs no browser, so it runs wherever the value is checked.
|
|
50
|
+
*/
|
|
51
|
+
export declare function encodeBarcode(value: string, type?: BarcodeType, { text, flat }?: BarcodeEncodeOptions): BarcodeEncoding | null;
|
|
52
|
+
//#endregion
|