@particle-academy/react-fancy 6.7.1 → 6.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/CHANGELOG.md +19 -0
- package/dist/{chunk-AE53RKWR.cjs → chunk-CNWTYRZC.cjs} +407 -22
- package/dist/chunk-CNWTYRZC.cjs.map +1 -0
- package/dist/{chunk-B6WTCAGY.js → chunk-PDVU2PXR.js} +97 -53
- package/dist/chunk-PDVU2PXR.js.map +1 -0
- package/dist/{chunk-RLE5WYY6.js → chunk-PIG3UTHU.js} +406 -24
- package/dist/chunk-PIG3UTHU.js.map +1 -0
- package/dist/{chunk-OUNGGQIS.cjs → chunk-VZTXUYPM.cjs} +97 -53
- package/dist/chunk-VZTXUYPM.cjs.map +1 -0
- package/dist/index.cjs +23 -11
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/prompt-input.cjs +4 -4
- package/dist/prompt-input.js +1 -1
- package/dist/qr-code.cjs +19 -4
- package/dist/qr-code.d.cts +221 -2
- package/dist/qr-code.d.ts +221 -2
- package/dist/qr-code.js +4 -1
- package/docs/PromptInput.md +10 -2
- package/docs/QrCode.md +108 -0
- package/package.json +1 -1
- package/dist/chunk-AE53RKWR.cjs.map +0 -1
- package/dist/chunk-B6WTCAGY.js.map +0 -1
- package/dist/chunk-OUNGGQIS.cjs.map +0 -1
- package/dist/chunk-RLE5WYY6.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [6.8.0] - 2026-11-04
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`QrCodePanel`: download, share and print a QR code.** It composes `<QrCode>` with four actions, each a react-fancy `Button`: download as SVG, download as PNG, share (the Web Share sheet, or a copy of the value where there is none; the button then reads "Copy link" or "Copy"), and print (a frame holding only the code, plus the value when shown). `showValue` puts the value on screen as selectable text, off by default because a value can be a password. The code tile is black on white in dark mode too, because a QR code needs a light surround to scan; that is 21:1, past WCAG AAA. Buttons default to `lg`, 44px tall, WCAG 2.5.5's AAA target size. Every action reports through `onAction` as plain JSON (`{ action, outcome, via?, fileName?, error? }`, `outcome` one of `"done"`, `"cancelled"`, `"failed"`), what each did is announced in a polite `role="status"`, and every part has a `data-react-fancy-qr-code-*` handle. In the `./qr-code` subpath. **Consumer action:** none. `<QrCode>` is unchanged.
|
|
19
|
+
- **`qrCodeSvg(value, options)` and `qrCodePng(value, options)`**, the panel's file formats as plain functions. The SVG is a standalone document with a `<title>` and an accessible name. The PNG is drawn with `Path2D` from the same path data, at a whole number of pixels per module (at most 8192 pixels a side), rather than by loading the SVG into an image, which a CSP of `img-src 'self'` would block. Files are always opaque: `transparent` becomes white and `currentColor` black, since a file has no page behind it. The label is escaped, and characters XML cannot carry (C0 controls other than tab, newline and return; lone surrogates) are dropped or replaced, so a hostile label still gives a valid file. **Consumer action:** none.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **`<QrCode>` draws its path with the same builder as the file exports**, so a downloaded file carries exactly the modules on screen. The output is unchanged, byte for byte. **Consumer action:** none.
|
|
24
|
+
|
|
25
|
+
## [6.7.2] - 2026-11-04
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- **`PromptInput`'s `/` and `@` menu keeps the highlighted row in view.** ArrowDown and ArrowUp only moved the highlight, so with more commands than the menu's height shows, the highlight went off the bottom of the list and stayed hidden. The active row is now scrolled into view (`block: "nearest"`) after every arrow key and after typing filters the list. A row the pointer hovers is not scrolled, so the list does not creep under a resting mouse. Reported by the Genie agent. **Consumer action:** none.
|
|
30
|
+
- **A long command name, hint or mention no longer gives the `PromptInput` menu a horizontal scrollbar.** The text did not wrap, so an unbroken token such as `/claude-code-setup:claude-automation-recommender` or a long path widened the row past the `w-72` menu. Command names and hints now wrap anywhere they must. A mention's name and kind truncate with an ellipsis and carry the whole text in `title`; the text itself is unchanged, so a screen reader still reads it in full. **Consumer action:** none.
|
|
31
|
+
- **The `PromptInput` menu has stable handles.** The menu is `[data-react-fancy-prompt-input-menu="cmd" | "mention"]` with `role="listbox"`, and each row is a `role="option"` `[data-react-fancy-prompt-input-menu-item="<command name or mention id>"]` with `aria-selected`, so an agent or a test reads the menu without guessing at its markup. **Consumer action:** none.
|
|
32
|
+
|
|
14
33
|
## [6.7.1] - 2026-11-03
|
|
15
34
|
|
|
16
35
|
### Security
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var chunkNZHVDWOM_cjs = require('./chunk-NZHVDWOM.cjs');
|
|
3
4
|
var chunkZU5Q77OT_cjs = require('./chunk-ZU5Q77OT.cjs');
|
|
4
5
|
var react = require('react');
|
|
5
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
@@ -596,6 +597,29 @@ function encodeQr(value, options = {}) {
|
|
|
596
597
|
mask: best.mask
|
|
597
598
|
};
|
|
598
599
|
}
|
|
600
|
+
|
|
601
|
+
// src/components/QrCode/qr-path.ts
|
|
602
|
+
function modulePath(modules, quietZone) {
|
|
603
|
+
const size = modules.length;
|
|
604
|
+
const parts = [];
|
|
605
|
+
for (let row = 0; row < size; row += 1) {
|
|
606
|
+
const line = modules[row];
|
|
607
|
+
let col = 0;
|
|
608
|
+
while (col < size) {
|
|
609
|
+
if (!line[col]) {
|
|
610
|
+
col += 1;
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
let run = 1;
|
|
614
|
+
while (col + run < size && line[col + run]) {
|
|
615
|
+
run += 1;
|
|
616
|
+
}
|
|
617
|
+
parts.push(`M${col + quietZone} ${row + quietZone}h${run}v1h-${run}z`);
|
|
618
|
+
col += run;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return parts.join("");
|
|
622
|
+
}
|
|
599
623
|
var QrCode = react.forwardRef(
|
|
600
624
|
({
|
|
601
625
|
value,
|
|
@@ -615,26 +639,7 @@ var QrCode = react.forwardRef(
|
|
|
615
639
|
[value, errorCorrection, mode, version]
|
|
616
640
|
);
|
|
617
641
|
const extent = symbol.size + quietZone * 2;
|
|
618
|
-
const path = react.useMemo(() =>
|
|
619
|
-
const parts = [];
|
|
620
|
-
for (let row = 0; row < symbol.size; row += 1) {
|
|
621
|
-
const line = symbol.modules[row];
|
|
622
|
-
let col = 0;
|
|
623
|
-
while (col < symbol.size) {
|
|
624
|
-
if (!line[col]) {
|
|
625
|
-
col += 1;
|
|
626
|
-
continue;
|
|
627
|
-
}
|
|
628
|
-
let run = 1;
|
|
629
|
-
while (col + run < symbol.size && line[col + run]) {
|
|
630
|
-
run += 1;
|
|
631
|
-
}
|
|
632
|
-
parts.push(`M${col + quietZone} ${row + quietZone}h${run}v1h-${run}z`);
|
|
633
|
-
col += run;
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
return parts.join("");
|
|
637
|
-
}, [symbol, quietZone]);
|
|
642
|
+
const path = react.useMemo(() => modulePath(symbol.modules, quietZone), [symbol, quietZone]);
|
|
638
643
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
639
644
|
"svg",
|
|
640
645
|
{
|
|
@@ -657,8 +662,388 @@ var QrCode = react.forwardRef(
|
|
|
657
662
|
);
|
|
658
663
|
QrCode.displayName = "QrCode";
|
|
659
664
|
|
|
665
|
+
// src/components/QrCode/qr-export.ts
|
|
666
|
+
var DEFAULT_PIXELS_PER_MODULE = 10;
|
|
667
|
+
var MAX_PNG_SIDE = 8192;
|
|
668
|
+
var WHITE = "#ffffff";
|
|
669
|
+
var BLACK = "#000000";
|
|
670
|
+
function fileBackground(colour) {
|
|
671
|
+
return colour === void 0 || colour === "" || colour === "transparent" || colour === "none" ? WHITE : colour;
|
|
672
|
+
}
|
|
673
|
+
function fileForeground(colour) {
|
|
674
|
+
return colour === void 0 || colour === "" || colour === "currentColor" ? BLACK : colour;
|
|
675
|
+
}
|
|
676
|
+
var REPLACEMENT = String.fromCharCode(65533);
|
|
677
|
+
function xmlChars(text) {
|
|
678
|
+
let out = "";
|
|
679
|
+
for (const char of text) {
|
|
680
|
+
const code = char.charCodeAt(0);
|
|
681
|
+
if (code < 32 && code !== 9 && code !== 10 && code !== 13) {
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
out += char.length === 1 && code >= 55296 && code <= 57343 ? REPLACEMENT : char;
|
|
685
|
+
}
|
|
686
|
+
return out;
|
|
687
|
+
}
|
|
688
|
+
function xml(text) {
|
|
689
|
+
return xmlChars(text).replace(/[&<>"']/g, (char) => {
|
|
690
|
+
switch (char) {
|
|
691
|
+
case "&":
|
|
692
|
+
return "&";
|
|
693
|
+
case "<":
|
|
694
|
+
return "<";
|
|
695
|
+
case ">":
|
|
696
|
+
return ">";
|
|
697
|
+
case '"':
|
|
698
|
+
return """;
|
|
699
|
+
default:
|
|
700
|
+
return "'";
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
function symbolFor(value, options) {
|
|
705
|
+
const symbol = encodeQr(value, {
|
|
706
|
+
errorCorrection: options.errorCorrection,
|
|
707
|
+
mode: options.mode,
|
|
708
|
+
version: options.version
|
|
709
|
+
});
|
|
710
|
+
const quietZone = Math.max(0, Math.floor(options.quietZone ?? 4));
|
|
711
|
+
return { symbol, quietZone, extent: symbol.size + quietZone * 2 };
|
|
712
|
+
}
|
|
713
|
+
function qrCodeSvg(value, options) {
|
|
714
|
+
const { symbol, quietZone, extent } = symbolFor(value, options);
|
|
715
|
+
const size = options.size !== void 0 && Number.isFinite(options.size) && options.size > 0 ? options.size : extent * DEFAULT_PIXELS_PER_MODULE;
|
|
716
|
+
const label = xml(options.label);
|
|
717
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
718
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${extent} ${extent}" shape-rendering="crispEdges" role="img" aria-label="${label}"><title>${label}</title><rect width="${extent}" height="${extent}" fill="${xml(fileBackground(options.background))}"/><path d="${modulePath(symbol.modules, quietZone)}" fill="${xml(fileForeground(options.foreground))}"/></svg>
|
|
719
|
+
`;
|
|
720
|
+
}
|
|
721
|
+
function pngScale(scale, extent) {
|
|
722
|
+
const wanted = scale === void 0 || Number.isNaN(scale) ? DEFAULT_PIXELS_PER_MODULE : Math.floor(scale);
|
|
723
|
+
const ceiling = Math.max(1, Math.floor(MAX_PNG_SIDE / extent));
|
|
724
|
+
return Math.min(Math.max(1, wanted), ceiling);
|
|
725
|
+
}
|
|
726
|
+
function qrCodePng(value, options) {
|
|
727
|
+
const { symbol, quietZone, extent } = symbolFor(value, options);
|
|
728
|
+
const scale = pngScale(options.scale, extent);
|
|
729
|
+
const canvas = options.canvas ?? document.createElement("canvas");
|
|
730
|
+
canvas.width = extent * scale;
|
|
731
|
+
canvas.height = extent * scale;
|
|
732
|
+
const ctx = canvas.getContext("2d");
|
|
733
|
+
if (!ctx) {
|
|
734
|
+
return Promise.reject(new Error("Canvas 2D is unavailable, so no PNG can be drawn"));
|
|
735
|
+
}
|
|
736
|
+
ctx.imageSmoothingEnabled = false;
|
|
737
|
+
ctx.fillStyle = WHITE;
|
|
738
|
+
ctx.fillStyle = fileBackground(options.background);
|
|
739
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
740
|
+
ctx.setTransform(scale, 0, 0, scale, 0, 0);
|
|
741
|
+
ctx.fillStyle = BLACK;
|
|
742
|
+
ctx.fillStyle = fileForeground(options.foreground);
|
|
743
|
+
ctx.fill(new Path2D(modulePath(symbol.modules, quietZone)));
|
|
744
|
+
return new Promise((resolve, reject) => {
|
|
745
|
+
canvas.toBlob((blob) => {
|
|
746
|
+
if (blob) {
|
|
747
|
+
resolve(blob);
|
|
748
|
+
} else {
|
|
749
|
+
reject(new Error("The browser produced no PNG for this canvas"));
|
|
750
|
+
}
|
|
751
|
+
}, "image/png");
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
function safeFileName(name, fallback) {
|
|
755
|
+
const replaced = name.slice(0, 1e3).replace(/[^A-Za-z0-9._-]+/g, "-");
|
|
756
|
+
let start = 0;
|
|
757
|
+
let end = replaced.length;
|
|
758
|
+
while (start < end && (replaced[start] === "." || replaced[start] === "-")) {
|
|
759
|
+
start += 1;
|
|
760
|
+
}
|
|
761
|
+
while (end > start && (replaced[end - 1] === "." || replaced[end - 1] === "-")) {
|
|
762
|
+
end -= 1;
|
|
763
|
+
}
|
|
764
|
+
const trimmed = replaced.slice(start, Math.min(end, start + 100));
|
|
765
|
+
return trimmed === "" ? fallback : trimmed;
|
|
766
|
+
}
|
|
767
|
+
var DEFAULT_ACTIONS = ["download-svg", "download-png", "share", "print"];
|
|
768
|
+
var DEFAULT_LABELS = {
|
|
769
|
+
downloadSvg: "Download SVG",
|
|
770
|
+
downloadPng: "Download PNG",
|
|
771
|
+
share: "Share",
|
|
772
|
+
copyLink: "Copy link",
|
|
773
|
+
copy: "Copy",
|
|
774
|
+
print: "Print",
|
|
775
|
+
downloaded: "Downloaded",
|
|
776
|
+
shared: "Shared",
|
|
777
|
+
copied: "Copied",
|
|
778
|
+
printing: "Opened the print dialog",
|
|
779
|
+
failed: "That did not work"
|
|
780
|
+
};
|
|
781
|
+
var ICONS = {
|
|
782
|
+
"download-svg": "download",
|
|
783
|
+
"download-png": "download",
|
|
784
|
+
share: "share-2",
|
|
785
|
+
print: "printer"
|
|
786
|
+
};
|
|
787
|
+
function isWebUrl(value) {
|
|
788
|
+
try {
|
|
789
|
+
const url = new URL(value);
|
|
790
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
791
|
+
} catch {
|
|
792
|
+
return false;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
function message(error) {
|
|
796
|
+
return error instanceof Error ? error.message : String(error);
|
|
797
|
+
}
|
|
798
|
+
function isAbort(error) {
|
|
799
|
+
return typeof error === "object" && error !== null && error.name === "AbortError";
|
|
800
|
+
}
|
|
801
|
+
function saveBlob(blob, fileName) {
|
|
802
|
+
const url = URL.createObjectURL(blob);
|
|
803
|
+
const anchor = document.createElement("a");
|
|
804
|
+
anchor.href = url;
|
|
805
|
+
anchor.download = fileName;
|
|
806
|
+
anchor.rel = "noopener";
|
|
807
|
+
anchor.click();
|
|
808
|
+
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
809
|
+
}
|
|
810
|
+
var PRINT_FRAME = "data-react-fancy-qr-code-print";
|
|
811
|
+
var QrCodePanel = react.forwardRef(
|
|
812
|
+
({
|
|
813
|
+
value,
|
|
814
|
+
label,
|
|
815
|
+
size = 192,
|
|
816
|
+
errorCorrection = "M",
|
|
817
|
+
quietZone = 4,
|
|
818
|
+
foreground = "#000000",
|
|
819
|
+
background = "#ffffff",
|
|
820
|
+
mode,
|
|
821
|
+
version,
|
|
822
|
+
actions = DEFAULT_ACTIONS,
|
|
823
|
+
showValue = false,
|
|
824
|
+
fileName = "qr-code",
|
|
825
|
+
pngScale: pngScale2,
|
|
826
|
+
shareTitle,
|
|
827
|
+
printSizeMm = 50,
|
|
828
|
+
labels: labelOverrides,
|
|
829
|
+
onAction,
|
|
830
|
+
buttonSize = "lg",
|
|
831
|
+
className,
|
|
832
|
+
...props
|
|
833
|
+
}, ref) => {
|
|
834
|
+
const labels = { ...DEFAULT_LABELS, ...labelOverrides };
|
|
835
|
+
const [status, setStatus] = react.useState("");
|
|
836
|
+
const [via, setVia] = react.useState("clipboard");
|
|
837
|
+
const frameRef = react.useRef(null);
|
|
838
|
+
react.useEffect(() => {
|
|
839
|
+
setVia(typeof navigator !== "undefined" && typeof navigator.share === "function" ? "share" : "clipboard");
|
|
840
|
+
}, []);
|
|
841
|
+
react.useEffect(
|
|
842
|
+
() => () => {
|
|
843
|
+
frameRef.current?.remove();
|
|
844
|
+
frameRef.current = null;
|
|
845
|
+
},
|
|
846
|
+
[]
|
|
847
|
+
);
|
|
848
|
+
const file = { label, errorCorrection, mode, version, quietZone, foreground, background };
|
|
849
|
+
const base = safeFileName(fileName, "qr-code");
|
|
850
|
+
const link = isWebUrl(value);
|
|
851
|
+
const report = react.useCallback(
|
|
852
|
+
(event, announcement) => {
|
|
853
|
+
setStatus(announcement);
|
|
854
|
+
onAction?.(event);
|
|
855
|
+
},
|
|
856
|
+
[onAction]
|
|
857
|
+
);
|
|
858
|
+
const downloadSvg = () => {
|
|
859
|
+
const name = `${base}.svg`;
|
|
860
|
+
try {
|
|
861
|
+
saveBlob(new Blob([qrCodeSvg(value, file)], { type: "image/svg+xml" }), name);
|
|
862
|
+
report({ action: "download-svg", outcome: "done", fileName: name }, labels.downloaded);
|
|
863
|
+
} catch (error) {
|
|
864
|
+
report({ action: "download-svg", outcome: "failed", error: message(error) }, labels.failed);
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
const downloadPng = async () => {
|
|
868
|
+
const name = `${base}.png`;
|
|
869
|
+
try {
|
|
870
|
+
const blob = await qrCodePng(value, { ...file, scale: pngScale2 });
|
|
871
|
+
saveBlob(blob, name);
|
|
872
|
+
report({ action: "download-png", outcome: "done", fileName: name }, labels.downloaded);
|
|
873
|
+
} catch (error) {
|
|
874
|
+
report({ action: "download-png", outcome: "failed", error: message(error) }, labels.failed);
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
const copy = async () => {
|
|
878
|
+
try {
|
|
879
|
+
const clipboard = typeof navigator !== "undefined" ? navigator.clipboard : void 0;
|
|
880
|
+
if (!clipboard || typeof clipboard.writeText !== "function") {
|
|
881
|
+
throw new Error("The Clipboard API is unavailable");
|
|
882
|
+
}
|
|
883
|
+
await clipboard.writeText(value);
|
|
884
|
+
report({ action: "share", outcome: "done", via: "clipboard" }, labels.copied);
|
|
885
|
+
} catch (error) {
|
|
886
|
+
report({ action: "share", outcome: "failed", via: "clipboard", error: message(error) }, labels.failed);
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
const share = async () => {
|
|
890
|
+
const title = shareTitle ?? label;
|
|
891
|
+
const data = link ? { title, url: value } : { title, text: value };
|
|
892
|
+
const canShare = typeof navigator !== "undefined" && typeof navigator.share === "function" && (typeof navigator.canShare !== "function" || navigator.canShare(data));
|
|
893
|
+
if (!canShare) {
|
|
894
|
+
await copy();
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
try {
|
|
898
|
+
await navigator.share(data);
|
|
899
|
+
report({ action: "share", outcome: "done", via: "share" }, labels.shared);
|
|
900
|
+
} catch (error) {
|
|
901
|
+
if (isAbort(error)) {
|
|
902
|
+
report({ action: "share", outcome: "cancelled", via: "share" }, "");
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
await copy();
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
const print = () => {
|
|
909
|
+
frameRef.current?.remove();
|
|
910
|
+
document.querySelectorAll(`iframe[${PRINT_FRAME}]`).forEach((stale) => stale.remove());
|
|
911
|
+
const frame = document.createElement("iframe");
|
|
912
|
+
frame.setAttribute(PRINT_FRAME, "");
|
|
913
|
+
frame.setAttribute("aria-hidden", "true");
|
|
914
|
+
frame.tabIndex = -1;
|
|
915
|
+
Object.assign(frame.style, { position: "fixed", right: "0", bottom: "0", width: "0", height: "0", border: "0" });
|
|
916
|
+
document.body.appendChild(frame);
|
|
917
|
+
frameRef.current = frame;
|
|
918
|
+
const doc = frame.contentDocument;
|
|
919
|
+
const win = frame.contentWindow;
|
|
920
|
+
if (!doc || !win) {
|
|
921
|
+
report({ action: "print", outcome: "failed", error: "The print frame has no document" }, labels.failed);
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
doc.title = label;
|
|
925
|
+
Object.assign(doc.body.style, {
|
|
926
|
+
margin: "0",
|
|
927
|
+
padding: "15mm 10mm",
|
|
928
|
+
display: "flex",
|
|
929
|
+
flexDirection: "column",
|
|
930
|
+
alignItems: "center",
|
|
931
|
+
gap: "6mm",
|
|
932
|
+
color: "#000000",
|
|
933
|
+
background: "#ffffff",
|
|
934
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
|
|
935
|
+
});
|
|
936
|
+
const parsed = new DOMParser().parseFromString(qrCodeSvg(value, file), "image/svg+xml");
|
|
937
|
+
const svg = doc.importNode(parsed.documentElement, true);
|
|
938
|
+
const millimetres = Number.isFinite(printSizeMm) && printSizeMm > 0 ? printSizeMm : 50;
|
|
939
|
+
svg.setAttribute("width", `${millimetres}mm`);
|
|
940
|
+
svg.setAttribute("height", `${millimetres}mm`);
|
|
941
|
+
doc.body.appendChild(svg);
|
|
942
|
+
if (showValue) {
|
|
943
|
+
const text2 = doc.createElement("p");
|
|
944
|
+
text2.textContent = value;
|
|
945
|
+
Object.assign(text2.style, {
|
|
946
|
+
margin: "0",
|
|
947
|
+
maxWidth: `${Math.max(millimetres, 80)}mm`,
|
|
948
|
+
fontSize: "12pt",
|
|
949
|
+
textAlign: "center",
|
|
950
|
+
overflowWrap: "anywhere"
|
|
951
|
+
});
|
|
952
|
+
doc.body.appendChild(text2);
|
|
953
|
+
}
|
|
954
|
+
win.addEventListener("afterprint", () => {
|
|
955
|
+
if (frameRef.current === frame) {
|
|
956
|
+
frameRef.current = null;
|
|
957
|
+
}
|
|
958
|
+
frame.remove();
|
|
959
|
+
});
|
|
960
|
+
setTimeout(() => {
|
|
961
|
+
try {
|
|
962
|
+
win.print();
|
|
963
|
+
report({ action: "print", outcome: "done" }, labels.printing);
|
|
964
|
+
} catch (error) {
|
|
965
|
+
report({ action: "print", outcome: "failed", error: message(error) }, labels.failed);
|
|
966
|
+
}
|
|
967
|
+
}, 0);
|
|
968
|
+
};
|
|
969
|
+
const run = {
|
|
970
|
+
"download-svg": downloadSvg,
|
|
971
|
+
"download-png": () => void downloadPng(),
|
|
972
|
+
share: () => void share(),
|
|
973
|
+
print
|
|
974
|
+
};
|
|
975
|
+
const text = {
|
|
976
|
+
"download-svg": labels.downloadSvg,
|
|
977
|
+
"download-png": labels.downloadPng,
|
|
978
|
+
share: via === "share" ? labels.share : link ? labels.copyLink : labels.copy,
|
|
979
|
+
print: labels.print
|
|
980
|
+
};
|
|
981
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
982
|
+
"div",
|
|
983
|
+
{
|
|
984
|
+
ref,
|
|
985
|
+
role: "group",
|
|
986
|
+
"aria-label": label,
|
|
987
|
+
"data-react-fancy-qr-code-panel": "",
|
|
988
|
+
className: chunkZU5Q77OT_cjs.cn("inline-flex max-w-full flex-col items-center gap-3", className),
|
|
989
|
+
...props,
|
|
990
|
+
children: [
|
|
991
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { "data-react-fancy-qr-code-panel-code": "", className: "leading-none", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
992
|
+
QrCode,
|
|
993
|
+
{
|
|
994
|
+
value,
|
|
995
|
+
label,
|
|
996
|
+
size,
|
|
997
|
+
errorCorrection,
|
|
998
|
+
quietZone,
|
|
999
|
+
foreground,
|
|
1000
|
+
background,
|
|
1001
|
+
mode,
|
|
1002
|
+
version
|
|
1003
|
+
}
|
|
1004
|
+
) }),
|
|
1005
|
+
showValue && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1006
|
+
"p",
|
|
1007
|
+
{
|
|
1008
|
+
"data-react-fancy-qr-code-panel-value": "",
|
|
1009
|
+
className: "m-0 max-w-full select-all break-all text-center font-mono text-sm text-zinc-900 dark:text-zinc-100",
|
|
1010
|
+
children: value
|
|
1011
|
+
}
|
|
1012
|
+
),
|
|
1013
|
+
actions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-react-fancy-qr-code-panel-actions": "", className: "flex flex-wrap justify-center gap-2", children: actions.map((action) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1014
|
+
chunkNZHVDWOM_cjs.Button,
|
|
1015
|
+
{
|
|
1016
|
+
size: buttonSize,
|
|
1017
|
+
icon: action === "share" && via === "clipboard" ? "copy" : ICONS[action],
|
|
1018
|
+
"data-react-fancy-qr-code-action": action,
|
|
1019
|
+
"data-via": action === "share" ? via : void 0,
|
|
1020
|
+
onClick: run[action],
|
|
1021
|
+
children: text[action]
|
|
1022
|
+
},
|
|
1023
|
+
action
|
|
1024
|
+
)) }),
|
|
1025
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1026
|
+
"span",
|
|
1027
|
+
{
|
|
1028
|
+
role: "status",
|
|
1029
|
+
"aria-live": "polite",
|
|
1030
|
+
"data-react-fancy-qr-code-panel-status": "",
|
|
1031
|
+
className: "sr-only",
|
|
1032
|
+
children: status
|
|
1033
|
+
}
|
|
1034
|
+
)
|
|
1035
|
+
]
|
|
1036
|
+
}
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
);
|
|
1040
|
+
QrCodePanel.displayName = "QrCodePanel";
|
|
1041
|
+
|
|
660
1042
|
exports.QrCode = QrCode;
|
|
1043
|
+
exports.QrCodePanel = QrCodePanel;
|
|
661
1044
|
exports.encodeQr = encodeQr;
|
|
1045
|
+
exports.qrCodePng = qrCodePng;
|
|
1046
|
+
exports.qrCodeSvg = qrCodeSvg;
|
|
662
1047
|
exports.selectMode = selectMode;
|
|
663
|
-
//# sourceMappingURL=chunk-
|
|
664
|
-
//# sourceMappingURL=chunk-
|
|
1048
|
+
//# sourceMappingURL=chunk-CNWTYRZC.cjs.map
|
|
1049
|
+
//# sourceMappingURL=chunk-CNWTYRZC.cjs.map
|