All files / src/shared/HFInputs/HFRichInput blockStyle.ts

100% Statements 13/13
100% Branches 8/8
100% Functions 2/2
100% Lines 13/13

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118                  100x 605x 144x 144x 1x   144x 144x 9x   144x                   100x                 100x                                   100x                                                 100x                                                                        
import { ContentBlock } from "draft-js";
 
// The block-data → rendered-class contract shared by the editor (HFRichInput)
// and the read-only viewer (DocumentViewer). Passing a custom blockStyleFn
// replaces react-draft-wysiwyg's default one, which is what maps the
// `text-align` block data (written by the toolbar's Alignment buttons) to an
// `rdw-{align}-aligned-block` class — mirror that mapping or aligned blocks
// stop aligning.
export const buildBlockStyleFn =
  ({ paragraphSmall = true }: { paragraphSmall?: boolean } = {}) =>
  (block: ContentBlock): string => {
    const classes: string[] = [];
    if (paragraphSmall && block.getType() === "paragraph-small") {
      classes.push("rdw-paragraph-small");
    }
    const alignment = block.getData().get("text-align");
    if (alignment) {
      classes.push(`rdw-${alignment}-aligned-block`);
    }
    return classes.join(" ");
  };
 
// react-draft-wysiwyg's stylesheet embeds Draft.css, whose
// `.public-DraftStyleDefault-ltr { text-align: left }` sits directly on the
// inner block div — beating the `text-align: … !important` the
// `rdw-*-aligned-block` classes put on the wrapper (a direct rule wins over
// an inherited one). Its `> div { display: inline-block }` trick only visibly
// aligns single-line blocks, so align the inner div itself or wrapped
// paragraphs stay left-aligned.
export const alignmentInnerDivSx = {
  "& .rdw-left-aligned-block > div": { textAlign: "left" },
  "& .rdw-center-aligned-block > div": { textAlign: "center" },
  "& .rdw-right-aligned-block > div": { textAlign: "right" },
} as const;
 
// Wrapper rules for surfaces that never load react-draft-wysiwyg's stylesheet
// (public ToS/Privacy, agreement panels), where the rdw-* alignment classes
// are otherwise unstyled.
export const alignmentWrapperSx = {
  "& .rdw-left-aligned-block": { textAlign: "left" },
  "& .rdw-center-aligned-block": { textAlign: "center" },
  "& .rdw-right-aligned-block": { textAlign: "right" },
} as const;
 
// Heading scale for authored documents (legal documents), shared by the editor
// and the read-only viewer so an author sees what a merchant is served.
//
// Without it headings fall back to the browser defaults, where h4–h6 are the
// same size as body text or smaller — a pasted agreement's deeper headings then
// read as ordinary paragraphs. The editor additionally forces `font-weight: 400`
// on every block element (see HFRichInput's Container), which is why a heading
// there looked lighter than the same heading in the viewer.
//
// Declared as literals rather than pulled from `theme.typography` because the
// viewer also renders on public pages (ToS / Privacy) that mount neither MUI
// theme provider.
export const documentHeadingSx = {
  "& h1": { fontSize: "32px", fontWeight: 600 },
  "& h2": { fontSize: "26px", fontWeight: 600 },
  "& h3": { fontSize: "22px", fontWeight: 600 },
  "& h4": { fontSize: "19px", fontWeight: 600 },
  "& h5": { fontSize: "17px", fontWeight: 600 },
  "& h6": { fontSize: "16px", fontWeight: 600 },
} as const;
 
// List rendering for the read-only viewer. The rich editor renders list items
// as a custom `div::before` bullet with the NATIVE marker suppressed — split
// across two stylesheets: TextEditor.css (`.editorClassName ul/ol` → the "•"
// / counter bullet, `list-style-position: inside`) and HFRichInput's Container
// (`.editorClassName li { list-style-type: none }`, specificity 0,2,1, which
// beats Draft.css's `disc` on the `<li>` at 0,2,0).
//
// The viewer shares the editor's `.editorClassName` (ReadOnlyEditor renders it
// too) and the detail page statically imports the editor, so TextEditor.css
// leaks onto the viewer — the "•" bullet AND `list-style-position: inside`. But
// the Container's disc-suppression does NOT (it's on the editor's wrapper), so
// Draft.css's native `disc` survives and `inside` pushes it onto its own line:
// a stray extra bullet, mis-indented, above each item. Mirror BOTH halves here,
// scoped to `.editorClassName` so `& .editorClassName li` (0,2,1) beats
// Draft.css. Self-sufficient: also renders correctly on viewer surfaces that
// never load TextEditor.css (public ToS/Privacy, agreement panels).
export const listStyleSx = {
  // Suppress Draft.css's native `<li>` disc marker — the missing half that the
  // editor gets from its Container. This is what removes the extra bullet.
  "& .editorClassName li": {
    counterIncrement: "count 1",
    listStyleType: "none",
    marginLeft: 0,
    "&::before": { display: "none" },
  },
  "& .editorClassName ul, & .editorClassName ol": {
    listStylePosition: "inside",
    paddingLeft: 0,
    listStyleType: "none",
    // `count` is incremented on every <li> (incl. <ul> items). Reset it at each
    // list container so ordered-list numbers restart at 1 per <ol> instead of
    // running continuously across lists / being offset by preceding <ul> items.
    counterReset: "count",
  },
  "& .editorClassName ul li > div::before": {
    content: '"•"',
    unicodeBidi: "isolate",
    textTransform: "none",
    textIndent: "0px !important",
    textAlign: "start !important",
    textAlignLast: "start !important",
    marginRight: "5px",
  },
  "& .editorClassName ol li > div::before": {
    content: 'counter(count) ". "',
    unicodeBidi: "isolate",
    textTransform: "none",
    textIndent: "0px !important",
    textAlign: "start !important",
    textAlignLast: "start !important",
  },
} as const;