nextworks 0.2.0-alpha.2 → 0.2.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/README.md +280 -228
  2. package/dist/cli_manifests/blocks_manifest.json +193 -194
  3. package/dist/commands/blocks.d.ts +3 -0
  4. package/dist/commands/blocks.d.ts.map +1 -1
  5. package/dist/commands/blocks.js +128 -19
  6. package/dist/commands/blocks.js.map +1 -1
  7. package/dist/commands/doctor.d.ts +136 -0
  8. package/dist/commands/doctor.d.ts.map +1 -0
  9. package/dist/commands/doctor.js +696 -0
  10. package/dist/commands/doctor.js.map +1 -0
  11. package/dist/commands/remove-blocks.d.ts +4 -1
  12. package/dist/commands/remove-blocks.d.ts.map +1 -1
  13. package/dist/commands/remove-blocks.js +24 -6
  14. package/dist/commands/remove-blocks.js.map +1 -1
  15. package/dist/index.js +125 -4
  16. package/dist/index.js.map +1 -1
  17. package/dist/kits/blocks/.nextworks/docs/BLOCKS_QUICKSTART.md +104 -194
  18. package/dist/kits/blocks/.nextworks/docs/BLOCKS_README.md +115 -75
  19. package/dist/kits/blocks/.nextworks/docs/THEME_GUIDE.md +269 -251
  20. package/dist/kits/blocks/app/templates/digitalagency/PresetThemeVars.tsx +80 -80
  21. package/dist/kits/blocks/app/templates/digitalagency/README.md +42 -36
  22. package/dist/kits/blocks/app/templates/digitalagency/components/About.tsx +71 -99
  23. package/dist/kits/blocks/app/templates/digitalagency/components/CTA.tsx +40 -74
  24. package/dist/kits/blocks/app/templates/digitalagency/components/Contact.tsx +123 -227
  25. package/dist/kits/blocks/app/templates/digitalagency/components/Footer.tsx +89 -89
  26. package/dist/kits/blocks/app/templates/digitalagency/components/Hero.tsx +83 -90
  27. package/dist/kits/blocks/app/templates/digitalagency/components/Navbar.tsx +121 -168
  28. package/dist/kits/blocks/app/templates/digitalagency/components/NetworkPattern.tsx +288 -297
  29. package/dist/kits/blocks/app/templates/digitalagency/components/Portfolio.tsx +157 -157
  30. package/dist/kits/blocks/app/templates/digitalagency/components/Pricing.tsx +114 -114
  31. package/dist/kits/blocks/app/templates/digitalagency/components/Process.tsx +59 -59
  32. package/dist/kits/blocks/app/templates/digitalagency/components/Services.tsx +55 -55
  33. package/dist/kits/blocks/app/templates/digitalagency/components/Team.tsx +28 -28
  34. package/dist/kits/blocks/app/templates/digitalagency/components/Testimonials.tsx +65 -65
  35. package/dist/kits/blocks/app/templates/digitalagency/page.tsx +38 -38
  36. package/dist/kits/blocks/app/templates/gallery/PresetThemeVars.tsx +130 -85
  37. package/dist/kits/blocks/app/templates/gallery/page.tsx +357 -303
  38. package/dist/kits/blocks/app/templates/productlaunch/PresetThemeVars.tsx +75 -74
  39. package/dist/kits/blocks/app/templates/productlaunch/README.md +62 -55
  40. package/dist/kits/blocks/app/templates/productlaunch/components/About.tsx +84 -178
  41. package/dist/kits/blocks/app/templates/productlaunch/components/CTA.tsx +50 -93
  42. package/dist/kits/blocks/app/templates/productlaunch/components/Contact.tsx +231 -231
  43. package/dist/kits/blocks/app/templates/productlaunch/components/FAQ.tsx +86 -93
  44. package/dist/kits/blocks/app/templates/productlaunch/components/Features.tsx +83 -84
  45. package/dist/kits/blocks/app/templates/productlaunch/components/Footer.tsx +132 -132
  46. package/dist/kits/blocks/app/templates/productlaunch/components/Hero.tsx +88 -89
  47. package/dist/kits/blocks/app/templates/productlaunch/components/Navbar.tsx +116 -162
  48. package/dist/kits/blocks/app/templates/productlaunch/components/Pricing.tsx +106 -106
  49. package/dist/kits/blocks/app/templates/productlaunch/components/ProcessTimeline.tsx +110 -110
  50. package/dist/kits/blocks/app/templates/productlaunch/components/ServicesGrid.tsx +68 -68
  51. package/dist/kits/blocks/app/templates/productlaunch/components/Team.tsx +104 -104
  52. package/dist/kits/blocks/app/templates/productlaunch/components/Testimonials.tsx +90 -89
  53. package/dist/kits/blocks/app/templates/productlaunch/components/TrustBadges.tsx +76 -76
  54. package/dist/kits/blocks/app/templates/productlaunch/page.tsx +43 -43
  55. package/dist/kits/blocks/app/templates/saasdashboard/PresetThemeVars.tsx +80 -80
  56. package/dist/kits/blocks/app/templates/saasdashboard/README.md +44 -38
  57. package/dist/kits/blocks/app/templates/saasdashboard/components/Contact.tsx +129 -176
  58. package/dist/kits/blocks/app/templates/saasdashboard/components/Dashboard.tsx +293 -293
  59. package/dist/kits/blocks/app/templates/saasdashboard/components/FAQ.tsx +55 -55
  60. package/dist/kits/blocks/app/templates/saasdashboard/components/Features.tsx +90 -91
  61. package/dist/kits/blocks/app/templates/saasdashboard/components/Footer.tsx +77 -77
  62. package/dist/kits/blocks/app/templates/saasdashboard/components/Hero.tsx +104 -105
  63. package/dist/kits/blocks/app/templates/saasdashboard/components/Hero_mask.tsx +126 -127
  64. package/dist/kits/blocks/app/templates/saasdashboard/components/Navbar.tsx +117 -159
  65. package/dist/kits/blocks/app/templates/saasdashboard/components/Pricing.tsx +90 -90
  66. package/dist/kits/blocks/app/templates/saasdashboard/components/SmoothScroll.tsx +96 -97
  67. package/dist/kits/blocks/app/templates/saasdashboard/components/Testimonials.tsx +72 -72
  68. package/dist/kits/blocks/app/templates/saasdashboard/components/TrustBadges.tsx +53 -53
  69. package/dist/kits/blocks/app/templates/saasdashboard/page.tsx +39 -39
  70. package/dist/kits/blocks/components/enhanced-theme-provider.tsx +195 -183
  71. package/dist/kits/blocks/components/providers/BlocksAppProviders.tsx +27 -27
  72. package/dist/kits/blocks/components/sections/About.tsx +291 -291
  73. package/dist/kits/blocks/components/sections/CTA.tsx +257 -258
  74. package/dist/kits/blocks/components/sections/CommandShowcase.tsx +517 -0
  75. package/dist/kits/blocks/components/sections/Contact.tsx +267 -267
  76. package/dist/kits/blocks/components/sections/FAQ.tsx +214 -225
  77. package/dist/kits/blocks/components/sections/FeaturedProjectShowcase.tsx +687 -0
  78. package/dist/kits/blocks/components/sections/Features.tsx +268 -270
  79. package/dist/kits/blocks/components/sections/Footer.tsx +302 -302
  80. package/dist/kits/blocks/components/sections/HeroMotion.tsx +308 -308
  81. package/dist/kits/blocks/components/sections/HeroOverlay.tsx +358 -358
  82. package/dist/kits/blocks/components/sections/HeroProductDemo.tsx +246 -0
  83. package/dist/kits/blocks/components/sections/HeroSplit.tsx +352 -352
  84. package/dist/kits/blocks/components/sections/HeroWithVideo.tsx +495 -0
  85. package/dist/kits/blocks/components/sections/Navbar.tsx +352 -353
  86. package/dist/kits/blocks/components/sections/PortfolioSimple.tsx +549 -550
  87. package/dist/kits/blocks/components/sections/Pricing.tsx +264 -264
  88. package/dist/kits/blocks/components/sections/ProcessTimeline.tsx +325 -325
  89. package/dist/kits/blocks/components/sections/ProjectDeepDive.tsx +805 -0
  90. package/dist/kits/blocks/components/sections/SelectedWorkRail.tsx +485 -0
  91. package/dist/kits/blocks/components/sections/ServicesGrid.tsx +210 -210
  92. package/dist/kits/blocks/components/sections/Team.tsx +309 -309
  93. package/dist/kits/blocks/components/sections/Testimonials.tsx +158 -158
  94. package/dist/kits/blocks/components/sections/TrustBadges.tsx +162 -162
  95. package/dist/kits/blocks/components/sections/product-demo/ApprovalInboxPanel.tsx +128 -0
  96. package/dist/kits/blocks/components/sections/product-demo/DemoStage.tsx +519 -0
  97. package/dist/kits/blocks/components/sections/product-demo/DemoWindow.tsx +140 -0
  98. package/dist/kits/blocks/components/sections/product-demo/KnowledgePanel.tsx +130 -0
  99. package/dist/kits/blocks/components/sections/product-demo/RunConsolePanel.tsx +430 -0
  100. package/dist/kits/blocks/components/sections/product-demo/TaskListPanel.tsx +302 -0
  101. package/dist/kits/blocks/components/sections/product-demo/WorkflowStudioPanel.tsx +744 -0
  102. package/dist/kits/blocks/components/sections/product-demo/types.ts +262 -0
  103. package/dist/kits/blocks/components/theme-provider.tsx +1 -34
  104. package/dist/kits/blocks/components/ui/alert-dialog.tsx +134 -134
  105. package/dist/kits/blocks/components/ui/brand-node.tsx +121 -121
  106. package/dist/kits/blocks/components/ui/button.tsx +122 -122
  107. package/dist/kits/blocks/components/ui/card.tsx +95 -95
  108. package/dist/kits/blocks/components/ui/checkbox.tsx +30 -30
  109. package/dist/kits/blocks/components/ui/cta-button.tsx +125 -125
  110. package/dist/kits/blocks/components/ui/dropdown-menu.tsx +201 -201
  111. package/dist/kits/blocks/components/ui/feature-card.tsx +91 -91
  112. package/dist/kits/blocks/components/ui/input.tsx +27 -27
  113. package/dist/kits/blocks/components/ui/label.tsx +29 -29
  114. package/dist/kits/blocks/components/ui/pricing-card.tsx +120 -120
  115. package/dist/kits/blocks/components/ui/select.tsx +25 -25
  116. package/dist/kits/blocks/components/ui/skeleton.tsx +13 -13
  117. package/dist/kits/blocks/components/ui/table.tsx +98 -98
  118. package/dist/kits/blocks/components/ui/testimonial-card.tsx +108 -108
  119. package/dist/kits/blocks/components/ui/textarea.tsx +26 -26
  120. package/dist/kits/blocks/components/ui/theme-selector.tsx +243 -247
  121. package/dist/kits/blocks/components/ui/theme-toggle.tsx +74 -74
  122. package/dist/kits/blocks/components/ui/toaster.tsx +7 -7
  123. package/dist/kits/blocks/lib/themes.ts +400 -400
  124. package/dist/kits/blocks/lib/utils.ts +6 -9
  125. package/dist/kits/blocks/package-deps.json +40 -28
  126. package/dist/kits/blocks/tsconfig.json +11 -0
  127. package/dist/kits/blocks/tsconfig.tsbuildinfo +1 -0
  128. package/dist/utils/file-operations.d.ts +3 -1
  129. package/dist/utils/file-operations.d.ts.map +1 -1
  130. package/dist/utils/file-operations.js +84 -12
  131. package/dist/utils/file-operations.js.map +1 -1
  132. package/dist/utils/installation-tracker.d.ts +2 -2
  133. package/dist/utils/installation-tracker.d.ts.map +1 -1
  134. package/dist/utils/installation-tracker.js +11 -11
  135. package/dist/utils/installation-tracker.js.map +1 -1
  136. package/dist/utils/package-manager.d.ts +6 -0
  137. package/dist/utils/package-manager.d.ts.map +1 -0
  138. package/dist/utils/package-manager.js +58 -0
  139. package/dist/utils/package-manager.js.map +1 -0
  140. package/dist/utils/yarn-pnp.d.ts +6 -0
  141. package/dist/utils/yarn-pnp.d.ts.map +1 -0
  142. package/dist/utils/yarn-pnp.js +39 -0
  143. package/dist/utils/yarn-pnp.js.map +1 -0
  144. package/package.json +5 -2
  145. package/dist/kits/blocks/notes/THEME_GUIDE.md +0 -29
@@ -1,225 +1,214 @@
1
- "use client";
2
-
3
- import React, { useMemo, useState } from "react";
4
- import { cn } from "@/lib/utils";
5
- import { ChevronDown, ChevronUp } from "lucide-react";
6
-
7
- /**
8
- * Represents a single FAQ item.
9
- * @public
10
- */
11
- export interface FAQS {
12
- /** The question text */
13
- question?: string;
14
- /** The answer text */
15
- answer?: string;
16
- }
17
-
18
- /**
19
- * Props for the FAQ section component.
20
- *
21
- * @remarks
22
- * - Styling: exposes slot-style className overrides; consumer classes are
23
- * merged after defaults via cn().
24
- * - Behavior: supports single or multiple open items; default open indices
25
- * are respected. Controlled internally with a Set.
26
- * - Motion: when enableMotion is false, collapse/expand transitions are
27
- * removed for a hard-cut.
28
- * - Accessibility: each item uses button+region with aria-controls and
29
- * aria-expanded. Section uses aria-label.
30
- */
31
- export interface FAQProps {
32
- /** Header text displayed at the top of the FAQ section. @defaultValue "Frequently Asked Questions" */
33
- faqSectionHeaderText?: string;
34
- /** Array of question/answer items. @defaultValue defaultFaqData */
35
- faqData?: FAQS[];
36
- /** Optional id for the section root. @defaultValue "faq" */
37
- sectionId?: string;
38
- /** Optional top-level class override */
39
- className?: string;
40
- /** When false, only one item can be open at a time. @defaultValue true */
41
- allowMultipleOpen?: boolean;
42
- /** Indices of items that should be open by default. @defaultValue [] */
43
- defaultOpenIndices?: number[];
44
- /** Optional icons for open/closed states */
45
- openIcon?: React.ReactNode;
46
- closedIcon?: React.ReactNode;
47
- /** ARIA label for the FAQ section. @defaultValue "Frequently asked questions section" */
48
- ariaLabel?: string;
49
- /** Slot-style overrides */
50
- section?: { className?: string };
51
- container?: { className?: string };
52
- heading?: { className?: string };
53
- grid?: { className?: string };
54
- item?: { className?: string };
55
- questionButton?: { className?: string };
56
- questionText?: { className?: string };
57
- chevronIcon?: { className?: string };
58
- answer?: { className?: string };
59
- answerText?: { className?: string };
60
- /** When false, removes transition on collapse/expand for a hard-cut */
61
- enableMotion?: boolean;
62
- }
63
-
64
- // const defaultFaqData: FAQS[] = [
65
- // {
66
- // question: "What does IntelliOpAI do?",
67
- // answer: "Automates ops with real-time insights.",
68
- // },
69
- // {
70
- // question: "Do I need coding skills?",
71
- // answer: "No, it's designed for ease of use.",
72
- // },
73
- // ];
74
-
75
- const defaultFaqData: FAQS[] = [
76
- {
77
- question: "What does IntelliOpAI do?",
78
- answer: "Automates ops with real-time insights.",
79
- },
80
- {
81
- question: "How secure is my data?",
82
- answer: "We use industry-standard encryption and strict access controls.",
83
- },
84
- {
85
- question: "Can I integrate with other tools?",
86
- answer:
87
- "Yes — supports APIs and popular integrations like Slack, GitHub, and Jira.",
88
- },
89
- {
90
- question: "How do I get support?",
91
- answer:
92
- "Contact our support team via in-app chat or the help center for assistance.",
93
- },
94
- ];
95
-
96
- /**
97
- * Expandable FAQ section with accessible toggles and optional icons.
98
- *
99
- * @remarks
100
- * - Supports multiple or single open item behavior via allowMultipleOpen.
101
- * - Uses button with aria-expanded and a region with aria-labelledby.
102
- * - Motion can be disabled via enableMotion for reduced animation.
103
- *
104
- * @example
105
- * <FAQ faqData={[{ question: 'What is X?', answer: 'Y' }]} />
106
- */
107
- export function FAQ({
108
- faqSectionHeaderText = "Frequently Asked Questions",
109
- faqData = defaultFaqData,
110
- sectionId = "faq",
111
- className,
112
- allowMultipleOpen = true,
113
- defaultOpenIndices = [],
114
- openIcon,
115
- closedIcon,
116
- ariaLabel = "Frequently asked questions section",
117
- section = { className: "py-10 px-5 bg-muted text-foreground" },
118
- container = { className: "mx-auto max-w-7xl" },
119
- heading = {
120
- className: "mb-6 text-center text-2xl font-bold text-foreground",
121
- },
122
- grid = { className: "grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6" },
123
- item = { className: "mb-4" },
124
- questionButton = {
125
- className:
126
- "shadow-md hover:shadow-lg border-[var(--btn-border)] focus-visible:ring-[var(--btn-ring)] bg-[var(--faq-btn-bg,var(--btn-bg))] text-[var(--faq-btn-fg,var(--btn-fg))] hover:bg-[var(--faq-btn-hover-bg,var(--btn-hover-bg))] hover:text-[var(--faq-btn-hover-fg,var(--btn-hover-fg))]",
127
- },
128
- questionText = { className: "" },
129
- chevronIcon = { className: "transition-transform duration-200" },
130
- answer = {
131
- className:
132
- "bg-[var(--faq-answer-bg,var(--card-bg))] text-[var(--faq-answer-fg,var(--card-fg))] border-[var(--card-border)]",
133
- },
134
- answerText = { className: "leading-relaxed" },
135
- enableMotion = true,
136
- }: FAQProps) {
137
- const [openSet, setOpenSet] = useState<Set<number>>(
138
- () => new Set(defaultOpenIndices),
139
- );
140
- const idPrefix = useMemo(() => sectionId || "faq", [sectionId]);
141
-
142
- const onToggle = (idx: number) => {
143
- setOpenSet((prev) => {
144
- const next = new Set(prev);
145
- if (next.has(idx)) next.delete(idx);
146
- else {
147
- if (!allowMultipleOpen) next.clear();
148
- next.add(idx);
149
- }
150
- return next;
151
- });
152
- };
153
-
154
- return (
155
- <section
156
- id={sectionId}
157
- className={cn(section.className, className)}
158
- aria-label={ariaLabel}
159
- >
160
- <div className={cn(container.className)}>
161
- {faqSectionHeaderText && (
162
- <h2 className={cn(heading.className)}>{faqSectionHeaderText}</h2>
163
- )}
164
- <div className={cn(grid.className)}>
165
- {faqData.map((faq, index) => {
166
- const transitionCls = enableMotion
167
- ? "transition-all duration-300 ease-in-out"
168
- : "transition-none";
169
- const isOpen = openSet.has(index);
170
- return (
171
- <div
172
- key={`${idPrefix}-item-${index}`}
173
- className={cn("w-full", item.className)}
174
- >
175
- <button
176
- id={`${idPrefix}-trigger-${index}`}
177
- aria-controls={`${idPrefix}-panel-${index}`}
178
- aria-expanded={isOpen}
179
- onClick={() => onToggle(index)}
180
- className={cn(
181
- "flex w-full items-center justify-between rounded-md p-4 text-left focus:outline-none",
182
- // preset-first ring/border
183
- "border-[var(--btn-border)] focus-visible:ring-2 focus-visible:ring-[var(--btn-ring)] focus-visible:ring-offset-2",
184
- questionButton.className,
185
- )}
186
- >
187
- <span
188
- className={cn(
189
- "flex-1 font-medium select-none",
190
- questionText.className,
191
- )}
192
- >
193
- {faq.question}
194
- </span>
195
- <span
196
- className={cn("ml-3 flex-shrink-0", chevronIcon.className)}
197
- >
198
- {isOpen
199
- ? (openIcon ?? <ChevronUp className="h-5 w-5" />)
200
- : (closedIcon ?? <ChevronDown className="h-5 w-5" />)}
201
- </span>
202
- </button>
203
- <div
204
- id={`${idPrefix}-panel-${index}`}
205
- role="region"
206
- aria-labelledby={`${idPrefix}-trigger-${index}`}
207
- className={cn(
208
- "border-border overflow-hidden rounded-md border",
209
- isOpen ? "max-h-96 opacity-100" : "max-h-0 opacity-0",
210
- transitionCls,
211
- answer.className,
212
- )}
213
- >
214
- <div className={cn("p-4", answerText.className)}>
215
- {faq.answer}
216
- </div>
217
- </div>
218
- </div>
219
- );
220
- })}
221
- </div>
222
- </div>
223
- </section>
224
- );
225
- }
1
+ "use client";
2
+
3
+ import React, { useMemo, useState } from "react";
4
+ import { cn } from "@/lib/utils";
5
+ import { ChevronDown, ChevronUp } from "lucide-react";
6
+
7
+ /**
8
+ * Represents a single FAQ item.
9
+ * @public
10
+ */
11
+ export interface FAQS {
12
+ /** The question text */
13
+ question?: string;
14
+ /** The answer text */
15
+ answer?: string;
16
+ }
17
+
18
+ /**
19
+ * Props for the FAQ section component.
20
+ *
21
+ * @remarks
22
+ * - Styling: exposes slot-style className overrides; consumer classes are
23
+ * merged after defaults via cn().
24
+ * - Behavior: supports single or multiple open items; default open indices
25
+ * are respected. Controlled internally with a Set.
26
+ * - Motion: when enableMotion is false, collapse/expand transitions are
27
+ * removed for a hard-cut.
28
+ * - Accessibility: each item uses button+region with aria-controls and
29
+ * aria-expanded. Section uses aria-label.
30
+ */
31
+ export interface FAQProps {
32
+ /** Header text displayed at the top of the FAQ section. @defaultValue "Frequently Asked Questions" */
33
+ faqSectionHeaderText?: string;
34
+ /** Array of question/answer items. @defaultValue defaultFaqData */
35
+ faqData?: FAQS[];
36
+ /** Optional id for the section root. @defaultValue "faq" */
37
+ sectionId?: string;
38
+ /** Optional top-level class override */
39
+ className?: string;
40
+ /** When false, only one item can be open at a time. @defaultValue true */
41
+ allowMultipleOpen?: boolean;
42
+ /** Indices of items that should be open by default. @defaultValue [] */
43
+ defaultOpenIndices?: number[];
44
+ /** Optional icons for open/closed states */
45
+ openIcon?: React.ReactNode;
46
+ closedIcon?: React.ReactNode;
47
+ /** ARIA label for the FAQ section. @defaultValue "Frequently asked questions section" */
48
+ ariaLabel?: string;
49
+ /** Slot-style overrides */
50
+ section?: { className?: string };
51
+ container?: { className?: string };
52
+ heading?: { className?: string };
53
+ grid?: { className?: string };
54
+ item?: { className?: string };
55
+ questionButton?: { className?: string };
56
+ questionText?: { className?: string };
57
+ chevronIcon?: { className?: string };
58
+ answer?: { className?: string };
59
+ answerText?: { className?: string };
60
+ /** When false, removes transition on collapse/expand for a hard-cut */
61
+ enableMotion?: boolean;
62
+ }
63
+
64
+ const defaultFaqData: FAQS[] = [
65
+ {
66
+ question: "What does IntelliOpAI do?",
67
+ answer: "Automates ops with real-time insights.",
68
+ },
69
+ {
70
+ question: "How secure is my data?",
71
+ answer: "We use industry-standard encryption and strict access controls.",
72
+ },
73
+ {
74
+ question: "Can I integrate with other tools?",
75
+ answer:
76
+ "Yes — supports APIs and popular integrations like Slack, GitHub, and Jira.",
77
+ },
78
+ {
79
+ question: "How do I get support?",
80
+ answer:
81
+ "Contact our support team via in-app chat or the help center for assistance.",
82
+ },
83
+ ];
84
+
85
+ /**
86
+ * Expandable FAQ section with accessible toggles and optional icons.
87
+ *
88
+ * @remarks
89
+ * - Supports multiple or single open item behavior via allowMultipleOpen.
90
+ * - Uses button with aria-expanded and a region with aria-labelledby.
91
+ * - Motion can be disabled via enableMotion for reduced animation.
92
+ *
93
+ * @example
94
+ * <FAQ faqData={[{ question: 'What is X?', answer: 'Y' }]} />
95
+ */
96
+ export function FAQ({
97
+ faqSectionHeaderText = "Frequently Asked Questions",
98
+ faqData = defaultFaqData,
99
+ sectionId = "faq",
100
+ className,
101
+ allowMultipleOpen = true,
102
+ defaultOpenIndices = [],
103
+ openIcon,
104
+ closedIcon,
105
+ ariaLabel = "Frequently asked questions section",
106
+ section = { className: "py-10 px-5 bg-muted text-foreground" },
107
+ container = { className: "mx-auto max-w-7xl" },
108
+ heading = {
109
+ className: "mb-6 text-center text-2xl font-bold text-foreground",
110
+ },
111
+ grid = { className: "grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6" },
112
+ item = { className: "mb-4" },
113
+ questionButton = {
114
+ className:
115
+ "shadow-md hover:shadow-lg border-[var(--btn-border)] focus-visible:ring-[var(--btn-ring)] bg-[var(--faq-btn-bg,var(--btn-bg))] text-[var(--faq-btn-fg,var(--btn-fg))] hover:bg-[var(--faq-btn-hover-bg,var(--btn-hover-bg))] hover:text-[var(--faq-btn-hover-fg,var(--btn-hover-fg))]",
116
+ },
117
+ questionText = { className: "" },
118
+ chevronIcon = { className: "transition-transform duration-200" },
119
+ answer = {
120
+ className:
121
+ "bg-[var(--faq-answer-bg,var(--card-bg))] text-[var(--faq-answer-fg,var(--card-fg))] border-[var(--card-border)]",
122
+ },
123
+ answerText = { className: "leading-relaxed" },
124
+ enableMotion = true,
125
+ }: FAQProps) {
126
+ const [openSet, setOpenSet] = useState<Set<number>>(
127
+ () => new Set(defaultOpenIndices),
128
+ );
129
+ const idPrefix = useMemo(() => sectionId || "faq", [sectionId]);
130
+
131
+ const onToggle = (idx: number) => {
132
+ setOpenSet((prev) => {
133
+ const next = new Set(prev);
134
+ if (next.has(idx)) next.delete(idx);
135
+ else {
136
+ if (!allowMultipleOpen) next.clear();
137
+ next.add(idx);
138
+ }
139
+ return next;
140
+ });
141
+ };
142
+
143
+ return (
144
+ <section
145
+ id={sectionId}
146
+ className={cn(section.className, className)}
147
+ aria-label={ariaLabel}
148
+ >
149
+ <div className={cn(container.className)}>
150
+ {faqSectionHeaderText && (
151
+ <h2 className={cn(heading.className)}>{faqSectionHeaderText}</h2>
152
+ )}
153
+ <div className={cn(grid.className)}>
154
+ {faqData.map((faq, index) => {
155
+ const transitionCls = enableMotion
156
+ ? "transition-all duration-300 ease-in-out"
157
+ : "transition-none";
158
+ const isOpen = openSet.has(index);
159
+ return (
160
+ <div
161
+ key={`${idPrefix}-item-${index}`}
162
+ className={cn("w-full", item.className)}
163
+ >
164
+ <button
165
+ id={`${idPrefix}-trigger-${index}`}
166
+ aria-controls={`${idPrefix}-panel-${index}`}
167
+ aria-expanded={isOpen}
168
+ onClick={() => onToggle(index)}
169
+ className={cn(
170
+ "flex w-full items-center justify-between rounded-md p-4 text-left focus:outline-none",
171
+ // preset-first ring/border
172
+ "border-[var(--btn-border)] focus-visible:ring-2 focus-visible:ring-[var(--btn-ring)] focus-visible:ring-offset-2",
173
+ questionButton.className,
174
+ )}
175
+ >
176
+ <span
177
+ className={cn(
178
+ "flex-1 font-medium select-none",
179
+ questionText.className,
180
+ )}
181
+ >
182
+ {faq.question}
183
+ </span>
184
+ <span
185
+ className={cn("ml-3 flex-shrink-0", chevronIcon.className)}
186
+ >
187
+ {isOpen
188
+ ? (openIcon ?? <ChevronUp className="h-5 w-5" />)
189
+ : (closedIcon ?? <ChevronDown className="h-5 w-5" />)}
190
+ </span>
191
+ </button>
192
+ <div
193
+ id={`${idPrefix}-panel-${index}`}
194
+ role="region"
195
+ aria-labelledby={`${idPrefix}-trigger-${index}`}
196
+ className={cn(
197
+ "border-border overflow-hidden rounded-md border",
198
+ isOpen ? "max-h-96 opacity-100" : "max-h-0 opacity-0",
199
+ transitionCls,
200
+ answer.className,
201
+ )}
202
+ >
203
+ <div className={cn("p-4", answerText.className)}>
204
+ {faq.answer}
205
+ </div>
206
+ </div>
207
+ </div>
208
+ );
209
+ })}
210
+ </div>
211
+ </div>
212
+ </section>
213
+ );
214
+ }