@ginia/ui 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/domain/resume/resume-preview/resume-preview.cjs +4 -9
- package/dist/components/domain/resume/resume-preview/resume-preview.cjs.map +1 -1
- package/dist/components/domain/resume/resume-preview/resume-preview.js +4 -9
- package/dist/components/domain/resume/resume-preview/resume-preview.js.map +1 -1
- package/dist/components/domain/resume/resume.cjs +9 -0
- package/dist/components/domain/resume/resume.cjs.map +1 -1
- package/dist/components/domain/resume/resume.js +9 -0
- package/dist/components/domain/resume/resume.js.map +1 -1
- package/dist/components/ui/combobox/combobox.cjs +4 -3
- package/dist/components/ui/combobox/combobox.cjs.map +1 -1
- package/dist/components/ui/combobox/combobox.js +4 -3
- package/dist/components/ui/combobox/combobox.js.map +1 -1
- package/dist/components/ui/command/command.cjs +5 -2
- package/dist/components/ui/command/command.cjs.map +1 -1
- package/dist/components/ui/command/command.d.cts +1 -1
- package/dist/components/ui/command/command.d.ts +1 -1
- package/dist/components/ui/command/command.js +5 -2
- package/dist/components/ui/command/command.js.map +1 -1
- package/dist/components/ui/rich-text-editor/rich-text-editor.cjs +1 -1
- package/dist/components/ui/rich-text-editor/rich-text-editor.cjs.map +1 -1
- package/dist/components/ui/rich-text-editor/rich-text-editor.js +1 -1
- package/dist/components/ui/rich-text-editor/rich-text-editor.js.map +1 -1
- package/package.json +1 -1
|
@@ -41,25 +41,20 @@ var import_experience_education = require("./sections/experience-education");
|
|
|
41
41
|
var import_footer_preview = require("./sections/footer-preview");
|
|
42
42
|
const ResumePreview = React.forwardRef(
|
|
43
43
|
function ResumePreview2({ data, headerSlot, theme, hideFields = [], hideSections = [], showTopBar = true, className }, ref) {
|
|
44
|
-
const themeStyle = theme ? { backgroundColor: theme } : void 0;
|
|
45
44
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
46
45
|
"div",
|
|
47
46
|
{
|
|
48
47
|
ref,
|
|
49
48
|
id: "resume-capture-area",
|
|
49
|
+
style: showTopBar && theme ? { borderTopColor: theme } : void 0,
|
|
50
50
|
className: (0, import_utils.cn)(
|
|
51
|
-
"relative mx-auto flex min-h-[1056px] w-full flex-col rounded-
|
|
51
|
+
"relative mx-auto flex min-h-[1056px] w-full flex-col rounded-lg border border-border bg-card p-8 text-card-foreground shadow-2xl sm:p-12 md:p-14 pdf-border-line",
|
|
52
|
+
showTopBar && "border-t-8",
|
|
53
|
+
showTopBar && !theme && "border-t-primary",
|
|
52
54
|
className
|
|
53
55
|
),
|
|
54
56
|
children: [
|
|
55
57
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Background, {}),
|
|
56
|
-
showTopBar && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
57
|
-
"div",
|
|
58
|
-
{
|
|
59
|
-
className: `absolute left-0 top-0 h-2 w-full ${theme ? "" : "bg-gradient-to-r from-primary to-primary/50"}`,
|
|
60
|
-
style: themeStyle
|
|
61
|
-
}
|
|
62
|
-
),
|
|
63
58
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative z-10", children: [
|
|
64
59
|
!hideSections.includes("Informaci\xF3n personal") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
65
60
|
import_header_preview.HeaderPreview,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/components/domain/resume/resume-preview/resume-preview.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { CandidateProfile } from \"../types\";\nimport { Background } from \"../../../ui\";\nimport { cn } from \"../../../../lib/utils\";\nimport { HeaderPreview } from \"./sections/header-preview\";\nimport { ContactSkills } from \"./sections/contact-skills\";\nimport { ExperienceEducation } from \"./sections/experience-education\";\nimport { FooterPreview } from \"./sections/footer-preview\";\n\nexport interface ResumePreviewProps {\n data: CandidateProfile;\n headerSlot?: React.ReactNode;\n theme?: string;\n hideFields?: string[];\n hideSections?: string[];\n showTopBar?: boolean;\n className?: string;\n}\n\nexport const ResumePreview = React.forwardRef<HTMLDivElement, ResumePreviewProps>(\n function ResumePreview(\n { data, headerSlot, theme, hideFields = [], hideSections = [], showTopBar = true, className },\n ref,\n ) {\n
|
|
1
|
+
{"version":3,"sources":["../../../../../src/components/domain/resume/resume-preview/resume-preview.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { CandidateProfile } from \"../types\";\nimport { Background } from \"../../../ui\";\nimport { cn } from \"../../../../lib/utils\";\nimport { HeaderPreview } from \"./sections/header-preview\";\nimport { ContactSkills } from \"./sections/contact-skills\";\nimport { ExperienceEducation } from \"./sections/experience-education\";\nimport { FooterPreview } from \"./sections/footer-preview\";\n\nexport interface ResumePreviewProps {\n data: CandidateProfile;\n headerSlot?: React.ReactNode;\n theme?: string;\n hideFields?: string[];\n hideSections?: string[];\n showTopBar?: boolean;\n className?: string;\n}\n\nexport const ResumePreview = React.forwardRef<HTMLDivElement, ResumePreviewProps>(\n function ResumePreview(\n { data, headerSlot, theme, hideFields = [], hideSections = [], showTopBar = true, className },\n ref,\n ) {\n return (\n <div\n ref={ref}\n id=\"resume-capture-area\"\n style={showTopBar && theme ? { borderTopColor: theme } : undefined}\n className={cn(\n \"relative mx-auto flex min-h-[1056px] w-full flex-col rounded-lg border border-border bg-card p-8 text-card-foreground shadow-2xl sm:p-12 md:p-14 pdf-border-line\",\n showTopBar && \"border-t-8\",\n showTopBar && !theme && \"border-t-primary\",\n className,\n\n )}\n >\n <Background />\n {/* Content layer — above background */}\n <div className=\"relative z-10\">\n {/* Header Profile */}\n {!hideSections.includes(\"Información personal\") && (\n <HeaderPreview\n data={data}\n headerSlot={headerSlot}\n theme={theme}\n hideFields={hideFields}\n />\n )}\n\n {/* Main Content Layout */}\n <div className=\"mt-8 mb-8 grid grid-cols-1 gap-10 lg:grid-cols-12\">\n {/* Left Column (4/12) */}\n <div className=\"space-y-8 lg:col-span-4 lg:pr-6\">\n <ContactSkills data={data} hideFields={hideFields} hideSections={hideSections} />\n </div>\n\n {/* Right Column (8/12) - border-l acts as divider, avoids overlap with long content */}\n <div className=\"space-y-8 lg:col-span-8 lg:border-l lg:border-border/40 lg:pl-8\">\n <ExperienceEducation\n data={data}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n />\n </div>\n </div>\n\n <FooterPreview />\n </div>\n </div>\n );\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAqCQ;AArCR,YAAuB;AAEvB,gBAA2B;AAC3B,mBAAmB;AACnB,4BAA8B;AAC9B,4BAA8B;AAC9B,kCAAoC;AACpC,4BAA8B;AAYvB,MAAM,gBAAgB,MAAM;AAAA,EACjC,SAASA,eACP,EAAE,MAAM,YAAY,OAAO,aAAa,CAAC,GAAG,eAAe,CAAC,GAAG,aAAa,MAAM,UAAU,GAC5F,KACA;AACA,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,IAAG;AAAA,QACH,OAAO,cAAc,QAAQ,EAAE,gBAAgB,MAAM,IAAI;AAAA,QACzD,eAAW;AAAA,UACT;AAAA,UACA,cAAc;AAAA,UACd,cAAc,CAAC,SAAS;AAAA,UACxB;AAAA,QAEF;AAAA,QAEA;AAAA,sDAAC,wBAAW;AAAA,UAEZ,6CAAC,SAAI,WAAU,iBAEZ;AAAA,aAAC,aAAa,SAAS,yBAAsB,KAC5C;AAAA,cAAC;AAAA;AAAA,gBACC;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA;AAAA,YACF;AAAA,YAIF,6CAAC,SAAI,WAAU,qDAEb;AAAA,0DAAC,SAAI,WAAU,mCACb,sDAAC,uCAAc,MAAY,YAAwB,cAA4B,GACjF;AAAA,cAGA,4CAAC,SAAI,WAAU,mEACb;AAAA,gBAAC;AAAA;AAAA,kBACC;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA;AAAA,cACF,GACF;AAAA,eACF;AAAA,YAEA,4CAAC,uCAAc;AAAA,aACjB;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AACF;","names":["ResumePreview"]}
|
|
@@ -8,25 +8,20 @@ import { ExperienceEducation } from "./sections/experience-education";
|
|
|
8
8
|
import { FooterPreview } from "./sections/footer-preview";
|
|
9
9
|
const ResumePreview = React.forwardRef(
|
|
10
10
|
function ResumePreview2({ data, headerSlot, theme, hideFields = [], hideSections = [], showTopBar = true, className }, ref) {
|
|
11
|
-
const themeStyle = theme ? { backgroundColor: theme } : void 0;
|
|
12
11
|
return /* @__PURE__ */ jsxs(
|
|
13
12
|
"div",
|
|
14
13
|
{
|
|
15
14
|
ref,
|
|
16
15
|
id: "resume-capture-area",
|
|
16
|
+
style: showTopBar && theme ? { borderTopColor: theme } : void 0,
|
|
17
17
|
className: cn(
|
|
18
|
-
"relative mx-auto flex min-h-[1056px] w-full flex-col rounded-
|
|
18
|
+
"relative mx-auto flex min-h-[1056px] w-full flex-col rounded-lg border border-border bg-card p-8 text-card-foreground shadow-2xl sm:p-12 md:p-14 pdf-border-line",
|
|
19
|
+
showTopBar && "border-t-8",
|
|
20
|
+
showTopBar && !theme && "border-t-primary",
|
|
19
21
|
className
|
|
20
22
|
),
|
|
21
23
|
children: [
|
|
22
24
|
/* @__PURE__ */ jsx(Background, {}),
|
|
23
|
-
showTopBar && /* @__PURE__ */ jsx(
|
|
24
|
-
"div",
|
|
25
|
-
{
|
|
26
|
-
className: `absolute left-0 top-0 h-2 w-full ${theme ? "" : "bg-gradient-to-r from-primary to-primary/50"}`,
|
|
27
|
-
style: themeStyle
|
|
28
|
-
}
|
|
29
|
-
),
|
|
30
25
|
/* @__PURE__ */ jsxs("div", { className: "relative z-10", children: [
|
|
31
26
|
!hideSections.includes("Informaci\xF3n personal") && /* @__PURE__ */ jsx(
|
|
32
27
|
HeaderPreview,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/components/domain/resume/resume-preview/resume-preview.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { CandidateProfile } from \"../types\";\nimport { Background } from \"../../../ui\";\nimport { cn } from \"../../../../lib/utils\";\nimport { HeaderPreview } from \"./sections/header-preview\";\nimport { ContactSkills } from \"./sections/contact-skills\";\nimport { ExperienceEducation } from \"./sections/experience-education\";\nimport { FooterPreview } from \"./sections/footer-preview\";\n\nexport interface ResumePreviewProps {\n data: CandidateProfile;\n headerSlot?: React.ReactNode;\n theme?: string;\n hideFields?: string[];\n hideSections?: string[];\n showTopBar?: boolean;\n className?: string;\n}\n\nexport const ResumePreview = React.forwardRef<HTMLDivElement, ResumePreviewProps>(\n function ResumePreview(\n { data, headerSlot, theme, hideFields = [], hideSections = [], showTopBar = true, className },\n ref,\n ) {\n
|
|
1
|
+
{"version":3,"sources":["../../../../../src/components/domain/resume/resume-preview/resume-preview.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { CandidateProfile } from \"../types\";\nimport { Background } from \"../../../ui\";\nimport { cn } from \"../../../../lib/utils\";\nimport { HeaderPreview } from \"./sections/header-preview\";\nimport { ContactSkills } from \"./sections/contact-skills\";\nimport { ExperienceEducation } from \"./sections/experience-education\";\nimport { FooterPreview } from \"./sections/footer-preview\";\n\nexport interface ResumePreviewProps {\n data: CandidateProfile;\n headerSlot?: React.ReactNode;\n theme?: string;\n hideFields?: string[];\n hideSections?: string[];\n showTopBar?: boolean;\n className?: string;\n}\n\nexport const ResumePreview = React.forwardRef<HTMLDivElement, ResumePreviewProps>(\n function ResumePreview(\n { data, headerSlot, theme, hideFields = [], hideSections = [], showTopBar = true, className },\n ref,\n ) {\n return (\n <div\n ref={ref}\n id=\"resume-capture-area\"\n style={showTopBar && theme ? { borderTopColor: theme } : undefined}\n className={cn(\n \"relative mx-auto flex min-h-[1056px] w-full flex-col rounded-lg border border-border bg-card p-8 text-card-foreground shadow-2xl sm:p-12 md:p-14 pdf-border-line\",\n showTopBar && \"border-t-8\",\n showTopBar && !theme && \"border-t-primary\",\n className,\n\n )}\n >\n <Background />\n {/* Content layer — above background */}\n <div className=\"relative z-10\">\n {/* Header Profile */}\n {!hideSections.includes(\"Información personal\") && (\n <HeaderPreview\n data={data}\n headerSlot={headerSlot}\n theme={theme}\n hideFields={hideFields}\n />\n )}\n\n {/* Main Content Layout */}\n <div className=\"mt-8 mb-8 grid grid-cols-1 gap-10 lg:grid-cols-12\">\n {/* Left Column (4/12) */}\n <div className=\"space-y-8 lg:col-span-4 lg:pr-6\">\n <ContactSkills data={data} hideFields={hideFields} hideSections={hideSections} />\n </div>\n\n {/* Right Column (8/12) - border-l acts as divider, avoids overlap with long content */}\n <div className=\"space-y-8 lg:col-span-8 lg:border-l lg:border-border/40 lg:pl-8\">\n <ExperienceEducation\n data={data}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n />\n </div>\n </div>\n\n <FooterPreview />\n </div>\n </div>\n );\n },\n);\n"],"mappings":"AAqCQ,cAcE,YAdF;AArCR,YAAY,WAAW;AAEvB,SAAS,kBAAkB;AAC3B,SAAS,UAAU;AACnB,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,2BAA2B;AACpC,SAAS,qBAAqB;AAYvB,MAAM,gBAAgB,MAAM;AAAA,EACjC,SAASA,eACP,EAAE,MAAM,YAAY,OAAO,aAAa,CAAC,GAAG,eAAe,CAAC,GAAG,aAAa,MAAM,UAAU,GAC5F,KACA;AACA,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,IAAG;AAAA,QACH,OAAO,cAAc,QAAQ,EAAE,gBAAgB,MAAM,IAAI;AAAA,QACzD,WAAW;AAAA,UACT;AAAA,UACA,cAAc;AAAA,UACd,cAAc,CAAC,SAAS;AAAA,UACxB;AAAA,QAEF;AAAA,QAEA;AAAA,8BAAC,cAAW;AAAA,UAEZ,qBAAC,SAAI,WAAU,iBAEZ;AAAA,aAAC,aAAa,SAAS,yBAAsB,KAC5C;AAAA,cAAC;AAAA;AAAA,gBACC;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA;AAAA,YACF;AAAA,YAIF,qBAAC,SAAI,WAAU,qDAEb;AAAA,kCAAC,SAAI,WAAU,mCACb,8BAAC,iBAAc,MAAY,YAAwB,cAA4B,GACjF;AAAA,cAGA,oBAAC,SAAI,WAAU,mEACb;AAAA,gBAAC;AAAA;AAAA,kBACC;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA;AAAA,cACF,GACF;AAAA,eACF;AAAA,YAEA,oBAAC,iBAAc;AAAA,aACjB;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AACF;","names":["ResumePreview"]}
|
|
@@ -168,6 +168,15 @@ function Resume({
|
|
|
168
168
|
clonedElement.style.height = "auto";
|
|
169
169
|
clonedElement.style.overflow = "visible";
|
|
170
170
|
clonedElement.style.width = "1200px";
|
|
171
|
+
const borderLineElements = Array.from(clonedElement.querySelectorAll(".pdf-border-line"));
|
|
172
|
+
if (clonedElement.classList.contains("pdf-border-line")) {
|
|
173
|
+
borderLineElements.push(clonedElement);
|
|
174
|
+
}
|
|
175
|
+
borderLineElements.forEach((el) => {
|
|
176
|
+
if (el instanceof HTMLElement) {
|
|
177
|
+
el.style.setProperty("border-radius", "0", "important");
|
|
178
|
+
}
|
|
179
|
+
});
|
|
171
180
|
const backgroundGradients = clonedElement.querySelectorAll("[data-pdf-background-gradient]");
|
|
172
181
|
const w = 1200;
|
|
173
182
|
const h = 160;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/domain/resume/resume.tsx"],"sourcesContent":["import { useRef, useState } from \"react\";\nimport html2canvas from \"html2canvas\";\nimport { jsPDF } from \"jspdf\";\nimport { CandidateProfile, ResumeProps, ResumeSavePayload, Action } from \"./types\";\nimport { ResumeEditor } from \"./resume-editor/resume-editor\";\nimport { ResumePreview } from \"./resume-preview/resume-preview\";\nimport { Button } from \"../../ui/button\";\nimport { Download, Edit, Loader2, Save, X } from \"lucide-react\";\nimport { Sheet, SheetTrigger, SheetContent, SheetTitle, SheetClose } from \"../../ui/sheet\";\n\nfunction calculateDiffs(original: CandidateProfile, current: CandidateProfile): ResumeSavePayload {\n // Determine diff for candidate table (academic info etc)\n const candidateData: Partial<CandidateProfile> = {};\n if (original.student_education_institution_id !== current.student_education_institution_id)\n candidateData.student_education_institution_id = current.student_education_institution_id;\n if (original.education_institution_id !== current.education_institution_id)\n candidateData.education_institution_id = current.education_institution_id;\n if (original.program_name !== current.program_name)\n candidateData.program_name = current.program_name;\n if (JSON.stringify(original.preferences) !== JSON.stringify(current.preferences))\n candidateData.preferences = current.preferences;\n if (JSON.stringify(original.advanced) !== JSON.stringify(current.advanced))\n candidateData.advanced = current.advanced;\n\n // Determine diff for user table (personal info)\n const userData: Partial<CandidateProfile> = {};\n const userFields: (keyof CandidateProfile)[] = [\n \"first_name\",\n \"middle_name\",\n \"last_name\",\n \"second_last_name\",\n \"phone_number\",\n \"phone_number_2\",\n \"email\",\n \"city\",\n \"address\",\n \"postal_code\",\n \"birth_date\",\n \"photo_url\",\n ];\n userFields.forEach((field) => {\n if (original[field] !== current[field]) {\n // @ts-expect-error - jspdf types are a bit funky\n userData[field] = current[field];\n }\n });\n\n // Helper for array diffs\n const calculateArrayDiff = (origArray: any[] = [], currArray: any[] = []) => {\n const changes: any[] = [];\n const origMap = new Map(origArray.map((item) => [item.id, item]));\n\n currArray.forEach((currItem) => {\n const origItem = origMap.get(currItem.id);\n if (!origItem) {\n // New item\n changes.push({ action: \"create\" as Action, data: currItem });\n } else if (JSON.stringify(origItem) !== JSON.stringify(currItem)) {\n // Updated item\n changes.push({ action: \"update\" as Action, data: currItem, id: currItem.id });\n }\n });\n\n const currMap = new Map(currArray.map((item) => [item.id, item]));\n origArray.forEach((origItem) => {\n if (!currMap.has(origItem.id)) {\n // Deleted item\n changes.push({ action: \"delete\" as Action, id: origItem.id });\n }\n });\n\n return changes;\n };\n\n const experiencesDiff = [\n ...calculateArrayDiff(original.work_experience, current.work_experience),\n ...calculateArrayDiff(original.education, current.education),\n ...calculateArrayDiff(original.activities, current.activities),\n ];\n\n const hwOrig = original.skills?.hard || [];\n const hwCurr = current.skills?.hard || [];\n const swOrig = original.skills?.soft || [];\n const swCurr = current.skills?.soft || [];\n\n const skillsDiff = [...calculateArrayDiff(hwOrig, hwCurr), ...calculateArrayDiff(swOrig, swCurr)];\n\n return {\n candidate: {\n action: Object.keys(candidateData).length > 0 ? \"update\" : \"none\",\n data: candidateData,\n },\n user: {\n action: Object.keys(userData).length > 0 ? \"update\" : \"none\",\n data: userData,\n },\n curriculum: {\n action: original.summary !== current.summary ? \"update\" : \"none\",\n data: { summary: current.summary },\n },\n experiences: experiencesDiff,\n skills: skillsDiff,\n certifications: calculateArrayDiff(original.certifications, current.certifications),\n };\n}\n\nexport function Resume({\n data,\n editable = false,\n headerSlot,\n withDownload = false,\n theme = \"hsl(var(--secondary))\",\n hideFields = [],\n hideSections = [],\n menuSlot = [],\n onSave,\n showTopBar = true,\n className,\n}: ResumeProps) {\n const themeStyle = theme ? ({ \"--cv-theme\": theme } as React.CSSProperties) : undefined;\n const [draftData, setDraftData] = useState<CandidateProfile>(data);\n const [isOpen, setIsOpen] = useState(false);\n const [isDownloading, setIsDownloading] = useState(false);\n const previewRef = useRef<HTMLDivElement>(null);\n\n const handleOpenChange = (open: boolean) => {\n if (open) {\n setDraftData(data);\n }\n setIsOpen(open);\n };\n\n const handleSave = () => {\n if (onSave) {\n const payload = calculateDiffs(data, draftData);\n onSave(payload, draftData);\n }\n setIsOpen(false);\n };\n\n const handleDownload = async () => {\n if (!previewRef.current || isDownloading) return;\n setIsDownloading(true);\n\n try {\n const element = previewRef.current;\n const canvas = await html2canvas(element, {\n scale: 2,\n useCORS: true,\n backgroundColor: \"#ffffff\",\n logging: false,\n windowWidth: 1200, // Force desktop layout breakpoint\n onclone: (clonedDoc) => {\n const clonedElement = clonedDoc.getElementById(\"resume-capture-area\");\n if (clonedElement instanceof HTMLElement) {\n clonedElement.style.height = \"auto\";\n clonedElement.style.overflow = \"visible\";\n clonedElement.style.width = \"1200px\";\n\n // Fix Background: html2canvas doesn't support mask-image. Render gradient to canvas\n // and use as background-image for pixel-perfect PDF output.\n const backgroundGradients = clonedElement.querySelectorAll(\"[data-pdf-background-gradient]\");\n const w = 1200;\n const h = 160;\n const canvas = clonedDoc.createElement(\"canvas\");\n canvas.width = w;\n canvas.height = h;\n const ctx = canvas.getContext(\"2d\");\n if (ctx) {\n const hGrad = ctx.createLinearGradient(0, 0, w, 0);\n hGrad.addColorStop(0, \"#ffffff\");\n hGrad.addColorStop(0.5, \"#f3e8ff\");\n hGrad.addColorStop(1, \"#bfdbfe\");\n ctx.fillStyle = hGrad;\n ctx.fillRect(0, 0, w, h);\n ctx.globalCompositeOperation = \"destination-in\";\n const vGrad = ctx.createLinearGradient(0, 0, 0, h);\n vGrad.addColorStop(0, \"rgba(0,0,0,1)\");\n vGrad.addColorStop(1, \"rgba(0,0,0,0)\");\n ctx.fillStyle = vGrad;\n ctx.fillRect(0, 0, w, h);\n ctx.globalCompositeOperation = \"source-over\";\n }\n const dataUrl = canvas.toDataURL(\"image/png\");\n backgroundGradients.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.maskImage = \"none\";\n el.style.background = \"none\";\n el.style.backgroundImage = `url(${dataUrl})`;\n el.style.backgroundSize = \"100% 100%\";\n el.style.backgroundPosition = \"0 0\";\n }\n });\n\n // Fix for images with object-fit: cover not being respected by html2canvas\n const objectCoverImages = clonedElement.querySelectorAll(\"img.object-cover\");\n objectCoverImages.forEach((img) => {\n if (img instanceof HTMLImageElement) {\n const parent = img.parentElement;\n if (parent) {\n // Replace img with a div that has background-image: cover\n const div = clonedDoc.createElement(\"div\");\n div.style.width = \"100%\";\n div.style.height = \"100%\";\n div.style.backgroundImage = `url(${img.src})`;\n div.style.backgroundSize = \"cover\";\n div.style.backgroundPosition = \"center\";\n div.style.borderRadius = window.getComputedStyle(img).borderRadius;\n div.className = img.className;\n parent.replaceChild(div, img);\n }\n }\n });\n\n // Fix Avatar alignment in PDF: apply consistent margin for both image and fallback\n const headerAvatars = clonedElement.querySelectorAll(\".pdf-header-avatar\");\n headerAvatars.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.marginTop = \"1.33rem\";\n }\n });\n\n // Hide elements marked as hidden for PDF\n const hiddenElements = clonedElement.querySelectorAll(\".pdf-hidden\");\n hiddenElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"none\";\n }\n });\n\n // Fix SVG vertical alignment for html2canvas\n // html2canvas doesn't properly handle flex align-items with SVGs\n const svgs = clonedElement.querySelectorAll(\"svg\");\n svgs.forEach((svg) => {\n svg.style.display = \"inline-block\";\n svg.style.verticalAlign = \"middle\";\n svg.style.flexShrink = \"0\";\n svg.style.marginRight = \"0.4rem\";\n svg.style.marginBottom = \"-1.2rem\";\n });\n\n // Fix line-clamp which causes text vertical cut-offs in html2canvas\n const clampElements = clonedElement.querySelectorAll(\n \".line-clamp-1, .line-clamp-2, .line-clamp-3\",\n );\n clampElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"block\";\n el.style.webkitLineClamp = \"unset\";\n el.style.webkitBoxOrient = \"unset\";\n el.style.overflow = \"visible\";\n el.style.whiteSpace = \"normal\";\n el.style.lineHeight = \"1.5\";\n }\n });\n\n // Replace generic HTML elements like custom badges with simple spans\n // because html2canvas struggles with flexbox and strict borders inside round pills\n const badges = clonedElement.querySelectorAll(\".pdf-badge\");\n badges.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.paddingTop = \"-10px\";\n el.style.display = \"block\";\n el.style.paddingTop = \"0px\";\n el.style.paddingBottom = \"0.8rem\";\n }\n });\n\n // Fix headers with bottom borders losing padding/margin\n const headersWithBorders = clonedElement.querySelectorAll(\"h1, h2, h3, h4\");\n headersWithBorders.forEach((el) => {\n if (el instanceof HTMLElement) {\n const hasBorder =\n el.className.includes(\"border-b\") || el.style.borderBottomWidth !== \"\";\n if (hasBorder) {\n el.style.paddingBottom = \"1.2rem\";\n }\n }\n });\n\n // Add margin top to pdf-line-v elements\n const verticalLines = clonedElement.querySelectorAll(\".pdf-line-v\");\n verticalLines.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.marginTop = \"0.5rem\";\n el.style.zIndex = \"5\";\n }\n });\n\n // Add margin bottom to initials in header for better alignment in PDF\n const headerInitials = clonedElement.querySelectorAll(\".pdf-header-initials\");\n headerInitials.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.paddingBottom = \"2rem\";\n }\n });\n\n // Add styles to summary for PDF\n const summaries = clonedElement.querySelectorAll(\".pdf-summary\");\n summaries.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.width = \"100%\";\n el.style.maxWidth = \"100%\";\n el.style.marginTop = \"1.5rem\";\n }\n });\n\n // Show footer in PDF and force grayscale for the logo\n const pdfFooters = clonedElement.querySelectorAll(\".pdf-footer\");\n pdfFooters.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"flex\";\n }\n });\n\n // Also make sure parent containers don't restrict height\n let parent = clonedElement.parentElement;\n while (parent) {\n parent.style.height = \"auto\";\n parent.style.overflow = \"visible\";\n parent = parent.parentElement;\n }\n }\n },\n });\n\n const imgData = canvas.toDataURL(\"image/jpeg\", 1);\n const imgWidthPx = canvas.width;\n const imgHeightPx = canvas.height;\n\n // Convert px to mm (at 96 DPI base * scale factor)\n const pxToMm = 25.4 / (96 * 2);\n const pdfWidth = imgWidthPx * pxToMm;\n const pdfHeight = imgHeightPx * pxToMm;\n\n const pdf = new jsPDF({\n orientation: pdfWidth > pdfHeight ? \"landscape\" : \"portrait\",\n unit: \"mm\",\n format: [pdfWidth, pdfHeight],\n });\n\n pdf.addImage(imgData, \"JPEG\", 0, 0, pdfWidth, pdfHeight, undefined, \"FAST\");\n\n const fileName = `CV_${data.first_name}_${data.last_name}`.replace(/\\s+/g, \"_\");\n pdf.save(`${fileName}.pdf`);\n } catch (error) {\n console.error(\"Error generating PDF:\", error);\n } finally {\n setIsDownloading(false);\n }\n };\n\n return (\n <div\n className=\"relative h-full w-full overflow-y-auto bg-muted/20 px-4 py-4 sm:px-8\"\n style={themeStyle}\n >\n {/* Preview Panel - Takes full width, centered like a document */}\n <div className=\"mx-auto h-full w-full max-w-5xl\">\n <ResumePreview\n ref={previewRef}\n data={draftData}\n headerSlot={headerSlot}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n showTopBar={showTopBar}\n className={className}\n />\n </div>\n\n {/* Floating Action Buttons */}\n <div className=\"fixed bottom-6 right-6 z-50 flex flex-col gap-3 sm:flex-row\">\n {editable && (\n <Sheet open={isOpen} onOpenChange={handleOpenChange}>\n <SheetTrigger asChild>\n <Button\n size=\"lg\"\n className=\"flex items-center gap-2 rounded-full bg-primary px-4 text-primary-foreground shadow-xl hover:bg-primary/90\"\n >\n <Edit className=\"h-5 w-5\" />\n <span className=\"hidden font-semibold sm:inline\">Editar</span>\n </Button>\n </SheetTrigger>\n {/* Drawer opens from the right side, taking good width */}\n <SheetContent\n side=\"right\"\n showCloseButton={false}\n className=\"flex w-full flex-col overflow-hidden bg-card p-0 sm:max-w-md md:max-w-2xl\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 pb-2\">\n <SheetTitle className=\"text-2xl font-bold text-foreground\">Editar</SheetTitle>\n <SheetClose className=\"rounded-full p-2 opacity-70 transition-colors hover:bg-muted hover:opacity-100\">\n <X className=\"h-6 w-6 text-foreground\" />\n <span className=\"sr-only\">Cerrar</span>\n </SheetClose>\n </div>\n\n {/* Scrollable editor content */}\n <div className=\"flex-1 overflow-y-auto px-6 pb-6 pt-2\">\n <ResumeEditor\n data={draftData}\n onChange={setDraftData}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n menuSlot={menuSlot}\n />\n </div>\n\n {/* Sticky save footer */}\n {onSave && (\n <div className=\"flex justify-end gap-3 p-4\">\n <SheetClose asChild>\n <Button variant=\"outline\">Cancelar</Button>\n </SheetClose>\n <Button onClick={handleSave} className=\"gap-2\">\n <Save className=\"h-4 w-4\" />\n Guardar\n </Button>\n </div>\n )}\n </SheetContent>\n </Sheet>\n )}\n\n {withDownload && (\n <Button\n size=\"lg\"\n variant=\"ghost\"\n disabled={isDownloading}\n onClick={handleDownload}\n className=\"flex items-center gap-2 rounded-full bg-purple-200 px-4 text-purple-100 text-primary shadow-xl\"\n >\n {isDownloading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n <Download className=\"h-5 w-5\" />\n )}\n <span className=\"hidden font-semibold sm:inline\">\n {isDownloading ? \"Generando...\" : \"Descargar\"}\n </span>\n </Button>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["html2canvas","canvas","parent"]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/domain/resume/resume.tsx"],"sourcesContent":["import { useRef, useState } from \"react\";\nimport html2canvas from \"html2canvas\";\nimport { jsPDF } from \"jspdf\";\nimport { CandidateProfile, ResumeProps, ResumeSavePayload, Action } from \"./types\";\nimport { ResumeEditor } from \"./resume-editor/resume-editor\";\nimport { ResumePreview } from \"./resume-preview/resume-preview\";\nimport { Button } from \"../../ui/button\";\nimport { Download, Edit, Loader2, Save, X } from \"lucide-react\";\nimport { Sheet, SheetTrigger, SheetContent, SheetTitle, SheetClose } from \"../../ui/sheet\";\n\nfunction calculateDiffs(original: CandidateProfile, current: CandidateProfile): ResumeSavePayload {\n // Determine diff for candidate table (academic info etc)\n const candidateData: Partial<CandidateProfile> = {};\n if (original.student_education_institution_id !== current.student_education_institution_id)\n candidateData.student_education_institution_id = current.student_education_institution_id;\n if (original.education_institution_id !== current.education_institution_id)\n candidateData.education_institution_id = current.education_institution_id;\n if (original.program_name !== current.program_name)\n candidateData.program_name = current.program_name;\n if (JSON.stringify(original.preferences) !== JSON.stringify(current.preferences))\n candidateData.preferences = current.preferences;\n if (JSON.stringify(original.advanced) !== JSON.stringify(current.advanced))\n candidateData.advanced = current.advanced;\n\n // Determine diff for user table (personal info)\n const userData: Partial<CandidateProfile> = {};\n const userFields: (keyof CandidateProfile)[] = [\n \"first_name\",\n \"middle_name\",\n \"last_name\",\n \"second_last_name\",\n \"phone_number\",\n \"phone_number_2\",\n \"email\",\n \"city\",\n \"address\",\n \"postal_code\",\n \"birth_date\",\n \"photo_url\",\n ];\n userFields.forEach((field) => {\n if (original[field] !== current[field]) {\n // @ts-expect-error - jspdf types are a bit funky\n userData[field] = current[field];\n }\n });\n\n // Helper for array diffs\n const calculateArrayDiff = (origArray: any[] = [], currArray: any[] = []) => {\n const changes: any[] = [];\n const origMap = new Map(origArray.map((item) => [item.id, item]));\n\n currArray.forEach((currItem) => {\n const origItem = origMap.get(currItem.id);\n if (!origItem) {\n // New item\n changes.push({ action: \"create\" as Action, data: currItem });\n } else if (JSON.stringify(origItem) !== JSON.stringify(currItem)) {\n // Updated item\n changes.push({ action: \"update\" as Action, data: currItem, id: currItem.id });\n }\n });\n\n const currMap = new Map(currArray.map((item) => [item.id, item]));\n origArray.forEach((origItem) => {\n if (!currMap.has(origItem.id)) {\n // Deleted item\n changes.push({ action: \"delete\" as Action, id: origItem.id });\n }\n });\n\n return changes;\n };\n\n const experiencesDiff = [\n ...calculateArrayDiff(original.work_experience, current.work_experience),\n ...calculateArrayDiff(original.education, current.education),\n ...calculateArrayDiff(original.activities, current.activities),\n ];\n\n const hwOrig = original.skills?.hard || [];\n const hwCurr = current.skills?.hard || [];\n const swOrig = original.skills?.soft || [];\n const swCurr = current.skills?.soft || [];\n\n const skillsDiff = [...calculateArrayDiff(hwOrig, hwCurr), ...calculateArrayDiff(swOrig, swCurr)];\n\n return {\n candidate: {\n action: Object.keys(candidateData).length > 0 ? \"update\" : \"none\",\n data: candidateData,\n },\n user: {\n action: Object.keys(userData).length > 0 ? \"update\" : \"none\",\n data: userData,\n },\n curriculum: {\n action: original.summary !== current.summary ? \"update\" : \"none\",\n data: { summary: current.summary },\n },\n experiences: experiencesDiff,\n skills: skillsDiff,\n certifications: calculateArrayDiff(original.certifications, current.certifications),\n };\n}\n\nexport function Resume({\n data,\n editable = false,\n headerSlot,\n withDownload = false,\n theme = \"hsl(var(--secondary))\",\n hideFields = [],\n hideSections = [],\n menuSlot = [],\n onSave,\n showTopBar = true,\n className,\n}: ResumeProps) {\n const themeStyle = theme ? ({ \"--cv-theme\": theme } as React.CSSProperties) : undefined;\n const [draftData, setDraftData] = useState<CandidateProfile>(data);\n const [isOpen, setIsOpen] = useState(false);\n const [isDownloading, setIsDownloading] = useState(false);\n const previewRef = useRef<HTMLDivElement>(null);\n\n const handleOpenChange = (open: boolean) => {\n if (open) {\n setDraftData(data);\n }\n setIsOpen(open);\n };\n\n const handleSave = () => {\n if (onSave) {\n const payload = calculateDiffs(data, draftData);\n onSave(payload, draftData);\n }\n setIsOpen(false);\n };\n\n const handleDownload = async () => {\n if (!previewRef.current || isDownloading) return;\n setIsDownloading(true);\n\n try {\n const element = previewRef.current;\n const canvas = await html2canvas(element, {\n scale: 2,\n useCORS: true,\n backgroundColor: \"#ffffff\",\n logging: false,\n windowWidth: 1200, // Force desktop layout breakpoint\n onclone: (clonedDoc) => {\n const clonedElement = clonedDoc.getElementById(\"resume-capture-area\");\n if (clonedElement instanceof HTMLElement) {\n clonedElement.style.height = \"auto\";\n clonedElement.style.overflow = \"visible\";\n clonedElement.style.width = \"1200px\";\n\n // Remove border radius for elements with pdf-border-line class in PDF\n const borderLineElements = Array.from(clonedElement.querySelectorAll(\".pdf-border-line\"));\n if (clonedElement.classList.contains(\"pdf-border-line\")) {\n borderLineElements.push(clonedElement);\n }\n borderLineElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.setProperty(\"border-radius\", \"0\", \"important\");\n }\n });\n\n // Fix Background: html2canvas doesn't support mask-image. Render gradient to canvas\n // and use as background-image for pixel-perfect PDF output.\n const backgroundGradients = clonedElement.querySelectorAll(\"[data-pdf-background-gradient]\");\n const w = 1200;\n const h = 160;\n const canvas = clonedDoc.createElement(\"canvas\");\n canvas.width = w;\n canvas.height = h;\n const ctx = canvas.getContext(\"2d\");\n if (ctx) {\n const hGrad = ctx.createLinearGradient(0, 0, w, 0);\n hGrad.addColorStop(0, \"#ffffff\");\n hGrad.addColorStop(0.5, \"#f3e8ff\");\n hGrad.addColorStop(1, \"#bfdbfe\");\n ctx.fillStyle = hGrad;\n ctx.fillRect(0, 0, w, h);\n ctx.globalCompositeOperation = \"destination-in\";\n const vGrad = ctx.createLinearGradient(0, 0, 0, h);\n vGrad.addColorStop(0, \"rgba(0,0,0,1)\");\n vGrad.addColorStop(1, \"rgba(0,0,0,0)\");\n ctx.fillStyle = vGrad;\n ctx.fillRect(0, 0, w, h);\n ctx.globalCompositeOperation = \"source-over\";\n }\n const dataUrl = canvas.toDataURL(\"image/png\");\n backgroundGradients.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.maskImage = \"none\";\n el.style.background = \"none\";\n el.style.backgroundImage = `url(${dataUrl})`;\n el.style.backgroundSize = \"100% 100%\";\n el.style.backgroundPosition = \"0 0\";\n }\n });\n\n // Fix for images with object-fit: cover not being respected by html2canvas\n const objectCoverImages = clonedElement.querySelectorAll(\"img.object-cover\");\n objectCoverImages.forEach((img) => {\n if (img instanceof HTMLImageElement) {\n const parent = img.parentElement;\n if (parent) {\n // Replace img with a div that has background-image: cover\n const div = clonedDoc.createElement(\"div\");\n div.style.width = \"100%\";\n div.style.height = \"100%\";\n div.style.backgroundImage = `url(${img.src})`;\n div.style.backgroundSize = \"cover\";\n div.style.backgroundPosition = \"center\";\n div.style.borderRadius = window.getComputedStyle(img).borderRadius;\n div.className = img.className;\n parent.replaceChild(div, img);\n }\n }\n });\n\n // Fix Avatar alignment in PDF: apply consistent margin for both image and fallback\n const headerAvatars = clonedElement.querySelectorAll(\".pdf-header-avatar\");\n headerAvatars.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.marginTop = \"1.33rem\";\n }\n });\n\n // Hide elements marked as hidden for PDF\n const hiddenElements = clonedElement.querySelectorAll(\".pdf-hidden\");\n hiddenElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"none\";\n }\n });\n\n // Fix SVG vertical alignment for html2canvas\n // html2canvas doesn't properly handle flex align-items with SVGs\n const svgs = clonedElement.querySelectorAll(\"svg\");\n svgs.forEach((svg) => {\n svg.style.display = \"inline-block\";\n svg.style.verticalAlign = \"middle\";\n svg.style.flexShrink = \"0\";\n svg.style.marginRight = \"0.4rem\";\n svg.style.marginBottom = \"-1.2rem\";\n });\n\n // Fix line-clamp which causes text vertical cut-offs in html2canvas\n const clampElements = clonedElement.querySelectorAll(\n \".line-clamp-1, .line-clamp-2, .line-clamp-3\",\n );\n clampElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"block\";\n el.style.webkitLineClamp = \"unset\";\n el.style.webkitBoxOrient = \"unset\";\n el.style.overflow = \"visible\";\n el.style.whiteSpace = \"normal\";\n el.style.lineHeight = \"1.5\";\n }\n });\n\n // Replace generic HTML elements like custom badges with simple spans\n // because html2canvas struggles with flexbox and strict borders inside round pills\n const badges = clonedElement.querySelectorAll(\".pdf-badge\");\n badges.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.paddingTop = \"-10px\";\n el.style.display = \"block\";\n el.style.paddingTop = \"0px\";\n el.style.paddingBottom = \"0.8rem\";\n }\n });\n\n // Fix headers with bottom borders losing padding/margin\n const headersWithBorders = clonedElement.querySelectorAll(\"h1, h2, h3, h4\");\n headersWithBorders.forEach((el) => {\n if (el instanceof HTMLElement) {\n const hasBorder =\n el.className.includes(\"border-b\") || el.style.borderBottomWidth !== \"\";\n if (hasBorder) {\n el.style.paddingBottom = \"1.2rem\";\n }\n }\n });\n\n // Add margin top to pdf-line-v elements\n const verticalLines = clonedElement.querySelectorAll(\".pdf-line-v\");\n verticalLines.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.marginTop = \"0.5rem\";\n el.style.zIndex = \"5\";\n }\n });\n\n // Add margin bottom to initials in header for better alignment in PDF\n const headerInitials = clonedElement.querySelectorAll(\".pdf-header-initials\");\n headerInitials.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.paddingBottom = \"2rem\";\n }\n });\n\n // Add styles to summary for PDF\n const summaries = clonedElement.querySelectorAll(\".pdf-summary\");\n summaries.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.width = \"100%\";\n el.style.maxWidth = \"100%\";\n el.style.marginTop = \"1.5rem\";\n }\n });\n\n // Show footer in PDF and force grayscale for the logo\n const pdfFooters = clonedElement.querySelectorAll(\".pdf-footer\");\n pdfFooters.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"flex\";\n }\n });\n\n // Also make sure parent containers don't restrict height\n let parent = clonedElement.parentElement;\n while (parent) {\n parent.style.height = \"auto\";\n parent.style.overflow = \"visible\";\n parent = parent.parentElement;\n }\n }\n },\n });\n\n const imgData = canvas.toDataURL(\"image/jpeg\", 1);\n const imgWidthPx = canvas.width;\n const imgHeightPx = canvas.height;\n\n // Convert px to mm (at 96 DPI base * scale factor)\n const pxToMm = 25.4 / (96 * 2);\n const pdfWidth = imgWidthPx * pxToMm;\n const pdfHeight = imgHeightPx * pxToMm;\n\n const pdf = new jsPDF({\n orientation: pdfWidth > pdfHeight ? \"landscape\" : \"portrait\",\n unit: \"mm\",\n format: [pdfWidth, pdfHeight],\n });\n\n pdf.addImage(imgData, \"JPEG\", 0, 0, pdfWidth, pdfHeight, undefined, \"FAST\");\n\n const fileName = `CV_${data.first_name}_${data.last_name}`.replace(/\\s+/g, \"_\");\n pdf.save(`${fileName}.pdf`);\n } catch (error) {\n console.error(\"Error generating PDF:\", error);\n } finally {\n setIsDownloading(false);\n }\n };\n\n return (\n <div\n className=\"relative h-full w-full overflow-y-auto bg-muted/20 px-4 py-4 sm:px-8\"\n style={themeStyle}\n >\n {/* Preview Panel - Takes full width, centered like a document */}\n <div className=\"mx-auto h-full w-full max-w-5xl\">\n <ResumePreview\n ref={previewRef}\n data={draftData}\n headerSlot={headerSlot}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n showTopBar={showTopBar}\n className={className}\n />\n </div>\n\n {/* Floating Action Buttons */}\n <div className=\"fixed bottom-6 right-6 z-50 flex flex-col gap-3 sm:flex-row\">\n {editable && (\n <Sheet open={isOpen} onOpenChange={handleOpenChange}>\n <SheetTrigger asChild>\n <Button\n size=\"lg\"\n className=\"flex items-center gap-2 rounded-full bg-primary px-4 text-primary-foreground shadow-xl hover:bg-primary/90\"\n >\n <Edit className=\"h-5 w-5\" />\n <span className=\"hidden font-semibold sm:inline\">Editar</span>\n </Button>\n </SheetTrigger>\n {/* Drawer opens from the right side, taking good width */}\n <SheetContent\n side=\"right\"\n showCloseButton={false}\n className=\"flex w-full flex-col overflow-hidden bg-card p-0 sm:max-w-md md:max-w-2xl\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 pb-2\">\n <SheetTitle className=\"text-2xl font-bold text-foreground\">Editar</SheetTitle>\n <SheetClose className=\"rounded-full p-2 opacity-70 transition-colors hover:bg-muted hover:opacity-100\">\n <X className=\"h-6 w-6 text-foreground\" />\n <span className=\"sr-only\">Cerrar</span>\n </SheetClose>\n </div>\n\n {/* Scrollable editor content */}\n <div className=\"flex-1 overflow-y-auto px-6 pb-6 pt-2\">\n <ResumeEditor\n data={draftData}\n onChange={setDraftData}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n menuSlot={menuSlot}\n />\n </div>\n\n {/* Sticky save footer */}\n {onSave && (\n <div className=\"flex justify-end gap-3 p-4\">\n <SheetClose asChild>\n <Button variant=\"outline\">Cancelar</Button>\n </SheetClose>\n <Button onClick={handleSave} className=\"gap-2\">\n <Save className=\"h-4 w-4\" />\n Guardar\n </Button>\n </div>\n )}\n </SheetContent>\n </Sheet>\n )}\n\n {withDownload && (\n <Button\n size=\"lg\"\n variant=\"ghost\"\n disabled={isDownloading}\n onClick={handleDownload}\n className=\"flex items-center gap-2 rounded-full bg-purple-200 px-4 text-purple-100 text-primary shadow-xl\"\n >\n {isDownloading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n <Download className=\"h-5 w-5\" />\n )}\n <span className=\"hidden font-semibold sm:inline\">\n {isDownloading ? \"Generando...\" : \"Descargar\"}\n </span>\n </Button>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["html2canvas","canvas","parent"]}
|
|
@@ -135,6 +135,15 @@ function Resume({
|
|
|
135
135
|
clonedElement.style.height = "auto";
|
|
136
136
|
clonedElement.style.overflow = "visible";
|
|
137
137
|
clonedElement.style.width = "1200px";
|
|
138
|
+
const borderLineElements = Array.from(clonedElement.querySelectorAll(".pdf-border-line"));
|
|
139
|
+
if (clonedElement.classList.contains("pdf-border-line")) {
|
|
140
|
+
borderLineElements.push(clonedElement);
|
|
141
|
+
}
|
|
142
|
+
borderLineElements.forEach((el) => {
|
|
143
|
+
if (el instanceof HTMLElement) {
|
|
144
|
+
el.style.setProperty("border-radius", "0", "important");
|
|
145
|
+
}
|
|
146
|
+
});
|
|
138
147
|
const backgroundGradients = clonedElement.querySelectorAll("[data-pdf-background-gradient]");
|
|
139
148
|
const w = 1200;
|
|
140
149
|
const h = 160;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/domain/resume/resume.tsx"],"sourcesContent":["import { useRef, useState } from \"react\";\nimport html2canvas from \"html2canvas\";\nimport { jsPDF } from \"jspdf\";\nimport { CandidateProfile, ResumeProps, ResumeSavePayload, Action } from \"./types\";\nimport { ResumeEditor } from \"./resume-editor/resume-editor\";\nimport { ResumePreview } from \"./resume-preview/resume-preview\";\nimport { Button } from \"../../ui/button\";\nimport { Download, Edit, Loader2, Save, X } from \"lucide-react\";\nimport { Sheet, SheetTrigger, SheetContent, SheetTitle, SheetClose } from \"../../ui/sheet\";\n\nfunction calculateDiffs(original: CandidateProfile, current: CandidateProfile): ResumeSavePayload {\n // Determine diff for candidate table (academic info etc)\n const candidateData: Partial<CandidateProfile> = {};\n if (original.student_education_institution_id !== current.student_education_institution_id)\n candidateData.student_education_institution_id = current.student_education_institution_id;\n if (original.education_institution_id !== current.education_institution_id)\n candidateData.education_institution_id = current.education_institution_id;\n if (original.program_name !== current.program_name)\n candidateData.program_name = current.program_name;\n if (JSON.stringify(original.preferences) !== JSON.stringify(current.preferences))\n candidateData.preferences = current.preferences;\n if (JSON.stringify(original.advanced) !== JSON.stringify(current.advanced))\n candidateData.advanced = current.advanced;\n\n // Determine diff for user table (personal info)\n const userData: Partial<CandidateProfile> = {};\n const userFields: (keyof CandidateProfile)[] = [\n \"first_name\",\n \"middle_name\",\n \"last_name\",\n \"second_last_name\",\n \"phone_number\",\n \"phone_number_2\",\n \"email\",\n \"city\",\n \"address\",\n \"postal_code\",\n \"birth_date\",\n \"photo_url\",\n ];\n userFields.forEach((field) => {\n if (original[field] !== current[field]) {\n // @ts-expect-error - jspdf types are a bit funky\n userData[field] = current[field];\n }\n });\n\n // Helper for array diffs\n const calculateArrayDiff = (origArray: any[] = [], currArray: any[] = []) => {\n const changes: any[] = [];\n const origMap = new Map(origArray.map((item) => [item.id, item]));\n\n currArray.forEach((currItem) => {\n const origItem = origMap.get(currItem.id);\n if (!origItem) {\n // New item\n changes.push({ action: \"create\" as Action, data: currItem });\n } else if (JSON.stringify(origItem) !== JSON.stringify(currItem)) {\n // Updated item\n changes.push({ action: \"update\" as Action, data: currItem, id: currItem.id });\n }\n });\n\n const currMap = new Map(currArray.map((item) => [item.id, item]));\n origArray.forEach((origItem) => {\n if (!currMap.has(origItem.id)) {\n // Deleted item\n changes.push({ action: \"delete\" as Action, id: origItem.id });\n }\n });\n\n return changes;\n };\n\n const experiencesDiff = [\n ...calculateArrayDiff(original.work_experience, current.work_experience),\n ...calculateArrayDiff(original.education, current.education),\n ...calculateArrayDiff(original.activities, current.activities),\n ];\n\n const hwOrig = original.skills?.hard || [];\n const hwCurr = current.skills?.hard || [];\n const swOrig = original.skills?.soft || [];\n const swCurr = current.skills?.soft || [];\n\n const skillsDiff = [...calculateArrayDiff(hwOrig, hwCurr), ...calculateArrayDiff(swOrig, swCurr)];\n\n return {\n candidate: {\n action: Object.keys(candidateData).length > 0 ? \"update\" : \"none\",\n data: candidateData,\n },\n user: {\n action: Object.keys(userData).length > 0 ? \"update\" : \"none\",\n data: userData,\n },\n curriculum: {\n action: original.summary !== current.summary ? \"update\" : \"none\",\n data: { summary: current.summary },\n },\n experiences: experiencesDiff,\n skills: skillsDiff,\n certifications: calculateArrayDiff(original.certifications, current.certifications),\n };\n}\n\nexport function Resume({\n data,\n editable = false,\n headerSlot,\n withDownload = false,\n theme = \"hsl(var(--secondary))\",\n hideFields = [],\n hideSections = [],\n menuSlot = [],\n onSave,\n showTopBar = true,\n className,\n}: ResumeProps) {\n const themeStyle = theme ? ({ \"--cv-theme\": theme } as React.CSSProperties) : undefined;\n const [draftData, setDraftData] = useState<CandidateProfile>(data);\n const [isOpen, setIsOpen] = useState(false);\n const [isDownloading, setIsDownloading] = useState(false);\n const previewRef = useRef<HTMLDivElement>(null);\n\n const handleOpenChange = (open: boolean) => {\n if (open) {\n setDraftData(data);\n }\n setIsOpen(open);\n };\n\n const handleSave = () => {\n if (onSave) {\n const payload = calculateDiffs(data, draftData);\n onSave(payload, draftData);\n }\n setIsOpen(false);\n };\n\n const handleDownload = async () => {\n if (!previewRef.current || isDownloading) return;\n setIsDownloading(true);\n\n try {\n const element = previewRef.current;\n const canvas = await html2canvas(element, {\n scale: 2,\n useCORS: true,\n backgroundColor: \"#ffffff\",\n logging: false,\n windowWidth: 1200, // Force desktop layout breakpoint\n onclone: (clonedDoc) => {\n const clonedElement = clonedDoc.getElementById(\"resume-capture-area\");\n if (clonedElement instanceof HTMLElement) {\n clonedElement.style.height = \"auto\";\n clonedElement.style.overflow = \"visible\";\n clonedElement.style.width = \"1200px\";\n\n // Fix Background: html2canvas doesn't support mask-image. Render gradient to canvas\n // and use as background-image for pixel-perfect PDF output.\n const backgroundGradients = clonedElement.querySelectorAll(\"[data-pdf-background-gradient]\");\n const w = 1200;\n const h = 160;\n const canvas = clonedDoc.createElement(\"canvas\");\n canvas.width = w;\n canvas.height = h;\n const ctx = canvas.getContext(\"2d\");\n if (ctx) {\n const hGrad = ctx.createLinearGradient(0, 0, w, 0);\n hGrad.addColorStop(0, \"#ffffff\");\n hGrad.addColorStop(0.5, \"#f3e8ff\");\n hGrad.addColorStop(1, \"#bfdbfe\");\n ctx.fillStyle = hGrad;\n ctx.fillRect(0, 0, w, h);\n ctx.globalCompositeOperation = \"destination-in\";\n const vGrad = ctx.createLinearGradient(0, 0, 0, h);\n vGrad.addColorStop(0, \"rgba(0,0,0,1)\");\n vGrad.addColorStop(1, \"rgba(0,0,0,0)\");\n ctx.fillStyle = vGrad;\n ctx.fillRect(0, 0, w, h);\n ctx.globalCompositeOperation = \"source-over\";\n }\n const dataUrl = canvas.toDataURL(\"image/png\");\n backgroundGradients.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.maskImage = \"none\";\n el.style.background = \"none\";\n el.style.backgroundImage = `url(${dataUrl})`;\n el.style.backgroundSize = \"100% 100%\";\n el.style.backgroundPosition = \"0 0\";\n }\n });\n\n // Fix for images with object-fit: cover not being respected by html2canvas\n const objectCoverImages = clonedElement.querySelectorAll(\"img.object-cover\");\n objectCoverImages.forEach((img) => {\n if (img instanceof HTMLImageElement) {\n const parent = img.parentElement;\n if (parent) {\n // Replace img with a div that has background-image: cover\n const div = clonedDoc.createElement(\"div\");\n div.style.width = \"100%\";\n div.style.height = \"100%\";\n div.style.backgroundImage = `url(${img.src})`;\n div.style.backgroundSize = \"cover\";\n div.style.backgroundPosition = \"center\";\n div.style.borderRadius = window.getComputedStyle(img).borderRadius;\n div.className = img.className;\n parent.replaceChild(div, img);\n }\n }\n });\n\n // Fix Avatar alignment in PDF: apply consistent margin for both image and fallback\n const headerAvatars = clonedElement.querySelectorAll(\".pdf-header-avatar\");\n headerAvatars.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.marginTop = \"1.33rem\";\n }\n });\n\n // Hide elements marked as hidden for PDF\n const hiddenElements = clonedElement.querySelectorAll(\".pdf-hidden\");\n hiddenElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"none\";\n }\n });\n\n // Fix SVG vertical alignment for html2canvas\n // html2canvas doesn't properly handle flex align-items with SVGs\n const svgs = clonedElement.querySelectorAll(\"svg\");\n svgs.forEach((svg) => {\n svg.style.display = \"inline-block\";\n svg.style.verticalAlign = \"middle\";\n svg.style.flexShrink = \"0\";\n svg.style.marginRight = \"0.4rem\";\n svg.style.marginBottom = \"-1.2rem\";\n });\n\n // Fix line-clamp which causes text vertical cut-offs in html2canvas\n const clampElements = clonedElement.querySelectorAll(\n \".line-clamp-1, .line-clamp-2, .line-clamp-3\",\n );\n clampElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"block\";\n el.style.webkitLineClamp = \"unset\";\n el.style.webkitBoxOrient = \"unset\";\n el.style.overflow = \"visible\";\n el.style.whiteSpace = \"normal\";\n el.style.lineHeight = \"1.5\";\n }\n });\n\n // Replace generic HTML elements like custom badges with simple spans\n // because html2canvas struggles with flexbox and strict borders inside round pills\n const badges = clonedElement.querySelectorAll(\".pdf-badge\");\n badges.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.paddingTop = \"-10px\";\n el.style.display = \"block\";\n el.style.paddingTop = \"0px\";\n el.style.paddingBottom = \"0.8rem\";\n }\n });\n\n // Fix headers with bottom borders losing padding/margin\n const headersWithBorders = clonedElement.querySelectorAll(\"h1, h2, h3, h4\");\n headersWithBorders.forEach((el) => {\n if (el instanceof HTMLElement) {\n const hasBorder =\n el.className.includes(\"border-b\") || el.style.borderBottomWidth !== \"\";\n if (hasBorder) {\n el.style.paddingBottom = \"1.2rem\";\n }\n }\n });\n\n // Add margin top to pdf-line-v elements\n const verticalLines = clonedElement.querySelectorAll(\".pdf-line-v\");\n verticalLines.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.marginTop = \"0.5rem\";\n el.style.zIndex = \"5\";\n }\n });\n\n // Add margin bottom to initials in header for better alignment in PDF\n const headerInitials = clonedElement.querySelectorAll(\".pdf-header-initials\");\n headerInitials.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.paddingBottom = \"2rem\";\n }\n });\n\n // Add styles to summary for PDF\n const summaries = clonedElement.querySelectorAll(\".pdf-summary\");\n summaries.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.width = \"100%\";\n el.style.maxWidth = \"100%\";\n el.style.marginTop = \"1.5rem\";\n }\n });\n\n // Show footer in PDF and force grayscale for the logo\n const pdfFooters = clonedElement.querySelectorAll(\".pdf-footer\");\n pdfFooters.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"flex\";\n }\n });\n\n // Also make sure parent containers don't restrict height\n let parent = clonedElement.parentElement;\n while (parent) {\n parent.style.height = \"auto\";\n parent.style.overflow = \"visible\";\n parent = parent.parentElement;\n }\n }\n },\n });\n\n const imgData = canvas.toDataURL(\"image/jpeg\", 1);\n const imgWidthPx = canvas.width;\n const imgHeightPx = canvas.height;\n\n // Convert px to mm (at 96 DPI base * scale factor)\n const pxToMm = 25.4 / (96 * 2);\n const pdfWidth = imgWidthPx * pxToMm;\n const pdfHeight = imgHeightPx * pxToMm;\n\n const pdf = new jsPDF({\n orientation: pdfWidth > pdfHeight ? \"landscape\" : \"portrait\",\n unit: \"mm\",\n format: [pdfWidth, pdfHeight],\n });\n\n pdf.addImage(imgData, \"JPEG\", 0, 0, pdfWidth, pdfHeight, undefined, \"FAST\");\n\n const fileName = `CV_${data.first_name}_${data.last_name}`.replace(/\\s+/g, \"_\");\n pdf.save(`${fileName}.pdf`);\n } catch (error) {\n console.error(\"Error generating PDF:\", error);\n } finally {\n setIsDownloading(false);\n }\n };\n\n return (\n <div\n className=\"relative h-full w-full overflow-y-auto bg-muted/20 px-4 py-4 sm:px-8\"\n style={themeStyle}\n >\n {/* Preview Panel - Takes full width, centered like a document */}\n <div className=\"mx-auto h-full w-full max-w-5xl\">\n <ResumePreview\n ref={previewRef}\n data={draftData}\n headerSlot={headerSlot}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n showTopBar={showTopBar}\n className={className}\n />\n </div>\n\n {/* Floating Action Buttons */}\n <div className=\"fixed bottom-6 right-6 z-50 flex flex-col gap-3 sm:flex-row\">\n {editable && (\n <Sheet open={isOpen} onOpenChange={handleOpenChange}>\n <SheetTrigger asChild>\n <Button\n size=\"lg\"\n className=\"flex items-center gap-2 rounded-full bg-primary px-4 text-primary-foreground shadow-xl hover:bg-primary/90\"\n >\n <Edit className=\"h-5 w-5\" />\n <span className=\"hidden font-semibold sm:inline\">Editar</span>\n </Button>\n </SheetTrigger>\n {/* Drawer opens from the right side, taking good width */}\n <SheetContent\n side=\"right\"\n showCloseButton={false}\n className=\"flex w-full flex-col overflow-hidden bg-card p-0 sm:max-w-md md:max-w-2xl\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 pb-2\">\n <SheetTitle className=\"text-2xl font-bold text-foreground\">Editar</SheetTitle>\n <SheetClose className=\"rounded-full p-2 opacity-70 transition-colors hover:bg-muted hover:opacity-100\">\n <X className=\"h-6 w-6 text-foreground\" />\n <span className=\"sr-only\">Cerrar</span>\n </SheetClose>\n </div>\n\n {/* Scrollable editor content */}\n <div className=\"flex-1 overflow-y-auto px-6 pb-6 pt-2\">\n <ResumeEditor\n data={draftData}\n onChange={setDraftData}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n menuSlot={menuSlot}\n />\n </div>\n\n {/* Sticky save footer */}\n {onSave && (\n <div className=\"flex justify-end gap-3 p-4\">\n <SheetClose asChild>\n <Button variant=\"outline\">Cancelar</Button>\n </SheetClose>\n <Button onClick={handleSave} className=\"gap-2\">\n <Save className=\"h-4 w-4\" />\n Guardar\n </Button>\n </div>\n )}\n </SheetContent>\n </Sheet>\n )}\n\n {withDownload && (\n <Button\n size=\"lg\"\n variant=\"ghost\"\n disabled={isDownloading}\n onClick={handleDownload}\n className=\"flex items-center gap-2 rounded-full bg-purple-200 px-4 text-purple-100 text-primary shadow-xl\"\n >\n {isDownloading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n <Download className=\"h-5 w-5\" />\n )}\n <span className=\"hidden font-semibold sm:inline\">\n {isDownloading ? \"Generando...\" : \"Descargar\"}\n </span>\n </Button>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["canvas","parent"]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/domain/resume/resume.tsx"],"sourcesContent":["import { useRef, useState } from \"react\";\nimport html2canvas from \"html2canvas\";\nimport { jsPDF } from \"jspdf\";\nimport { CandidateProfile, ResumeProps, ResumeSavePayload, Action } from \"./types\";\nimport { ResumeEditor } from \"./resume-editor/resume-editor\";\nimport { ResumePreview } from \"./resume-preview/resume-preview\";\nimport { Button } from \"../../ui/button\";\nimport { Download, Edit, Loader2, Save, X } from \"lucide-react\";\nimport { Sheet, SheetTrigger, SheetContent, SheetTitle, SheetClose } from \"../../ui/sheet\";\n\nfunction calculateDiffs(original: CandidateProfile, current: CandidateProfile): ResumeSavePayload {\n // Determine diff for candidate table (academic info etc)\n const candidateData: Partial<CandidateProfile> = {};\n if (original.student_education_institution_id !== current.student_education_institution_id)\n candidateData.student_education_institution_id = current.student_education_institution_id;\n if (original.education_institution_id !== current.education_institution_id)\n candidateData.education_institution_id = current.education_institution_id;\n if (original.program_name !== current.program_name)\n candidateData.program_name = current.program_name;\n if (JSON.stringify(original.preferences) !== JSON.stringify(current.preferences))\n candidateData.preferences = current.preferences;\n if (JSON.stringify(original.advanced) !== JSON.stringify(current.advanced))\n candidateData.advanced = current.advanced;\n\n // Determine diff for user table (personal info)\n const userData: Partial<CandidateProfile> = {};\n const userFields: (keyof CandidateProfile)[] = [\n \"first_name\",\n \"middle_name\",\n \"last_name\",\n \"second_last_name\",\n \"phone_number\",\n \"phone_number_2\",\n \"email\",\n \"city\",\n \"address\",\n \"postal_code\",\n \"birth_date\",\n \"photo_url\",\n ];\n userFields.forEach((field) => {\n if (original[field] !== current[field]) {\n // @ts-expect-error - jspdf types are a bit funky\n userData[field] = current[field];\n }\n });\n\n // Helper for array diffs\n const calculateArrayDiff = (origArray: any[] = [], currArray: any[] = []) => {\n const changes: any[] = [];\n const origMap = new Map(origArray.map((item) => [item.id, item]));\n\n currArray.forEach((currItem) => {\n const origItem = origMap.get(currItem.id);\n if (!origItem) {\n // New item\n changes.push({ action: \"create\" as Action, data: currItem });\n } else if (JSON.stringify(origItem) !== JSON.stringify(currItem)) {\n // Updated item\n changes.push({ action: \"update\" as Action, data: currItem, id: currItem.id });\n }\n });\n\n const currMap = new Map(currArray.map((item) => [item.id, item]));\n origArray.forEach((origItem) => {\n if (!currMap.has(origItem.id)) {\n // Deleted item\n changes.push({ action: \"delete\" as Action, id: origItem.id });\n }\n });\n\n return changes;\n };\n\n const experiencesDiff = [\n ...calculateArrayDiff(original.work_experience, current.work_experience),\n ...calculateArrayDiff(original.education, current.education),\n ...calculateArrayDiff(original.activities, current.activities),\n ];\n\n const hwOrig = original.skills?.hard || [];\n const hwCurr = current.skills?.hard || [];\n const swOrig = original.skills?.soft || [];\n const swCurr = current.skills?.soft || [];\n\n const skillsDiff = [...calculateArrayDiff(hwOrig, hwCurr), ...calculateArrayDiff(swOrig, swCurr)];\n\n return {\n candidate: {\n action: Object.keys(candidateData).length > 0 ? \"update\" : \"none\",\n data: candidateData,\n },\n user: {\n action: Object.keys(userData).length > 0 ? \"update\" : \"none\",\n data: userData,\n },\n curriculum: {\n action: original.summary !== current.summary ? \"update\" : \"none\",\n data: { summary: current.summary },\n },\n experiences: experiencesDiff,\n skills: skillsDiff,\n certifications: calculateArrayDiff(original.certifications, current.certifications),\n };\n}\n\nexport function Resume({\n data,\n editable = false,\n headerSlot,\n withDownload = false,\n theme = \"hsl(var(--secondary))\",\n hideFields = [],\n hideSections = [],\n menuSlot = [],\n onSave,\n showTopBar = true,\n className,\n}: ResumeProps) {\n const themeStyle = theme ? ({ \"--cv-theme\": theme } as React.CSSProperties) : undefined;\n const [draftData, setDraftData] = useState<CandidateProfile>(data);\n const [isOpen, setIsOpen] = useState(false);\n const [isDownloading, setIsDownloading] = useState(false);\n const previewRef = useRef<HTMLDivElement>(null);\n\n const handleOpenChange = (open: boolean) => {\n if (open) {\n setDraftData(data);\n }\n setIsOpen(open);\n };\n\n const handleSave = () => {\n if (onSave) {\n const payload = calculateDiffs(data, draftData);\n onSave(payload, draftData);\n }\n setIsOpen(false);\n };\n\n const handleDownload = async () => {\n if (!previewRef.current || isDownloading) return;\n setIsDownloading(true);\n\n try {\n const element = previewRef.current;\n const canvas = await html2canvas(element, {\n scale: 2,\n useCORS: true,\n backgroundColor: \"#ffffff\",\n logging: false,\n windowWidth: 1200, // Force desktop layout breakpoint\n onclone: (clonedDoc) => {\n const clonedElement = clonedDoc.getElementById(\"resume-capture-area\");\n if (clonedElement instanceof HTMLElement) {\n clonedElement.style.height = \"auto\";\n clonedElement.style.overflow = \"visible\";\n clonedElement.style.width = \"1200px\";\n\n // Remove border radius for elements with pdf-border-line class in PDF\n const borderLineElements = Array.from(clonedElement.querySelectorAll(\".pdf-border-line\"));\n if (clonedElement.classList.contains(\"pdf-border-line\")) {\n borderLineElements.push(clonedElement);\n }\n borderLineElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.setProperty(\"border-radius\", \"0\", \"important\");\n }\n });\n\n // Fix Background: html2canvas doesn't support mask-image. Render gradient to canvas\n // and use as background-image for pixel-perfect PDF output.\n const backgroundGradients = clonedElement.querySelectorAll(\"[data-pdf-background-gradient]\");\n const w = 1200;\n const h = 160;\n const canvas = clonedDoc.createElement(\"canvas\");\n canvas.width = w;\n canvas.height = h;\n const ctx = canvas.getContext(\"2d\");\n if (ctx) {\n const hGrad = ctx.createLinearGradient(0, 0, w, 0);\n hGrad.addColorStop(0, \"#ffffff\");\n hGrad.addColorStop(0.5, \"#f3e8ff\");\n hGrad.addColorStop(1, \"#bfdbfe\");\n ctx.fillStyle = hGrad;\n ctx.fillRect(0, 0, w, h);\n ctx.globalCompositeOperation = \"destination-in\";\n const vGrad = ctx.createLinearGradient(0, 0, 0, h);\n vGrad.addColorStop(0, \"rgba(0,0,0,1)\");\n vGrad.addColorStop(1, \"rgba(0,0,0,0)\");\n ctx.fillStyle = vGrad;\n ctx.fillRect(0, 0, w, h);\n ctx.globalCompositeOperation = \"source-over\";\n }\n const dataUrl = canvas.toDataURL(\"image/png\");\n backgroundGradients.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.maskImage = \"none\";\n el.style.background = \"none\";\n el.style.backgroundImage = `url(${dataUrl})`;\n el.style.backgroundSize = \"100% 100%\";\n el.style.backgroundPosition = \"0 0\";\n }\n });\n\n // Fix for images with object-fit: cover not being respected by html2canvas\n const objectCoverImages = clonedElement.querySelectorAll(\"img.object-cover\");\n objectCoverImages.forEach((img) => {\n if (img instanceof HTMLImageElement) {\n const parent = img.parentElement;\n if (parent) {\n // Replace img with a div that has background-image: cover\n const div = clonedDoc.createElement(\"div\");\n div.style.width = \"100%\";\n div.style.height = \"100%\";\n div.style.backgroundImage = `url(${img.src})`;\n div.style.backgroundSize = \"cover\";\n div.style.backgroundPosition = \"center\";\n div.style.borderRadius = window.getComputedStyle(img).borderRadius;\n div.className = img.className;\n parent.replaceChild(div, img);\n }\n }\n });\n\n // Fix Avatar alignment in PDF: apply consistent margin for both image and fallback\n const headerAvatars = clonedElement.querySelectorAll(\".pdf-header-avatar\");\n headerAvatars.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.marginTop = \"1.33rem\";\n }\n });\n\n // Hide elements marked as hidden for PDF\n const hiddenElements = clonedElement.querySelectorAll(\".pdf-hidden\");\n hiddenElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"none\";\n }\n });\n\n // Fix SVG vertical alignment for html2canvas\n // html2canvas doesn't properly handle flex align-items with SVGs\n const svgs = clonedElement.querySelectorAll(\"svg\");\n svgs.forEach((svg) => {\n svg.style.display = \"inline-block\";\n svg.style.verticalAlign = \"middle\";\n svg.style.flexShrink = \"0\";\n svg.style.marginRight = \"0.4rem\";\n svg.style.marginBottom = \"-1.2rem\";\n });\n\n // Fix line-clamp which causes text vertical cut-offs in html2canvas\n const clampElements = clonedElement.querySelectorAll(\n \".line-clamp-1, .line-clamp-2, .line-clamp-3\",\n );\n clampElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"block\";\n el.style.webkitLineClamp = \"unset\";\n el.style.webkitBoxOrient = \"unset\";\n el.style.overflow = \"visible\";\n el.style.whiteSpace = \"normal\";\n el.style.lineHeight = \"1.5\";\n }\n });\n\n // Replace generic HTML elements like custom badges with simple spans\n // because html2canvas struggles with flexbox and strict borders inside round pills\n const badges = clonedElement.querySelectorAll(\".pdf-badge\");\n badges.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.paddingTop = \"-10px\";\n el.style.display = \"block\";\n el.style.paddingTop = \"0px\";\n el.style.paddingBottom = \"0.8rem\";\n }\n });\n\n // Fix headers with bottom borders losing padding/margin\n const headersWithBorders = clonedElement.querySelectorAll(\"h1, h2, h3, h4\");\n headersWithBorders.forEach((el) => {\n if (el instanceof HTMLElement) {\n const hasBorder =\n el.className.includes(\"border-b\") || el.style.borderBottomWidth !== \"\";\n if (hasBorder) {\n el.style.paddingBottom = \"1.2rem\";\n }\n }\n });\n\n // Add margin top to pdf-line-v elements\n const verticalLines = clonedElement.querySelectorAll(\".pdf-line-v\");\n verticalLines.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.marginTop = \"0.5rem\";\n el.style.zIndex = \"5\";\n }\n });\n\n // Add margin bottom to initials in header for better alignment in PDF\n const headerInitials = clonedElement.querySelectorAll(\".pdf-header-initials\");\n headerInitials.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.paddingBottom = \"2rem\";\n }\n });\n\n // Add styles to summary for PDF\n const summaries = clonedElement.querySelectorAll(\".pdf-summary\");\n summaries.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.width = \"100%\";\n el.style.maxWidth = \"100%\";\n el.style.marginTop = \"1.5rem\";\n }\n });\n\n // Show footer in PDF and force grayscale for the logo\n const pdfFooters = clonedElement.querySelectorAll(\".pdf-footer\");\n pdfFooters.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"flex\";\n }\n });\n\n // Also make sure parent containers don't restrict height\n let parent = clonedElement.parentElement;\n while (parent) {\n parent.style.height = \"auto\";\n parent.style.overflow = \"visible\";\n parent = parent.parentElement;\n }\n }\n },\n });\n\n const imgData = canvas.toDataURL(\"image/jpeg\", 1);\n const imgWidthPx = canvas.width;\n const imgHeightPx = canvas.height;\n\n // Convert px to mm (at 96 DPI base * scale factor)\n const pxToMm = 25.4 / (96 * 2);\n const pdfWidth = imgWidthPx * pxToMm;\n const pdfHeight = imgHeightPx * pxToMm;\n\n const pdf = new jsPDF({\n orientation: pdfWidth > pdfHeight ? \"landscape\" : \"portrait\",\n unit: \"mm\",\n format: [pdfWidth, pdfHeight],\n });\n\n pdf.addImage(imgData, \"JPEG\", 0, 0, pdfWidth, pdfHeight, undefined, \"FAST\");\n\n const fileName = `CV_${data.first_name}_${data.last_name}`.replace(/\\s+/g, \"_\");\n pdf.save(`${fileName}.pdf`);\n } catch (error) {\n console.error(\"Error generating PDF:\", error);\n } finally {\n setIsDownloading(false);\n }\n };\n\n return (\n <div\n className=\"relative h-full w-full overflow-y-auto bg-muted/20 px-4 py-4 sm:px-8\"\n style={themeStyle}\n >\n {/* Preview Panel - Takes full width, centered like a document */}\n <div className=\"mx-auto h-full w-full max-w-5xl\">\n <ResumePreview\n ref={previewRef}\n data={draftData}\n headerSlot={headerSlot}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n showTopBar={showTopBar}\n className={className}\n />\n </div>\n\n {/* Floating Action Buttons */}\n <div className=\"fixed bottom-6 right-6 z-50 flex flex-col gap-3 sm:flex-row\">\n {editable && (\n <Sheet open={isOpen} onOpenChange={handleOpenChange}>\n <SheetTrigger asChild>\n <Button\n size=\"lg\"\n className=\"flex items-center gap-2 rounded-full bg-primary px-4 text-primary-foreground shadow-xl hover:bg-primary/90\"\n >\n <Edit className=\"h-5 w-5\" />\n <span className=\"hidden font-semibold sm:inline\">Editar</span>\n </Button>\n </SheetTrigger>\n {/* Drawer opens from the right side, taking good width */}\n <SheetContent\n side=\"right\"\n showCloseButton={false}\n className=\"flex w-full flex-col overflow-hidden bg-card p-0 sm:max-w-md md:max-w-2xl\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 pb-2\">\n <SheetTitle className=\"text-2xl font-bold text-foreground\">Editar</SheetTitle>\n <SheetClose className=\"rounded-full p-2 opacity-70 transition-colors hover:bg-muted hover:opacity-100\">\n <X className=\"h-6 w-6 text-foreground\" />\n <span className=\"sr-only\">Cerrar</span>\n </SheetClose>\n </div>\n\n {/* Scrollable editor content */}\n <div className=\"flex-1 overflow-y-auto px-6 pb-6 pt-2\">\n <ResumeEditor\n data={draftData}\n onChange={setDraftData}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n menuSlot={menuSlot}\n />\n </div>\n\n {/* Sticky save footer */}\n {onSave && (\n <div className=\"flex justify-end gap-3 p-4\">\n <SheetClose asChild>\n <Button variant=\"outline\">Cancelar</Button>\n </SheetClose>\n <Button onClick={handleSave} className=\"gap-2\">\n <Save className=\"h-4 w-4\" />\n Guardar\n </Button>\n </div>\n )}\n </SheetContent>\n </Sheet>\n )}\n\n {withDownload && (\n <Button\n size=\"lg\"\n variant=\"ghost\"\n disabled={isDownloading}\n onClick={handleDownload}\n className=\"flex items-center gap-2 rounded-full bg-purple-200 px-4 text-purple-100 text-primary shadow-xl\"\n >\n {isDownloading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n <Download className=\"h-5 w-5\" />\n )}\n <span className=\"hidden font-semibold sm:inline\">\n {isDownloading ? \"Generando...\" : \"Descargar\"}\n </span>\n </Button>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"AAkXQ,cAiBM,YAjBN;AAlXR,SAAS,QAAQ,gBAAgB;AACjC,OAAO,iBAAiB;AACxB,SAAS,aAAa;AAEtB,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,cAAc;AACvB,SAAS,UAAU,MAAM,SAAS,MAAM,SAAS;AACjD,SAAS,OAAO,cAAc,cAAc,YAAY,kBAAkB;AAE1E,SAAS,eAAe,UAA4B,SAA8C;AAEhG,QAAM,gBAA2C,CAAC;AAClD,MAAI,SAAS,qCAAqC,QAAQ;AACxD,kBAAc,mCAAmC,QAAQ;AAC3D,MAAI,SAAS,6BAA6B,QAAQ;AAChD,kBAAc,2BAA2B,QAAQ;AACnD,MAAI,SAAS,iBAAiB,QAAQ;AACpC,kBAAc,eAAe,QAAQ;AACvC,MAAI,KAAK,UAAU,SAAS,WAAW,MAAM,KAAK,UAAU,QAAQ,WAAW;AAC7E,kBAAc,cAAc,QAAQ;AACtC,MAAI,KAAK,UAAU,SAAS,QAAQ,MAAM,KAAK,UAAU,QAAQ,QAAQ;AACvE,kBAAc,WAAW,QAAQ;AAGnC,QAAM,WAAsC,CAAC;AAC7C,QAAM,aAAyC;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,aAAW,QAAQ,CAAC,UAAU;AAC5B,QAAI,SAAS,KAAK,MAAM,QAAQ,KAAK,GAAG;AAEtC,eAAS,KAAK,IAAI,QAAQ,KAAK;AAAA,IACjC;AAAA,EACF,CAAC;AAGD,QAAM,qBAAqB,CAAC,YAAmB,CAAC,GAAG,YAAmB,CAAC,MAAM;AAC3E,UAAM,UAAiB,CAAC;AACxB,UAAM,UAAU,IAAI,IAAI,UAAU,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AAEhE,cAAU,QAAQ,CAAC,aAAa;AAC9B,YAAM,WAAW,QAAQ,IAAI,SAAS,EAAE;AACxC,UAAI,CAAC,UAAU;AAEb,gBAAQ,KAAK,EAAE,QAAQ,UAAoB,MAAM,SAAS,CAAC;AAAA,MAC7D,WAAW,KAAK,UAAU,QAAQ,MAAM,KAAK,UAAU,QAAQ,GAAG;AAEhE,gBAAQ,KAAK,EAAE,QAAQ,UAAoB,MAAM,UAAU,IAAI,SAAS,GAAG,CAAC;AAAA,MAC9E;AAAA,IACF,CAAC;AAED,UAAM,UAAU,IAAI,IAAI,UAAU,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AAChE,cAAU,QAAQ,CAAC,aAAa;AAC9B,UAAI,CAAC,QAAQ,IAAI,SAAS,EAAE,GAAG;AAE7B,gBAAQ,KAAK,EAAE,QAAQ,UAAoB,IAAI,SAAS,GAAG,CAAC;AAAA,MAC9D;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT;AAEA,QAAM,kBAAkB;AAAA,IACtB,GAAG,mBAAmB,SAAS,iBAAiB,QAAQ,eAAe;AAAA,IACvE,GAAG,mBAAmB,SAAS,WAAW,QAAQ,SAAS;AAAA,IAC3D,GAAG,mBAAmB,SAAS,YAAY,QAAQ,UAAU;AAAA,EAC/D;AAEA,QAAM,SAAS,SAAS,QAAQ,QAAQ,CAAC;AACzC,QAAM,SAAS,QAAQ,QAAQ,QAAQ,CAAC;AACxC,QAAM,SAAS,SAAS,QAAQ,QAAQ,CAAC;AACzC,QAAM,SAAS,QAAQ,QAAQ,QAAQ,CAAC;AAExC,QAAM,aAAa,CAAC,GAAG,mBAAmB,QAAQ,MAAM,GAAG,GAAG,mBAAmB,QAAQ,MAAM,CAAC;AAEhG,SAAO;AAAA,IACL,WAAW;AAAA,MACT,QAAQ,OAAO,KAAK,aAAa,EAAE,SAAS,IAAI,WAAW;AAAA,MAC3D,MAAM;AAAA,IACR;AAAA,IACA,MAAM;AAAA,MACJ,QAAQ,OAAO,KAAK,QAAQ,EAAE,SAAS,IAAI,WAAW;AAAA,MACtD,MAAM;AAAA,IACR;AAAA,IACA,YAAY;AAAA,MACV,QAAQ,SAAS,YAAY,QAAQ,UAAU,WAAW;AAAA,MAC1D,MAAM,EAAE,SAAS,QAAQ,QAAQ;AAAA,IACnC;AAAA,IACA,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,gBAAgB,mBAAmB,SAAS,gBAAgB,QAAQ,cAAc;AAAA,EACpF;AACF;AAEO,SAAS,OAAO;AAAA,EACrB;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA,eAAe;AAAA,EACf,QAAQ;AAAA,EACR,aAAa,CAAC;AAAA,EACd,eAAe,CAAC;AAAA,EAChB,WAAW,CAAC;AAAA,EACZ;AAAA,EACA,aAAa;AAAA,EACb;AACF,GAAgB;AACd,QAAM,aAAa,QAAS,EAAE,cAAc,MAAM,IAA4B;AAC9E,QAAM,CAAC,WAAW,YAAY,IAAI,SAA2B,IAAI;AACjE,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAC1C,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,KAAK;AACxD,QAAM,aAAa,OAAuB,IAAI;AAE9C,QAAM,mBAAmB,CAAC,SAAkB;AAC1C,QAAI,MAAM;AACR,mBAAa,IAAI;AAAA,IACnB;AACA,cAAU,IAAI;AAAA,EAChB;AAEA,QAAM,aAAa,MAAM;AACvB,QAAI,QAAQ;AACV,YAAM,UAAU,eAAe,MAAM,SAAS;AAC9C,aAAO,SAAS,SAAS;AAAA,IAC3B;AACA,cAAU,KAAK;AAAA,EACjB;AAEA,QAAM,iBAAiB,YAAY;AACjC,QAAI,CAAC,WAAW,WAAW,cAAe;AAC1C,qBAAiB,IAAI;AAErB,QAAI;AACF,YAAM,UAAU,WAAW;AAC3B,YAAM,SAAS,MAAM,YAAY,SAAS;AAAA,QACxC,OAAO;AAAA,QACP,SAAS;AAAA,QACT,iBAAiB;AAAA,QACjB,SAAS;AAAA,QACT,aAAa;AAAA;AAAA,QACb,SAAS,CAAC,cAAc;AACtB,gBAAM,gBAAgB,UAAU,eAAe,qBAAqB;AACpE,cAAI,yBAAyB,aAAa;AACxC,0BAAc,MAAM,SAAS;AAC7B,0BAAc,MAAM,WAAW;AAC/B,0BAAc,MAAM,QAAQ;AAG5B,kBAAM,qBAAqB,MAAM,KAAK,cAAc,iBAAiB,kBAAkB,CAAC;AACxF,gBAAI,cAAc,UAAU,SAAS,iBAAiB,GAAG;AACvD,iCAAmB,KAAK,aAAa;AAAA,YACvC;AACA,+BAAmB,QAAQ,CAAC,OAAO;AACjC,kBAAI,cAAc,aAAa;AAC7B,mBAAG,MAAM,YAAY,iBAAiB,KAAK,WAAW;AAAA,cACxD;AAAA,YACF,CAAC;AAID,kBAAM,sBAAsB,cAAc,iBAAiB,gCAAgC;AAC3F,kBAAM,IAAI;AACV,kBAAM,IAAI;AACV,kBAAMA,UAAS,UAAU,cAAc,QAAQ;AAC/C,YAAAA,QAAO,QAAQ;AACf,YAAAA,QAAO,SAAS;AAChB,kBAAM,MAAMA,QAAO,WAAW,IAAI;AAClC,gBAAI,KAAK;AACP,oBAAM,QAAQ,IAAI,qBAAqB,GAAG,GAAG,GAAG,CAAC;AACjD,oBAAM,aAAa,GAAG,SAAS;AAC/B,oBAAM,aAAa,KAAK,SAAS;AACjC,oBAAM,aAAa,GAAG,SAAS;AAC/B,kBAAI,YAAY;AAChB,kBAAI,SAAS,GAAG,GAAG,GAAG,CAAC;AACvB,kBAAI,2BAA2B;AAC/B,oBAAM,QAAQ,IAAI,qBAAqB,GAAG,GAAG,GAAG,CAAC;AACjD,oBAAM,aAAa,GAAG,eAAe;AACrC,oBAAM,aAAa,GAAG,eAAe;AACrC,kBAAI,YAAY;AAChB,kBAAI,SAAS,GAAG,GAAG,GAAG,CAAC;AACvB,kBAAI,2BAA2B;AAAA,YACjC;AACA,kBAAM,UAAUA,QAAO,UAAU,WAAW;AAC5C,gCAAoB,QAAQ,CAAC,OAAO;AAClC,kBAAI,cAAc,aAAa;AAC7B,mBAAG,MAAM,YAAY;AACrB,mBAAG,MAAM,aAAa;AACtB,mBAAG,MAAM,kBAAkB,OAAO,OAAO;AACzC,mBAAG,MAAM,iBAAiB;AAC1B,mBAAG,MAAM,qBAAqB;AAAA,cAChC;AAAA,YACF,CAAC;AAGD,kBAAM,oBAAoB,cAAc,iBAAiB,kBAAkB;AAC3E,8BAAkB,QAAQ,CAAC,QAAQ;AACjC,kBAAI,eAAe,kBAAkB;AACnC,sBAAMC,UAAS,IAAI;AACnB,oBAAIA,SAAQ;AAEV,wBAAM,MAAM,UAAU,cAAc,KAAK;AACzC,sBAAI,MAAM,QAAQ;AAClB,sBAAI,MAAM,SAAS;AACnB,sBAAI,MAAM,kBAAkB,OAAO,IAAI,GAAG;AAC1C,sBAAI,MAAM,iBAAiB;AAC3B,sBAAI,MAAM,qBAAqB;AAC/B,sBAAI,MAAM,eAAe,OAAO,iBAAiB,GAAG,EAAE;AACtD,sBAAI,YAAY,IAAI;AACpB,kBAAAA,QAAO,aAAa,KAAK,GAAG;AAAA,gBAC9B;AAAA,cACF;AAAA,YACF,CAAC;AAGD,kBAAM,gBAAgB,cAAc,iBAAiB,oBAAoB;AACzE,0BAAc,QAAQ,CAAC,OAAO;AAC5B,kBAAI,cAAc,aAAa;AAC7B,mBAAG,MAAM,YAAY;AAAA,cACvB;AAAA,YACF,CAAC;AAGD,kBAAM,iBAAiB,cAAc,iBAAiB,aAAa;AACnE,2BAAe,QAAQ,CAAC,OAAO;AAC7B,kBAAI,cAAc,aAAa;AAC7B,mBAAG,MAAM,UAAU;AAAA,cACrB;AAAA,YACF,CAAC;AAID,kBAAM,OAAO,cAAc,iBAAiB,KAAK;AACjD,iBAAK,QAAQ,CAAC,QAAQ;AACpB,kBAAI,MAAM,UAAU;AACpB,kBAAI,MAAM,gBAAgB;AAC1B,kBAAI,MAAM,aAAa;AACvB,kBAAI,MAAM,cAAc;AACxB,kBAAI,MAAM,eAAe;AAAA,YAC3B,CAAC;AAGD,kBAAM,gBAAgB,cAAc;AAAA,cAClC;AAAA,YACF;AACA,0BAAc,QAAQ,CAAC,OAAO;AAC5B,kBAAI,cAAc,aAAa;AAC7B,mBAAG,MAAM,UAAU;AACnB,mBAAG,MAAM,kBAAkB;AAC3B,mBAAG,MAAM,kBAAkB;AAC3B,mBAAG,MAAM,WAAW;AACpB,mBAAG,MAAM,aAAa;AACtB,mBAAG,MAAM,aAAa;AAAA,cACxB;AAAA,YACF,CAAC;AAID,kBAAM,SAAS,cAAc,iBAAiB,YAAY;AAC1D,mBAAO,QAAQ,CAAC,OAAO;AACrB,kBAAI,cAAc,aAAa;AAC7B,mBAAG,MAAM,aAAa;AACtB,mBAAG,MAAM,UAAU;AACnB,mBAAG,MAAM,aAAa;AACtB,mBAAG,MAAM,gBAAgB;AAAA,cAC3B;AAAA,YACF,CAAC;AAGD,kBAAM,qBAAqB,cAAc,iBAAiB,gBAAgB;AAC1E,+BAAmB,QAAQ,CAAC,OAAO;AACjC,kBAAI,cAAc,aAAa;AAC7B,sBAAM,YACJ,GAAG,UAAU,SAAS,UAAU,KAAK,GAAG,MAAM,sBAAsB;AACtE,oBAAI,WAAW;AACb,qBAAG,MAAM,gBAAgB;AAAA,gBAC3B;AAAA,cACF;AAAA,YACF,CAAC;AAGD,kBAAM,gBAAgB,cAAc,iBAAiB,aAAa;AAClE,0BAAc,QAAQ,CAAC,OAAO;AAC5B,kBAAI,cAAc,aAAa;AAC7B,mBAAG,MAAM,YAAY;AACrB,mBAAG,MAAM,SAAS;AAAA,cACpB;AAAA,YACF,CAAC;AAGD,kBAAM,iBAAiB,cAAc,iBAAiB,sBAAsB;AAC5E,2BAAe,QAAQ,CAAC,OAAO;AAC7B,kBAAI,cAAc,aAAa;AAC7B,mBAAG,MAAM,gBAAgB;AAAA,cAC3B;AAAA,YACF,CAAC;AAGD,kBAAM,YAAY,cAAc,iBAAiB,cAAc;AAC/D,sBAAU,QAAQ,CAAC,OAAO;AACxB,kBAAI,cAAc,aAAa;AAC7B,mBAAG,MAAM,QAAQ;AACjB,mBAAG,MAAM,WAAW;AACpB,mBAAG,MAAM,YAAY;AAAA,cACvB;AAAA,YACF,CAAC;AAGD,kBAAM,aAAa,cAAc,iBAAiB,aAAa;AAC/D,uBAAW,QAAQ,CAAC,OAAO;AACzB,kBAAI,cAAc,aAAa;AAC7B,mBAAG,MAAM,UAAU;AAAA,cACrB;AAAA,YACF,CAAC;AAGD,gBAAI,SAAS,cAAc;AAC3B,mBAAO,QAAQ;AACb,qBAAO,MAAM,SAAS;AACtB,qBAAO,MAAM,WAAW;AACxB,uBAAS,OAAO;AAAA,YAClB;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAED,YAAM,UAAU,OAAO,UAAU,cAAc,CAAC;AAChD,YAAM,aAAa,OAAO;AAC1B,YAAM,cAAc,OAAO;AAG3B,YAAM,SAAS,QAAQ,KAAK;AAC5B,YAAM,WAAW,aAAa;AAC9B,YAAM,YAAY,cAAc;AAEhC,YAAM,MAAM,IAAI,MAAM;AAAA,QACpB,aAAa,WAAW,YAAY,cAAc;AAAA,QAClD,MAAM;AAAA,QACN,QAAQ,CAAC,UAAU,SAAS;AAAA,MAC9B,CAAC;AAED,UAAI,SAAS,SAAS,QAAQ,GAAG,GAAG,UAAU,WAAW,QAAW,MAAM;AAE1E,YAAM,WAAW,MAAM,KAAK,UAAU,IAAI,KAAK,SAAS,GAAG,QAAQ,QAAQ,GAAG;AAC9E,UAAI,KAAK,GAAG,QAAQ,MAAM;AAAA,IAC5B,SAAS,OAAO;AACd,cAAQ,MAAM,yBAAyB,KAAK;AAAA,IAC9C,UAAE;AACA,uBAAiB,KAAK;AAAA,IACxB;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO;AAAA,MAGP;AAAA,4BAAC,SAAI,WAAU,mCACb;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL,MAAM;AAAA,YACN;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA;AAAA,QACF,GACF;AAAA,QAGA,qBAAC,SAAI,WAAU,+DACZ;AAAA,sBACC,qBAAC,SAAM,MAAM,QAAQ,cAAc,kBACjC;AAAA,gCAAC,gBAAa,SAAO,MACnB;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBAEV;AAAA,sCAAC,QAAK,WAAU,WAAU;AAAA,kBAC1B,oBAAC,UAAK,WAAU,kCAAiC,oBAAM;AAAA;AAAA;AAAA,YACzD,GACF;AAAA,YAEA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,iBAAiB;AAAA,gBACjB,WAAU;AAAA,gBAGV;AAAA,uCAAC,SAAI,WAAU,8CACb;AAAA,wCAAC,cAAW,WAAU,sCAAqC,oBAAM;AAAA,oBACjE,qBAAC,cAAW,WAAU,kFACpB;AAAA,0CAAC,KAAE,WAAU,2BAA0B;AAAA,sBACvC,oBAAC,UAAK,WAAU,WAAU,oBAAM;AAAA,uBAClC;AAAA,qBACF;AAAA,kBAGA,oBAAC,SAAI,WAAU,yCACb;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAM;AAAA,sBACN,UAAU;AAAA,sBACV;AAAA,sBACA;AAAA,sBACA;AAAA,sBACA;AAAA;AAAA,kBACF,GACF;AAAA,kBAGC,UACC,qBAAC,SAAI,WAAU,8BACb;AAAA,wCAAC,cAAW,SAAO,MACjB,8BAAC,UAAO,SAAQ,WAAU,sBAAQ,GACpC;AAAA,oBACA,qBAAC,UAAO,SAAS,YAAY,WAAU,SACrC;AAAA,0CAAC,QAAK,WAAU,WAAU;AAAA,sBAAE;AAAA,uBAE9B;AAAA,qBACF;AAAA;AAAA;AAAA,YAEJ;AAAA,aACF;AAAA,UAGD,gBACC;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,UAAU;AAAA,cACV,SAAS;AAAA,cACT,WAAU;AAAA,cAET;AAAA,gCACC,oBAAC,WAAQ,WAAU,wBAAuB,IAE1C,oBAAC,YAAS,WAAU,WAAU;AAAA,gBAEhC,oBAAC,UAAK,WAAU,kCACb,0BAAgB,iBAAiB,aACpC;AAAA;AAAA;AAAA,UACF;AAAA,WAEJ;AAAA;AAAA;AAAA,EACF;AAEJ;","names":["canvas","parent"]}
|
|
@@ -79,7 +79,7 @@ function Combobox({
|
|
|
79
79
|
]
|
|
80
80
|
}
|
|
81
81
|
) }),
|
|
82
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_popover.PopoverContent, { className: "w-
|
|
82
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_popover.PopoverContent, { className: "w-[--radix-popover-trigger-width] p-0", align: "start", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
83
83
|
import_command.Command,
|
|
84
84
|
{
|
|
85
85
|
filter: (itemValue, search) => {
|
|
@@ -87,14 +87,15 @@ function Combobox({
|
|
|
87
87
|
if (normalizedSearch.length === 0) return 1;
|
|
88
88
|
return normalizeForSearch(itemValue).includes(normalizedSearch) ? 1 : 0;
|
|
89
89
|
},
|
|
90
|
+
className: "max-h-[300px]",
|
|
90
91
|
children: [
|
|
91
92
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_command.CommandInput, { ref: inputRef, placeholder: searchPlaceholder }),
|
|
92
93
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_command.CommandList, { children: [
|
|
93
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_command.CommandEmpty, { children: emptyText }),
|
|
94
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_command.CommandEmpty, { className: "px-4", children: emptyText }),
|
|
94
95
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_command.CommandGroup, { children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
95
96
|
import_command.CommandItem,
|
|
96
97
|
{
|
|
97
|
-
value: option.
|
|
98
|
+
value: option.label,
|
|
98
99
|
onSelect: () => {
|
|
99
100
|
onValueChange?.(option.value === value ? "" : option.value);
|
|
100
101
|
setOpen(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/ui/combobox/combobox.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Check, ChevronsUpDown } from \"lucide-react\";\n\nimport { cn } from \"../../../lib/utils\";\nimport { Button } from \"../button\";\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"../command\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../popover\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n}\n\ninterface ComboboxProps {\n options: ComboboxOption[];\n value?: string;\n onValueChange?: (value: string) => void;\n placeholder?: string;\n searchPlaceholder?: string;\n emptyText?: string;\n className?: string;\n disabled?: boolean;\n}\n\nfunction Combobox({\n options,\n value,\n onValueChange,\n placeholder = \"Select option...\",\n searchPlaceholder = \"Search...\",\n emptyText = \"No option found.\",\n className,\n disabled = false,\n}: ComboboxProps) {\n const [open, setOpen] = React.useState(false);\n const inputRef = React.useRef<HTMLInputElement | null>(null);\n\n const selectedOption = options.find((option) => option.value === value);\n\n const normalizeForSearch = (raw: string) =>\n raw\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .toLowerCase()\n .trim();\n\n React.useEffect(() => {\n if (!open) return;\n setTimeout(() => {\n inputRef.current?.focus();\n }, 0);\n }, [open]);\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n aria-expanded={open}\n data-slot=\"combobox\"\n className={cn(\n \"w-full justify-between\",\n !selectedOption && \"text-muted-foreground\",\n className,\n )}\n disabled={disabled}\n >\n {selectedOption ? selectedOption.label : placeholder}\n <ChevronsUpDown className=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/ui/combobox/combobox.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Check, ChevronsUpDown } from \"lucide-react\";\n\nimport { cn } from \"../../../lib/utils\";\nimport { Button } from \"../button\";\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"../command\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../popover\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n}\n\ninterface ComboboxProps {\n options: ComboboxOption[];\n value?: string;\n onValueChange?: (value: string) => void;\n placeholder?: string;\n searchPlaceholder?: string;\n emptyText?: string;\n className?: string;\n disabled?: boolean;\n}\n\nfunction Combobox({\n options,\n value,\n onValueChange,\n placeholder = \"Select option...\",\n searchPlaceholder = \"Search...\",\n emptyText = \"No option found.\",\n className,\n disabled = false,\n}: ComboboxProps) {\n const [open, setOpen] = React.useState(false);\n const inputRef = React.useRef<HTMLInputElement | null>(null);\n\n const selectedOption = options.find((option) => option.value === value);\n\n const normalizeForSearch = (raw: string) =>\n raw\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .toLowerCase()\n .trim();\n\n React.useEffect(() => {\n if (!open) return;\n setTimeout(() => {\n inputRef.current?.focus();\n }, 0);\n }, [open]);\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n aria-expanded={open}\n data-slot=\"combobox\"\n className={cn(\n \"w-full justify-between\",\n !selectedOption && \"text-muted-foreground\",\n className,\n )}\n disabled={disabled}\n >\n {selectedOption ? selectedOption.label : placeholder}\n <ChevronsUpDown className=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-[--radix-popover-trigger-width] p-0\" align=\"start\">\n <Command\n filter={(itemValue, search) => {\n const normalizedSearch = normalizeForSearch(search);\n if (normalizedSearch.length === 0) return 1;\n return normalizeForSearch(itemValue).includes(normalizedSearch) ? 1 : 0;\n }}\n className=\"max-h-[300px]\"\n >\n <CommandInput ref={inputRef} placeholder={searchPlaceholder} />\n <CommandList>\n <CommandEmpty className=\"px-4\">{emptyText}</CommandEmpty>\n <CommandGroup>\n {options.map((option) => (\n <CommandItem\n key={option.value}\n value={option.label}\n onSelect={() => {\n onValueChange?.(option.value === value ? \"\" : option.value);\n setOpen(false);\n }}\n >\n <Check\n className={cn(\n \"mr-2 h-4 w-4\",\n value === option.value ? \"opacity-100\" : \"opacity-0\",\n )}\n />\n {option.label}\n </CommandItem>\n ))}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n );\n}\n\nexport { Combobox };\nexport type { ComboboxProps };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiEQ;AA/DR,YAAuB;AACvB,0BAAsC;AAEtC,mBAAmB;AACnB,oBAAuB;AACvB,qBAOO;AACP,qBAAwD;AAkBxD,SAAS,SAAS;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB,YAAY;AAAA,EACZ;AAAA,EACA,WAAW;AACb,GAAkB;AAChB,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAC5C,QAAM,WAAW,MAAM,OAAgC,IAAI;AAE3D,QAAM,iBAAiB,QAAQ,KAAK,CAAC,WAAW,OAAO,UAAU,KAAK;AAEtE,QAAM,qBAAqB,CAAC,QAC1B,IACG,UAAU,KAAK,EACf,QAAQ,oBAAoB,EAAE,EAC9B,YAAY,EACZ,KAAK;AAEV,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,KAAM;AACX,eAAW,MAAM;AACf,eAAS,SAAS,MAAM;AAAA,IAC1B,GAAG,CAAC;AAAA,EACN,GAAG,CAAC,IAAI,CAAC;AAET,SACE,6CAAC,0BAAQ,MAAY,cAAc,SACjC;AAAA,gDAAC,iCAAe,SAAO,MACrB;AAAA,MAAC;AAAA;AAAA,QACC,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,iBAAe;AAAA,QACf,aAAU;AAAA,QACV,eAAW;AAAA,UACT;AAAA,UACA,CAAC,kBAAkB;AAAA,UACnB;AAAA,QACF;AAAA,QACA;AAAA,QAEC;AAAA,2BAAiB,eAAe,QAAQ;AAAA,UACzC,4CAAC,sCAAe,WAAU,oCAAmC;AAAA;AAAA;AAAA,IAC/D,GACF;AAAA,IACA,4CAAC,iCAAe,WAAU,yCAAwC,OAAM,SACtE;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ,CAAC,WAAW,WAAW;AAC7B,gBAAM,mBAAmB,mBAAmB,MAAM;AAClD,cAAI,iBAAiB,WAAW,EAAG,QAAO;AAC1C,iBAAO,mBAAmB,SAAS,EAAE,SAAS,gBAAgB,IAAI,IAAI;AAAA,QACxE;AAAA,QACA,WAAU;AAAA,QAEV;AAAA,sDAAC,+BAAa,KAAK,UAAU,aAAa,mBAAmB;AAAA,UAC7D,6CAAC,8BACC;AAAA,wDAAC,+BAAa,WAAU,QAAQ,qBAAU;AAAA,YAC1C,4CAAC,+BACE,kBAAQ,IAAI,CAAC,WACZ;AAAA,cAAC;AAAA;AAAA,gBAEC,OAAO,OAAO;AAAA,gBACd,UAAU,MAAM;AACd,kCAAgB,OAAO,UAAU,QAAQ,KAAK,OAAO,KAAK;AAC1D,0BAAQ,KAAK;AAAA,gBACf;AAAA,gBAEA;AAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,eAAW;AAAA,wBACT;AAAA,wBACA,UAAU,OAAO,QAAQ,gBAAgB;AAAA,sBAC3C;AAAA;AAAA,kBACF;AAAA,kBACC,OAAO;AAAA;AAAA;AAAA,cAbH,OAAO;AAAA,YAcd,CACD,GACH;AAAA,aACF;AAAA;AAAA;AAAA,IACF,GACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -53,7 +53,7 @@ function Combobox({
|
|
|
53
53
|
]
|
|
54
54
|
}
|
|
55
55
|
) }),
|
|
56
|
-
/* @__PURE__ */ jsx(PopoverContent, { className: "w-
|
|
56
|
+
/* @__PURE__ */ jsx(PopoverContent, { className: "w-[--radix-popover-trigger-width] p-0", align: "start", children: /* @__PURE__ */ jsxs(
|
|
57
57
|
Command,
|
|
58
58
|
{
|
|
59
59
|
filter: (itemValue, search) => {
|
|
@@ -61,14 +61,15 @@ function Combobox({
|
|
|
61
61
|
if (normalizedSearch.length === 0) return 1;
|
|
62
62
|
return normalizeForSearch(itemValue).includes(normalizedSearch) ? 1 : 0;
|
|
63
63
|
},
|
|
64
|
+
className: "max-h-[300px]",
|
|
64
65
|
children: [
|
|
65
66
|
/* @__PURE__ */ jsx(CommandInput, { ref: inputRef, placeholder: searchPlaceholder }),
|
|
66
67
|
/* @__PURE__ */ jsxs(CommandList, { children: [
|
|
67
|
-
/* @__PURE__ */ jsx(CommandEmpty, { children: emptyText }),
|
|
68
|
+
/* @__PURE__ */ jsx(CommandEmpty, { className: "px-4", children: emptyText }),
|
|
68
69
|
/* @__PURE__ */ jsx(CommandGroup, { children: options.map((option) => /* @__PURE__ */ jsxs(
|
|
69
70
|
CommandItem,
|
|
70
71
|
{
|
|
71
|
-
value: option.
|
|
72
|
+
value: option.label,
|
|
72
73
|
onSelect: () => {
|
|
73
74
|
onValueChange?.(option.value === value ? "" : option.value);
|
|
74
75
|
setOpen(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/ui/combobox/combobox.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Check, ChevronsUpDown } from \"lucide-react\";\n\nimport { cn } from \"../../../lib/utils\";\nimport { Button } from \"../button\";\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"../command\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../popover\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n}\n\ninterface ComboboxProps {\n options: ComboboxOption[];\n value?: string;\n onValueChange?: (value: string) => void;\n placeholder?: string;\n searchPlaceholder?: string;\n emptyText?: string;\n className?: string;\n disabled?: boolean;\n}\n\nfunction Combobox({\n options,\n value,\n onValueChange,\n placeholder = \"Select option...\",\n searchPlaceholder = \"Search...\",\n emptyText = \"No option found.\",\n className,\n disabled = false,\n}: ComboboxProps) {\n const [open, setOpen] = React.useState(false);\n const inputRef = React.useRef<HTMLInputElement | null>(null);\n\n const selectedOption = options.find((option) => option.value === value);\n\n const normalizeForSearch = (raw: string) =>\n raw\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .toLowerCase()\n .trim();\n\n React.useEffect(() => {\n if (!open) return;\n setTimeout(() => {\n inputRef.current?.focus();\n }, 0);\n }, [open]);\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n aria-expanded={open}\n data-slot=\"combobox\"\n className={cn(\n \"w-full justify-between\",\n !selectedOption && \"text-muted-foreground\",\n className,\n )}\n disabled={disabled}\n >\n {selectedOption ? selectedOption.label : placeholder}\n <ChevronsUpDown className=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/ui/combobox/combobox.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Check, ChevronsUpDown } from \"lucide-react\";\n\nimport { cn } from \"../../../lib/utils\";\nimport { Button } from \"../button\";\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"../command\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../popover\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n}\n\ninterface ComboboxProps {\n options: ComboboxOption[];\n value?: string;\n onValueChange?: (value: string) => void;\n placeholder?: string;\n searchPlaceholder?: string;\n emptyText?: string;\n className?: string;\n disabled?: boolean;\n}\n\nfunction Combobox({\n options,\n value,\n onValueChange,\n placeholder = \"Select option...\",\n searchPlaceholder = \"Search...\",\n emptyText = \"No option found.\",\n className,\n disabled = false,\n}: ComboboxProps) {\n const [open, setOpen] = React.useState(false);\n const inputRef = React.useRef<HTMLInputElement | null>(null);\n\n const selectedOption = options.find((option) => option.value === value);\n\n const normalizeForSearch = (raw: string) =>\n raw\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .toLowerCase()\n .trim();\n\n React.useEffect(() => {\n if (!open) return;\n setTimeout(() => {\n inputRef.current?.focus();\n }, 0);\n }, [open]);\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n aria-expanded={open}\n data-slot=\"combobox\"\n className={cn(\n \"w-full justify-between\",\n !selectedOption && \"text-muted-foreground\",\n className,\n )}\n disabled={disabled}\n >\n {selectedOption ? selectedOption.label : placeholder}\n <ChevronsUpDown className=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-[--radix-popover-trigger-width] p-0\" align=\"start\">\n <Command\n filter={(itemValue, search) => {\n const normalizedSearch = normalizeForSearch(search);\n if (normalizedSearch.length === 0) return 1;\n return normalizeForSearch(itemValue).includes(normalizedSearch) ? 1 : 0;\n }}\n className=\"max-h-[300px]\"\n >\n <CommandInput ref={inputRef} placeholder={searchPlaceholder} />\n <CommandList>\n <CommandEmpty className=\"px-4\">{emptyText}</CommandEmpty>\n <CommandGroup>\n {options.map((option) => (\n <CommandItem\n key={option.value}\n value={option.label}\n onSelect={() => {\n onValueChange?.(option.value === value ? \"\" : option.value);\n setOpen(false);\n }}\n >\n <Check\n className={cn(\n \"mr-2 h-4 w-4\",\n value === option.value ? \"opacity-100\" : \"opacity-0\",\n )}\n />\n {option.label}\n </CommandItem>\n ))}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n );\n}\n\nexport { Combobox };\nexport type { ComboboxProps };\n"],"mappings":";AAiEQ,SAaE,KAbF;AA/DR,YAAY,WAAW;AACvB,SAAS,OAAO,sBAAsB;AAEtC,SAAS,UAAU;AACnB,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,SAAS,gBAAgB,sBAAsB;AAkBxD,SAAS,SAAS;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB,YAAY;AAAA,EACZ;AAAA,EACA,WAAW;AACb,GAAkB;AAChB,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAC5C,QAAM,WAAW,MAAM,OAAgC,IAAI;AAE3D,QAAM,iBAAiB,QAAQ,KAAK,CAAC,WAAW,OAAO,UAAU,KAAK;AAEtE,QAAM,qBAAqB,CAAC,QAC1B,IACG,UAAU,KAAK,EACf,QAAQ,oBAAoB,EAAE,EAC9B,YAAY,EACZ,KAAK;AAEV,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,KAAM;AACX,eAAW,MAAM;AACf,eAAS,SAAS,MAAM;AAAA,IAC1B,GAAG,CAAC;AAAA,EACN,GAAG,CAAC,IAAI,CAAC;AAET,SACE,qBAAC,WAAQ,MAAY,cAAc,SACjC;AAAA,wBAAC,kBAAe,SAAO,MACrB;AAAA,MAAC;AAAA;AAAA,QACC,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,iBAAe;AAAA,QACf,aAAU;AAAA,QACV,WAAW;AAAA,UACT;AAAA,UACA,CAAC,kBAAkB;AAAA,UACnB;AAAA,QACF;AAAA,QACA;AAAA,QAEC;AAAA,2BAAiB,eAAe,QAAQ;AAAA,UACzC,oBAAC,kBAAe,WAAU,oCAAmC;AAAA;AAAA;AAAA,IAC/D,GACF;AAAA,IACA,oBAAC,kBAAe,WAAU,yCAAwC,OAAM,SACtE;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ,CAAC,WAAW,WAAW;AAC7B,gBAAM,mBAAmB,mBAAmB,MAAM;AAClD,cAAI,iBAAiB,WAAW,EAAG,QAAO;AAC1C,iBAAO,mBAAmB,SAAS,EAAE,SAAS,gBAAgB,IAAI,IAAI;AAAA,QACxE;AAAA,QACA,WAAU;AAAA,QAEV;AAAA,8BAAC,gBAAa,KAAK,UAAU,aAAa,mBAAmB;AAAA,UAC7D,qBAAC,eACC;AAAA,gCAAC,gBAAa,WAAU,QAAQ,qBAAU;AAAA,YAC1C,oBAAC,gBACE,kBAAQ,IAAI,CAAC,WACZ;AAAA,cAAC;AAAA;AAAA,gBAEC,OAAO,OAAO;AAAA,gBACd,UAAU,MAAM;AACd,kCAAgB,OAAO,UAAU,QAAQ,KAAK,OAAO,KAAK;AAC1D,0BAAQ,KAAK;AAAA,gBACf;AAAA,gBAEA;AAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,WAAW;AAAA,wBACT;AAAA,wBACA,UAAU,OAAO,QAAQ,gBAAgB;AAAA,sBAC3C;AAAA;AAAA,kBACF;AAAA,kBACC,OAAO;AAAA;AAAA;AAAA,cAbH,OAAO;AAAA,YAcd,CACD,GACH;AAAA,aACF;AAAA;AAAA;AAAA,IACF,GACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -80,12 +80,15 @@ function CommandList({ className, ...props }) {
|
|
|
80
80
|
}
|
|
81
81
|
);
|
|
82
82
|
}
|
|
83
|
-
function CommandEmpty({
|
|
83
|
+
function CommandEmpty({
|
|
84
|
+
className,
|
|
85
|
+
...props
|
|
86
|
+
}) {
|
|
84
87
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
85
88
|
import_cmdk.Command.Empty,
|
|
86
89
|
{
|
|
87
90
|
"data-slot": "command-empty",
|
|
88
|
-
className: "py-6 text-center text-sm",
|
|
91
|
+
className: (0, import_utils.cn)("py-6 text-center text-sm", className),
|
|
89
92
|
...props
|
|
90
93
|
}
|
|
91
94
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/ui/command/command.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { type DialogProps } from \"@radix-ui/react-dialog\";\nimport { Command as CommandPrimitive } from \"cmdk\";\nimport { Search } from \"lucide-react\";\n\nimport { cn } from \"../../../lib/utils\";\nimport { Dialog, DialogContent } from \"../dialog\";\n\nfunction Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {\n return (\n <CommandPrimitive\n data-slot=\"command\"\n className={cn(\n \"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground\",\n className,\n )}\n {...props}\n />\n );\n}\n\ntype CommandDialogProps = DialogProps;\n\nfunction CommandDialog({ children, ...props }: CommandDialogProps) {\n return (\n <Dialog {...props}>\n <DialogContent className=\"overflow-hidden p-0 shadow-lg\">\n <Command className=\"[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5\">\n {children}\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\n\nfunction CommandInput({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Input>) {\n return (\n <div className=\"flex items-center border-b px-3\" cmdk-input-wrapper=\"\">\n <Search className=\"mr-2 h-4 w-4 shrink-0 opacity-50\" />\n <CommandPrimitive.Input\n data-slot=\"command-input\"\n className={cn(\n \"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n {...props}\n />\n </div>\n );\n}\n\nfunction CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {\n return (\n <CommandPrimitive.List\n data-slot=\"command-list\"\n className={cn(\"max-h-[300px] overflow-y-auto overflow-x-hidden\", className)}\n {...props}\n />\n );\n}\n\nfunction CommandEmpty({
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/ui/command/command.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { type DialogProps } from \"@radix-ui/react-dialog\";\nimport { Command as CommandPrimitive } from \"cmdk\";\nimport { Search } from \"lucide-react\";\n\nimport { cn } from \"../../../lib/utils\";\nimport { Dialog, DialogContent } from \"../dialog\";\n\nfunction Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {\n return (\n <CommandPrimitive\n data-slot=\"command\"\n className={cn(\n \"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground\",\n className,\n )}\n {...props}\n />\n );\n}\n\ntype CommandDialogProps = DialogProps;\n\nfunction CommandDialog({ children, ...props }: CommandDialogProps) {\n return (\n <Dialog {...props}>\n <DialogContent className=\"overflow-hidden p-0 shadow-lg\">\n <Command className=\"[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5\">\n {children}\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\n\nfunction CommandInput({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Input>) {\n return (\n <div className=\"flex items-center border-b px-3\" cmdk-input-wrapper=\"\">\n <Search className=\"mr-2 h-4 w-4 shrink-0 opacity-50\" />\n <CommandPrimitive.Input\n data-slot=\"command-input\"\n className={cn(\n \"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n {...props}\n />\n </div>\n );\n}\n\nfunction CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {\n return (\n <CommandPrimitive.List\n data-slot=\"command-list\"\n className={cn(\"max-h-[300px] overflow-y-auto overflow-x-hidden\", className)}\n {...props}\n />\n );\n}\n\nfunction CommandEmpty({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Empty>) {\n return (\n <CommandPrimitive.Empty\n data-slot=\"command-empty\"\n className={cn(\"py-6 text-center text-sm\", className)}\n {...props}\n />\n );\n}\n\nfunction CommandGroup({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Group>) {\n return (\n <CommandPrimitive.Group\n data-slot=\"command-group\"\n className={cn(\n \"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CommandSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Separator>) {\n return (\n <CommandPrimitive.Separator\n data-slot=\"command-separator\"\n className={cn(\"-mx-1 h-px bg-border\", className)}\n {...props}\n />\n );\n}\n\nfunction CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {\n return (\n <CommandPrimitive.Item\n data-slot=\"command-item\"\n className={cn(\n \"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CommandShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"command-shortcut\"\n className={cn(\"ml-auto text-xs tracking-widest text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Command,\n CommandDialog,\n CommandInput,\n CommandList,\n CommandEmpty,\n CommandGroup,\n CommandItem,\n CommandShortcut,\n CommandSeparator,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYI;AARJ,kBAA4C;AAC5C,0BAAuB;AAEvB,mBAAmB;AACnB,oBAAsC;AAEtC,SAAS,QAAQ,EAAE,WAAW,GAAG,MAAM,GAAkD;AACvF,SACE;AAAA,IAAC,YAAAA;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,eAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,cAAc,EAAE,UAAU,GAAG,MAAM,GAAuB;AACjE,SACE,4CAAC,wBAAQ,GAAG,OACV,sDAAC,+BAAc,WAAU,iCACvB,sDAAC,WAAQ,WAAU,+WAChB,UACH,GACF,GACF;AAEJ;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA,GAAG;AACL,GAAwD;AACtD,SACE,6CAAC,SAAI,WAAU,mCAAkC,sBAAmB,IAClE;AAAA,gDAAC,8BAAO,WAAU,oCAAmC;AAAA,IACrD;AAAA,MAAC,YAAAA,QAAiB;AAAA,MAAjB;AAAA,QACC,aAAU;AAAA,QACV,eAAW;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,QACC,GAAG;AAAA;AAAA,IACN;AAAA,KACF;AAEJ;AAEA,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAuD;AAChG,SACE;AAAA,IAAC,YAAAA,QAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,eAAW,iBAAG,mDAAmD,SAAS;AAAA,MACzE,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA,GAAG;AACL,GAAwD;AACtD,SACE;AAAA,IAAC,YAAAA,QAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,eAAW,iBAAG,4BAA4B,SAAS;AAAA,MAClD,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA,GAAG;AACL,GAAwD;AACtD,SACE;AAAA,IAAC,YAAAA,QAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,eAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA,GAAG;AACL,GAA4D;AAC1D,SACE;AAAA,IAAC,YAAAA,QAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,eAAW,iBAAG,wBAAwB,SAAS;AAAA,MAC9C,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAuD;AAChG,SACE;AAAA,IAAC,YAAAA,QAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,eAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,gBAAgB,EAAE,WAAW,GAAG,MAAM,GAAiC;AAC9E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,eAAW,iBAAG,yDAAyD,SAAS;AAAA,MAC/E,GAAG;AAAA;AAAA,EACN;AAEJ;","names":["CommandPrimitive"]}
|
|
@@ -8,7 +8,7 @@ type CommandDialogProps = DialogProps;
|
|
|
8
8
|
declare function CommandDialog({ children, ...props }: CommandDialogProps): react_jsx_runtime.JSX.Element;
|
|
9
9
|
declare function CommandInput({ className, ...props }: React.ComponentProps<typeof Command$1.Input>): react_jsx_runtime.JSX.Element;
|
|
10
10
|
declare function CommandList({ className, ...props }: React.ComponentProps<typeof Command$1.List>): react_jsx_runtime.JSX.Element;
|
|
11
|
-
declare function CommandEmpty({ ...props }: React.ComponentProps<typeof Command$1.Empty>): react_jsx_runtime.JSX.Element;
|
|
11
|
+
declare function CommandEmpty({ className, ...props }: React.ComponentProps<typeof Command$1.Empty>): react_jsx_runtime.JSX.Element;
|
|
12
12
|
declare function CommandGroup({ className, ...props }: React.ComponentProps<typeof Command$1.Group>): react_jsx_runtime.JSX.Element;
|
|
13
13
|
declare function CommandSeparator({ className, ...props }: React.ComponentProps<typeof Command$1.Separator>): react_jsx_runtime.JSX.Element;
|
|
14
14
|
declare function CommandItem({ className, ...props }: React.ComponentProps<typeof Command$1.Item>): react_jsx_runtime.JSX.Element;
|
|
@@ -8,7 +8,7 @@ type CommandDialogProps = DialogProps;
|
|
|
8
8
|
declare function CommandDialog({ children, ...props }: CommandDialogProps): react_jsx_runtime.JSX.Element;
|
|
9
9
|
declare function CommandInput({ className, ...props }: React.ComponentProps<typeof Command$1.Input>): react_jsx_runtime.JSX.Element;
|
|
10
10
|
declare function CommandList({ className, ...props }: React.ComponentProps<typeof Command$1.List>): react_jsx_runtime.JSX.Element;
|
|
11
|
-
declare function CommandEmpty({ ...props }: React.ComponentProps<typeof Command$1.Empty>): react_jsx_runtime.JSX.Element;
|
|
11
|
+
declare function CommandEmpty({ className, ...props }: React.ComponentProps<typeof Command$1.Empty>): react_jsx_runtime.JSX.Element;
|
|
12
12
|
declare function CommandGroup({ className, ...props }: React.ComponentProps<typeof Command$1.Group>): react_jsx_runtime.JSX.Element;
|
|
13
13
|
declare function CommandSeparator({ className, ...props }: React.ComponentProps<typeof Command$1.Separator>): react_jsx_runtime.JSX.Element;
|
|
14
14
|
declare function CommandItem({ className, ...props }: React.ComponentProps<typeof Command$1.Item>): react_jsx_runtime.JSX.Element;
|
|
@@ -49,12 +49,15 @@ function CommandList({ className, ...props }) {
|
|
|
49
49
|
}
|
|
50
50
|
);
|
|
51
51
|
}
|
|
52
|
-
function CommandEmpty({
|
|
52
|
+
function CommandEmpty({
|
|
53
|
+
className,
|
|
54
|
+
...props
|
|
55
|
+
}) {
|
|
53
56
|
return /* @__PURE__ */ jsx(
|
|
54
57
|
CommandPrimitive.Empty,
|
|
55
58
|
{
|
|
56
59
|
"data-slot": "command-empty",
|
|
57
|
-
className: "py-6 text-center text-sm",
|
|
60
|
+
className: cn("py-6 text-center text-sm", className),
|
|
58
61
|
...props
|
|
59
62
|
}
|
|
60
63
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/ui/command/command.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { type DialogProps } from \"@radix-ui/react-dialog\";\nimport { Command as CommandPrimitive } from \"cmdk\";\nimport { Search } from \"lucide-react\";\n\nimport { cn } from \"../../../lib/utils\";\nimport { Dialog, DialogContent } from \"../dialog\";\n\nfunction Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {\n return (\n <CommandPrimitive\n data-slot=\"command\"\n className={cn(\n \"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground\",\n className,\n )}\n {...props}\n />\n );\n}\n\ntype CommandDialogProps = DialogProps;\n\nfunction CommandDialog({ children, ...props }: CommandDialogProps) {\n return (\n <Dialog {...props}>\n <DialogContent className=\"overflow-hidden p-0 shadow-lg\">\n <Command className=\"[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5\">\n {children}\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\n\nfunction CommandInput({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Input>) {\n return (\n <div className=\"flex items-center border-b px-3\" cmdk-input-wrapper=\"\">\n <Search className=\"mr-2 h-4 w-4 shrink-0 opacity-50\" />\n <CommandPrimitive.Input\n data-slot=\"command-input\"\n className={cn(\n \"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n {...props}\n />\n </div>\n );\n}\n\nfunction CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {\n return (\n <CommandPrimitive.List\n data-slot=\"command-list\"\n className={cn(\"max-h-[300px] overflow-y-auto overflow-x-hidden\", className)}\n {...props}\n />\n );\n}\n\nfunction CommandEmpty({
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/ui/command/command.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { type DialogProps } from \"@radix-ui/react-dialog\";\nimport { Command as CommandPrimitive } from \"cmdk\";\nimport { Search } from \"lucide-react\";\n\nimport { cn } from \"../../../lib/utils\";\nimport { Dialog, DialogContent } from \"../dialog\";\n\nfunction Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {\n return (\n <CommandPrimitive\n data-slot=\"command\"\n className={cn(\n \"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground\",\n className,\n )}\n {...props}\n />\n );\n}\n\ntype CommandDialogProps = DialogProps;\n\nfunction CommandDialog({ children, ...props }: CommandDialogProps) {\n return (\n <Dialog {...props}>\n <DialogContent className=\"overflow-hidden p-0 shadow-lg\">\n <Command className=\"[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5\">\n {children}\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\n\nfunction CommandInput({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Input>) {\n return (\n <div className=\"flex items-center border-b px-3\" cmdk-input-wrapper=\"\">\n <Search className=\"mr-2 h-4 w-4 shrink-0 opacity-50\" />\n <CommandPrimitive.Input\n data-slot=\"command-input\"\n className={cn(\n \"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n {...props}\n />\n </div>\n );\n}\n\nfunction CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {\n return (\n <CommandPrimitive.List\n data-slot=\"command-list\"\n className={cn(\"max-h-[300px] overflow-y-auto overflow-x-hidden\", className)}\n {...props}\n />\n );\n}\n\nfunction CommandEmpty({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Empty>) {\n return (\n <CommandPrimitive.Empty\n data-slot=\"command-empty\"\n className={cn(\"py-6 text-center text-sm\", className)}\n {...props}\n />\n );\n}\n\nfunction CommandGroup({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Group>) {\n return (\n <CommandPrimitive.Group\n data-slot=\"command-group\"\n className={cn(\n \"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CommandSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Separator>) {\n return (\n <CommandPrimitive.Separator\n data-slot=\"command-separator\"\n className={cn(\"-mx-1 h-px bg-border\", className)}\n {...props}\n />\n );\n}\n\nfunction CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {\n return (\n <CommandPrimitive.Item\n data-slot=\"command-item\"\n className={cn(\n \"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CommandShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"command-shortcut\"\n className={cn(\"ml-auto text-xs tracking-widest text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Command,\n CommandDialog,\n CommandInput,\n CommandList,\n CommandEmpty,\n CommandGroup,\n CommandItem,\n CommandShortcut,\n CommandSeparator,\n};\n"],"mappings":";AAYI,cA8BA,YA9BA;AARJ,SAAS,WAAW,wBAAwB;AAC5C,SAAS,cAAc;AAEvB,SAAS,UAAU;AACnB,SAAS,QAAQ,qBAAqB;AAEtC,SAAS,QAAQ,EAAE,WAAW,GAAG,MAAM,GAAkD;AACvF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,cAAc,EAAE,UAAU,GAAG,MAAM,GAAuB;AACjE,SACE,oBAAC,UAAQ,GAAG,OACV,8BAAC,iBAAc,WAAU,iCACvB,8BAAC,WAAQ,WAAU,+WAChB,UACH,GACF,GACF;AAEJ;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA,GAAG;AACL,GAAwD;AACtD,SACE,qBAAC,SAAI,WAAU,mCAAkC,sBAAmB,IAClE;AAAA,wBAAC,UAAO,WAAU,oCAAmC;AAAA,IACrD;AAAA,MAAC,iBAAiB;AAAA,MAAjB;AAAA,QACC,aAAU;AAAA,QACV,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,QACC,GAAG;AAAA;AAAA,IACN;AAAA,KACF;AAEJ;AAEA,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAuD;AAChG,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,mDAAmD,SAAS;AAAA,MACzE,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA,GAAG;AACL,GAAwD;AACtD,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,4BAA4B,SAAS;AAAA,MAClD,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA,GAAG;AACL,GAAwD;AACtD,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA,GAAG;AACL,GAA4D;AAC1D,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,wBAAwB,SAAS;AAAA,MAC9C,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAuD;AAChG,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,gBAAgB,EAAE,WAAW,GAAG,MAAM,GAAiC;AAC9E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,yDAAyD,SAAS;AAAA,MAC/E,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
|
|
@@ -100,7 +100,7 @@ function RichTextEditor({
|
|
|
100
100
|
},
|
|
101
101
|
editorProps: {
|
|
102
102
|
attributes: {
|
|
103
|
-
class: "prose prose-sm
|
|
103
|
+
class: "prose prose-sm mx-auto focus:outline-none max-w-none",
|
|
104
104
|
style: `min-height: ${height}px;`
|
|
105
105
|
}
|
|
106
106
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/ui/rich-text-editor/rich-text-editor.tsx"],"sourcesContent":["\"use client\";\n\nimport React from \"react\";\nimport { useEditor, EditorContent } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport TextAlign from \"@tiptap/extension-text-align\";\nimport Color from \"@tiptap/extension-color\";\nimport { TextStyle } from \"@tiptap/extension-text-style\";\nimport ImageExtension from \"@tiptap/extension-image\";\nimport LinkExtension from \"@tiptap/extension-link\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport {\n Bold,\n Italic,\n Strikethrough,\n List,\n ListOrdered,\n Link,\n AlignLeft,\n AlignCenter,\n AlignRight,\n Image as ImageIcon,\n Heading1,\n Heading2,\n Heading3,\n} from \"lucide-react\";\n\nimport { cn } from \"../../../lib/utils\";\n\ninterface RichTextEditorProps {\n value: string;\n onChange: (content: string) => void;\n placeholder?: string;\n height?: number;\n className?: string;\n readOnly?: boolean;\n}\n\nfunction ToolbarButton({\n onClick,\n children,\n title,\n isActive = false,\n}: {\n onClick: () => void;\n children: React.ReactNode;\n title: string;\n isActive?: boolean;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n title={title}\n className={cn(\n \"rounded p-2 transition-colors\",\n isActive ? \"bg-primary text-primary-foreground\" : \"text-muted-foreground hover:bg-muted\",\n )}\n >\n {children}\n </button>\n );\n}\n\nfunction RichTextEditor({\n value,\n onChange,\n placeholder = \"Escribe aquí...\",\n height = 200,\n className = \"\",\n readOnly = false,\n}: RichTextEditorProps) {\n const editor = useEditor({\n extensions: [\n StarterKit,\n TextAlign.configure({\n types: [\"heading\", \"paragraph\"],\n }),\n Color.configure({ types: [TextStyle.name] }),\n TextStyle,\n ImageExtension.configure({\n HTMLAttributes: {\n class: \"max-w-full h-auto rounded-lg\",\n },\n }),\n LinkExtension.configure({\n openOnClick: false,\n }),\n Placeholder.configure({\n placeholder: placeholder,\n }),\n ],\n content: value,\n editable: !readOnly,\n immediatelyRender: false,\n onUpdate: ({ editor: e }) => {\n onChange(e.getHTML());\n },\n editorProps: {\n attributes: {\n class:\n \"prose prose-sm sm:prose lg:prose-lg xl:prose-2xl mx-auto focus:outline-none max-w-none\",\n style: `min-height: ${height}px;`,\n },\n },\n });\n\n React.useEffect(() => {\n if (editor && editor.getHTML() !== value) {\n editor.commands.setContent(value);\n }\n }, [value, editor]);\n\n if (!editor) {\n return (\n <div className={cn(\"overflow-hidden rounded-lg border border-border\", className)}>\n <div className=\"flex h-32 animate-pulse items-center justify-center rounded bg-muted text-muted-foreground\">\n Cargando editor...\n </div>\n </div>\n );\n }\n\n return (\n <div\n data-slot=\"rich-text-editor\"\n className={cn(\"overflow-hidden rounded-lg border border-border\", className)}\n >\n {!readOnly && (\n <div className=\"flex flex-wrap gap-1 border-b border-border bg-muted/50 p-2\">\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}\n title=\"Título 1\"\n isActive={editor.isActive(\"heading\", { level: 1 })}\n >\n <Heading1 className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}\n title=\"Título 2\"\n isActive={editor.isActive(\"heading\", { level: 2 })}\n >\n <Heading2 className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}\n title=\"Título 3\"\n isActive={editor.isActive(\"heading\", { level: 3 })}\n >\n <Heading3 className=\"h-4 w-4\" />\n </ToolbarButton>\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleBold().run()}\n title=\"Negrita\"\n isActive={editor.isActive(\"bold\")}\n >\n <Bold className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleItalic().run()}\n title=\"Cursiva\"\n isActive={editor.isActive(\"italic\")}\n >\n <Italic className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleStrike().run()}\n title=\"Tachado\"\n isActive={editor.isActive(\"strike\")}\n >\n <Strikethrough className=\"h-4 w-4\" />\n </ToolbarButton>\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <div className=\"relative\">\n <input\n type=\"color\"\n className=\"h-8 w-8 cursor-pointer rounded border border-border\"\n onChange={(e) => {\n editor.chain().focus().setColor(e.target.value).run();\n }}\n title=\"Color del texto\"\n />\n </div>\n\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n title=\"Lista con viñetas\"\n isActive={editor.isActive(\"bulletList\")}\n >\n <List className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n title=\"Lista numerada\"\n isActive={editor.isActive(\"orderedList\")}\n >\n <ListOrdered className=\"h-4 w-4\" />\n </ToolbarButton>\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <ToolbarButton\n onClick={() => editor.chain().focus().setTextAlign(\"left\").run()}\n title=\"Alinear izquierda\"\n isActive={editor.isActive({ textAlign: \"left\" })}\n >\n <AlignLeft className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().setTextAlign(\"center\").run()}\n title=\"Centrar\"\n isActive={editor.isActive({ textAlign: \"center\" })}\n >\n <AlignCenter className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().setTextAlign(\"right\").run()}\n title=\"Alinear derecha\"\n isActive={editor.isActive({ textAlign: \"right\" })}\n >\n <AlignRight className=\"h-4 w-4\" />\n </ToolbarButton>\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <ToolbarButton\n onClick={() => {\n const url = prompt(\"Ingresa la URL:\");\n if (url) {\n editor.chain().focus().setLink({ href: url }).run();\n }\n }}\n title=\"Insertar enlace\"\n isActive={editor.isActive(\"link\")}\n >\n <Link className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => {\n const url = prompt(\"Ingresa la URL de la imagen:\");\n if (url) {\n editor.chain().focus().setImage({ src: url }).run();\n }\n }}\n title=\"Insertar imagen\"\n >\n <ImageIcon className=\"h-4 w-4\" />\n </ToolbarButton>\n </div>\n )}\n <EditorContent\n editor={editor}\n className=\"prose prose-sm max-w-none bg-card p-3\"\n style={{ minHeight: `${height}px` }}\n />\n </div>\n );\n}\n\nexport { RichTextEditor };\nexport type { RichTextEditorProps };\n"],"mappings":"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","names":["import_react","StarterKit","TextAlign","Color","ImageExtension","LinkExtension","Placeholder","React","ImageIcon"]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/ui/rich-text-editor/rich-text-editor.tsx"],"sourcesContent":["\"use client\";\n\nimport React from \"react\";\nimport { useEditor, EditorContent } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport TextAlign from \"@tiptap/extension-text-align\";\nimport Color from \"@tiptap/extension-color\";\nimport { TextStyle } from \"@tiptap/extension-text-style\";\nimport ImageExtension from \"@tiptap/extension-image\";\nimport LinkExtension from \"@tiptap/extension-link\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport {\n Bold,\n Italic,\n Strikethrough,\n List,\n ListOrdered,\n Link,\n AlignLeft,\n AlignCenter,\n AlignRight,\n Image as ImageIcon,\n Heading1,\n Heading2,\n Heading3,\n} from \"lucide-react\";\n\nimport { cn } from \"../../../lib/utils\";\n\ninterface RichTextEditorProps {\n value: string;\n onChange: (content: string) => void;\n placeholder?: string;\n height?: number;\n className?: string;\n readOnly?: boolean;\n}\n\nfunction ToolbarButton({\n onClick,\n children,\n title,\n isActive = false,\n}: {\n onClick: () => void;\n children: React.ReactNode;\n title: string;\n isActive?: boolean;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n title={title}\n className={cn(\n \"rounded p-2 transition-colors\",\n isActive ? \"bg-primary text-primary-foreground\" : \"text-muted-foreground hover:bg-muted\",\n )}\n >\n {children}\n </button>\n );\n}\n\nfunction RichTextEditor({\n value,\n onChange,\n placeholder = \"Escribe aquí...\",\n height = 200,\n className = \"\",\n readOnly = false,\n}: RichTextEditorProps) {\n const editor = useEditor({\n extensions: [\n StarterKit,\n TextAlign.configure({\n types: [\"heading\", \"paragraph\"],\n }),\n Color.configure({ types: [TextStyle.name] }),\n TextStyle,\n ImageExtension.configure({\n HTMLAttributes: {\n class: \"max-w-full h-auto rounded-lg\",\n },\n }),\n LinkExtension.configure({\n openOnClick: false,\n }),\n Placeholder.configure({\n placeholder: placeholder,\n }),\n ],\n content: value,\n editable: !readOnly,\n immediatelyRender: false,\n onUpdate: ({ editor: e }) => {\n onChange(e.getHTML());\n },\n editorProps: {\n attributes: {\n class:\n \"prose prose-sm mx-auto focus:outline-none max-w-none\",\n style: `min-height: ${height}px;`,\n },\n },\n });\n\n React.useEffect(() => {\n if (editor && editor.getHTML() !== value) {\n editor.commands.setContent(value);\n }\n }, [value, editor]);\n\n if (!editor) {\n return (\n <div className={cn(\"overflow-hidden rounded-lg border border-border\", className)}>\n <div className=\"flex h-32 animate-pulse items-center justify-center rounded bg-muted text-muted-foreground\">\n Cargando editor...\n </div>\n </div>\n );\n }\n\n return (\n <div\n data-slot=\"rich-text-editor\"\n className={cn(\"overflow-hidden rounded-lg border border-border\", className)}\n >\n {!readOnly && (\n <div className=\"flex flex-wrap gap-1 border-b border-border bg-muted/50 p-2\">\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}\n title=\"Título 1\"\n isActive={editor.isActive(\"heading\", { level: 1 })}\n >\n <Heading1 className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}\n title=\"Título 2\"\n isActive={editor.isActive(\"heading\", { level: 2 })}\n >\n <Heading2 className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}\n title=\"Título 3\"\n isActive={editor.isActive(\"heading\", { level: 3 })}\n >\n <Heading3 className=\"h-4 w-4\" />\n </ToolbarButton>\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleBold().run()}\n title=\"Negrita\"\n isActive={editor.isActive(\"bold\")}\n >\n <Bold className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleItalic().run()}\n title=\"Cursiva\"\n isActive={editor.isActive(\"italic\")}\n >\n <Italic className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleStrike().run()}\n title=\"Tachado\"\n isActive={editor.isActive(\"strike\")}\n >\n <Strikethrough className=\"h-4 w-4\" />\n </ToolbarButton>\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <div className=\"relative\">\n <input\n type=\"color\"\n className=\"h-8 w-8 cursor-pointer rounded border border-border\"\n onChange={(e) => {\n editor.chain().focus().setColor(e.target.value).run();\n }}\n title=\"Color del texto\"\n />\n </div>\n\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n title=\"Lista con viñetas\"\n isActive={editor.isActive(\"bulletList\")}\n >\n <List className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n title=\"Lista numerada\"\n isActive={editor.isActive(\"orderedList\")}\n >\n <ListOrdered className=\"h-4 w-4\" />\n </ToolbarButton>\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <ToolbarButton\n onClick={() => editor.chain().focus().setTextAlign(\"left\").run()}\n title=\"Alinear izquierda\"\n isActive={editor.isActive({ textAlign: \"left\" })}\n >\n <AlignLeft className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().setTextAlign(\"center\").run()}\n title=\"Centrar\"\n isActive={editor.isActive({ textAlign: \"center\" })}\n >\n <AlignCenter className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().setTextAlign(\"right\").run()}\n title=\"Alinear derecha\"\n isActive={editor.isActive({ textAlign: \"right\" })}\n >\n <AlignRight className=\"h-4 w-4\" />\n </ToolbarButton>\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <ToolbarButton\n onClick={() => {\n const url = prompt(\"Ingresa la URL:\");\n if (url) {\n editor.chain().focus().setLink({ href: url }).run();\n }\n }}\n title=\"Insertar enlace\"\n isActive={editor.isActive(\"link\")}\n >\n <Link className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => {\n const url = prompt(\"Ingresa la URL de la imagen:\");\n if (url) {\n editor.chain().focus().setImage({ src: url }).run();\n }\n }}\n title=\"Insertar imagen\"\n >\n <ImageIcon className=\"h-4 w-4\" />\n </ToolbarButton>\n </div>\n )}\n <EditorContent\n editor={editor}\n className=\"prose prose-sm max-w-none bg-card p-3\"\n style={{ minHeight: `${height}px` }}\n />\n </div>\n );\n}\n\nexport { RichTextEditor };\nexport type { RichTextEditorProps };\n"],"mappings":"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","names":["import_react","StarterKit","TextAlign","Color","ImageExtension","LinkExtension","Placeholder","React","ImageIcon"]}
|
|
@@ -81,7 +81,7 @@ function RichTextEditor({
|
|
|
81
81
|
},
|
|
82
82
|
editorProps: {
|
|
83
83
|
attributes: {
|
|
84
|
-
class: "prose prose-sm
|
|
84
|
+
class: "prose prose-sm mx-auto focus:outline-none max-w-none",
|
|
85
85
|
style: `min-height: ${height}px;`
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/ui/rich-text-editor/rich-text-editor.tsx"],"sourcesContent":["\"use client\";\n\nimport React from \"react\";\nimport { useEditor, EditorContent } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport TextAlign from \"@tiptap/extension-text-align\";\nimport Color from \"@tiptap/extension-color\";\nimport { TextStyle } from \"@tiptap/extension-text-style\";\nimport ImageExtension from \"@tiptap/extension-image\";\nimport LinkExtension from \"@tiptap/extension-link\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport {\n Bold,\n Italic,\n Strikethrough,\n List,\n ListOrdered,\n Link,\n AlignLeft,\n AlignCenter,\n AlignRight,\n Image as ImageIcon,\n Heading1,\n Heading2,\n Heading3,\n} from \"lucide-react\";\n\nimport { cn } from \"../../../lib/utils\";\n\ninterface RichTextEditorProps {\n value: string;\n onChange: (content: string) => void;\n placeholder?: string;\n height?: number;\n className?: string;\n readOnly?: boolean;\n}\n\nfunction ToolbarButton({\n onClick,\n children,\n title,\n isActive = false,\n}: {\n onClick: () => void;\n children: React.ReactNode;\n title: string;\n isActive?: boolean;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n title={title}\n className={cn(\n \"rounded p-2 transition-colors\",\n isActive ? \"bg-primary text-primary-foreground\" : \"text-muted-foreground hover:bg-muted\",\n )}\n >\n {children}\n </button>\n );\n}\n\nfunction RichTextEditor({\n value,\n onChange,\n placeholder = \"Escribe aquí...\",\n height = 200,\n className = \"\",\n readOnly = false,\n}: RichTextEditorProps) {\n const editor = useEditor({\n extensions: [\n StarterKit,\n TextAlign.configure({\n types: [\"heading\", \"paragraph\"],\n }),\n Color.configure({ types: [TextStyle.name] }),\n TextStyle,\n ImageExtension.configure({\n HTMLAttributes: {\n class: \"max-w-full h-auto rounded-lg\",\n },\n }),\n LinkExtension.configure({\n openOnClick: false,\n }),\n Placeholder.configure({\n placeholder: placeholder,\n }),\n ],\n content: value,\n editable: !readOnly,\n immediatelyRender: false,\n onUpdate: ({ editor: e }) => {\n onChange(e.getHTML());\n },\n editorProps: {\n attributes: {\n class:\n \"prose prose-sm sm:prose lg:prose-lg xl:prose-2xl mx-auto focus:outline-none max-w-none\",\n style: `min-height: ${height}px;`,\n },\n },\n });\n\n React.useEffect(() => {\n if (editor && editor.getHTML() !== value) {\n editor.commands.setContent(value);\n }\n }, [value, editor]);\n\n if (!editor) {\n return (\n <div className={cn(\"overflow-hidden rounded-lg border border-border\", className)}>\n <div className=\"flex h-32 animate-pulse items-center justify-center rounded bg-muted text-muted-foreground\">\n Cargando editor...\n </div>\n </div>\n );\n }\n\n return (\n <div\n data-slot=\"rich-text-editor\"\n className={cn(\"overflow-hidden rounded-lg border border-border\", className)}\n >\n {!readOnly && (\n <div className=\"flex flex-wrap gap-1 border-b border-border bg-muted/50 p-2\">\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}\n title=\"Título 1\"\n isActive={editor.isActive(\"heading\", { level: 1 })}\n >\n <Heading1 className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}\n title=\"Título 2\"\n isActive={editor.isActive(\"heading\", { level: 2 })}\n >\n <Heading2 className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}\n title=\"Título 3\"\n isActive={editor.isActive(\"heading\", { level: 3 })}\n >\n <Heading3 className=\"h-4 w-4\" />\n </ToolbarButton>\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleBold().run()}\n title=\"Negrita\"\n isActive={editor.isActive(\"bold\")}\n >\n <Bold className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleItalic().run()}\n title=\"Cursiva\"\n isActive={editor.isActive(\"italic\")}\n >\n <Italic className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleStrike().run()}\n title=\"Tachado\"\n isActive={editor.isActive(\"strike\")}\n >\n <Strikethrough className=\"h-4 w-4\" />\n </ToolbarButton>\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <div className=\"relative\">\n <input\n type=\"color\"\n className=\"h-8 w-8 cursor-pointer rounded border border-border\"\n onChange={(e) => {\n editor.chain().focus().setColor(e.target.value).run();\n }}\n title=\"Color del texto\"\n />\n </div>\n\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n title=\"Lista con viñetas\"\n isActive={editor.isActive(\"bulletList\")}\n >\n <List className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n title=\"Lista numerada\"\n isActive={editor.isActive(\"orderedList\")}\n >\n <ListOrdered className=\"h-4 w-4\" />\n </ToolbarButton>\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <ToolbarButton\n onClick={() => editor.chain().focus().setTextAlign(\"left\").run()}\n title=\"Alinear izquierda\"\n isActive={editor.isActive({ textAlign: \"left\" })}\n >\n <AlignLeft className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().setTextAlign(\"center\").run()}\n title=\"Centrar\"\n isActive={editor.isActive({ textAlign: \"center\" })}\n >\n <AlignCenter className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().setTextAlign(\"right\").run()}\n title=\"Alinear derecha\"\n isActive={editor.isActive({ textAlign: \"right\" })}\n >\n <AlignRight className=\"h-4 w-4\" />\n </ToolbarButton>\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <ToolbarButton\n onClick={() => {\n const url = prompt(\"Ingresa la URL:\");\n if (url) {\n editor.chain().focus().setLink({ href: url }).run();\n }\n }}\n title=\"Insertar enlace\"\n isActive={editor.isActive(\"link\")}\n >\n <Link className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => {\n const url = prompt(\"Ingresa la URL de la imagen:\");\n if (url) {\n editor.chain().focus().setImage({ src: url }).run();\n }\n }}\n title=\"Insertar imagen\"\n >\n <ImageIcon className=\"h-4 w-4\" />\n </ToolbarButton>\n </div>\n )}\n <EditorContent\n editor={editor}\n className=\"prose prose-sm max-w-none bg-card p-3\"\n style={{ minHeight: `${height}px` }}\n />\n </div>\n );\n}\n\nexport { RichTextEditor };\nexport type { RichTextEditorProps };\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/ui/rich-text-editor/rich-text-editor.tsx"],"sourcesContent":["\"use client\";\n\nimport React from \"react\";\nimport { useEditor, EditorContent } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport TextAlign from \"@tiptap/extension-text-align\";\nimport Color from \"@tiptap/extension-color\";\nimport { TextStyle } from \"@tiptap/extension-text-style\";\nimport ImageExtension from \"@tiptap/extension-image\";\nimport LinkExtension from \"@tiptap/extension-link\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport {\n Bold,\n Italic,\n Strikethrough,\n List,\n ListOrdered,\n Link,\n AlignLeft,\n AlignCenter,\n AlignRight,\n Image as ImageIcon,\n Heading1,\n Heading2,\n Heading3,\n} from \"lucide-react\";\n\nimport { cn } from \"../../../lib/utils\";\n\ninterface RichTextEditorProps {\n value: string;\n onChange: (content: string) => void;\n placeholder?: string;\n height?: number;\n className?: string;\n readOnly?: boolean;\n}\n\nfunction ToolbarButton({\n onClick,\n children,\n title,\n isActive = false,\n}: {\n onClick: () => void;\n children: React.ReactNode;\n title: string;\n isActive?: boolean;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n title={title}\n className={cn(\n \"rounded p-2 transition-colors\",\n isActive ? \"bg-primary text-primary-foreground\" : \"text-muted-foreground hover:bg-muted\",\n )}\n >\n {children}\n </button>\n );\n}\n\nfunction RichTextEditor({\n value,\n onChange,\n placeholder = \"Escribe aquí...\",\n height = 200,\n className = \"\",\n readOnly = false,\n}: RichTextEditorProps) {\n const editor = useEditor({\n extensions: [\n StarterKit,\n TextAlign.configure({\n types: [\"heading\", \"paragraph\"],\n }),\n Color.configure({ types: [TextStyle.name] }),\n TextStyle,\n ImageExtension.configure({\n HTMLAttributes: {\n class: \"max-w-full h-auto rounded-lg\",\n },\n }),\n LinkExtension.configure({\n openOnClick: false,\n }),\n Placeholder.configure({\n placeholder: placeholder,\n }),\n ],\n content: value,\n editable: !readOnly,\n immediatelyRender: false,\n onUpdate: ({ editor: e }) => {\n onChange(e.getHTML());\n },\n editorProps: {\n attributes: {\n class:\n \"prose prose-sm mx-auto focus:outline-none max-w-none\",\n style: `min-height: ${height}px;`,\n },\n },\n });\n\n React.useEffect(() => {\n if (editor && editor.getHTML() !== value) {\n editor.commands.setContent(value);\n }\n }, [value, editor]);\n\n if (!editor) {\n return (\n <div className={cn(\"overflow-hidden rounded-lg border border-border\", className)}>\n <div className=\"flex h-32 animate-pulse items-center justify-center rounded bg-muted text-muted-foreground\">\n Cargando editor...\n </div>\n </div>\n );\n }\n\n return (\n <div\n data-slot=\"rich-text-editor\"\n className={cn(\"overflow-hidden rounded-lg border border-border\", className)}\n >\n {!readOnly && (\n <div className=\"flex flex-wrap gap-1 border-b border-border bg-muted/50 p-2\">\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}\n title=\"Título 1\"\n isActive={editor.isActive(\"heading\", { level: 1 })}\n >\n <Heading1 className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}\n title=\"Título 2\"\n isActive={editor.isActive(\"heading\", { level: 2 })}\n >\n <Heading2 className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}\n title=\"Título 3\"\n isActive={editor.isActive(\"heading\", { level: 3 })}\n >\n <Heading3 className=\"h-4 w-4\" />\n </ToolbarButton>\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleBold().run()}\n title=\"Negrita\"\n isActive={editor.isActive(\"bold\")}\n >\n <Bold className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleItalic().run()}\n title=\"Cursiva\"\n isActive={editor.isActive(\"italic\")}\n >\n <Italic className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleStrike().run()}\n title=\"Tachado\"\n isActive={editor.isActive(\"strike\")}\n >\n <Strikethrough className=\"h-4 w-4\" />\n </ToolbarButton>\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <div className=\"relative\">\n <input\n type=\"color\"\n className=\"h-8 w-8 cursor-pointer rounded border border-border\"\n onChange={(e) => {\n editor.chain().focus().setColor(e.target.value).run();\n }}\n title=\"Color del texto\"\n />\n </div>\n\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n title=\"Lista con viñetas\"\n isActive={editor.isActive(\"bulletList\")}\n >\n <List className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n title=\"Lista numerada\"\n isActive={editor.isActive(\"orderedList\")}\n >\n <ListOrdered className=\"h-4 w-4\" />\n </ToolbarButton>\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <ToolbarButton\n onClick={() => editor.chain().focus().setTextAlign(\"left\").run()}\n title=\"Alinear izquierda\"\n isActive={editor.isActive({ textAlign: \"left\" })}\n >\n <AlignLeft className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().setTextAlign(\"center\").run()}\n title=\"Centrar\"\n isActive={editor.isActive({ textAlign: \"center\" })}\n >\n <AlignCenter className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().setTextAlign(\"right\").run()}\n title=\"Alinear derecha\"\n isActive={editor.isActive({ textAlign: \"right\" })}\n >\n <AlignRight className=\"h-4 w-4\" />\n </ToolbarButton>\n <div className=\"mx-1 h-6 w-px bg-border\" />\n\n <ToolbarButton\n onClick={() => {\n const url = prompt(\"Ingresa la URL:\");\n if (url) {\n editor.chain().focus().setLink({ href: url }).run();\n }\n }}\n title=\"Insertar enlace\"\n isActive={editor.isActive(\"link\")}\n >\n <Link className=\"h-4 w-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => {\n const url = prompt(\"Ingresa la URL de la imagen:\");\n if (url) {\n editor.chain().focus().setImage({ src: url }).run();\n }\n }}\n title=\"Insertar imagen\"\n >\n <ImageIcon className=\"h-4 w-4\" />\n </ToolbarButton>\n </div>\n )}\n <EditorContent\n editor={editor}\n className=\"prose prose-sm max-w-none bg-card p-3\"\n style={{ minHeight: `${height}px` }}\n />\n </div>\n );\n}\n\nexport { RichTextEditor };\nexport type { RichTextEditorProps };\n"],"mappings":"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","names":[]}
|