@super-ic/web-patterns 0.0.0-oidc-bootstrap.0 → 0.1.9
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 +309 -1
- package/contracts/contact-section.json +23 -0
- package/contracts/neighborhood-landing.json +162 -0
- package/contracts/public-content-refinement.json +38 -0
- package/css/case-study-card.css +29 -0
- package/css/contact-section.css +39 -0
- package/css/neighborhood-landing.css +143 -0
- package/css/powered-by-superic.css +33 -0
- package/css/public-content.css +187 -0
- package/css/tailwind.css +6 -0
- package/dist/case-study-card-types.d.ts +16 -0
- package/dist/case-study-card-types.js +1 -0
- package/dist/case-study-card.d.ts +66 -0
- package/dist/case-study-card.js +72 -0
- package/dist/contact-form.d.ts +56 -0
- package/dist/contact-form.js +75 -0
- package/dist/contact-section.d.ts +23 -0
- package/dist/contact-section.js +11 -0
- package/dist/editorial-image.d.ts +10 -0
- package/dist/editorial-image.js +18 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +6 -0
- package/dist/neighborhood-landing.d.ts +129 -0
- package/dist/neighborhood-landing.js +26 -0
- package/dist/powered-by-superic.d.ts +10 -0
- package/dist/powered-by-superic.js +20 -0
- package/dist/public-content.d.ts +152 -0
- package/dist/public-content.js +48 -0
- package/dist/surface-radius.d.ts +20 -0
- package/dist/surface-radius.js +30 -0
- package/package.json +50 -8
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useId, useRef } from "react";
|
|
4
|
+
import { Button } from "@super-ic/primitives/button";
|
|
5
|
+
import { Card, CardContent } from "@super-ic/primitives/card";
|
|
6
|
+
import { Input } from "@super-ic/primitives/input";
|
|
7
|
+
import { Label } from "@super-ic/primitives/label";
|
|
8
|
+
import { Textarea } from "@super-ic/primitives/textarea";
|
|
9
|
+
function validateFields(fields) {
|
|
10
|
+
if (fields.length < 1 || fields.length > 8)
|
|
11
|
+
throw new Error("ContactSection fields must contain between 1 and 8 entries.");
|
|
12
|
+
const names = new Set();
|
|
13
|
+
for (const field of fields) {
|
|
14
|
+
if (!/^[a-z][a-z0-9_-]*$/.test(field.name))
|
|
15
|
+
throw new Error("ContactSection field names must be lowercase identifiers.");
|
|
16
|
+
if (names.has(field.name))
|
|
17
|
+
throw new Error("ContactSection field names must be unique.");
|
|
18
|
+
names.add(field.name);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Shared contact behavior; consumers own content, validation and transport. */
|
|
22
|
+
export function ContactForm({ formTitle, formDescription, headingLevel = 2, fields, values, state, submitLabel, submittingLabel, onValueChange, onSubmit, reset, honeypot, notice, }) {
|
|
23
|
+
validateFields(fields);
|
|
24
|
+
if (honeypot && (!/^[a-z][a-z0-9_-]*$/.test(honeypot.name) || fields.some(field => field.name === honeypot.name))) {
|
|
25
|
+
throw new Error("ContactForm honeypot must have a unique lowercase identifier.");
|
|
26
|
+
}
|
|
27
|
+
const instanceId = useId();
|
|
28
|
+
const formRef = useRef(null);
|
|
29
|
+
const successRef = useRef(null);
|
|
30
|
+
const previousState = useRef({ kind: state.kind, validationId: state.kind === "invalid" ? state.validationId : undefined });
|
|
31
|
+
const formHeadingId = `${instanceId}-form-heading`;
|
|
32
|
+
const FormHeading = headingLevel === 2 ? "h2" : "h3";
|
|
33
|
+
const isSubmitting = state.kind === "submitting";
|
|
34
|
+
const isUnavailable = state.kind === "unavailable";
|
|
35
|
+
const isInvalid = state.kind === "invalid";
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const shouldFocusInvalid = state.kind === "invalid" && (previousState.current.kind !== "invalid" || previousState.current.validationId !== state.validationId);
|
|
38
|
+
if (shouldFocusInvalid) {
|
|
39
|
+
formRef.current?.querySelector('[aria-invalid="true"]')?.focus();
|
|
40
|
+
}
|
|
41
|
+
if (state.kind === "success" && previousState.current.kind !== "success")
|
|
42
|
+
successRef.current?.focus();
|
|
43
|
+
if (state.kind === "idle" && previousState.current.kind === "success") {
|
|
44
|
+
formRef.current?.querySelector('.sic-contact-section__field [data-slot="input"], .sic-contact-section__field [data-slot="textarea"]')?.focus();
|
|
45
|
+
}
|
|
46
|
+
previousState.current = { kind: state.kind, validationId: state.kind === "invalid" ? state.validationId : undefined };
|
|
47
|
+
}, [state.kind, state.kind === "invalid" ? state.validationId : undefined]);
|
|
48
|
+
function handleSubmit(event) {
|
|
49
|
+
event.preventDefault();
|
|
50
|
+
if (isSubmitting || isUnavailable)
|
|
51
|
+
return;
|
|
52
|
+
onSubmit();
|
|
53
|
+
}
|
|
54
|
+
return (_jsx(Card, { className: "sic-contact-form sic-contact-section__card", children: _jsx(CardContent, { children: state.kind === "success" ? (_jsxs("div", { ref: successRef, className: "sic-contact-section__success", tabIndex: -1, role: "status", "aria-live": "polite", children: [_jsx(FormHeading, { id: formHeadingId, children: state.title }), _jsx("p", { children: state.message }), reset ? _jsx(Button, { type: "button", variant: "outline", onClick: reset.onAction, children: reset.label }) : null] })) : (_jsxs("form", { ref: formRef, className: "sic-contact-section__form", "aria-labelledby": formHeadingId, "aria-busy": isSubmitting || undefined, noValidate: true, onSubmit: handleSubmit, children: [_jsx(FormHeading, { id: formHeadingId, children: formTitle }), formDescription ? _jsx("p", { className: "sic-contact-section__hint", children: formDescription }) : null, isInvalid ? _jsx("p", { className: "sic-contact-section__message", role: "alert", children: state.message }) : null, state.kind === "failure" ? _jsx("p", { className: "sic-contact-section__message", role: "alert", children: state.message }) : null, isSubmitting || isUnavailable ? _jsx("p", { className: "sic-contact-section__message", role: "status", children: state.message }) : null, honeypot ? _jsxs("div", { hidden: true, "aria-hidden": "true", children: [_jsx(Label, { htmlFor: `${instanceId}-honeypot`, children: honeypot.label }), _jsx(Input, { id: `${instanceId}-honeypot`, name: honeypot.name, value: values[honeypot.name] ?? "", onChange: event => onValueChange(honeypot.name, event.target.value), tabIndex: -1, autoComplete: "off" })] }) : null, _jsxs("fieldset", { disabled: isUnavailable, children: [fields.map(field => {
|
|
55
|
+
const inputId = `${instanceId}-field-${field.name}`;
|
|
56
|
+
const hintId = `${inputId}-hint`;
|
|
57
|
+
const errorId = `${inputId}-error`;
|
|
58
|
+
const error = isInvalid ? state.errors[field.name] : undefined;
|
|
59
|
+
const describedBy = [field.hint ? hintId : undefined, error ? errorId : undefined].filter(Boolean).join(" ") || undefined;
|
|
60
|
+
const common = {
|
|
61
|
+
id: inputId,
|
|
62
|
+
name: field.name,
|
|
63
|
+
value: values[field.name] ?? "",
|
|
64
|
+
required: field.required,
|
|
65
|
+
autoComplete: field.autoComplete,
|
|
66
|
+
placeholder: field.placeholder,
|
|
67
|
+
maxLength: field.maxLength,
|
|
68
|
+
readOnly: isSubmitting,
|
|
69
|
+
"aria-invalid": error ? true : undefined,
|
|
70
|
+
"aria-describedby": describedBy,
|
|
71
|
+
onChange: (event) => onValueChange(field.name, event.target.value),
|
|
72
|
+
};
|
|
73
|
+
return _jsxs("div", { className: "sic-contact-section__field", children: [_jsxs(Label, { htmlFor: inputId, children: [field.label, field.required ? _jsx("span", { "aria-hidden": "true", children: " *" }) : null] }), field.kind === "textarea" ? _jsx(Textarea, { ...common, rows: field.rows }) : _jsx(Input, { ...common, type: field.kind }), field.hint ? _jsx("p", { id: hintId, className: "sic-contact-section__hint", children: field.hint }) : null, error ? _jsx("p", { id: errorId, className: "sic-contact-section__error", children: error }) : null] }, field.name);
|
|
74
|
+
}), notice ? _jsx("p", { className: "sic-contact-section__hint", children: notice }) : null, _jsx(Button, { type: "submit", disabled: isSubmitting || isUnavailable, children: isSubmitting ? submittingLabel : submitLabel })] })] })) }) }));
|
|
75
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ContactFormProps } from "./contact-form.js";
|
|
2
|
+
export { ContactForm, type ContactFormProps, type ContactSectionField, type ContactSectionState } from "./contact-form.js";
|
|
3
|
+
export type ContactSectionProps = Omit<ContactFormProps, "headingLevel"> & {
|
|
4
|
+
intro: {
|
|
5
|
+
eyebrow: string;
|
|
6
|
+
title: string;
|
|
7
|
+
description: string;
|
|
8
|
+
};
|
|
9
|
+
headingLevel?: 1 | 2;
|
|
10
|
+
details?: readonly {
|
|
11
|
+
label: string;
|
|
12
|
+
text: string;
|
|
13
|
+
href?: string;
|
|
14
|
+
}[];
|
|
15
|
+
media?: {
|
|
16
|
+
src: string;
|
|
17
|
+
alt: string;
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
caption?: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export declare function ContactSection({ intro, headingLevel, details, media, ...form }: ContactSectionProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from "react";
|
|
4
|
+
import { ContactForm } from "./contact-form.js";
|
|
5
|
+
import { EditorialImage } from "./editorial-image.js";
|
|
6
|
+
export { ContactForm } from "./contact-form.js";
|
|
7
|
+
export function ContactSection({ intro, headingLevel = 2, details, media, ...form }) {
|
|
8
|
+
const introHeadingId = `${useId()}-intro-heading`;
|
|
9
|
+
const IntroHeading = headingLevel === 1 ? "h1" : "h2";
|
|
10
|
+
return (_jsx("section", { className: "sic-contact-section", "aria-labelledby": introHeadingId, children: _jsxs("div", { className: "sic-contact-section__layout", children: [_jsxs("div", { className: "sic-contact-section__introduction", children: [_jsx("p", { className: "sic-contact-section__eyebrow", children: intro.eyebrow }), _jsx(IntroHeading, { id: introHeadingId, children: intro.title }), _jsx("p", { className: "sic-contact-section__description", children: intro.description }), media ? _jsxs("figure", { className: "sic-contact-section__media", children: [_jsx(EditorialImage, { media: media }), media.caption ? _jsx("figcaption", { children: media.caption }) : null] }) : null, details?.length ? (_jsx("dl", { className: "sic-contact-section__details", children: details.map(detail => _jsxs("div", { children: [_jsx("dt", { children: detail.label }), _jsx("dd", { children: detail.href ? _jsx("a", { href: detail.href, children: detail.text }) : detail.text })] }, `${detail.label}-${detail.text}`)) })) : null] }), _jsx(ContactForm, { ...form, headingLevel: headingLevel === 1 ? 2 : 3 })] }) }));
|
|
11
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ImgHTMLAttributes } from "react";
|
|
2
|
+
import type { NeighborhoodLandingMedia } from "./neighborhood-landing.js";
|
|
3
|
+
type EditorialImageProps = {
|
|
4
|
+
media: NeighborhoodLandingMedia;
|
|
5
|
+
className?: string;
|
|
6
|
+
priority?: boolean;
|
|
7
|
+
} & Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "alt" | "width" | "height" | "className">;
|
|
8
|
+
/** Informative media fails locally. A changed source remounts a clean image state. */
|
|
9
|
+
export declare function EditorialImage(props: EditorialImageProps): import("react").JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useState } from "react";
|
|
4
|
+
function EditorialImageInstance({ media, className, priority = false, ...props }) {
|
|
5
|
+
const [failed, setFailed] = useState(false);
|
|
6
|
+
// A cached or early network failure can occur before hydration attaches onError.
|
|
7
|
+
const inspectImage = useCallback((image) => {
|
|
8
|
+
if (image?.complete && image.naturalWidth === 0)
|
|
9
|
+
setFailed(true);
|
|
10
|
+
}, []);
|
|
11
|
+
if (failed)
|
|
12
|
+
return null;
|
|
13
|
+
return _jsx("img", { ...props, ref: inspectImage, className: className, src: media.src, alt: media.alt, width: media.width, height: media.height, loading: priority ? undefined : "lazy", fetchPriority: priority ? "high" : undefined, onError: () => setFailed(true) });
|
|
14
|
+
}
|
|
15
|
+
/** Informative media fails locally. A changed source remounts a clean image state. */
|
|
16
|
+
export function EditorialImage(props) {
|
|
17
|
+
return _jsx(EditorialImageInstance, { ...props }, props.media.src);
|
|
18
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { CaseStudyCard, CaseStudyCardShell, type CaseStudyCardAccess, type CaseStudyCardDensity, type CaseStudyCardIdentityKind, type CaseStudyCardOpenControlProps, type CaseStudyCardPresentation, type CaseStudyCardProps, } from "./case-study-card.js";
|
|
2
|
+
export { SURFACE_RADIUS_CLASSES, SURFACE_RADIUS_TOKENS, nestedSurfaceRadius, nestedSurfaceRadiusValue, } from "./surface-radius.js";
|
|
3
|
+
export type { CaseStudyCardProductAccess, CaseStudyCardProvenance, LiveCaseStudyCardSlug, } from "./case-study-card-types.js";
|
|
4
|
+
export { NeighborhoodLanding, type NeighborhoodLandingProps, type NeighborhoodLandingLink, type NeighborhoodLandingStep, type NeighborhoodLandingExample, type NeighborhoodLandingJoin, type NeighborhoodLandingMedia } from "./neighborhood-landing.js";
|
|
5
|
+
export { ContactSection, ContactForm, type ContactFormProps, type ContactSectionProps, type ContactSectionField, type ContactSectionState } from "./contact-section.js";
|
|
6
|
+
export { PublicSiteFrame, PublicArticle, HelpDirectory, PublicAvailability, PublicDocumentDialog, type PublicDocumentDialogProps, type PublicContentLink, type PublicContentNavigation, type PublicSiteFrameProps, type PublicArticleSection, type PublicArticleProps, type HelpTopic, type HelpTopicGroup, type HelpDirectoryProps, type PublicAvailabilityProps, } from "./public-content.js";
|
|
7
|
+
export { PoweredBySuperIC, type PoweredBySuperICProps } from "./powered-by-superic.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { CaseStudyCard, CaseStudyCardShell, } from "./case-study-card.js";
|
|
2
|
+
export { SURFACE_RADIUS_CLASSES, SURFACE_RADIUS_TOKENS, nestedSurfaceRadius, nestedSurfaceRadiusValue, } from "./surface-radius.js";
|
|
3
|
+
export { NeighborhoodLanding } from "./neighborhood-landing.js";
|
|
4
|
+
export { ContactSection, ContactForm } from "./contact-section.js";
|
|
5
|
+
export { PublicSiteFrame, PublicArticle, HelpDirectory, PublicAvailability, PublicDocumentDialog, } from "./public-content.js";
|
|
6
|
+
export { PoweredBySuperIC } from "./powered-by-superic.js";
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { type AnchorHTMLAttributes, type ReactElement, type ReactNode } from "react";
|
|
2
|
+
export interface NeighborhoodLandingLink {
|
|
3
|
+
label: string;
|
|
4
|
+
href: string;
|
|
5
|
+
}
|
|
6
|
+
export interface NeighborhoodLandingStep {
|
|
7
|
+
id: string;
|
|
8
|
+
title: string;
|
|
9
|
+
body: string; /** Consumer-supplied functional icon; the recipe never assigns product meaning. */
|
|
10
|
+
icon?: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
export interface NeighborhoodLandingMedia {
|
|
13
|
+
src: string;
|
|
14
|
+
alt: string;
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
}
|
|
18
|
+
export interface NeighborhoodLandingExample {
|
|
19
|
+
id: string;
|
|
20
|
+
tabLabel: string;
|
|
21
|
+
label: string;
|
|
22
|
+
title: string;
|
|
23
|
+
context: string;
|
|
24
|
+
explanation: string;
|
|
25
|
+
media?: NeighborhoodLandingMedia;
|
|
26
|
+
steps: readonly [NeighborhoodLandingStep, NeighborhoodLandingStep, NeighborhoodLandingStep];
|
|
27
|
+
}
|
|
28
|
+
export type NeighborhoodLandingJoin = {
|
|
29
|
+
state: "available";
|
|
30
|
+
action: NeighborhoodLandingLink;
|
|
31
|
+
note: string;
|
|
32
|
+
} | {
|
|
33
|
+
state: "unavailable";
|
|
34
|
+
title: string;
|
|
35
|
+
description: string;
|
|
36
|
+
help: NeighborhoodLandingLink;
|
|
37
|
+
};
|
|
38
|
+
/** Presentation only. The consumer owns eligibility, activation, routes and every claim. */
|
|
39
|
+
export interface NeighborhoodLandingProps {
|
|
40
|
+
brand: {
|
|
41
|
+
name: string;
|
|
42
|
+
mark: ReactNode;
|
|
43
|
+
homeHref: string;
|
|
44
|
+
};
|
|
45
|
+
navigation: {
|
|
46
|
+
howItWorks: string;
|
|
47
|
+
joining: string;
|
|
48
|
+
signIn?: NeighborhoodLandingLink;
|
|
49
|
+
/** Real product destinations. Omitting these preserves the historical in-page anchors. */
|
|
50
|
+
links?: readonly NeighborhoodLandingLink[];
|
|
51
|
+
menuLabel?: string;
|
|
52
|
+
closeMenuLabel?: string;
|
|
53
|
+
navigationLabel?: string;
|
|
54
|
+
skipLabel?: string;
|
|
55
|
+
};
|
|
56
|
+
hero: {
|
|
57
|
+
eyebrow: string;
|
|
58
|
+
title: string;
|
|
59
|
+
description: string;
|
|
60
|
+
joinLabel: string;
|
|
61
|
+
/** Explicit product destination; omitted values retain the membership anchor. */
|
|
62
|
+
joinHref?: string;
|
|
63
|
+
availability: string;
|
|
64
|
+
media: NeighborhoodLandingMedia & {
|
|
65
|
+
caption: string; /** Preserve the complete scene when cropping would lose people or the shared object. */
|
|
66
|
+
fit?: "cover" | "contain";
|
|
67
|
+
};
|
|
68
|
+
/** Product-owned rich media, such as a video with its own accessible controls and fallback. */
|
|
69
|
+
heroMedia?: ReactNode;
|
|
70
|
+
example: {
|
|
71
|
+
label: string;
|
|
72
|
+
title: string;
|
|
73
|
+
detail: string;
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
possibilities?: {
|
|
77
|
+
eyebrow: string;
|
|
78
|
+
title: string;
|
|
79
|
+
description: string;
|
|
80
|
+
items: readonly {
|
|
81
|
+
id: string;
|
|
82
|
+
title: string;
|
|
83
|
+
description: string;
|
|
84
|
+
label: string;
|
|
85
|
+
media: NeighborhoodLandingMedia;
|
|
86
|
+
icon?: ReactNode;
|
|
87
|
+
}[];
|
|
88
|
+
};
|
|
89
|
+
sharing: {
|
|
90
|
+
eyebrow: string;
|
|
91
|
+
title: string;
|
|
92
|
+
description: string;
|
|
93
|
+
tabsLabel: string;
|
|
94
|
+
examples: readonly [NeighborhoodLandingExample, NeighborhoodLandingExample];
|
|
95
|
+
quiet: {
|
|
96
|
+
title: string;
|
|
97
|
+
description: string;
|
|
98
|
+
icon?: ReactNode;
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
membership: {
|
|
102
|
+
eyebrow: string;
|
|
103
|
+
title: string;
|
|
104
|
+
description: string;
|
|
105
|
+
methodsLabel: string;
|
|
106
|
+
methods: readonly NeighborhoodLandingStep[];
|
|
107
|
+
limitation: string;
|
|
108
|
+
join: NeighborhoodLandingJoin;
|
|
109
|
+
};
|
|
110
|
+
essentials: {
|
|
111
|
+
title: string;
|
|
112
|
+
description: string;
|
|
113
|
+
items: readonly {
|
|
114
|
+
id: string;
|
|
115
|
+
question: string;
|
|
116
|
+
answer: ReactNode;
|
|
117
|
+
}[];
|
|
118
|
+
};
|
|
119
|
+
footer: {
|
|
120
|
+
independence: string;
|
|
121
|
+
links: readonly NeighborhoodLandingLink[];
|
|
122
|
+
note: string;
|
|
123
|
+
};
|
|
124
|
+
/** Receives all semantic, focus and style props. Forward them to the framework link. */
|
|
125
|
+
renderLink?: (props: AnchorHTMLAttributes<HTMLAnchorElement> & {
|
|
126
|
+
href: string;
|
|
127
|
+
}) => ReactElement;
|
|
128
|
+
}
|
|
129
|
+
export declare function NeighborhoodLanding({ brand, navigation, hero, possibilities, sharing, membership, essentials, footer, renderLink }: NeighborhoodLandingProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useId, useState } from "react";
|
|
4
|
+
import { buttonVariants } from "@super-ic/primitives/button";
|
|
5
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@super-ic/primitives/tabs";
|
|
6
|
+
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@super-ic/primitives/accordion";
|
|
7
|
+
import { Button } from "@super-ic/primitives/button";
|
|
8
|
+
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@super-ic/primitives/sheet";
|
|
9
|
+
import { Icon } from "@super-ic/icon-contracts";
|
|
10
|
+
import { EditorialImage } from "./editorial-image.js";
|
|
11
|
+
export function NeighborhoodLanding({ brand, navigation, hero, possibilities, sharing, membership, essentials, footer, renderLink }) {
|
|
12
|
+
const instance = useId();
|
|
13
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
14
|
+
const mainId = `${instance}-main`;
|
|
15
|
+
const sharingId = `${instance}-sharing`;
|
|
16
|
+
const joinId = `${instance}-join`;
|
|
17
|
+
const link = (props) => renderLink ? renderLink(props) : _jsx("a", { ...props });
|
|
18
|
+
const navigationLinks = navigation.links ?? [
|
|
19
|
+
{ label: navigation.howItWorks, href: `#${sharingId}` },
|
|
20
|
+
{ label: navigation.joining, href: `#${joinId}` },
|
|
21
|
+
];
|
|
22
|
+
if (possibilities && (possibilities.items.some(item => item.id.trim().length === 0) || new Set(possibilities.items.map(item => item.id)).size !== possibilities.items.length))
|
|
23
|
+
throw new Error("NeighborhoodLanding possibilities require unique nonempty IDs.");
|
|
24
|
+
return (_jsxs("div", { className: "sic-neighborhood", "data-slot": "neighborhood-landing", children: [_jsx("a", { className: "sic-neighborhood__skip", href: `#${mainId}`, children: navigation.skipLabel ?? "Skip to content" }), _jsxs("header", { className: "sic-neighborhood__header sic-neighborhood__width", children: [link({ href: brand.homeHref, className: "sic-neighborhood__brand", "aria-label": brand.name, children: brand.mark }), _jsxs("nav", { "aria-label": navigation.navigationLabel ?? `${brand.name} navigation`, className: "sic-neighborhood__nav sic-neighborhood__nav--desktop", children: [navigationLinks.map(item => _jsx("span", { children: link({ href: item.href, children: item.label }) }, item.href)), navigation.signIn ? link({ href: navigation.signIn.href, className: "sic-neighborhood__account-link", children: navigation.signIn.label }) : null] }), _jsxs(Sheet, { open: menuOpen, onOpenChange: setMenuOpen, children: [_jsxs(SheetTrigger, { render: _jsx(Button, { type: "button", variant: "outline", className: "sic-neighborhood__menu-trigger" }), children: [_jsx(Icon, { name: "navigation.menu", decorative: true, size: 18 }), " ", _jsx("span", { children: navigation.menuLabel ?? "Menu" })] }), _jsxs(SheetContent, { closeLabel: navigation.closeMenuLabel ?? "Close menu", side: "right", className: "sic-neighborhood__menu-content", children: [_jsx(SheetHeader, { children: _jsx(SheetTitle, { children: brand.name }) }), _jsxs("nav", { "aria-label": navigation.navigationLabel ?? `${brand.name} navigation`, className: "sic-neighborhood__nav sic-neighborhood__nav--mobile", onClickCapture: event => { if (event.target instanceof Element && event.target.closest("a[href]"))
|
|
25
|
+
setMenuOpen(false); }, children: [navigationLinks.map(item => _jsx("span", { children: link({ href: item.href, children: item.label }) }, item.href)), navigation.signIn ? link({ href: navigation.signIn.href, className: "sic-neighborhood__account-link", children: navigation.signIn.label }) : null] })] })] })] }), _jsxs("main", { id: mainId, tabIndex: -1, className: "sic-neighborhood__main", children: [_jsxs("section", { className: "sic-neighborhood__hero sic-neighborhood__width", "aria-labelledby": `${instance}-title`, children: [_jsxs("div", { className: "sic-neighborhood__hero-copy", children: [_jsx("p", { className: "sic-neighborhood__eyebrow", children: hero.eyebrow }), _jsx("h1", { id: `${instance}-title`, children: hero.title }), _jsx("p", { className: "sic-neighborhood__intro", children: hero.description }), link({ className: `${buttonVariants({ size: "lg" })} sic-neighborhood__join-action`, href: hero.joinHref ?? `#${joinId}`, children: _jsxs(_Fragment, { children: [hero.joinLabel, _jsx(Icon, { name: "navigation.forward", decorative: true, size: 18 })] }) }), _jsx("p", { className: "sic-neighborhood__availability", children: hero.availability })] }), _jsxs("figure", { className: "sic-neighborhood__hero-figure", children: [hero.heroMedia ? _jsx("div", { className: "sic-neighborhood__hero-media", children: hero.heroMedia }) : _jsx(EditorialImage, { className: "sic-neighborhood__hero-image", "data-fit": hero.media.fit ?? "cover", media: hero.media, priority: true }), _jsxs("div", { className: "sic-neighborhood__example-note", children: [_jsx("span", { className: "sic-neighborhood__eyebrow", children: hero.example.label }), _jsx("p", { children: hero.example.title }), _jsx("span", { children: hero.example.detail })] }), _jsx("figcaption", { children: hero.media.caption })] })] }), possibilities ? _jsxs("section", { className: "sic-neighborhood__possibilities sic-neighborhood__width", "aria-labelledby": `${instance}-possibilities-title`, children: [_jsxs("div", { className: "sic-neighborhood__section-intro", children: [_jsx("p", { className: "sic-neighborhood__eyebrow", children: possibilities.eyebrow }), _jsx("h2", { id: `${instance}-possibilities-title`, children: possibilities.title }), _jsx("p", { children: possibilities.description })] }), _jsx("ul", { children: possibilities.items.map(item => _jsxs("li", { children: [item.icon ? _jsx("span", { className: "sic-neighborhood__section-icon", "aria-hidden": "true", children: item.icon }) : null, _jsx(EditorialImage, { media: item.media, className: "sic-neighborhood__possibility-media" }), _jsx("p", { className: "sic-neighborhood__eyebrow", children: item.label }), _jsx("h3", { children: item.title }), _jsx("p", { children: item.description })] }, item.id)) })] }) : null, _jsxs("section", { id: sharingId, tabIndex: -1, className: "sic-neighborhood__sharing sic-neighborhood__width", "aria-labelledby": `${instance}-sharing-title`, children: [_jsxs("div", { className: "sic-neighborhood__section-intro", children: [_jsx("p", { className: "sic-neighborhood__eyebrow", children: sharing.eyebrow }), _jsx("h2", { id: `${instance}-sharing-title`, children: sharing.title }), _jsx("p", { children: sharing.description })] }), _jsxs(Tabs, { defaultValue: sharing.examples[0].id, className: "sic-neighborhood__examples", children: [_jsx(TabsList, { activateOnFocus: true, "aria-label": sharing.tabsLabel, className: "sic-neighborhood__tabs", children: sharing.examples.map(example => _jsx(TabsTrigger, { value: example.id, children: example.tabLabel }, example.id)) }), sharing.examples.map(example => (_jsxs(TabsContent, { value: example.id, className: "sic-neighborhood__example-panel", children: [_jsxs("div", { className: "sic-neighborhood__example-message", children: [example.media ? _jsx(EditorialImage, { media: example.media, className: "sic-neighborhood__example-media" }) : null, _jsx("span", { className: "sic-neighborhood__eyebrow", children: example.label }), _jsx("h3", { children: example.title }), _jsx("p", { className: "sic-neighborhood__example-context", children: example.context }), _jsx("p", { children: example.explanation })] }), _jsx("ol", { className: "sic-neighborhood__steps", children: example.steps.map((step, index) => _jsxs("li", { children: [_jsx("span", { className: "sic-neighborhood__step-number", "aria-hidden": "true", children: step.icon ?? `0${index + 1}` }), _jsxs("div", { children: [_jsx("h3", { children: step.title }), _jsx("p", { children: step.body })] })] }, step.id)) })] }, example.id)))] }), _jsxs("div", { className: "sic-neighborhood__quiet", children: [_jsx("span", { "aria-hidden": "true", children: sharing.quiet.icon ?? _jsx(Icon, { name: "state.empty", decorative: true, size: 20 }) }), _jsxs("div", { children: [_jsx("h3", { children: sharing.quiet.title }), _jsx("p", { children: sharing.quiet.description })] })] })] }), _jsx("section", { id: joinId, tabIndex: -1, className: "sic-neighborhood__membership", "aria-labelledby": `${instance}-membership-title`, children: _jsxs("div", { className: "sic-neighborhood__membership-grid sic-neighborhood__width", children: [_jsxs("div", { className: "sic-neighborhood__section-intro", children: [_jsx("p", { className: "sic-neighborhood__eyebrow", children: membership.eyebrow }), _jsx("h2", { id: `${instance}-membership-title`, children: membership.title }), _jsx("p", { children: membership.description }), _jsx("div", { className: "sic-neighborhood__join-state", "data-join-state": membership.join.state, children: membership.join.state === "available" ? _jsxs(_Fragment, { children: [link({ href: membership.join.action.href, className: `${buttonVariants({ size: "lg" })} sic-neighborhood__join-action`, children: _jsxs(_Fragment, { children: [membership.join.action.label, _jsx(Icon, { name: "navigation.forward", decorative: true, size: 18 })] }) }), _jsx("p", { children: membership.join.note })] }) : _jsxs(_Fragment, { children: [_jsx("h3", { children: membership.join.title }), _jsx("p", { children: membership.join.description }), link({ href: membership.join.help.href, className: "sic-neighborhood__text-link", children: membership.join.help.label })] }) })] }), _jsxs("div", { className: "sic-neighborhood__residency", children: [_jsx("h3", { children: membership.methodsLabel }), _jsx("ul", { children: membership.methods.map(method => _jsxs("li", { children: [method.icon ? _jsx("span", { className: "sic-neighborhood__method-icon", "aria-hidden": "true", children: method.icon }) : null, _jsx("h4", { children: method.title }), _jsx("p", { children: method.body })] }, method.id)) }), _jsx("p", { className: "sic-neighborhood__limitation", children: membership.limitation })] })] }) }), _jsxs("section", { className: "sic-neighborhood__essentials sic-neighborhood__width", "aria-labelledby": `${instance}-essentials-title`, children: [_jsxs("div", { className: "sic-neighborhood__section-intro", children: [_jsx("h2", { id: `${instance}-essentials-title`, children: essentials.title }), _jsx("p", { children: essentials.description })] }), _jsx(Accordion, { className: "sic-neighborhood__faq", children: essentials.items.map(item => _jsxs(AccordionItem, { value: item.id, children: [_jsx(AccordionTrigger, { children: item.question }), _jsx(AccordionContent, { children: item.answer })] }, item.id)) })] })] }), _jsxs("footer", { className: "sic-neighborhood__footer sic-neighborhood__width", children: [_jsxs("div", { children: [_jsx("p", { className: "sic-neighborhood__footer-brand", children: brand.name }), _jsx("p", { children: footer.independence })] }), _jsx("nav", { "aria-label": `${brand.name} information`, children: footer.links.map(item => _jsx("span", { children: link({ href: item.href, children: item.label }) }, item.href)) }), _jsx("p", { className: "sic-neighborhood__footer-note", children: footer.note })] })] }));
|
|
26
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type PoweredBySuperICProps = {
|
|
2
|
+
href?: string;
|
|
3
|
+
variant?: "auto" | "light" | "dark";
|
|
4
|
+
size?: "sm" | "md";
|
|
5
|
+
label?: string;
|
|
6
|
+
source?: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
};
|
|
9
|
+
/** Import @super-ic/web-patterns/powered-by-superic.css with foundation tokens. */
|
|
10
|
+
export declare function PoweredBySuperIC({ href, variant, size, label, source, className }: PoweredBySuperICProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { SuperICLogo } from "@super-ic/brand-contracts/superic-logo";
|
|
3
|
+
function referralUrl(href, source) {
|
|
4
|
+
if (href !== href.trim() || /[\\\u0000-\u001f\u007f]/.test(href))
|
|
5
|
+
throw new Error("PoweredBySuperIC href must be an HTTP(S) or root-relative URL.");
|
|
6
|
+
const relative = href.startsWith("/") && !href.startsWith("//");
|
|
7
|
+
if (!relative && !/^https?:\/\//i.test(href))
|
|
8
|
+
throw new Error("PoweredBySuperIC href must be an HTTP(S) or root-relative URL.");
|
|
9
|
+
const url = new URL(href, "https://superic.invalid");
|
|
10
|
+
if (!["http:", "https:"].includes(url.protocol))
|
|
11
|
+
throw new Error("PoweredBySuperIC href must be an HTTP(S) or root-relative URL.");
|
|
12
|
+
if (!source)
|
|
13
|
+
return href;
|
|
14
|
+
url.searchParams.set("ref", source);
|
|
15
|
+
return relative ? `${url.pathname}${url.search}${url.hash}` : url.href;
|
|
16
|
+
}
|
|
17
|
+
/** Import @super-ic/web-patterns/powered-by-superic.css with foundation tokens. */
|
|
18
|
+
export function PoweredBySuperIC({ href = "https://superic.cc", variant = "auto", size = "sm", label = "Powered by", source, className }) {
|
|
19
|
+
return _jsxs("a", { href: referralUrl(href, source), target: "_blank", rel: "noopener noreferrer", "data-slot": "powered-by-superic", "data-variant": variant, "data-size": size, "aria-label": `${label} SuperIC`.trim(), className: ["sic-powered-by-superic", className].filter(Boolean).join(" "), children: [_jsx("span", { "aria-hidden": "true", children: label }), _jsx(SuperICLogo, { variant: variant, height: size === "sm" ? 14 : 18 })] });
|
|
20
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { type MouseEvent, type ReactNode } from "react";
|
|
2
|
+
import type { NeighborhoodLandingMedia } from "./neighborhood-landing.js";
|
|
3
|
+
export type PublicContentLink = Readonly<{
|
|
4
|
+
label: string;
|
|
5
|
+
href: string;
|
|
6
|
+
onNavigate?: (event: MouseEvent<HTMLAnchorElement>) => void;
|
|
7
|
+
}>;
|
|
8
|
+
export type PublicContentNavigation = PublicContentLink & Readonly<{
|
|
9
|
+
id: string;
|
|
10
|
+
current?: boolean;
|
|
11
|
+
}>;
|
|
12
|
+
export type PublicSiteFrameProps = Readonly<{
|
|
13
|
+
brand: {
|
|
14
|
+
name: string;
|
|
15
|
+
home: PublicContentLink;
|
|
16
|
+
mark: ReactNode;
|
|
17
|
+
};
|
|
18
|
+
navigation: readonly PublicContentNavigation[];
|
|
19
|
+
navigationLabel: string;
|
|
20
|
+
menuLabel?: string;
|
|
21
|
+
closeMenuLabel?: string;
|
|
22
|
+
skipLabel: string;
|
|
23
|
+
footer: {
|
|
24
|
+
description: string;
|
|
25
|
+
navigationLabel: string;
|
|
26
|
+
links: readonly PublicContentLink[];
|
|
27
|
+
note?: string;
|
|
28
|
+
};
|
|
29
|
+
/** Marketing may use wider media while reading defaults to a 69rem measure. */
|
|
30
|
+
width?: "reading" | "marketing";
|
|
31
|
+
children: ReactNode;
|
|
32
|
+
}>;
|
|
33
|
+
/** One public main landmark. The composed article, directory or boundary owns h1. */
|
|
34
|
+
export declare function PublicSiteFrame({ brand, navigation, navigationLabel, menuLabel, closeMenuLabel, skipLabel, footer, width, children }: PublicSiteFrameProps): import("react").JSX.Element;
|
|
35
|
+
export type PublicArticleSection = Readonly<{
|
|
36
|
+
/** Stable slug supplied by the consumer; used for real fragment navigation. */
|
|
37
|
+
id: string;
|
|
38
|
+
title: string;
|
|
39
|
+
media?: NeighborhoodLandingMedia;
|
|
40
|
+
/** Trusted React content. Use paragraphs, lists, links and h3 subheadings. */
|
|
41
|
+
body: ReactNode;
|
|
42
|
+
}>;
|
|
43
|
+
export type PublicArticleProps = Readonly<{
|
|
44
|
+
/** Optional document-scoped slug. Provide a unique value when stable deep links are required. */
|
|
45
|
+
anchorId?: string;
|
|
46
|
+
title: string;
|
|
47
|
+
introduction: string;
|
|
48
|
+
back?: PublicContentLink;
|
|
49
|
+
metadata?: Readonly<{
|
|
50
|
+
label: string;
|
|
51
|
+
dateTime?: string;
|
|
52
|
+
}>;
|
|
53
|
+
notice?: Readonly<{
|
|
54
|
+
title: string;
|
|
55
|
+
body: string;
|
|
56
|
+
link?: PublicContentLink;
|
|
57
|
+
}>;
|
|
58
|
+
contentsLabel: string;
|
|
59
|
+
sections: readonly PublicArticleSection[];
|
|
60
|
+
related?: Readonly<{
|
|
61
|
+
title: string;
|
|
62
|
+
links: readonly PublicContentLink[];
|
|
63
|
+
}>;
|
|
64
|
+
/** Opt-in editorial layout for marketing pages; default remains a document reader. */
|
|
65
|
+
presentation?: "document" | "marketing";
|
|
66
|
+
}>;
|
|
67
|
+
/** A readable document, not a legal-readiness decision or an acknowledgment form. */
|
|
68
|
+
export declare function PublicArticle({ anchorId, title, introduction, back, metadata, notice, contentsLabel, sections, related, presentation }: PublicArticleProps): import("react").JSX.Element;
|
|
69
|
+
export type HelpTopic = PublicContentLink & Readonly<{
|
|
70
|
+
id: string;
|
|
71
|
+
description: string;
|
|
72
|
+
}>;
|
|
73
|
+
export type HelpTopicGroup = Readonly<{
|
|
74
|
+
id: string;
|
|
75
|
+
title: string;
|
|
76
|
+
topics: readonly HelpTopic[];
|
|
77
|
+
}>;
|
|
78
|
+
export type HelpDirectoryProps = Readonly<{
|
|
79
|
+
title: string;
|
|
80
|
+
introduction: string;
|
|
81
|
+
search: {
|
|
82
|
+
label: string;
|
|
83
|
+
value: string;
|
|
84
|
+
placeholder?: string;
|
|
85
|
+
/** Optional native GET destination for pre-hydration search. */
|
|
86
|
+
action?: string;
|
|
87
|
+
/** Native query parameter name. Defaults to q. */
|
|
88
|
+
queryName?: string;
|
|
89
|
+
submitLabel: string;
|
|
90
|
+
onChange: (value: string) => void;
|
|
91
|
+
onSubmit: () => void;
|
|
92
|
+
};
|
|
93
|
+
results: {
|
|
94
|
+
state: "ready";
|
|
95
|
+
groups: readonly HelpTopicGroup[];
|
|
96
|
+
summary: string;
|
|
97
|
+
} | {
|
|
98
|
+
state: "empty";
|
|
99
|
+
title: string;
|
|
100
|
+
description: string;
|
|
101
|
+
reset: {
|
|
102
|
+
label: string;
|
|
103
|
+
onAction: () => void;
|
|
104
|
+
href?: string;
|
|
105
|
+
};
|
|
106
|
+
} | {
|
|
107
|
+
state: "loading";
|
|
108
|
+
message: string;
|
|
109
|
+
} | {
|
|
110
|
+
state: "failed";
|
|
111
|
+
message: string;
|
|
112
|
+
retry: {
|
|
113
|
+
label: string;
|
|
114
|
+
onAction: () => void;
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
contact: {
|
|
118
|
+
title: string;
|
|
119
|
+
description: string;
|
|
120
|
+
action: PublicContentLink;
|
|
121
|
+
};
|
|
122
|
+
}>;
|
|
123
|
+
/** Controlled search results. Products own search, stale-result suppression and help routing. */
|
|
124
|
+
export declare function HelpDirectory({ title, introduction, search, results, contact }: HelpDirectoryProps): import("react").JSX.Element;
|
|
125
|
+
export type PublicAvailabilityProps = Readonly<{
|
|
126
|
+
value: {
|
|
127
|
+
state: "loading";
|
|
128
|
+
title: string;
|
|
129
|
+
message: string;
|
|
130
|
+
} | {
|
|
131
|
+
state: "closed" | "restricted" | "sign-in" | "unavailable";
|
|
132
|
+
title: string;
|
|
133
|
+
description: string;
|
|
134
|
+
detail?: string;
|
|
135
|
+
primary?: PublicContentLink;
|
|
136
|
+
secondary: readonly PublicContentLink[];
|
|
137
|
+
};
|
|
138
|
+
}>;
|
|
139
|
+
/** Public-safe reason and recovery routes. A state label never grants access. */
|
|
140
|
+
export declare function PublicAvailability({ value }: PublicAvailabilityProps): import("react").JSX.Element;
|
|
141
|
+
export type PublicDocumentDialogProps = Readonly<{
|
|
142
|
+
open: boolean;
|
|
143
|
+
onOpenChange: (open: boolean) => void;
|
|
144
|
+
title: string;
|
|
145
|
+
description: string;
|
|
146
|
+
contentLabel: string;
|
|
147
|
+
returnLabel: string;
|
|
148
|
+
/** Trusted document content. Use paragraphs, lists, links and h3 section headings. */
|
|
149
|
+
children: ReactNode;
|
|
150
|
+
}>;
|
|
151
|
+
/** A readable document overlay. Reading and closing never constitute acceptance. */
|
|
152
|
+
export declare function PublicDocumentDialog({ open, onOpenChange, title, description, contentLabel, returnLabel, children }: PublicDocumentDialogProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useId, useState } from "react";
|
|
4
|
+
import { EditorialImage } from "./editorial-image.js";
|
|
5
|
+
import { Button, buttonVariants } from "@super-ic/primitives/button";
|
|
6
|
+
import { Input } from "@super-ic/primitives/input";
|
|
7
|
+
import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from "@super-ic/primitives/sheet";
|
|
8
|
+
import { Icon } from "@super-ic/icon-contracts";
|
|
9
|
+
function Link({ value, className, current }) {
|
|
10
|
+
return _jsx("a", { href: value.href, onClick: value.onNavigate, className: className, "aria-current": current ? "page" : undefined, children: value.label });
|
|
11
|
+
}
|
|
12
|
+
/** One public main landmark. The composed article, directory or boundary owns h1. */
|
|
13
|
+
export function PublicSiteFrame({ brand, navigation, navigationLabel, menuLabel = "Menu", closeMenuLabel = "Close menu", skipLabel, footer, width = "reading", children }) {
|
|
14
|
+
const id = useId();
|
|
15
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
16
|
+
if (new Set(navigation.map(item => item.id)).size !== navigation.length || navigation.filter(item => item.current).length > 1) {
|
|
17
|
+
throw new Error("PublicSiteFrame needs unique navigation IDs and at most one current destination.");
|
|
18
|
+
}
|
|
19
|
+
return _jsxs("div", { className: "sic-public", "data-slot": "public-site-frame", children: [_jsxs("header", { className: `sic-public__header sic-public__width ${width === "marketing" ? "sic-public__width--marketing" : ""}`, children: [_jsx("a", { className: "sic-public__skip", href: `#${id}-main`, children: skipLabel }), _jsx("a", { className: "sic-public__brand", href: brand.home.href, onClick: brand.home.onNavigate, "aria-label": brand.name, children: brand.mark }), _jsx("nav", { "aria-label": navigationLabel, className: "sic-public__nav sic-public__nav--desktop", children: navigation.map(item => _jsx(Link, { value: item, current: Boolean(item.current) }, item.id)) }), _jsxs(Sheet, { open: menuOpen, onOpenChange: setMenuOpen, children: [_jsxs(SheetTrigger, { render: _jsx(Button, { type: "button", variant: "outline", className: "sic-public__menu-trigger" }), children: [_jsx(Icon, { name: "navigation.menu", decorative: true, size: 18 }), " ", _jsx("span", { children: menuLabel })] }), _jsxs(SheetContent, { closeLabel: closeMenuLabel, side: "right", className: "sic-public__menu-content", children: [_jsx(SheetHeader, { children: _jsx(SheetTitle, { children: brand.name }) }), _jsx("nav", { "aria-label": navigationLabel, className: "sic-public__nav sic-public__nav--mobile", onClickCapture: event => { if (event.target instanceof Element && event.target.closest("a[href]"))
|
|
20
|
+
setMenuOpen(false); }, children: navigation.map(item => _jsx(Link, { value: item, current: Boolean(item.current) }, item.id)) })] })] })] }), _jsx("main", { className: `sic-public__main sic-public__width ${width === "marketing" ? "sic-public__width--marketing" : ""}`, id: `${id}-main`, tabIndex: -1, children: children }), _jsxs("footer", { className: `sic-public__footer sic-public__width ${width === "marketing" ? "sic-public__width--marketing" : ""}`, children: [_jsxs("div", { children: [_jsx("p", { className: "sic-public__footer-brand", children: brand.name }), _jsx("p", { children: footer.description })] }), _jsx("nav", { "aria-label": footer.navigationLabel, children: footer.links.map(link => _jsx(Link, { value: link }, link.href)) }), footer.note ? _jsx("p", { className: "sic-public__footer-note", children: footer.note }) : null] })] });
|
|
21
|
+
}
|
|
22
|
+
/** A readable document, not a legal-readiness decision or an acknowledgment form. */
|
|
23
|
+
export function PublicArticle({ anchorId, title, introduction, back, metadata, notice, contentsLabel, sections, related, presentation = "document" }) {
|
|
24
|
+
const instance = useId();
|
|
25
|
+
const validSlug = /^[a-z][a-z0-9-]*$/;
|
|
26
|
+
if (anchorId !== undefined && !validSlug.test(anchorId)) {
|
|
27
|
+
throw new Error("PublicArticle anchorId requires a lowercase fragment slug.");
|
|
28
|
+
}
|
|
29
|
+
if (new Set(sections.map(section => section.id)).size !== sections.length || sections.some(section => !validSlug.test(section.id))) {
|
|
30
|
+
throw new Error("PublicArticle sections require unique lowercase fragment slugs.");
|
|
31
|
+
}
|
|
32
|
+
const sectionAnchor = (section) => `${anchorId ?? instance}-${section.id}`;
|
|
33
|
+
return _jsxs("article", { className: "sic-public-article", "data-slot": "public-article", "data-presentation": presentation, children: [_jsxs("header", { className: "sic-public__page-heading", children: [back ? _jsx("p", { className: "sic-public__back", children: _jsx(Link, { value: back }) }) : null, _jsx("h1", { children: title }), _jsx("p", { className: "sic-public__intro", children: introduction }), metadata ? _jsx("p", { className: "sic-public__metadata", children: metadata.dateTime ? _jsx("time", { dateTime: metadata.dateTime, children: metadata.label }) : metadata.label }) : null] }), _jsxs("div", { className: "sic-public-article__layout", children: [presentation === "document" ? _jsx("div", { className: "sic-public-article__contents", children: _jsxs("details", { open: true, children: [_jsx("summary", { children: contentsLabel }), _jsx("nav", { "aria-label": contentsLabel, children: sections.map(section => _jsx("a", { href: `#${sectionAnchor(section)}`, children: section.title }, section.id)) })] }) }) : null, _jsxs("div", { className: "sic-public-article__body", children: [notice ? _jsxs("div", { role: "note", className: "sic-public__notice", "aria-labelledby": `${instance}-notice`, children: [_jsx("h2", { id: `${instance}-notice`, children: notice.title }), _jsx("p", { children: notice.body }), notice.link ? _jsx(Link, { value: notice.link }) : null] }) : null, sections.map(section => _jsx("section", { id: sectionAnchor(section), "aria-labelledby": `${instance}-${section.id}-heading`, tabIndex: -1, children: presentation === "marketing" ? _jsxs(_Fragment, { children: [_jsxs("div", { className: "sic-public-article__section-copy", children: [_jsx("h2", { id: `${instance}-${section.id}-heading`, children: section.title }), _jsx("div", { className: "sic-public__prose", children: section.body })] }), section.media ? _jsx(EditorialImage, { media: section.media, className: "sic-public-article__media" }) : null] }) : _jsxs(_Fragment, { children: [_jsx("h2", { id: `${instance}-${section.id}-heading`, children: section.title }), section.media ? _jsx(EditorialImage, { media: section.media, className: "sic-public-article__media" }) : null, _jsx("div", { className: "sic-public__prose", children: section.body })] }) }, section.id)), related ? _jsxs("section", { className: "sic-public__related", "aria-labelledby": `${instance}-related`, children: [_jsx("h2", { id: `${instance}-related`, children: related.title }), _jsx("ul", { children: related.links.map(link => _jsx("li", { children: _jsx(Link, { value: link }) }, link.href)) })] }) : null] })] })] });
|
|
34
|
+
}
|
|
35
|
+
/** Controlled search results. Products own search, stale-result suppression and help routing. */
|
|
36
|
+
export function HelpDirectory({ title, introduction, search, results, contact }) {
|
|
37
|
+
const id = useId();
|
|
38
|
+
return _jsxs("div", { "data-slot": "help-directory", className: "sic-public-help", children: [_jsxs("header", { className: "sic-public__page-heading", children: [_jsx("h1", { children: title }), _jsx("p", { className: "sic-public__intro", children: introduction })] }), _jsxs("form", { className: "sic-public-help__search", role: "search", method: "get", action: search.action, onSubmit: event => { event.preventDefault(); if (results.state !== "loading")
|
|
39
|
+
search.onSubmit(); }, children: [_jsx("label", { htmlFor: `${id}-search`, children: search.label }), _jsxs("div", { children: [_jsx(Input, { id: `${id}-search`, name: search.queryName ?? "q", type: "search", value: search.value, placeholder: search.placeholder, onChange: event => search.onChange(event.target.value) }), _jsx(Button, { type: "submit", disabled: results.state === "loading", children: search.submitLabel })] })] }), _jsxs("div", { className: "sic-public-help__results", "aria-busy": results.state === "loading", children: [results.state === "loading" ? _jsx("p", { role: "status", children: results.message }) : null, results.state === "failed" ? _jsxs("div", { className: "sic-public__notice", children: [_jsx("p", { role: "alert", children: results.message }), _jsx(Button, { variant: "outline", onClick: results.retry.onAction, children: results.retry.label })] }) : null, results.state === "empty" ? _jsxs("section", { className: "sic-public-help__empty", children: [_jsx("h2", { children: results.title }), _jsx("p", { role: "status", children: results.description }), results.reset.href ? _jsx(Link, { value: { label: results.reset.label, href: results.reset.href }, className: buttonVariants({ variant: "outline" }) }) : _jsx(Button, { variant: "outline", onClick: results.reset.onAction, children: results.reset.label })] }) : null, results.state === "ready" ? _jsxs(_Fragment, { children: [_jsx("p", { className: "sic-public__metadata", role: "status", children: results.summary }), _jsx("div", { className: "sic-public-help__groups", children: results.groups.map(group => _jsxs("section", { "aria-labelledby": `${id}-${group.id}`, children: [_jsx("h2", { id: `${id}-${group.id}`, children: group.title }), _jsx("ul", { children: group.topics.map(topic => _jsxs("li", { children: [_jsx(Link, { value: topic }), _jsx("p", { children: topic.description })] }, topic.id)) })] }, group.id)) })] }) : null] }), _jsxs("section", { className: "sic-public-help__contact", "aria-labelledby": `${id}-contact`, children: [_jsxs("div", { children: [_jsx("h2", { id: `${id}-contact`, children: contact.title }), _jsx("p", { children: contact.description })] }), _jsx(Link, { value: contact.action, className: buttonVariants({ variant: "outline" }) })] })] });
|
|
40
|
+
}
|
|
41
|
+
/** Public-safe reason and recovery routes. A state label never grants access. */
|
|
42
|
+
export function PublicAvailability({ value }) {
|
|
43
|
+
return _jsxs("section", { className: "sic-public-availability", "data-slot": "public-availability", "data-state": value.state, "aria-busy": value.state === "loading", children: [_jsx("h1", { children: value.title }), value.state === "loading" ? _jsx("p", { role: "status", children: value.message }) : _jsxs(_Fragment, { children: [_jsx("p", { className: "sic-public__intro", children: value.description }), value.detail ? _jsx("p", { children: value.detail }) : null, _jsxs("div", { className: "sic-public__actions", children: [value.primary ? _jsx(Link, { value: value.primary, className: `${buttonVariants({ size: "lg" })} sic-public__primary` }) : null, value.secondary.map(link => _jsx(Link, { value: link }, link.href))] })] })] });
|
|
44
|
+
}
|
|
45
|
+
/** A readable document overlay. Reading and closing never constitute acceptance. */
|
|
46
|
+
export function PublicDocumentDialog({ open, onOpenChange, title, description, contentLabel, returnLabel, children }) {
|
|
47
|
+
return _jsx(Sheet, { open: open, onOpenChange: onOpenChange, children: _jsxs(SheetContent, { side: "bottom", className: "sic-public-document-dialog", children: [_jsx(SheetHeader, { children: _jsx(SheetTitle, { children: title }) }), _jsxs("div", { className: "sic-public-document-dialog__body", "data-slot": "document-reader-content", role: "region", tabIndex: 0, "aria-label": contentLabel, children: [_jsx(SheetDescription, { children: description }), children] }), _jsx(SheetFooter, { children: _jsx(Button, { type: "button", onClick: () => onOpenChange(false), children: returnLabel }) })] }) });
|
|
48
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const SURFACE_RADIUS_TOKENS: {
|
|
2
|
+
readonly card: "--radius-card";
|
|
3
|
+
readonly border: "--surface-border";
|
|
4
|
+
readonly inset: "--surface-inset";
|
|
5
|
+
readonly cardFrame: "--radius-card-frame";
|
|
6
|
+
readonly cardInset: "--radius-card-inset";
|
|
7
|
+
};
|
|
8
|
+
export declare const SURFACE_RADIUS_CLASSES: {
|
|
9
|
+
readonly card: "rounded-[var(--radius-card)]";
|
|
10
|
+
readonly cardFrame: "rounded-[var(--radius-card-frame)]";
|
|
11
|
+
readonly cardFrameTop: "rounded-t-[var(--radius-card-frame)]";
|
|
12
|
+
readonly cardInset: "rounded-[var(--radius-card-inset)]";
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* The inner radius follows the actual uniform gap between two nested surfaces.
|
|
16
|
+
* max() prevents a small parent token from producing an invalid negative radius.
|
|
17
|
+
*/
|
|
18
|
+
export declare function nestedSurfaceRadius(parentToken?: string, insetToken?: string): string;
|
|
19
|
+
/** Numeric proof helper for responsive recipes that declare their actual inset in pixels. */
|
|
20
|
+
export declare function nestedSurfaceRadiusValue(parentRadius: number, inset: number): number;
|