tempest-react-sdk 0.81.0 → 0.82.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/README.md +5 -6
- package/bin/create-tempest-app.e2e.test.mjs +31 -0
- package/bin/create-tempest-app.mjs +11 -1
- package/bin/lib/doctor/doctor.e2e.test.mjs +29 -0
- package/bin/lib/doctor/vitest.mjs +120 -0
- package/bin/lib/doctor/vitest.test.mjs +139 -0
- package/bin/lib/fs/contain.mjs +7 -0
- package/bin/lib/openapi/escape.mjs +68 -4
- package/bin/lib/openapi/generate.hostile.test.mjs +137 -0
- package/bin/lib/openapi/generate.mjs +78 -13
- package/bin/lib/openapi/pattern-risk.mjs +134 -0
- package/bin/lib/openapi/pattern-risk.test.mjs +45 -0
- package/bin/tempest-gen.test.mjs +100 -0
- package/bin/tempest.mjs +24 -8
- package/dist/access/use-can.cjs +1 -1
- package/dist/access/use-can.cjs.map +1 -1
- package/dist/access/use-can.js +25 -28
- package/dist/access/use-can.js.map +1 -1
- package/dist/auth/create-tempest-auth.cjs +1 -1
- package/dist/auth/create-tempest-auth.cjs.map +1 -1
- package/dist/auth/create-tempest-auth.js +192 -149
- package/dist/auth/create-tempest-auth.js.map +1 -1
- package/dist/auth/session-identity.cjs +2 -0
- package/dist/auth/session-identity.cjs.map +1 -0
- package/dist/auth/session-identity.js +12 -0
- package/dist/auth/session-identity.js.map +1 -0
- package/dist/br/boleto.cjs.map +1 -1
- package/dist/br/boleto.js.map +1 -1
- package/dist/components/AuthForm/AuthLinks.cjs +1 -1
- package/dist/components/AuthForm/AuthLinks.cjs.map +1 -1
- package/dist/components/AuthForm/AuthLinks.js +18 -15
- package/dist/components/AuthForm/AuthLinks.js.map +1 -1
- package/dist/components/AuthPage/AuthPage.cjs +1 -1
- package/dist/components/AuthPage/AuthPage.cjs.map +1 -1
- package/dist/components/AuthPage/AuthPage.js +58 -52
- package/dist/components/AuthPage/AuthPage.js.map +1 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.cjs +1 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.cjs.map +1 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.js +22 -21
- package/dist/components/Breadcrumbs/Breadcrumbs.js.map +1 -1
- package/dist/components/Button/Button.cjs +1 -1
- package/dist/components/Button/Button.cjs.map +1 -1
- package/dist/components/Button/Button.js +41 -40
- package/dist/components/Button/Button.js.map +1 -1
- package/dist/components/Chat/ChatAttachments.cjs +1 -1
- package/dist/components/Chat/ChatAttachments.cjs.map +1 -1
- package/dist/components/Chat/ChatAttachments.js +38 -38
- package/dist/components/Form/Form.cjs +1 -1
- package/dist/components/Form/Form.cjs.map +1 -1
- package/dist/components/Form/Form.js +30 -32
- package/dist/components/Form/Form.js.map +1 -1
- package/dist/components/Layout/Layout.cjs +1 -1
- package/dist/components/Layout/Layout.cjs.map +1 -1
- package/dist/components/Layout/Layout.js +25 -24
- package/dist/components/Layout/Layout.js.map +1 -1
- package/dist/components/Layout/gap.cjs +2 -0
- package/dist/components/Layout/gap.cjs.map +1 -0
- package/dist/components/Layout/gap.js +15 -0
- package/dist/components/Layout/gap.js.map +1 -0
- package/dist/components/NavigationMenu/NavigationMenu.cjs +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.cjs.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.js +71 -70
- package/dist/components/NavigationMenu/NavigationMenu.js.map +1 -1
- package/dist/components/Sidebar/Sidebar.cjs.map +1 -1
- package/dist/components/Sidebar/Sidebar.js.map +1 -1
- package/dist/components/Sidebar/SidebarEntry.cjs +1 -1
- package/dist/components/Sidebar/SidebarEntry.cjs.map +1 -1
- package/dist/components/Sidebar/SidebarEntry.js +30 -29
- package/dist/components/Sidebar/SidebarEntry.js.map +1 -1
- package/dist/data/create-data-provider.cjs +1 -1
- package/dist/data/create-data-provider.cjs.map +1 -1
- package/dist/data/create-data-provider.js +1 -2
- package/dist/data/create-data-provider.js.map +1 -1
- package/dist/forms/validate-form.cjs +1 -1
- package/dist/forms/validate-form.cjs.map +1 -1
- package/dist/forms/validate-form.js +6 -1
- package/dist/forms/validate-form.js.map +1 -1
- package/dist/forms/zod-resolver.cjs +1 -1
- package/dist/forms/zod-resolver.cjs.map +1 -1
- package/dist/forms/zod-resolver.js +8 -3
- package/dist/forms/zod-resolver.js.map +1 -1
- package/dist/http/api-client.cjs +1 -1
- package/dist/http/api-client.cjs.map +1 -1
- package/dist/http/api-client.js +90 -72
- package/dist/http/api-client.js.map +1 -1
- package/dist/http/credential-scope.cjs +1 -1
- package/dist/http/credential-scope.cjs.map +1 -1
- package/dist/http/credential-scope.js +33 -9
- package/dist/http/credential-scope.js.map +1 -1
- package/dist/http/presigned-post.cjs.map +1 -1
- package/dist/http/presigned-post.js.map +1 -1
- package/dist/http/resumable-upload.cjs +1 -1
- package/dist/http/resumable-upload.cjs.map +1 -1
- package/dist/http/resumable-upload.js +90 -84
- package/dist/http/resumable-upload.js.map +1 -1
- package/dist/http/upload-with-progress.cjs +1 -1
- package/dist/http/upload-with-progress.cjs.map +1 -1
- package/dist/http/upload-with-progress.js +8 -8
- package/dist/http/upload-with-progress.js.map +1 -1
- package/dist/oauth/GoogleSignIn.cjs +1 -1
- package/dist/oauth/GoogleSignIn.cjs.map +1 -1
- package/dist/oauth/GoogleSignIn.js +5 -4
- package/dist/oauth/GoogleSignIn.js.map +1 -1
- package/dist/offline/create-offline-sync.cjs +1 -1
- package/dist/offline/create-offline-sync.cjs.map +1 -1
- package/dist/offline/create-offline-sync.js +87 -69
- package/dist/offline/create-offline-sync.js.map +1 -1
- package/dist/query/offline-persistence.cjs +1 -1
- package/dist/query/offline-persistence.cjs.map +1 -1
- package/dist/query/offline-persistence.js +54 -44
- package/dist/query/offline-persistence.js.map +1 -1
- package/dist/sse/create-event-stream.cjs +1 -1
- package/dist/sse/create-event-stream.cjs.map +1 -1
- package/dist/sse/create-event-stream.js +48 -41
- package/dist/sse/create-event-stream.js.map +1 -1
- package/dist/sse/ticket-event-stream.cjs +1 -1
- package/dist/sse/ticket-event-stream.cjs.map +1 -1
- package/dist/sse/ticket-event-stream.js +48 -19
- package/dist/sse/ticket-event-stream.js.map +1 -1
- package/dist/sse/use-event-stream.cjs +1 -1
- package/dist/sse/use-event-stream.cjs.map +1 -1
- package/dist/sse/use-event-stream.js +19 -14
- package/dist/sse/use-event-stream.js.map +1 -1
- package/dist/sw/background-sync.cjs +1 -1
- package/dist/sw/background-sync.cjs.map +1 -1
- package/dist/sw/background-sync.js +10 -8
- package/dist/sw/background-sync.js.map +1 -1
- package/dist/sw/cache.cjs +1 -1
- package/dist/sw/cache.cjs.map +1 -1
- package/dist/sw/cache.js +36 -30
- package/dist/sw/cache.js.map +1 -1
- package/dist/sw/create-push-handler.cjs +1 -1
- package/dist/sw/create-push-handler.cjs.map +1 -1
- package/dist/sw/create-push-handler.js +20 -10
- package/dist/sw/create-push-handler.js.map +1 -1
- package/dist/sw.d.ts +44 -13
- package/dist/tabular/compact.cjs +1 -1
- package/dist/tabular/compact.cjs.map +1 -1
- package/dist/tabular/compact.js +35 -16
- package/dist/tabular/compact.js.map +1 -1
- package/dist/tabular/manifest.cjs +1 -1
- package/dist/tabular/manifest.cjs.map +1 -1
- package/dist/tabular/manifest.js +33 -15
- package/dist/tabular/manifest.js.map +1 -1
- package/dist/tabular.d.ts +11 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +457 -68
- package/dist/tempest-react-sdk.js +224 -224
- package/dist/theme/initial-theme.cjs +4 -4
- package/dist/theme/initial-theme.cjs.map +1 -1
- package/dist/theme/initial-theme.js +8 -5
- package/dist/theme/initial-theme.js.map +1 -1
- package/dist/utils/compressed-storage.cjs +1 -1
- package/dist/utils/compressed-storage.cjs.map +1 -1
- package/dist/utils/compressed-storage.js +13 -7
- package/dist/utils/compressed-storage.js.map +1 -1
- package/dist/utils/objects.cjs +1 -1
- package/dist/utils/objects.cjs.map +1 -1
- package/dist/utils/objects.js +13 -7
- package/dist/utils/objects.js.map +1 -1
- package/dist/utils/safe-url.cjs +1 -1
- package/dist/utils/safe-url.cjs.map +1 -1
- package/dist/utils/safe-url.js +4 -1
- package/dist/utils/safe-url.js.map +1 -1
- package/dist/webrtc/peer-mesh.cjs +1 -1
- package/dist/webrtc/peer-mesh.cjs.map +1 -1
- package/dist/webrtc/peer-mesh.js +7 -1
- package/dist/webrtc/peer-mesh.js.map +1 -1
- package/dist/ws/create-web-socket.cjs +1 -1
- package/dist/ws/create-web-socket.cjs.map +1 -1
- package/dist/ws/create-web-socket.js +77 -61
- package/dist/ws/create-web-socket.js.map +1 -1
- package/dist/ws/use-web-socket.cjs +1 -1
- package/dist/ws/use-web-socket.cjs.map +1 -1
- package/dist/ws/use-web-socket.js +4 -3
- package/dist/ws/use-web-socket.js.map +1 -1
- package/package.json +1 -1
- package/template-pwa/src/pwa.tsx +42 -0
- package/template-pwa/src/sw.ts +5 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Breadcrumbs.cjs","names":["cn","styles","Fragment","require_cn","require_Breadcrumbs_module","react","react_jsx_runtime","Breadcrumbs","className","items","isLast","index","
|
|
1
|
+
{"version":3,"file":"Breadcrumbs.cjs","names":["cn","styles","vettedHref","Fragment","require_cn","require_safe_url","require_Breadcrumbs_module","react","react_jsx_runtime","Breadcrumbs","className","items","isLast","index","href","item","interactive","event","separator"],"sources":["../../../src/components/Breadcrumbs/Breadcrumbs.tsx"],"sourcesContent":["import { Fragment } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { vettedHref } from \"@/utils/safe-url\";\nimport styles from \"./Breadcrumbs.module.css\";\n\nexport interface BreadcrumbItem {\n label: ReactNode;\n href?: string;\n onClick?: () => void;\n}\n\nexport interface BreadcrumbsProps {\n items: BreadcrumbItem[];\n separator?: ReactNode;\n className?: string;\n}\n\n/**\n * Hierarchical navigation breadcrumbs. The last item is rendered as plain\n * text with `aria-current=\"page\"`. Items with neither `href` nor `onClick`\n * also render as text (intermediate but non-clickable).\n *\n * Each `href` is vetted with `safeLinkUrl`'s allowlist: an item whose URL uses a scheme\n * outside `http:`/`https:`/`mailto:`/`tel:`/`sms:` loses the `href`, and renders\n * as text unless it also has an `onClick`. A trail built from API data — the\n * folder names of a document tree — would otherwise put a clickable\n * `javascript:` link on the page under React 18, which writes it verbatim.\n */\nexport function Breadcrumbs({ items, separator = \"/\", className }: BreadcrumbsProps) {\n return (\n <nav aria-label=\"breadcrumb\" className={cn(styles.nav, className)}>\n {items.map((item, index) => {\n const isLast = index === items.length - 1;\n const href = vettedHref(item.href);\n const interactive = !isLast && (href || item.onClick);\n return (\n <Fragment key={index}>\n <span className={styles.item}>\n {interactive ? (\n <a\n href={href}\n onClick={(event) => {\n if (item.onClick) {\n event.preventDefault();\n item.onClick();\n }\n }}\n className={styles.link}\n >\n {item.label}\n </a>\n ) : (\n <span\n aria-current={isLast ? \"page\" : undefined}\n className={isLast ? styles.current : undefined}\n >\n {item.label}\n </span>\n )}\n </span>\n {!isLast && (\n <span className={styles.separator} aria-hidden>\n {separator}\n </span>\n )}\n </Fragment>\n );\n })}\n </nav>\n );\n}\n"],"mappings":"A,wKA6BA,SAAgBS,EAAY,CAAE,QAAO,YAAY,IAAK,aAA+B,CACjF,OACI,EAAAD,EAAA,IAAA,CAAC,MAAD,CAAK,aAAW,aAAa,UAAWR,EAAAA,GAAGC,EAAAA,QAAO,IAAKS,CAAS,EAC3D,SAAAC,EAAM,KAAK,EAAM,IAAU,CACxB,IAAMC,EAASC,IAAUF,EAAM,OAAS,EAClCG,EAAOZ,EAAAA,WAAWa,EAAK,IAAI,EAC3BC,EAAc,CAACJ,IAAWE,GAAQC,EAAK,SAC7C,OACI,EAAAP,EAAA,KAAA,CAACL,EAAAA,SAAD,CAAA,SAAA,EACI,EAAAK,EAAA,IAAA,CAAC,OAAD,CAAM,UAAWP,EAAAA,QAAO,KACnB,SAAAe,GACG,EAAAR,EAAA,IAAA,CAAC,IAAD,CACU,OACN,QAAU,GAAU,CACZO,EAAK,UACLE,EAAM,eAAe,EACrBF,EAAK,QAAQ,EAErB,EACA,UAAWd,EAAAA,QAAO,KAEjB,SAAAc,EAAK,KACP,CAAA,GAEH,EAAAP,EAAA,IAAA,CAAC,OAAD,CACI,eAAcI,EAAS,OAAS,IAAA,GAChC,UAAWA,EAASX,EAAAA,QAAO,QAAU,IAAA,GAEpC,SAAAc,EAAK,KACJ,CAAA,CAER,CAAA,EACL,CAACH,IACE,EAAAJ,EAAA,IAAA,CAAC,OAAD,CAAM,UAAWP,EAAAA,QAAO,UAAW,cAAA,GAC9B,SAAAiB,CACC,CAAA,CAEJ,CAAA,EA7BKL,CA6BL,CAElB,CAAC,CACA,CAAA,CAEb,C"}
|
|
@@ -1,37 +1,38 @@
|
|
|
1
1
|
import { cn as e } from "../../utils/cn.js";
|
|
2
|
-
import t from "
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
2
|
+
import { vettedHref as t } from "../../utils/safe-url.js";
|
|
3
|
+
import n from "./Breadcrumbs.module.js";
|
|
4
|
+
import { Fragment as r } from "react";
|
|
5
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
5
6
|
//#region src/components/Breadcrumbs/Breadcrumbs.tsx
|
|
6
|
-
function
|
|
7
|
-
return /* @__PURE__ */
|
|
7
|
+
function o({ items: o, separator: s = "/", className: c }) {
|
|
8
|
+
return /* @__PURE__ */ i("nav", {
|
|
8
9
|
"aria-label": "breadcrumb",
|
|
9
|
-
className: e(
|
|
10
|
-
children:
|
|
11
|
-
let
|
|
12
|
-
return /* @__PURE__ */
|
|
13
|
-
className:
|
|
14
|
-
children:
|
|
15
|
-
href:
|
|
10
|
+
className: e(n.nav, c),
|
|
11
|
+
children: o.map((e, c) => {
|
|
12
|
+
let l = c === o.length - 1, u = t(e.href), d = !l && (u || e.onClick);
|
|
13
|
+
return /* @__PURE__ */ a(r, { children: [/* @__PURE__ */ i("span", {
|
|
14
|
+
className: n.item,
|
|
15
|
+
children: d ? /* @__PURE__ */ i("a", {
|
|
16
|
+
href: u,
|
|
16
17
|
onClick: (t) => {
|
|
17
18
|
e.onClick && (t.preventDefault(), e.onClick());
|
|
18
19
|
},
|
|
19
|
-
className:
|
|
20
|
+
className: n.link,
|
|
20
21
|
children: e.label
|
|
21
|
-
}) : /* @__PURE__ */
|
|
22
|
-
"aria-current":
|
|
23
|
-
className:
|
|
22
|
+
}) : /* @__PURE__ */ i("span", {
|
|
23
|
+
"aria-current": l ? "page" : void 0,
|
|
24
|
+
className: l ? n.current : void 0,
|
|
24
25
|
children: e.label
|
|
25
26
|
})
|
|
26
|
-
}), !
|
|
27
|
-
className:
|
|
27
|
+
}), !l && /* @__PURE__ */ i("span", {
|
|
28
|
+
className: n.separator,
|
|
28
29
|
"aria-hidden": !0,
|
|
29
|
-
children:
|
|
30
|
-
})] },
|
|
30
|
+
children: s
|
|
31
|
+
})] }, c);
|
|
31
32
|
})
|
|
32
33
|
});
|
|
33
34
|
}
|
|
34
35
|
//#endregion
|
|
35
|
-
export {
|
|
36
|
+
export { o as Breadcrumbs };
|
|
36
37
|
|
|
37
38
|
//# sourceMappingURL=Breadcrumbs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Breadcrumbs.js","names":["styles","Breadcrumbs_module_default","Breadcrumbs","cn","className","items","isLast","index","
|
|
1
|
+
{"version":3,"file":"Breadcrumbs.js","names":["styles","Breadcrumbs_module_default","Breadcrumbs","cn","className","items","isLast","index","href","vettedHref","item","interactive","Fragment","event","separator"],"sources":["../../../src/components/Breadcrumbs/Breadcrumbs.tsx"],"sourcesContent":["import { Fragment } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { vettedHref } from \"@/utils/safe-url\";\nimport styles from \"./Breadcrumbs.module.css\";\n\nexport interface BreadcrumbItem {\n label: ReactNode;\n href?: string;\n onClick?: () => void;\n}\n\nexport interface BreadcrumbsProps {\n items: BreadcrumbItem[];\n separator?: ReactNode;\n className?: string;\n}\n\n/**\n * Hierarchical navigation breadcrumbs. The last item is rendered as plain\n * text with `aria-current=\"page\"`. Items with neither `href` nor `onClick`\n * also render as text (intermediate but non-clickable).\n *\n * Each `href` is vetted with `safeLinkUrl`'s allowlist: an item whose URL uses a scheme\n * outside `http:`/`https:`/`mailto:`/`tel:`/`sms:` loses the `href`, and renders\n * as text unless it also has an `onClick`. A trail built from API data — the\n * folder names of a document tree — would otherwise put a clickable\n * `javascript:` link on the page under React 18, which writes it verbatim.\n */\nexport function Breadcrumbs({ items, separator = \"/\", className }: BreadcrumbsProps) {\n return (\n <nav aria-label=\"breadcrumb\" className={cn(styles.nav, className)}>\n {items.map((item, index) => {\n const isLast = index === items.length - 1;\n const href = vettedHref(item.href);\n const interactive = !isLast && (href || item.onClick);\n return (\n <Fragment key={index}>\n <span className={styles.item}>\n {interactive ? (\n <a\n href={href}\n onClick={(event) => {\n if (item.onClick) {\n event.preventDefault();\n item.onClick();\n }\n }}\n className={styles.link}\n >\n {item.label}\n </a>\n ) : (\n <span\n aria-current={isLast ? \"page\" : undefined}\n className={isLast ? styles.current : undefined}\n >\n {item.label}\n </span>\n )}\n </span>\n {!isLast && (\n <span className={styles.separator} aria-hidden>\n {separator}\n </span>\n )}\n </Fragment>\n );\n })}\n </nav>\n );\n}\n"],"mappings":";;;;;;AA6BA,SAAgBE,EAAY,EAAE,UAAO,eAAY,KAAK,gBAA+B;CACjF,OACI,kBAAC,OAAD;EAAK,cAAW;EAAa,WAAWC,EAAGH,EAAO,KAAKI,CAAS;EAC3D,UAAAC,EAAM,KAAK,GAAM,MAAU;GACxB,IAAMC,IAASC,MAAUF,EAAM,SAAS,GAClCG,IAAOC,EAAWC,EAAK,IAAI,GAC3BC,IAAc,CAACL,MAAWE,KAAQE,EAAK;GAC7C,OACI,kBAACE,GAAD,EAAA,UAAA,CACI,kBAAC,QAAD;IAAM,WAAWZ,EAAO;IACnB,UAAAW,IACG,kBAAC,KAAD;KACU;KACN,UAAU,MAAU;MAChB,AAAID,EAAK,YACLG,EAAM,eAAe,GACrBH,EAAK,QAAQ;KAErB;KACA,WAAWV,EAAO;KAEjB,UAAAU,EAAK;IACP,CAAA,IAEH,kBAAC,QAAD;KACI,gBAAcJ,IAAS,SAAS,KAAA;KAChC,WAAWA,IAASN,EAAO,UAAU,KAAA;KAEpC,UAAAU,EAAK;IACJ,CAAA;GAER,CAAA,GACL,CAACJ,KACE,kBAAC,QAAD;IAAM,WAAWN,EAAO;IAAW,eAAA;IAC9B,UAAAc;GACC,CAAA,CAEJ,EAAA,GA7BKP,CA6BL;EAElB,CAAC;CACA,CAAA;AAEb"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./button-class.cjs"),
|
|
1
|
+
const e=require("../../utils/safe-url.cjs"),t=require("./button-class.cjs"),n=require("./ButtonContent.cjs"),r=require("./link-rel.cjs");let i=require("react"),a=require("react/jsx-runtime");var o=(0,i.forwardRef)(function(i,o){let{variant:s,size:c,loading:l=!1,disabled:u=!1,fullWidth:d,iconOnly:f,pill:p,leftIcon:m,rightIcon:h,className:g,children:_,href:v,target:y,rel:b,...x}=i,S=e.vettedHref(v),C=u||l||v!==void 0&&S===void 0,w=t.buttonClassName({variant:s,size:c,loading:l,fullWidth:d,iconOnly:f,pill:p},g),T=(0,a.jsx)(n.ButtonContent,{loading:l,leftIcon:m,rightIcon:h,children:_});return v===void 0?(0,a.jsx)(`button`,{...x,ref:o,className:w,disabled:C,"aria-busy":l||void 0,children:T}):(0,a.jsx)(`a`,{...x,ref:o,className:w,href:C?void 0:S,target:y,rel:r.resolveLinkRel(y,b),role:C?`link`:x.role,"aria-disabled":C||void 0,"aria-busy":l||void 0,tabIndex:C?-1:x.tabIndex,onClick:C?t.preventActivation:x.onClick,onAuxClick:C?t.preventActivation:x.onAuxClick,children:T})});exports.Button=o;
|
|
2
2
|
//# sourceMappingURL=Button.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.cjs","names":["forwardRef","buttonClassName","ButtonContent","resolveLinkRel","preventActivation","require_button_class","require_ButtonContent","require_link_rel","react","react_jsx_runtime","Button","rest","props","inactive","disabled","loading","classes","className","content","
|
|
1
|
+
{"version":3,"file":"Button.cjs","names":["forwardRef","vettedHref","buttonClassName","ButtonContent","resolveLinkRel","preventActivation","require_safe_url","require_button_class","require_ButtonContent","require_link_rel","react","react_jsx_runtime","Button","rest","props","safeHref","href","inactive","disabled","loading","classes","className","content","target","rel"],"sources":["../../../src/components/Button/Button.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — this is the design system's button: variant, size,\n * loading, fullWidth, iconOnly, pill, leftIcon and rightIcon are the whole\n * vocabulary every screen composes from, and href only picks which element carries\n * it. Nine orthogonal knobs on the most-used component is the API working — the\n * count rule is looking for two components fused together, and there is only one\n * here.\n */\nimport { forwardRef } from \"react\";\nimport type {\n AnchorHTMLAttributes,\n ButtonHTMLAttributes,\n ForwardedRef,\n HTMLAttributes,\n ReactElement,\n ReactNode,\n Ref,\n RefAttributes,\n} from \"react\";\nimport { vettedHref } from \"@/utils/safe-url\";\nimport { buttonClassName, preventActivation } from \"./button-class\";\nimport { ButtonContent } from \"./ButtonContent\";\nimport { resolveLinkRel } from \"./link-rel\";\nimport type { ButtonBaseProps } from \"./types\";\n\nexport type { ButtonBaseProps, ButtonSize, ButtonVariant } from \"./types\";\n\n/** `Button` rendering a native `<button>` — the default, when `href` is not set. */\nexport interface ButtonAsButtonProps\n extends ButtonBaseProps, ButtonHTMLAttributes<HTMLButtonElement> {\n href?: undefined;\n}\n\n/**\n * `Button` rendering an `<a href>` — a call to action that navigates.\n *\n * `type` is a `<button>` attribute and is rejected here; `target` and `rel` are\n * forwarded, and `target=\"_blank\"` always gets `noopener noreferrer` added.\n */\nexport interface ButtonAsLinkProps\n extends ButtonBaseProps, Omit<AnchorHTMLAttributes<HTMLAnchorElement>, \"href\" | \"type\"> {\n /** Destination. Its presence is what makes the button an `<a>`. */\n href: string;\n type?: never;\n}\n\nexport type ButtonProps = ButtonAsButtonProps | ButtonAsLinkProps;\n\n/**\n * Each mode's props with the ref type of the element it renders, so a `ref` to an\n * `HTMLButtonElement` on a link is a type error.\n */\ntype ButtonPropsWithRef =\n | (ButtonAsButtonProps & RefAttributes<HTMLButtonElement>)\n | (ButtonAsLinkProps & RefAttributes<HTMLAnchorElement>);\n\n/** `Button`'s call signature: one props union, each member with its own ref type. */\ntype ButtonComponent = ((props: ButtonPropsWithRef) => ReactElement) & { displayName?: string };\n\n/** The flattened view the render function works on after the call site was type-checked. */\ntype ButtonRenderProps = ButtonBaseProps &\n HTMLAttributes<HTMLElement> & {\n href?: string;\n target?: string;\n rel?: string;\n };\n\n/**\n * Primary action button with variants, sizes and a loading state that\n * preserves layout via an absolutely-positioned spinner.\n *\n * Variants: `primary` (solid), `secondary` (neutral), `danger`, `success`,\n * `ghost` (transparent), `soft` (tinted), `outline` (bordered), `link`.\n *\n * Sizes are density-aware — they read from `--tempest-control-height-*`\n * tokens which respond to the `data-tempest-density` attribute.\n *\n * With `href` it renders an `<a>` with the same look: a call to action that\n * navigates keeps the middle click, Ctrl+click, \"open in new tab\" and the URL\n * preview, which a `<button>` calling `location.assign` loses. `target=\"_blank\"`\n * gets `noopener noreferrer`. An `<a>` has no `disabled`, so a disabled or loading\n * link gets `aria-disabled=\"true\"`, `role=\"link\"`, `tabIndex={-1}`, no `href`, and\n * its click and middle-click cancelled.\n *\n * The `href` is vetted first, with `safeLinkUrl`'s allowlist: `http:`, `https:`, `mailto:`,\n * `tel:`, `sms:` and relative URLs pass, anything else renders as the same inert,\n * `aria-disabled` link. React 19 blocks a `javascript:` href but the peer range\n * admits React 18, which writes it verbatim (#447), and a CTA built from API data\n * should not need the app to remember that. A link to a custom scheme (`whatsapp:`,\n * an app deep link) goes through {@link ButtonSlot} with the app's own `<a>`.\n *\n * To style another element — a react-router `Link` — use {@link ButtonSlot}. It is\n * a separate component, not an `asChild` prop, so an app that never uses it does\n * not ship the prop merge and ref composition it needs.\n *\n * The ref is typed per mode: `HTMLButtonElement`, or `HTMLAnchorElement` with\n * `href`. `forwardRef` takes one ref type for all props, so its result is re-typed\n * to the per-mode union — the runtime is plain `forwardRef`.\n *\n * @example\n * <Button onClick={save}>Salvar</Button>\n *\n * @example\n * <Button href=\"https://wa.me/5586999999999\" target=\"_blank\" rightIcon={<ExternalLink />}>\n * Fale conosco\n * </Button>\n */\nexport const Button = forwardRef<HTMLElement, ButtonProps>(function Button(\n props: ButtonProps,\n ref: ForwardedRef<HTMLElement>,\n): ReactElement {\n const {\n variant,\n size,\n loading = false,\n disabled = false,\n fullWidth,\n iconOnly,\n pill,\n leftIcon,\n rightIcon,\n className,\n children,\n href,\n target,\n rel,\n ...rest\n } = props as ButtonRenderProps;\n const safeHref = vettedHref(href);\n const inactive = disabled || loading || (href !== undefined && safeHref === undefined);\n const classes = buttonClassName(\n { variant, size, loading, fullWidth, iconOnly, pill },\n className,\n );\n const content: ReactNode = (\n <ButtonContent loading={loading} leftIcon={leftIcon} rightIcon={rightIcon}>\n {children}\n </ButtonContent>\n );\n\n if (href !== undefined) {\n return (\n <a\n {...rest}\n ref={ref as Ref<HTMLAnchorElement>}\n className={classes}\n href={inactive ? undefined : safeHref}\n target={target}\n rel={resolveLinkRel(target, rel)}\n role={inactive ? \"link\" : rest.role}\n aria-disabled={inactive || undefined}\n aria-busy={loading || undefined}\n tabIndex={inactive ? -1 : rest.tabIndex}\n onClick={inactive ? preventActivation : rest.onClick}\n onAuxClick={inactive ? preventActivation : rest.onAuxClick}\n >\n {content}\n </a>\n );\n }\n\n return (\n <button\n {...(rest as ButtonHTMLAttributes<HTMLButtonElement>)}\n ref={ref as Ref<HTMLButtonElement>}\n className={classes}\n disabled={inactive}\n aria-busy={loading || undefined}\n >\n {content}\n </button>\n );\n}) as unknown as ButtonComponent;\n"],"mappings":"A,+LA2GA,IAAaY,GAAA,EAASZ,EAAAA,WAAAA,CAAqC,SACvD,EACA,EACY,CACZ,GAAM,CACF,UACA,OACA,UAAU,GACV,WAAW,GACX,YACA,WACA,OACA,WACA,YACA,YACA,WACA,OACA,SACA,MACA,GAAGa,GACHC,EACEC,EAAWd,EAAAA,WAAWe,CAAI,EAC1BC,EAAWC,GAAYC,GAAYH,IAAS,IAAA,IAAaD,IAAa,IAAA,GACtEK,EAAUlB,EAAAA,gBACZ,CAAE,UAAS,OAAM,UAAS,YAAW,WAAU,MAAK,EACpDmB,CACJ,EACMC,GACF,EAAAX,EAAA,IAAA,CAACR,EAAAA,cAAD,CAAwB,UAAmB,WAAqB,YAC3D,UACU,CAAA,EAwBnB,OArBIa,IAAS,IAAA,IAsBT,EAAAL,EAAA,IAAA,CAAC,SAAD,CACI,GAAKE,EACA,MACL,UAAWO,EACX,SAAUH,EACV,YAAWE,GAAW,IAAA,GAErB,SAAAG,CACG,CAAA,GA5BJ,EAAAX,EAAA,IAAA,CAAC,IAAD,CACI,GAAIE,EACC,MACL,UAAWO,EACX,KAAMH,EAAW,IAAA,GAAYF,EACrB,SACR,IAAKX,EAAAA,eAAemB,EAAQC,CAAG,EAC/B,KAAMP,EAAW,OAASJ,EAAK,KAC/B,gBAAeI,GAAY,IAAA,GAC3B,YAAWE,GAAW,IAAA,GACtB,SAAUF,EAAW,GAAKJ,EAAK,SAC/B,QAASI,EAAWZ,EAAAA,kBAAoBQ,EAAK,QAC7C,WAAYI,EAAWZ,EAAAA,kBAAoBQ,EAAK,WAE/C,SAAAS,CACF,CAAA,CAef,CAAC,E"}
|
|
@@ -1,47 +1,48 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { vettedHref as e } from "../../utils/safe-url.js";
|
|
2
|
+
import { buttonClassName as t, preventActivation as n } from "./button-class.js";
|
|
3
|
+
import { ButtonContent as r } from "./ButtonContent.js";
|
|
4
|
+
import { resolveLinkRel as i } from "./link-rel.js";
|
|
5
|
+
import { forwardRef as a } from "react";
|
|
6
|
+
import { jsx as o } from "react/jsx-runtime";
|
|
6
7
|
//#region src/components/Button/Button.tsx
|
|
7
|
-
var
|
|
8
|
-
let { variant:
|
|
9
|
-
variant:
|
|
10
|
-
size:
|
|
11
|
-
loading:
|
|
12
|
-
fullWidth:
|
|
13
|
-
iconOnly:
|
|
14
|
-
pill:
|
|
15
|
-
},
|
|
16
|
-
loading:
|
|
17
|
-
leftIcon:
|
|
18
|
-
rightIcon:
|
|
19
|
-
children:
|
|
8
|
+
var s = a(function(a, s) {
|
|
9
|
+
let { variant: c, size: l, loading: u = !1, disabled: d = !1, fullWidth: f, iconOnly: p, pill: m, leftIcon: h, rightIcon: g, className: _, children: v, href: y, target: b, rel: x, ...S } = a, C = e(y), w = d || u || y !== void 0 && C === void 0, T = t({
|
|
10
|
+
variant: c,
|
|
11
|
+
size: l,
|
|
12
|
+
loading: u,
|
|
13
|
+
fullWidth: f,
|
|
14
|
+
iconOnly: p,
|
|
15
|
+
pill: m
|
|
16
|
+
}, _), E = /* @__PURE__ */ o(r, {
|
|
17
|
+
loading: u,
|
|
18
|
+
leftIcon: h,
|
|
19
|
+
rightIcon: g,
|
|
20
|
+
children: v
|
|
20
21
|
});
|
|
21
|
-
return
|
|
22
|
-
...
|
|
23
|
-
ref:
|
|
24
|
-
className:
|
|
25
|
-
disabled:
|
|
26
|
-
"aria-busy":
|
|
27
|
-
children:
|
|
28
|
-
}) : /* @__PURE__ */
|
|
29
|
-
...
|
|
30
|
-
ref:
|
|
31
|
-
className:
|
|
32
|
-
href:
|
|
33
|
-
target:
|
|
34
|
-
rel:
|
|
35
|
-
role:
|
|
36
|
-
"aria-disabled":
|
|
37
|
-
"aria-busy":
|
|
38
|
-
tabIndex:
|
|
39
|
-
onClick:
|
|
40
|
-
onAuxClick:
|
|
41
|
-
children:
|
|
22
|
+
return y === void 0 ? /* @__PURE__ */ o("button", {
|
|
23
|
+
...S,
|
|
24
|
+
ref: s,
|
|
25
|
+
className: T,
|
|
26
|
+
disabled: w,
|
|
27
|
+
"aria-busy": u || void 0,
|
|
28
|
+
children: E
|
|
29
|
+
}) : /* @__PURE__ */ o("a", {
|
|
30
|
+
...S,
|
|
31
|
+
ref: s,
|
|
32
|
+
className: T,
|
|
33
|
+
href: w ? void 0 : C,
|
|
34
|
+
target: b,
|
|
35
|
+
rel: i(b, x),
|
|
36
|
+
role: w ? "link" : S.role,
|
|
37
|
+
"aria-disabled": w || void 0,
|
|
38
|
+
"aria-busy": u || void 0,
|
|
39
|
+
tabIndex: w ? -1 : S.tabIndex,
|
|
40
|
+
onClick: w ? n : S.onClick,
|
|
41
|
+
onAuxClick: w ? n : S.onAuxClick,
|
|
42
|
+
children: E
|
|
42
43
|
});
|
|
43
44
|
});
|
|
44
45
|
//#endregion
|
|
45
|
-
export {
|
|
46
|
+
export { s as Button };
|
|
46
47
|
|
|
47
48
|
//# sourceMappingURL=Button.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.js","names":["Button","forwardRef","rest","props","inactive","disabled","loading","classes","buttonClassName","className","content","ButtonContent","
|
|
1
|
+
{"version":3,"file":"Button.js","names":["Button","forwardRef","rest","props","safeHref","vettedHref","href","inactive","disabled","loading","classes","buttonClassName","className","content","ButtonContent","resolveLinkRel","target","rel","preventActivation"],"sources":["../../../src/components/Button/Button.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — this is the design system's button: variant, size,\n * loading, fullWidth, iconOnly, pill, leftIcon and rightIcon are the whole\n * vocabulary every screen composes from, and href only picks which element carries\n * it. Nine orthogonal knobs on the most-used component is the API working — the\n * count rule is looking for two components fused together, and there is only one\n * here.\n */\nimport { forwardRef } from \"react\";\nimport type {\n AnchorHTMLAttributes,\n ButtonHTMLAttributes,\n ForwardedRef,\n HTMLAttributes,\n ReactElement,\n ReactNode,\n Ref,\n RefAttributes,\n} from \"react\";\nimport { vettedHref } from \"@/utils/safe-url\";\nimport { buttonClassName, preventActivation } from \"./button-class\";\nimport { ButtonContent } from \"./ButtonContent\";\nimport { resolveLinkRel } from \"./link-rel\";\nimport type { ButtonBaseProps } from \"./types\";\n\nexport type { ButtonBaseProps, ButtonSize, ButtonVariant } from \"./types\";\n\n/** `Button` rendering a native `<button>` — the default, when `href` is not set. */\nexport interface ButtonAsButtonProps\n extends ButtonBaseProps, ButtonHTMLAttributes<HTMLButtonElement> {\n href?: undefined;\n}\n\n/**\n * `Button` rendering an `<a href>` — a call to action that navigates.\n *\n * `type` is a `<button>` attribute and is rejected here; `target` and `rel` are\n * forwarded, and `target=\"_blank\"` always gets `noopener noreferrer` added.\n */\nexport interface ButtonAsLinkProps\n extends ButtonBaseProps, Omit<AnchorHTMLAttributes<HTMLAnchorElement>, \"href\" | \"type\"> {\n /** Destination. Its presence is what makes the button an `<a>`. */\n href: string;\n type?: never;\n}\n\nexport type ButtonProps = ButtonAsButtonProps | ButtonAsLinkProps;\n\n/**\n * Each mode's props with the ref type of the element it renders, so a `ref` to an\n * `HTMLButtonElement` on a link is a type error.\n */\ntype ButtonPropsWithRef =\n | (ButtonAsButtonProps & RefAttributes<HTMLButtonElement>)\n | (ButtonAsLinkProps & RefAttributes<HTMLAnchorElement>);\n\n/** `Button`'s call signature: one props union, each member with its own ref type. */\ntype ButtonComponent = ((props: ButtonPropsWithRef) => ReactElement) & { displayName?: string };\n\n/** The flattened view the render function works on after the call site was type-checked. */\ntype ButtonRenderProps = ButtonBaseProps &\n HTMLAttributes<HTMLElement> & {\n href?: string;\n target?: string;\n rel?: string;\n };\n\n/**\n * Primary action button with variants, sizes and a loading state that\n * preserves layout via an absolutely-positioned spinner.\n *\n * Variants: `primary` (solid), `secondary` (neutral), `danger`, `success`,\n * `ghost` (transparent), `soft` (tinted), `outline` (bordered), `link`.\n *\n * Sizes are density-aware — they read from `--tempest-control-height-*`\n * tokens which respond to the `data-tempest-density` attribute.\n *\n * With `href` it renders an `<a>` with the same look: a call to action that\n * navigates keeps the middle click, Ctrl+click, \"open in new tab\" and the URL\n * preview, which a `<button>` calling `location.assign` loses. `target=\"_blank\"`\n * gets `noopener noreferrer`. An `<a>` has no `disabled`, so a disabled or loading\n * link gets `aria-disabled=\"true\"`, `role=\"link\"`, `tabIndex={-1}`, no `href`, and\n * its click and middle-click cancelled.\n *\n * The `href` is vetted first, with `safeLinkUrl`'s allowlist: `http:`, `https:`, `mailto:`,\n * `tel:`, `sms:` and relative URLs pass, anything else renders as the same inert,\n * `aria-disabled` link. React 19 blocks a `javascript:` href but the peer range\n * admits React 18, which writes it verbatim (#447), and a CTA built from API data\n * should not need the app to remember that. A link to a custom scheme (`whatsapp:`,\n * an app deep link) goes through {@link ButtonSlot} with the app's own `<a>`.\n *\n * To style another element — a react-router `Link` — use {@link ButtonSlot}. It is\n * a separate component, not an `asChild` prop, so an app that never uses it does\n * not ship the prop merge and ref composition it needs.\n *\n * The ref is typed per mode: `HTMLButtonElement`, or `HTMLAnchorElement` with\n * `href`. `forwardRef` takes one ref type for all props, so its result is re-typed\n * to the per-mode union — the runtime is plain `forwardRef`.\n *\n * @example\n * <Button onClick={save}>Salvar</Button>\n *\n * @example\n * <Button href=\"https://wa.me/5586999999999\" target=\"_blank\" rightIcon={<ExternalLink />}>\n * Fale conosco\n * </Button>\n */\nexport const Button = forwardRef<HTMLElement, ButtonProps>(function Button(\n props: ButtonProps,\n ref: ForwardedRef<HTMLElement>,\n): ReactElement {\n const {\n variant,\n size,\n loading = false,\n disabled = false,\n fullWidth,\n iconOnly,\n pill,\n leftIcon,\n rightIcon,\n className,\n children,\n href,\n target,\n rel,\n ...rest\n } = props as ButtonRenderProps;\n const safeHref = vettedHref(href);\n const inactive = disabled || loading || (href !== undefined && safeHref === undefined);\n const classes = buttonClassName(\n { variant, size, loading, fullWidth, iconOnly, pill },\n className,\n );\n const content: ReactNode = (\n <ButtonContent loading={loading} leftIcon={leftIcon} rightIcon={rightIcon}>\n {children}\n </ButtonContent>\n );\n\n if (href !== undefined) {\n return (\n <a\n {...rest}\n ref={ref as Ref<HTMLAnchorElement>}\n className={classes}\n href={inactive ? undefined : safeHref}\n target={target}\n rel={resolveLinkRel(target, rel)}\n role={inactive ? \"link\" : rest.role}\n aria-disabled={inactive || undefined}\n aria-busy={loading || undefined}\n tabIndex={inactive ? -1 : rest.tabIndex}\n onClick={inactive ? preventActivation : rest.onClick}\n onAuxClick={inactive ? preventActivation : rest.onAuxClick}\n >\n {content}\n </a>\n );\n }\n\n return (\n <button\n {...(rest as ButtonHTMLAttributes<HTMLButtonElement>)}\n ref={ref as Ref<HTMLButtonElement>}\n className={classes}\n disabled={inactive}\n aria-busy={loading || undefined}\n >\n {content}\n </button>\n );\n}) as unknown as ButtonComponent;\n"],"mappings":";;;;;;;AA2GA,IAAaA,IAASC,EAAqC,SACvD,GACA,GACY;CACZ,IAAM,EACF,YACA,SACA,aAAU,IACV,cAAW,IACX,cACA,aACA,SACA,aACA,cACA,cACA,aACA,SACA,WACA,QACA,GAAGC,MACHC,GACEC,IAAWC,EAAWC,CAAI,GAC1BC,IAAWC,KAAYC,KAAYH,MAAS,KAAA,KAAaF,MAAa,KAAA,GACtEM,IAAUC,EACZ;EAAE;EAAS;EAAM;EAAS;EAAW;EAAU;CAAK,GACpDC,CACJ,GACMC,IACF,kBAACC,GAAD;EAAwB;EAAmB;EAAqB;EAC3D;CACU,CAAA;CAwBnB,OArBIR,MAAS,KAAA,IAsBT,kBAAC,UAAD;EACI,GAAKJ;EACA;EACL,WAAWQ;EACX,UAAUH;EACV,aAAWE,KAAW,KAAA;EAErB,UAAAI;CACG,CAAA,IA5BJ,kBAAC,KAAD;EACI,GAAIX;EACC;EACL,WAAWQ;EACX,MAAMH,IAAW,KAAA,IAAYH;EACrB;EACR,KAAKW,EAAeC,GAAQC,CAAG;EAC/B,MAAMV,IAAW,SAASL,EAAK;EAC/B,iBAAeK,KAAY,KAAA;EAC3B,aAAWE,KAAW,KAAA;EACtB,UAAUF,IAAW,KAAKL,EAAK;EAC/B,SAASK,IAAWW,IAAoBhB,EAAK;EAC7C,YAAYK,IAAWW,IAAoBhB,EAAK;EAE/C,UAAAW;CACF,CAAA;AAef,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../utils/
|
|
1
|
+
const e=require("../../utils/safe-url.cjs"),t=require("../../utils/numbers.cjs"),n=require("../VideoPlayer/VideoPlayer.cjs"),r=require("../Lightbox/Lightbox.cjs"),i=require("./Chat.module.cjs"),a=require("../AudioPlayer/AudioPlayer.cjs");let o=require("react"),s=require("react/jsx-runtime");var c=Array.from({length:28},()=>.25);function l(e){let t=Math.round(e/1e3),n=Math.floor(t/60),r=t%60;return`${n}:${String(r).padStart(2,`0`)}`}function u({attachments:u,strings:d}){let f=u.filter(e=>e.kind===`image`),[p,m]=(0,o.useState)(null);return(0,s.jsxs)(`div`,{className:i.default.attachments,children:[u.map((r,o)=>{let u=`${r.kind}-${r.url}-${o}`;if(r.kind===`image`){let e=f.indexOf(r);return(0,s.jsx)(`button`,{type:`button`,className:i.default.imageAttachment,onClick:()=>m(e),children:(0,s.jsx)(`img`,{src:r.thumbnailUrl??r.url,alt:r.alt??r.name??``,loading:`lazy`})},u)}if(r.kind===`video`)return(0,s.jsx)(n.VideoPlayer,{src:r.url,durationMs:r.durationMs,poster:r.thumbnailUrl,className:i.default.videoAttachment},u);if(r.kind===`voice`)return(0,s.jsxs)(`div`,{className:i.default.voiceAttachment,children:[(0,s.jsx)(`div`,{className:i.default.waveform,"aria-hidden":`true`,children:(r.waveform??c).map((e,t)=>(0,s.jsx)(`span`,{style:{height:`${Math.max(8,Math.min(100,e*100))}%`}},t))}),(0,s.jsx)(a.AudioPlayer,{src:r.url,durationMs:r.durationMs,"aria-label":d.voiceNote})]},u);if(r.kind===`audio`)return(0,s.jsx)(a.AudioPlayer,{src:r.url,durationMs:r.durationMs},u);let p=e.safeAttachmentUrl(r.url),h=(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(`span`,{className:i.default.fileName,children:r.name??d.download}),(0,s.jsx)(`span`,{className:i.default.fileMeta,children:[r.sizeBytes===void 0?null:t.formatBytes(r.sizeBytes),r.durationMs===void 0?null:l(r.durationMs)].filter(Boolean).join(` · `)})]});return p?(0,s.jsx)(`a`,{className:i.default.fileAttachment,href:p,download:r.name??!0,children:h},u):(0,s.jsx)(`div`,{className:i.default.fileAttachment,children:h},u)}),f.length>0&&(0,s.jsx)(r.Lightbox,{items:f.map(e=>({src:e.url,alt:e.alt??e.name??``,thumbnail:e.thumbnailUrl})),open:p!==null,index:p??0,onIndexChange:m,onClose:()=>m(null)})]})}exports.ChatAttachments=u;
|
|
2
2
|
//# sourceMappingURL=ChatAttachments.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatAttachments.cjs","names":["useState","styles","VideoPlayer","AudioPlayer","safeAttachmentUrl","_Fragment","formatBytes","Lightbox","require_numbers","require_VideoPlayer","require_Lightbox","require_Chat_module","require_AudioPlayer","require_safe_url","react","react_jsx_runtime","FLAT_WAVEFORM","durationLabel","total","durationMs","minutes","seconds","ChatAttachments","images","attachments","attachment","lightboxIndex","setLightboxIndex","key","index","position","peak","bar","strings","href","row","image"],"sources":["../../../src/components/Chat/ChatAttachments.tsx"],"sourcesContent":["import { useState } from \"react\";\n\nimport { formatBytes } from \"@/utils/numbers\";\nimport { safeAttachmentUrl } from \"@/utils/safe-url\";\n\nimport { AudioPlayer } from \"../AudioPlayer\";\nimport { Lightbox } from \"../Lightbox\";\nimport { VideoPlayer } from \"../VideoPlayer\";\nimport type { ChatAttachment } from \"./chat-groups\";\nimport styles from \"./Chat.module.css\";\n\n/** Strings this view needs, passed in so the locale table stays in one file. */\nexport interface ChatAttachmentStrings {\n voiceNote: string;\n download: string;\n}\n\nexport interface ChatAttachmentsProps {\n /** What this message carries. */\n attachments: readonly ChatAttachment[];\n /** Labels, from `chatStrings(locale)`. */\n strings: ChatAttachmentStrings;\n}\n\n/** Bars a voice note draws when the app gave no peaks. */\nconst FLAT_WAVEFORM = Array.from({ length: 28 }, () => 0.25);\n\n/** `1:04` from milliseconds — a duration, never a clock time. */\nfunction durationLabel(durationMs: number): string {\n const total = Math.round(durationMs / 1000);\n const minutes = Math.floor(total / 60);\n const seconds = total % 60;\n return `${minutes}:${String(seconds).padStart(2, \"0\")}`;\n}\n\n/**\n * The media half of a bubble.\n *\n * Kept in its own module, and reached through `lazy()` from the thread, because\n * it pulls three of the heaviest components in the SDK — `Lightbox`,\n * `VideoPlayer`, `AudioPlayer` — and a support thread of plain text should not\n * pay for a photo viewer it never opens. The thread renders it only for messages\n * that actually carry an attachment, so that chunk is requested the first time\n * one appears.\n *\n * Nothing here is new UI: an image opens the SDK's lightbox, a video and an audio\n * clip use the SDK's players, and a file is a download row. That composition is\n * the whole point — every app was writing it again, the same way.\n *\n * The file row is the one place a sender's URL becomes an `href`, so it goes\n * through {@link safeAttachmentUrl}: `http:`, `https:`, `blob:` and relative URLs\n * are linked, anything else renders the same row without a link (#447). Media\n * `src` is left as given — an `<img>`, `<video>` or `<audio>` does not run a\n * `javascript:` URL.\n *\n * @param props - The attachments and the locale strings.\n * @returns The rendered attachment list.\n */\nexport function ChatAttachments({ attachments, strings }: ChatAttachmentsProps) {\n const images = attachments.filter((attachment) => attachment.kind === \"image\");\n const [lightboxIndex, setLightboxIndex] = useState<number | null>(null);\n\n return (\n <div className={styles.attachments}>\n {attachments.map((attachment, index) => {\n const key = `${attachment.kind}-${attachment.url}-${index}`;\n if (attachment.kind === \"image\") {\n const position = images.indexOf(attachment);\n return (\n <button\n key={key}\n type=\"button\"\n className={styles.imageAttachment}\n onClick={() => setLightboxIndex(position)}\n >\n <img\n src={attachment.thumbnailUrl ?? attachment.url}\n alt={attachment.alt ?? attachment.name ?? \"\"}\n loading=\"lazy\"\n />\n </button>\n );\n }\n if (attachment.kind === \"video\") {\n return (\n <VideoPlayer\n key={key}\n src={attachment.url}\n durationMs={attachment.durationMs}\n poster={attachment.thumbnailUrl}\n className={styles.videoAttachment}\n />\n );\n }\n if (attachment.kind === \"voice\") {\n return (\n <div key={key} className={styles.voiceAttachment}>\n <div className={styles.waveform} aria-hidden=\"true\">\n {(attachment.waveform ?? FLAT_WAVEFORM).map((peak, bar) => (\n <span\n key={bar}\n style={{\n height: `${Math.max(8, Math.min(100, peak * 100))}%`,\n }}\n />\n ))}\n </div>\n <AudioPlayer\n src={attachment.url}\n durationMs={attachment.durationMs}\n aria-label={strings.voiceNote}\n />\n </div>\n );\n }\n if (attachment.kind === \"audio\") {\n return (\n <AudioPlayer\n key={key}\n src={attachment.url}\n durationMs={attachment.durationMs}\n />\n );\n }\n const href = safeAttachmentUrl(attachment.url);\n const row = (\n <>\n <span className={styles.fileName}>\n {attachment.name ?? strings.download}\n </span>\n <span className={styles.fileMeta}>\n {[\n attachment.sizeBytes !== undefined\n ? formatBytes(attachment.sizeBytes)\n : null,\n attachment.durationMs !== undefined\n ? durationLabel(attachment.durationMs)\n : null,\n ]\n .filter(Boolean)\n .join(\" · \")}\n </span>\n </>\n );\n return href ? (\n <a\n key={key}\n className={styles.fileAttachment}\n href={href}\n download={attachment.name ?? true}\n >\n {row}\n </a>\n ) : (\n <div key={key} className={styles.fileAttachment}>\n {row}\n </div>\n );\n })}\n\n {images.length > 0 && (\n <Lightbox\n items={images.map((image) => ({\n src: image.url,\n alt: image.alt ?? image.name ?? \"\",\n thumbnail: image.thumbnailUrl,\n }))}\n open={lightboxIndex !== null}\n index={lightboxIndex ?? 0}\n onIndexChange={setLightboxIndex}\n onClose={() => setLightboxIndex(null)}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChatAttachments.cjs","names":["useState","styles","VideoPlayer","AudioPlayer","safeAttachmentUrl","_Fragment","formatBytes","Lightbox","require_safe_url","require_numbers","require_VideoPlayer","require_Lightbox","require_Chat_module","require_AudioPlayer","react","react_jsx_runtime","FLAT_WAVEFORM","durationLabel","total","durationMs","minutes","seconds","ChatAttachments","images","attachments","attachment","lightboxIndex","setLightboxIndex","key","index","position","peak","bar","strings","href","row","image"],"sources":["../../../src/components/Chat/ChatAttachments.tsx"],"sourcesContent":["import { useState } from \"react\";\n\nimport { formatBytes } from \"@/utils/numbers\";\nimport { safeAttachmentUrl } from \"@/utils/safe-url\";\n\nimport { AudioPlayer } from \"../AudioPlayer\";\nimport { Lightbox } from \"../Lightbox\";\nimport { VideoPlayer } from \"../VideoPlayer\";\nimport type { ChatAttachment } from \"./chat-groups\";\nimport styles from \"./Chat.module.css\";\n\n/** Strings this view needs, passed in so the locale table stays in one file. */\nexport interface ChatAttachmentStrings {\n voiceNote: string;\n download: string;\n}\n\nexport interface ChatAttachmentsProps {\n /** What this message carries. */\n attachments: readonly ChatAttachment[];\n /** Labels, from `chatStrings(locale)`. */\n strings: ChatAttachmentStrings;\n}\n\n/** Bars a voice note draws when the app gave no peaks. */\nconst FLAT_WAVEFORM = Array.from({ length: 28 }, () => 0.25);\n\n/** `1:04` from milliseconds — a duration, never a clock time. */\nfunction durationLabel(durationMs: number): string {\n const total = Math.round(durationMs / 1000);\n const minutes = Math.floor(total / 60);\n const seconds = total % 60;\n return `${minutes}:${String(seconds).padStart(2, \"0\")}`;\n}\n\n/**\n * The media half of a bubble.\n *\n * Kept in its own module, and reached through `lazy()` from the thread, because\n * it pulls three of the heaviest components in the SDK — `Lightbox`,\n * `VideoPlayer`, `AudioPlayer` — and a support thread of plain text should not\n * pay for a photo viewer it never opens. The thread renders it only for messages\n * that actually carry an attachment, so that chunk is requested the first time\n * one appears.\n *\n * Nothing here is new UI: an image opens the SDK's lightbox, a video and an audio\n * clip use the SDK's players, and a file is a download row. That composition is\n * the whole point — every app was writing it again, the same way.\n *\n * The file row is the one place a sender's URL becomes an `href`, so it goes\n * through {@link safeAttachmentUrl}: `http:`, `https:`, `blob:` and relative URLs\n * are linked, anything else renders the same row without a link (#447). Media\n * `src` is left as given — an `<img>`, `<video>` or `<audio>` does not run a\n * `javascript:` URL.\n *\n * @param props - The attachments and the locale strings.\n * @returns The rendered attachment list.\n */\nexport function ChatAttachments({ attachments, strings }: ChatAttachmentsProps) {\n const images = attachments.filter((attachment) => attachment.kind === \"image\");\n const [lightboxIndex, setLightboxIndex] = useState<number | null>(null);\n\n return (\n <div className={styles.attachments}>\n {attachments.map((attachment, index) => {\n const key = `${attachment.kind}-${attachment.url}-${index}`;\n if (attachment.kind === \"image\") {\n const position = images.indexOf(attachment);\n return (\n <button\n key={key}\n type=\"button\"\n className={styles.imageAttachment}\n onClick={() => setLightboxIndex(position)}\n >\n <img\n src={attachment.thumbnailUrl ?? attachment.url}\n alt={attachment.alt ?? attachment.name ?? \"\"}\n loading=\"lazy\"\n />\n </button>\n );\n }\n if (attachment.kind === \"video\") {\n return (\n <VideoPlayer\n key={key}\n src={attachment.url}\n durationMs={attachment.durationMs}\n poster={attachment.thumbnailUrl}\n className={styles.videoAttachment}\n />\n );\n }\n if (attachment.kind === \"voice\") {\n return (\n <div key={key} className={styles.voiceAttachment}>\n <div className={styles.waveform} aria-hidden=\"true\">\n {(attachment.waveform ?? FLAT_WAVEFORM).map((peak, bar) => (\n <span\n key={bar}\n style={{\n height: `${Math.max(8, Math.min(100, peak * 100))}%`,\n }}\n />\n ))}\n </div>\n <AudioPlayer\n src={attachment.url}\n durationMs={attachment.durationMs}\n aria-label={strings.voiceNote}\n />\n </div>\n );\n }\n if (attachment.kind === \"audio\") {\n return (\n <AudioPlayer\n key={key}\n src={attachment.url}\n durationMs={attachment.durationMs}\n />\n );\n }\n const href = safeAttachmentUrl(attachment.url);\n const row = (\n <>\n <span className={styles.fileName}>\n {attachment.name ?? strings.download}\n </span>\n <span className={styles.fileMeta}>\n {[\n attachment.sizeBytes !== undefined\n ? formatBytes(attachment.sizeBytes)\n : null,\n attachment.durationMs !== undefined\n ? durationLabel(attachment.durationMs)\n : null,\n ]\n .filter(Boolean)\n .join(\" · \")}\n </span>\n </>\n );\n return href ? (\n <a\n key={key}\n className={styles.fileAttachment}\n href={href}\n download={attachment.name ?? true}\n >\n {row}\n </a>\n ) : (\n <div key={key} className={styles.fileAttachment}>\n {row}\n </div>\n );\n })}\n\n {images.length > 0 && (\n <Lightbox\n items={images.map((image) => ({\n src: image.url,\n alt: image.alt ?? image.name ?? \"\",\n thumbnail: image.thumbnailUrl,\n }))}\n open={lightboxIndex !== null}\n index={lightboxIndex ?? 0}\n onIndexChange={setLightboxIndex}\n onClose={() => setLightboxIndex(null)}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import r from "
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
1
|
+
import { safeAttachmentUrl as e } from "../../utils/safe-url.js";
|
|
2
|
+
import { formatBytes as t } from "../../utils/numbers.js";
|
|
3
|
+
import { VideoPlayer as n } from "../VideoPlayer/VideoPlayer.js";
|
|
4
|
+
import { Lightbox as r } from "../Lightbox/Lightbox.js";
|
|
5
|
+
import i from "./Chat.module.js";
|
|
6
|
+
import { AudioPlayer as a } from "../AudioPlayer/AudioPlayer.js";
|
|
7
7
|
import { useState as o } from "react";
|
|
8
8
|
import { Fragment as s, jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
9
9
|
//#region src/components/Chat/ChatAttachments.tsx
|
|
@@ -15,61 +15,61 @@ function d(e) {
|
|
|
15
15
|
function f({ attachments: f, strings: p }) {
|
|
16
16
|
let m = f.filter((e) => e.kind === "image"), [h, g] = o(null);
|
|
17
17
|
return /* @__PURE__ */ l("div", {
|
|
18
|
-
className:
|
|
19
|
-
children: [f.map((
|
|
20
|
-
let f = `${
|
|
21
|
-
if (
|
|
22
|
-
let e = m.indexOf(
|
|
18
|
+
className: i.attachments,
|
|
19
|
+
children: [f.map((r, o) => {
|
|
20
|
+
let f = `${r.kind}-${r.url}-${o}`;
|
|
21
|
+
if (r.kind === "image") {
|
|
22
|
+
let e = m.indexOf(r);
|
|
23
23
|
return /* @__PURE__ */ c("button", {
|
|
24
24
|
type: "button",
|
|
25
|
-
className:
|
|
25
|
+
className: i.imageAttachment,
|
|
26
26
|
onClick: () => g(e),
|
|
27
27
|
children: /* @__PURE__ */ c("img", {
|
|
28
|
-
src:
|
|
29
|
-
alt:
|
|
28
|
+
src: r.thumbnailUrl ?? r.url,
|
|
29
|
+
alt: r.alt ?? r.name ?? "",
|
|
30
30
|
loading: "lazy"
|
|
31
31
|
})
|
|
32
32
|
}, f);
|
|
33
33
|
}
|
|
34
|
-
if (
|
|
35
|
-
src:
|
|
36
|
-
durationMs:
|
|
37
|
-
poster:
|
|
38
|
-
className:
|
|
34
|
+
if (r.kind === "video") return /* @__PURE__ */ c(n, {
|
|
35
|
+
src: r.url,
|
|
36
|
+
durationMs: r.durationMs,
|
|
37
|
+
poster: r.thumbnailUrl,
|
|
38
|
+
className: i.videoAttachment
|
|
39
39
|
}, f);
|
|
40
|
-
if (
|
|
41
|
-
className:
|
|
40
|
+
if (r.kind === "voice") return /* @__PURE__ */ l("div", {
|
|
41
|
+
className: i.voiceAttachment,
|
|
42
42
|
children: [/* @__PURE__ */ c("div", {
|
|
43
|
-
className:
|
|
43
|
+
className: i.waveform,
|
|
44
44
|
"aria-hidden": "true",
|
|
45
|
-
children: (
|
|
46
|
-
}), /* @__PURE__ */ c(
|
|
47
|
-
src:
|
|
48
|
-
durationMs:
|
|
45
|
+
children: (r.waveform ?? u).map((e, t) => /* @__PURE__ */ c("span", { style: { height: `${Math.max(8, Math.min(100, e * 100))}%` } }, t))
|
|
46
|
+
}), /* @__PURE__ */ c(a, {
|
|
47
|
+
src: r.url,
|
|
48
|
+
durationMs: r.durationMs,
|
|
49
49
|
"aria-label": p.voiceNote
|
|
50
50
|
})]
|
|
51
51
|
}, f);
|
|
52
|
-
if (
|
|
53
|
-
src:
|
|
54
|
-
durationMs:
|
|
52
|
+
if (r.kind === "audio") return /* @__PURE__ */ c(a, {
|
|
53
|
+
src: r.url,
|
|
54
|
+
durationMs: r.durationMs
|
|
55
55
|
}, f);
|
|
56
|
-
let h =
|
|
57
|
-
className:
|
|
58
|
-
children:
|
|
56
|
+
let h = e(r.url), _ = /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ c("span", {
|
|
57
|
+
className: i.fileName,
|
|
58
|
+
children: r.name ?? p.download
|
|
59
59
|
}), /* @__PURE__ */ c("span", {
|
|
60
|
-
className:
|
|
61
|
-
children: [
|
|
60
|
+
className: i.fileMeta,
|
|
61
|
+
children: [r.sizeBytes === void 0 ? null : t(r.sizeBytes), r.durationMs === void 0 ? null : d(r.durationMs)].filter(Boolean).join(" · ")
|
|
62
62
|
})] });
|
|
63
63
|
return h ? /* @__PURE__ */ c("a", {
|
|
64
|
-
className:
|
|
64
|
+
className: i.fileAttachment,
|
|
65
65
|
href: h,
|
|
66
|
-
download:
|
|
66
|
+
download: r.name ?? !0,
|
|
67
67
|
children: _
|
|
68
68
|
}, f) : /* @__PURE__ */ c("div", {
|
|
69
|
-
className:
|
|
69
|
+
className: i.fileAttachment,
|
|
70
70
|
children: _
|
|
71
71
|
}, f);
|
|
72
|
-
}), m.length > 0 && /* @__PURE__ */ c(
|
|
72
|
+
}), m.length > 0 && /* @__PURE__ */ c(r, {
|
|
73
73
|
items: m.map((e) => ({
|
|
74
74
|
src: e.url,
|
|
75
75
|
alt: e.alt ?? e.name ?? "",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../utils/cn.cjs"),t=require("./Form.module.cjs");let
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("../Layout/gap.cjs"),n=require("./Form.module.cjs");let r=require("react"),i=require("react/jsx-runtime");function a(e){return e===void 0?`repeat(2, minmax(0, 1fr))`:typeof e==`number`?`repeat(${e}, minmax(0, 1fr))`:e}var o=(0,r.forwardRef)(function({layout:r=`stack`,columns:o,gap:s,className:c,style:l,children:u,...d},f){let p={gap:t.resolveGap(`Form`,s??4),...r===`grid`?{gridTemplateColumns:a(o)}:null,...l};return(0,i.jsx)(`form`,{ref:f,className:e.cn(n.default.form,n.default[r],c),style:p,...d,children:u})});function s({title:r,description:o,layout:s=`stack`,columns:c,gap:l,className:u,style:d,children:f,...p}){let m={gap:t.resolveGap(`FormSection`,l??4),...s===`grid`?{gridTemplateColumns:a(c)}:null};return(0,i.jsxs)(`section`,{className:e.cn(n.default.section,u),style:d,...p,children:[(r||o)&&(0,i.jsxs)(`header`,{className:n.default.sectionHeader,children:[r&&(0,i.jsx)(`h3`,{className:n.default.sectionTitle,children:r}),o&&(0,i.jsx)(`p`,{className:n.default.sectionDescription,children:o})]}),(0,i.jsx)(`div`,{className:e.cn(n.default.sectionBody,n.default[s]),style:m,children:f})]})}function c({gap:r,className:a,style:o,children:s,...c}){let l={gap:t.resolveGap(`FormRow`,r??3),...o};return(0,i.jsx)(`div`,{className:e.cn(n.default.row,a),style:l,...c,children:s})}function l({align:r=`end`,gap:a,className:o,style:s,children:c,...l}){let u={gap:t.resolveGap(`FormActions`,a??2),...s};return(0,i.jsx)(`div`,{className:e.cn(n.default.actions,n.default[r],o),style:u,...l,children:c})}exports.Form=o,exports.FormActions=l,exports.FormRow=c,exports.FormSection=s;
|
|
2
2
|
//# sourceMappingURL=Form.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Form.cjs","names":["forwardRef","cn","styles","require_cn","require_Form_module","react","react_jsx_runtime","
|
|
1
|
+
{"version":3,"file":"Form.cjs","names":["forwardRef","resolveGap","cn","styles","require_cn","require_gap","require_Form_module","react","react_jsx_runtime","resolveColumns","columns","Form","props","finalStyle","gap","layout","style","className","FormSection","bodyStyle","title","description","FormRow","FormActions","align"],"sources":["../../../src/components/Form/Form.tsx"],"sourcesContent":["import { forwardRef } from \"react\";\nimport type { CSSProperties, FormHTMLAttributes, HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { resolveGap } from \"../Layout/gap\";\nimport styles from \"./Form.module.css\";\n\nexport type FormLayout = \"stack\" | \"inline\" | \"grid\";\nexport type FormActionsAlign = \"start\" | \"center\" | \"end\" | \"between\";\n\ninterface LayoutProps {\n /** Field arrangement. `stack` stacks fields vertically, `inline` lines them up in a wrapping row, `grid` arranges them in `columns` equal-width tracks. Default `stack`. */\n layout?: FormLayout;\n /** Number of columns when `layout=\"grid\"`, or a custom `grid-template-columns` value. Default `2`. */\n columns?: number | string;\n /** Gap between fields. Numbers map to a multiple of the 4px scale (default `4` → 16px). A number is a step, not pixels (`6` → 24px): above `12` a development build warns once, and a string such as `\"96px\"` passes through untouched. */\n gap?: number | string;\n}\n\nfunction resolveColumns(columns: number | string | undefined): string {\n if (columns === undefined) return \"repeat(2, minmax(0, 1fr))\";\n return typeof columns === \"number\" ? `repeat(${columns}, minmax(0, 1fr))` : columns;\n}\n\nexport interface FormProps extends FormHTMLAttributes<HTMLFormElement>, LayoutProps {}\n\n/**\n * Form wrapper with a built-in layout variant. Replaces ad-hoc `<form>` +\n * `<Stack>` / `<Grid>` boilerplate; pair with [[FormRow]], [[FormSection]],\n * and [[FormActions]] for richer layouts.\n *\n * @example\n * <Form layout=\"grid\" columns={2} gap={4} onSubmit={form.handleSubmit(save)}>\n * <Input label=\"Nome\" {...form.register(\"name\")} />\n * <Input label=\"Email\" {...form.register(\"email\")} />\n * <FormActions align=\"end\">\n * <Button type=\"submit\">Salvar</Button>\n * </FormActions>\n * </Form>\n */\nexport const Form = forwardRef<HTMLFormElement, FormProps>(function Form(\n { layout = \"stack\", columns, gap, className, style, children, ...props },\n ref,\n) {\n const finalStyle: CSSProperties = {\n gap: resolveGap(\"Form\", gap ?? 4),\n ...(layout === \"grid\" ? { gridTemplateColumns: resolveColumns(columns) } : null),\n ...style,\n };\n return (\n <form\n ref={ref}\n className={cn(styles.form, styles[layout], className)}\n style={finalStyle}\n {...props}\n >\n {children}\n </form>\n );\n});\n\nexport interface FormSectionProps extends Omit<HTMLAttributes<HTMLElement>, \"title\">, LayoutProps {\n title?: ReactNode;\n description?: ReactNode;\n}\n\n/**\n * Visually grouped subset of fields with an optional title and description.\n * Has its own `layout`/`columns`/`gap` so subsets can stack while the parent\n * grids (or vice versa).\n *\n * The header is deliberately not a `SectionHeader`: a field group sits one step\n * below a page section, and its title is `text-lg` / semibold / `leading-snug`\n * with `space-3` below, against `text-2xl` / bold / `leading-tight` with\n * `space-5` for `SectionHeader`. Sharing the component would either grow every\n * form group to page-section size or add a size switch to `SectionHeader` that\n * only this caller uses.\n *\n * @example\n * <FormSection title=\"Endereço\" description=\"Usado para entrega\" layout=\"grid\" columns={3}>\n * <Input label=\"CEP\" />\n * <Input label=\"Rua\" />\n * <Input label=\"Número\" />\n * </FormSection>\n */\nexport function FormSection({\n title,\n description,\n layout = \"stack\",\n columns,\n gap,\n className,\n style,\n children,\n ...props\n}: FormSectionProps) {\n const bodyStyle: CSSProperties = {\n gap: resolveGap(\"FormSection\", gap ?? 4),\n ...(layout === \"grid\" ? { gridTemplateColumns: resolveColumns(columns) } : null),\n };\n return (\n <section className={cn(styles.section, className)} style={style} {...props}>\n {(title || description) && (\n <header className={styles.sectionHeader}>\n {title && <h3 className={styles.sectionTitle}>{title}</h3>}\n {description && <p className={styles.sectionDescription}>{description}</p>}\n </header>\n )}\n <div className={cn(styles.sectionBody, styles[layout])} style={bodyStyle}>\n {children}\n </div>\n </section>\n );\n}\n\nexport interface FormRowProps extends HTMLAttributes<HTMLDivElement> {\n /** Gap between row items. Numbers map to a multiple of the 4px scale (default `3` → 12px). A number is a step, not pixels (`6` → 24px): above `12` a development build warns once, and a string such as `\"96px\"` passes through untouched. */\n gap?: number | string;\n}\n\n/**\n * Forces a horizontal row regardless of parent layout — useful for grouping\n * two short fields side-by-side inside an otherwise stacked form (e.g. CEP +\n * city, expiry month + year).\n *\n * @example\n * <Form layout=\"stack\">\n * <FormRow>\n * <Input label=\"CEP\" />\n * <Input label=\"Cidade\" />\n * </FormRow>\n * <Input label=\"Endereço completo\" />\n * </Form>\n */\nexport function FormRow({ gap, className, style, children, ...props }: FormRowProps) {\n const finalStyle: CSSProperties = { gap: resolveGap(\"FormRow\", gap ?? 3), ...style };\n return (\n <div className={cn(styles.row, className)} style={finalStyle} {...props}>\n {children}\n </div>\n );\n}\n\nexport interface FormActionsProps extends HTMLAttributes<HTMLDivElement> {\n /** Horizontal alignment of buttons. Default `end`. */\n align?: FormActionsAlign;\n /** Gap between buttons. Numbers map to a multiple of the 4px scale (default `2` → 8px). A number is a step, not pixels (`6` → 24px): above `12` a development build warns once, and a string such as `\"96px\"` passes through untouched. */\n gap?: number | string;\n}\n\n/**\n * Footer button row for a form. Use inside (or after) [[Form]] / [[FormSection]].\n *\n * @example\n * <FormActions align=\"end\">\n * <Button variant=\"ghost\" onClick={onCancel}>Cancelar</Button>\n * <Button type=\"submit\" loading={form.formState.isSubmitting}>Salvar</Button>\n * </FormActions>\n */\nexport function FormActions({\n align = \"end\",\n gap,\n className,\n style,\n children,\n ...props\n}: FormActionsProps) {\n const finalStyle: CSSProperties = { gap: resolveGap(\"FormActions\", gap ?? 2), ...style };\n return (\n <div className={cn(styles.actions, styles[align], className)} style={finalStyle} {...props}>\n {children}\n </div>\n );\n}\n"],"mappings":"A,0JAkBA,SAASS,EAAe,EAA8C,CAElE,OADIC,IAAY,IAAA,GAAkB,4BAC3B,OAAOA,GAAY,SAAW,UAAUA,EAAQ,mBAAqBA,CAChF,CAkBA,IAAaC,GAAA,EAAOX,EAAAA,WAAAA,CAAuC,SACvD,CAAE,SAAS,QAAS,UAAS,MAAK,YAAW,QAAO,WAAU,GAAGY,GACjE,EACF,CACE,IAAMC,EAA4B,CAC9B,IAAKZ,EAAAA,WAAW,OAAQa,GAAO,CAAC,EAChC,GAAIC,IAAW,OAAS,CAAE,oBAAqBN,EAAeC,CAAO,CAAE,EAAI,KAC3E,GAAGM,CACP,EACA,OACI,EAAAR,EAAA,IAAA,CAAC,OAAD,CACS,MACL,UAAWN,EAAAA,GAAGC,EAAAA,QAAO,KAAMA,EAAAA,QAAOY,GAASE,CAAS,EACpD,MAAOJ,EACP,GAAID,EAEH,UACC,CAAA,CAEd,CAAC,EA0BD,SAAgBM,EAAY,CACxB,QACA,cACA,SAAS,QACT,UACA,MACA,YACA,QACA,WACA,GAAGN,GACc,CACjB,IAAMO,EAA2B,CAC7B,IAAKlB,EAAAA,WAAW,cAAea,GAAO,CAAC,EACvC,GAAIC,IAAW,OAAS,CAAE,oBAAqBN,EAAeC,CAAO,CAAE,EAAI,IAC/E,EACA,OACI,EAAAF,EAAA,KAAA,CAAC,UAAD,CAAS,UAAWN,EAAAA,GAAGC,EAAAA,QAAO,QAASc,CAAS,EAAU,QAAO,GAAIL,EAArE,SAAA,EACMQ,GAASC,KACP,EAAAb,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAWL,EAAAA,QAAO,cAA1B,SAAA,CACKiB,IAAS,EAAAZ,EAAA,IAAA,CAAC,KAAD,CAAI,UAAWL,EAAAA,QAAO,aAAe,SAAAiB,CAAU,CAAA,EACxDC,IAAe,EAAAb,EAAA,IAAA,CAAC,IAAD,CAAG,UAAWL,EAAAA,QAAO,mBAAqB,SAAAkB,CAAe,CAAA,CACrE,CAEZ,CAAA,GAAA,EAAAb,EAAA,IAAA,CAAC,MAAD,CAAK,UAAWN,EAAAA,GAAGC,EAAAA,QAAO,YAAaA,EAAAA,QAAOY,EAAO,EAAG,MAAOI,EAC1D,UACA,CAAA,CACA,C,EAEjB,CAqBA,SAAgBG,EAAQ,CAAE,MAAK,YAAW,QAAO,WAAU,GAAGV,GAAuB,CACjF,IAAMC,EAA4B,CAAE,IAAKZ,EAAAA,WAAW,UAAWa,GAAO,CAAC,EAAG,GAAGE,CAAM,EACnF,OACI,EAAAR,EAAA,IAAA,CAAC,MAAD,CAAK,UAAWN,EAAAA,GAAGC,EAAAA,QAAO,IAAKc,CAAS,EAAG,MAAOJ,EAAY,GAAID,EAC7D,UACA,CAAA,CAEb,CAkBA,SAAgBW,EAAY,CACxB,QAAQ,MACR,MACA,YACA,QACA,WACA,GAAGX,GACc,CACjB,IAAMC,EAA4B,CAAE,IAAKZ,EAAAA,WAAW,cAAea,GAAO,CAAC,EAAG,GAAGE,CAAM,EACvF,OACI,EAAAR,EAAA,IAAA,CAAC,MAAD,CAAK,UAAWN,EAAAA,GAAGC,EAAAA,QAAO,QAASA,EAAAA,QAAOqB,GAAQP,CAAS,EAAG,MAAOJ,EAAY,GAAID,EAChF,UACA,CAAA,CAEb,C"}
|