jaci-ui 0.9.1 → 0.9.3
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 +16 -0
- package/README.md +20 -12
- package/dist/components/button/button.cjs +14 -4
- package/dist/components/button/button.cjs.map +1 -1
- package/dist/components/button/button.d.cts +1 -0
- package/dist/components/button/button.d.ts +1 -0
- package/dist/components/button/button.js +14 -4
- package/dist/components/button/button.js.map +1 -1
- package/dist/components/qr-code/qr-code.cjs +86 -41
- package/dist/components/qr-code/qr-code.cjs.map +1 -1
- package/dist/components/qr-code/qr-code.d.cts +4 -1
- package/dist/components/qr-code/qr-code.d.ts +4 -1
- package/dist/components/qr-code/qr-code.js +87 -42
- package/dist/components/qr-code/qr-code.js.map +1 -1
- package/dist/styles.css +8 -6
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- caa17c1: Allow Button links composed with `render={<a />}` to render safely from Next.js server components.
|
|
8
|
+
- 3efbbee: Keep Accordion roots at a stable responsive width when all panels are collapsed.
|
|
9
|
+
|
|
10
|
+
## 0.9.2
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 894b4e8: Update Storybook and compatible workspace tooling dependencies to their latest patch releases.
|
|
15
|
+
- 10446a3: Fix `Button.render` with native and framework link elements, including Next.js client boundaries.
|
|
16
|
+
- cdcfb85: Improve QRCode finder contrast in dark themes and add native SVG download support.
|
|
17
|
+
- 77a1b9f: Improve QRCode's native SVG download link accessibility and preserve the QR code's image semantics.
|
|
18
|
+
|
|
3
19
|
## 0.9.1
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -38,6 +38,9 @@ export function Example() {
|
|
|
38
38
|
<Stack gap="md">
|
|
39
39
|
<Button variant="solid">Continue</Button>
|
|
40
40
|
|
|
41
|
+
{/* Button links also work in a Next App Router Server Component. */}
|
|
42
|
+
<Button variant="outline" render={<a href="/docs" />}>Read the docs</Button>
|
|
43
|
+
|
|
41
44
|
<ColorPicker.Root defaultValue="#2563eb" name="brand-color">
|
|
42
45
|
<ColorPicker.Label>Brand color</ColorPicker.Label>
|
|
43
46
|
<ColorPicker.Control>
|
|
@@ -84,10 +87,10 @@ import { AspectRatio, Code, Figure, Image, Kbd, Quote, Stat, StatGroup } from "j
|
|
|
84
87
|
</StatGroup.Root>;
|
|
85
88
|
```
|
|
86
89
|
|
|
87
|
-
###
|
|
90
|
+
### Media and visual utilities
|
|
88
91
|
|
|
89
|
-
`Carousel`
|
|
90
|
-
|
|
92
|
+
`Carousel` provides accessible slides with keyboard navigation, indicators, swipe support and
|
|
93
|
+
optional autoplay. Always provide an `aria-label` on the root and alternative text for images:
|
|
91
94
|
|
|
92
95
|
```tsx
|
|
93
96
|
<Carousel.Root aria-label="Featured projects" loop>
|
|
@@ -108,8 +111,8 @@ opcional. Sempre forneça um `aria-label` no root e texto alternativo nas imagen
|
|
|
108
111
|
</Carousel.Root>
|
|
109
112
|
```
|
|
110
113
|
|
|
111
|
-
`QRCode`
|
|
112
|
-
|
|
114
|
+
`QRCode` generates SVG on the server and client, with circular modules and rounded square eyes.
|
|
115
|
+
The application can control the value directly:
|
|
113
116
|
|
|
114
117
|
```tsx
|
|
115
118
|
<QRCode
|
|
@@ -117,10 +120,15 @@ arredondados. O valor pode ser controlado diretamente pela aplicação:
|
|
|
117
120
|
size={240}
|
|
118
121
|
errorCorrectionLevel="Q"
|
|
119
122
|
label="Jaci UI website"
|
|
123
|
+
download="jaci-ui-qr.svg"
|
|
124
|
+
downloadLabel="Download Jaci UI QR code"
|
|
120
125
|
/>
|
|
121
126
|
```
|
|
122
127
|
|
|
123
|
-
|
|
128
|
+
When `download` is provided, the SVG exposes a native link for downloading the code without
|
|
129
|
+
using browser APIs during SSR.
|
|
130
|
+
|
|
131
|
+
For downloads, `DownloadTrigger` is a regular HTML link: it does not fetch or create a `Blob`:
|
|
124
132
|
|
|
125
133
|
```tsx
|
|
126
134
|
<DownloadTrigger href="/files/report.pdf" download="report.pdf">
|
|
@@ -128,19 +136,19 @@ Para downloads, `DownloadTrigger` é um link HTML comum e não faz fetch nem cri
|
|
|
128
136
|
</DownloadTrigger>
|
|
129
137
|
```
|
|
130
138
|
|
|
131
|
-
|
|
132
|
-
|
|
139
|
+
For cross-origin URLs, the filename and the download authorization remain subject to browser
|
|
140
|
+
rules and server headers.
|
|
133
141
|
|
|
134
|
-
Use `Separator`
|
|
135
|
-
|
|
142
|
+
Use `Separator` for a semantic or decorative line between sections, and `Spacer` when only
|
|
143
|
+
space—not a line—is needed:
|
|
136
144
|
|
|
137
145
|
```tsx
|
|
138
146
|
<Separator orientation="horizontal" />
|
|
139
147
|
<Spacer axis="vertical" size="lg" />
|
|
140
148
|
```
|
|
141
149
|
|
|
142
|
-
|
|
143
|
-
|
|
150
|
+
These components support SSR and React 18/19. Interactive modules declare their client boundary
|
|
151
|
+
where required and do not access browser APIs during import or server rendering.
|
|
144
152
|
|
|
145
153
|
`AspectRatio` fills the available inline size of its parent. In shrink-to-fit or flex
|
|
146
154
|
layouts, give the parent an explicit `width`/`max-width` so the ratio has a measurable
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use client";
|
|
2
1
|
const require_cx = require("../../styled-system/css/cx.cjs");
|
|
3
2
|
const require_button = require("../../styled-system/recipes/button.cjs");
|
|
4
3
|
const require_index = require("../layout/index.cjs");
|
|
@@ -16,7 +15,7 @@ function content(children, loading, startIcon, endIcon) {
|
|
|
16
15
|
!loading ? endIcon : void 0
|
|
17
16
|
] });
|
|
18
17
|
}
|
|
19
|
-
const Button = (0, react.forwardRef)(function Button({ children, className, variant = "outline", size = "md", fullWidth = false, loading = false, startIcon, endIcon, disabled, render, type = "button", ...props }, ref) {
|
|
18
|
+
const Button = (0, react.forwardRef)(function Button({ children, className, variant = "outline", size = "md", fullWidth = false, loading = false, startIcon, endIcon, disabled, onClick: buttonOnClick, render, type = "button", ...props }, ref) {
|
|
20
19
|
const buttonClassName = require_cx.cx(require_button.button({
|
|
21
20
|
variant,
|
|
22
21
|
size,
|
|
@@ -24,7 +23,13 @@ const Button = (0, react.forwardRef)(function Button({ children, className, vari
|
|
|
24
23
|
}), className);
|
|
25
24
|
const childrenWithIcons = content(children, loading, startIcon, endIcon);
|
|
26
25
|
if (render) {
|
|
27
|
-
const renderedProps = render.props;
|
|
26
|
+
const renderedProps = render.props ?? {};
|
|
27
|
+
const renderedOnClick = renderedProps.onClick;
|
|
28
|
+
const handleRenderedClick = renderedOnClick || buttonOnClick ? disabled || loading ? (event) => event.preventDefault() : (event) => {
|
|
29
|
+
renderedOnClick?.(event);
|
|
30
|
+
if (!event.defaultPrevented) buttonOnClick?.(event);
|
|
31
|
+
} : void 0;
|
|
32
|
+
const renderedHref = disabled || loading ? render.type === "a" ? void 0 : renderedProps.href : renderedProps.href;
|
|
28
33
|
return (0, react.cloneElement)(render, {
|
|
29
34
|
...props,
|
|
30
35
|
"aria-busy": loading || void 0,
|
|
@@ -33,7 +38,11 @@ const Button = (0, react.forwardRef)(function Button({ children, className, vari
|
|
|
33
38
|
"data-jaci-component": "button",
|
|
34
39
|
"data-slot": "button",
|
|
35
40
|
children: childrenWithIcons,
|
|
36
|
-
onClick:
|
|
41
|
+
onClick: handleRenderedClick,
|
|
42
|
+
...render.type === "a" && (disabled || loading) ? {
|
|
43
|
+
href: renderedHref,
|
|
44
|
+
tabIndex: -1
|
|
45
|
+
} : {}
|
|
37
46
|
});
|
|
38
47
|
}
|
|
39
48
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -43,6 +52,7 @@ const Button = (0, react.forwardRef)(function Button({ children, className, vari
|
|
|
43
52
|
"data-jaci-component": "button",
|
|
44
53
|
"data-slot": "button",
|
|
45
54
|
disabled: disabled || loading,
|
|
55
|
+
onClick: buttonOnClick,
|
|
46
56
|
ref,
|
|
47
57
|
type,
|
|
48
58
|
children: childrenWithIcons
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.cjs","names":["Spinner","cx","button"],"sources":["../../../src/components/button/button.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"button.cjs","names":["Spinner","cx","button"],"sources":["../../../src/components/button/button.tsx"],"sourcesContent":["import { cloneElement, forwardRef } from \"react\";\nimport type {\n ComponentPropsWithoutRef,\n MouseEvent,\n MouseEventHandler,\n ReactElement,\n ReactNode,\n} from \"react\";\n\nimport { cx } from \"../../styled-system/css\";\nimport { button } from \"../../styled-system/recipes\";\nimport { Spinner } from \"../layout\";\n\ntype RenderableButtonProps = Record<string, unknown> & {\n children?: ReactNode | undefined;\n className?: string | undefined;\n href?: string | undefined;\n onClick?: MouseEventHandler<HTMLElement> | undefined;\n};\n\nexport interface ButtonProps\n extends Omit<ComponentPropsWithoutRef<\"button\">, \"color\" | \"children\"> {\n children?: ReactNode;\n variant?: \"outline\" | \"solid\" | \"ghost\" | \"danger\";\n size?: \"sm\" | \"md\" | \"lg\";\n fullWidth?: boolean;\n loading?: boolean;\n startIcon?: ReactNode;\n endIcon?: ReactNode;\n render?: ReactElement<RenderableButtonProps>;\n}\n\nfunction content(children: ReactNode, loading: boolean, startIcon: ReactNode, endIcon: ReactNode) {\n return (\n <>\n {loading ? <Spinner aria-hidden=\"true\" label=\"\" size=\"sm\" /> : startIcon}\n {children}\n {!loading ? endIcon : undefined}\n </>\n );\n}\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n children,\n className,\n variant = \"outline\",\n size = \"md\",\n fullWidth = false,\n loading = false,\n startIcon,\n endIcon,\n disabled,\n onClick: buttonOnClick,\n render,\n type = \"button\",\n ...props\n },\n ref,\n) {\n const buttonClassName = cx(button({ variant, size, fullWidth }), className);\n const childrenWithIcons = content(children, loading, startIcon, endIcon);\n\n if (render) {\n // Keep the render path tolerant of framework-rendered elements and avoid\n // adding a synthetic client handler when the composed element has none.\n const renderedProps = render.props ?? {};\n const renderedOnClick = renderedProps.onClick as MouseEventHandler<HTMLElement> | undefined;\n const handleRenderedClick: MouseEventHandler<HTMLElement> | undefined =\n renderedOnClick || buttonOnClick\n ? disabled || loading\n ? (event) => event.preventDefault()\n : (event) => {\n renderedOnClick?.(event);\n if (!event.defaultPrevented) {\n buttonOnClick?.(event as unknown as MouseEvent<HTMLButtonElement>);\n }\n }\n : undefined;\n\n // A server component can compose Button with a native anchor. Remove its\n // href while disabled/loading instead of attaching a client-only event\n // handler during the server render.\n const renderedHref =\n disabled || loading\n ? render.type === \"a\"\n ? undefined\n : renderedProps.href\n : renderedProps.href;\n\n return cloneElement(render, {\n ...props,\n \"aria-busy\": loading || undefined,\n \"aria-disabled\": disabled || loading || undefined,\n className: cx(buttonClassName, renderedProps.className),\n \"data-jaci-component\": \"button\",\n \"data-slot\": \"button\",\n children: childrenWithIcons,\n onClick: handleRenderedClick,\n ...(render.type === \"a\" && (disabled || loading) ? { href: renderedHref, tabIndex: -1 } : {}),\n });\n }\n\n return (\n <button\n {...props}\n aria-busy={loading || undefined}\n className={buttonClassName}\n data-jaci-component=\"button\"\n data-slot=\"button\"\n disabled={disabled || loading}\n onClick={buttonOnClick}\n ref={ref}\n type={type}\n >\n {childrenWithIcons}\n </button>\n );\n});\n"],"mappings":";;;;;;AAgCA,SAAS,QAAQ,UAAqB,SAAkB,WAAsB,SAAoB;CAChG,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA;EACG,UAAU,iBAAA,GAAA,kBAAA,IAAA,CAACA,cAAAA,SAAD;GAAS,eAAY;GAAO,OAAM;GAAG,MAAK;EAAM,CAAA,IAAI;EAC9D;EACA,CAAC,UAAU,UAAU,KAAA;CACtB,EAAA,CAAA;AAEN;AAEA,MAAa,UAAA,GAAA,MAAA,WAAA,CAAoD,SAAS,OACxE,EACE,UACA,WACA,UAAU,WACV,OAAO,MACP,YAAY,OACZ,UAAU,OACV,WACA,SACA,UACA,SAAS,eACT,QACA,OAAO,UACP,GAAG,SAEL,KACA;CACA,MAAM,kBAAkBC,WAAAA,GAAGC,eAAAA,OAAO;EAAE;EAAS;EAAM;CAAU,CAAC,GAAG,SAAS;CAC1E,MAAM,oBAAoB,QAAQ,UAAU,SAAS,WAAW,OAAO;CAEvE,IAAI,QAAQ;EAGV,MAAM,gBAAgB,OAAO,SAAS,CAAC;EACvC,MAAM,kBAAkB,cAAc;EACtC,MAAM,sBACJ,mBAAmB,gBACf,YAAY,WACT,UAAU,MAAM,eAAe,KAC/B,UAAU;GACT,kBAAkB,KAAK;GACvB,IAAI,CAAC,MAAM,kBACT,gBAAgB,KAAiD;EAErE,IACF,KAAA;EAKN,MAAM,eACJ,YAAY,UACR,OAAO,SAAS,MACd,KAAA,IACA,cAAc,OAChB,cAAc;EAEpB,QAAA,GAAA,MAAA,aAAA,CAAoB,QAAQ;GAC1B,GAAG;GACH,aAAa,WAAW,KAAA;GACxB,iBAAiB,YAAY,WAAW,KAAA;GACxC,WAAWD,WAAAA,GAAG,iBAAiB,cAAc,SAAS;GACtD,uBAAuB;GACvB,aAAa;GACb,UAAU;GACV,SAAS;GACT,GAAI,OAAO,SAAS,QAAQ,YAAY,WAAW;IAAE,MAAM;IAAc,UAAU;GAAG,IAAI,CAAC;EAC7F,CAAC;CACH;CAEA,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;EACE,GAAI;EACJ,aAAW,WAAW,KAAA;EACtB,WAAW;EACX,uBAAoB;EACpB,aAAU;EACV,UAAU,YAAY;EACtB,SAAS;EACJ;EACC;YAEL;CACK,CAAA;AAEZ,CAAC"}
|
|
@@ -3,6 +3,7 @@ import { ComponentPropsWithoutRef, MouseEventHandler, ReactElement, ReactNode }
|
|
|
3
3
|
type RenderableButtonProps = Record<string, unknown> & {
|
|
4
4
|
children?: ReactNode | undefined;
|
|
5
5
|
className?: string | undefined;
|
|
6
|
+
href?: string | undefined;
|
|
6
7
|
onClick?: MouseEventHandler<HTMLElement> | undefined;
|
|
7
8
|
};
|
|
8
9
|
interface ButtonProps extends Omit<ComponentPropsWithoutRef<"button">, "color" | "children"> {
|
|
@@ -3,6 +3,7 @@ import { ComponentPropsWithoutRef, MouseEventHandler, ReactElement, ReactNode }
|
|
|
3
3
|
type RenderableButtonProps = Record<string, unknown> & {
|
|
4
4
|
children?: ReactNode | undefined;
|
|
5
5
|
className?: string | undefined;
|
|
6
|
+
href?: string | undefined;
|
|
6
7
|
onClick?: MouseEventHandler<HTMLElement> | undefined;
|
|
7
8
|
};
|
|
8
9
|
interface ButtonProps extends Omit<ComponentPropsWithoutRef<"button">, "color" | "children"> {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use client";
|
|
2
1
|
import { cx } from "../../styled-system/css/cx.js";
|
|
3
2
|
import { button } from "../../styled-system/recipes/button.js";
|
|
4
3
|
import { Spinner } from "../layout/index.js";
|
|
@@ -16,7 +15,7 @@ function content(children, loading, startIcon, endIcon) {
|
|
|
16
15
|
!loading ? endIcon : void 0
|
|
17
16
|
] });
|
|
18
17
|
}
|
|
19
|
-
const Button = forwardRef(function Button({ children, className, variant = "outline", size = "md", fullWidth = false, loading = false, startIcon, endIcon, disabled, render, type = "button", ...props }, ref) {
|
|
18
|
+
const Button = forwardRef(function Button({ children, className, variant = "outline", size = "md", fullWidth = false, loading = false, startIcon, endIcon, disabled, onClick: buttonOnClick, render, type = "button", ...props }, ref) {
|
|
20
19
|
const buttonClassName = cx(button({
|
|
21
20
|
variant,
|
|
22
21
|
size,
|
|
@@ -24,7 +23,13 @@ const Button = forwardRef(function Button({ children, className, variant = "outl
|
|
|
24
23
|
}), className);
|
|
25
24
|
const childrenWithIcons = content(children, loading, startIcon, endIcon);
|
|
26
25
|
if (render) {
|
|
27
|
-
const renderedProps = render.props;
|
|
26
|
+
const renderedProps = render.props ?? {};
|
|
27
|
+
const renderedOnClick = renderedProps.onClick;
|
|
28
|
+
const handleRenderedClick = renderedOnClick || buttonOnClick ? disabled || loading ? (event) => event.preventDefault() : (event) => {
|
|
29
|
+
renderedOnClick?.(event);
|
|
30
|
+
if (!event.defaultPrevented) buttonOnClick?.(event);
|
|
31
|
+
} : void 0;
|
|
32
|
+
const renderedHref = disabled || loading ? render.type === "a" ? void 0 : renderedProps.href : renderedProps.href;
|
|
28
33
|
return cloneElement(render, {
|
|
29
34
|
...props,
|
|
30
35
|
"aria-busy": loading || void 0,
|
|
@@ -33,7 +38,11 @@ const Button = forwardRef(function Button({ children, className, variant = "outl
|
|
|
33
38
|
"data-jaci-component": "button",
|
|
34
39
|
"data-slot": "button",
|
|
35
40
|
children: childrenWithIcons,
|
|
36
|
-
onClick:
|
|
41
|
+
onClick: handleRenderedClick,
|
|
42
|
+
...render.type === "a" && (disabled || loading) ? {
|
|
43
|
+
href: renderedHref,
|
|
44
|
+
tabIndex: -1
|
|
45
|
+
} : {}
|
|
37
46
|
});
|
|
38
47
|
}
|
|
39
48
|
return /* @__PURE__ */ jsx("button", {
|
|
@@ -43,6 +52,7 @@ const Button = forwardRef(function Button({ children, className, variant = "outl
|
|
|
43
52
|
"data-jaci-component": "button",
|
|
44
53
|
"data-slot": "button",
|
|
45
54
|
disabled: disabled || loading,
|
|
55
|
+
onClick: buttonOnClick,
|
|
46
56
|
ref,
|
|
47
57
|
type,
|
|
48
58
|
children: childrenWithIcons
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.js","names":[],"sources":["../../../src/components/button/button.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"button.js","names":[],"sources":["../../../src/components/button/button.tsx"],"sourcesContent":["import { cloneElement, forwardRef } from \"react\";\nimport type {\n ComponentPropsWithoutRef,\n MouseEvent,\n MouseEventHandler,\n ReactElement,\n ReactNode,\n} from \"react\";\n\nimport { cx } from \"../../styled-system/css\";\nimport { button } from \"../../styled-system/recipes\";\nimport { Spinner } from \"../layout\";\n\ntype RenderableButtonProps = Record<string, unknown> & {\n children?: ReactNode | undefined;\n className?: string | undefined;\n href?: string | undefined;\n onClick?: MouseEventHandler<HTMLElement> | undefined;\n};\n\nexport interface ButtonProps\n extends Omit<ComponentPropsWithoutRef<\"button\">, \"color\" | \"children\"> {\n children?: ReactNode;\n variant?: \"outline\" | \"solid\" | \"ghost\" | \"danger\";\n size?: \"sm\" | \"md\" | \"lg\";\n fullWidth?: boolean;\n loading?: boolean;\n startIcon?: ReactNode;\n endIcon?: ReactNode;\n render?: ReactElement<RenderableButtonProps>;\n}\n\nfunction content(children: ReactNode, loading: boolean, startIcon: ReactNode, endIcon: ReactNode) {\n return (\n <>\n {loading ? <Spinner aria-hidden=\"true\" label=\"\" size=\"sm\" /> : startIcon}\n {children}\n {!loading ? endIcon : undefined}\n </>\n );\n}\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n children,\n className,\n variant = \"outline\",\n size = \"md\",\n fullWidth = false,\n loading = false,\n startIcon,\n endIcon,\n disabled,\n onClick: buttonOnClick,\n render,\n type = \"button\",\n ...props\n },\n ref,\n) {\n const buttonClassName = cx(button({ variant, size, fullWidth }), className);\n const childrenWithIcons = content(children, loading, startIcon, endIcon);\n\n if (render) {\n // Keep the render path tolerant of framework-rendered elements and avoid\n // adding a synthetic client handler when the composed element has none.\n const renderedProps = render.props ?? {};\n const renderedOnClick = renderedProps.onClick as MouseEventHandler<HTMLElement> | undefined;\n const handleRenderedClick: MouseEventHandler<HTMLElement> | undefined =\n renderedOnClick || buttonOnClick\n ? disabled || loading\n ? (event) => event.preventDefault()\n : (event) => {\n renderedOnClick?.(event);\n if (!event.defaultPrevented) {\n buttonOnClick?.(event as unknown as MouseEvent<HTMLButtonElement>);\n }\n }\n : undefined;\n\n // A server component can compose Button with a native anchor. Remove its\n // href while disabled/loading instead of attaching a client-only event\n // handler during the server render.\n const renderedHref =\n disabled || loading\n ? render.type === \"a\"\n ? undefined\n : renderedProps.href\n : renderedProps.href;\n\n return cloneElement(render, {\n ...props,\n \"aria-busy\": loading || undefined,\n \"aria-disabled\": disabled || loading || undefined,\n className: cx(buttonClassName, renderedProps.className),\n \"data-jaci-component\": \"button\",\n \"data-slot\": \"button\",\n children: childrenWithIcons,\n onClick: handleRenderedClick,\n ...(render.type === \"a\" && (disabled || loading) ? { href: renderedHref, tabIndex: -1 } : {}),\n });\n }\n\n return (\n <button\n {...props}\n aria-busy={loading || undefined}\n className={buttonClassName}\n data-jaci-component=\"button\"\n data-slot=\"button\"\n disabled={disabled || loading}\n onClick={buttonOnClick}\n ref={ref}\n type={type}\n >\n {childrenWithIcons}\n </button>\n );\n});\n"],"mappings":";;;;;;AAgCA,SAAS,QAAQ,UAAqB,SAAkB,WAAsB,SAAoB;CAChG,OACE,qBAAA,UAAA,EAAA,UAAA;EACG,UAAU,oBAAC,SAAD;GAAS,eAAY;GAAO,OAAM;GAAG,MAAK;EAAM,CAAA,IAAI;EAC9D;EACA,CAAC,UAAU,UAAU,KAAA;CACtB,EAAA,CAAA;AAEN;AAEA,MAAa,SAAS,WAA2C,SAAS,OACxE,EACE,UACA,WACA,UAAU,WACV,OAAO,MACP,YAAY,OACZ,UAAU,OACV,WACA,SACA,UACA,SAAS,eACT,QACA,OAAO,UACP,GAAG,SAEL,KACA;CACA,MAAM,kBAAkB,GAAG,OAAO;EAAE;EAAS;EAAM;CAAU,CAAC,GAAG,SAAS;CAC1E,MAAM,oBAAoB,QAAQ,UAAU,SAAS,WAAW,OAAO;CAEvE,IAAI,QAAQ;EAGV,MAAM,gBAAgB,OAAO,SAAS,CAAC;EACvC,MAAM,kBAAkB,cAAc;EACtC,MAAM,sBACJ,mBAAmB,gBACf,YAAY,WACT,UAAU,MAAM,eAAe,KAC/B,UAAU;GACT,kBAAkB,KAAK;GACvB,IAAI,CAAC,MAAM,kBACT,gBAAgB,KAAiD;EAErE,IACF,KAAA;EAKN,MAAM,eACJ,YAAY,UACR,OAAO,SAAS,MACd,KAAA,IACA,cAAc,OAChB,cAAc;EAEpB,OAAO,aAAa,QAAQ;GAC1B,GAAG;GACH,aAAa,WAAW,KAAA;GACxB,iBAAiB,YAAY,WAAW,KAAA;GACxC,WAAW,GAAG,iBAAiB,cAAc,SAAS;GACtD,uBAAuB;GACvB,aAAa;GACb,UAAU;GACV,SAAS;GACT,GAAI,OAAO,SAAS,QAAQ,YAAY,WAAW;IAAE,MAAM;IAAc,UAAU;GAAG,IAAI,CAAC;EAC7F,CAAC;CACH;CAEA,OACE,oBAAC,UAAD;EACE,GAAI;EACJ,aAAW,WAAW,KAAA;EACtB,WAAW;EACX,uBAAoB;EACpB,aAAU;EACV,UAAU,YAAY;EACtB,SAAS;EACJ;EACC;YAEL;CACK,CAAA;AAEZ,CAAC"}
|
|
@@ -14,6 +14,7 @@ function Finder({ x, y, color, background }) {
|
|
|
14
14
|
"data-slot": "qr-code-eye",
|
|
15
15
|
children: [
|
|
16
16
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
17
|
+
"data-slot": "qr-code-eye-frame",
|
|
17
18
|
fill: color,
|
|
18
19
|
height: "7",
|
|
19
20
|
rx: "1",
|
|
@@ -22,6 +23,7 @@ function Finder({ x, y, color, background }) {
|
|
|
22
23
|
y
|
|
23
24
|
}),
|
|
24
25
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
26
|
+
"data-slot": "qr-code-eye-cutout",
|
|
25
27
|
fill: background,
|
|
26
28
|
height: "5",
|
|
27
29
|
rx: "0.75",
|
|
@@ -30,6 +32,7 @@ function Finder({ x, y, color, background }) {
|
|
|
30
32
|
y: y + 1
|
|
31
33
|
}),
|
|
32
34
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
35
|
+
"data-slot": "qr-code-eyeball",
|
|
33
36
|
fill: color,
|
|
34
37
|
height: "3",
|
|
35
38
|
rx: "0.5",
|
|
@@ -40,66 +43,108 @@ function Finder({ x, y, color, background }) {
|
|
|
40
43
|
]
|
|
41
44
|
});
|
|
42
45
|
}
|
|
43
|
-
|
|
46
|
+
function escapeXml(value) {
|
|
47
|
+
return value.replace(/[&<>'"]/g, (character) => {
|
|
48
|
+
return {
|
|
49
|
+
"&": "&",
|
|
50
|
+
"<": "<",
|
|
51
|
+
">": ">",
|
|
52
|
+
"'": "'",
|
|
53
|
+
"\"": """
|
|
54
|
+
}[character] ?? character;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function createDownloadSvg({ background, dots, foreground, finderBackground, label, margin, moduleCount }) {
|
|
58
|
+
const viewBoxSize = moduleCount + margin * 2;
|
|
59
|
+
const backgroundMarkup = background === "transparent" ? "" : `<rect fill="${escapeXml(background)}" height="${viewBoxSize}" width="${viewBoxSize}" x="0" y="0"/>`;
|
|
60
|
+
const dotsMarkup = dots.map(([row, column]) => `<circle cx="${margin + column + .5}" cy="${margin + row + .5}" fill="${escapeXml(foreground)}" r="0.43"/>`).join("");
|
|
61
|
+
const finderMarkup = (x, y) => `<g><rect fill="${escapeXml(foreground)}" height="7" rx="1" width="7" x="${x}" y="${y}"/><rect fill="${escapeXml(finderBackground)}" height="5" rx="0.75" width="5" x="${x + 1}" y="${y + 1}"/><rect fill="${escapeXml(foreground)}" height="3" rx="0.5" width="3" x="${x + 2}" y="${y + 2}"/></g>`;
|
|
62
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" aria-label="${escapeXml(label)}" role="img" viewBox="0 0 ${viewBoxSize} ${viewBoxSize}">${backgroundMarkup}${dotsMarkup}${finderMarkup(margin, margin)}${finderMarkup(margin + moduleCount - 7, margin)}${finderMarkup(margin, margin + moduleCount - 7)}</svg>`;
|
|
63
|
+
}
|
|
64
|
+
const QRCode = (0, react.forwardRef)(function QRCode({ background = "#ffffff", className, download = false, downloadLabel = "Download QR code", errorCorrectionLevel = "Q", fallback, foreground = "#18181b", label = "QR code", margin = 2, size = 192, style, value, ...props }, ref) {
|
|
44
65
|
const safeSize = Number.isFinite(size) && size > 0 ? size : 192;
|
|
45
66
|
const safeMargin = Number.isFinite(margin) && margin >= 0 ? Math.floor(margin) : 2;
|
|
46
|
-
const finderBackground = background === "transparent" ? "
|
|
67
|
+
const finderBackground = background === "transparent" ? "var(--jaci-colors-surface-raised)" : background;
|
|
47
68
|
try {
|
|
48
69
|
const code = qrcode.create(value, { errorCorrectionLevel });
|
|
49
70
|
const moduleCount = code.modules.size;
|
|
50
71
|
const viewBoxSize = moduleCount + safeMargin * 2;
|
|
51
72
|
const modules = [];
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
73
|
+
const dotCoordinates = [];
|
|
74
|
+
for (let row = 0; row < moduleCount; row += 1) for (let column = 0; column < moduleCount; column += 1) if (code.modules.get(row, column) && !isFinderCell(row, column, moduleCount)) {
|
|
75
|
+
dotCoordinates.push([row, column]);
|
|
76
|
+
modules.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
77
|
+
cx: safeMargin + column + .5,
|
|
78
|
+
cy: safeMargin + row + .5,
|
|
79
|
+
"data-slot": "qr-code-dot",
|
|
80
|
+
fill: foreground,
|
|
81
|
+
r: "0.43"
|
|
82
|
+
}, `${row}-${column}`));
|
|
83
|
+
}
|
|
84
|
+
const downloadHref = download && `data:image/svg+xml;charset=utf-8,${encodeURIComponent(createDownloadSvg({
|
|
85
|
+
background,
|
|
86
|
+
dots: dotCoordinates,
|
|
87
|
+
foreground,
|
|
88
|
+
finderBackground: background === "transparent" ? "#ffffff" : background,
|
|
89
|
+
label,
|
|
90
|
+
margin: safeMargin,
|
|
91
|
+
moduleCount
|
|
92
|
+
}))}`;
|
|
93
|
+
const downloadFilename = typeof download === "string" ? download : "qrcode.svg";
|
|
94
|
+
const visualCode = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
95
|
+
background !== "transparent" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
96
|
+
fill: background,
|
|
97
|
+
height: viewBoxSize,
|
|
98
|
+
width: viewBoxSize,
|
|
99
|
+
x: "0",
|
|
100
|
+
y: "0"
|
|
101
|
+
}) : null,
|
|
102
|
+
modules,
|
|
103
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Finder, {
|
|
104
|
+
background: finderBackground,
|
|
105
|
+
color: foreground,
|
|
106
|
+
x: safeMargin,
|
|
107
|
+
y: safeMargin
|
|
108
|
+
}),
|
|
109
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Finder, {
|
|
110
|
+
background: finderBackground,
|
|
111
|
+
color: foreground,
|
|
112
|
+
x: safeMargin + moduleCount - 7,
|
|
113
|
+
y: safeMargin
|
|
114
|
+
}),
|
|
115
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Finder, {
|
|
116
|
+
background: finderBackground,
|
|
117
|
+
color: foreground,
|
|
118
|
+
x: safeMargin,
|
|
119
|
+
y: safeMargin + moduleCount - 7
|
|
120
|
+
})
|
|
121
|
+
] });
|
|
122
|
+
const svg = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
60
123
|
...props,
|
|
61
124
|
ref,
|
|
62
|
-
"aria-
|
|
125
|
+
"aria-hidden": downloadHref ? true : void 0,
|
|
126
|
+
"aria-label": downloadHref ? void 0 : label,
|
|
63
127
|
className: require_cx.cx(require_qr_code.qrCode(), className),
|
|
64
128
|
"data-dot-shape": "circle",
|
|
129
|
+
"data-download": download ? "true" : void 0,
|
|
65
130
|
"data-eye-shape": "rounded-square",
|
|
66
131
|
"data-jaci-component": "qr-code",
|
|
67
132
|
"data-slot": "qr-code",
|
|
68
133
|
height: safeSize,
|
|
69
|
-
role: "img",
|
|
134
|
+
role: downloadHref ? void 0 : "img",
|
|
70
135
|
style,
|
|
71
136
|
viewBox: `0 0 ${viewBoxSize} ${viewBoxSize}`,
|
|
72
137
|
width: safeSize,
|
|
73
|
-
children: [
|
|
74
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("title", { children: label }),
|
|
75
|
-
background !== "transparent" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
76
|
-
fill: background,
|
|
77
|
-
height: viewBoxSize,
|
|
78
|
-
width: viewBoxSize,
|
|
79
|
-
x: "0",
|
|
80
|
-
y: "0"
|
|
81
|
-
}) : null,
|
|
82
|
-
modules,
|
|
83
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Finder, {
|
|
84
|
-
background: finderBackground,
|
|
85
|
-
color: foreground,
|
|
86
|
-
x: safeMargin,
|
|
87
|
-
y: safeMargin
|
|
88
|
-
}),
|
|
89
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Finder, {
|
|
90
|
-
background: finderBackground,
|
|
91
|
-
color: foreground,
|
|
92
|
-
x: safeMargin + moduleCount - 7,
|
|
93
|
-
y: safeMargin
|
|
94
|
-
}),
|
|
95
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Finder, {
|
|
96
|
-
background: finderBackground,
|
|
97
|
-
color: foreground,
|
|
98
|
-
x: safeMargin,
|
|
99
|
-
y: safeMargin + moduleCount - 7
|
|
100
|
-
})
|
|
101
|
-
]
|
|
138
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("title", { children: label }), visualCode]
|
|
102
139
|
});
|
|
140
|
+
return downloadHref ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
141
|
+
"aria-label": downloadLabel,
|
|
142
|
+
"data-jaci-component": "qr-code-download",
|
|
143
|
+
"data-slot": "qr-code-download",
|
|
144
|
+
download: downloadFilename,
|
|
145
|
+
href: downloadHref,
|
|
146
|
+
children: svg
|
|
147
|
+
}) : svg;
|
|
103
148
|
} catch {
|
|
104
149
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
105
150
|
"aria-label": label,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qr-code.cjs","names":["QRCodeEncoder","cx","qrCode"],"sources":["../../../src/components/qr-code/qr-code.tsx"],"sourcesContent":["import * as QRCodeEncoder from \"qrcode\";\nimport { forwardRef } from \"react\";\nimport type { ComponentPropsWithoutRef, ReactNode } from \"react\";\n\nimport { cx } from \"../../styled-system/css\";\nimport { qrCode } from \"../../styled-system/recipes\";\n\nexport type QRCodeErrorCorrectionLevel = \"L\" | \"M\" | \"Q\" | \"H\";\n\nexport interface QRCodeProps extends Omit<ComponentPropsWithoutRef<\"svg\">, \"children\"> {\n value: string;\n size?: number;\n margin?: number;\n errorCorrectionLevel?: QRCodeErrorCorrectionLevel;\n foreground?: string;\n background?: string;\n label?: string;\n fallback?: ReactNode;\n}\n\nfunction isFinderCell(row: number, column: number, size: number) {\n return (\n (row < 7 && column < 7) || (row < 7 && column >= size - 7) || (row >= size - 7 && column < 7)\n );\n}\n\nfunction Finder({\n x,\n y,\n color,\n background,\n}: {\n x: number;\n y: number;\n color: string;\n background: string;\n}) {\n return (\n <g data-slot=\"qr-code-eye\">\n <rect fill={color} height=\"7\" rx=\"1\" width=\"7\" x={x} y={y} />\n <rect fill={background} height=\"5\" rx=\"0.75\" width=\"5\" x={x + 1} y={y + 1} />\n <rect fill={color} height=\"3\" rx=\"0.5\" width=\"3\" x={x + 2} y={y + 2} />\n </g>\n );\n}\n\nexport const QRCode = forwardRef<SVGSVGElement, QRCodeProps>(function QRCode(\n {\n background = \"transparent\",\n className,\n errorCorrectionLevel = \"Q\",\n fallback,\n foreground = \"currentColor\",\n label = \"QR code\",\n margin = 2,\n size = 192,\n style,\n value,\n ...props\n },\n ref,\n) {\n const safeSize = Number.isFinite(size) && size > 0 ? size : 192;\n const safeMargin = Number.isFinite(margin) && margin >= 0 ? Math.floor(margin) : 2;\n // Finder patterns need a contrasting quiet area between the frame and the\n // eyeball. Keep the overall SVG transparent by default, but use a white\n // cutout when no background was supplied so the code remains scannable.\n const finderBackground = background === \"transparent\" ? \"white\" : background;\n\n try {\n const code = QRCodeEncoder.create(value, { errorCorrectionLevel });\n const moduleCount = code.modules.size;\n const viewBoxSize = moduleCount + safeMargin * 2;\n const modules: ReactNode[] = [];\n\n for (let row = 0; row < moduleCount; row += 1) {\n for (let column = 0; column < moduleCount; column += 1) {\n if (code.modules.get(row, column) && !isFinderCell(row, column, moduleCount)) {\n modules.push(\n <circle\n cx={safeMargin + column + 0.5}\n cy={safeMargin + row + 0.5}\n data-slot=\"qr-code-dot\"\n fill={foreground}\n key={`${row}-${column}`}\n r=\"0.43\"\n />,\n );\n }\n }\n }\n\n return (\n <svg\n {...props}\n ref={ref}\n aria-label={label}\n className={cx(qrCode(), className)}\n data-dot-shape=\"circle\"\n data-eye-shape=\"rounded-square\"\n data-jaci-component=\"qr-code\"\n data-slot=\"qr-code\"\n height={safeSize}\n role=\"img\"\n style={style}\n viewBox={`0 0 ${viewBoxSize} ${viewBoxSize}`}\n width={safeSize}\n >\n <title>{label}</title>\n {background !== \"transparent\" ? (\n <rect fill={background} height={viewBoxSize} width={viewBoxSize} x=\"0\" y=\"0\" />\n ) : null}\n {modules}\n <Finder background={finderBackground} color={foreground} x={safeMargin} y={safeMargin} />\n <Finder\n background={finderBackground}\n color={foreground}\n x={safeMargin + moduleCount - 7}\n y={safeMargin}\n />\n <Finder\n background={finderBackground}\n color={foreground}\n x={safeMargin}\n y={safeMargin + moduleCount - 7}\n />\n </svg>\n );\n } catch {\n return (\n <span\n aria-label={label}\n className={cx(qrCode(), className)}\n data-jaci-component=\"qr-code\"\n data-slot=\"qr-code\"\n data-status=\"error\"\n role=\"img\"\n >\n {fallback ?? \"Unable to generate QR code\"}\n </span>\n );\n }\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"qr-code.cjs","names":["QRCodeEncoder","cx","qrCode"],"sources":["../../../src/components/qr-code/qr-code.tsx"],"sourcesContent":["import * as QRCodeEncoder from \"qrcode\";\nimport { forwardRef } from \"react\";\nimport type { ComponentPropsWithoutRef, ReactNode } from \"react\";\n\nimport { cx } from \"../../styled-system/css\";\nimport { qrCode } from \"../../styled-system/recipes\";\n\nexport type QRCodeErrorCorrectionLevel = \"L\" | \"M\" | \"Q\" | \"H\";\n\nexport interface QRCodeProps\n extends Omit<ComponentPropsWithoutRef<\"svg\">, \"children\" | \"download\"> {\n value: string;\n size?: number;\n margin?: number;\n errorCorrectionLevel?: QRCodeErrorCorrectionLevel;\n foreground?: string;\n background?: string;\n label?: string;\n fallback?: ReactNode;\n /** Enables a native SVG download link inside the rendered code. */\n download?: boolean | string;\n downloadLabel?: string;\n}\n\nfunction isFinderCell(row: number, column: number, size: number) {\n return (\n (row < 7 && column < 7) || (row < 7 && column >= size - 7) || (row >= size - 7 && column < 7)\n );\n}\n\nfunction Finder({\n x,\n y,\n color,\n background,\n}: {\n x: number;\n y: number;\n color: string;\n background: string;\n}) {\n return (\n <g data-slot=\"qr-code-eye\">\n <rect data-slot=\"qr-code-eye-frame\" fill={color} height=\"7\" rx=\"1\" width=\"7\" x={x} y={y} />\n <rect\n data-slot=\"qr-code-eye-cutout\"\n fill={background}\n height=\"5\"\n rx=\"0.75\"\n width=\"5\"\n x={x + 1}\n y={y + 1}\n />\n <rect\n data-slot=\"qr-code-eyeball\"\n fill={color}\n height=\"3\"\n rx=\"0.5\"\n width=\"3\"\n x={x + 2}\n y={y + 2}\n />\n </g>\n );\n}\n\nfunction escapeXml(value: string) {\n return value.replace(/[&<>'\"]/g, (character) => {\n const entities: Record<string, string> = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n \"'\": \"'\",\n '\"': \""\",\n };\n return entities[character] ?? character;\n });\n}\n\nfunction createDownloadSvg({\n background,\n dots,\n foreground,\n finderBackground,\n label,\n margin,\n moduleCount,\n}: {\n background: string;\n dots: readonly [number, number][];\n foreground: string;\n finderBackground: string;\n label: string;\n margin: number;\n moduleCount: number;\n}) {\n const viewBoxSize = moduleCount + margin * 2;\n const backgroundMarkup =\n background === \"transparent\"\n ? \"\"\n : `<rect fill=\"${escapeXml(background)}\" height=\"${viewBoxSize}\" width=\"${viewBoxSize}\" x=\"0\" y=\"0\"/>`;\n const dotsMarkup = dots\n .map(\n ([row, column]) =>\n `<circle cx=\"${margin + column + 0.5}\" cy=\"${margin + row + 0.5}\" fill=\"${escapeXml(foreground)}\" r=\"0.43\"/>`,\n )\n .join(\"\");\n const finderMarkup = (x: number, y: number) =>\n `<g><rect fill=\"${escapeXml(foreground)}\" height=\"7\" rx=\"1\" width=\"7\" x=\"${x}\" y=\"${y}\"/><rect fill=\"${escapeXml(finderBackground)}\" height=\"5\" rx=\"0.75\" width=\"5\" x=\"${x + 1}\" y=\"${y + 1}\"/><rect fill=\"${escapeXml(foreground)}\" height=\"3\" rx=\"0.5\" width=\"3\" x=\"${x + 2}\" y=\"${y + 2}\"/></g>`;\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" aria-label=\"${escapeXml(label)}\" role=\"img\" viewBox=\"0 0 ${viewBoxSize} ${viewBoxSize}\">${backgroundMarkup}${dotsMarkup}${finderMarkup(margin, margin)}${finderMarkup(margin + moduleCount - 7, margin)}${finderMarkup(margin, margin + moduleCount - 7)}</svg>`;\n}\n\nexport const QRCode = forwardRef<SVGSVGElement, QRCodeProps>(function QRCode(\n {\n // A QR code needs a deterministic high-contrast base. Inheriting\n // `currentColor` or a transparent surface can make finder layers merge\n // in dark scopes, so the defaults intentionally remain black on white.\n background = \"#ffffff\",\n className,\n download = false,\n downloadLabel = \"Download QR code\",\n errorCorrectionLevel = \"Q\",\n fallback,\n foreground = \"#18181b\",\n label = \"QR code\",\n margin = 2,\n size = 192,\n style,\n value,\n ...props\n },\n ref,\n) {\n const safeSize = Number.isFinite(size) && size > 0 ? size : 192;\n const safeMargin = Number.isFinite(margin) && margin >= 0 ? Math.floor(margin) : 2;\n const finderBackground =\n background === \"transparent\" ? \"var(--jaci-colors-surface-raised)\" : background;\n\n try {\n const code = QRCodeEncoder.create(value, { errorCorrectionLevel });\n const moduleCount = code.modules.size;\n const viewBoxSize = moduleCount + safeMargin * 2;\n const modules: ReactNode[] = [];\n const dotCoordinates: [number, number][] = [];\n\n for (let row = 0; row < moduleCount; row += 1) {\n for (let column = 0; column < moduleCount; column += 1) {\n if (code.modules.get(row, column) && !isFinderCell(row, column, moduleCount)) {\n dotCoordinates.push([row, column]);\n modules.push(\n <circle\n cx={safeMargin + column + 0.5}\n cy={safeMargin + row + 0.5}\n data-slot=\"qr-code-dot\"\n fill={foreground}\n key={`${row}-${column}`}\n r=\"0.43\"\n />,\n );\n }\n }\n }\n\n const downloadHref =\n download &&\n `data:image/svg+xml;charset=utf-8,${encodeURIComponent(\n createDownloadSvg({\n background,\n dots: dotCoordinates,\n foreground,\n finderBackground: background === \"transparent\" ? \"#ffffff\" : background,\n label,\n margin: safeMargin,\n moduleCount,\n }),\n )}`;\n const downloadFilename = typeof download === \"string\" ? download : \"qrcode.svg\";\n const visualCode = (\n <>\n {background !== \"transparent\" ? (\n <rect fill={background} height={viewBoxSize} width={viewBoxSize} x=\"0\" y=\"0\" />\n ) : null}\n {modules}\n <Finder background={finderBackground} color={foreground} x={safeMargin} y={safeMargin} />\n <Finder\n background={finderBackground}\n color={foreground}\n x={safeMargin + moduleCount - 7}\n y={safeMargin}\n />\n <Finder\n background={finderBackground}\n color={foreground}\n x={safeMargin}\n y={safeMargin + moduleCount - 7}\n />\n </>\n );\n\n const svg = (\n <svg\n {...props}\n ref={ref}\n aria-hidden={downloadHref ? true : undefined}\n aria-label={downloadHref ? undefined : label}\n className={cx(qrCode(), className)}\n data-dot-shape=\"circle\"\n data-download={download ? \"true\" : undefined}\n data-eye-shape=\"rounded-square\"\n data-jaci-component=\"qr-code\"\n data-slot=\"qr-code\"\n height={safeSize}\n role={downloadHref ? undefined : \"img\"}\n style={style}\n viewBox={`0 0 ${viewBoxSize} ${viewBoxSize}`}\n width={safeSize}\n >\n <title>{label}</title>\n {visualCode}\n </svg>\n );\n\n return downloadHref ? (\n <a\n aria-label={downloadLabel}\n data-jaci-component=\"qr-code-download\"\n data-slot=\"qr-code-download\"\n download={downloadFilename}\n href={downloadHref}\n >\n {svg}\n </a>\n ) : (\n svg\n );\n } catch {\n return (\n <span\n aria-label={label}\n className={cx(qrCode(), className)}\n data-jaci-component=\"qr-code\"\n data-slot=\"qr-code\"\n data-status=\"error\"\n role=\"img\"\n >\n {fallback ?? \"Unable to generate QR code\"}\n </span>\n );\n }\n});\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
2
2
|
//#region src/components/qr-code/qr-code.d.ts
|
|
3
3
|
type QRCodeErrorCorrectionLevel = "L" | "M" | "Q" | "H";
|
|
4
|
-
interface QRCodeProps extends Omit<ComponentPropsWithoutRef<"svg">, "children"> {
|
|
4
|
+
interface QRCodeProps extends Omit<ComponentPropsWithoutRef<"svg">, "children" | "download"> {
|
|
5
5
|
value: string;
|
|
6
6
|
size?: number;
|
|
7
7
|
margin?: number;
|
|
@@ -10,6 +10,9 @@ interface QRCodeProps extends Omit<ComponentPropsWithoutRef<"svg">, "children">
|
|
|
10
10
|
background?: string;
|
|
11
11
|
label?: string;
|
|
12
12
|
fallback?: ReactNode;
|
|
13
|
+
/** Enables a native SVG download link inside the rendered code. */
|
|
14
|
+
download?: boolean | string;
|
|
15
|
+
downloadLabel?: string;
|
|
13
16
|
}
|
|
14
17
|
declare const QRCode: import("react").ForwardRefExoticComponent<QRCodeProps & import("react").RefAttributes<SVGSVGElement>>;
|
|
15
18
|
//#endregion
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
2
2
|
//#region src/components/qr-code/qr-code.d.ts
|
|
3
3
|
type QRCodeErrorCorrectionLevel = "L" | "M" | "Q" | "H";
|
|
4
|
-
interface QRCodeProps extends Omit<ComponentPropsWithoutRef<"svg">, "children"> {
|
|
4
|
+
interface QRCodeProps extends Omit<ComponentPropsWithoutRef<"svg">, "children" | "download"> {
|
|
5
5
|
value: string;
|
|
6
6
|
size?: number;
|
|
7
7
|
margin?: number;
|
|
@@ -10,6 +10,9 @@ interface QRCodeProps extends Omit<ComponentPropsWithoutRef<"svg">, "children">
|
|
|
10
10
|
background?: string;
|
|
11
11
|
label?: string;
|
|
12
12
|
fallback?: ReactNode;
|
|
13
|
+
/** Enables a native SVG download link inside the rendered code. */
|
|
14
|
+
download?: boolean | string;
|
|
15
|
+
downloadLabel?: string;
|
|
13
16
|
}
|
|
14
17
|
declare const QRCode: import("react").ForwardRefExoticComponent<QRCodeProps & import("react").RefAttributes<SVGSVGElement>>;
|
|
15
18
|
//#endregion
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cx } from "../../styled-system/css/cx.js";
|
|
2
2
|
import { qrCode } from "../../styled-system/recipes/qr-code.js";
|
|
3
3
|
import { forwardRef } from "react";
|
|
4
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import * as QRCodeEncoder from "qrcode";
|
|
6
6
|
//#region src/components/qr-code/qr-code.tsx
|
|
7
7
|
function isFinderCell(row, column, size) {
|
|
@@ -12,6 +12,7 @@ function Finder({ x, y, color, background }) {
|
|
|
12
12
|
"data-slot": "qr-code-eye",
|
|
13
13
|
children: [
|
|
14
14
|
/* @__PURE__ */ jsx("rect", {
|
|
15
|
+
"data-slot": "qr-code-eye-frame",
|
|
15
16
|
fill: color,
|
|
16
17
|
height: "7",
|
|
17
18
|
rx: "1",
|
|
@@ -20,6 +21,7 @@ function Finder({ x, y, color, background }) {
|
|
|
20
21
|
y
|
|
21
22
|
}),
|
|
22
23
|
/* @__PURE__ */ jsx("rect", {
|
|
24
|
+
"data-slot": "qr-code-eye-cutout",
|
|
23
25
|
fill: background,
|
|
24
26
|
height: "5",
|
|
25
27
|
rx: "0.75",
|
|
@@ -28,6 +30,7 @@ function Finder({ x, y, color, background }) {
|
|
|
28
30
|
y: y + 1
|
|
29
31
|
}),
|
|
30
32
|
/* @__PURE__ */ jsx("rect", {
|
|
33
|
+
"data-slot": "qr-code-eyeball",
|
|
31
34
|
fill: color,
|
|
32
35
|
height: "3",
|
|
33
36
|
rx: "0.5",
|
|
@@ -38,66 +41,108 @@ function Finder({ x, y, color, background }) {
|
|
|
38
41
|
]
|
|
39
42
|
});
|
|
40
43
|
}
|
|
41
|
-
|
|
44
|
+
function escapeXml(value) {
|
|
45
|
+
return value.replace(/[&<>'"]/g, (character) => {
|
|
46
|
+
return {
|
|
47
|
+
"&": "&",
|
|
48
|
+
"<": "<",
|
|
49
|
+
">": ">",
|
|
50
|
+
"'": "'",
|
|
51
|
+
"\"": """
|
|
52
|
+
}[character] ?? character;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function createDownloadSvg({ background, dots, foreground, finderBackground, label, margin, moduleCount }) {
|
|
56
|
+
const viewBoxSize = moduleCount + margin * 2;
|
|
57
|
+
const backgroundMarkup = background === "transparent" ? "" : `<rect fill="${escapeXml(background)}" height="${viewBoxSize}" width="${viewBoxSize}" x="0" y="0"/>`;
|
|
58
|
+
const dotsMarkup = dots.map(([row, column]) => `<circle cx="${margin + column + .5}" cy="${margin + row + .5}" fill="${escapeXml(foreground)}" r="0.43"/>`).join("");
|
|
59
|
+
const finderMarkup = (x, y) => `<g><rect fill="${escapeXml(foreground)}" height="7" rx="1" width="7" x="${x}" y="${y}"/><rect fill="${escapeXml(finderBackground)}" height="5" rx="0.75" width="5" x="${x + 1}" y="${y + 1}"/><rect fill="${escapeXml(foreground)}" height="3" rx="0.5" width="3" x="${x + 2}" y="${y + 2}"/></g>`;
|
|
60
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" aria-label="${escapeXml(label)}" role="img" viewBox="0 0 ${viewBoxSize} ${viewBoxSize}">${backgroundMarkup}${dotsMarkup}${finderMarkup(margin, margin)}${finderMarkup(margin + moduleCount - 7, margin)}${finderMarkup(margin, margin + moduleCount - 7)}</svg>`;
|
|
61
|
+
}
|
|
62
|
+
const QRCode = forwardRef(function QRCode({ background = "#ffffff", className, download = false, downloadLabel = "Download QR code", errorCorrectionLevel = "Q", fallback, foreground = "#18181b", label = "QR code", margin = 2, size = 192, style, value, ...props }, ref) {
|
|
42
63
|
const safeSize = Number.isFinite(size) && size > 0 ? size : 192;
|
|
43
64
|
const safeMargin = Number.isFinite(margin) && margin >= 0 ? Math.floor(margin) : 2;
|
|
44
|
-
const finderBackground = background === "transparent" ? "
|
|
65
|
+
const finderBackground = background === "transparent" ? "var(--jaci-colors-surface-raised)" : background;
|
|
45
66
|
try {
|
|
46
67
|
const code = QRCodeEncoder.create(value, { errorCorrectionLevel });
|
|
47
68
|
const moduleCount = code.modules.size;
|
|
48
69
|
const viewBoxSize = moduleCount + safeMargin * 2;
|
|
49
70
|
const modules = [];
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
71
|
+
const dotCoordinates = [];
|
|
72
|
+
for (let row = 0; row < moduleCount; row += 1) for (let column = 0; column < moduleCount; column += 1) if (code.modules.get(row, column) && !isFinderCell(row, column, moduleCount)) {
|
|
73
|
+
dotCoordinates.push([row, column]);
|
|
74
|
+
modules.push(/* @__PURE__ */ jsx("circle", {
|
|
75
|
+
cx: safeMargin + column + .5,
|
|
76
|
+
cy: safeMargin + row + .5,
|
|
77
|
+
"data-slot": "qr-code-dot",
|
|
78
|
+
fill: foreground,
|
|
79
|
+
r: "0.43"
|
|
80
|
+
}, `${row}-${column}`));
|
|
81
|
+
}
|
|
82
|
+
const downloadHref = download && `data:image/svg+xml;charset=utf-8,${encodeURIComponent(createDownloadSvg({
|
|
83
|
+
background,
|
|
84
|
+
dots: dotCoordinates,
|
|
85
|
+
foreground,
|
|
86
|
+
finderBackground: background === "transparent" ? "#ffffff" : background,
|
|
87
|
+
label,
|
|
88
|
+
margin: safeMargin,
|
|
89
|
+
moduleCount
|
|
90
|
+
}))}`;
|
|
91
|
+
const downloadFilename = typeof download === "string" ? download : "qrcode.svg";
|
|
92
|
+
const visualCode = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
93
|
+
background !== "transparent" ? /* @__PURE__ */ jsx("rect", {
|
|
94
|
+
fill: background,
|
|
95
|
+
height: viewBoxSize,
|
|
96
|
+
width: viewBoxSize,
|
|
97
|
+
x: "0",
|
|
98
|
+
y: "0"
|
|
99
|
+
}) : null,
|
|
100
|
+
modules,
|
|
101
|
+
/* @__PURE__ */ jsx(Finder, {
|
|
102
|
+
background: finderBackground,
|
|
103
|
+
color: foreground,
|
|
104
|
+
x: safeMargin,
|
|
105
|
+
y: safeMargin
|
|
106
|
+
}),
|
|
107
|
+
/* @__PURE__ */ jsx(Finder, {
|
|
108
|
+
background: finderBackground,
|
|
109
|
+
color: foreground,
|
|
110
|
+
x: safeMargin + moduleCount - 7,
|
|
111
|
+
y: safeMargin
|
|
112
|
+
}),
|
|
113
|
+
/* @__PURE__ */ jsx(Finder, {
|
|
114
|
+
background: finderBackground,
|
|
115
|
+
color: foreground,
|
|
116
|
+
x: safeMargin,
|
|
117
|
+
y: safeMargin + moduleCount - 7
|
|
118
|
+
})
|
|
119
|
+
] });
|
|
120
|
+
const svg = /* @__PURE__ */ jsxs("svg", {
|
|
58
121
|
...props,
|
|
59
122
|
ref,
|
|
60
|
-
"aria-
|
|
123
|
+
"aria-hidden": downloadHref ? true : void 0,
|
|
124
|
+
"aria-label": downloadHref ? void 0 : label,
|
|
61
125
|
className: cx(qrCode(), className),
|
|
62
126
|
"data-dot-shape": "circle",
|
|
127
|
+
"data-download": download ? "true" : void 0,
|
|
63
128
|
"data-eye-shape": "rounded-square",
|
|
64
129
|
"data-jaci-component": "qr-code",
|
|
65
130
|
"data-slot": "qr-code",
|
|
66
131
|
height: safeSize,
|
|
67
|
-
role: "img",
|
|
132
|
+
role: downloadHref ? void 0 : "img",
|
|
68
133
|
style,
|
|
69
134
|
viewBox: `0 0 ${viewBoxSize} ${viewBoxSize}`,
|
|
70
135
|
width: safeSize,
|
|
71
|
-
children: [
|
|
72
|
-
/* @__PURE__ */ jsx("title", { children: label }),
|
|
73
|
-
background !== "transparent" ? /* @__PURE__ */ jsx("rect", {
|
|
74
|
-
fill: background,
|
|
75
|
-
height: viewBoxSize,
|
|
76
|
-
width: viewBoxSize,
|
|
77
|
-
x: "0",
|
|
78
|
-
y: "0"
|
|
79
|
-
}) : null,
|
|
80
|
-
modules,
|
|
81
|
-
/* @__PURE__ */ jsx(Finder, {
|
|
82
|
-
background: finderBackground,
|
|
83
|
-
color: foreground,
|
|
84
|
-
x: safeMargin,
|
|
85
|
-
y: safeMargin
|
|
86
|
-
}),
|
|
87
|
-
/* @__PURE__ */ jsx(Finder, {
|
|
88
|
-
background: finderBackground,
|
|
89
|
-
color: foreground,
|
|
90
|
-
x: safeMargin + moduleCount - 7,
|
|
91
|
-
y: safeMargin
|
|
92
|
-
}),
|
|
93
|
-
/* @__PURE__ */ jsx(Finder, {
|
|
94
|
-
background: finderBackground,
|
|
95
|
-
color: foreground,
|
|
96
|
-
x: safeMargin,
|
|
97
|
-
y: safeMargin + moduleCount - 7
|
|
98
|
-
})
|
|
99
|
-
]
|
|
136
|
+
children: [/* @__PURE__ */ jsx("title", { children: label }), visualCode]
|
|
100
137
|
});
|
|
138
|
+
return downloadHref ? /* @__PURE__ */ jsx("a", {
|
|
139
|
+
"aria-label": downloadLabel,
|
|
140
|
+
"data-jaci-component": "qr-code-download",
|
|
141
|
+
"data-slot": "qr-code-download",
|
|
142
|
+
download: downloadFilename,
|
|
143
|
+
href: downloadHref,
|
|
144
|
+
children: svg
|
|
145
|
+
}) : svg;
|
|
101
146
|
} catch {
|
|
102
147
|
return /* @__PURE__ */ jsx("span", {
|
|
103
148
|
"aria-label": label,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qr-code.js","names":[],"sources":["../../../src/components/qr-code/qr-code.tsx"],"sourcesContent":["import * as QRCodeEncoder from \"qrcode\";\nimport { forwardRef } from \"react\";\nimport type { ComponentPropsWithoutRef, ReactNode } from \"react\";\n\nimport { cx } from \"../../styled-system/css\";\nimport { qrCode } from \"../../styled-system/recipes\";\n\nexport type QRCodeErrorCorrectionLevel = \"L\" | \"M\" | \"Q\" | \"H\";\n\nexport interface QRCodeProps extends Omit<ComponentPropsWithoutRef<\"svg\">, \"children\"> {\n value: string;\n size?: number;\n margin?: number;\n errorCorrectionLevel?: QRCodeErrorCorrectionLevel;\n foreground?: string;\n background?: string;\n label?: string;\n fallback?: ReactNode;\n}\n\nfunction isFinderCell(row: number, column: number, size: number) {\n return (\n (row < 7 && column < 7) || (row < 7 && column >= size - 7) || (row >= size - 7 && column < 7)\n );\n}\n\nfunction Finder({\n x,\n y,\n color,\n background,\n}: {\n x: number;\n y: number;\n color: string;\n background: string;\n}) {\n return (\n <g data-slot=\"qr-code-eye\">\n <rect fill={color} height=\"7\" rx=\"1\" width=\"7\" x={x} y={y} />\n <rect fill={background} height=\"5\" rx=\"0.75\" width=\"5\" x={x + 1} y={y + 1} />\n <rect fill={color} height=\"3\" rx=\"0.5\" width=\"3\" x={x + 2} y={y + 2} />\n </g>\n );\n}\n\nexport const QRCode = forwardRef<SVGSVGElement, QRCodeProps>(function QRCode(\n {\n background = \"transparent\",\n className,\n errorCorrectionLevel = \"Q\",\n fallback,\n foreground = \"currentColor\",\n label = \"QR code\",\n margin = 2,\n size = 192,\n style,\n value,\n ...props\n },\n ref,\n) {\n const safeSize = Number.isFinite(size) && size > 0 ? size : 192;\n const safeMargin = Number.isFinite(margin) && margin >= 0 ? Math.floor(margin) : 2;\n // Finder patterns need a contrasting quiet area between the frame and the\n // eyeball. Keep the overall SVG transparent by default, but use a white\n // cutout when no background was supplied so the code remains scannable.\n const finderBackground = background === \"transparent\" ? \"white\" : background;\n\n try {\n const code = QRCodeEncoder.create(value, { errorCorrectionLevel });\n const moduleCount = code.modules.size;\n const viewBoxSize = moduleCount + safeMargin * 2;\n const modules: ReactNode[] = [];\n\n for (let row = 0; row < moduleCount; row += 1) {\n for (let column = 0; column < moduleCount; column += 1) {\n if (code.modules.get(row, column) && !isFinderCell(row, column, moduleCount)) {\n modules.push(\n <circle\n cx={safeMargin + column + 0.5}\n cy={safeMargin + row + 0.5}\n data-slot=\"qr-code-dot\"\n fill={foreground}\n key={`${row}-${column}`}\n r=\"0.43\"\n />,\n );\n }\n }\n }\n\n return (\n <svg\n {...props}\n ref={ref}\n aria-label={label}\n className={cx(qrCode(), className)}\n data-dot-shape=\"circle\"\n data-eye-shape=\"rounded-square\"\n data-jaci-component=\"qr-code\"\n data-slot=\"qr-code\"\n height={safeSize}\n role=\"img\"\n style={style}\n viewBox={`0 0 ${viewBoxSize} ${viewBoxSize}`}\n width={safeSize}\n >\n <title>{label}</title>\n {background !== \"transparent\" ? (\n <rect fill={background} height={viewBoxSize} width={viewBoxSize} x=\"0\" y=\"0\" />\n ) : null}\n {modules}\n <Finder background={finderBackground} color={foreground} x={safeMargin} y={safeMargin} />\n <Finder\n background={finderBackground}\n color={foreground}\n x={safeMargin + moduleCount - 7}\n y={safeMargin}\n />\n <Finder\n background={finderBackground}\n color={foreground}\n x={safeMargin}\n y={safeMargin + moduleCount - 7}\n />\n </svg>\n );\n } catch {\n return (\n <span\n aria-label={label}\n className={cx(qrCode(), className)}\n data-jaci-component=\"qr-code\"\n data-slot=\"qr-code\"\n data-status=\"error\"\n role=\"img\"\n >\n {fallback ?? \"Unable to generate QR code\"}\n </span>\n );\n }\n});\n"],"mappings":";;;;;;AAoBA,SAAS,aAAa,KAAa,QAAgB,MAAc;CAC/D,OACG,MAAM,KAAK,SAAS,KAAO,MAAM,KAAK,UAAU,OAAO,KAAO,OAAO,OAAO,KAAK,SAAS;AAE/F;AAEA,SAAS,OAAO,EACd,GACA,GACA,OACA,cAMC;CACD,OACE,qBAAC,KAAD;EAAG,aAAU;YAAb;GACE,oBAAC,QAAD;IAAM,MAAM;IAAO,QAAO;IAAI,IAAG;IAAI,OAAM;IAAO;IAAM;GAAI,CAAA;GAC5D,oBAAC,QAAD;IAAM,MAAM;IAAY,QAAO;IAAI,IAAG;IAAO,OAAM;IAAI,GAAG,IAAI;IAAG,GAAG,IAAI;GAAI,CAAA;GAC5E,oBAAC,QAAD;IAAM,MAAM;IAAO,QAAO;IAAI,IAAG;IAAM,OAAM;IAAI,GAAG,IAAI;IAAG,GAAG,IAAI;GAAI,CAAA;EACrE;;AAEP;AAEA,MAAa,SAAS,WAAuC,SAAS,OACpE,EACE,aAAa,eACb,WACA,uBAAuB,KACvB,UACA,aAAa,gBACb,QAAQ,WACR,SAAS,GACT,OAAO,KACP,OACA,OACA,GAAG,SAEL,KACA;CACA,MAAM,WAAW,OAAO,SAAS,IAAI,KAAK,OAAO,IAAI,OAAO;CAC5D,MAAM,aAAa,OAAO,SAAS,MAAM,KAAK,UAAU,IAAI,KAAK,MAAM,MAAM,IAAI;CAIjF,MAAM,mBAAmB,eAAe,gBAAgB,UAAU;CAElE,IAAI;EACF,MAAM,OAAO,cAAc,OAAO,OAAO,EAAE,qBAAqB,CAAC;EACjE,MAAM,cAAc,KAAK,QAAQ;EACjC,MAAM,cAAc,cAAc,aAAa;EAC/C,MAAM,UAAuB,CAAC;EAE9B,KAAK,IAAI,MAAM,GAAG,MAAM,aAAa,OAAO,GAC1C,KAAK,IAAI,SAAS,GAAG,SAAS,aAAa,UAAU,GACnD,IAAI,KAAK,QAAQ,IAAI,KAAK,MAAM,KAAK,CAAC,aAAa,KAAK,QAAQ,WAAW,GACzE,QAAQ,KACN,oBAAC,UAAD;GACE,IAAI,aAAa,SAAS;GAC1B,IAAI,aAAa,MAAM;GACvB,aAAU;GACV,MAAM;GAEN,GAAE;EACH,GAFM,GAAG,IAAI,GAAG,QAEhB,CACH;EAKN,OACE,qBAAC,OAAD;GACE,GAAI;GACC;GACL,cAAY;GACZ,WAAW,GAAG,OAAO,GAAG,SAAS;GACjC,kBAAe;GACf,kBAAe;GACf,uBAAoB;GACpB,aAAU;GACV,QAAQ;GACR,MAAK;GACE;GACP,SAAS,OAAO,YAAY,GAAG;GAC/B,OAAO;aAbT;IAeE,oBAAC,SAAD,EAAA,UAAQ,MAAa,CAAA;IACpB,eAAe,gBACd,oBAAC,QAAD;KAAM,MAAM;KAAY,QAAQ;KAAa,OAAO;KAAa,GAAE;KAAI,GAAE;IAAK,CAAA,IAC5E;IACH;IACD,oBAAC,QAAD;KAAQ,YAAY;KAAkB,OAAO;KAAY,GAAG;KAAY,GAAG;IAAa,CAAA;IACxF,oBAAC,QAAD;KACE,YAAY;KACZ,OAAO;KACP,GAAG,aAAa,cAAc;KAC9B,GAAG;IACJ,CAAA;IACD,oBAAC,QAAD;KACE,YAAY;KACZ,OAAO;KACP,GAAG;KACH,GAAG,aAAa,cAAc;IAC/B,CAAA;GACE;;CAET,QAAQ;EACN,OACE,oBAAC,QAAD;GACE,cAAY;GACZ,WAAW,GAAG,OAAO,GAAG,SAAS;GACjC,uBAAoB;GACpB,aAAU;GACV,eAAY;GACZ,MAAK;aAEJ,YAAY;EACT,CAAA;CAEV;AACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"qr-code.js","names":[],"sources":["../../../src/components/qr-code/qr-code.tsx"],"sourcesContent":["import * as QRCodeEncoder from \"qrcode\";\nimport { forwardRef } from \"react\";\nimport type { ComponentPropsWithoutRef, ReactNode } from \"react\";\n\nimport { cx } from \"../../styled-system/css\";\nimport { qrCode } from \"../../styled-system/recipes\";\n\nexport type QRCodeErrorCorrectionLevel = \"L\" | \"M\" | \"Q\" | \"H\";\n\nexport interface QRCodeProps\n extends Omit<ComponentPropsWithoutRef<\"svg\">, \"children\" | \"download\"> {\n value: string;\n size?: number;\n margin?: number;\n errorCorrectionLevel?: QRCodeErrorCorrectionLevel;\n foreground?: string;\n background?: string;\n label?: string;\n fallback?: ReactNode;\n /** Enables a native SVG download link inside the rendered code. */\n download?: boolean | string;\n downloadLabel?: string;\n}\n\nfunction isFinderCell(row: number, column: number, size: number) {\n return (\n (row < 7 && column < 7) || (row < 7 && column >= size - 7) || (row >= size - 7 && column < 7)\n );\n}\n\nfunction Finder({\n x,\n y,\n color,\n background,\n}: {\n x: number;\n y: number;\n color: string;\n background: string;\n}) {\n return (\n <g data-slot=\"qr-code-eye\">\n <rect data-slot=\"qr-code-eye-frame\" fill={color} height=\"7\" rx=\"1\" width=\"7\" x={x} y={y} />\n <rect\n data-slot=\"qr-code-eye-cutout\"\n fill={background}\n height=\"5\"\n rx=\"0.75\"\n width=\"5\"\n x={x + 1}\n y={y + 1}\n />\n <rect\n data-slot=\"qr-code-eyeball\"\n fill={color}\n height=\"3\"\n rx=\"0.5\"\n width=\"3\"\n x={x + 2}\n y={y + 2}\n />\n </g>\n );\n}\n\nfunction escapeXml(value: string) {\n return value.replace(/[&<>'\"]/g, (character) => {\n const entities: Record<string, string> = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n \"'\": \"'\",\n '\"': \""\",\n };\n return entities[character] ?? character;\n });\n}\n\nfunction createDownloadSvg({\n background,\n dots,\n foreground,\n finderBackground,\n label,\n margin,\n moduleCount,\n}: {\n background: string;\n dots: readonly [number, number][];\n foreground: string;\n finderBackground: string;\n label: string;\n margin: number;\n moduleCount: number;\n}) {\n const viewBoxSize = moduleCount + margin * 2;\n const backgroundMarkup =\n background === \"transparent\"\n ? \"\"\n : `<rect fill=\"${escapeXml(background)}\" height=\"${viewBoxSize}\" width=\"${viewBoxSize}\" x=\"0\" y=\"0\"/>`;\n const dotsMarkup = dots\n .map(\n ([row, column]) =>\n `<circle cx=\"${margin + column + 0.5}\" cy=\"${margin + row + 0.5}\" fill=\"${escapeXml(foreground)}\" r=\"0.43\"/>`,\n )\n .join(\"\");\n const finderMarkup = (x: number, y: number) =>\n `<g><rect fill=\"${escapeXml(foreground)}\" height=\"7\" rx=\"1\" width=\"7\" x=\"${x}\" y=\"${y}\"/><rect fill=\"${escapeXml(finderBackground)}\" height=\"5\" rx=\"0.75\" width=\"5\" x=\"${x + 1}\" y=\"${y + 1}\"/><rect fill=\"${escapeXml(foreground)}\" height=\"3\" rx=\"0.5\" width=\"3\" x=\"${x + 2}\" y=\"${y + 2}\"/></g>`;\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" aria-label=\"${escapeXml(label)}\" role=\"img\" viewBox=\"0 0 ${viewBoxSize} ${viewBoxSize}\">${backgroundMarkup}${dotsMarkup}${finderMarkup(margin, margin)}${finderMarkup(margin + moduleCount - 7, margin)}${finderMarkup(margin, margin + moduleCount - 7)}</svg>`;\n}\n\nexport const QRCode = forwardRef<SVGSVGElement, QRCodeProps>(function QRCode(\n {\n // A QR code needs a deterministic high-contrast base. Inheriting\n // `currentColor` or a transparent surface can make finder layers merge\n // in dark scopes, so the defaults intentionally remain black on white.\n background = \"#ffffff\",\n className,\n download = false,\n downloadLabel = \"Download QR code\",\n errorCorrectionLevel = \"Q\",\n fallback,\n foreground = \"#18181b\",\n label = \"QR code\",\n margin = 2,\n size = 192,\n style,\n value,\n ...props\n },\n ref,\n) {\n const safeSize = Number.isFinite(size) && size > 0 ? size : 192;\n const safeMargin = Number.isFinite(margin) && margin >= 0 ? Math.floor(margin) : 2;\n const finderBackground =\n background === \"transparent\" ? \"var(--jaci-colors-surface-raised)\" : background;\n\n try {\n const code = QRCodeEncoder.create(value, { errorCorrectionLevel });\n const moduleCount = code.modules.size;\n const viewBoxSize = moduleCount + safeMargin * 2;\n const modules: ReactNode[] = [];\n const dotCoordinates: [number, number][] = [];\n\n for (let row = 0; row < moduleCount; row += 1) {\n for (let column = 0; column < moduleCount; column += 1) {\n if (code.modules.get(row, column) && !isFinderCell(row, column, moduleCount)) {\n dotCoordinates.push([row, column]);\n modules.push(\n <circle\n cx={safeMargin + column + 0.5}\n cy={safeMargin + row + 0.5}\n data-slot=\"qr-code-dot\"\n fill={foreground}\n key={`${row}-${column}`}\n r=\"0.43\"\n />,\n );\n }\n }\n }\n\n const downloadHref =\n download &&\n `data:image/svg+xml;charset=utf-8,${encodeURIComponent(\n createDownloadSvg({\n background,\n dots: dotCoordinates,\n foreground,\n finderBackground: background === \"transparent\" ? \"#ffffff\" : background,\n label,\n margin: safeMargin,\n moduleCount,\n }),\n )}`;\n const downloadFilename = typeof download === \"string\" ? download : \"qrcode.svg\";\n const visualCode = (\n <>\n {background !== \"transparent\" ? (\n <rect fill={background} height={viewBoxSize} width={viewBoxSize} x=\"0\" y=\"0\" />\n ) : null}\n {modules}\n <Finder background={finderBackground} color={foreground} x={safeMargin} y={safeMargin} />\n <Finder\n background={finderBackground}\n color={foreground}\n x={safeMargin + moduleCount - 7}\n y={safeMargin}\n />\n <Finder\n background={finderBackground}\n color={foreground}\n x={safeMargin}\n y={safeMargin + moduleCount - 7}\n />\n </>\n );\n\n const svg = (\n <svg\n {...props}\n ref={ref}\n aria-hidden={downloadHref ? true : undefined}\n aria-label={downloadHref ? undefined : label}\n className={cx(qrCode(), className)}\n data-dot-shape=\"circle\"\n data-download={download ? \"true\" : undefined}\n data-eye-shape=\"rounded-square\"\n data-jaci-component=\"qr-code\"\n data-slot=\"qr-code\"\n height={safeSize}\n role={downloadHref ? undefined : \"img\"}\n style={style}\n viewBox={`0 0 ${viewBoxSize} ${viewBoxSize}`}\n width={safeSize}\n >\n <title>{label}</title>\n {visualCode}\n </svg>\n );\n\n return downloadHref ? (\n <a\n aria-label={downloadLabel}\n data-jaci-component=\"qr-code-download\"\n data-slot=\"qr-code-download\"\n download={downloadFilename}\n href={downloadHref}\n >\n {svg}\n </a>\n ) : (\n svg\n );\n } catch {\n return (\n <span\n aria-label={label}\n className={cx(qrCode(), className)}\n data-jaci-component=\"qr-code\"\n data-slot=\"qr-code\"\n data-status=\"error\"\n role=\"img\"\n >\n {fallback ?? \"Unable to generate QR code\"}\n </span>\n );\n }\n});\n"],"mappings":"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"}
|
package/dist/styles.css
CHANGED
|
@@ -2053,6 +2053,8 @@
|
|
|
2053
2053
|
overflow: hidden;
|
|
2054
2054
|
background-color: var(--jaci-colors-surface-raised);
|
|
2055
2055
|
color: var(--jaci-colors-fg-default);
|
|
2056
|
+
min-width: var(--jaci-sizes-0);
|
|
2057
|
+
width: 100%;
|
|
2056
2058
|
}
|
|
2057
2059
|
|
|
2058
2060
|
.jaci-accordion__item {
|
|
@@ -10872,12 +10874,12 @@
|
|
|
10872
10874
|
|
|
10873
10875
|
@layer utilities{
|
|
10874
10876
|
|
|
10875
|
-
.jaci-
|
|
10876
|
-
background: var(--jaci-colors-
|
|
10877
|
+
.jaci-bg_var\(--jaci-colors-surface-raised\) {
|
|
10878
|
+
background: var(--jaci-colors-surface-raised);
|
|
10877
10879
|
}
|
|
10878
10880
|
|
|
10879
|
-
.jaci-
|
|
10880
|
-
background:
|
|
10881
|
+
.jaci-bg_\#ffffff {
|
|
10882
|
+
background: #ffffff;
|
|
10881
10883
|
}
|
|
10882
10884
|
|
|
10883
10885
|
.jaci-gap_8px {
|
|
@@ -10896,8 +10898,8 @@
|
|
|
10896
10898
|
page: 1px;
|
|
10897
10899
|
}
|
|
10898
10900
|
|
|
10899
|
-
.jaci-
|
|
10900
|
-
color:
|
|
10901
|
+
.jaci-c_\#18181b {
|
|
10902
|
+
color: #18181b;
|
|
10901
10903
|
}
|
|
10902
10904
|
|
|
10903
10905
|
.jaci-translate-x_2 {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jaci-ui",
|
|
3
3
|
"description": "Accessible, themeable React components by Jaci UI",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.3",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Richard Borges",
|
|
@@ -86,13 +86,13 @@
|
|
|
86
86
|
"access": "public"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
|
-
"@pandacss/dev": "^1.11.
|
|
89
|
+
"@pandacss/dev": "^1.11.5",
|
|
90
90
|
"@types/qrcode": "^1.5.6",
|
|
91
|
-
"@types/react": "^19.2.
|
|
92
|
-
"@types/react-dom": "^19.2.
|
|
91
|
+
"@types/react": "^19.2.18",
|
|
92
|
+
"@types/react-dom": "^19.2.4",
|
|
93
93
|
"jsdom": "^27.4.0",
|
|
94
|
-
"react": "^19.2.
|
|
95
|
-
"react-dom": "^19.2.
|
|
94
|
+
"react": "^19.2.8",
|
|
95
|
+
"react-dom": "^19.2.8",
|
|
96
96
|
"tsdown": "^0.22.4"
|
|
97
97
|
},
|
|
98
98
|
"scripts": {
|