analytica-frontend-lib 1.2.79 → 1.2.80

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.
Files changed (39) hide show
  1. package/dist/ActivitiesHistory/index.css +6 -0
  2. package/dist/ActivitiesHistory/index.css.map +1 -1
  3. package/dist/ActivityCardQuestionBanks/index.css +6 -0
  4. package/dist/ActivityCardQuestionBanks/index.css.map +1 -1
  5. package/dist/ActivityCardQuestionPreview/index.css +6 -0
  6. package/dist/ActivityCardQuestionPreview/index.css.map +1 -1
  7. package/dist/ActivityDetails/index.css +6 -0
  8. package/dist/ActivityDetails/index.css.map +1 -1
  9. package/dist/ActivityFilters/index.css +6 -0
  10. package/dist/ActivityFilters/index.css.map +1 -1
  11. package/dist/ActivityPreview/index.css +6 -0
  12. package/dist/ActivityPreview/index.css.map +1 -1
  13. package/dist/AlertManager/index.css +6 -0
  14. package/dist/AlertManager/index.css.map +1 -1
  15. package/dist/RecommendedLessonsHistory/index.css +6 -0
  16. package/dist/RecommendedLessonsHistory/index.css.map +1 -1
  17. package/dist/SendActivityModal/SendActivityModal.css +6 -0
  18. package/dist/SendActivityModal/SendActivityModal.css.map +1 -1
  19. package/dist/SendActivityModal/index.css +6 -0
  20. package/dist/SendActivityModal/index.css.map +1 -1
  21. package/dist/StudentRanking/index.d.ts +69 -0
  22. package/dist/StudentRanking/index.d.ts.map +1 -0
  23. package/dist/StudentRanking/index.js +270 -0
  24. package/dist/StudentRanking/index.js.map +1 -0
  25. package/dist/StudentRanking/index.mjs +244 -0
  26. package/dist/StudentRanking/index.mjs.map +1 -0
  27. package/dist/TableProvider/index.css +6 -0
  28. package/dist/TableProvider/index.css.map +1 -1
  29. package/dist/index.css +6 -0
  30. package/dist/index.css.map +1 -1
  31. package/dist/index.d.ts +2 -0
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +1087 -905
  34. package/dist/index.js.map +1 -1
  35. package/dist/index.mjs +1065 -885
  36. package/dist/index.mjs.map +1 -1
  37. package/dist/styles.css +6 -0
  38. package/dist/styles.css.map +1 -1
  39. package/package.json +2 -1
@@ -0,0 +1,69 @@
1
+ import { HTMLAttributes } from 'react';
2
+ /**
3
+ * Student data item for the ranking
4
+ */
5
+ export interface StudentRankingItem {
6
+ /** Student position in the ranking */
7
+ position: number;
8
+ /** Student name */
9
+ name: string;
10
+ /** Performance percentage (0-100) */
11
+ percentage: number;
12
+ }
13
+ /**
14
+ * Card variant type
15
+ */
16
+ export type StudentRankingVariant = 'highlight' | 'attention';
17
+ /**
18
+ * Props for a single ranking card
19
+ */
20
+ export interface RankingCardProps extends HTMLAttributes<HTMLDivElement> {
21
+ /** Card title */
22
+ title: string;
23
+ /** Card variant: highlight (best students) or attention (needs attention) */
24
+ variant: StudentRankingVariant;
25
+ /** List of students to display */
26
+ students: StudentRankingItem[];
27
+ }
28
+ /**
29
+ * Single ranking card component (can be used independently)
30
+ */
31
+ export declare const RankingCard: ({ title, variant, students, className, ...props }: RankingCardProps) => import("react/jsx-runtime").JSX.Element;
32
+ /**
33
+ * Props for the StudentRanking component
34
+ */
35
+ export interface StudentRankingProps extends HTMLAttributes<HTMLDivElement> {
36
+ /** Title for the highlight (best students) card */
37
+ highlightTitle?: string;
38
+ /** Title for the attention (needs attention) card */
39
+ attentionTitle?: string;
40
+ /** List of highlighted (best performing) students */
41
+ highlightStudents: StudentRankingItem[];
42
+ /** List of students needing attention (lowest performing) */
43
+ attentionStudents: StudentRankingItem[];
44
+ }
45
+ /**
46
+ * StudentRanking component - displays two cards side by side showing
47
+ * the best performing students and students that need attention.
48
+ *
49
+ * @example
50
+ * ```tsx
51
+ * <StudentRanking
52
+ * highlightTitle="Estudantes em destaque"
53
+ * attentionTitle="Estudantes precisando de atenção"
54
+ * highlightStudents={[
55
+ * { position: 1, name: 'Valentina Ribeiro', percentage: 100 },
56
+ * { position: 2, name: 'Lucas Almeida', percentage: 100 },
57
+ * { position: 3, name: 'Fernanda Costa', percentage: 100 },
58
+ * ]}
59
+ * attentionStudents={[
60
+ * { position: 1, name: 'Ricardo Silva', percentage: 80 },
61
+ * { position: 2, name: 'Juliana Santos', percentage: 50 },
62
+ * { position: 3, name: 'Gabriel Oliveira', percentage: 40 },
63
+ * ]}
64
+ * />
65
+ * ```
66
+ */
67
+ export declare const StudentRanking: ({ highlightTitle, attentionTitle, highlightStudents, attentionStudents, className, ...props }: StudentRankingProps) => import("react/jsx-runtime").JSX.Element;
68
+ export default StudentRanking;
69
+ //# sourceMappingURL=StudentRanking.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StudentRanking.d.ts","sourceRoot":"","sources":["../../../src/components/StudentRanking/StudentRanking.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAKvC;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,WAAW,GAAG,WAAW,CAAC;AAmH9D;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,cAAc,CAAC,cAAc,CAAC;IACtE,iBAAiB;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,OAAO,EAAE,qBAAqB,CAAC;IAC/B,kCAAkC;IAClC,QAAQ,EAAE,kBAAkB,EAAE,CAAC;CAChC;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,mDAMzB,gBAAgB,4CAiDlB,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,cAAc,CAAC,cAAc,CAAC;IACzE,mDAAmD;IACnD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,iBAAiB,EAAE,kBAAkB,EAAE,CAAC;IACxC,6DAA6D;IAC7D,iBAAiB,EAAE,kBAAkB,EAAE,CAAC;CACzC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,cAAc,GAAI,+FAO5B,mBAAmB,4CAkBrB,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -0,0 +1,270 @@
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/StudentRanking/StudentRanking.tsx
21
+ var StudentRanking_exports = {};
22
+ __export(StudentRanking_exports, {
23
+ RankingCard: () => RankingCard,
24
+ StudentRanking: () => StudentRanking,
25
+ default: () => StudentRanking_default
26
+ });
27
+ module.exports = __toCommonJS(StudentRanking_exports);
28
+ var import_phosphor_react = require("phosphor-react");
29
+
30
+ // src/utils/utils.ts
31
+ var import_clsx = require("clsx");
32
+ var import_tailwind_merge = require("tailwind-merge");
33
+ function cn(...inputs) {
34
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
35
+ }
36
+
37
+ // src/components/Text/Text.tsx
38
+ var import_jsx_runtime = require("react/jsx-runtime");
39
+ var Text = ({
40
+ children,
41
+ size = "md",
42
+ weight = "normal",
43
+ color = "text-text-950",
44
+ as,
45
+ className = "",
46
+ ...props
47
+ }) => {
48
+ let sizeClasses = "";
49
+ let weightClasses = "";
50
+ const sizeClassMap = {
51
+ "2xs": "text-2xs",
52
+ xs: "text-xs",
53
+ sm: "text-sm",
54
+ md: "text-md",
55
+ lg: "text-lg",
56
+ xl: "text-xl",
57
+ "2xl": "text-2xl",
58
+ "3xl": "text-3xl",
59
+ "4xl": "text-4xl",
60
+ "5xl": "text-5xl",
61
+ "6xl": "text-6xl"
62
+ };
63
+ sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;
64
+ const weightClassMap = {
65
+ hairline: "font-hairline",
66
+ light: "font-light",
67
+ normal: "font-normal",
68
+ medium: "font-medium",
69
+ semibold: "font-semibold",
70
+ bold: "font-bold",
71
+ extrabold: "font-extrabold",
72
+ black: "font-black"
73
+ };
74
+ weightClasses = weightClassMap[weight] ?? weightClassMap.normal;
75
+ const baseClasses = "font-primary";
76
+ const Component = as ?? "p";
77
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
78
+ Component,
79
+ {
80
+ className: cn(baseClasses, sizeClasses, weightClasses, color, className),
81
+ ...props,
82
+ children
83
+ }
84
+ );
85
+ };
86
+ var Text_default = Text;
87
+
88
+ // src/components/StudentRanking/StudentRanking.tsx
89
+ var import_jsx_runtime2 = require("react/jsx-runtime");
90
+ var CARD_BACKGROUND_CLASSES = {
91
+ highlight: {
92
+ 1: "bg-success-200",
93
+ 2: "bg-success-100",
94
+ 3: "bg-success-background"
95
+ },
96
+ attention: {
97
+ 1: "bg-error-200",
98
+ 2: "bg-error-100",
99
+ 3: "bg-error-background"
100
+ }
101
+ };
102
+ var BADGE_BACKGROUND_CLASSES = {
103
+ highlight: "bg-indicator-positive",
104
+ attention: "bg-indicator-negative"
105
+ };
106
+ var PERCENTAGE_BADGE_CLASSES = {
107
+ highlight: "bg-success-700",
108
+ attention: "bg-indicator-negative"
109
+ };
110
+ var HEADER_BADGE_CLASSES = {
111
+ highlight: "bg-indicator-positive",
112
+ attention: "bg-indicator-negative"
113
+ };
114
+ var getPositionBackgroundClass = (variant, position) => {
115
+ const positionKey = Math.max(1, Math.min(position, 3));
116
+ return CARD_BACKGROUND_CLASSES[variant][positionKey];
117
+ };
118
+ var StudentCard = ({ student, variant }) => {
119
+ const TrendIcon = variant === "highlight" ? import_phosphor_react.TrendUp : import_phosphor_react.TrendDown;
120
+ const backgroundClass = getPositionBackgroundClass(variant, student.position);
121
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
122
+ "div",
123
+ {
124
+ className: cn(
125
+ "flex flex-row items-center w-full p-4 gap-2 rounded-xl",
126
+ backgroundClass
127
+ ),
128
+ children: [
129
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
130
+ Text_default,
131
+ {
132
+ size: "xs",
133
+ weight: "bold",
134
+ "aria-label": `Posi\xE7\xE3o ${student.position}`,
135
+ className: cn(
136
+ "w-5 h-5 rounded-full flex items-center justify-center text-text",
137
+ BADGE_BACKGROUND_CLASSES[variant]
138
+ ),
139
+ children: student.position
140
+ }
141
+ ),
142
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
143
+ Text_default,
144
+ {
145
+ size: "sm",
146
+ weight: "bold",
147
+ className: "flex-1 min-w-0 text-text-950 tracking-[0.2px] truncate",
148
+ children: student.name
149
+ }
150
+ ),
151
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
152
+ Text_default,
153
+ {
154
+ size: "xs",
155
+ weight: "bold",
156
+ "aria-label": `Desempenho ${student.percentage}%`,
157
+ className: cn(
158
+ "flex flex-row items-center h-[22px] px-2 gap-1 rounded text-text",
159
+ PERCENTAGE_BADGE_CLASSES[variant]
160
+ ),
161
+ children: [
162
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TrendIcon, { size: 16, weight: "bold", "aria-hidden": "true" }),
163
+ student.percentage,
164
+ "%"
165
+ ]
166
+ }
167
+ )
168
+ ]
169
+ }
170
+ );
171
+ };
172
+ var RankingCard = ({
173
+ title,
174
+ variant,
175
+ students,
176
+ className,
177
+ ...props
178
+ }) => {
179
+ const HeaderIcon = variant === "highlight" ? import_phosphor_react.Trophy : import_phosphor_react.Warning;
180
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
181
+ "div",
182
+ {
183
+ className: cn(
184
+ "flex flex-col flex-1 min-h-[254px] p-5 gap-4 bg-background border border-border-50 rounded-xl",
185
+ className
186
+ ),
187
+ ...props,
188
+ children: [
189
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-row justify-between items-center h-6 gap-4", children: [
190
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
191
+ Text_default,
192
+ {
193
+ as: "h3",
194
+ size: "lg",
195
+ weight: "bold",
196
+ className: "text-text-950 tracking-[0.2px]",
197
+ children: title
198
+ }
199
+ ),
200
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
201
+ "span",
202
+ {
203
+ className: cn(
204
+ "w-6 h-6 rounded-full flex items-center justify-center",
205
+ HEADER_BADGE_CLASSES[variant]
206
+ ),
207
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
208
+ HeaderIcon,
209
+ {
210
+ size: 14,
211
+ weight: "fill",
212
+ className: variant === "highlight" ? "text-text-950" : "text-text"
213
+ }
214
+ )
215
+ }
216
+ )
217
+ ] }),
218
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex flex-col gap-2", children: students.map((student, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
219
+ StudentCard,
220
+ {
221
+ student,
222
+ variant
223
+ },
224
+ `${variant}-${index}-${student.position}`
225
+ )) })
226
+ ]
227
+ }
228
+ );
229
+ };
230
+ var StudentRanking = ({
231
+ highlightTitle = "Estudantes em destaque",
232
+ attentionTitle = "Estudantes precisando de aten\xE7\xE3o",
233
+ highlightStudents,
234
+ attentionStudents,
235
+ className,
236
+ ...props
237
+ }) => {
238
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
239
+ "div",
240
+ {
241
+ className: cn("flex flex-col md:flex-row w-full gap-4", className),
242
+ ...props,
243
+ children: [
244
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
245
+ RankingCard,
246
+ {
247
+ title: highlightTitle,
248
+ variant: "highlight",
249
+ students: highlightStudents
250
+ }
251
+ ),
252
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
253
+ RankingCard,
254
+ {
255
+ title: attentionTitle,
256
+ variant: "attention",
257
+ students: attentionStudents
258
+ }
259
+ )
260
+ ]
261
+ }
262
+ );
263
+ };
264
+ var StudentRanking_default = StudentRanking;
265
+ // Annotate the CommonJS export names for ESM import in node:
266
+ 0 && (module.exports = {
267
+ RankingCard,
268
+ StudentRanking
269
+ });
270
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/StudentRanking/StudentRanking.tsx","../../src/utils/utils.ts","../../src/components/Text/Text.tsx"],"sourcesContent":["import { HTMLAttributes } from 'react';\nimport { Trophy, Warning, TrendUp, TrendDown } from 'phosphor-react';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\n\n/**\n * Student data item for the ranking\n */\nexport interface StudentRankingItem {\n /** Student position in the ranking */\n position: number;\n /** Student name */\n name: string;\n /** Performance percentage (0-100) */\n percentage: number;\n}\n\n/**\n * Card variant type\n */\nexport type StudentRankingVariant = 'highlight' | 'attention';\n\n/**\n * Props for individual student card\n */\ninterface StudentCardProps {\n student: StudentRankingItem;\n variant: StudentRankingVariant;\n}\n\n/**\n * Lookup table for card background colors by position\n * Position 1 = most intense, Position 3+ = lightest\n */\nconst CARD_BACKGROUND_CLASSES = {\n highlight: {\n 1: 'bg-success-200',\n 2: 'bg-success-100',\n 3: 'bg-success-background',\n },\n attention: {\n 1: 'bg-error-200',\n 2: 'bg-error-100',\n 3: 'bg-error-background',\n },\n} as const;\n\n/**\n * Lookup table for badge background colors\n */\nconst BADGE_BACKGROUND_CLASSES = {\n highlight: 'bg-indicator-positive',\n attention: 'bg-indicator-negative',\n} as const;\n\n/**\n * Lookup table for percentage badge background colors\n */\nconst PERCENTAGE_BADGE_CLASSES = {\n highlight: 'bg-success-700',\n attention: 'bg-indicator-negative',\n} as const;\n\n/**\n * Lookup table for header badge background colors\n */\nconst HEADER_BADGE_CLASSES = {\n highlight: 'bg-indicator-positive',\n attention: 'bg-indicator-negative',\n} as const;\n\n/**\n * Get background class based on position (1, 2, or 3+)\n */\nconst getPositionBackgroundClass = (\n variant: StudentRankingVariant,\n position: number\n): string => {\n const positionKey = Math.max(1, Math.min(position, 3)) as 1 | 2 | 3;\n return CARD_BACKGROUND_CLASSES[variant][positionKey];\n};\n\n/**\n * Individual student card component\n */\nconst StudentCard = ({ student, variant }: StudentCardProps) => {\n const TrendIcon = variant === 'highlight' ? TrendUp : TrendDown;\n const backgroundClass = getPositionBackgroundClass(variant, student.position);\n\n return (\n <div\n className={cn(\n 'flex flex-row items-center w-full p-4 gap-2 rounded-xl',\n backgroundClass\n )}\n >\n {/* Position badge */}\n <Text\n size=\"xs\"\n weight=\"bold\"\n aria-label={`Posição ${student.position}`}\n className={cn(\n 'w-5 h-5 rounded-full flex items-center justify-center text-text',\n BADGE_BACKGROUND_CLASSES[variant]\n )}\n >\n {student.position}\n </Text>\n\n {/* Student name */}\n <Text\n size=\"sm\"\n weight=\"bold\"\n className=\"flex-1 min-w-0 text-text-950 tracking-[0.2px] truncate\"\n >\n {student.name}\n </Text>\n\n {/* Percentage badge */}\n <Text\n size=\"xs\"\n weight=\"bold\"\n aria-label={`Desempenho ${student.percentage}%`}\n className={cn(\n 'flex flex-row items-center h-[22px] px-2 gap-1 rounded text-text',\n PERCENTAGE_BADGE_CLASSES[variant]\n )}\n >\n <TrendIcon size={16} weight=\"bold\" aria-hidden=\"true\" />\n {student.percentage}%\n </Text>\n </div>\n );\n};\n\n/**\n * Props for a single ranking card\n */\nexport interface RankingCardProps extends HTMLAttributes<HTMLDivElement> {\n /** Card title */\n title: string;\n /** Card variant: highlight (best students) or attention (needs attention) */\n variant: StudentRankingVariant;\n /** List of students to display */\n students: StudentRankingItem[];\n}\n\n/**\n * Single ranking card component (can be used independently)\n */\nexport const RankingCard = ({\n title,\n variant,\n students,\n className,\n ...props\n}: RankingCardProps) => {\n const HeaderIcon = variant === 'highlight' ? Trophy : Warning;\n\n return (\n <div\n className={cn(\n 'flex flex-col flex-1 min-h-[254px] p-5 gap-4 bg-background border border-border-50 rounded-xl',\n className\n )}\n {...props}\n >\n {/* Header */}\n <div className=\"flex flex-row justify-between items-center h-6 gap-4\">\n <Text\n as=\"h3\"\n size=\"lg\"\n weight=\"bold\"\n className=\"text-text-950 tracking-[0.2px]\"\n >\n {title}\n </Text>\n\n {/* Header badge with icon */}\n <span\n className={cn(\n 'w-6 h-6 rounded-full flex items-center justify-center',\n HEADER_BADGE_CLASSES[variant]\n )}\n >\n <HeaderIcon\n size={14}\n weight=\"fill\"\n className={variant === 'highlight' ? 'text-text-950' : 'text-text'}\n />\n </span>\n </div>\n\n {/* Students list */}\n <div className=\"flex flex-col gap-2\">\n {students.map((student, index) => (\n <StudentCard\n key={`${variant}-${index}-${student.position}`}\n student={student}\n variant={variant}\n />\n ))}\n </div>\n </div>\n );\n};\n\n/**\n * Props for the StudentRanking component\n */\nexport interface StudentRankingProps extends HTMLAttributes<HTMLDivElement> {\n /** Title for the highlight (best students) card */\n highlightTitle?: string;\n /** Title for the attention (needs attention) card */\n attentionTitle?: string;\n /** List of highlighted (best performing) students */\n highlightStudents: StudentRankingItem[];\n /** List of students needing attention (lowest performing) */\n attentionStudents: StudentRankingItem[];\n}\n\n/**\n * StudentRanking component - displays two cards side by side showing\n * the best performing students and students that need attention.\n *\n * @example\n * ```tsx\n * <StudentRanking\n * highlightTitle=\"Estudantes em destaque\"\n * attentionTitle=\"Estudantes precisando de atenção\"\n * highlightStudents={[\n * { position: 1, name: 'Valentina Ribeiro', percentage: 100 },\n * { position: 2, name: 'Lucas Almeida', percentage: 100 },\n * { position: 3, name: 'Fernanda Costa', percentage: 100 },\n * ]}\n * attentionStudents={[\n * { position: 1, name: 'Ricardo Silva', percentage: 80 },\n * { position: 2, name: 'Juliana Santos', percentage: 50 },\n * { position: 3, name: 'Gabriel Oliveira', percentage: 40 },\n * ]}\n * />\n * ```\n */\nexport const StudentRanking = ({\n highlightTitle = 'Estudantes em destaque',\n attentionTitle = 'Estudantes precisando de atenção',\n highlightStudents,\n attentionStudents,\n className,\n ...props\n}: StudentRankingProps) => {\n return (\n <div\n className={cn('flex flex-col md:flex-row w-full gap-4', className)}\n {...props}\n >\n <RankingCard\n title={highlightTitle}\n variant=\"highlight\"\n students={highlightStudents}\n />\n <RankingCard\n title={attentionTitle}\n variant=\"attention\"\n students={attentionStudents}\n />\n </div>\n );\n};\n\nexport default StudentRanking;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n areFiltersEqual,\n} from './activityFilters';\nexport {\n mapQuestionTypeToEnum,\n mapQuestionTypeToEnumRequired,\n} from './questionTypeUtils';\nexport {\n getStatusBadgeConfig,\n formatTimeSpent,\n formatQuestionNumbers,\n formatDateToBrazilian,\n} from './activityDetailsUtils';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\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={cn(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"]}
@@ -0,0 +1,244 @@
1
+ // src/components/StudentRanking/StudentRanking.tsx
2
+ import { Trophy, Warning, TrendUp, TrendDown } from "phosphor-react";
3
+
4
+ // src/utils/utils.ts
5
+ import { clsx } from "clsx";
6
+ import { twMerge } from "tailwind-merge";
7
+ function cn(...inputs) {
8
+ return twMerge(clsx(inputs));
9
+ }
10
+
11
+ // src/components/Text/Text.tsx
12
+ import { jsx } from "react/jsx-runtime";
13
+ var Text = ({
14
+ children,
15
+ size = "md",
16
+ weight = "normal",
17
+ color = "text-text-950",
18
+ as,
19
+ className = "",
20
+ ...props
21
+ }) => {
22
+ let sizeClasses = "";
23
+ let weightClasses = "";
24
+ const sizeClassMap = {
25
+ "2xs": "text-2xs",
26
+ xs: "text-xs",
27
+ sm: "text-sm",
28
+ md: "text-md",
29
+ lg: "text-lg",
30
+ xl: "text-xl",
31
+ "2xl": "text-2xl",
32
+ "3xl": "text-3xl",
33
+ "4xl": "text-4xl",
34
+ "5xl": "text-5xl",
35
+ "6xl": "text-6xl"
36
+ };
37
+ sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;
38
+ const weightClassMap = {
39
+ hairline: "font-hairline",
40
+ light: "font-light",
41
+ normal: "font-normal",
42
+ medium: "font-medium",
43
+ semibold: "font-semibold",
44
+ bold: "font-bold",
45
+ extrabold: "font-extrabold",
46
+ black: "font-black"
47
+ };
48
+ weightClasses = weightClassMap[weight] ?? weightClassMap.normal;
49
+ const baseClasses = "font-primary";
50
+ const Component = as ?? "p";
51
+ return /* @__PURE__ */ jsx(
52
+ Component,
53
+ {
54
+ className: cn(baseClasses, sizeClasses, weightClasses, color, className),
55
+ ...props,
56
+ children
57
+ }
58
+ );
59
+ };
60
+ var Text_default = Text;
61
+
62
+ // src/components/StudentRanking/StudentRanking.tsx
63
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
64
+ var CARD_BACKGROUND_CLASSES = {
65
+ highlight: {
66
+ 1: "bg-success-200",
67
+ 2: "bg-success-100",
68
+ 3: "bg-success-background"
69
+ },
70
+ attention: {
71
+ 1: "bg-error-200",
72
+ 2: "bg-error-100",
73
+ 3: "bg-error-background"
74
+ }
75
+ };
76
+ var BADGE_BACKGROUND_CLASSES = {
77
+ highlight: "bg-indicator-positive",
78
+ attention: "bg-indicator-negative"
79
+ };
80
+ var PERCENTAGE_BADGE_CLASSES = {
81
+ highlight: "bg-success-700",
82
+ attention: "bg-indicator-negative"
83
+ };
84
+ var HEADER_BADGE_CLASSES = {
85
+ highlight: "bg-indicator-positive",
86
+ attention: "bg-indicator-negative"
87
+ };
88
+ var getPositionBackgroundClass = (variant, position) => {
89
+ const positionKey = Math.max(1, Math.min(position, 3));
90
+ return CARD_BACKGROUND_CLASSES[variant][positionKey];
91
+ };
92
+ var StudentCard = ({ student, variant }) => {
93
+ const TrendIcon = variant === "highlight" ? TrendUp : TrendDown;
94
+ const backgroundClass = getPositionBackgroundClass(variant, student.position);
95
+ return /* @__PURE__ */ jsxs(
96
+ "div",
97
+ {
98
+ className: cn(
99
+ "flex flex-row items-center w-full p-4 gap-2 rounded-xl",
100
+ backgroundClass
101
+ ),
102
+ children: [
103
+ /* @__PURE__ */ jsx2(
104
+ Text_default,
105
+ {
106
+ size: "xs",
107
+ weight: "bold",
108
+ "aria-label": `Posi\xE7\xE3o ${student.position}`,
109
+ className: cn(
110
+ "w-5 h-5 rounded-full flex items-center justify-center text-text",
111
+ BADGE_BACKGROUND_CLASSES[variant]
112
+ ),
113
+ children: student.position
114
+ }
115
+ ),
116
+ /* @__PURE__ */ jsx2(
117
+ Text_default,
118
+ {
119
+ size: "sm",
120
+ weight: "bold",
121
+ className: "flex-1 min-w-0 text-text-950 tracking-[0.2px] truncate",
122
+ children: student.name
123
+ }
124
+ ),
125
+ /* @__PURE__ */ jsxs(
126
+ Text_default,
127
+ {
128
+ size: "xs",
129
+ weight: "bold",
130
+ "aria-label": `Desempenho ${student.percentage}%`,
131
+ className: cn(
132
+ "flex flex-row items-center h-[22px] px-2 gap-1 rounded text-text",
133
+ PERCENTAGE_BADGE_CLASSES[variant]
134
+ ),
135
+ children: [
136
+ /* @__PURE__ */ jsx2(TrendIcon, { size: 16, weight: "bold", "aria-hidden": "true" }),
137
+ student.percentage,
138
+ "%"
139
+ ]
140
+ }
141
+ )
142
+ ]
143
+ }
144
+ );
145
+ };
146
+ var RankingCard = ({
147
+ title,
148
+ variant,
149
+ students,
150
+ className,
151
+ ...props
152
+ }) => {
153
+ const HeaderIcon = variant === "highlight" ? Trophy : Warning;
154
+ return /* @__PURE__ */ jsxs(
155
+ "div",
156
+ {
157
+ className: cn(
158
+ "flex flex-col flex-1 min-h-[254px] p-5 gap-4 bg-background border border-border-50 rounded-xl",
159
+ className
160
+ ),
161
+ ...props,
162
+ children: [
163
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-row justify-between items-center h-6 gap-4", children: [
164
+ /* @__PURE__ */ jsx2(
165
+ Text_default,
166
+ {
167
+ as: "h3",
168
+ size: "lg",
169
+ weight: "bold",
170
+ className: "text-text-950 tracking-[0.2px]",
171
+ children: title
172
+ }
173
+ ),
174
+ /* @__PURE__ */ jsx2(
175
+ "span",
176
+ {
177
+ className: cn(
178
+ "w-6 h-6 rounded-full flex items-center justify-center",
179
+ HEADER_BADGE_CLASSES[variant]
180
+ ),
181
+ children: /* @__PURE__ */ jsx2(
182
+ HeaderIcon,
183
+ {
184
+ size: 14,
185
+ weight: "fill",
186
+ className: variant === "highlight" ? "text-text-950" : "text-text"
187
+ }
188
+ )
189
+ }
190
+ )
191
+ ] }),
192
+ /* @__PURE__ */ jsx2("div", { className: "flex flex-col gap-2", children: students.map((student, index) => /* @__PURE__ */ jsx2(
193
+ StudentCard,
194
+ {
195
+ student,
196
+ variant
197
+ },
198
+ `${variant}-${index}-${student.position}`
199
+ )) })
200
+ ]
201
+ }
202
+ );
203
+ };
204
+ var StudentRanking = ({
205
+ highlightTitle = "Estudantes em destaque",
206
+ attentionTitle = "Estudantes precisando de aten\xE7\xE3o",
207
+ highlightStudents,
208
+ attentionStudents,
209
+ className,
210
+ ...props
211
+ }) => {
212
+ return /* @__PURE__ */ jsxs(
213
+ "div",
214
+ {
215
+ className: cn("flex flex-col md:flex-row w-full gap-4", className),
216
+ ...props,
217
+ children: [
218
+ /* @__PURE__ */ jsx2(
219
+ RankingCard,
220
+ {
221
+ title: highlightTitle,
222
+ variant: "highlight",
223
+ students: highlightStudents
224
+ }
225
+ ),
226
+ /* @__PURE__ */ jsx2(
227
+ RankingCard,
228
+ {
229
+ title: attentionTitle,
230
+ variant: "attention",
231
+ students: attentionStudents
232
+ }
233
+ )
234
+ ]
235
+ }
236
+ );
237
+ };
238
+ var StudentRanking_default = StudentRanking;
239
+ export {
240
+ RankingCard,
241
+ StudentRanking,
242
+ StudentRanking_default as default
243
+ };
244
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/StudentRanking/StudentRanking.tsx","../../src/utils/utils.ts","../../src/components/Text/Text.tsx"],"sourcesContent":["import { HTMLAttributes } from 'react';\nimport { Trophy, Warning, TrendUp, TrendDown } from 'phosphor-react';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\n\n/**\n * Student data item for the ranking\n */\nexport interface StudentRankingItem {\n /** Student position in the ranking */\n position: number;\n /** Student name */\n name: string;\n /** Performance percentage (0-100) */\n percentage: number;\n}\n\n/**\n * Card variant type\n */\nexport type StudentRankingVariant = 'highlight' | 'attention';\n\n/**\n * Props for individual student card\n */\ninterface StudentCardProps {\n student: StudentRankingItem;\n variant: StudentRankingVariant;\n}\n\n/**\n * Lookup table for card background colors by position\n * Position 1 = most intense, Position 3+ = lightest\n */\nconst CARD_BACKGROUND_CLASSES = {\n highlight: {\n 1: 'bg-success-200',\n 2: 'bg-success-100',\n 3: 'bg-success-background',\n },\n attention: {\n 1: 'bg-error-200',\n 2: 'bg-error-100',\n 3: 'bg-error-background',\n },\n} as const;\n\n/**\n * Lookup table for badge background colors\n */\nconst BADGE_BACKGROUND_CLASSES = {\n highlight: 'bg-indicator-positive',\n attention: 'bg-indicator-negative',\n} as const;\n\n/**\n * Lookup table for percentage badge background colors\n */\nconst PERCENTAGE_BADGE_CLASSES = {\n highlight: 'bg-success-700',\n attention: 'bg-indicator-negative',\n} as const;\n\n/**\n * Lookup table for header badge background colors\n */\nconst HEADER_BADGE_CLASSES = {\n highlight: 'bg-indicator-positive',\n attention: 'bg-indicator-negative',\n} as const;\n\n/**\n * Get background class based on position (1, 2, or 3+)\n */\nconst getPositionBackgroundClass = (\n variant: StudentRankingVariant,\n position: number\n): string => {\n const positionKey = Math.max(1, Math.min(position, 3)) as 1 | 2 | 3;\n return CARD_BACKGROUND_CLASSES[variant][positionKey];\n};\n\n/**\n * Individual student card component\n */\nconst StudentCard = ({ student, variant }: StudentCardProps) => {\n const TrendIcon = variant === 'highlight' ? TrendUp : TrendDown;\n const backgroundClass = getPositionBackgroundClass(variant, student.position);\n\n return (\n <div\n className={cn(\n 'flex flex-row items-center w-full p-4 gap-2 rounded-xl',\n backgroundClass\n )}\n >\n {/* Position badge */}\n <Text\n size=\"xs\"\n weight=\"bold\"\n aria-label={`Posição ${student.position}`}\n className={cn(\n 'w-5 h-5 rounded-full flex items-center justify-center text-text',\n BADGE_BACKGROUND_CLASSES[variant]\n )}\n >\n {student.position}\n </Text>\n\n {/* Student name */}\n <Text\n size=\"sm\"\n weight=\"bold\"\n className=\"flex-1 min-w-0 text-text-950 tracking-[0.2px] truncate\"\n >\n {student.name}\n </Text>\n\n {/* Percentage badge */}\n <Text\n size=\"xs\"\n weight=\"bold\"\n aria-label={`Desempenho ${student.percentage}%`}\n className={cn(\n 'flex flex-row items-center h-[22px] px-2 gap-1 rounded text-text',\n PERCENTAGE_BADGE_CLASSES[variant]\n )}\n >\n <TrendIcon size={16} weight=\"bold\" aria-hidden=\"true\" />\n {student.percentage}%\n </Text>\n </div>\n );\n};\n\n/**\n * Props for a single ranking card\n */\nexport interface RankingCardProps extends HTMLAttributes<HTMLDivElement> {\n /** Card title */\n title: string;\n /** Card variant: highlight (best students) or attention (needs attention) */\n variant: StudentRankingVariant;\n /** List of students to display */\n students: StudentRankingItem[];\n}\n\n/**\n * Single ranking card component (can be used independently)\n */\nexport const RankingCard = ({\n title,\n variant,\n students,\n className,\n ...props\n}: RankingCardProps) => {\n const HeaderIcon = variant === 'highlight' ? Trophy : Warning;\n\n return (\n <div\n className={cn(\n 'flex flex-col flex-1 min-h-[254px] p-5 gap-4 bg-background border border-border-50 rounded-xl',\n className\n )}\n {...props}\n >\n {/* Header */}\n <div className=\"flex flex-row justify-between items-center h-6 gap-4\">\n <Text\n as=\"h3\"\n size=\"lg\"\n weight=\"bold\"\n className=\"text-text-950 tracking-[0.2px]\"\n >\n {title}\n </Text>\n\n {/* Header badge with icon */}\n <span\n className={cn(\n 'w-6 h-6 rounded-full flex items-center justify-center',\n HEADER_BADGE_CLASSES[variant]\n )}\n >\n <HeaderIcon\n size={14}\n weight=\"fill\"\n className={variant === 'highlight' ? 'text-text-950' : 'text-text'}\n />\n </span>\n </div>\n\n {/* Students list */}\n <div className=\"flex flex-col gap-2\">\n {students.map((student, index) => (\n <StudentCard\n key={`${variant}-${index}-${student.position}`}\n student={student}\n variant={variant}\n />\n ))}\n </div>\n </div>\n );\n};\n\n/**\n * Props for the StudentRanking component\n */\nexport interface StudentRankingProps extends HTMLAttributes<HTMLDivElement> {\n /** Title for the highlight (best students) card */\n highlightTitle?: string;\n /** Title for the attention (needs attention) card */\n attentionTitle?: string;\n /** List of highlighted (best performing) students */\n highlightStudents: StudentRankingItem[];\n /** List of students needing attention (lowest performing) */\n attentionStudents: StudentRankingItem[];\n}\n\n/**\n * StudentRanking component - displays two cards side by side showing\n * the best performing students and students that need attention.\n *\n * @example\n * ```tsx\n * <StudentRanking\n * highlightTitle=\"Estudantes em destaque\"\n * attentionTitle=\"Estudantes precisando de atenção\"\n * highlightStudents={[\n * { position: 1, name: 'Valentina Ribeiro', percentage: 100 },\n * { position: 2, name: 'Lucas Almeida', percentage: 100 },\n * { position: 3, name: 'Fernanda Costa', percentage: 100 },\n * ]}\n * attentionStudents={[\n * { position: 1, name: 'Ricardo Silva', percentage: 80 },\n * { position: 2, name: 'Juliana Santos', percentage: 50 },\n * { position: 3, name: 'Gabriel Oliveira', percentage: 40 },\n * ]}\n * />\n * ```\n */\nexport const StudentRanking = ({\n highlightTitle = 'Estudantes em destaque',\n attentionTitle = 'Estudantes precisando de atenção',\n highlightStudents,\n attentionStudents,\n className,\n ...props\n}: StudentRankingProps) => {\n return (\n <div\n className={cn('flex flex-col md:flex-row w-full gap-4', className)}\n {...props}\n >\n <RankingCard\n title={highlightTitle}\n variant=\"highlight\"\n students={highlightStudents}\n />\n <RankingCard\n title={attentionTitle}\n variant=\"attention\"\n students={attentionStudents}\n />\n </div>\n );\n};\n\nexport default StudentRanking;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n areFiltersEqual,\n} from './activityFilters';\nexport {\n mapQuestionTypeToEnum,\n mapQuestionTypeToEnumRequired,\n} from './questionTypeUtils';\nexport {\n getStatusBadgeConfig,\n formatTimeSpent,\n formatQuestionNumbers,\n formatDateToBrazilian,\n} from './activityDetailsUtils';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\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={cn(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"]}