@nextworks/blocks-sections 0.1.0-alpha.0

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 (80) hide show
  1. package/README.md +44 -0
  2. package/dist/components/About.d.ts +93 -0
  3. package/dist/components/About.d.ts.map +1 -0
  4. package/dist/components/About.js +129 -0
  5. package/dist/components/About.jsx +153 -0
  6. package/dist/components/CTA.d.ts +118 -0
  7. package/dist/components/CTA.d.ts.map +1 -0
  8. package/dist/components/CTA.js +58 -0
  9. package/dist/components/CTA.jsx +88 -0
  10. package/dist/components/Contact.d.ts +111 -0
  11. package/dist/components/Contact.d.ts.map +1 -0
  12. package/dist/components/Contact.js +82 -0
  13. package/dist/components/Contact.jsx +107 -0
  14. package/dist/components/FAQ.d.ts +89 -0
  15. package/dist/components/FAQ.d.ts.map +1 -0
  16. package/dist/components/FAQ.js +78 -0
  17. package/dist/components/FAQ.jsx +98 -0
  18. package/dist/components/Features.d.ts +111 -0
  19. package/dist/components/Features.d.ts.map +1 -0
  20. package/dist/components/Features.js +109 -0
  21. package/dist/components/Features.jsx +122 -0
  22. package/dist/components/Footer.d.ts +120 -0
  23. package/dist/components/Footer.d.ts.map +1 -0
  24. package/dist/components/Footer.js +101 -0
  25. package/dist/components/Footer.jsx +138 -0
  26. package/dist/components/HeroMotion.d.ts +107 -0
  27. package/dist/components/HeroMotion.d.ts.map +1 -0
  28. package/dist/components/HeroMotion.js +55 -0
  29. package/dist/components/HeroMotion.jsx +95 -0
  30. package/dist/components/HeroOverlay.d.ts +116 -0
  31. package/dist/components/HeroOverlay.d.ts.map +1 -0
  32. package/dist/components/HeroOverlay.js +111 -0
  33. package/dist/components/HeroOverlay.jsx +141 -0
  34. package/dist/components/HeroSplit.d.ts +98 -0
  35. package/dist/components/HeroSplit.d.ts.map +1 -0
  36. package/dist/components/HeroSplit.js +100 -0
  37. package/dist/components/HeroSplit.jsx +134 -0
  38. package/dist/components/Navbar.d.ts +112 -0
  39. package/dist/components/Navbar.d.ts.map +1 -0
  40. package/dist/components/Navbar.js +80 -0
  41. package/dist/components/Navbar.jsx +127 -0
  42. package/dist/components/Newsletter.d.ts +59 -0
  43. package/dist/components/Newsletter.d.ts.map +1 -0
  44. package/dist/components/Newsletter.js +34 -0
  45. package/dist/components/Newsletter.jsx +54 -0
  46. package/dist/components/PortfolioSimple.d.ts +137 -0
  47. package/dist/components/PortfolioSimple.d.ts.map +1 -0
  48. package/dist/components/PortfolioSimple.js +180 -0
  49. package/dist/components/PortfolioSimple.jsx +259 -0
  50. package/dist/components/Pricing.d.ts +96 -0
  51. package/dist/components/Pricing.d.ts.map +1 -0
  52. package/dist/components/Pricing.js +91 -0
  53. package/dist/components/Pricing.jsx +103 -0
  54. package/dist/components/ProcessTimeline.d.ts +121 -0
  55. package/dist/components/ProcessTimeline.d.ts.map +1 -0
  56. package/dist/components/ProcessTimeline.js +88 -0
  57. package/dist/components/ProcessTimeline.jsx +151 -0
  58. package/dist/components/ServicesGrid.d.ts +64 -0
  59. package/dist/components/ServicesGrid.d.ts.map +1 -0
  60. package/dist/components/ServicesGrid.js +72 -0
  61. package/dist/components/ServicesGrid.jsx +97 -0
  62. package/dist/components/Team.d.ts +115 -0
  63. package/dist/components/Team.d.ts.map +1 -0
  64. package/dist/components/Team.js +107 -0
  65. package/dist/components/Team.jsx +135 -0
  66. package/dist/components/Testimonials.d.ts +81 -0
  67. package/dist/components/Testimonials.d.ts.map +1 -0
  68. package/dist/components/Testimonials.js +46 -0
  69. package/dist/components/Testimonials.jsx +58 -0
  70. package/dist/components/TrustBadges.d.ts +80 -0
  71. package/dist/components/TrustBadges.d.ts.map +1 -0
  72. package/dist/components/TrustBadges.js +46 -0
  73. package/dist/components/TrustBadges.jsx +64 -0
  74. package/dist/components/index.d.ts +21 -0
  75. package/dist/components/index.d.ts.map +1 -0
  76. package/dist/components/index.js +18 -0
  77. package/dist/index.d.ts +2 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +1 -0
  80. package/package.json +26 -0
@@ -0,0 +1,88 @@
1
+ "use client";
2
+ import React from "react";
3
+ import Link from "next/link";
4
+ import { Button } from "@nextworks/blocks-core";
5
+ import { cn } from "@nextworks/blocks-core";
6
+ /**
7
+ * Call-to-Action section with a heading, optional subheading/description,
8
+ * and up to two actions (primary and secondary).
9
+ *
10
+ * @remarks
11
+ * - Styling: exposes slot-style className overrides (section, container,
12
+ * contentWrapper, headingText, subheadingText, descriptionText, actionsWrapper,
13
+ * ctaButtonStyle, secondaryButtonStyle). Consumer classes are merged after
14
+ * defaults via cn().
15
+ * - Accessibility: rendered as a semantic <section> with aria-label. The role
16
+ * defaults to "region" but can be customized with the role prop.
17
+ * - Motion: subtle hover lift effects on buttons are included by default.
18
+ *
19
+ * @example
20
+ * <CTA
21
+ * headingText={{ text: "Join The Launch Today!" }}
22
+ * descriptionText={{ text: "Start your free trial in minutes." }}
23
+ * ctaButton={{ label: "Sign Up", href: "#contact" }}
24
+ * secondaryButton={{ label: "Learn More", href: "#features" }}
25
+ * />
26
+ */
27
+ export function CTA({ id = "cta", className, section = {
28
+ className: "bg-background text-foreground",
29
+ }, container = {
30
+ className: "mx-auto flex min-h-[42vh] w-full max-w-6xl flex-col items-center justify-center overflow-hidden px-4 pb-7",
31
+ }, contentWrapper = {
32
+ className: "flex w-full flex-col items-center text-center",
33
+ }, headingText = {
34
+ text: "Join The Launch Today!",
35
+ className: "text-3xl font-bold leading-[1.1] text-primary sm:text-4xl md:text-5xl text-[var(--heading-fg)]",
36
+ }, subheadingText = {
37
+ text: "",
38
+ className: "mt-2 text-lg font-medium text-muted-foreground sm:text-xl text-[var(--subheading-fg)]",
39
+ }, descriptionText = {
40
+ text: "",
41
+ className: "mt-3 max-w-2xl text-base text-foreground/80 sm:text-lg text-[var(--description-fg)]",
42
+ }, actionsWrapper = {
43
+ className: "mt-6 flex flex-col items-center gap-3 sm:flex-row",
44
+ }, ctaButton = { label: "Sign Up Now", href: "#contact" }, ctaButtonStyle = {
45
+ variant: "default",
46
+ size: "default",
47
+ className: "shadow-lg transition-all duration-200 hover:-translate-y-0.5 hover:shadow-xl border-[var(--btn-border)] focus-visible:ring-[var(--btn-ring)]",
48
+ }, ctaButtonWrapper = { className: "" }, secondaryButton = null, secondaryButtonStyle = {
49
+ variant: "outline",
50
+ size: "default",
51
+ className: "transition-transform duration-200 hover:-translate-y-0.5 border-[var(--btn-border)] focus-visible:ring-[var(--btn-ring)]",
52
+ }, secondaryButtonWrapper = { className: "" }, spacing = { topMargin: "mt-0 sm:mt-12" },
53
+ // spacing = { topMargin: "mt-[17vh]" },
54
+ ariaLabel = "Call to action section", role = "region", }) {
55
+ // default class for actions wrapper (keeps a gap and responsive row layout)
56
+ const actionsWrapperDefault = "mt-6 flex flex-col items-center gap-3 sm:flex-row";
57
+ return (<section id={id} role={role} aria-label={ariaLabel} className={cn("w-full", section.className, className)}>
58
+ <div className={cn(container.className)}>
59
+ <div className={cn(contentWrapper.className)}>
60
+ {(headingText === null || headingText === void 0 ? void 0 : headingText.text) ? (<h2 className={cn("text-center", spacing === null || spacing === void 0 ? void 0 : spacing.topMargin, headingText.className)}>
61
+ {headingText.text}
62
+ </h2>) : null}
63
+
64
+ {(subheadingText === null || subheadingText === void 0 ? void 0 : subheadingText.text) ? (<p className={cn(subheadingText.className)}>
65
+ {subheadingText.text}
66
+ </p>) : null}
67
+
68
+ {(descriptionText === null || descriptionText === void 0 ? void 0 : descriptionText.text) ? (<p className={cn(descriptionText.className)}>
69
+ {descriptionText.text}
70
+ </p>) : null}
71
+
72
+ <div className={cn(actionsWrapperDefault, actionsWrapper.className)}>
73
+ {ctaButton && (<Button asChild unstyled={ctaButtonStyle.unstyled} variant={ctaButtonStyle.variant} size={ctaButtonStyle.size} className={cn(ctaButtonWrapper.className, ctaButtonStyle.className)} style={ctaButtonStyle.style}>
74
+ <Link href={ctaButton.href} aria-label={ctaButton.label}>
75
+ {ctaButton.label}
76
+ </Link>
77
+ </Button>)}
78
+
79
+ {secondaryButton && (<Button asChild unstyled={secondaryButtonStyle.unstyled} variant={secondaryButtonStyle.variant} size={secondaryButtonStyle.size} className={cn(secondaryButtonWrapper.className, secondaryButtonStyle.className)} style={secondaryButtonStyle.style}>
80
+ <Link href={secondaryButton.href} aria-label={secondaryButton.label}>
81
+ {secondaryButton.label}
82
+ </Link>
83
+ </Button>)}
84
+ </div>
85
+ </div>
86
+ </div>
87
+ </section>);
88
+ }
@@ -0,0 +1,111 @@
1
+ import React from "react";
2
+ type FieldType = "text" | "email" | "tel" | "textarea";
3
+ /**
4
+ * Configuration for a single form field in the Contact section.
5
+ * @public
6
+ */
7
+ export interface ContactField {
8
+ /** Unique id/name for the field. Used for htmlFor and form submission. */
9
+ id: string;
10
+ /** Visible label text for the field */
11
+ label: string;
12
+ /** Placeholder text rendered inside the input */
13
+ placeholder?: string;
14
+ /** Whether the field is required for form submission */
15
+ required?: boolean;
16
+ /** Type of field to render (text, email, tel, textarea). */
17
+ type?: FieldType;
18
+ }
19
+ /**
20
+ * Props for the Contact section component.
21
+ *
22
+ * @remarks
23
+ * - Styling: exposes slot-style className overrides (section, container,
24
+ * headerWrapper, headerText, subheaderText, form, fieldsWrapper, field,
25
+ * label, input, textarea, submitButtonWrapper, submitButtonStyle). Consumer
26
+ * classes are merged after defaults via cn().
27
+ * - Behavior: onSubmit is called with the form event after default
28
+ * prevention. Provide your own handler to integrate with APIs.
29
+ * - Motion: controlled by enableMotion; when false, removes button hover lift.
30
+ * - Accessibility: rendered as a semantic <section> with aria-label.
31
+ */
32
+ export interface ContactProps {
33
+ /** Array of fields to render in the form. @defaultValue defaultFormData */
34
+ fields?: ContactField[];
35
+ /** Heading text above the form. @defaultValue "Ready to Grow Your Business?" */
36
+ contactHeaderText?: string;
37
+ /** Subheading under the header. @defaultValue "Schedule a free consultation with our experts." */
38
+ contactSubHeaderText?: string;
39
+ /** Optional id to attach to the root section element. @defaultValue "contact" */
40
+ id?: string;
41
+ /** Optional top-level class to override the section root */
42
+ className?: string;
43
+ /** Styling configuration objects (slots) */
44
+ section?: {
45
+ className?: string;
46
+ };
47
+ container?: {
48
+ className?: string;
49
+ };
50
+ headerWrapper?: {
51
+ className?: string;
52
+ };
53
+ headerText?: {
54
+ className?: string;
55
+ };
56
+ subheaderText?: {
57
+ className?: string;
58
+ };
59
+ form?: {
60
+ className?: string;
61
+ };
62
+ fieldsWrapper?: {
63
+ className?: string;
64
+ };
65
+ field?: {
66
+ className?: string;
67
+ };
68
+ label?: {
69
+ className?: string;
70
+ };
71
+ input?: {
72
+ className?: string;
73
+ };
74
+ textarea?: {
75
+ className?: string;
76
+ };
77
+ submitButtonWrapper?: {
78
+ className?: string;
79
+ };
80
+ submitButtonStyle?: {
81
+ unstyled?: boolean;
82
+ style?: React.CSSProperties;
83
+ variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link";
84
+ size?: "default" | "sm" | "lg" | "icon";
85
+ className?: string;
86
+ };
87
+ /** Text for the submit button. @defaultValue "Schedule Free Consultation" */
88
+ submitButtonText?: string;
89
+ /** Callback fired on submit; default prevented. */
90
+ onSubmit?: (e: React.FormEvent<HTMLFormElement>) => void;
91
+ /** ARIA label for the section. @defaultValue "Contact section" */
92
+ ariaLabel?: string;
93
+ /** When false, removes hover lift/transition on the submit button */
94
+ enableMotion?: boolean;
95
+ }
96
+ /**
97
+ * Contact section with a configurable form and submit action.
98
+ *
99
+ * @remarks
100
+ * - Styling: slot-style className overrides are merged via cn().
101
+ * - Behavior: onSubmit is invoked with the form event after calling
102
+ * preventDefault().
103
+ * - Motion: enableMotion toggles the button hover lift/transition.
104
+ * - Accessibility: Uses a semantic <section> with aria-label.
105
+ *
106
+ * @example
107
+ * <Contact onSubmit={(e) => { // send to your API }} />
108
+ */
109
+ export declare function Contact({ fields, contactHeaderText, contactSubHeaderText, id, className, section, container, headerWrapper, headerText, subheaderText, form, fieldsWrapper, field, label, input, textarea, submitButtonWrapper, submitButtonStyle, submitButtonText, onSubmit, ariaLabel, enableMotion, }: ContactProps): React.JSX.Element;
110
+ export {};
111
+ //# sourceMappingURL=Contact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Contact.d.ts","sourceRoot":"","sources":["../../src/components/Contact.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { cn } from "@nextworks/blocks-core";
4
+ import { Button, Input, Textarea, Label } from "@nextworks/blocks-core";
5
+ const defaultFormData = [
6
+ {
7
+ id: "name",
8
+ label: "Your Full Name",
9
+ placeholder: "John Doe",
10
+ required: true,
11
+ type: "text",
12
+ },
13
+ {
14
+ id: "email",
15
+ label: "Email Address",
16
+ placeholder: "you@example.com",
17
+ required: true,
18
+ type: "email",
19
+ },
20
+ {
21
+ id: "phone",
22
+ label: "Phone Number",
23
+ placeholder: "Enter your phone number",
24
+ required: false,
25
+ type: "tel",
26
+ },
27
+ {
28
+ id: "company",
29
+ label: "Company",
30
+ placeholder: "Enter your company name",
31
+ required: false,
32
+ type: "text",
33
+ },
34
+ {
35
+ id: "message",
36
+ label: "Message",
37
+ placeholder: "Tell us about your project...",
38
+ required: true,
39
+ type: "textarea",
40
+ },
41
+ ];
42
+ /**
43
+ * Contact section with a configurable form and submit action.
44
+ *
45
+ * @remarks
46
+ * - Styling: slot-style className overrides are merged via cn().
47
+ * - Behavior: onSubmit is invoked with the form event after calling
48
+ * preventDefault().
49
+ * - Motion: enableMotion toggles the button hover lift/transition.
50
+ * - Accessibility: Uses a semantic <section> with aria-label.
51
+ *
52
+ * @example
53
+ * <Contact onSubmit={(e) => { // send to your API }} />
54
+ */
55
+ export function Contact({ fields = defaultFormData, contactHeaderText = "Ready to Grow Your Business?", contactSubHeaderText = "Schedule a free consultation with our experts.", id = "contact", className, section = { className: "py-16 px-4 bg-primary" }, container = { className: "mx-auto max-w-4xl" }, headerWrapper = { className: "mb-8 text-center" }, headerText = {
56
+ className: "text-2xl font-bold font-poppins text-primary-foreground",
57
+ }, subheaderText = {
58
+ className: "mt-2 text-lg font-inter text-primary-foreground px-4 md:px-14",
59
+ }, form = {
60
+ className: "bg-card p-8 rounded-lg shadow-md border border-border bg-[var(--card-bg)] text-[var(--card-fg)] border-[var(--card-border)] shadow-[var(--card-shadow)]",
61
+ }, fieldsWrapper = { className: "space-y-4" }, field = { className: "space-y-2" }, label = {
62
+ className: "text-card-foreground text-sm font-medium font-poppins block text-[var(--label-fg)]",
63
+ }, input = {
64
+ className: "w-full p-3 border border-input rounded-md bg-background text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent font-inter border-[var(--input-border)] bg-[var(--input-bg)] text-[var(--input-fg)] placeholder:text-[var(--input-placeholder)] focus-visible:ring-[var(--input-focus-ring)] focus-visible:ring-offset-[var(--input-ring-offset)]",
65
+ }, textarea = {
66
+ className: "w-full p-3 border border-input rounded-md bg-background text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent resize-vertical min-h-[120px] font-inter border-[var(--input-border)] bg-[var(--input-bg)] text-[var(--input-fg)] placeholder:text-[var(--input-placeholder)] focus-visible:ring-[var(--input-focus-ring)] focus-visible:ring-offset-[var(--input-ring-offset)]",
67
+ }, submitButtonWrapper = { className: "pt-2" }, submitButtonStyle = {
68
+ variant: "default",
69
+ size: "lg",
70
+ className: "w-full shadow-lg hover:shadow-xl transition-all duration-200 hover:-translate-y-0.5 border-[var(--btn-border)] focus-visible:ring-[var(--btn-ring)]",
71
+ }, submitButtonText = "Schedule Free Consultation", onSubmit, ariaLabel = "Contact section", enableMotion = true, }) {
72
+ const handleSubmit = (e) => {
73
+ e.preventDefault();
74
+ onSubmit === null || onSubmit === void 0 ? void 0 : onSubmit(e);
75
+ };
76
+ return (_jsx("section", { id: id, className: cn("w-full", section.className, className), "aria-label": ariaLabel, children: _jsxs("div", { className: cn(container.className), children: [_jsxs("div", { className: cn(headerWrapper.className), children: [contactHeaderText && (_jsx("h2", { className: cn(headerText.className), children: contactHeaderText })), contactSubHeaderText && (_jsx("p", { className: cn(subheaderText.className), children: contactSubHeaderText }))] }), _jsx("form", { onSubmit: handleSubmit, className: cn(form.className), children: _jsxs("div", { className: cn(fieldsWrapper.className), children: [fields.map((f) => {
77
+ var _a;
78
+ const isTextarea = f.type === "textarea";
79
+ return (_jsxs("div", { className: cn(field.className), children: [_jsxs(Label, { htmlFor: f.id, className: cn(label.className), children: [f.label, f.required && (_jsx("span", { className: "text-destructive ml-1", children: "*" }))] }), isTextarea ? (_jsx(Textarea, { id: f.id, name: f.id, placeholder: f.placeholder, required: f.required, rows: 4, className: cn(textarea.className) })) : (_jsx(Input, { type: (_a = f.type) !== null && _a !== void 0 ? _a : "text", id: f.id, name: f.id, placeholder: f.placeholder, required: f.required, className: cn(input.className) }))] }, f.id));
80
+ }), _jsx("div", { className: cn(submitButtonWrapper.className), children: _jsx(Button, { type: "submit", unstyled: submitButtonStyle.unstyled, variant: submitButtonStyle.variant, size: submitButtonStyle.size, className: cn(submitButtonStyle.className, !enableMotion &&
81
+ "transition-none hover:!translate-y-0 hover:shadow-none"), style: submitButtonStyle.style, children: submitButtonText }) })] }) })] }) }));
82
+ }
@@ -0,0 +1,107 @@
1
+ "use client";
2
+ import React from "react";
3
+ import { cn } from "@nextworks/blocks-core";
4
+ import { Button, Input, Textarea, Label } from "@nextworks/blocks-core";
5
+ const defaultFormData = [
6
+ {
7
+ id: "name",
8
+ label: "Your Full Name",
9
+ placeholder: "John Doe",
10
+ required: true,
11
+ type: "text",
12
+ },
13
+ {
14
+ id: "email",
15
+ label: "Email Address",
16
+ placeholder: "you@example.com",
17
+ required: true,
18
+ type: "email",
19
+ },
20
+ {
21
+ id: "phone",
22
+ label: "Phone Number",
23
+ placeholder: "Enter your phone number",
24
+ required: false,
25
+ type: "tel",
26
+ },
27
+ {
28
+ id: "company",
29
+ label: "Company",
30
+ placeholder: "Enter your company name",
31
+ required: false,
32
+ type: "text",
33
+ },
34
+ {
35
+ id: "message",
36
+ label: "Message",
37
+ placeholder: "Tell us about your project...",
38
+ required: true,
39
+ type: "textarea",
40
+ },
41
+ ];
42
+ /**
43
+ * Contact section with a configurable form and submit action.
44
+ *
45
+ * @remarks
46
+ * - Styling: slot-style className overrides are merged via cn().
47
+ * - Behavior: onSubmit is invoked with the form event after calling
48
+ * preventDefault().
49
+ * - Motion: enableMotion toggles the button hover lift/transition.
50
+ * - Accessibility: Uses a semantic <section> with aria-label.
51
+ *
52
+ * @example
53
+ * <Contact onSubmit={(e) => { // send to your API }} />
54
+ */
55
+ export function Contact({ fields = defaultFormData, contactHeaderText = "Ready to Grow Your Business?", contactSubHeaderText = "Schedule a free consultation with our experts.", id = "contact", className, section = { className: "py-16 px-4 bg-primary" }, container = { className: "mx-auto max-w-4xl" }, headerWrapper = { className: "mb-8 text-center" }, headerText = {
56
+ className: "text-2xl font-bold font-poppins text-primary-foreground",
57
+ }, subheaderText = {
58
+ className: "mt-2 text-lg font-inter text-primary-foreground px-4 md:px-14",
59
+ }, form = {
60
+ className: "bg-card p-8 rounded-lg shadow-md border border-border bg-[var(--card-bg)] text-[var(--card-fg)] border-[var(--card-border)] shadow-[var(--card-shadow)]",
61
+ }, fieldsWrapper = { className: "space-y-4" }, field = { className: "space-y-2" }, label = {
62
+ className: "text-card-foreground text-sm font-medium font-poppins block text-[var(--label-fg)]",
63
+ }, input = {
64
+ className: "w-full p-3 border border-input rounded-md bg-background text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent font-inter border-[var(--input-border)] bg-[var(--input-bg)] text-[var(--input-fg)] placeholder:text-[var(--input-placeholder)] focus-visible:ring-[var(--input-focus-ring)] focus-visible:ring-offset-[var(--input-ring-offset)]",
65
+ }, textarea = {
66
+ className: "w-full p-3 border border-input rounded-md bg-background text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent resize-vertical min-h-[120px] font-inter border-[var(--input-border)] bg-[var(--input-bg)] text-[var(--input-fg)] placeholder:text-[var(--input-placeholder)] focus-visible:ring-[var(--input-focus-ring)] focus-visible:ring-offset-[var(--input-ring-offset)]",
67
+ }, submitButtonWrapper = { className: "pt-2" }, submitButtonStyle = {
68
+ variant: "default",
69
+ size: "lg",
70
+ className: "w-full shadow-lg hover:shadow-xl transition-all duration-200 hover:-translate-y-0.5 border-[var(--btn-border)] focus-visible:ring-[var(--btn-ring)]",
71
+ }, submitButtonText = "Schedule Free Consultation", onSubmit, ariaLabel = "Contact section", enableMotion = true, }) {
72
+ const handleSubmit = (e) => {
73
+ e.preventDefault();
74
+ onSubmit === null || onSubmit === void 0 ? void 0 : onSubmit(e);
75
+ };
76
+ return (<section id={id} className={cn("w-full", section.className, className)} aria-label={ariaLabel}>
77
+ <div className={cn(container.className)}>
78
+ <div className={cn(headerWrapper.className)}>
79
+ {contactHeaderText && (<h2 className={cn(headerText.className)}>{contactHeaderText}</h2>)}
80
+ {contactSubHeaderText && (<p className={cn(subheaderText.className)}>
81
+ {contactSubHeaderText}
82
+ </p>)}
83
+ </div>
84
+ <form onSubmit={handleSubmit} className={cn(form.className)}>
85
+ <div className={cn(fieldsWrapper.className)}>
86
+ {fields.map((f) => {
87
+ var _a;
88
+ const isTextarea = f.type === "textarea";
89
+ return (<div key={f.id} className={cn(field.className)}>
90
+ <Label htmlFor={f.id} className={cn(label.className)}>
91
+ {f.label}
92
+ {f.required && (<span className="text-destructive ml-1">*</span>)}
93
+ </Label>
94
+ {isTextarea ? (<Textarea id={f.id} name={f.id} placeholder={f.placeholder} required={f.required} rows={4} className={cn(textarea.className)}/>) : (<Input type={(_a = f.type) !== null && _a !== void 0 ? _a : "text"} id={f.id} name={f.id} placeholder={f.placeholder} required={f.required} className={cn(input.className)}/>)}
95
+ </div>);
96
+ })}
97
+ <div className={cn(submitButtonWrapper.className)}>
98
+ <Button type="submit" unstyled={submitButtonStyle.unstyled} variant={submitButtonStyle.variant} size={submitButtonStyle.size} className={cn(submitButtonStyle.className, !enableMotion &&
99
+ "transition-none hover:!translate-y-0 hover:shadow-none")} style={submitButtonStyle.style}>
100
+ {submitButtonText}
101
+ </Button>
102
+ </div>
103
+ </div>
104
+ </form>
105
+ </div>
106
+ </section>);
107
+ }
@@ -0,0 +1,89 @@
1
+ import React from "react";
2
+ /**
3
+ * Represents a single FAQ item.
4
+ * @public
5
+ */
6
+ export interface FAQS {
7
+ /** The question text */
8
+ question?: string;
9
+ /** The answer text */
10
+ answer?: string;
11
+ }
12
+ /**
13
+ * Props for the FAQ section component.
14
+ *
15
+ * @remarks
16
+ * - Styling: exposes slot-style className overrides; consumer classes are
17
+ * merged after defaults via cn().
18
+ * - Behavior: supports single or multiple open items; default open indices
19
+ * are respected. Controlled internally with a Set.
20
+ * - Motion: when enableMotion is false, collapse/expand transitions are
21
+ * removed for a hard-cut.
22
+ * - Accessibility: each item uses button+region with aria-controls and
23
+ * aria-expanded. Section uses aria-label.
24
+ */
25
+ export interface FAQProps {
26
+ /** Header text displayed at the top of the FAQ section. @defaultValue "Frequently Asked Questions" */
27
+ faqSectionHeaderText?: string;
28
+ /** Array of question/answer items. @defaultValue defaultFaqData */
29
+ faqData?: FAQS[];
30
+ /** Optional id for the section root. @defaultValue "faq" */
31
+ sectionId?: string;
32
+ /** Optional top-level class override */
33
+ className?: string;
34
+ /** When false, only one item can be open at a time. @defaultValue true */
35
+ allowMultipleOpen?: boolean;
36
+ /** Indices of items that should be open by default. @defaultValue [] */
37
+ defaultOpenIndices?: number[];
38
+ /** Optional icons for open/closed states */
39
+ openIcon?: React.ReactNode;
40
+ closedIcon?: React.ReactNode;
41
+ /** ARIA label for the FAQ section. @defaultValue "Frequently asked questions section" */
42
+ ariaLabel?: string;
43
+ /** Slot-style overrides */
44
+ section?: {
45
+ className?: string;
46
+ };
47
+ container?: {
48
+ className?: string;
49
+ };
50
+ heading?: {
51
+ className?: string;
52
+ };
53
+ grid?: {
54
+ className?: string;
55
+ };
56
+ item?: {
57
+ className?: string;
58
+ };
59
+ questionButton?: {
60
+ className?: string;
61
+ };
62
+ questionText?: {
63
+ className?: string;
64
+ };
65
+ chevronIcon?: {
66
+ className?: string;
67
+ };
68
+ answer?: {
69
+ className?: string;
70
+ };
71
+ answerText?: {
72
+ className?: string;
73
+ };
74
+ /** When false, removes transition on collapse/expand for a hard-cut */
75
+ enableMotion?: boolean;
76
+ }
77
+ /**
78
+ * Expandable FAQ section with accessible toggles and optional icons.
79
+ *
80
+ * @remarks
81
+ * - Supports multiple or single open item behavior via allowMultipleOpen.
82
+ * - Uses button with aria-expanded and a region with aria-labelledby.
83
+ * - Motion can be disabled via enableMotion for reduced animation.
84
+ *
85
+ * @example
86
+ * <FAQ faqData={[{ question: 'What is X?', answer: 'Y' }]} />
87
+ */
88
+ export declare function FAQ({ faqSectionHeaderText, faqData, sectionId, className, allowMultipleOpen, defaultOpenIndices, openIcon, closedIcon, ariaLabel, section, container, heading, grid, item, questionButton, questionText, chevronIcon, answer, answerText, enableMotion, }: FAQProps): React.JSX.Element;
89
+ //# sourceMappingURL=FAQ.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FAQ.d.ts","sourceRoot":"","sources":["../../src/components/FAQ.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAKjD;;;GAGG;AACH,MAAM,WAAW,IAAI;IACnB,wBAAwB;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,QAAQ;IACvB,sGAAsG;IACtG,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,mEAAmE;IACnE,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC;IACjB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,wEAAwE;IACxE,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,yFAAyF;IACzF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjC,SAAS,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjC,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9B,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9B,cAAc,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,YAAY,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,WAAW,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAChC,UAAU,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,uEAAuE;IACvE,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAkCD;;;;;;;;;;GAUG;AACH,wBAAgB,GAAG,CAAC,EAClB,oBAAmD,EACnD,OAAwB,EACxB,SAAiB,EACjB,SAAS,EACT,iBAAwB,EACxB,kBAAuB,EACvB,QAAQ,EACR,UAAU,EACV,SAAgD,EAChD,OAA8D,EAC9D,SAA8C,EAC9C,OAEC,EACD,IAAsE,EACtE,IAA4B,EAC5B,cAGC,EACD,YAAgC,EAChC,WAAgE,EAChE,MAGC,EACD,UAA6C,EAC7C,YAAmB,GACpB,EAAE,QAAQ,qBAyFV"}
@@ -0,0 +1,78 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useMemo, useState } from "react";
4
+ import { cn } from "@nextworks/blocks-core";
5
+ import { ChevronDown, ChevronUp } from "lucide-react";
6
+ // const defaultFaqData: FAQS[] = [
7
+ // {
8
+ // question: "What does IntelliOpAI do?",
9
+ // answer: "Automates ops with real-time insights.",
10
+ // },
11
+ // {
12
+ // question: "Do I need coding skills?",
13
+ // answer: "No, it's designed for ease of use.",
14
+ // },
15
+ // ];
16
+ const defaultFaqData = [
17
+ {
18
+ question: "What does IntelliOpAI do?",
19
+ answer: "Automates ops with real-time insights.",
20
+ },
21
+ {
22
+ question: "How secure is my data?",
23
+ answer: "We use industry-standard encryption and strict access controls.",
24
+ },
25
+ {
26
+ question: "Can I integrate with other tools?",
27
+ answer: "Yes — supports APIs and popular integrations like Slack, GitHub, and Jira.",
28
+ },
29
+ {
30
+ question: "How do I get support?",
31
+ answer: "Contact our support team via in-app chat or the help center for assistance.",
32
+ },
33
+ ];
34
+ /**
35
+ * Expandable FAQ section with accessible toggles and optional icons.
36
+ *
37
+ * @remarks
38
+ * - Supports multiple or single open item behavior via allowMultipleOpen.
39
+ * - Uses button with aria-expanded and a region with aria-labelledby.
40
+ * - Motion can be disabled via enableMotion for reduced animation.
41
+ *
42
+ * @example
43
+ * <FAQ faqData={[{ question: 'What is X?', answer: 'Y' }]} />
44
+ */
45
+ export function FAQ({ faqSectionHeaderText = "Frequently Asked Questions", faqData = defaultFaqData, sectionId = "faq", className, allowMultipleOpen = true, defaultOpenIndices = [], openIcon, closedIcon, ariaLabel = "Frequently asked questions section", section = { className: "py-10 px-5 bg-muted text-foreground" }, container = { className: "mx-auto max-w-7xl" }, heading = {
46
+ className: "mb-6 text-center text-2xl font-bold text-foreground",
47
+ }, grid = { className: "grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6" }, item = { className: "mb-4" }, questionButton = {
48
+ className: "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))]",
49
+ }, questionText = { className: "" }, chevronIcon = { className: "transition-transform duration-200" }, answer = {
50
+ className: "bg-[var(--faq-answer-bg,var(--card-bg))] text-[var(--faq-answer-fg,var(--card-fg))] border-[var(--card-border)]",
51
+ }, answerText = { className: "leading-relaxed" }, enableMotion = true, }) {
52
+ const [openSet, setOpenSet] = useState(() => new Set(defaultOpenIndices));
53
+ const idPrefix = useMemo(() => sectionId || "faq", [sectionId]);
54
+ const onToggle = (idx) => {
55
+ setOpenSet((prev) => {
56
+ const next = new Set(prev);
57
+ if (next.has(idx))
58
+ next.delete(idx);
59
+ else {
60
+ if (!allowMultipleOpen)
61
+ next.clear();
62
+ next.add(idx);
63
+ }
64
+ return next;
65
+ });
66
+ };
67
+ return (_jsx("section", { id: sectionId, className: cn(section.className, className), "aria-label": ariaLabel, children: _jsxs("div", { className: cn(container.className), children: [faqSectionHeaderText && (_jsx("h2", { className: cn(heading.className), children: faqSectionHeaderText })), _jsx("div", { className: cn(grid.className), children: faqData.map((faq, index) => {
68
+ const transitionCls = enableMotion
69
+ ? "transition-all duration-300 ease-in-out"
70
+ : "transition-none";
71
+ const isOpen = openSet.has(index);
72
+ return (_jsxs("div", { className: cn("w-full", item.className), children: [_jsxs("button", { id: `${idPrefix}-trigger-${index}`, "aria-controls": `${idPrefix}-panel-${index}`, "aria-expanded": isOpen, onClick: () => onToggle(index), className: cn("flex w-full items-center justify-between rounded-md p-4 text-left focus:outline-none",
73
+ // preset-first ring/border
74
+ "border-[var(--btn-border)] focus-visible:ring-2 focus-visible:ring-[var(--btn-ring)] focus-visible:ring-offset-2", questionButton.className), children: [_jsx("span", { className: cn("flex-1 font-medium select-none", questionText.className), children: faq.question }), _jsx("span", { className: cn("ml-3 flex-shrink-0", chevronIcon.className), children: isOpen
75
+ ? (openIcon !== null && openIcon !== void 0 ? openIcon : _jsx(ChevronUp, { className: "h-5 w-5" }))
76
+ : (closedIcon !== null && closedIcon !== void 0 ? closedIcon : _jsx(ChevronDown, { className: "h-5 w-5" })) })] }), _jsx("div", { id: `${idPrefix}-panel-${index}`, role: "region", "aria-labelledby": `${idPrefix}-trigger-${index}`, className: cn("border-border overflow-hidden rounded-md border", isOpen ? "max-h-96 opacity-100" : "max-h-0 opacity-0", transitionCls, answer.className), children: _jsx("div", { className: cn("p-4", answerText.className), children: faq.answer }) })] }, `${idPrefix}-item-${index}`));
77
+ }) })] }) }));
78
+ }