@flamingo-stack/openframe-frontend-core 0.0.266 → 0.0.267
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/dist/{chunk-G5GQVAPI.cjs → chunk-5QJQAFTP.cjs} +126 -57
- package/dist/chunk-5QJQAFTP.cjs.map +1 -0
- package/dist/chunk-722TTRIJ.cjs +29 -0
- package/dist/chunk-722TTRIJ.cjs.map +1 -0
- package/dist/{chunk-QXRXIHY6.cjs → chunk-AA77CC2Y.cjs} +12 -12
- package/dist/{chunk-QXRXIHY6.cjs.map → chunk-AA77CC2Y.cjs.map} +1 -1
- package/dist/{chunk-WB2JXKEI.cjs → chunk-CI5XE4LB.cjs} +37 -37
- package/dist/{chunk-WB2JXKEI.cjs.map → chunk-CI5XE4LB.cjs.map} +1 -1
- package/dist/{chunk-UUARMJJE.cjs → chunk-CICK3FUU.cjs} +100 -5
- package/dist/chunk-CICK3FUU.cjs.map +1 -0
- package/dist/{chunk-K4K4X62E.js → chunk-CYMMGPPV.js} +125 -56
- package/dist/chunk-CYMMGPPV.js.map +1 -0
- package/dist/{chunk-FRU4LQJ4.js → chunk-GJVSLS2R.js} +5 -7
- package/dist/{chunk-FRU4LQJ4.js.map → chunk-GJVSLS2R.js.map} +1 -1
- package/dist/{chunk-HTR3ARN6.js → chunk-ICOMPUMX.js} +343 -248
- package/dist/chunk-ICOMPUMX.js.map +1 -0
- package/dist/{chunk-DTJ32U7J.js → chunk-J6O57C7S.js} +8 -72
- package/dist/chunk-J6O57C7S.js.map +1 -0
- package/dist/{chunk-QAQ7AROS.cjs → chunk-O6UA475M.cjs} +302 -366
- package/dist/chunk-O6UA475M.cjs.map +1 -0
- package/dist/chunk-OO4ZGDRO.js +29 -0
- package/dist/chunk-OO4ZGDRO.js.map +1 -0
- package/dist/{chunk-TX2VP43R.js → chunk-P7JHUX4X.js} +2 -2
- package/dist/{chunk-NR535XIY.js → chunk-T2URJAX2.js} +66 -1
- package/dist/chunk-T2URJAX2.js.map +1 -0
- package/dist/{chunk-VYGS24RO.cjs → chunk-TPZI7NBN.cjs} +16 -18
- package/dist/chunk-TPZI7NBN.cjs.map +1 -0
- package/dist/{chunk-SRM7HE2A.js → chunk-XSK72QRR.js} +3 -3
- package/dist/{chunk-7VMEOFEA.cjs → chunk-Y2B6WDU6.cjs} +67 -2
- package/dist/chunk-Y2B6WDU6.cjs.map +1 -0
- package/dist/{chunk-Q52JZP3P.cjs → chunk-YV55JZAD.cjs} +27 -27
- package/dist/{chunk-Q52JZP3P.cjs.map → chunk-YV55JZAD.cjs.map} +1 -1
- package/dist/{chunk-KUYYJ2XM.js → chunk-Z6MKG53Z.js} +4 -4
- package/dist/components/chat/index.cjs +5 -5
- package/dist/components/chat/index.js +4 -4
- package/dist/components/contact/index.cjs +6 -6
- package/dist/components/contact/index.js +5 -5
- package/dist/components/faq/faq-section.d.ts +20 -23
- package/dist/components/faq/faq-section.d.ts.map +1 -1
- package/dist/components/faq/index.cjs +4 -4
- package/dist/components/faq/index.js +3 -3
- package/dist/components/faq-accordion.d.ts +0 -5
- package/dist/components/faq-accordion.d.ts.map +1 -1
- package/dist/components/features/index.cjs +7 -8
- package/dist/components/features/index.cjs.map +1 -1
- package/dist/components/features/index.js +6 -7
- package/dist/components/index.cjs +84 -84
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +13 -13
- package/dist/components/navigation/index.cjs +5 -5
- package/dist/components/navigation/index.js +4 -4
- package/dist/components/onboarding-guides/index.cjs +24 -25
- package/dist/components/onboarding-guides/index.cjs.map +1 -1
- package/dist/components/onboarding-guides/index.js +4 -5
- package/dist/components/onboarding-guides/index.js.map +1 -1
- package/dist/components/related-content/index.cjs +6 -6
- package/dist/components/related-content/index.js +5 -5
- package/dist/components/tickets/index.cjs +69 -69
- package/dist/components/tickets/index.cjs.map +1 -1
- package/dist/components/tickets/index.js +10 -10
- package/dist/components/ui/index.cjs +7 -7
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +7 -7
- package/dist/index.cjs +10 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +10 -10
- package/package.json +1 -1
- package/src/components/faq/faq-section.tsx +212 -104
- package/src/components/faq-accordion.tsx +1 -15
- package/dist/chunk-7VMEOFEA.cjs.map +0 -1
- package/dist/chunk-DTJ32U7J.js.map +0 -1
- package/dist/chunk-FREG2KXV.js +0 -128
- package/dist/chunk-FREG2KXV.js.map +0 -1
- package/dist/chunk-G5GQVAPI.cjs.map +0 -1
- package/dist/chunk-HTR3ARN6.js.map +0 -1
- package/dist/chunk-JMU4RU5J.cjs +0 -128
- package/dist/chunk-JMU4RU5J.cjs.map +0 -1
- package/dist/chunk-K4K4X62E.js.map +0 -1
- package/dist/chunk-NR535XIY.js.map +0 -1
- package/dist/chunk-QAQ7AROS.cjs.map +0 -1
- package/dist/chunk-UUARMJJE.cjs.map +0 -1
- package/dist/chunk-VYGS24RO.cjs.map +0 -1
- /package/dist/{chunk-TX2VP43R.js.map → chunk-P7JHUX4X.js.map} +0 -0
- /package/dist/{chunk-SRM7HE2A.js.map → chunk-XSK72QRR.js.map} +0 -0
- /package/dist/{chunk-KUYYJ2XM.js.map → chunk-Z6MKG53Z.js.map} +0 -0
|
@@ -6,13 +6,11 @@ var _chunkT7AJBKZPcjs = require('./chunk-T7AJBKZP.cjs');
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
var _chunk7VMEOFEAcjs = require('./chunk-7VMEOFEA.cjs');
|
|
10
9
|
|
|
10
|
+
var _chunkY2B6WDU6cjs = require('./chunk-Y2B6WDU6.cjs');
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
var _chunkJMU4RU5Jcjs = require('./chunk-JMU4RU5J.cjs');
|
|
13
|
+
var _chunk722TTRIJcjs = require('./chunk-722TTRIJ.cjs');
|
|
16
14
|
|
|
17
15
|
|
|
18
16
|
|
|
@@ -76,19 +74,9 @@ function FaqAccordion({ items, defaultOpenIds = [] }) {
|
|
|
76
74
|
"aria-expanded": isOpen,
|
|
77
75
|
className: "flex w-full items-center justify-between px-6 md:px-8 py-6 text-left focus:outline-none transition-colors cursor-pointer",
|
|
78
76
|
children: [
|
|
79
|
-
/* @__PURE__ */ _jsxruntime.
|
|
80
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { children: item.question }),
|
|
81
|
-
item.badge && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
82
|
-
_chunkJMU4RU5Jcjs.StatusBadge,
|
|
83
|
-
{
|
|
84
|
-
text: item.badge.toUpperCase(),
|
|
85
|
-
variant: "card",
|
|
86
|
-
className: _chunkJMU4RU5Jcjs.TAG_BADGE_CLASS
|
|
87
|
-
}
|
|
88
|
-
)
|
|
89
|
-
] }),
|
|
77
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "min-w-0 pr-4", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { children: item.question }) }),
|
|
90
78
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex-shrink-0", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
91
|
-
|
|
79
|
+
_chunkY2B6WDU6cjs.ChevronButton,
|
|
92
80
|
{
|
|
93
81
|
"aria-label": isOpen ? "Collapse question" : "Expand question",
|
|
94
82
|
size: "md",
|
|
@@ -115,6 +103,9 @@ function FaqAccordion({ items, defaultOpenIds = [] }) {
|
|
|
115
103
|
}) });
|
|
116
104
|
}
|
|
117
105
|
|
|
106
|
+
// src/components/faq/faq-section.tsx
|
|
107
|
+
_chunkFIG2RKZFcjs.init_cn.call(void 0, );
|
|
108
|
+
|
|
118
109
|
// src/components/faq/json-ld.ts
|
|
119
110
|
var DEFAULT_NAME = "Frequently Asked Questions";
|
|
120
111
|
var DEFAULT_DESCRIPTION = "Answers to common questions.";
|
|
@@ -145,15 +136,114 @@ function buildFaqJsonLdFromFaqs(faqs, opts = {}) {
|
|
|
145
136
|
|
|
146
137
|
var DEFAULT_HEADING_TEXT = "Frequently Asked Questions";
|
|
147
138
|
function buildFaqsUrl(entityType, entityId, minResults, apiBaseUrl = "") {
|
|
148
|
-
return
|
|
139
|
+
return _chunkY2B6WDU6cjs.buildSuggestionUrl.call(void 0, "/api/faqs", { apiBaseUrl, entityType, entityId, count: minResults });
|
|
140
|
+
}
|
|
141
|
+
function sectionSlug(section) {
|
|
142
|
+
return "faq-" + section.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
149
143
|
}
|
|
150
|
-
function
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
144
|
+
function groupFaqsBySection(faqs) {
|
|
145
|
+
const order = [];
|
|
146
|
+
const byName = /* @__PURE__ */ new Map();
|
|
147
|
+
let uncategorized = null;
|
|
148
|
+
for (const faq of faqs) {
|
|
149
|
+
const item = { id: faq.id, question: faq.question, answer: faq.answer };
|
|
150
|
+
const name = _optionalChain([faq, 'access', _2 => _2.section, 'optionalAccess', _3 => _3.trim, 'call', _4 => _4()]);
|
|
151
|
+
if (!name) {
|
|
152
|
+
if (!uncategorized) uncategorized = { section: null, slug: null, items: [] };
|
|
153
|
+
uncategorized.items.push(item);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
let group = byName.get(name);
|
|
157
|
+
if (!group) {
|
|
158
|
+
group = { section: name, slug: sectionSlug(name), items: [] };
|
|
159
|
+
byName.set(name, group);
|
|
160
|
+
order.push(name);
|
|
161
|
+
}
|
|
162
|
+
group.items.push(item);
|
|
163
|
+
}
|
|
164
|
+
const groups = order.map((name) => byName.get(name));
|
|
165
|
+
if (uncategorized) groups.push(uncategorized);
|
|
166
|
+
return groups;
|
|
167
|
+
}
|
|
168
|
+
var FAQ_NAV_HEADER_OFFSET = 96;
|
|
169
|
+
function GroupedFaqList({
|
|
170
|
+
groups,
|
|
171
|
+
categoryHeadingAs
|
|
172
|
+
}) {
|
|
173
|
+
const CategoryHeading = categoryHeadingAs;
|
|
174
|
+
const navGroups = _react.useMemo.call(void 0, () => groups.filter((g) => g.slug), [groups]);
|
|
175
|
+
const [activeSlug, setActiveSlug] = _react.useState.call(void 0, _nullishCoalesce(_optionalChain([navGroups, 'access', _5 => _5[0], 'optionalAccess', _6 => _6.slug]), () => ( null)));
|
|
176
|
+
const slugKey = navGroups.map((g) => g.slug).join("|");
|
|
177
|
+
_react.useEffect.call(void 0, () => {
|
|
178
|
+
if (navGroups.length < 2) return;
|
|
179
|
+
const tops = /* @__PURE__ */ new Map();
|
|
180
|
+
const observer = new IntersectionObserver(
|
|
181
|
+
(entries) => {
|
|
182
|
+
for (const entry of entries) {
|
|
183
|
+
const id = entry.target.id;
|
|
184
|
+
if (entry.isIntersecting) tops.set(id, entry.boundingClientRect.top);
|
|
185
|
+
else tops.delete(id);
|
|
186
|
+
}
|
|
187
|
+
let bestId = null;
|
|
188
|
+
let bestTop = Number.POSITIVE_INFINITY;
|
|
189
|
+
for (const [id, top] of tops) {
|
|
190
|
+
if (top < bestTop) {
|
|
191
|
+
bestTop = top;
|
|
192
|
+
bestId = id;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (bestId) setActiveSlug(bestId);
|
|
196
|
+
},
|
|
197
|
+
{ rootMargin: `-${FAQ_NAV_HEADER_OFFSET}px 0px -55% 0px`, threshold: 0 }
|
|
198
|
+
);
|
|
199
|
+
for (const group of navGroups) {
|
|
200
|
+
const el = group.slug ? document.getElementById(group.slug) : null;
|
|
201
|
+
if (el) observer.observe(el);
|
|
202
|
+
}
|
|
203
|
+
return () => observer.disconnect();
|
|
204
|
+
}, [slugKey]);
|
|
205
|
+
const handleJump = _react.useCallback.call(void 0,
|
|
206
|
+
(e, slug) => {
|
|
207
|
+
e.preventDefault();
|
|
208
|
+
setActiveSlug(slug);
|
|
209
|
+
_chunkY2B6WDU6cjs.scrollElementIntoView.call(void 0, document.getElementById(slug), {
|
|
210
|
+
headerOffset: FAQ_NAV_HEADER_OFFSET
|
|
211
|
+
});
|
|
212
|
+
if (typeof history !== "undefined") history.replaceState(null, "", `#${slug}`);
|
|
213
|
+
},
|
|
214
|
+
[]
|
|
215
|
+
);
|
|
216
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "space-y-8", children: [
|
|
217
|
+
navGroups.length > 1 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "nav", { "aria-label": "FAQ categories", className: "flex flex-wrap gap-2", children: navGroups.map((group) => {
|
|
218
|
+
const isActive = group.slug === activeSlug;
|
|
219
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
220
|
+
"a",
|
|
221
|
+
{
|
|
222
|
+
href: `#${group.slug}`,
|
|
223
|
+
"aria-current": isActive ? "true" : void 0,
|
|
224
|
+
onClick: (e) => handleJump(e, group.slug),
|
|
225
|
+
className: _chunkFIG2RKZFcjs.cn.call(void 0,
|
|
226
|
+
"rounded-full border px-4 py-2 text-sm font-medium font-['DM_Sans'] transition-colors",
|
|
227
|
+
isActive ? "border-ods-text-primary bg-ods-card text-ods-text-primary" : "border-ods-border bg-ods-card text-ods-text-secondary hover:border-ods-text-secondary hover:text-ods-text-primary"
|
|
228
|
+
),
|
|
229
|
+
children: group.section
|
|
230
|
+
},
|
|
231
|
+
group.slug
|
|
232
|
+
);
|
|
233
|
+
}) }),
|
|
234
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "space-y-10", children: groups.map((group) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
235
|
+
"section",
|
|
236
|
+
{
|
|
237
|
+
id: _nullishCoalesce(group.slug, () => ( void 0)),
|
|
238
|
+
className: "scroll-mt-24 space-y-4",
|
|
239
|
+
children: [
|
|
240
|
+
group.section && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, CategoryHeading, { className: _chunk722TTRIJcjs.SECTION_HEADING_CLASS, children: group.section }),
|
|
241
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, FaqAccordion, { items: group.items })
|
|
242
|
+
]
|
|
243
|
+
},
|
|
244
|
+
_nullishCoalesce(group.slug, () => ( "faq-uncategorized"))
|
|
245
|
+
)) })
|
|
246
|
+
] });
|
|
157
247
|
}
|
|
158
248
|
function FaqSkeleton() {
|
|
159
249
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "space-y-8 animate-pulse", children: [
|
|
@@ -169,7 +259,6 @@ function FaqSection({
|
|
|
169
259
|
entityType,
|
|
170
260
|
entityId,
|
|
171
261
|
heading,
|
|
172
|
-
variant,
|
|
173
262
|
emitJsonLd = false,
|
|
174
263
|
jsonLd,
|
|
175
264
|
className,
|
|
@@ -182,45 +271,25 @@ function FaqSection({
|
|
|
182
271
|
[initialFaqs]
|
|
183
272
|
);
|
|
184
273
|
const { data, isLoading, error } = _chunkT7AJBKZPcjs.useSelfFetch.call(void 0, url, { initialData });
|
|
185
|
-
const faqs = _nullishCoalesce(_optionalChain([data, 'optionalAccess',
|
|
186
|
-
const
|
|
187
|
-
const headingNode = heading === void 0 ?
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
// (one FAQ heading per page, never tag/section names).
|
|
191
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { className: _chunkJMU4RU5Jcjs.SECTION_HEADING_CLASS, children: DEFAULT_HEADING_TEXT })
|
|
192
|
-
) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h1", { className: _chunkJMU4RU5Jcjs.PAGE_HEADING_CLASS, children: DEFAULT_HEADING_TEXT }) : heading;
|
|
193
|
-
if (error && isEmbedded) {
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
if (!isLoading && !error && faqs.length === 0 && isEmbedded) {
|
|
197
|
-
return null;
|
|
198
|
-
}
|
|
274
|
+
const faqs = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _7 => _7.faqs]), () => ( []));
|
|
275
|
+
const groups = _react.useMemo.call(void 0, () => faqs.length > 0 ? groupFaqsBySection(faqs) : [], [faqs]);
|
|
276
|
+
const headingNode = heading === void 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { className: _chunk722TTRIJcjs.SECTION_HEADING_CLASS, children: DEFAULT_HEADING_TEXT }) : heading;
|
|
277
|
+
if (error) return null;
|
|
278
|
+
if (!isLoading && faqs.length === 0) return null;
|
|
199
279
|
if (isLoading && faqs.length === 0) {
|
|
200
|
-
|
|
201
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, FaqSkeleton, {}) });
|
|
202
|
-
}
|
|
203
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "main", { className: _nullishCoalesce(className, () => ( "bg-ods-bg")), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "max-w-[1920px] px-6 md:px-20 py-6 md:py-10 mx-auto", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, FaqSkeleton, {}) }) });
|
|
204
|
-
}
|
|
205
|
-
if (error && faqs.length === 0) {
|
|
206
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "main", { className: _nullishCoalesce(className, () => ( "bg-ods-bg flex items-center justify-center py-20")), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-ods-text-primary font-medium", children: "Failed to load FAQs. Please try again later." }) });
|
|
280
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, FaqSkeleton, {}) });
|
|
207
281
|
}
|
|
208
282
|
const schema = emitJsonLd ? buildFaqJsonLdFromFaqs(faqs, jsonLd) : null;
|
|
209
|
-
const accordion = /* @__PURE__ */ _jsxruntime.jsx.call(void 0, FaqAccordion, { items: toAccordionItems(faqs) });
|
|
210
|
-
const body = isEmbedded ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "section", { className: _nullishCoalesce(className, () => ( "space-y-10")), children: [
|
|
211
|
-
headingNode,
|
|
212
|
-
accordion
|
|
213
|
-
] }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "main", { className: _nullishCoalesce(className, () => ( "bg-ods-bg")), children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "max-w-[1920px] px-6 md:px-20 py-6 md:py-10 mx-auto space-y-10", children: [
|
|
214
|
-
headingNode,
|
|
215
|
-
accordion
|
|
216
|
-
] }) });
|
|
217
283
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
218
|
-
|
|
284
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "section", { className: _nullishCoalesce(className, () => ( "space-y-10")), children: [
|
|
285
|
+
headingNode,
|
|
286
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, GroupedFaqList, { groups, categoryHeadingAs: heading === null ? "h2" : "h3" })
|
|
287
|
+
] }),
|
|
219
288
|
schema && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
220
289
|
"script",
|
|
221
290
|
{
|
|
222
291
|
type: "application/ld+json",
|
|
223
|
-
dangerouslySetInnerHTML: { __html:
|
|
292
|
+
dangerouslySetInnerHTML: { __html: _chunkY2B6WDU6cjs.serializeJsonLd.call(void 0, schema) }
|
|
224
293
|
}
|
|
225
294
|
)
|
|
226
295
|
] });
|
|
@@ -230,4 +299,4 @@ function FaqSection({
|
|
|
230
299
|
|
|
231
300
|
|
|
232
301
|
exports.FaqAccordion = FaqAccordion; exports.FaqSection = FaqSection;
|
|
233
|
-
//# sourceMappingURL=chunk-
|
|
302
|
+
//# sourceMappingURL=chunk-5QJQAFTP.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-5QJQAFTP.cjs","../src/components/faq/faq-section.tsx","../src/components/faq-accordion.tsx","../src/components/faq/json-ld.ts"],"names":["jsx","jsxs"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-5QJQAFTP.cjs","sourcesContent":[null,"\"use client\"\n\nimport React, { useCallback, useEffect, useMemo, useState } from 'react'\nimport type { Faq } from '../../types/faq'\nimport { FaqAccordion, type FaqItem } from '../faq-accordion'\nimport { useSelfFetch } from '../../hooks/use-self-fetch'\nimport { buildSuggestionUrl } from '../../utils/suggestion-url'\nimport { serializeJsonLd } from '../../utils/common'\nimport { scrollElementIntoView } from '../../utils/scroll-into-view'\nimport { cn } from '../../utils/cn'\nimport { buildFaqJsonLdFromFaqs, type FaqSchemaOptions } from './json-ld'\nimport { SECTION_HEADING_CLASS } from '../layout/page-heading'\n\nexport interface FaqSectionProps {\n /**\n * SSR hydrate. When provided, the hook skips the first client fetch (per\n * useSelfFetch contract). The consuming server page resolves FAQs then drills\n * them into this prop — the lib never re-fetches what the host already gated on.\n */\n initialFaqs?: Faq[]\n /** Both required together for entity-attached FAQs; partial → bare /api/faqs. */\n entityType?: string\n entityId?: number | string\n /**\n * Heading node above the grouped list. `undefined` → default\n * `<h2>`\"Frequently Asked Questions\". `null` → no heading (the host page\n * owns the `<h1>`, as the standalone /faqs surface does). A React node lets a\n * platform drill its own <PageHeading> without the lib referencing platform\n * state. Also drives category nesting so the document outline stays correct:\n * `null` → categories render `<h2>` (directly under the page `<h1>`);\n * otherwise categories render `<h3>` beneath this heading.\n */\n heading?: React.ReactNode | null\n /** Inject FAQPage schema.org JSON-LD as a <script>. Off by default so embeds\n * don't emit duplicate schema. */\n emitJsonLd?: boolean\n /** Overrides for the JSON-LD's name/description/url. */\n jsonLd?: FaqSchemaOptions\n className?: string\n /** Maps to /api/faqs `?count=` (the 5-tier fill target). Absent → param\n * not sent (server default applies). */\n minResults?: number\n /** Fetch-URL prefix for third-party embeds / reverse proxies\n * ('' = same-origin relative). */\n apiBaseUrl?: string\n}\n\nconst DEFAULT_HEADING_TEXT = 'Frequently Asked Questions'\n\n/** URL composition shared with RelatedContentSection (`buildSuggestionUrl`)\n * — byte-identical to the historical `buildFaqsUrl` output when\n * `minResults`/`apiBaseUrl` are absent. */\nfunction buildFaqsUrl(\n entityType?: string,\n entityId?: number | string,\n minResults?: number,\n apiBaseUrl = '',\n): string {\n return buildSuggestionUrl('/api/faqs', { apiBaseUrl, entityType, entityId, count: minResults })\n}\n\n/** Stable, URL-safe anchor id for a category. Prefixed so it can't collide\n * with other in-page ids, and so a bare numeric/blank section still yields a\n * valid id. */\nfunction sectionSlug(section: string): string {\n return (\n 'faq-' +\n section\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n )\n}\n\ninterface FaqGroup {\n /** null → the uncategorized bucket: no heading, no jump pill, rendered last. */\n section: string | null\n slug: string | null\n items: FaqItem[]\n}\n\n/** Group FAQs by `faq.section`, preserving the server's first-seen\n * (display_order) order for BOTH the section order and the rows within each\n * section. The uncategorized bucket (blank/missing section) sinks to the end\n * since it renders without a heading. Items carry NO badge here — the `<h2>`\n * IS the category, so a per-row chip would be redundant. */\nfunction groupFaqsBySection(faqs: Faq[]): FaqGroup[] {\n const order: string[] = []\n const byName = new Map<string, FaqGroup>()\n let uncategorized: FaqGroup | null = null\n for (const faq of faqs) {\n const item: FaqItem = { id: faq.id, question: faq.question, answer: faq.answer }\n const name = faq.section?.trim()\n if (!name) {\n if (!uncategorized) uncategorized = { section: null, slug: null, items: [] }\n uncategorized.items.push(item)\n continue\n }\n let group = byName.get(name)\n if (!group) {\n group = { section: name, slug: sectionSlug(name), items: [] }\n byName.set(name, group)\n order.push(name)\n }\n group.items.push(item)\n }\n const groups = order.map((name) => byName.get(name)!)\n if (uncategorized) groups.push(uncategorized)\n return groups\n}\n\n/** The standard hub sticky-header height — same offset `useNavLink`'s hash\n * scroll uses, so a category jump lands below the header, not under it. */\nconst FAQ_NAV_HEADER_OFFSET = 96\n\n/**\n * Grouped FAQ layout: a category jump-nav above stacked `<h2>` category\n * sections (each its own accordion). Isolated into its own component so the\n * scroll-spy hooks only mount in grouped mode — `FaqSection`'s own hooks stay\n * unconditional.\n *\n * The pills are real `<a href=\"#slug\">` anchors (crawlable in-page links,\n * deep-linkable, work without JS); the click handler upgrades the jump to the\n * cancellation-proof `scrollElementIntoView` tween and syncs the URL hash.\n */\nfunction GroupedFaqList({\n groups,\n categoryHeadingAs,\n}: {\n groups: FaqGroup[]\n /** Heading tag for each category, so the document outline nests correctly\n * under whatever owns the heading above this block: `h2` on the standalone\n * page (the page owns the `<h1>`), `h3` beneath an embed's `<h2>` title.\n * The VISUAL is `SECTION_HEADING_CLASS` either way, so categories look\n * identical on every surface. */\n categoryHeadingAs: 'h2' | 'h3'\n}) {\n const CategoryHeading = categoryHeadingAs\n const navGroups = useMemo(() => groups.filter((g) => g.slug), [groups])\n const [activeSlug, setActiveSlug] = useState<string | null>(navGroups[0]?.slug ?? null)\n // Identity-stable key for the section set so the observer re-binds only when\n // the categories actually change (not on every parent re-render).\n const slugKey = navGroups.map((g) => g.slug).join('|')\n\n // Scroll-spy: mark the pill for the category currently at the top of the\n // viewport. rootMargin drops the trigger line just below the sticky header\n // and ignores the bottom ~55% so \"active\" is the section being read, not the\n // next one peeking in.\n useEffect(() => {\n if (navGroups.length < 2) return\n const tops = new Map<string, number>()\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n const id = (entry.target as HTMLElement).id\n if (entry.isIntersecting) tops.set(id, entry.boundingClientRect.top)\n else tops.delete(id)\n }\n let bestId: string | null = null\n let bestTop = Number.POSITIVE_INFINITY\n for (const [id, top] of tops) {\n if (top < bestTop) {\n bestTop = top\n bestId = id\n }\n }\n if (bestId) setActiveSlug(bestId)\n },\n { rootMargin: `-${FAQ_NAV_HEADER_OFFSET}px 0px -55% 0px`, threshold: 0 },\n )\n for (const group of navGroups) {\n const el = group.slug ? document.getElementById(group.slug) : null\n if (el) observer.observe(el)\n }\n return () => observer.disconnect()\n // slugKey encodes the section set; re-observe only when it changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [slugKey])\n\n const handleJump = useCallback(\n (e: React.MouseEvent<HTMLAnchorElement>, slug: string) => {\n e.preventDefault()\n setActiveSlug(slug)\n scrollElementIntoView(document.getElementById(slug), {\n headerOffset: FAQ_NAV_HEADER_OFFSET,\n })\n if (typeof history !== 'undefined') history.replaceState(null, '', `#${slug}`)\n },\n [],\n )\n\n return (\n <div className=\"space-y-8\">\n {navGroups.length > 1 && (\n <nav aria-label=\"FAQ categories\" className=\"flex flex-wrap gap-2\">\n {navGroups.map((group) => {\n const isActive = group.slug === activeSlug\n return (\n <a\n key={group.slug}\n href={`#${group.slug}`}\n aria-current={isActive ? 'true' : undefined}\n onClick={(e) => handleJump(e, group.slug as string)}\n className={cn(\n \"rounded-full border px-4 py-2 text-sm font-medium font-['DM_Sans'] transition-colors\",\n isActive\n ? 'border-ods-text-primary bg-ods-card text-ods-text-primary'\n : 'border-ods-border bg-ods-card text-ods-text-secondary hover:border-ods-text-secondary hover:text-ods-text-primary',\n )}\n >\n {group.section}\n </a>\n )\n })}\n </nav>\n )}\n <div className=\"space-y-10\">\n {groups.map((group) => (\n <section\n key={group.slug ?? 'faq-uncategorized'}\n id={group.slug ?? undefined}\n className=\"scroll-mt-24 space-y-4\"\n >\n {group.section && (\n <CategoryHeading className={SECTION_HEADING_CLASS}>{group.section}</CategoryHeading>\n )}\n <FaqAccordion items={group.items} />\n </section>\n ))}\n </div>\n </div>\n )\n}\n\nfunction FaqSkeleton() {\n return (\n <div className=\"space-y-8 animate-pulse\">\n <div className=\"h-12 md:h-14 w-2/3 rounded bg-ods-border\" />\n <div className=\"rounded-3xl border border-ods-border overflow-hidden bg-ods-card divide-y divide-ods-border w-full\">\n {Array.from({ length: 8 }).map((_, idx) => (\n <div key={idx} className=\"flex items-center justify-between px-6 md:px-8 py-6\">\n <div className=\"h-6 w-5/6 rounded bg-ods-border\" />\n <div className=\"h-10 w-10 rounded-md bg-ods-border\" />\n </div>\n ))}\n </div>\n </div>\n )\n}\n\ninterface FaqsResponse {\n faqs: Faq[]\n}\n\n/**\n * The FAQ display surface — ONE rendering on every host: the list grouped by\n * `faq.section` (each category a heading + its own accordion, with a category\n * jump-nav once there are 2+ categories). There is no flat/ungrouped mode and\n * no page-vs-embedded shell fork; the standalone /faqs page and every embed\n * render through this single path, so they cannot drift.\n *\n * - Standalone /faqs page: pass `initialFaqs` (SSR) + `heading={null}` (the\n * page owns the <h1>) + `emitJsonLd` with `jsonLd` overrides for SEO.\n * - Per-entity embed: pass `entityType` + `entityId` (no `initialFaqs`); the\n * hook self-fetches `GET /api/faqs`, and `heading` is this block's own <h2>.\n *\n * CONTRACT: the consuming app MUST implement `GET /api/faqs`. On a fetch error\n * (or zero FAQs) the component renders nothing so the host page isn't\n * disfigured. The host always supplies the page shell — this renders a bare\n * <section>.\n */\nexport function FaqSection({\n initialFaqs,\n entityType,\n entityId,\n heading,\n emitJsonLd = false,\n jsonLd,\n className,\n minResults,\n apiBaseUrl = '',\n}: FaqSectionProps) {\n const url = buildFaqsUrl(entityType, entityId, minResults, apiBaseUrl)\n // Memoized — useSelfFetch re-syncs on [initialData]; a fresh per-render\n // wrapper object would setState-loop under re-rendering parents.\n const initialData = useMemo<FaqsResponse | undefined>(\n () => (initialFaqs ? { faqs: initialFaqs } : undefined),\n [initialFaqs],\n )\n const { data, isLoading, error } = useSelfFetch<FaqsResponse>(url, { initialData })\n\n const faqs = data?.faqs ?? []\n // Grouped before the early returns so the hook order stays stable.\n const groups = useMemo(() => (faqs.length > 0 ? groupFaqsBySection(faqs) : []), [faqs])\n\n // `undefined` → default <h2> title; `null` → the host page owns the <h1>, so\n // no title renders here. `heading === null` also makes the category headings\n // <h2> (directly under the page <h1>); otherwise they nest as <h3>.\n const headingNode =\n heading === undefined ? <h2 className={SECTION_HEADING_CLASS}>{DEFAULT_HEADING_TEXT}</h2> : heading\n\n // Degrade silently — never show an error banner or an empty section shell\n // where FAQs would be (host pages and the standalone surface both rely on it).\n if (error) return null\n if (!isLoading && faqs.length === 0) return null\n if (isLoading && faqs.length === 0) {\n return (\n <div className={className}>\n <FaqSkeleton />\n </div>\n )\n }\n\n const schema = emitJsonLd ? buildFaqJsonLdFromFaqs(faqs, jsonLd) : null\n\n return (\n <>\n <section className={className ?? 'space-y-10'}>\n {headingNode}\n <GroupedFaqList groups={groups} categoryHeadingAs={heading === null ? 'h2' : 'h3'} />\n </section>\n {schema && (\n <script\n type=\"application/ld+json\"\n // eslint-disable-next-line react/no-danger\n // serializeJsonLd, NOT raw JSON.stringify — FAQ answers are\n // admin-entered; an embedded \"</script>\" must not break the tag.\n dangerouslySetInnerHTML={{ __html: serializeJsonLd(schema) }}\n />\n )}\n </>\n )\n}\n","\"use client\"\n\nimport React, { useRef, useState, useEffect, useCallback } from 'react'\nimport { ChevronButton } from './ui/chevron-button'\nimport { cn } from \"../utils/cn\"\n\nexport interface FaqItem {\n id: number | string\n question: string\n answer: string\n}\n\ninterface FaqAccordionProps {\n items: FaqItem[]\n defaultOpenIds?: (number | string)[]\n}\n\n// Utility to measure scrollHeight outside render cycle\nconst useMeasuredHeight = (isOpen: boolean) => {\n const ref = useRef<HTMLDivElement | null>(null)\n const [maxHeight, setMaxHeight] = useState<string>('0px')\n\n const measure = useCallback(() => {\n if (ref.current) {\n const height = ref.current.scrollHeight\n setMaxHeight(`${height}px`)\n }\n }, [])\n\n // Update height only when section is open\n useEffect(() => {\n if (isOpen) {\n measure()\n } else {\n setMaxHeight('0px')\n }\n }, [isOpen, measure])\n\n return { ref, maxHeight }\n}\n\nexport function FaqAccordion({ items, defaultOpenIds = [] }: FaqAccordionProps) {\n const [openSet, setOpenSet] = useState<Set<string | number>>(new Set(defaultOpenIds))\n\n const toggle = (id: string | number) => {\n setOpenSet(prev => {\n const next = new Set(prev)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n return next\n })\n }\n\n return (\n <div className=\"rounded-3xl border border-ods-border divide-y divide-ods-border bg-ods-card overflow-hidden\">\n {items.map(item => {\n const isOpen = openSet.has(item.id)\n const { ref, maxHeight } = useMeasuredHeight(isOpen)\n\n return (\n <div\n key={item.id}\n className={cn('group transition-colors hover:bg-[#1E1E1E]', isOpen ? 'bg-ods-bg' : 'bg-transparent')}\n >\n {/* Header */}\n <div\n role=\"button\"\n tabIndex={0}\n onClick={() => toggle(item.id)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggle(item.id);\n }\n }}\n aria-expanded={isOpen}\n className=\"flex w-full items-center justify-between px-6 md:px-8 py-6 text-left focus:outline-none transition-colors cursor-pointer\"\n >\n <div className=\"min-w-0 pr-4\">\n <h3>\n {item.question}\n </h3>\n </div>\n <div className=\"flex-shrink-0\">\n <ChevronButton\n aria-label={isOpen ? 'Collapse question' : 'Expand question'}\n size=\"md\"\n isExpanded={isOpen}\n backgroundColor=\"transparent\"\n borderColor=\"#3A3A3A\"\n />\n </div>\n </div>\n {/* Content wrapper with max-height animation */}\n <div\n style={{ maxHeight, transition: 'max-height 0.35s ease-in-out, opacity 0.35s ease-in-out', opacity: isOpen ? 1 : 0 }}\n className=\"overflow-hidden group-hover:bg-[#1E1E1E]/30\"\n >\n <div ref={ref} className=\"px-6 md:px-8 pb-6 text-ods-text-primary text-h4\">\n {item.answer}\n </div>\n </div>\n </div>\n )\n })}\n </div>\n )\n} ","/**\n * Pure FAQ JSON-LD builder. No React, no client-only deps — safe to import from\n * Server Components via the `./components/faq` subpath export. (Do NOT import\n * through the root `./components` barrel — that barrel is `\"use client\"` and\n * dragging it into a Server Component would force the client graph into the\n * server.)\n *\n * The hub used to harcode `name`/`description` here for OpenMSP; in the lib we\n * accept overrides so every embedder can supply its own platform branding.\n */\nimport type { Faq } from '../../types/faq'\n\nexport interface FaqSchemaOptions {\n name?: string\n description?: string\n url?: string\n}\n\nconst DEFAULT_NAME = 'Frequently Asked Questions'\nconst DEFAULT_DESCRIPTION =\n 'Answers to common questions.'\n\nexport function baseFaqSchema(opts: FaqSchemaOptions = {}) {\n return {\n '@context': 'https://schema.org',\n '@type': 'FAQPage',\n name: opts.name ?? DEFAULT_NAME,\n description: opts.description ?? DEFAULT_DESCRIPTION,\n ...(opts.url ? { url: opts.url } : {}),\n } as const\n}\n\nexport function buildFaqJsonLdFromFaqs(faqs: Faq[], opts: FaqSchemaOptions = {}) {\n return {\n ...baseFaqSchema(opts),\n mainEntity: faqs.map((faq) => ({\n '@type': 'Question',\n name: faq.question,\n acceptedAnswer: {\n '@type': 'Answer',\n text: faq.answer,\n },\n })),\n } as const\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
|
|
2
|
+
|
|
3
|
+
// src/components/layout/page-heading.tsx
|
|
4
|
+
var _jsxruntime = require('react/jsx-runtime');
|
|
5
|
+
var PAGE_HEADING_CLASS = "text-h1 text-ods-text-primary";
|
|
6
|
+
var SECTION_HEADING_CLASS = "text-h2 text-ods-text-primary";
|
|
7
|
+
var DESCRIPTION_CLASS = "mt-6 max-w-[640px] font-['DM_Sans'] text-[16px] md:text-[18px] leading-[24px] md:leading-[28px] text-ods-text-secondary";
|
|
8
|
+
function PageHeading({
|
|
9
|
+
children,
|
|
10
|
+
as: Tag = "h1",
|
|
11
|
+
description,
|
|
12
|
+
className,
|
|
13
|
+
descriptionClassName
|
|
14
|
+
}) {
|
|
15
|
+
const headingClass = className ? `${PAGE_HEADING_CLASS} ${className}` : PAGE_HEADING_CLASS;
|
|
16
|
+
const descClass = descriptionClassName ? `${DESCRIPTION_CLASS} ${descriptionClassName}` : DESCRIPTION_CLASS;
|
|
17
|
+
const hasDescription = description != null && description !== "" && typeof description !== "boolean";
|
|
18
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
19
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Tag, { className: headingClass, children }),
|
|
20
|
+
hasDescription && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: descClass, children: description })
|
|
21
|
+
] });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
exports.PAGE_HEADING_CLASS = PAGE_HEADING_CLASS; exports.SECTION_HEADING_CLASS = SECTION_HEADING_CLASS; exports.PageHeading = PageHeading;
|
|
29
|
+
//# sourceMappingURL=chunk-722TTRIJ.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-722TTRIJ.cjs","../src/components/layout/page-heading.tsx"],"names":[],"mappings":"AAAA,qFAAY;AACZ;AACA;AC6DI,+CAAA;AAnDG,IAAM,mBAAA,EAAqB,+BAAA;AAS3B,IAAM,sBAAA,EAAwB,+BAAA;AAErC,IAAM,kBAAA,EACJ,yHAAA;AAwBK,SAAS,WAAA,CAAY;AAAA,EAC1B,QAAA;AAAA,EACA,EAAA,EAAI,IAAA,EAAM,IAAA;AAAA,EACV,WAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,EAAqB;AACnB,EAAA,MAAM,aAAA,EAAe,UAAA,EAAY,CAAA,EAAA;AACf,EAAA;AAKhB,EAAA;AAEA,EAAA;AACO,oBAAA;AACc,IAAA;AACrB,EAAA;AAEJ;AD9CoC;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-722TTRIJ.cjs","sourcesContent":[null,"import type { ReactNode } from 'react'\n\n/**\n * THE page-title style — the ODS `text-h1` token (`--font-size-h1-title` =\n * 40 / 48 / 56px, Azeret Mono semibold), which already owns the responsive\n * letter-spacing (-0.02em). Mirrors the global `h1 { @apply text-h1 }` base, so\n * every page heading is the SAME size across the lib and every consuming app.\n *\n * Single source of truth — do NOT hardcode a px size or re-assert the token's\n * tracking; render `<PageHeading>` (or, for the rare non-h1 caller, apply\n * `PAGE_HEADING_CLASS`).\n */\nexport const PAGE_HEADING_CLASS = 'text-h1 text-ods-text-primary'\n\n/**\n * THE section-heading style — the ODS `text-h2` sub-title token\n * (`--font-size-h2-sub-title` = 24 / 32 / 32px), one step below the page\n * title so a section `<h2>`/`<h3>` is always visually distinguishable from\n * the page's `<h1>`. Same single-source rule as PAGE_HEADING_CLASS: never\n * hardcode a px ramp or re-assert the token's tracking on a section heading.\n */\nexport const SECTION_HEADING_CLASS = 'text-h2 text-ods-text-primary'\n\nconst DESCRIPTION_CLASS =\n \"mt-6 max-w-[640px] font-['DM_Sans'] text-[16px] md:text-[18px] leading-[24px] md:leading-[28px] text-ods-text-secondary\"\n\nexport interface PageHeadingProps {\n /** Heading content — plain text or nodes (e.g. an accent <span>). */\n children: ReactNode\n /**\n * Heading level. Defaults to 'h1' (exactly one per page). Pass 'h2' where the\n * page already renders an <h1> above (e.g. a hero/featured item).\n */\n as?: 'h1' | 'h2'\n /** Optional supporting copy rendered as a <p> beneath the heading. */\n description?: ReactNode\n /** Extra classes merged onto the heading (margins, width, truncate, etc.). */\n className?: string\n /** Extra classes merged onto the description <p>. */\n descriptionClassName?: string\n}\n\n/**\n * Unified page heading. Renders the canonical page-title <h1> (or <h2>) plus an\n * optional description, so every page shares one consistent style instead of\n * duplicating the class string. Layout (PageContainer, margins, surrounding\n * sections) stays with the caller — this owns only the heading + description.\n */\nexport function PageHeading({\n children,\n as: Tag = 'h1',\n description,\n className,\n descriptionClassName,\n}: PageHeadingProps) {\n const headingClass = className ? `${PAGE_HEADING_CLASS} ${className}` : PAGE_HEADING_CLASS\n const descClass = descriptionClassName ? `${DESCRIPTION_CLASS} ${descriptionClassName}` : DESCRIPTION_CLASS\n // `description` is a ReactNode, so `description={cond && '...'}` can pass a\n // boolean `false` — exclude it (and empty string) so we never render an empty\n // <p> that adds phantom vertical gap beneath the heading.\n const hasDescription =\n description != null && description !== '' && typeof description !== 'boolean'\n return (\n <>\n <Tag className={headingClass}>{children}</Tag>\n {hasDescription && <p className={descClass}>{description}</p>}\n </>\n )\n}\n"]}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
var
|
|
12
|
+
var _chunkCICK3FUUcjs = require('./chunk-CICK3FUU.cjs');
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
var _chunkDAAFVOTLcjs = require('./chunk-DAAFVOTL.cjs');
|
|
@@ -47,7 +47,7 @@ function DocSearchResultRow({
|
|
|
47
47
|
}) {
|
|
48
48
|
const docType = _optionalChain([result, 'access', _2 => _2.metadata, 'optionalAccess', _3 => _3.documentType]) || void 0;
|
|
49
49
|
const sourceRepo = _optionalChain([result, 'access', _4 => _4.metadata, 'optionalAccess', _5 => _5.sourceRepo]) || void 0;
|
|
50
|
-
const { Icon: SourceIcon, label: iconLabel } =
|
|
50
|
+
const { Icon: SourceIcon, label: iconLabel } = _chunkCICK3FUUcjs.resolveSourceIcon.call(void 0, {
|
|
51
51
|
sourceRepo,
|
|
52
52
|
documentType: docType
|
|
53
53
|
});
|
|
@@ -90,7 +90,7 @@ function DocSearchBar({
|
|
|
90
90
|
renderResult
|
|
91
91
|
}) {
|
|
92
92
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
93
|
-
|
|
93
|
+
_chunkCICK3FUUcjs.SearchInput,
|
|
94
94
|
{
|
|
95
95
|
placeholder,
|
|
96
96
|
value: query,
|
|
@@ -199,7 +199,7 @@ function resolveSearchResultAction(result, source, runtimeMode) {
|
|
|
199
199
|
const externalUrl = meta.externalUrl;
|
|
200
200
|
if (externalUrl) {
|
|
201
201
|
const targetPlatform = meta.targetPlatform;
|
|
202
|
-
const isNewTab =
|
|
202
|
+
const isNewTab = _chunkCICK3FUUcjs.decideNewTab.call(void 0, {
|
|
203
203
|
href: externalUrl,
|
|
204
204
|
targetPlatform,
|
|
205
205
|
surface: "useUnifiedNav",
|
|
@@ -300,7 +300,7 @@ function useDocSearch(config) {
|
|
|
300
300
|
if (_optionalChain([runtime, 'optionalAccess', _13 => _13.navigation, 'access', _14 => _14.mode]) === "embed") {
|
|
301
301
|
setKeepOpen(true);
|
|
302
302
|
const targetPlatform = _nullishCoalesce(_optionalChain([result, 'access', _15 => _15.metadata, 'optionalAccess', _16 => _16.targetPlatform]), () => ( null));
|
|
303
|
-
|
|
303
|
+
_chunkCICK3FUUcjs.resolveExternalNavigation.call(void 0, {
|
|
304
304
|
href: action.href,
|
|
305
305
|
targetPlatform,
|
|
306
306
|
runtime
|
|
@@ -309,18 +309,18 @@ function useDocSearch(config) {
|
|
|
309
309
|
}
|
|
310
310
|
if (wantsNewTab) {
|
|
311
311
|
setKeepOpen(true);
|
|
312
|
-
window.open(action.href, "_blank",
|
|
312
|
+
window.open(action.href, "_blank", _chunkCICK3FUUcjs.NEW_TAB_FEATURES);
|
|
313
313
|
return;
|
|
314
314
|
}
|
|
315
315
|
setKeepOpen(false);
|
|
316
316
|
const path = baseRoute && action.href.startsWith(`${baseRoute}/`) ? action.href.slice(baseRoute.length + 1) : null;
|
|
317
317
|
if (path && _optionalChain([onInPageSwap, 'optionalCall', _17 => _17(path)])) return;
|
|
318
|
-
router.push(
|
|
318
|
+
router.push(_chunkCICK3FUUcjs.stripSameOriginToPath.call(void 0, action.href));
|
|
319
319
|
return;
|
|
320
320
|
}
|
|
321
321
|
case "navigate-new-tab":
|
|
322
322
|
setKeepOpen(true);
|
|
323
|
-
window.open(action.href, "_blank",
|
|
323
|
+
window.open(action.href, "_blank", _chunkCICK3FUUcjs.NEW_TAB_FEATURES);
|
|
324
324
|
return;
|
|
325
325
|
case "ask-ai":
|
|
326
326
|
setKeepOpen(false);
|
|
@@ -358,7 +358,7 @@ function DetailPageSkeleton({
|
|
|
358
358
|
showImageGallery = true
|
|
359
359
|
} = {}) {
|
|
360
360
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
361
|
-
|
|
361
|
+
_chunkCICK3FUUcjs.PageContainer,
|
|
362
362
|
{
|
|
363
363
|
as: "article",
|
|
364
364
|
backgroundClassName: "bg-ods-bg",
|
|
@@ -419,7 +419,7 @@ function ArticleAuthorByline({
|
|
|
419
419
|
}) {
|
|
420
420
|
const runtime = _chunkMEAR2WHScjs.useChatRuntime.call(void 0, );
|
|
421
421
|
if (!author) return null;
|
|
422
|
-
const proxiedAvatar = avatar ? proxyImageUrl ? proxyImageUrl(avatar) : _nullishCoalesce(
|
|
422
|
+
const proxiedAvatar = avatar ? proxyImageUrl ? proxyImageUrl(avatar) : _nullishCoalesce(_chunkCICK3FUUcjs.getProxiedImageUrl.call(void 0, avatar, {
|
|
423
423
|
proxyPrefix: _optionalChain([runtime, 'optionalAccess', _18 => _18.endpoints, 'access', _19 => _19.imageProxyUrlPrefix]),
|
|
424
424
|
skipDomains: _optionalChain([runtime, 'optionalAccess', _20 => _20.endpoints, 'access', _21 => _21.imageProxySkipDomains]),
|
|
425
425
|
directHttps: true
|
|
@@ -427,7 +427,7 @@ function ArticleAuthorByline({
|
|
|
427
427
|
const avatarSizeClass = size === "lg" ? "w-16 h-16" : "w-14 h-14";
|
|
428
428
|
const avatarIconClass = size === "lg" ? "w-8 h-8" : "w-7 h-7";
|
|
429
429
|
const avatarDim = size === "lg" ? 64 : 56;
|
|
430
|
-
const formattedBio =
|
|
430
|
+
const formattedBio = _chunkCICK3FUUcjs.formatBioText.call(void 0, _nullishCoalesce(bio, () => ( null)));
|
|
431
431
|
const dateLabel = publishedAt ? formatDate(publishedAt) : "";
|
|
432
432
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
433
433
|
"div",
|
|
@@ -483,4 +483,4 @@ function ArticleAuthorByline({
|
|
|
483
483
|
|
|
484
484
|
|
|
485
485
|
exports.formatRelativePath = formatRelativePath; exports.DocSearchResultRow = DocSearchResultRow; exports.DocSearchBar = DocSearchBar; exports.mapDocSearchResults = mapDocSearchResults; exports.resolveSearchResultAction = resolveSearchResultAction; exports.useDocSearch = useDocSearch; exports.DetailPageSkeleton = DetailPageSkeleton; exports.ArticleAuthorByline = ArticleAuthorByline;
|
|
486
|
-
//# sourceMappingURL=chunk-
|
|
486
|
+
//# sourceMappingURL=chunk-AA77CC2Y.cjs.map
|