analytica-frontend-lib 1.0.47 → 1.0.48
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/Modal/index.d.mts +66 -0
- package/dist/Modal/index.d.ts +66 -0
- package/dist/Modal/index.js +113 -0
- package/dist/Modal/index.js.map +1 -0
- package/dist/Modal/index.mjs +92 -0
- package/dist/Modal/index.mjs.map +1 -0
- package/dist/TextArea/index.js +1 -9
- package/dist/TextArea/index.js.map +1 -1
- package/dist/TextArea/index.mjs +1 -9
- package/dist/TextArea/index.mjs.map +1 -1
- package/dist/index.css +0 -3
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +3 -66
- package/dist/index.d.ts +3 -66
- package/dist/index.js +1 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -9
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +0 -3
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Modal component props interface
|
|
6
|
+
*/
|
|
7
|
+
type ModalProps = {
|
|
8
|
+
/** Whether the modal is open */
|
|
9
|
+
isOpen: boolean;
|
|
10
|
+
/** Function to close the modal */
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
/** Modal title */
|
|
13
|
+
title: string;
|
|
14
|
+
/** Modal description/content */
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
/** Size of the modal */
|
|
17
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
18
|
+
/** Additional CSS classes for the modal content */
|
|
19
|
+
className?: string;
|
|
20
|
+
/** Whether clicking the backdrop should close the modal */
|
|
21
|
+
closeOnBackdropClick?: boolean;
|
|
22
|
+
/** Whether pressing Escape should close the modal */
|
|
23
|
+
closeOnEscape?: boolean;
|
|
24
|
+
/** Footer content (typically buttons) */
|
|
25
|
+
footer?: ReactNode;
|
|
26
|
+
/** Hide the close button */
|
|
27
|
+
hideCloseButton?: boolean;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Modal component for Analytica Ensino platforms
|
|
31
|
+
*
|
|
32
|
+
* A flexible modal component with multiple size variants and customizable behavior.
|
|
33
|
+
*
|
|
34
|
+
* @param isOpen - Whether the modal is currently open
|
|
35
|
+
* @param onClose - Callback function called when the modal should be closed
|
|
36
|
+
* @param title - The title displayed at the top of the modal
|
|
37
|
+
* @param children - The main content of the modal
|
|
38
|
+
* @param size - The size variant (xs, sm, md, lg, xl)
|
|
39
|
+
* @param className - Additional CSS classes for the modal content
|
|
40
|
+
* @param closeOnBackdropClick - Whether clicking the backdrop closes the modal (default: true)
|
|
41
|
+
* @param closeOnEscape - Whether pressing Escape closes the modal (default: true)
|
|
42
|
+
* @param footer - Footer content, typically action buttons
|
|
43
|
+
* @param hideCloseButton - Whether to hide the X close button (default: false)
|
|
44
|
+
* @returns A modal overlay with content
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```tsx
|
|
48
|
+
* <Modal
|
|
49
|
+
* isOpen={isModalOpen}
|
|
50
|
+
* onClose={() => setIsModalOpen(false)}
|
|
51
|
+
* title="Invite your team"
|
|
52
|
+
* size="md"
|
|
53
|
+
* footer={
|
|
54
|
+
* <div className="flex gap-3">
|
|
55
|
+
* <Button variant="outline" onClick={() => setIsModalOpen(false)}>Cancel</Button>
|
|
56
|
+
* <Button variant="solid" onClick={handleExplore}>Explore</Button>
|
|
57
|
+
* </div>
|
|
58
|
+
* }
|
|
59
|
+
* >
|
|
60
|
+
* Elevate user interactions with our versatile modals.
|
|
61
|
+
* </Modal>
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
declare const Modal: ({ isOpen, onClose, title, children, size, className, closeOnBackdropClick, closeOnEscape, footer, hideCloseButton, }: ModalProps) => react_jsx_runtime.JSX.Element | null;
|
|
65
|
+
|
|
66
|
+
export { Modal as default };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Modal component props interface
|
|
6
|
+
*/
|
|
7
|
+
type ModalProps = {
|
|
8
|
+
/** Whether the modal is open */
|
|
9
|
+
isOpen: boolean;
|
|
10
|
+
/** Function to close the modal */
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
/** Modal title */
|
|
13
|
+
title: string;
|
|
14
|
+
/** Modal description/content */
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
/** Size of the modal */
|
|
17
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
18
|
+
/** Additional CSS classes for the modal content */
|
|
19
|
+
className?: string;
|
|
20
|
+
/** Whether clicking the backdrop should close the modal */
|
|
21
|
+
closeOnBackdropClick?: boolean;
|
|
22
|
+
/** Whether pressing Escape should close the modal */
|
|
23
|
+
closeOnEscape?: boolean;
|
|
24
|
+
/** Footer content (typically buttons) */
|
|
25
|
+
footer?: ReactNode;
|
|
26
|
+
/** Hide the close button */
|
|
27
|
+
hideCloseButton?: boolean;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Modal component for Analytica Ensino platforms
|
|
31
|
+
*
|
|
32
|
+
* A flexible modal component with multiple size variants and customizable behavior.
|
|
33
|
+
*
|
|
34
|
+
* @param isOpen - Whether the modal is currently open
|
|
35
|
+
* @param onClose - Callback function called when the modal should be closed
|
|
36
|
+
* @param title - The title displayed at the top of the modal
|
|
37
|
+
* @param children - The main content of the modal
|
|
38
|
+
* @param size - The size variant (xs, sm, md, lg, xl)
|
|
39
|
+
* @param className - Additional CSS classes for the modal content
|
|
40
|
+
* @param closeOnBackdropClick - Whether clicking the backdrop closes the modal (default: true)
|
|
41
|
+
* @param closeOnEscape - Whether pressing Escape closes the modal (default: true)
|
|
42
|
+
* @param footer - Footer content, typically action buttons
|
|
43
|
+
* @param hideCloseButton - Whether to hide the X close button (default: false)
|
|
44
|
+
* @returns A modal overlay with content
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```tsx
|
|
48
|
+
* <Modal
|
|
49
|
+
* isOpen={isModalOpen}
|
|
50
|
+
* onClose={() => setIsModalOpen(false)}
|
|
51
|
+
* title="Invite your team"
|
|
52
|
+
* size="md"
|
|
53
|
+
* footer={
|
|
54
|
+
* <div className="flex gap-3">
|
|
55
|
+
* <Button variant="outline" onClick={() => setIsModalOpen(false)}>Cancel</Button>
|
|
56
|
+
* <Button variant="solid" onClick={handleExplore}>Explore</Button>
|
|
57
|
+
* </div>
|
|
58
|
+
* }
|
|
59
|
+
* >
|
|
60
|
+
* Elevate user interactions with our versatile modals.
|
|
61
|
+
* </Modal>
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
declare const Modal: ({ isOpen, onClose, title, children, size, className, closeOnBackdropClick, closeOnEscape, footer, hideCloseButton, }: ModalProps) => react_jsx_runtime.JSX.Element | null;
|
|
65
|
+
|
|
66
|
+
export { Modal as default };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/components/Modal/Modal.tsx
|
|
21
|
+
var Modal_exports = {};
|
|
22
|
+
__export(Modal_exports, {
|
|
23
|
+
default: () => Modal_default
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(Modal_exports);
|
|
26
|
+
var import_react = require("react");
|
|
27
|
+
var import_phosphor_react = require("phosphor-react");
|
|
28
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
29
|
+
var SIZE_CLASSES = {
|
|
30
|
+
xs: "max-w-[360px]",
|
|
31
|
+
sm: "max-w-[420px]",
|
|
32
|
+
md: "max-w-[510px]",
|
|
33
|
+
lg: "max-w-[640px]",
|
|
34
|
+
xl: "max-w-[970px]"
|
|
35
|
+
};
|
|
36
|
+
var Modal = ({
|
|
37
|
+
isOpen,
|
|
38
|
+
onClose,
|
|
39
|
+
title,
|
|
40
|
+
children,
|
|
41
|
+
size = "md",
|
|
42
|
+
className = "",
|
|
43
|
+
closeOnBackdropClick = true,
|
|
44
|
+
closeOnEscape = true,
|
|
45
|
+
footer,
|
|
46
|
+
hideCloseButton = false
|
|
47
|
+
}) => {
|
|
48
|
+
(0, import_react.useEffect)(() => {
|
|
49
|
+
if (!isOpen || !closeOnEscape) return;
|
|
50
|
+
const handleEscape = (event) => {
|
|
51
|
+
if (event.key === "Escape") {
|
|
52
|
+
onClose();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
document.addEventListener("keydown", handleEscape);
|
|
56
|
+
return () => document.removeEventListener("keydown", handleEscape);
|
|
57
|
+
}, [isOpen, closeOnEscape, onClose]);
|
|
58
|
+
(0, import_react.useEffect)(() => {
|
|
59
|
+
const originalOverflow = document.body.style.overflow;
|
|
60
|
+
if (isOpen) {
|
|
61
|
+
document.body.style.overflow = "hidden";
|
|
62
|
+
} else {
|
|
63
|
+
document.body.style.overflow = originalOverflow;
|
|
64
|
+
}
|
|
65
|
+
return () => {
|
|
66
|
+
document.body.style.overflow = originalOverflow;
|
|
67
|
+
};
|
|
68
|
+
}, [isOpen]);
|
|
69
|
+
const handleBackdropClick = (event) => {
|
|
70
|
+
if (closeOnBackdropClick && event.target === event.currentTarget) {
|
|
71
|
+
onClose();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const handleBackdropKeyDown = (event) => {
|
|
75
|
+
if (closeOnBackdropClick && (event.key === "Enter" || event.key === " ")) {
|
|
76
|
+
onClose();
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
if (!isOpen) return null;
|
|
80
|
+
const sizeClasses = SIZE_CLASSES[size];
|
|
81
|
+
const baseClasses = "bg-background rounded-3xl shadow-hard-shadow-2 border border-border-100 w-full mx-4";
|
|
82
|
+
const dialogResetClasses = "p-0 m-0 border-none outline-none max-h-none static";
|
|
83
|
+
const modalClasses = `${baseClasses} ${sizeClasses} ${dialogResetClasses} ${className}`;
|
|
84
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
85
|
+
"div",
|
|
86
|
+
{
|
|
87
|
+
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs",
|
|
88
|
+
onClick: handleBackdropClick,
|
|
89
|
+
onKeyDown: handleBackdropKeyDown,
|
|
90
|
+
role: "button",
|
|
91
|
+
tabIndex: closeOnBackdropClick ? 0 : -1,
|
|
92
|
+
"aria-label": "Fechar modal clicando no fundo",
|
|
93
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dialog", { className: modalClasses, "aria-labelledby": "modal-title", open: true, children: [
|
|
94
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between px-6 py-6", children: [
|
|
95
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { id: "modal-title", className: "text-lg font-semibold text-text-950", children: title }),
|
|
96
|
+
!hideCloseButton && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
97
|
+
"button",
|
|
98
|
+
{
|
|
99
|
+
onClick: onClose,
|
|
100
|
+
className: "p-1 text-text-500 hover:text-text-700 hover:bg-background-50 rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-indicator-info focus:ring-offset-2",
|
|
101
|
+
"aria-label": "Fechar modal",
|
|
102
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_phosphor_react.X, { size: 18 })
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
] }),
|
|
106
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "px-6 pb-6", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-text-500 font-normal text-sm leading-6", children }) }),
|
|
107
|
+
footer && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex justify-end gap-3 px-6 pb-6", children: footer })
|
|
108
|
+
] })
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
var Modal_default = Modal;
|
|
113
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/Modal/Modal.tsx"],"sourcesContent":["import { ReactNode, MouseEvent, useEffect, KeyboardEvent } from 'react';\nimport { X } from 'phosphor-react';\n\n/**\n * Lookup table for size classes\n */\nconst SIZE_CLASSES = {\n xs: 'max-w-[360px]',\n sm: 'max-w-[420px]',\n md: 'max-w-[510px]',\n lg: 'max-w-[640px]',\n xl: 'max-w-[970px]',\n} as const;\n\n/**\n * Modal component props interface\n */\ntype ModalProps = {\n /** Whether the modal is open */\n isOpen: boolean;\n /** Function to close the modal */\n onClose: () => void;\n /** Modal title */\n title: string;\n /** Modal description/content */\n children: ReactNode;\n /** Size of the modal */\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n /** Additional CSS classes for the modal content */\n className?: string;\n /** Whether clicking the backdrop should close the modal */\n closeOnBackdropClick?: boolean;\n /** Whether pressing Escape should close the modal */\n closeOnEscape?: boolean;\n /** Footer content (typically buttons) */\n footer?: ReactNode;\n /** Hide the close button */\n hideCloseButton?: boolean;\n};\n\n/**\n * Modal component for Analytica Ensino platforms\n *\n * A flexible modal component with multiple size variants and customizable behavior.\n *\n * @param isOpen - Whether the modal is currently open\n * @param onClose - Callback function called when the modal should be closed\n * @param title - The title displayed at the top of the modal\n * @param children - The main content of the modal\n * @param size - The size variant (xs, sm, md, lg, xl)\n * @param className - Additional CSS classes for the modal content\n * @param closeOnBackdropClick - Whether clicking the backdrop closes the modal (default: true)\n * @param closeOnEscape - Whether pressing Escape closes the modal (default: true)\n * @param footer - Footer content, typically action buttons\n * @param hideCloseButton - Whether to hide the X close button (default: false)\n * @returns A modal overlay with content\n *\n * @example\n * ```tsx\n * <Modal\n * isOpen={isModalOpen}\n * onClose={() => setIsModalOpen(false)}\n * title=\"Invite your team\"\n * size=\"md\"\n * footer={\n * <div className=\"flex gap-3\">\n * <Button variant=\"outline\" onClick={() => setIsModalOpen(false)}>Cancel</Button>\n * <Button variant=\"solid\" onClick={handleExplore}>Explore</Button>\n * </div>\n * }\n * >\n * Elevate user interactions with our versatile modals.\n * </Modal>\n * ```\n */\nconst Modal = ({\n isOpen,\n onClose,\n title,\n children,\n size = 'md',\n className = '',\n closeOnBackdropClick = true,\n closeOnEscape = true,\n footer,\n hideCloseButton = false,\n}: ModalProps) => {\n // Handle escape key\n useEffect(() => {\n if (!isOpen || !closeOnEscape) return;\n\n const handleEscape = (event: globalThis.KeyboardEvent) => {\n if (event.key === 'Escape') {\n onClose();\n }\n };\n\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [isOpen, closeOnEscape, onClose]);\n\n // Handle body scroll lock\n useEffect(() => {\n const originalOverflow = document.body.style.overflow;\n if (isOpen) {\n document.body.style.overflow = 'hidden';\n } else {\n document.body.style.overflow = originalOverflow;\n }\n\n return () => {\n document.body.style.overflow = originalOverflow;\n };\n }, [isOpen]);\n\n // Handle backdrop click\n const handleBackdropClick = (event: MouseEvent<HTMLDivElement>) => {\n if (closeOnBackdropClick && event.target === event.currentTarget) {\n onClose();\n }\n };\n\n // Handle backdrop keyboard interaction\n const handleBackdropKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (closeOnBackdropClick && (event.key === 'Enter' || event.key === ' ')) {\n onClose();\n }\n };\n\n if (!isOpen) return null;\n\n const sizeClasses = SIZE_CLASSES[size];\n const baseClasses =\n 'bg-background rounded-3xl shadow-hard-shadow-2 border border-border-100 w-full mx-4';\n // Reset dialog default styles to prevent positioning issues\n const dialogResetClasses =\n 'p-0 m-0 border-none outline-none max-h-none static';\n const modalClasses = `${baseClasses} ${sizeClasses} ${dialogResetClasses} ${className}`;\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs\"\n onClick={handleBackdropClick}\n onKeyDown={handleBackdropKeyDown}\n role=\"button\"\n tabIndex={closeOnBackdropClick ? 0 : -1}\n aria-label=\"Fechar modal clicando no fundo\"\n >\n <dialog className={modalClasses} aria-labelledby=\"modal-title\" open>\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-6\">\n <h2 id=\"modal-title\" className=\"text-lg font-semibold text-text-950\">\n {title}\n </h2>\n {!hideCloseButton && (\n <button\n onClick={onClose}\n className=\"p-1 text-text-500 hover:text-text-700 hover:bg-background-50 rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-indicator-info focus:ring-offset-2\"\n aria-label=\"Fechar modal\"\n >\n <X size={18} />\n </button>\n )}\n </div>\n\n {/* Content */}\n <div className=\"px-6 pb-6\">\n <div className=\"text-text-500 font-normal text-sm leading-6\">\n {children}\n </div>\n </div>\n\n {/* Footer */}\n {footer && (\n <div className=\"flex justify-end gap-3 px-6 pb-6\">{footer}</div>\n )}\n </dialog>\n </div>\n );\n};\n\nexport default Modal;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAgE;AAChE,4BAAkB;AAqJV;AAhJR,IAAM,eAAe;AAAA,EACnB,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AA+DA,IAAM,QAAQ,CAAC;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,uBAAuB;AAAA,EACvB,gBAAgB;AAAA,EAChB;AAAA,EACA,kBAAkB;AACpB,MAAkB;AAEhB,8BAAU,MAAM;AACd,QAAI,CAAC,UAAU,CAAC,cAAe;AAE/B,UAAM,eAAe,CAAC,UAAoC;AACxD,UAAI,MAAM,QAAQ,UAAU;AAC1B,gBAAQ;AAAA,MACV;AAAA,IACF;AAEA,aAAS,iBAAiB,WAAW,YAAY;AACjD,WAAO,MAAM,SAAS,oBAAoB,WAAW,YAAY;AAAA,EACnE,GAAG,CAAC,QAAQ,eAAe,OAAO,CAAC;AAGnC,8BAAU,MAAM;AACd,UAAM,mBAAmB,SAAS,KAAK,MAAM;AAC7C,QAAI,QAAQ;AACV,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC,OAAO;AACL,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC;AAEA,WAAO,MAAM;AACX,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAGX,QAAM,sBAAsB,CAAC,UAAsC;AACjE,QAAI,wBAAwB,MAAM,WAAW,MAAM,eAAe;AAChE,cAAQ;AAAA,IACV;AAAA,EACF;AAGA,QAAM,wBAAwB,CAAC,UAAyC;AACtE,QAAI,yBAAyB,MAAM,QAAQ,WAAW,MAAM,QAAQ,MAAM;AACxE,cAAQ;AAAA,IACV;AAAA,EACF;AAEA,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,cAAc,aAAa,IAAI;AACrC,QAAM,cACJ;AAEF,QAAM,qBACJ;AACF,QAAM,eAAe,GAAG,WAAW,IAAI,WAAW,IAAI,kBAAkB,IAAI,SAAS;AAErF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,SAAS;AAAA,MACT,WAAW;AAAA,MACX,MAAK;AAAA,MACL,UAAU,uBAAuB,IAAI;AAAA,MACrC,cAAW;AAAA,MAEX,uDAAC,YAAO,WAAW,cAAc,mBAAgB,eAAc,MAAI,MAEjE;AAAA,qDAAC,SAAI,WAAU,+CACb;AAAA,sDAAC,QAAG,IAAG,eAAc,WAAU,uCAC5B,iBACH;AAAA,UACC,CAAC,mBACA;AAAA,YAAC;AAAA;AAAA,cACC,SAAS;AAAA,cACT,WAAU;AAAA,cACV,cAAW;AAAA,cAEX,sDAAC,2BAAE,MAAM,IAAI;AAAA;AAAA,UACf;AAAA,WAEJ;AAAA,QAGA,4CAAC,SAAI,WAAU,aACb,sDAAC,SAAI,WAAU,+CACZ,UACH,GACF;AAAA,QAGC,UACC,4CAAC,SAAI,WAAU,oCAAoC,kBAAO;AAAA,SAE9D;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,gBAAQ;","names":[]}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// src/components/Modal/Modal.tsx
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
import { X } from "phosphor-react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
var SIZE_CLASSES = {
|
|
6
|
+
xs: "max-w-[360px]",
|
|
7
|
+
sm: "max-w-[420px]",
|
|
8
|
+
md: "max-w-[510px]",
|
|
9
|
+
lg: "max-w-[640px]",
|
|
10
|
+
xl: "max-w-[970px]"
|
|
11
|
+
};
|
|
12
|
+
var Modal = ({
|
|
13
|
+
isOpen,
|
|
14
|
+
onClose,
|
|
15
|
+
title,
|
|
16
|
+
children,
|
|
17
|
+
size = "md",
|
|
18
|
+
className = "",
|
|
19
|
+
closeOnBackdropClick = true,
|
|
20
|
+
closeOnEscape = true,
|
|
21
|
+
footer,
|
|
22
|
+
hideCloseButton = false
|
|
23
|
+
}) => {
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!isOpen || !closeOnEscape) return;
|
|
26
|
+
const handleEscape = (event) => {
|
|
27
|
+
if (event.key === "Escape") {
|
|
28
|
+
onClose();
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
document.addEventListener("keydown", handleEscape);
|
|
32
|
+
return () => document.removeEventListener("keydown", handleEscape);
|
|
33
|
+
}, [isOpen, closeOnEscape, onClose]);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const originalOverflow = document.body.style.overflow;
|
|
36
|
+
if (isOpen) {
|
|
37
|
+
document.body.style.overflow = "hidden";
|
|
38
|
+
} else {
|
|
39
|
+
document.body.style.overflow = originalOverflow;
|
|
40
|
+
}
|
|
41
|
+
return () => {
|
|
42
|
+
document.body.style.overflow = originalOverflow;
|
|
43
|
+
};
|
|
44
|
+
}, [isOpen]);
|
|
45
|
+
const handleBackdropClick = (event) => {
|
|
46
|
+
if (closeOnBackdropClick && event.target === event.currentTarget) {
|
|
47
|
+
onClose();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const handleBackdropKeyDown = (event) => {
|
|
51
|
+
if (closeOnBackdropClick && (event.key === "Enter" || event.key === " ")) {
|
|
52
|
+
onClose();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
if (!isOpen) return null;
|
|
56
|
+
const sizeClasses = SIZE_CLASSES[size];
|
|
57
|
+
const baseClasses = "bg-background rounded-3xl shadow-hard-shadow-2 border border-border-100 w-full mx-4";
|
|
58
|
+
const dialogResetClasses = "p-0 m-0 border-none outline-none max-h-none static";
|
|
59
|
+
const modalClasses = `${baseClasses} ${sizeClasses} ${dialogResetClasses} ${className}`;
|
|
60
|
+
return /* @__PURE__ */ jsx(
|
|
61
|
+
"div",
|
|
62
|
+
{
|
|
63
|
+
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs",
|
|
64
|
+
onClick: handleBackdropClick,
|
|
65
|
+
onKeyDown: handleBackdropKeyDown,
|
|
66
|
+
role: "button",
|
|
67
|
+
tabIndex: closeOnBackdropClick ? 0 : -1,
|
|
68
|
+
"aria-label": "Fechar modal clicando no fundo",
|
|
69
|
+
children: /* @__PURE__ */ jsxs("dialog", { className: modalClasses, "aria-labelledby": "modal-title", open: true, children: [
|
|
70
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-6", children: [
|
|
71
|
+
/* @__PURE__ */ jsx("h2", { id: "modal-title", className: "text-lg font-semibold text-text-950", children: title }),
|
|
72
|
+
!hideCloseButton && /* @__PURE__ */ jsx(
|
|
73
|
+
"button",
|
|
74
|
+
{
|
|
75
|
+
onClick: onClose,
|
|
76
|
+
className: "p-1 text-text-500 hover:text-text-700 hover:bg-background-50 rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-indicator-info focus:ring-offset-2",
|
|
77
|
+
"aria-label": "Fechar modal",
|
|
78
|
+
children: /* @__PURE__ */ jsx(X, { size: 18 })
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
] }),
|
|
82
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 pb-6", children: /* @__PURE__ */ jsx("div", { className: "text-text-500 font-normal text-sm leading-6", children }) }),
|
|
83
|
+
footer && /* @__PURE__ */ jsx("div", { className: "flex justify-end gap-3 px-6 pb-6", children: footer })
|
|
84
|
+
] })
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
var Modal_default = Modal;
|
|
89
|
+
export {
|
|
90
|
+
Modal_default as default
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/Modal/Modal.tsx"],"sourcesContent":["import { ReactNode, MouseEvent, useEffect, KeyboardEvent } from 'react';\nimport { X } from 'phosphor-react';\n\n/**\n * Lookup table for size classes\n */\nconst SIZE_CLASSES = {\n xs: 'max-w-[360px]',\n sm: 'max-w-[420px]',\n md: 'max-w-[510px]',\n lg: 'max-w-[640px]',\n xl: 'max-w-[970px]',\n} as const;\n\n/**\n * Modal component props interface\n */\ntype ModalProps = {\n /** Whether the modal is open */\n isOpen: boolean;\n /** Function to close the modal */\n onClose: () => void;\n /** Modal title */\n title: string;\n /** Modal description/content */\n children: ReactNode;\n /** Size of the modal */\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n /** Additional CSS classes for the modal content */\n className?: string;\n /** Whether clicking the backdrop should close the modal */\n closeOnBackdropClick?: boolean;\n /** Whether pressing Escape should close the modal */\n closeOnEscape?: boolean;\n /** Footer content (typically buttons) */\n footer?: ReactNode;\n /** Hide the close button */\n hideCloseButton?: boolean;\n};\n\n/**\n * Modal component for Analytica Ensino platforms\n *\n * A flexible modal component with multiple size variants and customizable behavior.\n *\n * @param isOpen - Whether the modal is currently open\n * @param onClose - Callback function called when the modal should be closed\n * @param title - The title displayed at the top of the modal\n * @param children - The main content of the modal\n * @param size - The size variant (xs, sm, md, lg, xl)\n * @param className - Additional CSS classes for the modal content\n * @param closeOnBackdropClick - Whether clicking the backdrop closes the modal (default: true)\n * @param closeOnEscape - Whether pressing Escape closes the modal (default: true)\n * @param footer - Footer content, typically action buttons\n * @param hideCloseButton - Whether to hide the X close button (default: false)\n * @returns A modal overlay with content\n *\n * @example\n * ```tsx\n * <Modal\n * isOpen={isModalOpen}\n * onClose={() => setIsModalOpen(false)}\n * title=\"Invite your team\"\n * size=\"md\"\n * footer={\n * <div className=\"flex gap-3\">\n * <Button variant=\"outline\" onClick={() => setIsModalOpen(false)}>Cancel</Button>\n * <Button variant=\"solid\" onClick={handleExplore}>Explore</Button>\n * </div>\n * }\n * >\n * Elevate user interactions with our versatile modals.\n * </Modal>\n * ```\n */\nconst Modal = ({\n isOpen,\n onClose,\n title,\n children,\n size = 'md',\n className = '',\n closeOnBackdropClick = true,\n closeOnEscape = true,\n footer,\n hideCloseButton = false,\n}: ModalProps) => {\n // Handle escape key\n useEffect(() => {\n if (!isOpen || !closeOnEscape) return;\n\n const handleEscape = (event: globalThis.KeyboardEvent) => {\n if (event.key === 'Escape') {\n onClose();\n }\n };\n\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [isOpen, closeOnEscape, onClose]);\n\n // Handle body scroll lock\n useEffect(() => {\n const originalOverflow = document.body.style.overflow;\n if (isOpen) {\n document.body.style.overflow = 'hidden';\n } else {\n document.body.style.overflow = originalOverflow;\n }\n\n return () => {\n document.body.style.overflow = originalOverflow;\n };\n }, [isOpen]);\n\n // Handle backdrop click\n const handleBackdropClick = (event: MouseEvent<HTMLDivElement>) => {\n if (closeOnBackdropClick && event.target === event.currentTarget) {\n onClose();\n }\n };\n\n // Handle backdrop keyboard interaction\n const handleBackdropKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (closeOnBackdropClick && (event.key === 'Enter' || event.key === ' ')) {\n onClose();\n }\n };\n\n if (!isOpen) return null;\n\n const sizeClasses = SIZE_CLASSES[size];\n const baseClasses =\n 'bg-background rounded-3xl shadow-hard-shadow-2 border border-border-100 w-full mx-4';\n // Reset dialog default styles to prevent positioning issues\n const dialogResetClasses =\n 'p-0 m-0 border-none outline-none max-h-none static';\n const modalClasses = `${baseClasses} ${sizeClasses} ${dialogResetClasses} ${className}`;\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs\"\n onClick={handleBackdropClick}\n onKeyDown={handleBackdropKeyDown}\n role=\"button\"\n tabIndex={closeOnBackdropClick ? 0 : -1}\n aria-label=\"Fechar modal clicando no fundo\"\n >\n <dialog className={modalClasses} aria-labelledby=\"modal-title\" open>\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-6\">\n <h2 id=\"modal-title\" className=\"text-lg font-semibold text-text-950\">\n {title}\n </h2>\n {!hideCloseButton && (\n <button\n onClick={onClose}\n className=\"p-1 text-text-500 hover:text-text-700 hover:bg-background-50 rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-indicator-info focus:ring-offset-2\"\n aria-label=\"Fechar modal\"\n >\n <X size={18} />\n </button>\n )}\n </div>\n\n {/* Content */}\n <div className=\"px-6 pb-6\">\n <div className=\"text-text-500 font-normal text-sm leading-6\">\n {children}\n </div>\n </div>\n\n {/* Footer */}\n {footer && (\n <div className=\"flex justify-end gap-3 px-6 pb-6\">{footer}</div>\n )}\n </dialog>\n </div>\n );\n};\n\nexport default Modal;\n"],"mappings":";AAAA,SAAgC,iBAAgC;AAChE,SAAS,SAAS;AAqJV,SACE,KADF;AAhJR,IAAM,eAAe;AAAA,EACnB,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AA+DA,IAAM,QAAQ,CAAC;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,uBAAuB;AAAA,EACvB,gBAAgB;AAAA,EAChB;AAAA,EACA,kBAAkB;AACpB,MAAkB;AAEhB,YAAU,MAAM;AACd,QAAI,CAAC,UAAU,CAAC,cAAe;AAE/B,UAAM,eAAe,CAAC,UAAoC;AACxD,UAAI,MAAM,QAAQ,UAAU;AAC1B,gBAAQ;AAAA,MACV;AAAA,IACF;AAEA,aAAS,iBAAiB,WAAW,YAAY;AACjD,WAAO,MAAM,SAAS,oBAAoB,WAAW,YAAY;AAAA,EACnE,GAAG,CAAC,QAAQ,eAAe,OAAO,CAAC;AAGnC,YAAU,MAAM;AACd,UAAM,mBAAmB,SAAS,KAAK,MAAM;AAC7C,QAAI,QAAQ;AACV,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC,OAAO;AACL,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC;AAEA,WAAO,MAAM;AACX,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAGX,QAAM,sBAAsB,CAAC,UAAsC;AACjE,QAAI,wBAAwB,MAAM,WAAW,MAAM,eAAe;AAChE,cAAQ;AAAA,IACV;AAAA,EACF;AAGA,QAAM,wBAAwB,CAAC,UAAyC;AACtE,QAAI,yBAAyB,MAAM,QAAQ,WAAW,MAAM,QAAQ,MAAM;AACxE,cAAQ;AAAA,IACV;AAAA,EACF;AAEA,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,cAAc,aAAa,IAAI;AACrC,QAAM,cACJ;AAEF,QAAM,qBACJ;AACF,QAAM,eAAe,GAAG,WAAW,IAAI,WAAW,IAAI,kBAAkB,IAAI,SAAS;AAErF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,SAAS;AAAA,MACT,WAAW;AAAA,MACX,MAAK;AAAA,MACL,UAAU,uBAAuB,IAAI;AAAA,MACrC,cAAW;AAAA,MAEX,+BAAC,YAAO,WAAW,cAAc,mBAAgB,eAAc,MAAI,MAEjE;AAAA,6BAAC,SAAI,WAAU,+CACb;AAAA,8BAAC,QAAG,IAAG,eAAc,WAAU,uCAC5B,iBACH;AAAA,UACC,CAAC,mBACA;AAAA,YAAC;AAAA;AAAA,cACC,SAAS;AAAA,cACT,WAAU;AAAA,cACV,cAAW;AAAA,cAEX,8BAAC,KAAE,MAAM,IAAI;AAAA;AAAA,UACf;AAAA,WAEJ;AAAA,QAGA,oBAAC,SAAI,WAAU,aACb,8BAAC,SAAI,WAAU,+CACZ,UACH,GACF;AAAA,QAGC,UACC,oBAAC,SAAI,WAAU,oCAAoC,kBAAO;AAAA,SAE9D;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,gBAAQ;","names":[]}
|
package/dist/TextArea/index.js
CHANGED
|
@@ -80,29 +80,21 @@ var Text_default = Text;
|
|
|
80
80
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
81
81
|
var SIZE_CLASSES = {
|
|
82
82
|
small: {
|
|
83
|
-
container: "w-72",
|
|
84
|
-
// 288px width
|
|
85
83
|
textarea: "h-24 text-sm",
|
|
86
84
|
// 96px height, 14px font
|
|
87
85
|
textSize: "sm"
|
|
88
86
|
},
|
|
89
87
|
medium: {
|
|
90
|
-
container: "w-72",
|
|
91
|
-
// 288px width
|
|
92
88
|
textarea: "h-24 text-base",
|
|
93
89
|
// 96px height, 16px font
|
|
94
90
|
textSize: "md"
|
|
95
91
|
},
|
|
96
92
|
large: {
|
|
97
|
-
container: "w-72",
|
|
98
|
-
// 288px width
|
|
99
93
|
textarea: "h-24 text-lg",
|
|
100
94
|
// 96px height, 18px font
|
|
101
95
|
textSize: "lg"
|
|
102
96
|
},
|
|
103
97
|
extraLarge: {
|
|
104
|
-
container: "w-72",
|
|
105
|
-
// 288px width
|
|
106
98
|
textarea: "h-24 text-xl",
|
|
107
99
|
// 96px height, 20px font
|
|
108
100
|
textSize: "xl"
|
|
@@ -172,7 +164,7 @@ var TextArea = (0, import_react.forwardRef)(
|
|
|
172
164
|
const sizeClasses = SIZE_CLASSES[size];
|
|
173
165
|
const stateClasses = STATE_CLASSES[currentState];
|
|
174
166
|
const textareaClasses = `${BASE_TEXTAREA_CLASSES} ${sizeClasses.textarea} ${stateClasses.base} ${stateClasses.hover} ${stateClasses.focus} ${className}`;
|
|
175
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `flex flex-col
|
|
167
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `flex flex-col`, children: [
|
|
176
168
|
label && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
177
169
|
Text_default,
|
|
178
170
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/TextArea/TextArea.tsx","../../src/components/Text/Text.tsx"],"sourcesContent":["import {\n TextareaHTMLAttributes,\n ReactNode,\n forwardRef,\n useState,\n useId,\n ChangeEvent,\n FocusEvent,\n} from 'react';\nimport Text from '../Text/Text';\n\n/**\n * TextArea size variants\n */\ntype TextAreaSize = 'small' | 'medium' | 'large' | 'extraLarge';\n\n/**\n * TextArea visual state\n */\ntype TextAreaState = 'default' | 'hovered' | 'focused' | 'invalid' | 'disabled';\n\n/**\n * Size configurations with exact pixel specifications\n */\nconst SIZE_CLASSES = {\n small: {\n container: 'w-72', // 288px width\n textarea: 'h-24 text-sm', // 96px height, 14px font\n textSize: 'sm' as const,\n },\n medium: {\n container: 'w-72', // 288px width\n textarea: 'h-24 text-base', // 96px height, 16px font\n textSize: 'md' as const,\n },\n large: {\n container: 'w-72', // 288px width\n textarea: 'h-24 text-lg', // 96px height, 18px font\n textSize: 'lg' as const,\n },\n extraLarge: {\n container: 'w-72', // 288px width\n textarea: 'h-24 text-xl', // 96px height, 20px font\n textSize: 'xl' as const,\n },\n} as const;\n\n/**\n * Base textarea styling classes using design system colors\n */\nconst BASE_TEXTAREA_CLASSES =\n 'w-full box-border p-3 bg-background border border-solid rounded-[4px] resize-none focus:outline-none font-roboto font-normal leading-[150%] placeholder:text-text-600 transition-all duration-200';\n\n/**\n * State-based styling classes using design system colors from styles.css\n */\nconst STATE_CLASSES = {\n default: {\n base: 'border-border-300 bg-background text-text-600',\n hover: 'hover:border-border-400',\n focus: 'focus:border-border-500',\n },\n hovered: {\n base: 'border-border-400 bg-background text-text-600',\n hover: '',\n focus: 'focus:border-border-500',\n },\n focused: {\n base: 'border-2 border-primary-950 bg-background text-text-900',\n hover: '',\n focus: '',\n },\n invalid: {\n base: 'border-2 border-red-700 bg-white text-gray-800',\n hover: 'hover:border-red-700',\n focus: 'focus:border-red-700',\n },\n disabled: {\n base: 'border-border-300 bg-background text-text-600 cursor-not-allowed opacity-40',\n hover: '',\n focus: '',\n },\n} as const;\n\n/**\n * TextArea component props interface\n */\nexport type TextAreaProps = {\n /** Label text to display above the textarea */\n label?: ReactNode;\n /** Size variant of the textarea */\n size?: TextAreaSize;\n /** Visual state of the textarea */\n state?: TextAreaState;\n /** Error message to display */\n errorMessage?: string;\n /** Helper text to display */\n helperMessage?: string;\n /** Additional CSS classes */\n className?: string;\n /** Label CSS classes */\n labelClassName?: string;\n} & Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'>;\n\n/**\n * TextArea component for Analytica Ensino platforms\n *\n * A textarea component with essential states, sizes and themes.\n * Uses exact design specifications with 288px width, 96px height, and specific\n * color values. Includes Text component integration for consistent typography.\n *\n * @example\n * ```tsx\n * // Basic textarea\n * <TextArea label=\"Description\" placeholder=\"Enter description...\" />\n *\n * // Small size\n * <TextArea size=\"small\" label=\"Comment\" />\n *\n * // Invalid state\n * <TextArea state=\"invalid\" label=\"Required field\" errorMessage=\"This field is required\" />\n *\n * // Disabled state\n * <TextArea disabled label=\"Read-only field\" />\n * ```\n */\nconst TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(\n (\n {\n label,\n size = 'medium',\n state = 'default',\n errorMessage,\n helperMessage,\n className = '',\n labelClassName = '',\n disabled,\n id,\n onChange,\n placeholder,\n ...props\n },\n ref\n ) => {\n // Generate unique ID if not provided\n const generatedId = useId();\n const inputId = id ?? `textarea-${generatedId}`;\n\n // Internal state for focus tracking\n const [isFocused, setIsFocused] = useState(false);\n\n // Handle change events\n const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {\n onChange?.(event);\n };\n\n // Handle focus events\n const handleFocus = (event: FocusEvent<HTMLTextAreaElement>) => {\n setIsFocused(true);\n props.onFocus?.(event);\n };\n\n // Handle blur events\n const handleBlur = (event: FocusEvent<HTMLTextAreaElement>) => {\n setIsFocused(false);\n props.onBlur?.(event);\n };\n\n // Determine current state based on props and focus\n let currentState = disabled ? 'disabled' : state;\n\n // Override state based on focus\n if (\n isFocused &&\n currentState !== 'invalid' &&\n currentState !== 'disabled'\n ) {\n currentState = 'focused';\n }\n\n // Get size classes\n const sizeClasses = SIZE_CLASSES[size];\n\n // Get styling classes\n const stateClasses = STATE_CLASSES[currentState];\n\n // Get final textarea classes\n const textareaClasses = `${BASE_TEXTAREA_CLASSES} ${sizeClasses.textarea} ${stateClasses.base} ${stateClasses.hover} ${stateClasses.focus} ${className}`;\n\n return (\n <div className={`flex flex-col ${sizeClasses.container}`}>\n {/* Label */}\n {label && (\n <Text\n as=\"label\"\n htmlFor={inputId}\n size={sizeClasses.textSize}\n weight=\"medium\"\n color=\"text-text-950\"\n className={`mb-1.5 ${labelClassName}`}\n >\n {label}\n </Text>\n )}\n\n {/* Textarea */}\n <textarea\n ref={ref}\n id={inputId}\n disabled={disabled}\n onChange={handleChange}\n onFocus={handleFocus}\n onBlur={handleBlur}\n className={textareaClasses}\n placeholder={placeholder}\n {...props}\n />\n\n {/* Error message */}\n {errorMessage && (\n <Text size=\"sm\" weight=\"normal\" className=\"mt-1.5 text-error-600\">\n {errorMessage}\n </Text>\n )}\n\n {/* Helper text */}\n {helperMessage && !errorMessage && (\n <Text size=\"sm\" weight=\"normal\" className=\"mt-1.5 text-text-500\">\n {helperMessage}\n </Text>\n )}\n </div>\n );\n }\n);\n\nTextArea.displayName = 'TextArea';\n\nexport default TextArea;\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses = '';\n let weightClasses = '';\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={`${baseClasses} ${sizeClasses} ${weightClasses} ${color} ${className}`}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n"],"mappings":"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","names":["import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/TextArea/TextArea.tsx","../../src/components/Text/Text.tsx"],"sourcesContent":["import {\n TextareaHTMLAttributes,\n ReactNode,\n forwardRef,\n useState,\n useId,\n ChangeEvent,\n FocusEvent,\n} from 'react';\nimport Text from '../Text/Text';\n\n/**\n * TextArea size variants\n */\ntype TextAreaSize = 'small' | 'medium' | 'large' | 'extraLarge';\n\n/**\n * TextArea visual state\n */\ntype TextAreaState = 'default' | 'hovered' | 'focused' | 'invalid' | 'disabled';\n\n/**\n * Size configurations with exact pixel specifications\n */\nconst SIZE_CLASSES = {\n small: {\n textarea: 'h-24 text-sm', // 96px height, 14px font\n textSize: 'sm' as const,\n },\n medium: {\n textarea: 'h-24 text-base', // 96px height, 16px font\n textSize: 'md' as const,\n },\n large: {\n textarea: 'h-24 text-lg', // 96px height, 18px font\n textSize: 'lg' as const,\n },\n extraLarge: {\n textarea: 'h-24 text-xl', // 96px height, 20px font\n textSize: 'xl' as const,\n },\n} as const;\n\n/**\n * Base textarea styling classes using design system colors\n */\nconst BASE_TEXTAREA_CLASSES =\n 'w-full box-border p-3 bg-background border border-solid rounded-[4px] resize-none focus:outline-none font-roboto font-normal leading-[150%] placeholder:text-text-600 transition-all duration-200';\n\n/**\n * State-based styling classes using design system colors from styles.css\n */\nconst STATE_CLASSES = {\n default: {\n base: 'border-border-300 bg-background text-text-600',\n hover: 'hover:border-border-400',\n focus: 'focus:border-border-500',\n },\n hovered: {\n base: 'border-border-400 bg-background text-text-600',\n hover: '',\n focus: 'focus:border-border-500',\n },\n focused: {\n base: 'border-2 border-primary-950 bg-background text-text-900',\n hover: '',\n focus: '',\n },\n invalid: {\n base: 'border-2 border-red-700 bg-white text-gray-800',\n hover: 'hover:border-red-700',\n focus: 'focus:border-red-700',\n },\n disabled: {\n base: 'border-border-300 bg-background text-text-600 cursor-not-allowed opacity-40',\n hover: '',\n focus: '',\n },\n} as const;\n\n/**\n * TextArea component props interface\n */\nexport type TextAreaProps = {\n /** Label text to display above the textarea */\n label?: ReactNode;\n /** Size variant of the textarea */\n size?: TextAreaSize;\n /** Visual state of the textarea */\n state?: TextAreaState;\n /** Error message to display */\n errorMessage?: string;\n /** Helper text to display */\n helperMessage?: string;\n /** Additional CSS classes */\n className?: string;\n /** Label CSS classes */\n labelClassName?: string;\n} & Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'>;\n\n/**\n * TextArea component for Analytica Ensino platforms\n *\n * A textarea component with essential states, sizes and themes.\n * Uses exact design specifications with 288px width, 96px height, and specific\n * color values. Includes Text component integration for consistent typography.\n *\n * @example\n * ```tsx\n * // Basic textarea\n * <TextArea label=\"Description\" placeholder=\"Enter description...\" />\n *\n * // Small size\n * <TextArea size=\"small\" label=\"Comment\" />\n *\n * // Invalid state\n * <TextArea state=\"invalid\" label=\"Required field\" errorMessage=\"This field is required\" />\n *\n * // Disabled state\n * <TextArea disabled label=\"Read-only field\" />\n * ```\n */\nconst TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(\n (\n {\n label,\n size = 'medium',\n state = 'default',\n errorMessage,\n helperMessage,\n className = '',\n labelClassName = '',\n disabled,\n id,\n onChange,\n placeholder,\n ...props\n },\n ref\n ) => {\n // Generate unique ID if not provided\n const generatedId = useId();\n const inputId = id ?? `textarea-${generatedId}`;\n\n // Internal state for focus tracking\n const [isFocused, setIsFocused] = useState(false);\n\n // Handle change events\n const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {\n onChange?.(event);\n };\n\n // Handle focus events\n const handleFocus = (event: FocusEvent<HTMLTextAreaElement>) => {\n setIsFocused(true);\n props.onFocus?.(event);\n };\n\n // Handle blur events\n const handleBlur = (event: FocusEvent<HTMLTextAreaElement>) => {\n setIsFocused(false);\n props.onBlur?.(event);\n };\n\n // Determine current state based on props and focus\n let currentState = disabled ? 'disabled' : state;\n\n // Override state based on focus\n if (\n isFocused &&\n currentState !== 'invalid' &&\n currentState !== 'disabled'\n ) {\n currentState = 'focused';\n }\n\n // Get size classes\n const sizeClasses = SIZE_CLASSES[size];\n\n // Get styling classes\n const stateClasses = STATE_CLASSES[currentState];\n\n // Get final textarea classes\n const textareaClasses = `${BASE_TEXTAREA_CLASSES} ${sizeClasses.textarea} ${stateClasses.base} ${stateClasses.hover} ${stateClasses.focus} ${className}`;\n\n return (\n <div className={`flex flex-col`}>\n {/* Label */}\n {label && (\n <Text\n as=\"label\"\n htmlFor={inputId}\n size={sizeClasses.textSize}\n weight=\"medium\"\n color=\"text-text-950\"\n className={`mb-1.5 ${labelClassName}`}\n >\n {label}\n </Text>\n )}\n\n {/* Textarea */}\n <textarea\n ref={ref}\n id={inputId}\n disabled={disabled}\n onChange={handleChange}\n onFocus={handleFocus}\n onBlur={handleBlur}\n className={textareaClasses}\n placeholder={placeholder}\n {...props}\n />\n\n {/* Error message */}\n {errorMessage && (\n <Text size=\"sm\" weight=\"normal\" className=\"mt-1.5 text-error-600\">\n {errorMessage}\n </Text>\n )}\n\n {/* Helper text */}\n {helperMessage && !errorMessage && (\n <Text size=\"sm\" weight=\"normal\" className=\"mt-1.5 text-text-500\">\n {helperMessage}\n </Text>\n )}\n </div>\n );\n }\n);\n\nTextArea.displayName = 'TextArea';\n\nexport default TextArea;\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses = '';\n let weightClasses = '';\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={`${baseClasses} ${sizeClasses} ${weightClasses} ${color} ${className}`}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n"],"mappings":"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","names":["import_jsx_runtime"]}
|
package/dist/TextArea/index.mjs
CHANGED
|
@@ -60,29 +60,21 @@ var Text_default = Text;
|
|
|
60
60
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
61
61
|
var SIZE_CLASSES = {
|
|
62
62
|
small: {
|
|
63
|
-
container: "w-72",
|
|
64
|
-
// 288px width
|
|
65
63
|
textarea: "h-24 text-sm",
|
|
66
64
|
// 96px height, 14px font
|
|
67
65
|
textSize: "sm"
|
|
68
66
|
},
|
|
69
67
|
medium: {
|
|
70
|
-
container: "w-72",
|
|
71
|
-
// 288px width
|
|
72
68
|
textarea: "h-24 text-base",
|
|
73
69
|
// 96px height, 16px font
|
|
74
70
|
textSize: "md"
|
|
75
71
|
},
|
|
76
72
|
large: {
|
|
77
|
-
container: "w-72",
|
|
78
|
-
// 288px width
|
|
79
73
|
textarea: "h-24 text-lg",
|
|
80
74
|
// 96px height, 18px font
|
|
81
75
|
textSize: "lg"
|
|
82
76
|
},
|
|
83
77
|
extraLarge: {
|
|
84
|
-
container: "w-72",
|
|
85
|
-
// 288px width
|
|
86
78
|
textarea: "h-24 text-xl",
|
|
87
79
|
// 96px height, 20px font
|
|
88
80
|
textSize: "xl"
|
|
@@ -152,7 +144,7 @@ var TextArea = forwardRef(
|
|
|
152
144
|
const sizeClasses = SIZE_CLASSES[size];
|
|
153
145
|
const stateClasses = STATE_CLASSES[currentState];
|
|
154
146
|
const textareaClasses = `${BASE_TEXTAREA_CLASSES} ${sizeClasses.textarea} ${stateClasses.base} ${stateClasses.hover} ${stateClasses.focus} ${className}`;
|
|
155
|
-
return /* @__PURE__ */ jsxs("div", { className: `flex flex-col
|
|
147
|
+
return /* @__PURE__ */ jsxs("div", { className: `flex flex-col`, children: [
|
|
156
148
|
label && /* @__PURE__ */ jsx2(
|
|
157
149
|
Text_default,
|
|
158
150
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/TextArea/TextArea.tsx","../../src/components/Text/Text.tsx"],"sourcesContent":["import {\n TextareaHTMLAttributes,\n ReactNode,\n forwardRef,\n useState,\n useId,\n ChangeEvent,\n FocusEvent,\n} from 'react';\nimport Text from '../Text/Text';\n\n/**\n * TextArea size variants\n */\ntype TextAreaSize = 'small' | 'medium' | 'large' | 'extraLarge';\n\n/**\n * TextArea visual state\n */\ntype TextAreaState = 'default' | 'hovered' | 'focused' | 'invalid' | 'disabled';\n\n/**\n * Size configurations with exact pixel specifications\n */\nconst SIZE_CLASSES = {\n small: {\n container: 'w-72', // 288px width\n textarea: 'h-24 text-sm', // 96px height, 14px font\n textSize: 'sm' as const,\n },\n medium: {\n container: 'w-72', // 288px width\n textarea: 'h-24 text-base', // 96px height, 16px font\n textSize: 'md' as const,\n },\n large: {\n container: 'w-72', // 288px width\n textarea: 'h-24 text-lg', // 96px height, 18px font\n textSize: 'lg' as const,\n },\n extraLarge: {\n container: 'w-72', // 288px width\n textarea: 'h-24 text-xl', // 96px height, 20px font\n textSize: 'xl' as const,\n },\n} as const;\n\n/**\n * Base textarea styling classes using design system colors\n */\nconst BASE_TEXTAREA_CLASSES =\n 'w-full box-border p-3 bg-background border border-solid rounded-[4px] resize-none focus:outline-none font-roboto font-normal leading-[150%] placeholder:text-text-600 transition-all duration-200';\n\n/**\n * State-based styling classes using design system colors from styles.css\n */\nconst STATE_CLASSES = {\n default: {\n base: 'border-border-300 bg-background text-text-600',\n hover: 'hover:border-border-400',\n focus: 'focus:border-border-500',\n },\n hovered: {\n base: 'border-border-400 bg-background text-text-600',\n hover: '',\n focus: 'focus:border-border-500',\n },\n focused: {\n base: 'border-2 border-primary-950 bg-background text-text-900',\n hover: '',\n focus: '',\n },\n invalid: {\n base: 'border-2 border-red-700 bg-white text-gray-800',\n hover: 'hover:border-red-700',\n focus: 'focus:border-red-700',\n },\n disabled: {\n base: 'border-border-300 bg-background text-text-600 cursor-not-allowed opacity-40',\n hover: '',\n focus: '',\n },\n} as const;\n\n/**\n * TextArea component props interface\n */\nexport type TextAreaProps = {\n /** Label text to display above the textarea */\n label?: ReactNode;\n /** Size variant of the textarea */\n size?: TextAreaSize;\n /** Visual state of the textarea */\n state?: TextAreaState;\n /** Error message to display */\n errorMessage?: string;\n /** Helper text to display */\n helperMessage?: string;\n /** Additional CSS classes */\n className?: string;\n /** Label CSS classes */\n labelClassName?: string;\n} & Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'>;\n\n/**\n * TextArea component for Analytica Ensino platforms\n *\n * A textarea component with essential states, sizes and themes.\n * Uses exact design specifications with 288px width, 96px height, and specific\n * color values. Includes Text component integration for consistent typography.\n *\n * @example\n * ```tsx\n * // Basic textarea\n * <TextArea label=\"Description\" placeholder=\"Enter description...\" />\n *\n * // Small size\n * <TextArea size=\"small\" label=\"Comment\" />\n *\n * // Invalid state\n * <TextArea state=\"invalid\" label=\"Required field\" errorMessage=\"This field is required\" />\n *\n * // Disabled state\n * <TextArea disabled label=\"Read-only field\" />\n * ```\n */\nconst TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(\n (\n {\n label,\n size = 'medium',\n state = 'default',\n errorMessage,\n helperMessage,\n className = '',\n labelClassName = '',\n disabled,\n id,\n onChange,\n placeholder,\n ...props\n },\n ref\n ) => {\n // Generate unique ID if not provided\n const generatedId = useId();\n const inputId = id ?? `textarea-${generatedId}`;\n\n // Internal state for focus tracking\n const [isFocused, setIsFocused] = useState(false);\n\n // Handle change events\n const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {\n onChange?.(event);\n };\n\n // Handle focus events\n const handleFocus = (event: FocusEvent<HTMLTextAreaElement>) => {\n setIsFocused(true);\n props.onFocus?.(event);\n };\n\n // Handle blur events\n const handleBlur = (event: FocusEvent<HTMLTextAreaElement>) => {\n setIsFocused(false);\n props.onBlur?.(event);\n };\n\n // Determine current state based on props and focus\n let currentState = disabled ? 'disabled' : state;\n\n // Override state based on focus\n if (\n isFocused &&\n currentState !== 'invalid' &&\n currentState !== 'disabled'\n ) {\n currentState = 'focused';\n }\n\n // Get size classes\n const sizeClasses = SIZE_CLASSES[size];\n\n // Get styling classes\n const stateClasses = STATE_CLASSES[currentState];\n\n // Get final textarea classes\n const textareaClasses = `${BASE_TEXTAREA_CLASSES} ${sizeClasses.textarea} ${stateClasses.base} ${stateClasses.hover} ${stateClasses.focus} ${className}`;\n\n return (\n <div className={`flex flex-col ${sizeClasses.container}`}>\n {/* Label */}\n {label && (\n <Text\n as=\"label\"\n htmlFor={inputId}\n size={sizeClasses.textSize}\n weight=\"medium\"\n color=\"text-text-950\"\n className={`mb-1.5 ${labelClassName}`}\n >\n {label}\n </Text>\n )}\n\n {/* Textarea */}\n <textarea\n ref={ref}\n id={inputId}\n disabled={disabled}\n onChange={handleChange}\n onFocus={handleFocus}\n onBlur={handleBlur}\n className={textareaClasses}\n placeholder={placeholder}\n {...props}\n />\n\n {/* Error message */}\n {errorMessage && (\n <Text size=\"sm\" weight=\"normal\" className=\"mt-1.5 text-error-600\">\n {errorMessage}\n </Text>\n )}\n\n {/* Helper text */}\n {helperMessage && !errorMessage && (\n <Text size=\"sm\" weight=\"normal\" className=\"mt-1.5 text-text-500\">\n {helperMessage}\n </Text>\n )}\n </div>\n );\n }\n);\n\nTextArea.displayName = 'TextArea';\n\nexport default TextArea;\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses = '';\n let weightClasses = '';\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={`${baseClasses} ${sizeClasses} ${weightClasses} ${color} ${className}`}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n"],"mappings":"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","names":["jsx"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/TextArea/TextArea.tsx","../../src/components/Text/Text.tsx"],"sourcesContent":["import {\n TextareaHTMLAttributes,\n ReactNode,\n forwardRef,\n useState,\n useId,\n ChangeEvent,\n FocusEvent,\n} from 'react';\nimport Text from '../Text/Text';\n\n/**\n * TextArea size variants\n */\ntype TextAreaSize = 'small' | 'medium' | 'large' | 'extraLarge';\n\n/**\n * TextArea visual state\n */\ntype TextAreaState = 'default' | 'hovered' | 'focused' | 'invalid' | 'disabled';\n\n/**\n * Size configurations with exact pixel specifications\n */\nconst SIZE_CLASSES = {\n small: {\n textarea: 'h-24 text-sm', // 96px height, 14px font\n textSize: 'sm' as const,\n },\n medium: {\n textarea: 'h-24 text-base', // 96px height, 16px font\n textSize: 'md' as const,\n },\n large: {\n textarea: 'h-24 text-lg', // 96px height, 18px font\n textSize: 'lg' as const,\n },\n extraLarge: {\n textarea: 'h-24 text-xl', // 96px height, 20px font\n textSize: 'xl' as const,\n },\n} as const;\n\n/**\n * Base textarea styling classes using design system colors\n */\nconst BASE_TEXTAREA_CLASSES =\n 'w-full box-border p-3 bg-background border border-solid rounded-[4px] resize-none focus:outline-none font-roboto font-normal leading-[150%] placeholder:text-text-600 transition-all duration-200';\n\n/**\n * State-based styling classes using design system colors from styles.css\n */\nconst STATE_CLASSES = {\n default: {\n base: 'border-border-300 bg-background text-text-600',\n hover: 'hover:border-border-400',\n focus: 'focus:border-border-500',\n },\n hovered: {\n base: 'border-border-400 bg-background text-text-600',\n hover: '',\n focus: 'focus:border-border-500',\n },\n focused: {\n base: 'border-2 border-primary-950 bg-background text-text-900',\n hover: '',\n focus: '',\n },\n invalid: {\n base: 'border-2 border-red-700 bg-white text-gray-800',\n hover: 'hover:border-red-700',\n focus: 'focus:border-red-700',\n },\n disabled: {\n base: 'border-border-300 bg-background text-text-600 cursor-not-allowed opacity-40',\n hover: '',\n focus: '',\n },\n} as const;\n\n/**\n * TextArea component props interface\n */\nexport type TextAreaProps = {\n /** Label text to display above the textarea */\n label?: ReactNode;\n /** Size variant of the textarea */\n size?: TextAreaSize;\n /** Visual state of the textarea */\n state?: TextAreaState;\n /** Error message to display */\n errorMessage?: string;\n /** Helper text to display */\n helperMessage?: string;\n /** Additional CSS classes */\n className?: string;\n /** Label CSS classes */\n labelClassName?: string;\n} & Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'>;\n\n/**\n * TextArea component for Analytica Ensino platforms\n *\n * A textarea component with essential states, sizes and themes.\n * Uses exact design specifications with 288px width, 96px height, and specific\n * color values. Includes Text component integration for consistent typography.\n *\n * @example\n * ```tsx\n * // Basic textarea\n * <TextArea label=\"Description\" placeholder=\"Enter description...\" />\n *\n * // Small size\n * <TextArea size=\"small\" label=\"Comment\" />\n *\n * // Invalid state\n * <TextArea state=\"invalid\" label=\"Required field\" errorMessage=\"This field is required\" />\n *\n * // Disabled state\n * <TextArea disabled label=\"Read-only field\" />\n * ```\n */\nconst TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(\n (\n {\n label,\n size = 'medium',\n state = 'default',\n errorMessage,\n helperMessage,\n className = '',\n labelClassName = '',\n disabled,\n id,\n onChange,\n placeholder,\n ...props\n },\n ref\n ) => {\n // Generate unique ID if not provided\n const generatedId = useId();\n const inputId = id ?? `textarea-${generatedId}`;\n\n // Internal state for focus tracking\n const [isFocused, setIsFocused] = useState(false);\n\n // Handle change events\n const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {\n onChange?.(event);\n };\n\n // Handle focus events\n const handleFocus = (event: FocusEvent<HTMLTextAreaElement>) => {\n setIsFocused(true);\n props.onFocus?.(event);\n };\n\n // Handle blur events\n const handleBlur = (event: FocusEvent<HTMLTextAreaElement>) => {\n setIsFocused(false);\n props.onBlur?.(event);\n };\n\n // Determine current state based on props and focus\n let currentState = disabled ? 'disabled' : state;\n\n // Override state based on focus\n if (\n isFocused &&\n currentState !== 'invalid' &&\n currentState !== 'disabled'\n ) {\n currentState = 'focused';\n }\n\n // Get size classes\n const sizeClasses = SIZE_CLASSES[size];\n\n // Get styling classes\n const stateClasses = STATE_CLASSES[currentState];\n\n // Get final textarea classes\n const textareaClasses = `${BASE_TEXTAREA_CLASSES} ${sizeClasses.textarea} ${stateClasses.base} ${stateClasses.hover} ${stateClasses.focus} ${className}`;\n\n return (\n <div className={`flex flex-col`}>\n {/* Label */}\n {label && (\n <Text\n as=\"label\"\n htmlFor={inputId}\n size={sizeClasses.textSize}\n weight=\"medium\"\n color=\"text-text-950\"\n className={`mb-1.5 ${labelClassName}`}\n >\n {label}\n </Text>\n )}\n\n {/* Textarea */}\n <textarea\n ref={ref}\n id={inputId}\n disabled={disabled}\n onChange={handleChange}\n onFocus={handleFocus}\n onBlur={handleBlur}\n className={textareaClasses}\n placeholder={placeholder}\n {...props}\n />\n\n {/* Error message */}\n {errorMessage && (\n <Text size=\"sm\" weight=\"normal\" className=\"mt-1.5 text-error-600\">\n {errorMessage}\n </Text>\n )}\n\n {/* Helper text */}\n {helperMessage && !errorMessage && (\n <Text size=\"sm\" weight=\"normal\" className=\"mt-1.5 text-text-500\">\n {helperMessage}\n </Text>\n )}\n </div>\n );\n }\n);\n\nTextArea.displayName = 'TextArea';\n\nexport default TextArea;\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses = '';\n let weightClasses = '';\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={`${baseClasses} ${sizeClasses} ${weightClasses} ${color} ${className}`}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n"],"mappings":"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","names":["jsx"]}
|