@brycks/core-front 0.6.0 → 0.7.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/dist/components/form/Textarea/Textarea.cjs +1 -1
- package/dist/components/form/Textarea/Textarea.cjs.map +1 -1
- package/dist/components/form/Textarea/Textarea.js +57 -52
- package/dist/components/form/Textarea/Textarea.js.map +1 -1
- package/dist/components/primitives/Button/Button.cjs +1 -1
- package/dist/components/primitives/Button/Button.cjs.map +1 -1
- package/dist/components/primitives/Button/Button.js +47 -43
- package/dist/components/primitives/Button/Button.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("react/jsx-runtime"),e=require("react"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("react/jsx-runtime"),e=require("react"),H=require("../../../utils/styles.cjs"),O={sm:{fontSize:13,padding:10,lineHeight:1.5},md:{fontSize:14,padding:12,lineHeight:1.5},lg:{fontSize:15,padding:14,lineHeight:1.5}},b=e.forwardRef(function({size:x="md",variant:k="outline",isInvalid:u,autoResize:n,minRows:S=3,maxRows:w=10,showCount:C,maxLength:r,className:j,style:T,testId:N,disabled:c,value:i,defaultValue:o,onChange:$,...q},a){const[p,d]=e.useState(0),y=e.useRef(null),v=e.useCallback(t=>{y.current=t,typeof a=="function"?a(t):a&&(a.current=t)},[a]),s=O[x],f=s.fontSize*s.lineHeight,l=f*S+s.padding*2,g=f*w+s.padding*2,m=e.useCallback(()=>{const t=y.current;if(!t||!n)return;t.style.height="auto";const E=t.scrollHeight,P=Math.min(Math.max(E,l),g);t.style.height=`${P}px`},[n,l,g]);e.useEffect(()=>{m()},[i,m]),e.useEffect(()=>{const t=String(i??o??"");d(t.length)},[i,o]);const z={minHeight:l,maxHeight:n?g:void 0,resize:c||n?"none":"vertical"},M=t=>{d(t.target.value.length),$?.(t)},R=p>=(r??1/0);return h.jsxs("div",{className:H.cx("brycks-textarea-wrapper",`brycks-textarea-wrapper--${k}`,`brycks-textarea-wrapper--${x}`,u&&"brycks-textarea-wrapper--invalid",c&&"brycks-textarea-wrapper--disabled",j),style:T,children:[h.jsx("textarea",{ref:v,className:"brycks-textarea",style:z,disabled:c,"aria-invalid":u,"data-testid":N,value:i,defaultValue:o,maxLength:r,onChange:M,...q}),C&&h.jsxs("span",{className:H.cx("brycks-textarea-count",R&&"brycks-textarea-count--limit"),children:[p,r?`/${r}`:""]})]})});b.displayName="Textarea";exports.Textarea=b;
|
|
2
2
|
//# sourceMappingURL=Textarea.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Textarea.cjs","sources":["../../../../src/components/form/Textarea/Textarea.tsx"],"sourcesContent":["/**\n * Textarea Component\n *\n * Multi-line text input with Apple-inspired styling.\n * Supports auto-resize and character counting.\n */\n\nimport {\n forwardRef,\n useState,\n useEffect,\n useRef,\n useCallback,\n type CSSProperties,\n type TextareaHTMLAttributes,\n} from 'react'\nimport { cx } from '../../../utils/styles'\n\nexport type TextareaSize = 'sm' | 'md' | 'lg'\nexport type TextareaVariant = 'outline' | 'filled'\n\nexport interface TextareaProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'> {\n /** Textarea size */\n size?: TextareaSize\n /** Visual variant */\n variant?: TextareaVariant\n /** Error state */\n isInvalid?: boolean\n /** Auto resize based on content */\n autoResize?: boolean\n /** Minimum rows */\n minRows?: number\n /** Maximum rows */\n maxRows?: number\n /** Show character count */\n showCount?: boolean\n /** Max character length */\n maxLength?: number\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nconst sizeStyles: Record<TextareaSize, { fontSize: number; padding: number; lineHeight: number }> = {\n sm: { fontSize: 13, padding: 10, lineHeight: 1.5 },\n md: { fontSize: 14, padding: 12, lineHeight: 1.5 },\n lg: { fontSize: 15, padding: 14, lineHeight: 1.5 },\n}\n\nexport const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea(\n {\n size = 'md',\n variant = 'outline',\n isInvalid,\n autoResize,\n minRows = 3,\n maxRows = 10,\n showCount,\n maxLength,\n className,\n style,\n testId,\n disabled,\n value,\n defaultValue,\n onChange,\n ...props\n },\n ref\n) {\n const [charCount, setCharCount] = useState(0)\n const internalRef = useRef<HTMLTextAreaElement>(null)\n const
|
|
1
|
+
{"version":3,"file":"Textarea.cjs","sources":["../../../../src/components/form/Textarea/Textarea.tsx"],"sourcesContent":["/**\n * Textarea Component\n *\n * Multi-line text input with Apple-inspired styling.\n * Supports auto-resize and character counting.\n */\n\nimport {\n forwardRef,\n useState,\n useEffect,\n useRef,\n useCallback,\n type CSSProperties,\n type TextareaHTMLAttributes,\n} from 'react'\nimport { cx } from '../../../utils/styles'\n\nexport type TextareaSize = 'sm' | 'md' | 'lg'\nexport type TextareaVariant = 'outline' | 'filled'\n\nexport interface TextareaProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'> {\n /** Textarea size */\n size?: TextareaSize\n /** Visual variant */\n variant?: TextareaVariant\n /** Error state */\n isInvalid?: boolean\n /** Auto resize based on content */\n autoResize?: boolean\n /** Minimum rows */\n minRows?: number\n /** Maximum rows */\n maxRows?: number\n /** Show character count */\n showCount?: boolean\n /** Max character length */\n maxLength?: number\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nconst sizeStyles: Record<TextareaSize, { fontSize: number; padding: number; lineHeight: number }> = {\n sm: { fontSize: 13, padding: 10, lineHeight: 1.5 },\n md: { fontSize: 14, padding: 12, lineHeight: 1.5 },\n lg: { fontSize: 15, padding: 14, lineHeight: 1.5 },\n}\n\nexport const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea(\n {\n size = 'md',\n variant = 'outline',\n isInvalid,\n autoResize,\n minRows = 3,\n maxRows = 10,\n showCount,\n maxLength,\n className,\n style,\n testId,\n disabled,\n value,\n defaultValue,\n onChange,\n ...props\n },\n ref\n) {\n const [charCount, setCharCount] = useState(0)\n const internalRef = useRef<HTMLTextAreaElement>(null)\n // Merge the forwarded ref (object OR callback) with our internal ref. The old\n // `(ref as RefObject) || internalRef` broke with callback refs: a function ref\n // is truthy, so `.current` was undefined and autoResize silently stopped.\n const setTextareaRef = useCallback(\n (node: HTMLTextAreaElement | null) => {\n internalRef.current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) (ref as React.MutableRefObject<HTMLTextAreaElement | null>).current = node\n },\n [ref]\n )\n\n const sizeValue = sizeStyles[size]\n const lineHeightPx = sizeValue.fontSize * sizeValue.lineHeight\n const minHeight = lineHeightPx * minRows + sizeValue.padding * 2\n const maxHeight = lineHeightPx * maxRows + sizeValue.padding * 2\n\n const adjustHeight = useCallback(() => {\n const textarea = internalRef.current\n if (!textarea || !autoResize) return\n\n textarea.style.height = 'auto'\n const scrollHeight = textarea.scrollHeight\n const newHeight = Math.min(Math.max(scrollHeight, minHeight), maxHeight)\n textarea.style.height = `${newHeight}px`\n }, [autoResize, minHeight, maxHeight])\n\n useEffect(() => {\n adjustHeight()\n }, [value, adjustHeight])\n\n useEffect(() => {\n const initialValue = String(value ?? defaultValue ?? '')\n setCharCount(initialValue.length)\n }, [value, defaultValue])\n\n // Only the computed min/max height (derived from rows + font metrics) and the\n // resize mode are dynamic; the rest of the visual styling lives in CSS.\n const textareaStyle: CSSProperties = {\n minHeight,\n maxHeight: autoResize ? maxHeight : undefined,\n resize: disabled || autoResize ? 'none' : 'vertical',\n }\n\n const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setCharCount(e.target.value.length)\n onChange?.(e)\n }\n\n const atLimit = charCount >= (maxLength ?? Infinity)\n\n return (\n <div\n className={cx(\n 'brycks-textarea-wrapper',\n `brycks-textarea-wrapper--${variant}`,\n `brycks-textarea-wrapper--${size}`,\n isInvalid && 'brycks-textarea-wrapper--invalid',\n disabled && 'brycks-textarea-wrapper--disabled',\n className\n )}\n style={style}\n >\n <textarea\n ref={setTextareaRef}\n className=\"brycks-textarea\"\n style={textareaStyle}\n disabled={disabled}\n aria-invalid={isInvalid}\n data-testid={testId}\n value={value}\n defaultValue={defaultValue}\n maxLength={maxLength}\n onChange={handleChange}\n {...props}\n />\n\n {showCount && (\n <span className={cx('brycks-textarea-count', atLimit && 'brycks-textarea-count--limit')}>\n {charCount}{maxLength ? `/${maxLength}` : ''}\n </span>\n )}\n </div>\n )\n})\n\nTextarea.displayName = 'Textarea'\n"],"names":["sizeStyles","Textarea","forwardRef","size","variant","isInvalid","autoResize","minRows","maxRows","showCount","maxLength","className","style","testId","disabled","value","defaultValue","onChange","props","ref","charCount","setCharCount","useState","internalRef","useRef","setTextareaRef","useCallback","node","sizeValue","lineHeightPx","minHeight","maxHeight","adjustHeight","textarea","scrollHeight","newHeight","useEffect","initialValue","textareaStyle","handleChange","e","atLimit","jsxs","cx","jsx"],"mappings":"+KA4CMA,EAA8F,CAClG,GAAI,CAAE,SAAU,GAAI,QAAS,GAAI,WAAY,GAAA,EAC7C,GAAI,CAAE,SAAU,GAAI,QAAS,GAAI,WAAY,GAAA,EAC7C,GAAI,CAAE,SAAU,GAAI,QAAS,GAAI,WAAY,GAAA,CAC/C,EAEaC,EAAWC,EAAAA,WAA+C,SACrE,CACE,KAAAC,EAAO,KACP,QAAAC,EAAU,UACV,UAAAC,EACA,WAAAC,EACA,QAAAC,EAAU,EACV,QAAAC,EAAU,GACV,UAAAC,EACA,UAAAC,EACA,UAAAC,EACA,MAAAC,EACA,OAAAC,EACA,SAAAC,EACA,MAAAC,EACA,aAAAC,EACA,SAAAC,EACA,GAAGC,CACL,EACAC,EACA,CACA,KAAM,CAACC,EAAWC,CAAY,EAAIC,EAAAA,SAAS,CAAC,EACtCC,EAAcC,EAAAA,OAA4B,IAAI,EAI9CC,EAAiBC,EAAAA,YACpBC,GAAqC,CACpCJ,EAAY,QAAUI,EAClB,OAAOR,GAAQ,WAAYA,EAAIQ,CAAI,EAC9BR,IAAMA,EAA2D,QAAUQ,EACtF,EACA,CAACR,CAAG,CAAA,EAGAS,EAAY5B,EAAWG,CAAI,EAC3B0B,EAAeD,EAAU,SAAWA,EAAU,WAC9CE,EAAYD,EAAetB,EAAUqB,EAAU,QAAU,EACzDG,EAAYF,EAAerB,EAAUoB,EAAU,QAAU,EAEzDI,EAAeN,EAAAA,YAAY,IAAM,CACrC,MAAMO,EAAWV,EAAY,QAC7B,GAAI,CAACU,GAAY,CAAC3B,EAAY,OAE9B2B,EAAS,MAAM,OAAS,OACxB,MAAMC,EAAeD,EAAS,aACxBE,EAAY,KAAK,IAAI,KAAK,IAAID,EAAcJ,CAAS,EAAGC,CAAS,EACvEE,EAAS,MAAM,OAAS,GAAGE,CAAS,IACtC,EAAG,CAAC7B,EAAYwB,EAAWC,CAAS,CAAC,EAErCK,EAAAA,UAAU,IAAM,CACdJ,EAAA,CACF,EAAG,CAACjB,EAAOiB,CAAY,CAAC,EAExBI,EAAAA,UAAU,IAAM,CACd,MAAMC,EAAe,OAAOtB,GAASC,GAAgB,EAAE,EACvDK,EAAagB,EAAa,MAAM,CAClC,EAAG,CAACtB,EAAOC,CAAY,CAAC,EAIxB,MAAMsB,EAA+B,CACnC,UAAAR,EACA,UAAWxB,EAAayB,EAAY,OACpC,OAAQjB,GAAYR,EAAa,OAAS,UAAA,EAGtCiC,EAAgBC,GAA8C,CAClEnB,EAAamB,EAAE,OAAO,MAAM,MAAM,EAClCvB,IAAWuB,CAAC,CACd,EAEMC,EAAUrB,IAAcV,GAAa,KAE3C,OACEgC,EAAAA,KAAC,MAAA,CACC,UAAWC,EAAAA,GACT,0BACA,4BAA4BvC,CAAO,GACnC,4BAA4BD,CAAI,GAChCE,GAAa,mCACbS,GAAY,oCACZH,CAAA,EAEF,MAAAC,EAEA,SAAA,CAAAgC,EAAAA,IAAC,WAAA,CACC,IAAKnB,EACL,UAAU,kBACV,MAAOa,EACP,SAAAxB,EACA,eAAcT,EACd,cAAaQ,EACb,MAAAE,EACA,aAAAC,EACA,UAAAN,EACA,SAAU6B,EACT,GAAGrB,CAAA,CAAA,EAGLT,UACE,OAAA,CAAK,UAAWkC,KAAG,wBAAyBF,GAAW,8BAA8B,EACnF,SAAA,CAAArB,EAAWV,EAAY,IAAIA,CAAS,GAAK,EAAA,CAAA,CAC5C,CAAA,CAAA,CAAA,CAIR,CAAC,EAEDT,EAAS,YAAc"}
|
|
@@ -1,80 +1,85 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { forwardRef as
|
|
3
|
-
import { cx as
|
|
1
|
+
import { jsxs as y, jsx as A } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef as B, useState as D, useRef as F, useCallback as u, useEffect as H } from "react";
|
|
3
|
+
import { cx as k } from "../../../utils/styles.js";
|
|
4
4
|
const G = {
|
|
5
5
|
sm: { fontSize: 13, padding: 10, lineHeight: 1.5 },
|
|
6
6
|
md: { fontSize: 14, padding: 12, lineHeight: 1.5 },
|
|
7
7
|
lg: { fontSize: 15, padding: 14, lineHeight: 1.5 }
|
|
8
|
-
}, I =
|
|
8
|
+
}, I = B(function({
|
|
9
9
|
size: h = "md",
|
|
10
|
-
variant:
|
|
10
|
+
variant: b = "outline",
|
|
11
11
|
isInvalid: g,
|
|
12
|
-
autoResize:
|
|
13
|
-
minRows:
|
|
14
|
-
maxRows:
|
|
15
|
-
showCount:
|
|
16
|
-
maxLength:
|
|
17
|
-
className:
|
|
18
|
-
style:
|
|
19
|
-
testId:
|
|
20
|
-
disabled:
|
|
12
|
+
autoResize: a,
|
|
13
|
+
minRows: S = 3,
|
|
14
|
+
maxRows: w = 10,
|
|
15
|
+
showCount: C,
|
|
16
|
+
maxLength: n,
|
|
17
|
+
className: N,
|
|
18
|
+
style: T,
|
|
19
|
+
testId: $,
|
|
20
|
+
disabled: s,
|
|
21
21
|
value: r,
|
|
22
|
-
defaultValue:
|
|
23
|
-
onChange:
|
|
24
|
-
...
|
|
25
|
-
},
|
|
26
|
-
const [p, x] =
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
defaultValue: c,
|
|
23
|
+
onChange: j,
|
|
24
|
+
...z
|
|
25
|
+
}, e) {
|
|
26
|
+
const [p, x] = D(0), d = F(null), v = u(
|
|
27
|
+
(t) => {
|
|
28
|
+
d.current = t, typeof e == "function" ? e(t) : e && (e.current = t);
|
|
29
|
+
},
|
|
30
|
+
[e]
|
|
31
|
+
), i = G[h], m = i.fontSize * i.lineHeight, o = m * S + i.padding * 2, l = m * w + i.padding * 2, f = u(() => {
|
|
32
|
+
const t = d.current;
|
|
33
|
+
if (!t || !a) return;
|
|
29
34
|
t.style.height = "auto";
|
|
30
|
-
const
|
|
31
|
-
t.style.height = `${
|
|
32
|
-
}, [
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}, [r,
|
|
36
|
-
const t = String(r ??
|
|
35
|
+
const R = t.scrollHeight, q = Math.min(Math.max(R, o), l);
|
|
36
|
+
t.style.height = `${q}px`;
|
|
37
|
+
}, [a, o, l]);
|
|
38
|
+
H(() => {
|
|
39
|
+
f();
|
|
40
|
+
}, [r, f]), H(() => {
|
|
41
|
+
const t = String(r ?? c ?? "");
|
|
37
42
|
x(t.length);
|
|
38
|
-
}, [r,
|
|
39
|
-
const
|
|
43
|
+
}, [r, c]);
|
|
44
|
+
const M = {
|
|
40
45
|
minHeight: o,
|
|
41
|
-
maxHeight:
|
|
42
|
-
resize:
|
|
43
|
-
},
|
|
44
|
-
x(t.target.value.length),
|
|
45
|
-
},
|
|
46
|
-
return /* @__PURE__ */
|
|
46
|
+
maxHeight: a ? l : void 0,
|
|
47
|
+
resize: s || a ? "none" : "vertical"
|
|
48
|
+
}, E = (t) => {
|
|
49
|
+
x(t.target.value.length), j?.(t);
|
|
50
|
+
}, P = p >= (n ?? 1 / 0);
|
|
51
|
+
return /* @__PURE__ */ y(
|
|
47
52
|
"div",
|
|
48
53
|
{
|
|
49
|
-
className:
|
|
54
|
+
className: k(
|
|
50
55
|
"brycks-textarea-wrapper",
|
|
51
|
-
`brycks-textarea-wrapper--${
|
|
56
|
+
`brycks-textarea-wrapper--${b}`,
|
|
52
57
|
`brycks-textarea-wrapper--${h}`,
|
|
53
58
|
g && "brycks-textarea-wrapper--invalid",
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
s && "brycks-textarea-wrapper--disabled",
|
|
60
|
+
N
|
|
56
61
|
),
|
|
57
|
-
style:
|
|
62
|
+
style: T,
|
|
58
63
|
children: [
|
|
59
|
-
/* @__PURE__ */
|
|
64
|
+
/* @__PURE__ */ A(
|
|
60
65
|
"textarea",
|
|
61
66
|
{
|
|
62
|
-
ref:
|
|
67
|
+
ref: v,
|
|
63
68
|
className: "brycks-textarea",
|
|
64
|
-
style:
|
|
65
|
-
disabled:
|
|
69
|
+
style: M,
|
|
70
|
+
disabled: s,
|
|
66
71
|
"aria-invalid": g,
|
|
67
|
-
"data-testid":
|
|
72
|
+
"data-testid": $,
|
|
68
73
|
value: r,
|
|
69
|
-
defaultValue:
|
|
70
|
-
maxLength:
|
|
71
|
-
onChange:
|
|
72
|
-
...
|
|
74
|
+
defaultValue: c,
|
|
75
|
+
maxLength: n,
|
|
76
|
+
onChange: E,
|
|
77
|
+
...z
|
|
73
78
|
}
|
|
74
79
|
),
|
|
75
|
-
|
|
80
|
+
C && /* @__PURE__ */ y("span", { className: k("brycks-textarea-count", P && "brycks-textarea-count--limit"), children: [
|
|
76
81
|
p,
|
|
77
|
-
|
|
82
|
+
n ? `/${n}` : ""
|
|
78
83
|
] })
|
|
79
84
|
]
|
|
80
85
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Textarea.js","sources":["../../../../src/components/form/Textarea/Textarea.tsx"],"sourcesContent":["/**\n * Textarea Component\n *\n * Multi-line text input with Apple-inspired styling.\n * Supports auto-resize and character counting.\n */\n\nimport {\n forwardRef,\n useState,\n useEffect,\n useRef,\n useCallback,\n type CSSProperties,\n type TextareaHTMLAttributes,\n} from 'react'\nimport { cx } from '../../../utils/styles'\n\nexport type TextareaSize = 'sm' | 'md' | 'lg'\nexport type TextareaVariant = 'outline' | 'filled'\n\nexport interface TextareaProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'> {\n /** Textarea size */\n size?: TextareaSize\n /** Visual variant */\n variant?: TextareaVariant\n /** Error state */\n isInvalid?: boolean\n /** Auto resize based on content */\n autoResize?: boolean\n /** Minimum rows */\n minRows?: number\n /** Maximum rows */\n maxRows?: number\n /** Show character count */\n showCount?: boolean\n /** Max character length */\n maxLength?: number\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nconst sizeStyles: Record<TextareaSize, { fontSize: number; padding: number; lineHeight: number }> = {\n sm: { fontSize: 13, padding: 10, lineHeight: 1.5 },\n md: { fontSize: 14, padding: 12, lineHeight: 1.5 },\n lg: { fontSize: 15, padding: 14, lineHeight: 1.5 },\n}\n\nexport const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea(\n {\n size = 'md',\n variant = 'outline',\n isInvalid,\n autoResize,\n minRows = 3,\n maxRows = 10,\n showCount,\n maxLength,\n className,\n style,\n testId,\n disabled,\n value,\n defaultValue,\n onChange,\n ...props\n },\n ref\n) {\n const [charCount, setCharCount] = useState(0)\n const internalRef = useRef<HTMLTextAreaElement>(null)\n const
|
|
1
|
+
{"version":3,"file":"Textarea.js","sources":["../../../../src/components/form/Textarea/Textarea.tsx"],"sourcesContent":["/**\n * Textarea Component\n *\n * Multi-line text input with Apple-inspired styling.\n * Supports auto-resize and character counting.\n */\n\nimport {\n forwardRef,\n useState,\n useEffect,\n useRef,\n useCallback,\n type CSSProperties,\n type TextareaHTMLAttributes,\n} from 'react'\nimport { cx } from '../../../utils/styles'\n\nexport type TextareaSize = 'sm' | 'md' | 'lg'\nexport type TextareaVariant = 'outline' | 'filled'\n\nexport interface TextareaProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'> {\n /** Textarea size */\n size?: TextareaSize\n /** Visual variant */\n variant?: TextareaVariant\n /** Error state */\n isInvalid?: boolean\n /** Auto resize based on content */\n autoResize?: boolean\n /** Minimum rows */\n minRows?: number\n /** Maximum rows */\n maxRows?: number\n /** Show character count */\n showCount?: boolean\n /** Max character length */\n maxLength?: number\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nconst sizeStyles: Record<TextareaSize, { fontSize: number; padding: number; lineHeight: number }> = {\n sm: { fontSize: 13, padding: 10, lineHeight: 1.5 },\n md: { fontSize: 14, padding: 12, lineHeight: 1.5 },\n lg: { fontSize: 15, padding: 14, lineHeight: 1.5 },\n}\n\nexport const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea(\n {\n size = 'md',\n variant = 'outline',\n isInvalid,\n autoResize,\n minRows = 3,\n maxRows = 10,\n showCount,\n maxLength,\n className,\n style,\n testId,\n disabled,\n value,\n defaultValue,\n onChange,\n ...props\n },\n ref\n) {\n const [charCount, setCharCount] = useState(0)\n const internalRef = useRef<HTMLTextAreaElement>(null)\n // Merge the forwarded ref (object OR callback) with our internal ref. The old\n // `(ref as RefObject) || internalRef` broke with callback refs: a function ref\n // is truthy, so `.current` was undefined and autoResize silently stopped.\n const setTextareaRef = useCallback(\n (node: HTMLTextAreaElement | null) => {\n internalRef.current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) (ref as React.MutableRefObject<HTMLTextAreaElement | null>).current = node\n },\n [ref]\n )\n\n const sizeValue = sizeStyles[size]\n const lineHeightPx = sizeValue.fontSize * sizeValue.lineHeight\n const minHeight = lineHeightPx * minRows + sizeValue.padding * 2\n const maxHeight = lineHeightPx * maxRows + sizeValue.padding * 2\n\n const adjustHeight = useCallback(() => {\n const textarea = internalRef.current\n if (!textarea || !autoResize) return\n\n textarea.style.height = 'auto'\n const scrollHeight = textarea.scrollHeight\n const newHeight = Math.min(Math.max(scrollHeight, minHeight), maxHeight)\n textarea.style.height = `${newHeight}px`\n }, [autoResize, minHeight, maxHeight])\n\n useEffect(() => {\n adjustHeight()\n }, [value, adjustHeight])\n\n useEffect(() => {\n const initialValue = String(value ?? defaultValue ?? '')\n setCharCount(initialValue.length)\n }, [value, defaultValue])\n\n // Only the computed min/max height (derived from rows + font metrics) and the\n // resize mode are dynamic; the rest of the visual styling lives in CSS.\n const textareaStyle: CSSProperties = {\n minHeight,\n maxHeight: autoResize ? maxHeight : undefined,\n resize: disabled || autoResize ? 'none' : 'vertical',\n }\n\n const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setCharCount(e.target.value.length)\n onChange?.(e)\n }\n\n const atLimit = charCount >= (maxLength ?? Infinity)\n\n return (\n <div\n className={cx(\n 'brycks-textarea-wrapper',\n `brycks-textarea-wrapper--${variant}`,\n `brycks-textarea-wrapper--${size}`,\n isInvalid && 'brycks-textarea-wrapper--invalid',\n disabled && 'brycks-textarea-wrapper--disabled',\n className\n )}\n style={style}\n >\n <textarea\n ref={setTextareaRef}\n className=\"brycks-textarea\"\n style={textareaStyle}\n disabled={disabled}\n aria-invalid={isInvalid}\n data-testid={testId}\n value={value}\n defaultValue={defaultValue}\n maxLength={maxLength}\n onChange={handleChange}\n {...props}\n />\n\n {showCount && (\n <span className={cx('brycks-textarea-count', atLimit && 'brycks-textarea-count--limit')}>\n {charCount}{maxLength ? `/${maxLength}` : ''}\n </span>\n )}\n </div>\n )\n})\n\nTextarea.displayName = 'Textarea'\n"],"names":["sizeStyles","Textarea","forwardRef","size","variant","isInvalid","autoResize","minRows","maxRows","showCount","maxLength","className","style","testId","disabled","value","defaultValue","onChange","props","ref","charCount","setCharCount","useState","internalRef","useRef","setTextareaRef","useCallback","node","sizeValue","lineHeightPx","minHeight","maxHeight","adjustHeight","textarea","scrollHeight","newHeight","useEffect","initialValue","textareaStyle","handleChange","e","atLimit","jsxs","cx","jsx"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react/jsx-runtime"),c=require("react"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react/jsx-runtime"),c=require("react"),g=require("./Button.styles.cjs"),z=require("../../../utils/styles.cjs"),C=c.memo(function({size:n}){return t.jsxs("svg",{width:n,height:n,viewBox:"0 0 24 24",fill:"none",className:"brycks-animate-spin","aria-hidden":"true",children:[t.jsx("circle",{cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeDasharray:"31.4 31.4",opacity:"0.25"}),t.jsx("circle",{cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeDasharray:"31.4 31.4",strokeDashoffset:"62.8",style:{transform:"rotate(-90deg)",transformOrigin:"center"}})]})}),q=({as:s,variant:n="solid",intent:h="primary",size:u="md",fullWidth:x,isLoading:e,isDisabled:k,leftIcon:d,rightIcon:y,isIconOnly:o,className:m,style:B,children:b,testId:j,...i},S)=>{const p=s||"button",a=k||e,r=g.sizeConfig[u];typeof process<"u"&&process.env.NODE_ENV!=="production"&&o&&!i["aria-label"]&&!i["aria-labelledby"]&&console.warn("Brycks: an icon-only Button/IconButton has no accessible name. Pass `aria-label` or `aria-labelledby`.");const w={display:"inline-flex",alignItems:"center",justifyContent:"center",...B},v=z.cx("brycks-button",`brycks-button--${n}`,`brycks-button--${h}`,`brycks-button--${u}`,x&&"brycks-button--full-width",o&&"brycks-button--icon-only",e&&"brycks-button--loading",a&&"brycks-button--disabled",m),N=p==="button"||s===void 0?{type:i.type??"button",disabled:a}:{"aria-disabled":a||void 0,role:"button",tabIndex:a?-1:0};return t.jsxs(p,{ref:S,className:v,style:w,"data-testid":j,...N,...i,children:[e&&t.jsx("span",{style:{display:"inline-flex",position:o?"absolute":"relative"},children:t.jsx(C,{size:r.iconSize})}),!e&&d&&t.jsx("span",{style:{display:"inline-flex",width:r.iconSize,height:r.iconSize},children:d}),!o&&t.jsx("span",{style:{opacity:e&&!o?.5:1},children:b}),!e&&y&&t.jsx("span",{style:{display:"inline-flex",width:r.iconSize,height:r.iconSize},children:y}),o&&!e&&b]})},l=c.forwardRef(q);l.displayName="Button";const D=(s,n)=>t.jsx(l,{ref:n,isIconOnly:!0,...s}),f=c.forwardRef(D);f.displayName="IconButton";exports.Button=l;exports.IconButton=f;
|
|
2
2
|
//# sourceMappingURL=Button.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.cjs","sources":["../../../../src/components/primitives/Button/Button.tsx"],"sourcesContent":["/**\r\n * Button Component\r\n *\r\n * A versatile, polymorphic button component with multiple variants,\r\n * sizes, and states. Designed with Apple-inspired aesthetics -\r\n * smooth transitions, tactile feedback, and refined visual details.\r\n *\r\n * All color styles are handled via CSS classes for easy customization.\r\n * Layout styles (display, alignment) use inline styles for flexibility.\r\n */\r\n\r\nimport React, {\r\n forwardRef,\r\n type ElementType,\r\n type ComponentPropsWithRef,\r\n type CSSProperties,\r\n} from 'react'\r\nimport type { ButtonProps } from './Button.types'\r\nimport { sizeConfig } from './Button.styles'\r\nimport { cx } from '../../../utils/styles'\r\n\r\ninterface ButtonComponent {\r\n <E extends ElementType = 'button'>(\r\n props: ButtonProps<E> & { ref?: ComponentPropsWithRef<E>['ref'] }\r\n ): React.JSX.Element | null\r\n displayName?: string\r\n}\r\n\r\n/** Loading spinner component - uses global CSS keyframes */\r\nconst LoadingSpinner = React.memo(function LoadingSpinner({ size }: { size: number }) {\r\n return (\r\n <svg\r\n width={size}\r\n height={size}\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n className=\"brycks-animate-spin\"\r\n aria-hidden=\"true\"\r\n >\r\n <circle\r\n cx=\"12\"\r\n cy=\"12\"\r\n r=\"10\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeDasharray=\"31.4 31.4\"\r\n opacity=\"0.25\"\r\n />\r\n <circle\r\n cx=\"12\"\r\n cy=\"12\"\r\n r=\"10\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeDasharray=\"31.4 31.4\"\r\n strokeDashoffset=\"62.8\"\r\n style={{ transform: 'rotate(-90deg)', transformOrigin: 'center' }}\r\n />\r\n </svg>\r\n )\r\n})\r\n\r\nconst ButtonInner = <E extends ElementType = 'button'>(\r\n {\r\n as,\r\n variant = 'solid',\r\n intent = 'primary',\r\n size = 'md',\r\n fullWidth,\r\n isLoading,\r\n isDisabled,\r\n leftIcon,\r\n rightIcon,\r\n isIconOnly,\r\n className,\r\n style,\r\n children,\r\n testId,\r\n ...props\r\n }: ButtonProps<E>,\r\n ref: React.ForwardedRef<Element>\r\n) => {\r\n const Component = (as || 'button') as ElementType\r\n\r\n const disabled = isDisabled || isLoading\r\n\r\n const sizeValues = sizeConfig[size]\r\n\r\n // Layout-only inline styles (colors handled by CSS classes)\r\n const layoutStyle: CSSProperties = {\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n ...style,\r\n }\r\n\r\n // Build class name - CSS handles all colors and states\r\n const buttonClassName = cx(\r\n 'brycks-button',\r\n `brycks-button--${variant}`,\r\n `brycks-button--${intent}`,\r\n `brycks-button--${size}`,\r\n fullWidth && 'brycks-button--full-width',\r\n isIconOnly && 'brycks-button--icon-only',\r\n isLoading && 'brycks-button--loading',\r\n disabled && 'brycks-button--disabled',\r\n className\r\n )\r\n\r\n const isButton = Component === 'button' || as === undefined\r\n const buttonProps = isButton\r\n ? {\r\n type: (props as { type?: string }).type ?? 'button',\r\n disabled,\r\n }\r\n : {\r\n 'aria-disabled': disabled || undefined,\r\n role: 'button',\r\n tabIndex: disabled ? -1 : 0,\r\n }\r\n\r\n return (\r\n <Component\r\n ref={ref}\r\n className={buttonClassName}\r\n style={layoutStyle}\r\n data-testid={testId}\r\n {...buttonProps}\r\n {...props}\r\n >\r\n {isLoading && (\r\n <span\r\n style={{\r\n display: 'inline-flex',\r\n position: isIconOnly ? 'absolute' : 'relative',\r\n }}\r\n >\r\n <LoadingSpinner size={sizeValues.iconSize} />\r\n </span>\r\n )}\r\n\r\n {!isLoading && leftIcon && (\r\n <span\r\n style={{\r\n display: 'inline-flex',\r\n width: sizeValues.iconSize,\r\n height: sizeValues.iconSize,\r\n }}\r\n >\r\n {leftIcon}\r\n </span>\r\n )}\r\n\r\n {!isIconOnly && (\r\n <span\r\n style={{\r\n opacity: isLoading && !isIconOnly ? 0.5 : 1,\r\n }}\r\n >\r\n {children}\r\n </span>\r\n )}\r\n\r\n {!isLoading && rightIcon && (\r\n <span\r\n style={{\r\n display: 'inline-flex',\r\n width: sizeValues.iconSize,\r\n height: sizeValues.iconSize,\r\n }}\r\n >\r\n {rightIcon}\r\n </span>\r\n )}\r\n\r\n {isIconOnly && !isLoading && children}\r\n </Component>\r\n )\r\n}\r\n\r\nexport const Button = forwardRef(ButtonInner) as ButtonComponent\r\n\r\nButton.displayName = 'Button'\r\n\r\n/** Icon button shorthand */\r\nconst IconButtonInner = <E extends ElementType = 'button'>(\r\n props: Omit<ButtonProps<E>, 'isIconOnly'>,\r\n ref: React.ForwardedRef<Element>\r\n) => {\r\n return <Button ref={ref as React.ForwardedRef<HTMLButtonElement>} isIconOnly {...(props as ButtonProps<'button'>)} />\r\n}\r\n\r\nexport const IconButton = forwardRef(IconButtonInner) as ButtonComponent\r\n\r\nIconButton.displayName = 'IconButton'\r\n"],"names":["LoadingSpinner","React","size","jsxs","jsx","ButtonInner","as","variant","intent","fullWidth","isLoading","isDisabled","leftIcon","rightIcon","isIconOnly","className","style","children","testId","props","ref","Component","disabled","sizeValues","sizeConfig","layoutStyle","buttonClassName","cx","buttonProps","Button","forwardRef","IconButtonInner","IconButton"],"mappings":"gNA6BMA,EAAiBC,EAAM,KAAK,SAAwB,CAAE,KAAAC,GAA0B,CACpF,OACEC,EAAAA,KAAC,MAAA,CACC,MAAOD,EACP,OAAQA,EACR,QAAQ,YACR,KAAK,OACL,UAAU,sBACV,cAAY,OAEZ,SAAA,CAAAE,EAAAA,IAAC,SAAA,CACC,GAAG,KACH,GAAG,KACH,EAAE,KACF,OAAO,eACP,YAAY,IACZ,cAAc,QACd,gBAAgB,YAChB,QAAQ,MAAA,CAAA,EAEVA,EAAAA,IAAC,SAAA,CACC,GAAG,KACH,GAAG,KACH,EAAE,KACF,OAAO,eACP,YAAY,IACZ,cAAc,QACd,gBAAgB,YAChB,iBAAiB,OACjB,MAAO,CAAE,UAAW,iBAAkB,gBAAiB,QAAA,CAAS,CAAA,CAClE,CAAA,CAAA,CAGN,CAAC,EAEKC,EAAc,CAClB,CACE,GAAAC,EACA,QAAAC,EAAU,QACV,OAAAC,EAAS,UACT,KAAAN,EAAO,KACP,UAAAO,EACA,UAAAC,EACA,WAAAC,EACA,SAAAC,EACA,UAAAC,EACA,WAAAC,EACA,UAAAC,EACA,MAAAC,EACA,SAAAC,EACA,OAAAC,EACA,GAAGC,CACL,EACAC,IACG,CACH,MAAMC,EAAaf,GAAM,SAEnBgB,EAAWX,GAAcD,EAEzBa,EAAaC,EAAAA,WAAWtB,CAAI,
|
|
1
|
+
{"version":3,"file":"Button.cjs","sources":["../../../../src/components/primitives/Button/Button.tsx"],"sourcesContent":["/**\r\n * Button Component\r\n *\r\n * A versatile, polymorphic button component with multiple variants,\r\n * sizes, and states. Designed with Apple-inspired aesthetics -\r\n * smooth transitions, tactile feedback, and refined visual details.\r\n *\r\n * All color styles are handled via CSS classes for easy customization.\r\n * Layout styles (display, alignment) use inline styles for flexibility.\r\n */\r\n\r\nimport React, {\r\n forwardRef,\r\n type ElementType,\r\n type ComponentPropsWithRef,\r\n type CSSProperties,\r\n} from 'react'\r\nimport type { ButtonProps } from './Button.types'\r\nimport { sizeConfig } from './Button.styles'\r\nimport { cx } from '../../../utils/styles'\r\n\r\ninterface ButtonComponent {\r\n <E extends ElementType = 'button'>(\r\n props: ButtonProps<E> & { ref?: ComponentPropsWithRef<E>['ref'] }\r\n ): React.JSX.Element | null\r\n displayName?: string\r\n}\r\n\r\n/** Loading spinner component - uses global CSS keyframes */\r\nconst LoadingSpinner = React.memo(function LoadingSpinner({ size }: { size: number }) {\r\n return (\r\n <svg\r\n width={size}\r\n height={size}\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n className=\"brycks-animate-spin\"\r\n aria-hidden=\"true\"\r\n >\r\n <circle\r\n cx=\"12\"\r\n cy=\"12\"\r\n r=\"10\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeDasharray=\"31.4 31.4\"\r\n opacity=\"0.25\"\r\n />\r\n <circle\r\n cx=\"12\"\r\n cy=\"12\"\r\n r=\"10\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeDasharray=\"31.4 31.4\"\r\n strokeDashoffset=\"62.8\"\r\n style={{ transform: 'rotate(-90deg)', transformOrigin: 'center' }}\r\n />\r\n </svg>\r\n )\r\n})\r\n\r\nconst ButtonInner = <E extends ElementType = 'button'>(\r\n {\r\n as,\r\n variant = 'solid',\r\n intent = 'primary',\r\n size = 'md',\r\n fullWidth,\r\n isLoading,\r\n isDisabled,\r\n leftIcon,\r\n rightIcon,\r\n isIconOnly,\r\n className,\r\n style,\r\n children,\r\n testId,\r\n ...props\r\n }: ButtonProps<E>,\r\n ref: React.ForwardedRef<Element>\r\n) => {\r\n const Component = (as || 'button') as ElementType\r\n\r\n const disabled = isDisabled || isLoading\r\n\r\n const sizeValues = sizeConfig[size]\r\n\r\n // Dev-only a11y guard: an icon-only button has no text, so it needs an\r\n // accessible name. Reliable across the polymorphic `as` API where a type\r\n // constraint isn't (the check is stripped from production builds).\r\n if (\r\n typeof process !== 'undefined' &&\r\n process.env.NODE_ENV !== 'production' &&\r\n isIconOnly &&\r\n !(props as Record<string, unknown>)['aria-label'] &&\r\n !(props as Record<string, unknown>)['aria-labelledby']\r\n ) {\r\n console.warn(\r\n 'Brycks: an icon-only Button/IconButton has no accessible name. Pass `aria-label` or `aria-labelledby`.'\r\n )\r\n }\r\n\r\n // Layout-only inline styles (colors handled by CSS classes)\r\n const layoutStyle: CSSProperties = {\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n ...style,\r\n }\r\n\r\n // Build class name - CSS handles all colors and states\r\n const buttonClassName = cx(\r\n 'brycks-button',\r\n `brycks-button--${variant}`,\r\n `brycks-button--${intent}`,\r\n `brycks-button--${size}`,\r\n fullWidth && 'brycks-button--full-width',\r\n isIconOnly && 'brycks-button--icon-only',\r\n isLoading && 'brycks-button--loading',\r\n disabled && 'brycks-button--disabled',\r\n className\r\n )\r\n\r\n const isButton = Component === 'button' || as === undefined\r\n const buttonProps = isButton\r\n ? {\r\n type: (props as { type?: string }).type ?? 'button',\r\n disabled,\r\n }\r\n : {\r\n 'aria-disabled': disabled || undefined,\r\n role: 'button',\r\n tabIndex: disabled ? -1 : 0,\r\n }\r\n\r\n return (\r\n <Component\r\n ref={ref}\r\n className={buttonClassName}\r\n style={layoutStyle}\r\n data-testid={testId}\r\n {...buttonProps}\r\n {...props}\r\n >\r\n {isLoading && (\r\n <span\r\n style={{\r\n display: 'inline-flex',\r\n position: isIconOnly ? 'absolute' : 'relative',\r\n }}\r\n >\r\n <LoadingSpinner size={sizeValues.iconSize} />\r\n </span>\r\n )}\r\n\r\n {!isLoading && leftIcon && (\r\n <span\r\n style={{\r\n display: 'inline-flex',\r\n width: sizeValues.iconSize,\r\n height: sizeValues.iconSize,\r\n }}\r\n >\r\n {leftIcon}\r\n </span>\r\n )}\r\n\r\n {!isIconOnly && (\r\n <span\r\n style={{\r\n opacity: isLoading && !isIconOnly ? 0.5 : 1,\r\n }}\r\n >\r\n {children}\r\n </span>\r\n )}\r\n\r\n {!isLoading && rightIcon && (\r\n <span\r\n style={{\r\n display: 'inline-flex',\r\n width: sizeValues.iconSize,\r\n height: sizeValues.iconSize,\r\n }}\r\n >\r\n {rightIcon}\r\n </span>\r\n )}\r\n\r\n {isIconOnly && !isLoading && children}\r\n </Component>\r\n )\r\n}\r\n\r\nexport const Button = forwardRef(ButtonInner) as ButtonComponent\r\n\r\nButton.displayName = 'Button'\r\n\r\n/** Icon button shorthand */\r\nconst IconButtonInner = <E extends ElementType = 'button'>(\r\n props: Omit<ButtonProps<E>, 'isIconOnly'>,\r\n ref: React.ForwardedRef<Element>\r\n) => {\r\n return <Button ref={ref as React.ForwardedRef<HTMLButtonElement>} isIconOnly {...(props as ButtonProps<'button'>)} />\r\n}\r\n\r\nexport const IconButton = forwardRef(IconButtonInner) as ButtonComponent\r\n\r\nIconButton.displayName = 'IconButton'\r\n"],"names":["LoadingSpinner","React","size","jsxs","jsx","ButtonInner","as","variant","intent","fullWidth","isLoading","isDisabled","leftIcon","rightIcon","isIconOnly","className","style","children","testId","props","ref","Component","disabled","sizeValues","sizeConfig","layoutStyle","buttonClassName","cx","buttonProps","Button","forwardRef","IconButtonInner","IconButton"],"mappings":"gNA6BMA,EAAiBC,EAAM,KAAK,SAAwB,CAAE,KAAAC,GAA0B,CACpF,OACEC,EAAAA,KAAC,MAAA,CACC,MAAOD,EACP,OAAQA,EACR,QAAQ,YACR,KAAK,OACL,UAAU,sBACV,cAAY,OAEZ,SAAA,CAAAE,EAAAA,IAAC,SAAA,CACC,GAAG,KACH,GAAG,KACH,EAAE,KACF,OAAO,eACP,YAAY,IACZ,cAAc,QACd,gBAAgB,YAChB,QAAQ,MAAA,CAAA,EAEVA,EAAAA,IAAC,SAAA,CACC,GAAG,KACH,GAAG,KACH,EAAE,KACF,OAAO,eACP,YAAY,IACZ,cAAc,QACd,gBAAgB,YAChB,iBAAiB,OACjB,MAAO,CAAE,UAAW,iBAAkB,gBAAiB,QAAA,CAAS,CAAA,CAClE,CAAA,CAAA,CAGN,CAAC,EAEKC,EAAc,CAClB,CACE,GAAAC,EACA,QAAAC,EAAU,QACV,OAAAC,EAAS,UACT,KAAAN,EAAO,KACP,UAAAO,EACA,UAAAC,EACA,WAAAC,EACA,SAAAC,EACA,UAAAC,EACA,WAAAC,EACA,UAAAC,EACA,MAAAC,EACA,SAAAC,EACA,OAAAC,EACA,GAAGC,CACL,EACAC,IACG,CACH,MAAMC,EAAaf,GAAM,SAEnBgB,EAAWX,GAAcD,EAEzBa,EAAaC,EAAAA,WAAWtB,CAAI,EAMhC,OAAO,QAAY,KACnB,QAAQ,IAAI,WAAa,cACzBY,GACA,CAAEK,EAAkC,YAAY,GAChD,CAAEA,EAAkC,iBAAiB,GAErD,QAAQ,KACN,wGAAA,EAKJ,MAAMM,EAA6B,CACjC,QAAS,cACT,WAAY,SACZ,eAAgB,SAChB,GAAGT,CAAA,EAICU,EAAkBC,EAAAA,GACtB,gBACA,kBAAkBpB,CAAO,GACzB,kBAAkBC,CAAM,GACxB,kBAAkBN,CAAI,GACtBO,GAAa,4BACbK,GAAc,2BACdJ,GAAa,yBACbY,GAAY,0BACZP,CAAA,EAIIa,EADWP,IAAc,UAAYf,IAAO,OAE9C,CACE,KAAOa,EAA4B,MAAQ,SAC3C,SAAAG,CAAA,EAEF,CACE,gBAAiBA,GAAY,OAC7B,KAAM,SACN,SAAUA,EAAW,GAAK,CAAA,EAGhC,OACEnB,EAAAA,KAACkB,EAAA,CACC,IAAAD,EACA,UAAWM,EACX,MAAOD,EACP,cAAaP,EACZ,GAAGU,EACH,GAAGT,EAEH,SAAA,CAAAT,GACCN,EAAAA,IAAC,OAAA,CACC,MAAO,CACL,QAAS,cACT,SAAUU,EAAa,WAAa,UAAA,EAGtC,SAAAV,EAAAA,IAACJ,EAAA,CAAe,KAAMuB,EAAW,QAAA,CAAU,CAAA,CAAA,EAI9C,CAACb,GAAaE,GACbR,EAAAA,IAAC,OAAA,CACC,MAAO,CACL,QAAS,cACT,MAAOmB,EAAW,SAClB,OAAQA,EAAW,QAAA,EAGpB,SAAAX,CAAA,CAAA,EAIJ,CAACE,GACAV,EAAAA,IAAC,OAAA,CACC,MAAO,CACL,QAASM,GAAa,CAACI,EAAa,GAAM,CAAA,EAG3C,SAAAG,CAAA,CAAA,EAIJ,CAACP,GAAaG,GACbT,EAAAA,IAAC,OAAA,CACC,MAAO,CACL,QAAS,cACT,MAAOmB,EAAW,SAClB,OAAQA,EAAW,QAAA,EAGpB,SAAAV,CAAA,CAAA,EAIJC,GAAc,CAACJ,GAAaO,CAAA,CAAA,CAAA,CAGnC,EAEaY,EAASC,EAAAA,WAAWzB,CAAW,EAE5CwB,EAAO,YAAc,SAGrB,MAAME,EAAkB,CACtBZ,EACAC,UAEQS,EAAA,CAAO,IAAAT,EAAmD,WAAU,GAAE,GAAID,EAAiC,EAGxGa,EAAaF,EAAAA,WAAWC,CAAe,EAEpDC,EAAW,YAAc"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import
|
|
1
|
+
import { jsxs as y, jsx as t } from "react/jsx-runtime";
|
|
2
|
+
import C, { forwardRef as p } from "react";
|
|
3
3
|
import { sizeConfig as g } from "./Button.styles.js";
|
|
4
|
-
import { cx as
|
|
5
|
-
const
|
|
6
|
-
return /* @__PURE__ */
|
|
4
|
+
import { cx as D } from "../../../utils/styles.js";
|
|
5
|
+
const j = C.memo(function({ size: n }) {
|
|
6
|
+
return /* @__PURE__ */ y(
|
|
7
7
|
"svg",
|
|
8
8
|
{
|
|
9
9
|
width: n,
|
|
@@ -44,66 +44,70 @@ const D = N.memo(function({ size: n }) {
|
|
|
44
44
|
}
|
|
45
45
|
);
|
|
46
46
|
}), $ = ({
|
|
47
|
-
as:
|
|
47
|
+
as: r,
|
|
48
48
|
variant: n = "solid",
|
|
49
|
-
intent:
|
|
49
|
+
intent: f = "primary",
|
|
50
50
|
size: c = "md",
|
|
51
|
-
fullWidth:
|
|
51
|
+
fullWidth: h,
|
|
52
52
|
isLoading: e,
|
|
53
53
|
isDisabled: k,
|
|
54
|
-
leftIcon:
|
|
55
|
-
rightIcon:
|
|
56
|
-
isIconOnly:
|
|
54
|
+
leftIcon: l,
|
|
55
|
+
rightIcon: u,
|
|
56
|
+
isIconOnly: o,
|
|
57
57
|
className: x,
|
|
58
58
|
style: B,
|
|
59
|
-
children:
|
|
59
|
+
children: d,
|
|
60
60
|
testId: S,
|
|
61
|
-
...
|
|
61
|
+
...i
|
|
62
62
|
}, w) => {
|
|
63
|
-
const
|
|
63
|
+
const b = r || "button", a = k || e, s = g[c];
|
|
64
|
+
typeof process < "u" && process.env.NODE_ENV !== "production" && o && !i["aria-label"] && !i["aria-labelledby"] && console.warn(
|
|
65
|
+
"Brycks: an icon-only Button/IconButton has no accessible name. Pass `aria-label` or `aria-labelledby`."
|
|
66
|
+
);
|
|
67
|
+
const N = {
|
|
64
68
|
display: "inline-flex",
|
|
65
69
|
alignItems: "center",
|
|
66
70
|
justifyContent: "center",
|
|
67
71
|
...B
|
|
68
|
-
},
|
|
72
|
+
}, v = D(
|
|
69
73
|
"brycks-button",
|
|
70
74
|
`brycks-button--${n}`,
|
|
71
|
-
`brycks-button--${
|
|
75
|
+
`brycks-button--${f}`,
|
|
72
76
|
`brycks-button--${c}`,
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
h && "brycks-button--full-width",
|
|
78
|
+
o && "brycks-button--icon-only",
|
|
75
79
|
e && "brycks-button--loading",
|
|
76
|
-
|
|
80
|
+
a && "brycks-button--disabled",
|
|
77
81
|
x
|
|
78
|
-
),
|
|
79
|
-
type:
|
|
80
|
-
disabled:
|
|
82
|
+
), z = b === "button" || r === void 0 ? {
|
|
83
|
+
type: i.type ?? "button",
|
|
84
|
+
disabled: a
|
|
81
85
|
} : {
|
|
82
|
-
"aria-disabled":
|
|
86
|
+
"aria-disabled": a || void 0,
|
|
83
87
|
role: "button",
|
|
84
|
-
tabIndex:
|
|
88
|
+
tabIndex: a ? -1 : 0
|
|
85
89
|
};
|
|
86
|
-
return /* @__PURE__ */
|
|
87
|
-
|
|
90
|
+
return /* @__PURE__ */ y(
|
|
91
|
+
b,
|
|
88
92
|
{
|
|
89
93
|
ref: w,
|
|
90
|
-
className:
|
|
91
|
-
style:
|
|
94
|
+
className: v,
|
|
95
|
+
style: N,
|
|
92
96
|
"data-testid": S,
|
|
93
|
-
...
|
|
94
|
-
...
|
|
97
|
+
...z,
|
|
98
|
+
...i,
|
|
95
99
|
children: [
|
|
96
100
|
e && /* @__PURE__ */ t(
|
|
97
101
|
"span",
|
|
98
102
|
{
|
|
99
103
|
style: {
|
|
100
104
|
display: "inline-flex",
|
|
101
|
-
position:
|
|
105
|
+
position: o ? "absolute" : "relative"
|
|
102
106
|
},
|
|
103
|
-
children: /* @__PURE__ */ t(
|
|
107
|
+
children: /* @__PURE__ */ t(j, { size: s.iconSize })
|
|
104
108
|
}
|
|
105
109
|
),
|
|
106
|
-
!e &&
|
|
110
|
+
!e && l && /* @__PURE__ */ t(
|
|
107
111
|
"span",
|
|
108
112
|
{
|
|
109
113
|
style: {
|
|
@@ -111,19 +115,19 @@ const D = N.memo(function({ size: n }) {
|
|
|
111
115
|
width: s.iconSize,
|
|
112
116
|
height: s.iconSize
|
|
113
117
|
},
|
|
114
|
-
children:
|
|
118
|
+
children: l
|
|
115
119
|
}
|
|
116
120
|
),
|
|
117
|
-
!
|
|
121
|
+
!o && /* @__PURE__ */ t(
|
|
118
122
|
"span",
|
|
119
123
|
{
|
|
120
124
|
style: {
|
|
121
|
-
opacity: e && !
|
|
125
|
+
opacity: e && !o ? 0.5 : 1
|
|
122
126
|
},
|
|
123
|
-
children:
|
|
127
|
+
children: d
|
|
124
128
|
}
|
|
125
129
|
),
|
|
126
|
-
!e &&
|
|
130
|
+
!e && u && /* @__PURE__ */ t(
|
|
127
131
|
"span",
|
|
128
132
|
{
|
|
129
133
|
style: {
|
|
@@ -131,19 +135,19 @@ const D = N.memo(function({ size: n }) {
|
|
|
131
135
|
width: s.iconSize,
|
|
132
136
|
height: s.iconSize
|
|
133
137
|
},
|
|
134
|
-
children:
|
|
138
|
+
children: u
|
|
135
139
|
}
|
|
136
140
|
),
|
|
137
|
-
|
|
141
|
+
o && !e && d
|
|
138
142
|
]
|
|
139
143
|
}
|
|
140
144
|
);
|
|
141
145
|
}, m = p($);
|
|
142
146
|
m.displayName = "Button";
|
|
143
|
-
const
|
|
144
|
-
|
|
147
|
+
const E = (r, n) => /* @__PURE__ */ t(m, { ref: n, isIconOnly: !0, ...r }), I = p(E);
|
|
148
|
+
I.displayName = "IconButton";
|
|
145
149
|
export {
|
|
146
150
|
m as Button,
|
|
147
|
-
|
|
151
|
+
I as IconButton
|
|
148
152
|
};
|
|
149
153
|
//# sourceMappingURL=Button.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.js","sources":["../../../../src/components/primitives/Button/Button.tsx"],"sourcesContent":["/**\r\n * Button Component\r\n *\r\n * A versatile, polymorphic button component with multiple variants,\r\n * sizes, and states. Designed with Apple-inspired aesthetics -\r\n * smooth transitions, tactile feedback, and refined visual details.\r\n *\r\n * All color styles are handled via CSS classes for easy customization.\r\n * Layout styles (display, alignment) use inline styles for flexibility.\r\n */\r\n\r\nimport React, {\r\n forwardRef,\r\n type ElementType,\r\n type ComponentPropsWithRef,\r\n type CSSProperties,\r\n} from 'react'\r\nimport type { ButtonProps } from './Button.types'\r\nimport { sizeConfig } from './Button.styles'\r\nimport { cx } from '../../../utils/styles'\r\n\r\ninterface ButtonComponent {\r\n <E extends ElementType = 'button'>(\r\n props: ButtonProps<E> & { ref?: ComponentPropsWithRef<E>['ref'] }\r\n ): React.JSX.Element | null\r\n displayName?: string\r\n}\r\n\r\n/** Loading spinner component - uses global CSS keyframes */\r\nconst LoadingSpinner = React.memo(function LoadingSpinner({ size }: { size: number }) {\r\n return (\r\n <svg\r\n width={size}\r\n height={size}\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n className=\"brycks-animate-spin\"\r\n aria-hidden=\"true\"\r\n >\r\n <circle\r\n cx=\"12\"\r\n cy=\"12\"\r\n r=\"10\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeDasharray=\"31.4 31.4\"\r\n opacity=\"0.25\"\r\n />\r\n <circle\r\n cx=\"12\"\r\n cy=\"12\"\r\n r=\"10\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeDasharray=\"31.4 31.4\"\r\n strokeDashoffset=\"62.8\"\r\n style={{ transform: 'rotate(-90deg)', transformOrigin: 'center' }}\r\n />\r\n </svg>\r\n )\r\n})\r\n\r\nconst ButtonInner = <E extends ElementType = 'button'>(\r\n {\r\n as,\r\n variant = 'solid',\r\n intent = 'primary',\r\n size = 'md',\r\n fullWidth,\r\n isLoading,\r\n isDisabled,\r\n leftIcon,\r\n rightIcon,\r\n isIconOnly,\r\n className,\r\n style,\r\n children,\r\n testId,\r\n ...props\r\n }: ButtonProps<E>,\r\n ref: React.ForwardedRef<Element>\r\n) => {\r\n const Component = (as || 'button') as ElementType\r\n\r\n const disabled = isDisabled || isLoading\r\n\r\n const sizeValues = sizeConfig[size]\r\n\r\n // Layout-only inline styles (colors handled by CSS classes)\r\n const layoutStyle: CSSProperties = {\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n ...style,\r\n }\r\n\r\n // Build class name - CSS handles all colors and states\r\n const buttonClassName = cx(\r\n 'brycks-button',\r\n `brycks-button--${variant}`,\r\n `brycks-button--${intent}`,\r\n `brycks-button--${size}`,\r\n fullWidth && 'brycks-button--full-width',\r\n isIconOnly && 'brycks-button--icon-only',\r\n isLoading && 'brycks-button--loading',\r\n disabled && 'brycks-button--disabled',\r\n className\r\n )\r\n\r\n const isButton = Component === 'button' || as === undefined\r\n const buttonProps = isButton\r\n ? {\r\n type: (props as { type?: string }).type ?? 'button',\r\n disabled,\r\n }\r\n : {\r\n 'aria-disabled': disabled || undefined,\r\n role: 'button',\r\n tabIndex: disabled ? -1 : 0,\r\n }\r\n\r\n return (\r\n <Component\r\n ref={ref}\r\n className={buttonClassName}\r\n style={layoutStyle}\r\n data-testid={testId}\r\n {...buttonProps}\r\n {...props}\r\n >\r\n {isLoading && (\r\n <span\r\n style={{\r\n display: 'inline-flex',\r\n position: isIconOnly ? 'absolute' : 'relative',\r\n }}\r\n >\r\n <LoadingSpinner size={sizeValues.iconSize} />\r\n </span>\r\n )}\r\n\r\n {!isLoading && leftIcon && (\r\n <span\r\n style={{\r\n display: 'inline-flex',\r\n width: sizeValues.iconSize,\r\n height: sizeValues.iconSize,\r\n }}\r\n >\r\n {leftIcon}\r\n </span>\r\n )}\r\n\r\n {!isIconOnly && (\r\n <span\r\n style={{\r\n opacity: isLoading && !isIconOnly ? 0.5 : 1,\r\n }}\r\n >\r\n {children}\r\n </span>\r\n )}\r\n\r\n {!isLoading && rightIcon && (\r\n <span\r\n style={{\r\n display: 'inline-flex',\r\n width: sizeValues.iconSize,\r\n height: sizeValues.iconSize,\r\n }}\r\n >\r\n {rightIcon}\r\n </span>\r\n )}\r\n\r\n {isIconOnly && !isLoading && children}\r\n </Component>\r\n )\r\n}\r\n\r\nexport const Button = forwardRef(ButtonInner) as ButtonComponent\r\n\r\nButton.displayName = 'Button'\r\n\r\n/** Icon button shorthand */\r\nconst IconButtonInner = <E extends ElementType = 'button'>(\r\n props: Omit<ButtonProps<E>, 'isIconOnly'>,\r\n ref: React.ForwardedRef<Element>\r\n) => {\r\n return <Button ref={ref as React.ForwardedRef<HTMLButtonElement>} isIconOnly {...(props as ButtonProps<'button'>)} />\r\n}\r\n\r\nexport const IconButton = forwardRef(IconButtonInner) as ButtonComponent\r\n\r\nIconButton.displayName = 'IconButton'\r\n"],"names":["LoadingSpinner","React","size","jsxs","jsx","ButtonInner","as","variant","intent","fullWidth","isLoading","isDisabled","leftIcon","rightIcon","isIconOnly","className","style","children","testId","props","ref","Component","disabled","sizeValues","sizeConfig","layoutStyle","buttonClassName","cx","buttonProps","Button","forwardRef","IconButtonInner","IconButton"],"mappings":";;;;AA6BA,MAAMA,IAAiBC,EAAM,KAAK,SAAwB,EAAE,MAAAC,KAA0B;AACpF,SACE,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAOD;AAAA,MACP,QAAQA;AAAA,MACR,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,WAAU;AAAA,MACV,eAAY;AAAA,MAEZ,UAAA;AAAA,QAAA,gBAAAE;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,IAAG;AAAA,YACH,IAAG;AAAA,YACH,GAAE;AAAA,YACF,QAAO;AAAA,YACP,aAAY;AAAA,YACZ,eAAc;AAAA,YACd,iBAAgB;AAAA,YAChB,SAAQ;AAAA,UAAA;AAAA,QAAA;AAAA,QAEV,gBAAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,IAAG;AAAA,YACH,IAAG;AAAA,YACH,GAAE;AAAA,YACF,QAAO;AAAA,YACP,aAAY;AAAA,YACZ,eAAc;AAAA,YACd,iBAAgB;AAAA,YAChB,kBAAiB;AAAA,YACjB,OAAO,EAAE,WAAW,kBAAkB,iBAAiB,SAAA;AAAA,UAAS;AAAA,QAAA;AAAA,MAClE;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC,GAEKC,IAAc,CAClB;AAAA,EACE,IAAAC;AAAA,EACA,SAAAC,IAAU;AAAA,EACV,QAAAC,IAAS;AAAA,EACT,MAAAN,IAAO;AAAA,EACP,WAAAO;AAAA,EACA,WAAAC;AAAA,EACA,YAAAC;AAAA,EACA,UAAAC;AAAA,EACA,WAAAC;AAAA,EACA,YAAAC;AAAA,EACA,WAAAC;AAAA,EACA,OAAAC;AAAA,EACA,UAAAC;AAAA,EACA,QAAAC;AAAA,EACA,GAAGC;AACL,GACAC,MACG;AACH,QAAMC,IAAaf,KAAM,UAEnBgB,IAAWX,KAAcD,GAEzBa,IAAaC,EAAWtB,CAAI,
|
|
1
|
+
{"version":3,"file":"Button.js","sources":["../../../../src/components/primitives/Button/Button.tsx"],"sourcesContent":["/**\r\n * Button Component\r\n *\r\n * A versatile, polymorphic button component with multiple variants,\r\n * sizes, and states. Designed with Apple-inspired aesthetics -\r\n * smooth transitions, tactile feedback, and refined visual details.\r\n *\r\n * All color styles are handled via CSS classes for easy customization.\r\n * Layout styles (display, alignment) use inline styles for flexibility.\r\n */\r\n\r\nimport React, {\r\n forwardRef,\r\n type ElementType,\r\n type ComponentPropsWithRef,\r\n type CSSProperties,\r\n} from 'react'\r\nimport type { ButtonProps } from './Button.types'\r\nimport { sizeConfig } from './Button.styles'\r\nimport { cx } from '../../../utils/styles'\r\n\r\ninterface ButtonComponent {\r\n <E extends ElementType = 'button'>(\r\n props: ButtonProps<E> & { ref?: ComponentPropsWithRef<E>['ref'] }\r\n ): React.JSX.Element | null\r\n displayName?: string\r\n}\r\n\r\n/** Loading spinner component - uses global CSS keyframes */\r\nconst LoadingSpinner = React.memo(function LoadingSpinner({ size }: { size: number }) {\r\n return (\r\n <svg\r\n width={size}\r\n height={size}\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n className=\"brycks-animate-spin\"\r\n aria-hidden=\"true\"\r\n >\r\n <circle\r\n cx=\"12\"\r\n cy=\"12\"\r\n r=\"10\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeDasharray=\"31.4 31.4\"\r\n opacity=\"0.25\"\r\n />\r\n <circle\r\n cx=\"12\"\r\n cy=\"12\"\r\n r=\"10\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeDasharray=\"31.4 31.4\"\r\n strokeDashoffset=\"62.8\"\r\n style={{ transform: 'rotate(-90deg)', transformOrigin: 'center' }}\r\n />\r\n </svg>\r\n )\r\n})\r\n\r\nconst ButtonInner = <E extends ElementType = 'button'>(\r\n {\r\n as,\r\n variant = 'solid',\r\n intent = 'primary',\r\n size = 'md',\r\n fullWidth,\r\n isLoading,\r\n isDisabled,\r\n leftIcon,\r\n rightIcon,\r\n isIconOnly,\r\n className,\r\n style,\r\n children,\r\n testId,\r\n ...props\r\n }: ButtonProps<E>,\r\n ref: React.ForwardedRef<Element>\r\n) => {\r\n const Component = (as || 'button') as ElementType\r\n\r\n const disabled = isDisabled || isLoading\r\n\r\n const sizeValues = sizeConfig[size]\r\n\r\n // Dev-only a11y guard: an icon-only button has no text, so it needs an\r\n // accessible name. Reliable across the polymorphic `as` API where a type\r\n // constraint isn't (the check is stripped from production builds).\r\n if (\r\n typeof process !== 'undefined' &&\r\n process.env.NODE_ENV !== 'production' &&\r\n isIconOnly &&\r\n !(props as Record<string, unknown>)['aria-label'] &&\r\n !(props as Record<string, unknown>)['aria-labelledby']\r\n ) {\r\n console.warn(\r\n 'Brycks: an icon-only Button/IconButton has no accessible name. Pass `aria-label` or `aria-labelledby`.'\r\n )\r\n }\r\n\r\n // Layout-only inline styles (colors handled by CSS classes)\r\n const layoutStyle: CSSProperties = {\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n ...style,\r\n }\r\n\r\n // Build class name - CSS handles all colors and states\r\n const buttonClassName = cx(\r\n 'brycks-button',\r\n `brycks-button--${variant}`,\r\n `brycks-button--${intent}`,\r\n `brycks-button--${size}`,\r\n fullWidth && 'brycks-button--full-width',\r\n isIconOnly && 'brycks-button--icon-only',\r\n isLoading && 'brycks-button--loading',\r\n disabled && 'brycks-button--disabled',\r\n className\r\n )\r\n\r\n const isButton = Component === 'button' || as === undefined\r\n const buttonProps = isButton\r\n ? {\r\n type: (props as { type?: string }).type ?? 'button',\r\n disabled,\r\n }\r\n : {\r\n 'aria-disabled': disabled || undefined,\r\n role: 'button',\r\n tabIndex: disabled ? -1 : 0,\r\n }\r\n\r\n return (\r\n <Component\r\n ref={ref}\r\n className={buttonClassName}\r\n style={layoutStyle}\r\n data-testid={testId}\r\n {...buttonProps}\r\n {...props}\r\n >\r\n {isLoading && (\r\n <span\r\n style={{\r\n display: 'inline-flex',\r\n position: isIconOnly ? 'absolute' : 'relative',\r\n }}\r\n >\r\n <LoadingSpinner size={sizeValues.iconSize} />\r\n </span>\r\n )}\r\n\r\n {!isLoading && leftIcon && (\r\n <span\r\n style={{\r\n display: 'inline-flex',\r\n width: sizeValues.iconSize,\r\n height: sizeValues.iconSize,\r\n }}\r\n >\r\n {leftIcon}\r\n </span>\r\n )}\r\n\r\n {!isIconOnly && (\r\n <span\r\n style={{\r\n opacity: isLoading && !isIconOnly ? 0.5 : 1,\r\n }}\r\n >\r\n {children}\r\n </span>\r\n )}\r\n\r\n {!isLoading && rightIcon && (\r\n <span\r\n style={{\r\n display: 'inline-flex',\r\n width: sizeValues.iconSize,\r\n height: sizeValues.iconSize,\r\n }}\r\n >\r\n {rightIcon}\r\n </span>\r\n )}\r\n\r\n {isIconOnly && !isLoading && children}\r\n </Component>\r\n )\r\n}\r\n\r\nexport const Button = forwardRef(ButtonInner) as ButtonComponent\r\n\r\nButton.displayName = 'Button'\r\n\r\n/** Icon button shorthand */\r\nconst IconButtonInner = <E extends ElementType = 'button'>(\r\n props: Omit<ButtonProps<E>, 'isIconOnly'>,\r\n ref: React.ForwardedRef<Element>\r\n) => {\r\n return <Button ref={ref as React.ForwardedRef<HTMLButtonElement>} isIconOnly {...(props as ButtonProps<'button'>)} />\r\n}\r\n\r\nexport const IconButton = forwardRef(IconButtonInner) as ButtonComponent\r\n\r\nIconButton.displayName = 'IconButton'\r\n"],"names":["LoadingSpinner","React","size","jsxs","jsx","ButtonInner","as","variant","intent","fullWidth","isLoading","isDisabled","leftIcon","rightIcon","isIconOnly","className","style","children","testId","props","ref","Component","disabled","sizeValues","sizeConfig","layoutStyle","buttonClassName","cx","buttonProps","Button","forwardRef","IconButtonInner","IconButton"],"mappings":"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"}
|