@flamingo-stack/openframe-frontend-core 0.0.265 → 0.0.266
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-EXU5N5RM.js → chunk-DTJ32U7J.js} +2457 -2478
- package/dist/chunk-DTJ32U7J.js.map +1 -0
- package/dist/{chunk-ERFO4JFK.js → chunk-FREG2KXV.js} +30 -6
- package/dist/chunk-FREG2KXV.js.map +1 -0
- package/dist/{chunk-Z5YEQWLC.js → chunk-FRU4LQJ4.js} +4 -4
- package/dist/{chunk-J5R3QIOJ.cjs → chunk-G5GQVAPI.cjs} +8 -6
- package/dist/chunk-G5GQVAPI.cjs.map +1 -0
- package/dist/{chunk-URJQGMNH.js → chunk-HTR3ARN6.js} +6 -3
- package/dist/chunk-HTR3ARN6.js.map +1 -0
- package/dist/{chunk-TT2VBR7P.cjs → chunk-JMU4RU5J.cjs} +26 -2
- package/dist/chunk-JMU4RU5J.cjs.map +1 -0
- package/dist/{chunk-GBUGYHDY.js → chunk-K4K4X62E.js} +6 -4
- package/dist/chunk-K4K4X62E.js.map +1 -0
- package/dist/{chunk-6A2YQ5CY.js → chunk-KUYYJ2XM.js} +3 -3
- package/dist/{chunk-D4TWJNO5.cjs → chunk-Q52JZP3P.cjs} +29 -26
- package/dist/chunk-Q52JZP3P.cjs.map +1 -0
- package/dist/{chunk-2P6V2EWZ.cjs → chunk-QAQ7AROS.cjs} +298 -319
- package/dist/chunk-QAQ7AROS.cjs.map +1 -0
- package/dist/{chunk-A56L7UQG.cjs → chunk-QXRXIHY6.cjs} +12 -12
- package/dist/{chunk-A56L7UQG.cjs.map → chunk-QXRXIHY6.cjs.map} +1 -1
- package/dist/{chunk-266DUZ6D.js → chunk-SRM7HE2A.js} +6 -3
- package/dist/chunk-SRM7HE2A.js.map +1 -0
- package/dist/{chunk-TZ3PV5NT.js → chunk-TX2VP43R.js} +2 -2
- package/dist/{chunk-7MNBE7XA.cjs → chunk-UUARMJJE.cjs} +6 -3
- package/dist/chunk-UUARMJJE.cjs.map +1 -0
- package/dist/{chunk-HMHMK6U6.cjs → chunk-VYGS24RO.cjs} +16 -16
- package/dist/{chunk-HMHMK6U6.cjs.map → chunk-VYGS24RO.cjs.map} +1 -1
- package/dist/{chunk-J5BBBDNA.cjs → chunk-WB2JXKEI.cjs} +34 -34
- package/dist/{chunk-J5BBBDNA.cjs.map → chunk-WB2JXKEI.cjs.map} +1 -1
- package/dist/components/chat/index.cjs +4 -4
- package/dist/components/chat/index.js +3 -3
- package/dist/components/contact/contact-form.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +5 -5
- package/dist/components/contact/index.js +4 -4
- package/dist/components/faq/faq-section.d.ts.map +1 -1
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/entity-video-section.d.ts.map +1 -1
- package/dist/components/features/index.cjs +4 -4
- package/dist/components/features/index.js +3 -3
- package/dist/components/features/video-bites-display.d.ts.map +1 -1
- package/dist/components/index.cjs +83 -81
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +11 -9
- package/dist/components/index.js.map +1 -1
- package/dist/components/layout/page-heading.d.ts +8 -0
- package/dist/components/layout/page-heading.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +4 -4
- package/dist/components/navigation/index.js +3 -3
- package/dist/components/onboarding-guides/index.cjs +23 -23
- package/dist/components/onboarding-guides/index.js +3 -3
- package/dist/components/open-source-features.d.ts.map +1 -1
- package/dist/components/related-content/index.cjs +5 -5
- package/dist/components/related-content/index.js +4 -4
- package/dist/components/shared/onboarding/onboarding-walkthrough.d.ts.map +1 -1
- package/dist/components/tickets/index.cjs +65 -65
- package/dist/components/tickets/index.js +5 -5
- package/dist/components/ui/index.cjs +8 -6
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +7 -5
- package/dist/components/why-it-matters.d.ts.map +1 -1
- package/dist/hooks/use-toast.d.ts +1 -1
- package/dist/index.cjs +8 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7 -5
- package/package.json +1 -1
- package/src/components/contact/contact-form.tsx +2 -1
- package/src/components/faq/faq-section.tsx +3 -2
- package/src/components/features/entity-video-section.tsx +2 -1
- package/src/components/features/video-bites-display.tsx +2 -1
- package/src/components/layout/page-heading.tsx +9 -0
- package/src/components/open-source-features.tsx +3 -2
- package/src/components/shared/onboarding/onboarding-walkthrough.tsx +2 -1
- package/src/components/why-it-matters.tsx +3 -2
- package/dist/chunk-266DUZ6D.js.map +0 -1
- package/dist/chunk-2P6V2EWZ.cjs.map +0 -1
- package/dist/chunk-7MNBE7XA.cjs.map +0 -1
- package/dist/chunk-D4TWJNO5.cjs.map +0 -1
- package/dist/chunk-ERFO4JFK.js.map +0 -1
- package/dist/chunk-EXU5N5RM.js.map +0 -1
- package/dist/chunk-GBUGYHDY.js.map +0 -1
- package/dist/chunk-J5R3QIOJ.cjs.map +0 -1
- package/dist/chunk-TT2VBR7P.cjs.map +0 -1
- package/dist/chunk-URJQGMNH.js.map +0 -1
- /package/dist/{chunk-Z5YEQWLC.js.map → chunk-FRU4LQJ4.js.map} +0 -0
- /package/dist/{chunk-6A2YQ5CY.js.map → chunk-KUYYJ2XM.js.map} +0 -0
- /package/dist/{chunk-TZ3PV5NT.js.map → chunk-TX2VP43R.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/faq/faq-section.tsx","../src/components/faq-accordion.tsx","../src/components/faq/json-ld.ts"],"sourcesContent":["\"use client\"\n\nimport React, { useMemo } 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 { buildFaqJsonLdFromFaqs, type FaqSchemaOptions } from './json-ld'\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. `undefined` → variant default (page: <h1>, embedded: <h2>\n * \"Frequently Asked Questions\"); `null` → no heading. React node so\n * platforms can drill their local <PageHeading> component without the lib\n * referencing platform state.\n */\n heading?: React.ReactNode | null\n /**\n * Shell variant — the FAQ LIST itself renders identically everywhere (ONE\n * flat accordion, `faq.section` as a per-row chip, never a heading):\n * - 'page' — the standalone /faqs surface: <main> shell + page gutters,\n * default <h1> heading.\n * - 'embedded' — a section inside a host page (entity detail rails, the\n * global nested-page block): bare <section>, default <h2> heading.\n * Back-compat default: `heading === null` → 'embedded', else 'page' (the\n * pre-variant discriminator).\n */\n variant?: 'page' | 'embedded'\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/** ONE list shape for every surface — standalone /faqs and embeds render the\n * IDENTICAL flat accordion (`faq.section` = per-row chip, never a heading),\n * so the two can't drift visually. */\nfunction toAccordionItems(faqs: Faq[]): FaqItem[] {\n return faqs.map((faq) => ({\n id: faq.id,\n question: faq.question,\n answer: faq.answer,\n badge: faq.section || undefined,\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 * Generic, embeddable FAQ display surface.\n *\n * - Standalone /faqs page: pass `initialFaqs` from the server + `heading` +\n * `emitJsonLd` with `jsonLd` overrides for SEO.\n * - Per-entity embed: pass `entityType` + `entityId` (no `initialFaqs`); the\n * hook self-fetches and the public /api/faqs endpoint picks override-vs-fallback.\n *\n * CONTRACT: the consuming app MUST implement `GET /api/faqs`. Without it,\n * useSelfFetch reports `error:true` (logged once); in embedded mode (heading\n * === null) this component renders nothing so the host page isn't disfigured.\n */\nexport function FaqSection({\n initialFaqs,\n entityType,\n entityId,\n heading,\n variant,\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 const isEmbedded = variant ? variant === 'embedded' : heading === null\n const headingNode =\n heading === undefined ? (\n isEmbedded ? (\n // Embedded default is an <h2> — a host page already owns the <h1>,\n // and the schema/heading pair must read \"Frequently Asked Questions\"\n // (one FAQ heading per page, never tag/section names).\n <h2 className=\"text-h2 tracking-[-0.04em] text-ods-text-primary\">{DEFAULT_HEADING_TEXT}</h2>\n ) : (\n <h1 className=\"text-h1 tracking-[-0.04em] text-ods-text-primary\">{DEFAULT_HEADING_TEXT}</h1>\n )\n ) : (\n heading\n )\n\n // Embedded mode: degrade silently on error.\n if (error && isEmbedded) {\n return null\n }\n\n // Embedded mode, zero FAQs after load: render nothing — an embedded host\n // page (blog/case-study detail) must not show an empty section shell.\n // The standalone /faqs page keeps its existing behavior.\n if (!isLoading && !error && faqs.length === 0 && isEmbedded) {\n return null\n }\n\n if (isLoading && faqs.length === 0) {\n // Embedded mode renders skeletons WITHOUT the standalone page shell —\n // a host detail page already provides <main> + gutters; nesting them\n // here would emit invalid markup and double padding.\n if (isEmbedded) {\n return (\n <div className={className}>\n <FaqSkeleton />\n </div>\n )\n }\n return (\n <main className={className ?? 'bg-ods-bg'}>\n <div className=\"max-w-[1920px] px-6 md:px-20 py-6 md:py-10 mx-auto\">\n <FaqSkeleton />\n </div>\n </main>\n )\n }\n\n if (error && faqs.length === 0) {\n return (\n <main className={className ?? 'bg-ods-bg flex items-center justify-center py-20'}>\n <p className=\"text-ods-text-primary font-medium\">Failed to load FAQs. Please try again later.</p>\n </main>\n )\n }\n\n const schema = emitJsonLd ? buildFaqJsonLdFromFaqs(faqs, jsonLd) : null\n\n // ONE flat accordion on EVERY surface — `faq.section` becomes a per-row\n // chip, NOT a heading, so the page outline stays heading + h3(questions).\n // Order is the server's (post-specific first, then reused) — grouping by\n // section here would destroy it. The variants differ ONLY in shell:\n // standalone /faqs keeps its <main> + page gutters, embeds render bare.\n const accordion = <FaqAccordion items={toAccordionItems(faqs)} />\n const body = isEmbedded ? (\n <section className={className ?? 'space-y-10'}>\n {headingNode}\n {accordion}\n </section>\n ) : (\n <main className={className ?? 'bg-ods-bg'}>\n <div className=\"max-w-[1920px] px-6 md:px-20 py-6 md:py-10 mx-auto space-y-10\">\n {headingNode}\n {accordion}\n </div>\n </main>\n )\n\n return (\n <>\n {body}\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 { StatusBadge } from './ui/status-badge'\nimport { TAG_BADGE_CLASS } from './features/entity-tag-badges'\nimport { cn } from \"../utils/cn\"\n\nexport interface FaqItem {\n id: number | string\n question: string\n answer: string\n /** Optional category label (faq.section = tag name) rendered under the\n * question as the product's standard tag badge — the same StatusBadge\n * card skin EntityTagBadges uses everywhere (never a heading, so the\n * document outline stays h2/h3 only). */\n badge?: 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=\"flex min-w-0 flex-col items-start gap-2 pr-4\">\n <h3>\n {item.question}\n </h3>\n {item.badge && (\n <StatusBadge\n text={item.badge.toUpperCase()}\n variant=\"card\"\n className={TAG_BADGE_CLASS}\n />\n )}\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"],"mappings":";;;;;;;;;;;;;;;;;;;AAEA,SAAgB,eAAe;;;ACA/B,SAAgB,QAAQ,UAAU,WAAW,mBAAmB;AAIhE;AA+Ec,SACE,KADF;AA5Dd,IAAM,oBAAoB,CAAC,WAAoB;AAC7C,QAAM,MAAM,OAA8B,IAAI;AAC9C,QAAM,CAAC,WAAW,YAAY,IAAI,SAAiB,KAAK;AAExD,QAAM,UAAU,YAAY,MAAM;AAChC,QAAI,IAAI,SAAS;AACf,YAAM,SAAS,IAAI,QAAQ;AAC3B,mBAAa,GAAG,MAAM,IAAI;AAAA,IAC5B;AAAA,EACF,GAAG,CAAC,CAAC;AAGL,YAAU,MAAM;AACd,QAAI,QAAQ;AACV,cAAQ;AAAA,IACV,OAAO;AACL,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,QAAQ,OAAO,CAAC;AAEpB,SAAO,EAAE,KAAK,UAAU;AAC1B;AAEO,SAAS,aAAa,EAAE,OAAO,iBAAiB,CAAC,EAAE,GAAsB;AAC9E,QAAM,CAAC,SAAS,UAAU,IAAI,SAA+B,IAAI,IAAI,cAAc,CAAC;AAEpF,QAAM,SAAS,CAAC,OAAwB;AACtC,eAAW,UAAQ;AACjB,YAAM,OAAO,IAAI,IAAI,IAAI;AACzB,UAAI,KAAK,IAAI,EAAE,EAAG,MAAK,OAAO,EAAE;AAAA,UAC3B,MAAK,IAAI,EAAE;AAChB,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,SACE,oBAAC,SAAI,WAAU,+FACZ,gBAAM,IAAI,UAAQ;AACjB,UAAM,SAAS,QAAQ,IAAI,KAAK,EAAE;AAClC,UAAM,EAAE,KAAK,UAAU,IAAI,kBAAkB,MAAM;AAEnD,WACE;AAAA,MAAC;AAAA;AAAA,QAEC,WAAW,GAAG,8CAA8C,SAAS,cAAc,gBAAgB;AAAA,QAGnG;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,UAAU;AAAA,cACV,SAAS,MAAM,OAAO,KAAK,EAAE;AAAA,cAC7B,WAAW,CAAC,MAAM;AAChB,oBAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AACtC,oBAAE,eAAe;AACjB,yBAAO,KAAK,EAAE;AAAA,gBAChB;AAAA,cACF;AAAA,cACA,iBAAe;AAAA,cACf,WAAU;AAAA,cAEV;AAAA,qCAAC,SAAI,WAAU,gDACb;AAAA,sCAAC,QACE,eAAK,UACR;AAAA,kBACC,KAAK,SACJ;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAM,KAAK,MAAM,YAAY;AAAA,sBAC7B,SAAQ;AAAA,sBACR,WAAW;AAAA;AAAA,kBACb;AAAA,mBAEJ;AAAA,gBACA,oBAAC,SAAI,WAAU,iBACb;AAAA,kBAAC;AAAA;AAAA,oBACC,cAAY,SAAS,sBAAsB;AAAA,oBAC3C,MAAK;AAAA,oBACL,YAAY;AAAA,oBACZ,iBAAgB;AAAA,oBAChB,aAAY;AAAA;AAAA,gBACd,GACF;AAAA;AAAA;AAAA,UACF;AAAA,UAEA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,EAAE,WAAW,YAAY,2DAA2D,SAAS,SAAS,IAAI,EAAE;AAAA,cACnH,WAAU;AAAA,cAEV,8BAAC,SAAI,KAAU,WAAU,mDACtB,eAAK,QACR;AAAA;AAAA,UACF;AAAA;AAAA;AAAA,MA/CK,KAAK;AAAA,IAgDZ;AAAA,EAEJ,CAAC,GACH;AAEJ;;;ACvGA,IAAM,eAAe;AACrB,IAAM,sBACJ;AAEK,SAAS,cAAc,OAAyB,CAAC,GAAG;AACzD,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,MAAM,KAAK,QAAQ;AAAA,IACnB,aAAa,KAAK,eAAe;AAAA,IACjC,GAAI,KAAK,MAAM,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC;AAAA,EACtC;AACF;AAEO,SAAS,uBAAuB,MAAa,OAAyB,CAAC,GAAG;AAC/E,SAAO;AAAA,IACL,GAAG,cAAc,IAAI;AAAA,IACrB,YAAY,KAAK,IAAI,CAAC,SAAS;AAAA,MAC7B,SAAS;AAAA,MACT,MAAM,IAAI;AAAA,MACV,gBAAgB;AAAA,QACd,SAAS;AAAA,QACT,MAAM,IAAI;AAAA,MACZ;AAAA,IACF,EAAE;AAAA,EACJ;AACF;;;AFqCM,SAiIF,UAjIE,OAAAA,MAGI,QAAAC,aAHJ;AA7BN,IAAM,uBAAuB;AAK7B,SAAS,aACP,YACA,UACA,YACA,aAAa,IACL;AACR,SAAO,mBAAmB,aAAa,EAAE,YAAY,YAAY,UAAU,OAAO,WAAW,CAAC;AAChG;AAKA,SAAS,iBAAiB,MAAwB;AAChD,SAAO,KAAK,IAAI,CAAC,SAAS;AAAA,IACxB,IAAI,IAAI;AAAA,IACR,UAAU,IAAI;AAAA,IACd,QAAQ,IAAI;AAAA,IACZ,OAAO,IAAI,WAAW;AAAA,EACxB,EAAE;AACJ;AAEA,SAAS,cAAc;AACrB,SACE,gBAAAA,MAAC,SAAI,WAAU,2BACb;AAAA,oBAAAD,KAAC,SAAI,WAAU,4CAA2C;AAAA,IAC1D,gBAAAA,KAAC,SAAI,WAAU,sGACZ,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,QACjC,gBAAAC,MAAC,SAAc,WAAU,uDACvB;AAAA,sBAAAD,KAAC,SAAI,WAAU,mCAAkC;AAAA,MACjD,gBAAAA,KAAC,SAAI,WAAU,sCAAqC;AAAA,SAF5C,GAGV,CACD,GACH;AAAA,KACF;AAEJ;AAkBO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AACf,GAAoB;AAClB,QAAM,MAAM,aAAa,YAAY,UAAU,YAAY,UAAU;AAGrE,QAAM,cAAc;AAAA,IAClB,MAAO,cAAc,EAAE,MAAM,YAAY,IAAI;AAAA,IAC7C,CAAC,WAAW;AAAA,EACd;AACA,QAAM,EAAE,MAAM,WAAW,MAAM,IAAI,aAA2B,KAAK,EAAE,YAAY,CAAC;AAElF,QAAM,OAAO,MAAM,QAAQ,CAAC;AAC5B,QAAM,aAAa,UAAU,YAAY,aAAa,YAAY;AAClE,QAAM,cACJ,YAAY,SACV;AAAA;AAAA;AAAA;AAAA,IAIE,gBAAAA,KAAC,QAAG,WAAU,oDAAoD,gCAAqB;AAAA,MAEvF,gBAAAA,KAAC,QAAG,WAAU,oDAAoD,gCAAqB,IAGzF;AAIJ,MAAI,SAAS,YAAY;AACvB,WAAO;AAAA,EACT;AAKA,MAAI,CAAC,aAAa,CAAC,SAAS,KAAK,WAAW,KAAK,YAAY;AAC3D,WAAO;AAAA,EACT;AAEA,MAAI,aAAa,KAAK,WAAW,GAAG;AAIlC,QAAI,YAAY;AACd,aACE,gBAAAA,KAAC,SAAI,WACH,0BAAAA,KAAC,eAAY,GACf;AAAA,IAEJ;AACA,WACE,gBAAAA,KAAC,UAAK,WAAW,aAAa,aAC5B,0BAAAA,KAAC,SAAI,WAAU,sDACb,0BAAAA,KAAC,eAAY,GACf,GACF;AAAA,EAEJ;AAEA,MAAI,SAAS,KAAK,WAAW,GAAG;AAC9B,WACE,gBAAAA,KAAC,UAAK,WAAW,aAAa,oDAC5B,0BAAAA,KAAC,OAAE,WAAU,qCAAoC,0DAA4C,GAC/F;AAAA,EAEJ;AAEA,QAAM,SAAS,aAAa,uBAAuB,MAAM,MAAM,IAAI;AAOnE,QAAM,YAAY,gBAAAA,KAAC,gBAAa,OAAO,iBAAiB,IAAI,GAAG;AAC/D,QAAM,OAAO,aACX,gBAAAC,MAAC,aAAQ,WAAW,aAAa,cAC9B;AAAA;AAAA,IACA;AAAA,KACH,IAEA,gBAAAD,KAAC,UAAK,WAAW,aAAa,aAC5B,0BAAAC,MAAC,SAAI,WAAU,iEACZ;AAAA;AAAA,IACA;AAAA,KACH,GACF;AAGF,SACE,gBAAAA,MAAA,YACG;AAAA;AAAA,IACA,UACC,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QAIL,yBAAyB,EAAE,QAAQ,gBAAgB,MAAM,EAAE;AAAA;AAAA,IAC7D;AAAA,KAEJ;AAEJ;","names":["jsx","jsxs"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-J5R3QIOJ.cjs","../src/components/faq/faq-section.tsx","../src/components/faq-accordion.tsx","../src/components/faq/json-ld.ts"],"names":["jsx"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-J5R3QIOJ.cjs","sourcesContent":[null,"\"use client\"\n\nimport React, { useMemo } 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 { buildFaqJsonLdFromFaqs, type FaqSchemaOptions } from './json-ld'\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. `undefined` → variant default (page: <h1>, embedded: <h2>\n * \"Frequently Asked Questions\"); `null` → no heading. React node so\n * platforms can drill their local <PageHeading> component without the lib\n * referencing platform state.\n */\n heading?: React.ReactNode | null\n /**\n * Shell variant — the FAQ LIST itself renders identically everywhere (ONE\n * flat accordion, `faq.section` as a per-row chip, never a heading):\n * - 'page' — the standalone /faqs surface: <main> shell + page gutters,\n * default <h1> heading.\n * - 'embedded' — a section inside a host page (entity detail rails, the\n * global nested-page block): bare <section>, default <h2> heading.\n * Back-compat default: `heading === null` → 'embedded', else 'page' (the\n * pre-variant discriminator).\n */\n variant?: 'page' | 'embedded'\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/** ONE list shape for every surface — standalone /faqs and embeds render the\n * IDENTICAL flat accordion (`faq.section` = per-row chip, never a heading),\n * so the two can't drift visually. */\nfunction toAccordionItems(faqs: Faq[]): FaqItem[] {\n return faqs.map((faq) => ({\n id: faq.id,\n question: faq.question,\n answer: faq.answer,\n badge: faq.section || undefined,\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 * Generic, embeddable FAQ display surface.\n *\n * - Standalone /faqs page: pass `initialFaqs` from the server + `heading` +\n * `emitJsonLd` with `jsonLd` overrides for SEO.\n * - Per-entity embed: pass `entityType` + `entityId` (no `initialFaqs`); the\n * hook self-fetches and the public /api/faqs endpoint picks override-vs-fallback.\n *\n * CONTRACT: the consuming app MUST implement `GET /api/faqs`. Without it,\n * useSelfFetch reports `error:true` (logged once); in embedded mode (heading\n * === null) this component renders nothing so the host page isn't disfigured.\n */\nexport function FaqSection({\n initialFaqs,\n entityType,\n entityId,\n heading,\n variant,\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 const isEmbedded = variant ? variant === 'embedded' : heading === null\n const headingNode =\n heading === undefined ? (\n isEmbedded ? (\n // Embedded default is an <h2> — a host page already owns the <h1>,\n // and the schema/heading pair must read \"Frequently Asked Questions\"\n // (one FAQ heading per page, never tag/section names).\n <h2 className=\"text-h2 tracking-[-0.04em] text-ods-text-primary\">{DEFAULT_HEADING_TEXT}</h2>\n ) : (\n <h1 className=\"text-h1 tracking-[-0.04em] text-ods-text-primary\">{DEFAULT_HEADING_TEXT}</h1>\n )\n ) : (\n heading\n )\n\n // Embedded mode: degrade silently on error.\n if (error && isEmbedded) {\n return null\n }\n\n // Embedded mode, zero FAQs after load: render nothing — an embedded host\n // page (blog/case-study detail) must not show an empty section shell.\n // The standalone /faqs page keeps its existing behavior.\n if (!isLoading && !error && faqs.length === 0 && isEmbedded) {\n return null\n }\n\n if (isLoading && faqs.length === 0) {\n // Embedded mode renders skeletons WITHOUT the standalone page shell —\n // a host detail page already provides <main> + gutters; nesting them\n // here would emit invalid markup and double padding.\n if (isEmbedded) {\n return (\n <div className={className}>\n <FaqSkeleton />\n </div>\n )\n }\n return (\n <main className={className ?? 'bg-ods-bg'}>\n <div className=\"max-w-[1920px] px-6 md:px-20 py-6 md:py-10 mx-auto\">\n <FaqSkeleton />\n </div>\n </main>\n )\n }\n\n if (error && faqs.length === 0) {\n return (\n <main className={className ?? 'bg-ods-bg flex items-center justify-center py-20'}>\n <p className=\"text-ods-text-primary font-medium\">Failed to load FAQs. Please try again later.</p>\n </main>\n )\n }\n\n const schema = emitJsonLd ? buildFaqJsonLdFromFaqs(faqs, jsonLd) : null\n\n // ONE flat accordion on EVERY surface — `faq.section` becomes a per-row\n // chip, NOT a heading, so the page outline stays heading + h3(questions).\n // Order is the server's (post-specific first, then reused) — grouping by\n // section here would destroy it. The variants differ ONLY in shell:\n // standalone /faqs keeps its <main> + page gutters, embeds render bare.\n const accordion = <FaqAccordion items={toAccordionItems(faqs)} />\n const body = isEmbedded ? (\n <section className={className ?? 'space-y-10'}>\n {headingNode}\n {accordion}\n </section>\n ) : (\n <main className={className ?? 'bg-ods-bg'}>\n <div className=\"max-w-[1920px] px-6 md:px-20 py-6 md:py-10 mx-auto space-y-10\">\n {headingNode}\n {accordion}\n </div>\n </main>\n )\n\n return (\n <>\n {body}\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 { StatusBadge } from './ui/status-badge'\nimport { TAG_BADGE_CLASS } from './features/entity-tag-badges'\nimport { cn } from \"../utils/cn\"\n\nexport interface FaqItem {\n id: number | string\n question: string\n answer: string\n /** Optional category label (faq.section = tag name) rendered under the\n * question as the product's standard tag badge — the same StatusBadge\n * card skin EntityTagBadges uses everywhere (never a heading, so the\n * document outline stays h2/h3 only). */\n badge?: 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=\"flex min-w-0 flex-col items-start gap-2 pr-4\">\n <h3>\n {item.question}\n </h3>\n {item.badge && (\n <StatusBadge\n text={item.badge.toUpperCase()}\n variant=\"card\"\n className={TAG_BADGE_CLASS}\n />\n )}\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"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-TT2VBR7P.cjs","../src/components/ui/status-badge.tsx","../src/components/features/entity-tag-badges.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-TT2VBR7P.cjs","sourcesContent":[null,"\"use client\"\n\nimport React from 'react';\nimport { cn } from '../../utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\n\nconst statusBadgeVariants = cva(\n \"inline-flex items-center justify-center rounded font-mono font-medium uppercase tracking-wide\",\n {\n variants: {\n variant: {\n card: \"px-3 py-1.5 text-sm\",\n button: \"px-2 py-0.5 text-[10px] leading-none\",\n },\n colorScheme: {\n cyan: \"bg-[var(--ods-flamingo-cyan-base)] text-ods-text-on-accent\",\n pink: \"bg-[var(--ods-flamingo-pink-base)] text-ods-text-on-accent\",\n yellow: \"bg-[var(--ods-flamingo-yellow-base)] text-ods-text-on-accent border border-[var(--ods-system-greys-black)]\",\n green: \"bg-[var(--ods-flamingo-green-base)] text-ods-text-on-accent\",\n purple: \"bg-[var(--ods-flamingo-purple-base)] text-ods-text-on-accent\",\n success: \"bg-[var(--ods-attention-green-success-secondary)] text-[var(--ods-attention-green-success)]\",\n error: \"bg-[var(--ods-attention-red-error-secondary)] text-[var(--ods-attention-red-error)]\",\n warning: \"bg-[var(--ods-attention-yellow-warning-secondary)] text-[var(--ods-attention-yellow-warning)]\",\n default: \"bg-ods-bg-secondary text-ods-text-primary\",\n // Border-only variants (no background) - for task type badges\n accentBorder: \"bg-transparent border-2 text-ods-accent border-ods-accent\",\n errorBorder: \"bg-transparent border-2 text-[var(--ods-attention-red-error)] border-[var(--ods-attention-red-error)]\",\n whiteBorder: \"bg-transparent border-2 text-ods-text-primary border-ods-text-primary\",\n },\n },\n defaultVariants: {\n variant: \"card\",\n colorScheme: \"default\",\n },\n }\n);\n\nexport interface StatusBadgeProps\n extends React.HTMLAttributes<HTMLSpanElement>,\n VariantProps<typeof statusBadgeVariants> {\n text: string;\n /**\n * When true, renders `text` verbatim on a single line, bypassing the\n * default multi-word vertical-stack behavior used by `variant=\"button\"`.\n * Use this for compact inline contexts (e.g. chat-inline roadmap cards)\n * where the stamp-like stacked layout is undesirable.\n */\n singleLine?: boolean;\n}\n\nfunction StatusBadge({\n text,\n variant,\n colorScheme,\n className,\n singleLine,\n ...props\n}: StatusBadgeProps) {\n // Outer element is `<span>` so the badge is HTML-valid in any inline\n // context (e.g. inside a markdown `<p>` next to a compact chat card,\n // or inside an `<a>`). The `inline-flex` base class in\n // `statusBadgeVariants` keeps the layout identical to the previous\n // `<div>` outer — only the element name changed.\n //\n // Escape hatch: callers can pass `singleLine` to opt out of the\n // multi-word stacking applied for `variant=\"button\"`. This is needed\n // for compact inline contexts (chat-inline roadmap cards) where the\n // default stamp-like vertical stack (\"TO\" / \"DO\") breaks layout.\n const renderText = () => {\n if (singleLine) return text;\n if (variant === 'button' && text.includes(' ')) {\n const words = text.split(' ');\n return (\n <span className=\"flex flex-col items-center justify-center text-center gap-0\">\n {words.map((word, index) => (\n <span key={index} className=\"block\">{word}</span>\n ))}\n </span>\n );\n }\n return text;\n };\n\n return (\n <span\n className={cn(statusBadgeVariants({ variant, colorScheme }), className)}\n {...props}\n >\n {renderText()}\n </span>\n );\n}\n\nexport { StatusBadge, statusBadgeVariants };","import Link from 'next/link'\nimport { StatusBadge } from '../ui/status-badge'\nimport type { TagAssoc } from '../../types/blog'\n\ninterface EntityTagBadgesProps {\n /** Flat `<entity>_tags[]` association array (from entity_tags hydration). */\n tags?: TagAssoc[] | null\n /**\n * When set, each badge links to `${basePath}?tags=${slug}` (clickable, SPA nav\n * via next/link). Omit for a non-interactive display (badges with no slug also\n * render non-interactive even when basePath is set).\n */\n basePath?: string\n /** Cap visible badges; the remainder collapse into a \"+N\" badge. */\n max?: number\n /** Extra classes on the wrapper row. */\n className?: string\n}\n\n// The ONE tag-badge skin (OpenFrame design): rounded-rect, font-mono uppercase\n// StatusBadge on ods-card + ods-border. Clickable badges add the accent hover.\n// Exported so other tag-shaped chips (e.g. FaqAccordion's section badge)\n// render the IDENTICAL skin without re-declaring it.\nexport const TAG_BADGE_CLASS = 'bg-ods-card border border-ods-border'\nconst BADGE_CLASS = TAG_BADGE_CLASS\n\n/**\n * THE single tag-badge renderer for the whole product (hub + lib). Renders the\n * OpenFrame `StatusBadge` chip skin for a flat `<entity>_tags[]` array, optionally\n * clickable (links each tag to its filtered list via `basePath`). The hub's\n * `EntityTagList` delegates here so there is exactly one tag-display design.\n * Renders nothing when there are no tags.\n */\nexport function EntityTagBadges({ tags, basePath, max, className }: EntityTagBadgesProps) {\n const items = (tags || []).filter((t): t is TagAssoc => !!t && !!t.name)\n if (items.length === 0) return null\n\n // Only null/undefined means \"no cap\"; max={0} shows none, negatives clamp to 0.\n const visibleLimit = max == null ? items.length : Math.max(0, max)\n const shown = items.slice(0, visibleLimit)\n const overflow = items.length - shown.length\n\n return (\n <div className={`flex flex-wrap items-center gap-2 w-full ${className || ''}`}>\n {shown.map((tag) => {\n const key = tag.tag_id ?? tag.id ?? tag.slug ?? tag.name\n const label = (tag.name || '').toUpperCase()\n if (basePath && tag.slug) {\n return (\n <Link key={key} href={`${basePath}?tags=${tag.slug}`} className=\"inline-flex\">\n <StatusBadge\n text={label}\n variant=\"card\"\n className={`${BADGE_CLASS} cursor-pointer transition-colors hover:border-ods-accent`}\n />\n </Link>\n )\n }\n return <StatusBadge key={key} text={label} variant=\"card\" className={BADGE_CLASS} />\n })}\n {overflow > 0 && (\n <StatusBadge text={`+${overflow}`} variant=\"card\" className={`${BADGE_CLASS} text-ods-text-secondary`} />\n )}\n </div>\n )\n}\n"]}
|