@certrev/cert-block 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builder/index.d.ts +54 -52
- package/dist/builder/index.d.ts.map +1 -1
- package/dist/builder/index.js +64 -47
- package/dist/builder/index.js.map +1 -1
- package/dist/components/CertBadge.d.ts +18 -3
- package/dist/components/CertBadge.d.ts.map +1 -1
- package/dist/components/CertBadge.js +8 -2
- package/dist/components/CertBadge.js.map +1 -1
- package/dist/components/CertReview.d.ts +7 -0
- package/dist/components/CertReview.d.ts.map +1 -1
- package/dist/components/CertReview.js +1 -1
- package/dist/components/CertReview.js.map +1 -1
- package/dist/components/format.d.ts +14 -2
- package/dist/components/format.d.ts.map +1 -1
- package/dist/components/format.js +18 -5
- package/dist/components/format.js.map +1 -1
- package/dist/components/render-cert-block.d.ts +89 -0
- package/dist/components/render-cert-block.d.ts.map +1 -0
- package/dist/components/render-cert-block.js +282 -0
- package/dist/components/render-cert-block.js.map +1 -0
- package/dist/components/render-def.d.ts +178 -0
- package/dist/components/render-def.d.ts.map +1 -0
- package/dist/components/render-def.js +204 -0
- package/dist/components/render-def.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/webcomponent/render-badge-html.d.ts +8 -0
- package/dist/webcomponent/render-badge-html.d.ts.map +1 -1
- package/dist/webcomponent/render-badge-html.js +31 -4
- package/dist/webcomponent/render-badge-html.js.map +1 -1
- package/package.json +3 -2
- package/src/__tests__/__snapshots__/render-cert-block.test.ts.snap +13 -0
- package/src/__tests__/components.test.tsx +16 -1
- package/src/__tests__/render-cert-block.test.ts +259 -0
- package/src/__tests__/render-def.test.tsx +286 -0
- package/src/__tests__/webcomponent.test.tsx +10 -0
- package/src/builder/__tests__/builder.test.tsx +38 -51
- package/src/builder/index.tsx +86 -64
- package/src/components/CertBadge.tsx +56 -4
- package/src/components/CertReview.tsx +8 -0
- package/src/components/format.ts +22 -5
- package/src/components/render-cert-block.ts +403 -0
- package/src/components/render-def.ts +286 -0
- package/src/index.ts +35 -0
- package/src/webcomponent/render-badge-html.ts +46 -4
package/dist/builder/index.d.ts
CHANGED
|
@@ -1,73 +1,75 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @certrev/cert-block/builder —
|
|
2
|
+
* @certrev/cert-block/builder — Builder.io adapter, "ambient-URL anchor" model.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Validated by an independent architecture panel (Codex gpt-5.5 + Gemini, blind): a visual
|
|
5
|
+
* page builder must NOT choose WHICH credential renders — that invites cross-article misuse and
|
|
6
|
+
* re-opens the wrong-subject surface. Instead:
|
|
7
|
+
* • the CMS controls LAYOUT — an editor drags a ZERO-INPUT <CertRevAnchor/> to position the
|
|
8
|
+
* badge on the page;
|
|
9
|
+
* • the SYSTEM controls TRUTH — the headless loader resolves THIS page's credential from its
|
|
10
|
+
* own URL (Track-1 delivery), crypto-verifies it (fail-closed VerdictKernel), and supplies
|
|
11
|
+
* the verdict via context.
|
|
12
|
+
* The anchor renders the current page's verified badge or NOTHING. It cannot be pointed at
|
|
13
|
+
* another article, and there is no `placementId` input to forge or mis-select.
|
|
9
14
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* tampered / expired / wrong-subject placement that an editor drops into Builder renders
|
|
14
|
-
* nothing, even though the block is present in the published content.
|
|
15
|
+
* JSON-LD is DECOUPLED (panel finding): the anchor renders the BADGE only (`omitJsonLd`). The
|
|
16
|
+
* article template emits the schema.org JSON-LD server-side from the same verified verdict, so a
|
|
17
|
+
* page builder can't duplicate, move, or omit structured data. (See the route example.)
|
|
15
18
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* EDGE-CACHE DISCIPLINE (panel finding): the consumer's loader MUST verify per request as a
|
|
20
|
+
* dynamic edge subrequest and must NOT long-cache the rendered badge HTML — a revoked/expired
|
|
21
|
+
* credential sitting in a stale edge cache would defeat fail-closed. Bound any positive-verdict
|
|
22
|
+
* cache by min(expiry, revocation-TTL, content-version).
|
|
23
|
+
*
|
|
24
|
+
* cert-block carries no Builder dependency: the registration shape is declared structurally, and
|
|
25
|
+
* `isEditing` is a passed-in flag (the consumer supplies Builder's `isPreviewing()`).
|
|
21
26
|
*/
|
|
22
27
|
import { type ReactNode } from 'react';
|
|
23
28
|
import type { CertVerdict } from '../contract/kernel.js';
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
export interface VerifiedPlacement {
|
|
29
|
+
/** The current page's credential, resolved + verified by the loader (Track-1 delivery). */
|
|
30
|
+
export interface CurrentCredential {
|
|
27
31
|
readonly verdict: CertVerdict;
|
|
32
|
+
/** Canonical page URL for JSON-LD @id alignment (used by the server-side JSON-LD, not the anchor). */
|
|
28
33
|
readonly pageUrl?: string;
|
|
29
34
|
}
|
|
30
|
-
/**
|
|
31
|
-
*
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
*
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Supplies THIS page's loader-verified credential to the anchor. `current` is the verdict for
|
|
37
|
+
* the current page (or null if the page has no delivered credential). `isEditing` is the
|
|
38
|
+
* consumer's Builder `isPreviewing()` result — it ONLY toggles a design-time placeholder and
|
|
39
|
+
* NEVER affects the published render.
|
|
40
|
+
*/
|
|
41
|
+
export declare function CertRevProvider({ current, isEditing, children, }: {
|
|
42
|
+
readonly current: CurrentCredential | null;
|
|
43
|
+
readonly isEditing?: boolean;
|
|
37
44
|
readonly children: ReactNode;
|
|
38
45
|
}): import("react").JSX.Element;
|
|
39
|
-
export interface CertReviewBlockProps {
|
|
40
|
-
/** The CertREV placement id the editor set on this Builder block. */
|
|
41
|
-
readonly placementId?: string;
|
|
42
|
-
}
|
|
43
46
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
|
|
47
|
+
* Design-time placeholder shown ONLY in the Builder editor when the current page has no live
|
|
48
|
+
* verified credential. Truthful (never a fake badge), emits NO JSON-LD, and never renders in
|
|
49
|
+
* production — so it cannot leak to or be crawled on the live site.
|
|
50
|
+
*/
|
|
51
|
+
export declare function CertRevEditorPlaceholder(): import("react").JSX.Element;
|
|
52
|
+
/**
|
|
53
|
+
* The Builder block. ZERO inputs — a layout anchor only. Renders the current page's VERIFIED
|
|
54
|
+
* badge (badge only; JSON-LD is emitted server-side), the editor placeholder in design mode, or
|
|
55
|
+
* NOTHING in production (fail-closed). The credential is whatever Track-1 delivered + verified for
|
|
56
|
+
* THIS page; the editor can neither choose nor forge it.
|
|
48
57
|
*/
|
|
49
|
-
export declare function
|
|
58
|
+
export declare function CertRevAnchor(): import("react").JSX.Element | null;
|
|
50
59
|
/**
|
|
51
|
-
* Structural shape of `@builder.io/sdk-react`'s `RegisteredComponent` —
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* `RegisteredComponent` at the consumer's `<Content customComponents={[...]}>` call site.
|
|
60
|
+
* Structural shape of `@builder.io/sdk-react`'s `RegisteredComponent` — the subset cert-block
|
|
61
|
+
* populates. Declared locally so cert-block needs no Builder build/runtime dependency; assignable
|
|
62
|
+
* to Builder's `RegisteredComponent` at the consumer's `<Content customComponents={[...]}>`.
|
|
55
63
|
*/
|
|
56
|
-
export interface
|
|
57
|
-
component: typeof
|
|
64
|
+
export interface CertRevBuilderRegistration {
|
|
65
|
+
component: typeof CertRevAnchor;
|
|
58
66
|
name: string;
|
|
59
|
-
inputs:
|
|
60
|
-
name: string;
|
|
61
|
-
type: 'string';
|
|
62
|
-
required?: boolean;
|
|
63
|
-
helperText?: string;
|
|
64
|
-
}>;
|
|
67
|
+
inputs: never[];
|
|
65
68
|
}
|
|
66
69
|
/**
|
|
67
|
-
* The registration
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* Builder Write API (no visual-editor registration required for rendering).
|
|
70
|
+
* The registration to pass to Builder's `<Content customComponents={[...]}>`. ZERO inputs by
|
|
71
|
+
* design — the editor places it (layout); the system resolves the credential from the page URL
|
|
72
|
+
* (truth). Matched by NAME at render, so Write-API content works without visual-editor setup.
|
|
71
73
|
*/
|
|
72
|
-
export declare const
|
|
74
|
+
export declare const certRevAnchorComponent: CertRevBuilderRegistration;
|
|
73
75
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/builder/index.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/builder/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAiB,KAAK,SAAS,EAAc,MAAM,OAAO,CAAA;AAEjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,2FAA2F;AAC3F,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,sGAAsG;IACtG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CACzB;AASD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAC/B,OAAO,EACP,SAAiB,EACjB,QAAQ,GACR,EAAE;IACF,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAC1C,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;CAC5B,+BAEA;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,gCAiBvC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,uCAO5B;AAED;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IAC1C,SAAS,EAAE,OAAO,aAAa,CAAA;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,KAAK,EAAE,CAAA;CACf;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,0BAIpC,CAAA"}
|
package/dist/builder/index.js
CHANGED
|
@@ -1,63 +1,80 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
|
-
* @certrev/cert-block/builder —
|
|
3
|
+
* @certrev/cert-block/builder — Builder.io adapter, "ambient-URL anchor" model.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* Validated by an independent architecture panel (Codex gpt-5.5 + Gemini, blind): a visual
|
|
6
|
+
* page builder must NOT choose WHICH credential renders — that invites cross-article misuse and
|
|
7
|
+
* re-opens the wrong-subject surface. Instead:
|
|
8
|
+
* • the CMS controls LAYOUT — an editor drags a ZERO-INPUT <CertRevAnchor/> to position the
|
|
9
|
+
* badge on the page;
|
|
10
|
+
* • the SYSTEM controls TRUTH — the headless loader resolves THIS page's credential from its
|
|
11
|
+
* own URL (Track-1 delivery), crypto-verifies it (fail-closed VerdictKernel), and supplies
|
|
12
|
+
* the verdict via context.
|
|
13
|
+
* The anchor renders the current page's verified badge or NOTHING. It cannot be pointed at
|
|
14
|
+
* another article, and there is no `placementId` input to forge or mis-select.
|
|
10
15
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* tampered / expired / wrong-subject placement that an editor drops into Builder renders
|
|
15
|
-
* nothing, even though the block is present in the published content.
|
|
16
|
+
* JSON-LD is DECOUPLED (panel finding): the anchor renders the BADGE only (`omitJsonLd`). The
|
|
17
|
+
* article template emits the schema.org JSON-LD server-side from the same verified verdict, so a
|
|
18
|
+
* page builder can't duplicate, move, or omit structured data. (See the route example.)
|
|
16
19
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
20
|
+
* EDGE-CACHE DISCIPLINE (panel finding): the consumer's loader MUST verify per request as a
|
|
21
|
+
* dynamic edge subrequest and must NOT long-cache the rendered badge HTML — a revoked/expired
|
|
22
|
+
* credential sitting in a stale edge cache would defeat fail-closed. Bound any positive-verdict
|
|
23
|
+
* cache by min(expiry, revocation-TTL, content-version).
|
|
24
|
+
*
|
|
25
|
+
* cert-block carries no Builder dependency: the registration shape is declared structurally, and
|
|
26
|
+
* `isEditing` is a passed-in flag (the consumer supplies Builder's `isPreviewing()`).
|
|
22
27
|
*/
|
|
23
28
|
import { createContext, useContext } from 'react';
|
|
24
29
|
import { CertReview } from '../components/CertReview.js';
|
|
25
|
-
const
|
|
26
|
-
/**
|
|
27
|
-
*
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
const CertRevContext = createContext({ current: null, isEditing: false });
|
|
31
|
+
/**
|
|
32
|
+
* Supplies THIS page's loader-verified credential to the anchor. `current` is the verdict for
|
|
33
|
+
* the current page (or null if the page has no delivered credential). `isEditing` is the
|
|
34
|
+
* consumer's Builder `isPreviewing()` result — it ONLY toggles a design-time placeholder and
|
|
35
|
+
* NEVER affects the published render.
|
|
36
|
+
*/
|
|
37
|
+
export function CertRevProvider({ current, isEditing = false, children, }) {
|
|
38
|
+
return _jsx(CertRevContext.Provider, { value: { current, isEditing }, children: children });
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Design-time placeholder shown ONLY in the Builder editor when the current page has no live
|
|
42
|
+
* verified credential. Truthful (never a fake badge), emits NO JSON-LD, and never renders in
|
|
43
|
+
* production — so it cannot leak to or be crawled on the live site.
|
|
44
|
+
*/
|
|
45
|
+
export function CertRevEditorPlaceholder() {
|
|
46
|
+
return (_jsxs("div", { "data-certrev-editor-placeholder": "", style: {
|
|
47
|
+
border: '1px dashed #e6007e',
|
|
48
|
+
borderRadius: 8,
|
|
49
|
+
padding: '10px 14px',
|
|
50
|
+
font: '13px system-ui',
|
|
51
|
+
color: '#6b7280',
|
|
52
|
+
background: '#fff5fa',
|
|
53
|
+
}, children: ["CertREV Review \u2014 ", _jsx("b", { children: "resolves at publish" }), " from issuer verification. The expert badge appears here once this page\u2019s article is certified, and only while the credential is valid."] }));
|
|
30
54
|
}
|
|
31
55
|
/**
|
|
32
|
-
* The
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
56
|
+
* The Builder block. ZERO inputs — a layout anchor only. Renders the current page's VERIFIED
|
|
57
|
+
* badge (badge only; JSON-LD is emitted server-side), the editor placeholder in design mode, or
|
|
58
|
+
* NOTHING in production (fail-closed). The credential is whatever Track-1 delivered + verified for
|
|
59
|
+
* THIS page; the editor can neither choose nor forge it.
|
|
36
60
|
*/
|
|
37
|
-
export function
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
61
|
+
export function CertRevAnchor() {
|
|
62
|
+
const { current, isEditing } = useContext(CertRevContext);
|
|
63
|
+
if (current && current.verdict.decision === 'render') {
|
|
64
|
+
return _jsx(CertReview, { verdict: current.verdict, pageUrl: current.pageUrl, omitJsonLd: true });
|
|
65
|
+
}
|
|
66
|
+
if (isEditing)
|
|
67
|
+
return _jsx(CertRevEditorPlaceholder, {});
|
|
68
|
+
return null;
|
|
43
69
|
}
|
|
44
70
|
/**
|
|
45
|
-
* The registration
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* Builder Write API (no visual-editor registration required for rendering).
|
|
71
|
+
* The registration to pass to Builder's `<Content customComponents={[...]}>`. ZERO inputs by
|
|
72
|
+
* design — the editor places it (layout); the system resolves the credential from the page URL
|
|
73
|
+
* (truth). Matched by NAME at render, so Write-API content works without visual-editor setup.
|
|
49
74
|
*/
|
|
50
|
-
export const
|
|
51
|
-
component:
|
|
75
|
+
export const certRevAnchorComponent = {
|
|
76
|
+
component: CertRevAnchor,
|
|
52
77
|
name: 'CertREV Review',
|
|
53
|
-
inputs: [
|
|
54
|
-
{
|
|
55
|
-
name: 'placementId',
|
|
56
|
-
type: 'string',
|
|
57
|
-
required: true,
|
|
58
|
-
helperText: 'CertREV placement id (which certified article this badge attests). The headless ' +
|
|
59
|
-
'loader verifies that placement’s signed envelope; only a render-verdict shows a badge.',
|
|
60
|
-
},
|
|
61
|
-
],
|
|
78
|
+
inputs: [],
|
|
62
79
|
};
|
|
63
80
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/builder/index.tsx"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/builder/index.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,aAAa,EAAkB,UAAU,EAAE,MAAM,OAAO,CAAA;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAexD,MAAM,cAAc,GAAG,aAAa,CAAsB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAA;AAE9F;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,EAC/B,OAAO,EACP,SAAS,GAAG,KAAK,EACjB,QAAQ,GAKR;IACA,OAAO,KAAC,cAAc,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,YAAG,QAAQ,GAA2B,CAAA;AACpG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB;IACvC,OAAO,CACN,kDACiC,EAAE,EAClC,KAAK,EAAE;YACN,MAAM,EAAE,oBAAoB;YAC5B,YAAY,EAAE,CAAC;YACf,OAAO,EAAE,WAAW;YACpB,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,SAAS;YAChB,UAAU,EAAE,SAAS;SACrB,uCAEgB,8CAA0B,oJAEtC,CACN,CAAA;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa;IAC5B,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU,CAAC,cAAc,CAAC,CAAA;IACzD,IAAI,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACtD,OAAO,KAAC,UAAU,IAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,UAAU,SAAG,CAAA;IACrF,CAAC;IACD,IAAI,SAAS;QAAE,OAAO,KAAC,wBAAwB,KAAG,CAAA;IAClD,OAAO,IAAI,CAAA;AACZ,CAAC;AAaD;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAA+B;IACjE,SAAS,EAAE,aAAa;IACxB,IAAI,EAAE,gBAAgB;IACtB,MAAM,EAAE,EAAE;CACV,CAAA"}
|
|
@@ -11,19 +11,34 @@
|
|
|
11
11
|
* an inline custom property (no color-only meaning — the credential text carries it), an
|
|
12
12
|
* expert photo with meaningful alt text, and a verify link with a descriptive label.
|
|
13
13
|
*
|
|
14
|
-
* `badgeStyle: 'compact'` collapses to a single line (name + credentials
|
|
15
|
-
* `'full'` shows the photo, certified/updated dates, and the memo (per display flags).
|
|
14
|
+
* `badgeStyle: 'compact'` collapses the reviewer block to a single line (name + credentials);
|
|
15
|
+
* `'full'` shows the photo, certified/updated dates, and the memo (per display flags). BOTH
|
|
16
|
+
* styles carry the compensation cue + FTC scope line (the always-on compliance disclosures).
|
|
17
|
+
*
|
|
18
|
+
* THEMING (WS6): presentation defaults come from `payload.content.display`; an optional
|
|
19
|
+
* `renderDef` prop layers a brand's validated themeable tokens (emitted as `--certrev-*`
|
|
20
|
+
* CSS custom properties) and its subtractive field-visibility on top. No `renderDef` → the
|
|
21
|
+
* CertREV preset default (theme output byte-identical to un-themed).
|
|
16
22
|
*/
|
|
17
23
|
import type { CertPayload } from '../contract/kernel.js';
|
|
24
|
+
import { type CertRenderDef } from './render-def.js';
|
|
18
25
|
export interface CertBadgeProps {
|
|
19
26
|
/** The cryptographically-verified payload (from `getVerifiedEnvelope`). */
|
|
20
27
|
readonly payload: CertPayload;
|
|
21
|
-
/** Optional accent override (else `display.accentColor`, else CertREV default). */
|
|
28
|
+
/** Optional accent override (else render-def, else `display.accentColor`, else CertREV default). */
|
|
22
29
|
readonly accentColor?: string;
|
|
23
30
|
/** Extra class names appended to the root element's class list. */
|
|
24
31
|
readonly className?: string;
|
|
25
32
|
/** Override the badge style independent of the signed display config. */
|
|
26
33
|
readonly badgeStyle?: 'full' | 'compact';
|
|
34
|
+
/**
|
|
35
|
+
* The brand render-def (WS6). ABSENT → the badge renders the CertREV preset
|
|
36
|
+
* default (fail-safe; theme output byte-identical to un-themed). PRESENT → its
|
|
37
|
+
* validated themeable tokens are emitted as `--certrev-*` CSS custom properties
|
|
38
|
+
* and its subtractive hide-set removes brand-toggleable fields. Invalid tokens
|
|
39
|
+
* are dropped (defense-in-depth over the write-path gate).
|
|
40
|
+
*/
|
|
41
|
+
readonly renderDef?: CertRenderDef;
|
|
27
42
|
}
|
|
28
43
|
export declare function CertBadge(props: CertBadgeProps): import("react").JSX.Element;
|
|
29
44
|
//# sourceMappingURL=CertBadge.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CertBadge.d.ts","sourceRoot":"","sources":["../../src/components/CertBadge.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"CertBadge.d.ts","sourceRoot":"","sources":["../../src/components/CertBadge.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAe,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAGrE,OAAO,EAGN,KAAK,aAAa,EAGlB,MAAM,iBAAiB,CAAA;AAExB,MAAM,WAAW,cAAc;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,oGAAoG;IACpG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAA;CAClC;AAuCD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,+BA6H9C"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { safeHttpUrl } from './escape.js';
|
|
3
3
|
import { credentialSuffix, expertNameWithCredentials, formatDate, resolveDisplay } from './format.js';
|
|
4
|
+
import { CERT_SCOPE_LINE, CERT_STRINGS_VERSION, resolveRenderTheme, themeCssVarDeclarations, } from './render-def.js';
|
|
4
5
|
const ROOT_CLASS = 'certrev-badge';
|
|
5
6
|
/** Inline-SVG verified checkmark — no external asset, accent-tinted, decorative. */
|
|
6
7
|
function VerifiedMark({ accent }) {
|
|
@@ -15,7 +16,8 @@ function ExpertPhoto({ content, show }) {
|
|
|
15
16
|
export function CertBadge(props) {
|
|
16
17
|
const { payload } = props;
|
|
17
18
|
const content = payload.content;
|
|
18
|
-
const display = resolveDisplay(content.display, props.accentColor);
|
|
19
|
+
const display = resolveDisplay(content.display, props.accentColor, props.renderDef);
|
|
20
|
+
const theme = resolveRenderTheme(props.renderDef);
|
|
19
21
|
const style = props.badgeStyle ?? display.badgeStyle;
|
|
20
22
|
const verifyUrl = safeHttpUrl(content.verifyUrl);
|
|
21
23
|
const profileUrl = safeHttpUrl(content.expert.profileUrl);
|
|
@@ -26,11 +28,15 @@ export function CertBadge(props) {
|
|
|
26
28
|
const rootClass = `${ROOT_CLASS} ${ROOT_CLASS}--${style}${props.className ? ` ${props.className}` : ''}`;
|
|
27
29
|
// Accent is exposed as a custom property so brand themes can read it, and applied
|
|
28
30
|
// directly to the accent border so the component is self-styling without a stylesheet.
|
|
31
|
+
// A THEMED brand additionally emits the surface/radius/font vars; with no render-def
|
|
32
|
+
// `themeCssVarDeclarations` is empty, so the un-themed style is byte-identical.
|
|
29
33
|
const rootStyle = {
|
|
30
34
|
['--certrev-accent']: accent,
|
|
31
35
|
borderInlineStartColor: accent,
|
|
32
36
|
};
|
|
37
|
+
for (const [name, value] of themeCssVarDeclarations(theme))
|
|
38
|
+
rootStyle[name] = value;
|
|
33
39
|
const expertName = (_jsxs("span", { className: `${ROOT_CLASS}__expert-name`, children: [content.expert.displayName, suffix ? _jsxs("span", { className: `${ROOT_CLASS}__credentials`, children: [", ", suffix] }) : null] }));
|
|
34
|
-
return (_jsxs("section", { className: rootClass, style: rootStyle, "data-certrev-cert-id": payload.certId, "aria-label": `Content reviewed by ${expertNameWithCredentials(content)}`, children: [_jsxs("div", { className: `${ROOT_CLASS}__header`, children: [_jsx(VerifiedMark, { accent: accent }), style === 'full' ? _jsx(ExpertPhoto, { content: content, show: display.showExpertPhoto }) : null, _jsxs("div", { className: `${ROOT_CLASS}__heading`, children: [_jsx("span", { className: `${ROOT_CLASS}__eyebrow`, children: "Expert reviewed" }), _jsxs("span", { className: `${ROOT_CLASS}__byline`, children: ["Reviewed by", ' ', profileUrl ? (_jsx("a", { className: `${ROOT_CLASS}__expert-link`, href: profileUrl, rel: "noopener", children: expertName })) : (expertName)] })] })] }), style === 'full' ? (_jsxs(_Fragment, { children: [display.showMemo && content.memo ? _jsx("p", { className: `${ROOT_CLASS}__memo`, children: content.memo }) : null, display.showAuthor && content.author.name && content.author.name !== content.expert.displayName ? (_jsxs("p", { className: `${ROOT_CLASS}__author`, children: ["Written by ", content.author.name, content.author.title ? `, ${content.author.title}` : ''] })) : null, _jsxs("dl", { className: `${ROOT_CLASS}__dates`, children: [certifiedLabel ? (_jsxs("div", { className: `${ROOT_CLASS}__date`, children: [_jsx("dt", { children: "Certified" }), _jsx("dd", { children: _jsx("time", { dateTime: content.certifiedAt, children: certifiedLabel }) })] })) : null, updatedLabel ? (_jsxs("div", { className: `${ROOT_CLASS}__date`, children: [_jsx("dt", { children: "Last updated" }), _jsx("dd", { children: _jsx("time", { dateTime: content.contentModifiedAt ?? undefined, children: updatedLabel }) })] })) : null] })] })) : null, verifyUrl ? (_jsx("a", { className: `${ROOT_CLASS}__verify`, href: verifyUrl, rel: "noopener", "aria-label": "Verify this certification on CertREV", children: "Verify on CertREV" })) : null] }));
|
|
40
|
+
return (_jsxs("section", { className: rootClass, style: rootStyle, "data-certrev-cert-id": payload.certId, "data-certrev-strings-version": CERT_STRINGS_VERSION, "data-certrev-def-version": theme.version, "aria-label": `Content reviewed by ${expertNameWithCredentials(content)}`, children: [_jsxs("div", { className: `${ROOT_CLASS}__header`, children: [_jsx(VerifiedMark, { accent: accent }), style === 'full' ? _jsx(ExpertPhoto, { content: content, show: display.showExpertPhoto }) : null, _jsxs("div", { className: `${ROOT_CLASS}__heading`, children: [_jsx("span", { className: `${ROOT_CLASS}__eyebrow`, children: "Expert reviewed" }), _jsxs("span", { className: `${ROOT_CLASS}__byline`, children: ["Reviewed by", ' ', profileUrl ? (_jsx("a", { className: `${ROOT_CLASS}__expert-link`, href: profileUrl, rel: "noopener", children: expertName })) : (expertName)] })] })] }), style === 'full' ? (_jsxs(_Fragment, { children: [display.showMemo && content.memo ? _jsx("p", { className: `${ROOT_CLASS}__memo`, children: content.memo }) : null, display.showAuthor && content.author.name && content.author.name !== content.expert.displayName ? (_jsxs("p", { className: `${ROOT_CLASS}__author`, children: ["Written by ", content.author.name, content.author.title ? `, ${content.author.title}` : ''] })) : null, _jsxs("dl", { className: `${ROOT_CLASS}__dates`, children: [certifiedLabel ? (_jsxs("div", { className: `${ROOT_CLASS}__date`, children: [_jsx("dt", { children: "Certified" }), _jsx("dd", { children: _jsx("time", { dateTime: content.certifiedAt, children: certifiedLabel }) })] })) : null, updatedLabel ? (_jsxs("div", { className: `${ROOT_CLASS}__date`, children: [_jsx("dt", { children: "Last updated" }), _jsx("dd", { children: _jsx("time", { dateTime: content.contentModifiedAt ?? undefined, children: updatedLabel }) })] })) : null] })] })) : null, _jsxs("div", { className: `${ROOT_CLASS}__disclosure`, children: [_jsxs("span", { className: `${ROOT_CLASS}__compensated-cue`, children: ["Compensated", ' ', profileUrl ? (_jsx("a", { className: `${ROOT_CLASS}__cue-link`, href: profileUrl, rel: "noopener", children: "expert" })) : ('expert')] }), _jsx("p", { className: `${ROOT_CLASS}__ftc`, "data-ftc-disclosure": true, children: _jsx("span", { "data-ftc-line": true, children: CERT_SCOPE_LINE }) })] }), verifyUrl ? (_jsx("a", { className: `${ROOT_CLASS}__verify`, href: verifyUrl, rel: "noopener", "aria-label": "Verify this certification on CertREV", children: "Verify on CertREV" })) : null] }));
|
|
35
41
|
}
|
|
36
42
|
//# sourceMappingURL=CertBadge.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CertBadge.js","sourceRoot":"","sources":["../../src/components/CertBadge.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"CertBadge.js","sourceRoot":"","sources":["../../src/components/CertBadge.tsx"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* expert bio + backlink remain separately placeable for brands that want finer control.
|
|
9
9
|
*/
|
|
10
10
|
import type { CertVerdict } from '../contract/kernel.js';
|
|
11
|
+
import type { CertRenderDef } from './render-def.js';
|
|
11
12
|
export interface CertReviewProps {
|
|
12
13
|
/** The verdict from `getVerifiedEnvelope` (or a direct kernel call). */
|
|
13
14
|
readonly verdict: CertVerdict;
|
|
@@ -16,6 +17,12 @@ export interface CertReviewProps {
|
|
|
16
17
|
readonly accentColor?: string;
|
|
17
18
|
readonly className?: string;
|
|
18
19
|
readonly badgeStyle?: 'full' | 'compact';
|
|
20
|
+
/**
|
|
21
|
+
* The brand render-def (WS6). Forwarded to `<CertBadge>` for themeable-token
|
|
22
|
+
* emission + subtractive visibility; absent → the CertREV preset default. The
|
|
23
|
+
* JSON-LD is presentation-blind, so the def never touches the structured data.
|
|
24
|
+
*/
|
|
25
|
+
readonly renderDef?: CertRenderDef;
|
|
19
26
|
/** Omit the JSON-LD (e.g. the page already emits it elsewhere). Default false. */
|
|
20
27
|
readonly omitJsonLd?: boolean;
|
|
21
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CertReview.d.ts","sourceRoot":"","sources":["../../src/components/CertReview.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"CertReview.d.ts","sourceRoot":"","sources":["../../src/components/CertReview.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAGxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD,MAAM,WAAW,eAAe;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,uFAAuF;IACvF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAA;IAClC,kFAAkF;IAClF,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAC7B;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,sCAgBhD"}
|
|
@@ -6,6 +6,6 @@ export function CertReview(props) {
|
|
|
6
6
|
if (props.verdict.decision !== 'render')
|
|
7
7
|
return null;
|
|
8
8
|
const payload = props.verdict.payload;
|
|
9
|
-
return (_jsxs(_Fragment, { children: [_jsx(CertBadge, { payload: payload, accentColor: props.accentColor, className: props.className, badgeStyle: props.badgeStyle }), props.omitJsonLd ? null : _jsx(CertJsonLd, { payload: payload, pageUrl: props.pageUrl })] }));
|
|
9
|
+
return (_jsxs(_Fragment, { children: [_jsx(CertBadge, { payload: payload, accentColor: props.accentColor, className: props.className, badgeStyle: props.badgeStyle, renderDef: props.renderDef }), props.omitJsonLd ? null : _jsx(CertJsonLd, { payload: payload, pageUrl: props.pageUrl })] }));
|
|
10
10
|
}
|
|
11
11
|
//# sourceMappingURL=CertReview.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CertReview.js","sourceRoot":"","sources":["../../src/components/CertReview.tsx"],"names":[],"mappings":";AAWA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"CertReview.js","sourceRoot":"","sources":["../../src/components/CertReview.tsx"],"names":[],"mappings":";AAWA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAqB5C,MAAM,UAAU,UAAU,CAAC,KAAsB;IAChD,yEAAyE;IACzE,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IACpD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAA;IACrC,OAAO,CACN,8BACC,KAAC,SAAS,IACT,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,KAAK,CAAC,WAAW,EAC9B,SAAS,EAAE,KAAK,CAAC,SAAS,EAC1B,UAAU,EAAE,KAAK,CAAC,UAAU,EAC5B,SAAS,EAAE,KAAK,CAAC,SAAS,GACzB,EACD,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAC,UAAU,IAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,GAAI,IACjF,CACH,CAAA;AACF,CAAC"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* no React — so the same formatting drives JSX and the hand-built Web Component string.
|
|
4
4
|
*/
|
|
5
5
|
import type { CertContent, CertDisplayConfig } from '../contract/kernel.js';
|
|
6
|
+
import { type CertRenderDef } from './render-def.js';
|
|
6
7
|
/** Default accent if the brand didn't pin one (CertREV teal). */
|
|
7
8
|
export declare const DEFAULT_ACCENT = "#0f766e";
|
|
8
9
|
export interface ResolvedDisplay {
|
|
@@ -12,8 +13,19 @@ export interface ResolvedDisplay {
|
|
|
12
13
|
readonly showMemo: boolean;
|
|
13
14
|
readonly badgeStyle: 'full' | 'compact';
|
|
14
15
|
}
|
|
15
|
-
/**
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the optional display config + optional brand render-def to concrete
|
|
18
|
+
* presentation flags + accent.
|
|
19
|
+
*
|
|
20
|
+
* Accent PRECEDENCE mirrors the Shopify Liquid engine exactly: an explicit
|
|
21
|
+
* `accentOverride` (a block/prop override) wins, then the render-def's validated
|
|
22
|
+
* accent (a THEMED brand), then the signed display's accent, else the CertREV
|
|
23
|
+
* default. Visibility is SUBTRACTIVE: the render-def's hide-set can only turn a
|
|
24
|
+
* preset-placed field OFF (never on) — a hidden `reviewerPhoto` / `memo` wins over
|
|
25
|
+
* a display flag that shows it. When no render-def is supplied the theme resolves
|
|
26
|
+
* empty, so accent + visibility are byte-identical to the pre-theming behavior.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveDisplay(display: CertDisplayConfig | undefined, accentOverride?: string, renderDef?: CertRenderDef): ResolvedDisplay;
|
|
17
29
|
export declare function formatDate(iso: string | null | undefined): string | null;
|
|
18
30
|
/** "PhD, RD" — the expert's credential abbreviations, comma-joined. */
|
|
19
31
|
export declare function credentialSuffix(content: CertContent): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/components/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/components/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAC3E,OAAO,EAAE,KAAK,aAAa,EAAqC,MAAM,iBAAiB,CAAA;AAEvF,iEAAiE;AACjE,eAAO,MAAM,cAAc,YAAY,CAAA;AAEvC,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;CACvC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,iBAAiB,GAAG,SAAS,EACtC,cAAc,CAAC,EAAE,MAAM,EACvB,SAAS,CAAC,EAAE,aAAa,GACvB,eAAe,CASjB;AAUD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAMxE;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAE7D;AAED,8EAA8E;AAC9E,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAGtE"}
|
|
@@ -2,15 +2,28 @@
|
|
|
2
2
|
* Presentation helpers shared by the React components + Web Component. Pure, no DOM,
|
|
3
3
|
* no React — so the same formatting drives JSX and the hand-built Web Component string.
|
|
4
4
|
*/
|
|
5
|
+
import { isFieldHidden, resolveRenderTheme } from './render-def.js';
|
|
5
6
|
/** Default accent if the brand didn't pin one (CertREV teal). */
|
|
6
7
|
export const DEFAULT_ACCENT = '#0f766e';
|
|
7
|
-
/**
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the optional display config + optional brand render-def to concrete
|
|
10
|
+
* presentation flags + accent.
|
|
11
|
+
*
|
|
12
|
+
* Accent PRECEDENCE mirrors the Shopify Liquid engine exactly: an explicit
|
|
13
|
+
* `accentOverride` (a block/prop override) wins, then the render-def's validated
|
|
14
|
+
* accent (a THEMED brand), then the signed display's accent, else the CertREV
|
|
15
|
+
* default. Visibility is SUBTRACTIVE: the render-def's hide-set can only turn a
|
|
16
|
+
* preset-placed field OFF (never on) — a hidden `reviewerPhoto` / `memo` wins over
|
|
17
|
+
* a display flag that shows it. When no render-def is supplied the theme resolves
|
|
18
|
+
* empty, so accent + visibility are byte-identical to the pre-theming behavior.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveDisplay(display, accentOverride, renderDef) {
|
|
21
|
+
const theme = resolveRenderTheme(renderDef);
|
|
9
22
|
return {
|
|
10
|
-
accentColor: accentOverride ?? display?.accentColor ?? DEFAULT_ACCENT,
|
|
11
|
-
showExpertPhoto: display?.showExpertPhoto ?? true,
|
|
23
|
+
accentColor: accentOverride ?? theme.accent ?? display?.accentColor ?? DEFAULT_ACCENT,
|
|
24
|
+
showExpertPhoto: (display?.showExpertPhoto ?? true) && !isFieldHidden(renderDef, 'reviewerPhoto'),
|
|
12
25
|
showAuthor: display?.showAuthor ?? true,
|
|
13
|
-
showMemo: display?.showMemo ?? true,
|
|
26
|
+
showMemo: (display?.showMemo ?? true) && !isFieldHidden(renderDef, 'memo'),
|
|
14
27
|
badgeStyle: display?.badgeStyle ?? 'full',
|
|
15
28
|
};
|
|
16
29
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/components/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/components/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAsB,aAAa,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAEvF,iEAAiE;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAAA;AAUvC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAC7B,OAAsC,EACtC,cAAuB,EACvB,SAAyB;IAEzB,MAAM,KAAK,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAA;IAC3C,OAAO;QACN,WAAW,EAAE,cAAc,IAAI,KAAK,CAAC,MAAM,IAAI,OAAO,EAAE,WAAW,IAAI,cAAc;QACrF,eAAe,EAAE,CAAC,OAAO,EAAE,eAAe,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,eAAe,CAAC;QACjG,UAAU,EAAE,OAAO,EAAE,UAAU,IAAI,IAAI;QACvC,QAAQ,EAAE,CAAC,OAAO,EAAE,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,MAAM,CAAC;QAC1E,UAAU,EAAE,OAAO,EAAE,UAAU,IAAI,MAAM;KACzC,CAAA;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;AAEnG,MAAM,UAAU,UAAU,CAAC,GAA8B;IACxD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IACrB,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC1B,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAAE,OAAO,IAAI,CAAA;IACjC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAA;IACtB,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,cAAc,EAAE,EAAE,CAAA;AAC7E,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,gBAAgB,CAAC,OAAoB;IACpD,OAAO,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACxE,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,yBAAyB,CAAC,OAAoB;IAC7D,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACxC,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAA;AACxF,CAAC"}
|