nextworks 0.2.0-alpha.11 → 0.2.0-alpha.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/README.md +283 -282
- package/dist/cli_manifests/blocks_manifest.json +198 -175
- package/dist/kits/blocks/.nextworks/docs/BLOCKS_QUICKSTART.md +101 -100
- package/dist/kits/blocks/.nextworks/docs/BLOCKS_README.md +105 -104
- package/dist/kits/blocks/.nextworks/docs/THEME_GUIDE.md +1 -1
- package/dist/kits/blocks/app/templates/aiworkflow/PresetThemeVars.tsx +58 -0
- package/dist/kits/blocks/app/templates/aiworkflow/README.md +46 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/CTA.tsx +44 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Contact.tsx +105 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/FAQ.tsx +63 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Features.tsx +65 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Footer.tsx +109 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Hero.tsx +636 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Navbar.tsx +90 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Pricing.tsx +86 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/ProcessTimeline.tsx +103 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Testimonials.tsx +56 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/TrustBadges.tsx +59 -0
- package/dist/kits/blocks/app/templates/aiworkflow/page.tsx +43 -0
- package/dist/kits/blocks/app/templates/digitalagency/PresetThemeVars.tsx +80 -80
- package/dist/kits/blocks/app/templates/digitalagency/README.md +42 -42
- package/dist/kits/blocks/app/templates/digitalagency/components/Pricing.tsx +114 -114
- package/dist/kits/blocks/app/templates/digitalagency/components/Process.tsx +59 -59
- package/dist/kits/blocks/app/templates/digitalagency/components/Services.tsx +55 -55
- package/dist/kits/blocks/app/templates/digitalagency/components/Team.tsx +28 -28
- package/dist/kits/blocks/app/templates/digitalagency/components/Testimonials.tsx +65 -65
- package/dist/kits/blocks/app/templates/digitalagency/page.tsx +38 -38
- package/dist/kits/blocks/app/templates/gallery/PresetThemeVars.tsx +84 -84
- package/dist/kits/blocks/app/templates/productlaunch/PresetThemeVars.tsx +75 -75
- package/dist/kits/blocks/app/templates/productlaunch/README.md +62 -62
- package/dist/kits/blocks/app/templates/productlaunch/components/About.tsx +84 -84
- package/dist/kits/blocks/app/templates/productlaunch/components/CTA.tsx +50 -50
- package/dist/kits/blocks/app/templates/productlaunch/components/Contact.tsx +231 -231
- package/dist/kits/blocks/app/templates/productlaunch/components/FAQ.tsx +86 -86
- package/dist/kits/blocks/app/templates/productlaunch/components/Features.tsx +83 -83
- package/dist/kits/blocks/app/templates/productlaunch/components/Footer.tsx +132 -132
- package/dist/kits/blocks/app/templates/productlaunch/components/Hero.tsx +88 -88
- package/dist/kits/blocks/app/templates/productlaunch/components/Navbar.tsx +116 -116
- package/dist/kits/blocks/app/templates/productlaunch/components/Pricing.tsx +106 -106
- package/dist/kits/blocks/app/templates/productlaunch/components/ProcessTimeline.tsx +110 -110
- package/dist/kits/blocks/app/templates/productlaunch/components/ServicesGrid.tsx +68 -68
- package/dist/kits/blocks/app/templates/productlaunch/components/Team.tsx +104 -104
- package/dist/kits/blocks/app/templates/productlaunch/components/Testimonials.tsx +90 -90
- package/dist/kits/blocks/app/templates/productlaunch/components/TrustBadges.tsx +76 -76
- package/dist/kits/blocks/app/templates/productlaunch/page.tsx +43 -43
- package/dist/kits/blocks/app/templates/saasdashboard/PresetThemeVars.tsx +80 -80
- package/dist/kits/blocks/app/templates/saasdashboard/README.md +44 -44
- package/dist/kits/blocks/app/templates/saasdashboard/components/Contact.tsx +129 -129
- package/dist/kits/blocks/app/templates/saasdashboard/components/Dashboard.tsx +293 -293
- package/dist/kits/blocks/app/templates/saasdashboard/components/FAQ.tsx +55 -55
- package/dist/kits/blocks/app/templates/saasdashboard/components/Features.tsx +90 -90
- package/dist/kits/blocks/app/templates/saasdashboard/components/Footer.tsx +77 -77
- package/dist/kits/blocks/app/templates/saasdashboard/components/Hero.tsx +104 -104
- package/dist/kits/blocks/app/templates/saasdashboard/components/Hero_mask.tsx +126 -126
- package/dist/kits/blocks/app/templates/saasdashboard/components/Navbar.tsx +117 -117
- package/dist/kits/blocks/app/templates/saasdashboard/components/Pricing.tsx +90 -90
- package/dist/kits/blocks/app/templates/saasdashboard/components/SmoothScroll.tsx +96 -96
- package/dist/kits/blocks/app/templates/saasdashboard/components/Testimonials.tsx +72 -72
- package/dist/kits/blocks/app/templates/saasdashboard/components/TrustBadges.tsx +53 -53
- package/dist/kits/blocks/app/templates/saasdashboard/page.tsx +39 -39
- package/dist/kits/blocks/components/enhanced-theme-provider.tsx +195 -195
- package/dist/kits/blocks/components/providers/BlocksAppProviders.tsx +27 -27
- package/dist/kits/blocks/components/sections/About.tsx +291 -291
- package/dist/kits/blocks/components/sections/CTA.tsx +257 -257
- package/dist/kits/blocks/components/sections/Contact.tsx +267 -267
- package/dist/kits/blocks/components/sections/FAQ.tsx +214 -214
- package/dist/kits/blocks/components/sections/Features.tsx +268 -268
- package/dist/kits/blocks/components/sections/Footer.tsx +302 -302
- package/dist/kits/blocks/components/sections/HeroMotion.tsx +308 -308
- package/dist/kits/blocks/components/sections/HeroOverlay.tsx +358 -358
- package/dist/kits/blocks/components/sections/HeroProductDemo.tsx +236 -0
- package/dist/kits/blocks/components/sections/HeroSplit.tsx +352 -352
- package/dist/kits/blocks/components/sections/Navbar.tsx +350 -350
- package/dist/kits/blocks/components/sections/PortfolioSimple.tsx +549 -549
- package/dist/kits/blocks/components/sections/Pricing.tsx +264 -264
- package/dist/kits/blocks/components/sections/ProcessTimeline.tsx +325 -325
- package/dist/kits/blocks/components/sections/ServicesGrid.tsx +210 -210
- package/dist/kits/blocks/components/sections/Team.tsx +309 -309
- package/dist/kits/blocks/components/sections/Testimonials.tsx +158 -158
- package/dist/kits/blocks/components/sections/TrustBadges.tsx +162 -162
- package/dist/kits/blocks/components/sections/product-demo/ApprovalInboxPanel.tsx +125 -0
- package/dist/kits/blocks/components/sections/product-demo/DemoStage.tsx +397 -0
- package/dist/kits/blocks/components/sections/product-demo/DemoWindow.tsx +128 -0
- package/dist/kits/blocks/components/sections/product-demo/KnowledgePanel.tsx +127 -0
- package/dist/kits/blocks/components/sections/product-demo/RunConsolePanel.tsx +150 -0
- package/dist/kits/blocks/components/sections/product-demo/WorkflowStudioPanel.tsx +191 -0
- package/dist/kits/blocks/components/sections/product-demo/types.ts +193 -0
- package/dist/kits/blocks/components/theme-provider.tsx +1 -1
- package/dist/kits/blocks/components/ui/alert-dialog.tsx +134 -134
- package/dist/kits/blocks/components/ui/brand-node.tsx +121 -121
- package/dist/kits/blocks/components/ui/button.tsx +122 -122
- package/dist/kits/blocks/components/ui/card.tsx +95 -95
- package/dist/kits/blocks/components/ui/checkbox.tsx +30 -30
- package/dist/kits/blocks/components/ui/cta-button.tsx +125 -125
- package/dist/kits/blocks/components/ui/dropdown-menu.tsx +201 -201
- package/dist/kits/blocks/components/ui/feature-card.tsx +91 -91
- package/dist/kits/blocks/components/ui/input.tsx +27 -27
- package/dist/kits/blocks/components/ui/label.tsx +29 -29
- package/dist/kits/blocks/components/ui/pricing-card.tsx +120 -120
- package/dist/kits/blocks/components/ui/select.tsx +25 -25
- package/dist/kits/blocks/components/ui/skeleton.tsx +13 -13
- package/dist/kits/blocks/components/ui/table.tsx +98 -98
- package/dist/kits/blocks/components/ui/testimonial-card.tsx +108 -108
- package/dist/kits/blocks/components/ui/textarea.tsx +26 -26
- package/dist/kits/blocks/components/ui/theme-selector.tsx +243 -243
- package/dist/kits/blocks/components/ui/theme-toggle.tsx +74 -74
- package/dist/kits/blocks/components/ui/toaster.tsx +7 -7
- package/dist/kits/blocks/lib/themes.ts +400 -400
- package/dist/kits/blocks/lib/utils.ts +6 -6
- package/dist/kits/blocks/package-deps.json +3 -3
- package/package.json +1 -1
|
@@ -1,214 +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: "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
|
-
}
|
|
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
|
+
}
|