@opensite/ui 2.1.9 → 2.2.2

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.
@@ -2,13 +2,12 @@
2
2
  'use strict';
3
3
 
4
4
  var React = require('react');
5
- var forms = require('@page-speed/forms');
5
+ var integration = require('@page-speed/forms/integration');
6
6
  var clsx = require('clsx');
7
7
  var tailwindMerge = require('tailwind-merge');
8
8
  var classVarianceAuthority = require('class-variance-authority');
9
9
  var jsxRuntime = require('react/jsx-runtime');
10
10
  var icon = require('@page-speed/icon');
11
- var integration = require('@page-speed/forms/integration');
12
11
 
13
12
  function _interopNamespace(e) {
14
13
  if (e && e.__esModule) return e;
@@ -469,6 +468,16 @@ function Card({ className, ...props }) {
469
468
  }
470
469
  );
471
470
  }
471
+ function CardContent({ className, ...props }) {
472
+ return /* @__PURE__ */ jsxRuntime.jsx(
473
+ "div",
474
+ {
475
+ "data-slot": "card-content",
476
+ className: cn("px-6", className),
477
+ ...props
478
+ }
479
+ );
480
+ }
472
481
  var maxWidthStyles = {
473
482
  sm: "max-w-screen-sm",
474
483
  md: "max-w-screen-md",
@@ -844,168 +853,115 @@ var Section = React__namespace.default.forwardRef(
844
853
  }
845
854
  );
846
855
  Section.displayName = "Section";
847
- var MOBILE_CLASSES = {
848
- "fit-left": "items-start md:items-center",
849
- "fit-center": "items-center",
850
- "fit-right": "items-end md:items-center",
851
- "full-left": "items-stretch md:items-center",
852
- "full-center": "items-stretch md:items-center",
853
- "full-right": "items-stretch md:items-center"
856
+ var DEFAULT_STYLE_RULES = {
857
+ formContainer: "",
858
+ fieldsContainer: "",
859
+ fieldClassName: "",
860
+ formClassName: "space-y-6"
854
861
  };
855
- function BlockActions({
856
- mobileConfig,
857
- actionsClassName,
858
- verticalSpacing = "mt-4 md:mt-8",
859
- actions,
860
- actionsSlot
861
- }) {
862
- const width = mobileConfig?.width ?? "full";
863
- const position = mobileConfig?.position ?? "center";
864
- const mobileLayoutClass = MOBILE_CLASSES[`${width}-${position}`];
865
- if (actionsSlot) {
866
- return /* @__PURE__ */ jsxRuntime.jsx("div", { children: actionsSlot });
867
- } else if (actions && actions?.length > 0) {
868
- return /* @__PURE__ */ jsxRuntime.jsx(
869
- "div",
870
- {
871
- className: cn(
872
- "flex flex-col md:flex-row flex-wrap gap-4",
873
- mobileLayoutClass,
874
- actionsClassName,
875
- verticalSpacing
876
- ),
877
- children: actions.map((action, index) => /* @__PURE__ */ jsxRuntime.jsx(ActionComponent, { action }, index))
878
- }
879
- );
880
- } else {
881
- return null;
882
- }
883
- }
884
- function ActionComponent({ action }) {
885
- const {
886
- label,
887
- icon,
888
- iconAfter,
889
- children,
890
- href,
891
- onClick,
892
- className: actionClassName,
893
- ...pressableProps
894
- } = action;
895
- return /* @__PURE__ */ jsxRuntime.jsx(
896
- Pressable,
897
- {
898
- href,
899
- onClick,
900
- asButton: action.asButton ?? true,
901
- className: actionClassName,
902
- ...pressableProps,
903
- children: children ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
904
- icon,
905
- label,
906
- iconAfter
907
- ] })
908
- }
909
- );
910
- }
911
862
  var DEFAULT_FORM_FIELDS = [
912
863
  {
913
- name: "firstName",
864
+ name: "fullName",
914
865
  type: "text",
915
- label: "First Name",
916
- placeholder: "John",
866
+ label: "Full Name",
867
+ placeholder: "Full Name",
917
868
  required: true,
918
869
  columnSpan: 6
919
870
  },
920
871
  {
921
- name: "lastName",
922
- type: "text",
923
- label: "Last Name",
924
- placeholder: "Doe",
872
+ name: "email",
873
+ type: "email",
874
+ label: "Email",
875
+ placeholder: "your@email.com",
925
876
  required: true,
926
877
  columnSpan: 6
927
878
  },
928
879
  {
929
- name: "email",
930
- type: "email",
931
- label: "Email Address",
932
- placeholder: "john@example.com",
933
- required: true,
880
+ name: "phone",
881
+ type: "tel",
882
+ label: "Phone",
883
+ placeholder: "+1 (555) 000-0000",
934
884
  columnSpan: 12
935
885
  },
936
886
  {
937
887
  name: "message",
938
888
  type: "textarea",
939
- label: "Message",
940
- placeholder: "How can we help you?",
889
+ label: "Your Message",
890
+ placeholder: "How can we help you today?",
941
891
  required: true,
942
892
  rows: 4,
943
893
  columnSpan: 12
944
- },
945
- {
946
- name: "privacyPolicy",
947
- type: "checkbox",
948
- label: "I agree to the privacy policy",
949
- required: true,
950
- columnSpan: 12
951
894
  }
952
895
  ];
953
896
  function ContactCard({
954
- heading,
955
- description,
956
- actions,
957
- actionsSlot,
958
- actionsClassName,
897
+ heading = "Get In Touch",
898
+ description = "We'd love to hear from you. Send us a message and we'll respond as soon as possible.",
899
+ buttonText = "Send Message",
900
+ buttonIcon,
959
901
  contactOptions,
960
902
  contactOptionsSlot,
961
- formFields,
962
- successMessage = "Thank you! We'll be in touch soon.",
903
+ socialLinks,
904
+ socialLinksSlot,
905
+ formEngineSetup,
963
906
  className,
964
907
  containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
965
908
  cardClassName,
966
- formClassName,
967
- successMessageClassName,
968
- errorMessageClassName,
909
+ cardContentClassName,
969
910
  infoPanelClassName,
970
911
  headingClassName,
971
912
  descriptionClassName,
972
913
  contactOptionsClassName,
914
+ socialLinksClassName,
915
+ gridClassName,
973
916
  background,
974
917
  spacing = "py-16 md:py-32",
975
918
  pattern,
976
- patternOpacity,
977
- formConfig,
978
- onSubmit,
979
- onSuccess,
980
- onError
919
+ patternOpacity
981
920
  }) {
982
- const fields = React.useMemo(() => formFields || DEFAULT_FORM_FIELDS, [formFields]);
983
- const { form, submissionError, formMethod, resetSubmissionState } = integration.useContactForm({
984
- formFields: fields,
985
- formConfig,
986
- onSubmit,
987
- onSuccess,
988
- onError
989
- });
990
- const contactOptionsContent = React.useMemo(() => {
921
+ const contactOptionsContent = React__namespace.useMemo(() => {
991
922
  if (contactOptionsSlot) return contactOptionsSlot;
992
923
  if (contactOptions && contactOptions.length > 0) {
993
- return contactOptions.map((option, key) => /* @__PURE__ */ jsxRuntime.jsxs(
924
+ return contactOptions.map((option, key) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
925
+ /* @__PURE__ */ jsxRuntime.jsx(
926
+ DynamicIcon,
927
+ {
928
+ name: option.icon,
929
+ size: 20,
930
+ className: "shrink-0 text-muted-foreground"
931
+ }
932
+ ),
933
+ option.href ? /* @__PURE__ */ jsxRuntime.jsx(
934
+ Pressable,
935
+ {
936
+ href: option.href,
937
+ className: "text-sm font-medium text-muted-foreground",
938
+ children: option.info
939
+ }
940
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-muted-foreground", children: option.info })
941
+ ] }, key));
942
+ }
943
+ return null;
944
+ }, [contactOptionsSlot, contactOptions]);
945
+ const socialLinksContent = React__namespace.useMemo(() => {
946
+ if (socialLinksSlot) return socialLinksSlot;
947
+ if (socialLinks && socialLinks.length > 0) {
948
+ return socialLinks.map((social, key) => /* @__PURE__ */ jsxRuntime.jsx(
994
949
  Pressable,
995
950
  {
996
- variant: "link",
997
- href: option.href,
998
- className: "gap-4 font-bold",
999
- children: [
1000
- /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: option.icon, size: 20 }),
1001
- option.info
1002
- ]
951
+ href: social.href,
952
+ className: cn(
953
+ "flex h-9 w-9 items-center justify-center",
954
+ "rounded-full transition-colors",
955
+ "text-muted-foreground hover:text-foreground"
956
+ ),
957
+ "aria-label": social.label,
958
+ children: /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: social.icon, size: 18 })
1003
959
  },
1004
960
  key
1005
961
  ));
1006
962
  }
1007
963
  return null;
1008
- }, [contactOptionsSlot, contactOptions]);
964
+ }, [socialLinksSlot, socialLinks]);
1009
965
  return /* @__PURE__ */ jsxRuntime.jsx(
1010
966
  Section,
1011
967
  {
@@ -1015,82 +971,89 @@ function ContactCard({
1015
971
  patternOpacity,
1016
972
  className,
1017
973
  containerClassName,
1018
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid items-start gap-10 md:gap-12 lg:grid-cols-2", children: [
1019
- /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn("p-6 lg:p-8 order-2 md:order-1", cardClassName), children: /* @__PURE__ */ jsxRuntime.jsxs(
1020
- forms.Form,
1021
- {
1022
- form,
1023
- action: formConfig?.endpoint,
1024
- method: formMethod,
1025
- submissionError,
1026
- successMessage,
1027
- successMessageClassName,
1028
- errorMessageClassName,
1029
- submissionConfig: formConfig?.submissionConfig,
1030
- onNewSubmission: resetSubmissionState,
1031
- className: cn("space-y-6", formClassName),
1032
- children: [
1033
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-12 gap-6", children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(
1034
- "div",
1035
- {
1036
- className: integration.getColumnSpanClass(field.columnSpan),
1037
- children: /* @__PURE__ */ jsxRuntime.jsx(integration.DynamicFormField, { field })
974
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
975
+ "div",
976
+ {
977
+ className: cn(
978
+ "grid items-start gap-10 md:gap-12 grid-cols-1 lg:grid-cols-2",
979
+ gridClassName
980
+ ),
981
+ children: [
982
+ /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn("order-2 lg:order-1 pt-0 pb-0", cardClassName), children: /* @__PURE__ */ jsxRuntime.jsx(CardContent, { className: cn("p-6 lg:p-8", cardContentClassName), children: formEngineSetup ? /* @__PURE__ */ jsxRuntime.jsx(
983
+ integration.FormEngine,
984
+ {
985
+ formEngineSetup: {
986
+ ...formEngineSetup,
987
+ formLayoutSettings: {
988
+ ...formEngineSetup.formLayoutSettings,
989
+ formLayout: "standard",
990
+ submitButtonSetup: {
991
+ ...formEngineSetup.formLayoutSettings?.submitButtonSetup,
992
+ submitLabel: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
993
+ buttonIcon,
994
+ buttonText
995
+ ] })
996
+ }
997
+ }
1038
998
  },
1039
- field.name
1040
- )) }),
1041
- /* @__PURE__ */ jsxRuntime.jsx(
1042
- BlockActions,
1043
- {
1044
- actions,
1045
- actionsSlot,
1046
- actionsClassName
1047
- }
1048
- )
1049
- ]
1050
- }
1051
- ) }),
1052
- /* @__PURE__ */ jsxRuntime.jsxs(
1053
- "div",
1054
- {
1055
- className: cn(
1056
- "flex flex-col items-start gap-2 md:gap-4 order-1 md:order-2",
1057
- infoPanelClassName
1058
- ),
1059
- children: [
1060
- heading && (typeof heading === "string" ? /* @__PURE__ */ jsxRuntime.jsx(
1061
- "h2",
1062
- {
1063
- className: cn(
1064
- "text-4xl lg:text-6xl font-bold tracking-tight text-pretty",
1065
- headingClassName
1066
- ),
1067
- children: heading
1068
- }
1069
- ) : heading),
1070
- description && (typeof description === "string" ? /* @__PURE__ */ jsxRuntime.jsx(
1071
- "p",
1072
- {
1073
- className: cn(
1074
- "leading-relaxed text-pretty text-lg opacity-70",
1075
- descriptionClassName
1076
- ),
1077
- children: description
1078
- }
1079
- ) : description),
1080
- /* @__PURE__ */ jsxRuntime.jsx(
1081
- "div",
1082
- {
1083
- className: cn(
1084
- "mt-4 space-y-4 w-full md:w-fit px-6 py-6 md:px-12 md:py-8 bg-muted rounded-xl text-sm md:text-normal flex flex-col items-start gap-1",
1085
- contactOptionsClassName
999
+ defaultFields: DEFAULT_FORM_FIELDS,
1000
+ defaultStyleRules: DEFAULT_STYLE_RULES
1001
+ }
1002
+ ) : null }) }),
1003
+ /* @__PURE__ */ jsxRuntime.jsxs(
1004
+ "div",
1005
+ {
1006
+ className: cn(
1007
+ "flex flex-col items-start gap-2 md:gap-4 order-1 lg:order-2",
1008
+ infoPanelClassName
1009
+ ),
1010
+ children: [
1011
+ heading && (typeof heading === "string" ? /* @__PURE__ */ jsxRuntime.jsx(
1012
+ "h2",
1013
+ {
1014
+ className: cn(
1015
+ "text-4xl lg:text-6xl font-bold tracking-tight text-pretty",
1016
+ headingClassName
1017
+ ),
1018
+ children: heading
1019
+ }
1020
+ ) : heading),
1021
+ description && (typeof description === "string" ? /* @__PURE__ */ jsxRuntime.jsx(
1022
+ "p",
1023
+ {
1024
+ className: cn(
1025
+ "leading-relaxed text-pretty text-lg text-muted-foreground",
1026
+ descriptionClassName
1027
+ ),
1028
+ children: description
1029
+ }
1030
+ ) : description),
1031
+ contactOptionsContent && /* @__PURE__ */ jsxRuntime.jsx(
1032
+ "div",
1033
+ {
1034
+ className: cn(
1035
+ "mt-4 w-full rounded-xl bg-muted px-6 py-6 md:px-8 md:py-8 space-y-6",
1036
+ contactOptionsClassName
1037
+ ),
1038
+ children: contactOptionsContent
1039
+ }
1086
1040
  ),
1087
- children: contactOptionsContent
1088
- }
1089
- )
1090
- ]
1091
- }
1092
- )
1093
- ] }) })
1041
+ socialLinksContent && /* @__PURE__ */ jsxRuntime.jsx(
1042
+ "div",
1043
+ {
1044
+ className: cn(
1045
+ "mt-2 flex items-center gap-2",
1046
+ socialLinksClassName
1047
+ ),
1048
+ children: socialLinksContent
1049
+ }
1050
+ )
1051
+ ]
1052
+ }
1053
+ )
1054
+ ]
1055
+ }
1056
+ ) })
1094
1057
  }
1095
1058
  );
1096
1059
  }
@@ -1,8 +1,9 @@
1
1
  import * as React from 'react';
2
- import { FormFieldConfig, PageSpeedFormConfig } from '@page-speed/forms/integration';
2
+ import { FormEngineProps } from '@page-speed/forms/integration';
3
3
  import { f as SectionBackground, g as SectionSpacing, t as PatternName } from './community-initiatives-Bz_A5vLU.cjs';
4
- import { A as ActionConfig } from './blocks-Cohq4eio.cjs';
4
+ import { C as ContactDarkSocialLink } from './contact-dark-VpMwwNkH.cjs';
5
5
  import 'react/jsx-runtime';
6
+ import './blocks-Cohq4eio.cjs';
6
7
  import 'class-variance-authority';
7
8
  import './button-variants-lRElsmTc.cjs';
8
9
  import 'class-variance-authority/types';
@@ -30,10 +31,6 @@ interface ContactCardProps {
30
31
  * Description text below the heading
31
32
  */
32
33
  description?: React.ReactNode;
33
- /**
34
- * Form card heading
35
- */
36
- formHeading?: React.ReactNode;
37
34
  /**
38
35
  * Submit button text
39
36
  */
@@ -43,15 +40,7 @@ interface ContactCardProps {
43
40
  */
44
41
  buttonIcon?: React.ReactNode;
45
42
  /**
46
- * Array of action configurations for additional buttons
47
- */
48
- actions?: ActionConfig[];
49
- /**
50
- * Custom slot for rendering actions (overrides actions array and default submit)
51
- */
52
- actionsSlot?: React.ReactNode;
53
- /**
54
- * Contact options to display
43
+ * Contact options to display with icons
55
44
  */
56
45
  contactOptions?: ContactOption[];
57
46
  /**
@@ -59,15 +48,17 @@ interface ContactCardProps {
59
48
  */
60
49
  contactOptionsSlot?: React.ReactNode;
61
50
  /**
62
- * Array of form field configurations
63
- * If not provided, defaults to: firstName, lastName, email, message, privacyPolicy
51
+ * Social media links to display
52
+ */
53
+ socialLinks?: ContactDarkSocialLink[];
54
+ /**
55
+ * Custom slot for rendering social links (overrides socialLinks array)
64
56
  */
65
- formFields?: FormFieldConfig[];
57
+ socialLinksSlot?: React.ReactNode;
66
58
  /**
67
- * Success message to display after form submission
68
- * @default "Thank you! We'll be in touch soon."
59
+ * Full form engine setup and props
69
60
  */
70
- successMessage?: React.ReactNode;
61
+ formEngineSetup?: FormEngineProps;
71
62
  /**
72
63
  * Additional CSS classes for the section
73
64
  */
@@ -82,25 +73,9 @@ interface ContactCardProps {
82
73
  */
83
74
  cardClassName?: string;
84
75
  /**
85
- * Additional CSS classes for the form heading
86
- */
87
- formHeadingClassName?: string;
88
- /**
89
- * Additional CSS classes for the form
90
- */
91
- formClassName?: string;
92
- /**
93
- * Additional CSS classes for the submit button
94
- */
95
- submitClassName?: string;
96
- /**
97
- * Additional CSS classes for the success message
98
- */
99
- successMessageClassName?: string;
100
- /**
101
- * Additional CSS classes for the error message
76
+ * Additional CSS classes for the card content
102
77
  */
103
- errorMessageClassName?: string;
78
+ cardContentClassName?: string;
104
79
  /**
105
80
  * Additional CSS classes for the info panel
106
81
  */
@@ -117,13 +92,21 @@ interface ContactCardProps {
117
92
  * Additional CSS classes for the contact options container
118
93
  */
119
94
  contactOptionsClassName?: string;
95
+ /**
96
+ * Additional CSS classes for the social links container
97
+ */
98
+ socialLinksClassName?: string;
99
+ /**
100
+ * Additional CSS classes for the two-column grid wrapper
101
+ */
102
+ gridClassName?: string;
120
103
  /**
121
104
  * Background style for the section
122
105
  */
123
106
  background?: SectionBackground;
124
107
  /**
125
108
  * Vertical spacing for the section
126
- * @default "py-8 md:py-32"
109
+ * @default "py-16 md:py-32"
127
110
  */
128
111
  spacing?: SectionSpacing;
129
112
  /**
@@ -134,43 +117,24 @@ interface ContactCardProps {
134
117
  * Pattern overlay opacity (0-1)
135
118
  */
136
119
  patternOpacity?: number;
137
- /**
138
- * Optional form submission configuration.
139
- * See `FORMS_INTEGRATION_GUIDE.md` for complete examples.
140
- */
141
- formConfig?: PageSpeedFormConfig;
142
- /**
143
- * Optional custom submission handler for maximum flexibility.
144
- */
145
- onSubmit?: (values: Record<string, unknown>) => void | Promise<void>;
146
- /**
147
- * Optional success callback invoked after successful submission.
148
- */
149
- onSuccess?: (data: unknown) => void;
150
- /**
151
- * Optional error callback invoked if submission fails.
152
- */
153
- onError?: (error: Error) => void;
154
- /**
155
- * Additional CSS classes for the actions container
156
- */
157
- actionsClassName?: string;
158
120
  }
159
121
  /**
160
- * ContactCard - A simple contact form with card layout and contact information sidebar.
161
- * Perfect for basic contact pages with multiple contact methods displayed.
122
+ * ContactCard - A split-layout contact form with a card-wrapped form on one side
123
+ * and heading, description, and icon-based contact details on the other.
162
124
  *
163
125
  * @example
164
126
  * ```tsx
165
127
  * <ContactCard
166
- * heading="Get in Touch"
167
- * description="Have questions? We'd love to hear from you."
168
- * formHeading="Contact us"
169
- * buttonText="Send Message"
170
- * formConfig={{ endpoint: "/api/contact", format: "json" }}
128
+ * heading="Get In Touch"
129
+ * description="We'd love to hear from you."
130
+ * contactOptions={[
131
+ * { icon: "Phone", info: "+1 (555) 987-6543", href: "tel:+15559876543" },
132
+ * { icon: "Mail", info: "support@example.com", href: "mailto:support@example.com" },
133
+ * ]}
134
+ * formEngineSetup={{ formConfig: { endpoint: "/api/contact", format: "json" } }}
171
135
  * />
172
136
  * ```
173
137
  */
174
- declare function ContactCard({ heading, description, actions, actionsSlot, actionsClassName, contactOptions, contactOptionsSlot, formFields, successMessage, className, containerClassName, cardClassName, formClassName, successMessageClassName, errorMessageClassName, infoPanelClassName, headingClassName, descriptionClassName, contactOptionsClassName, background, spacing, pattern, patternOpacity, formConfig, onSubmit, onSuccess, onError, }: ContactCardProps): React.JSX.Element;
138
+ declare function ContactCard({ heading, description, buttonText, buttonIcon, contactOptions, contactOptionsSlot, socialLinks, socialLinksSlot, formEngineSetup, className, containerClassName, cardClassName, cardContentClassName, infoPanelClassName, headingClassName, descriptionClassName, contactOptionsClassName, socialLinksClassName, gridClassName, background, spacing, pattern, patternOpacity, }: ContactCardProps): React.JSX.Element;
175
139
 
176
140
  export { ContactCard, type ContactCardProps };