@stackone/malachite 2.69.0 → 2.71.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.
@@ -1 +1 @@
1
- {"version":3,"file":"MarkdownBlock.js","sources":["../../../src/components/Typography/MarkdownBlock.tsx"],"sourcesContent":["import Markdown, { ReactRenderer } from 'marked-react';\nimport React from 'react';\nimport type { RenderLink } from '../PageLayout/types';\nimport { FOCUS_RING } from '../focusRing';\nimport CodeText from './CodeText';\nimport PageTitle from './PageTitle';\nimport SectionTitle from './SectionTitle';\nimport type { FontSize, TextColor } from './Text';\nimport { getInlineMarked } from './inlineMarkdown';\nimport { getFontSize, getTextColor } from './textStyles';\n\n/** `inline`: one line of inline syntax, with no autolinked emails or URLs. */\nexport type MarkdownVariant = 'block' | 'flow' | 'inline';\n\n/** `inert` draws links without an anchor, for markdown inside a <button>. */\nexport type MarkdownLinks = 'interactive' | 'inert';\n\nexport interface MarkdownProps {\n text: string;\n color?: TextColor;\n size?: FontSize | 'inherit';\n omitMargin?: boolean;\n variant?: MarkdownVariant;\n normalizeSource?: boolean;\n links?: MarkdownLinks;\n /** Renders root-relative links (`/path`) through the app's router, in the same tab. Other\n * links still open in a new tab. Pass a stable function: a new one re-parses the markdown. */\n renderLink?: RenderLink;\n}\n\nconst FLOW_BULLET = '• ';\n\n// Inline, not `md-link`, for portals outside the shadow root; the link token is unreadable on tooltips.\nconst INERT_LINK_STYLE: React.CSSProperties = {\n color: 'inherit',\n textDecoration: 'underline',\n fontWeight: 600,\n};\n\n// Third-party content, and React does not sanitize href — `javascript:` would execute.\nconst ALLOWED_SCHEMES = new Set(['http:', 'https:', 'mailto:', 'tel:']);\n\nconst isSafeHref = (href: unknown): href is string => {\n if (typeof href !== 'string') {\n return false;\n }\n const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(href.replace(/[\\u0000-\\u0020]/g, ''));\n // No scheme means relative or fragment, which has nothing to vet.\n return !scheme || ALLOWED_SCHEMES.has(`${scheme[1].toLowerCase()}:`);\n};\n\n// `//host` and `/\\host` are protocol-relative to a browser, so they leave the app.\n// URL parsing drops tab, CR and LF, so `/\\t/evil.com` would resolve off-site.\nconst isRootRelative = (href: string): boolean => /^\\/(?![/\\\\])[^\\t\\n\\r]*$/.test(href);\n\nconst LINK_CLASSES = `md-link rounded-xs ${FOCUS_RING}`;\n\n// Renderer args are always arrays: never unwrap with `x[0]`, it drops all tokens after the first.\nconst getRendererFunction = ({\n omitMargin,\n links,\n renderLink,\n}: Pick<MarkdownProps, 'omitMargin' | 'links' | 'renderLink'>): Partial<ReactRenderer> => ({\n blockquote(this: ReactRenderer, quote) {\n return (\n <div\n key={this.elementId}\n style={{\n backgroundColor: 'var(--malachite-card-dimmed-background)',\n padding: '20px',\n borderRadius: '20px',\n border: '1px solid var(--malachite-card-border)',\n margin: '16px 0',\n }}\n >\n {quote}\n </div>\n );\n },\n\n paragraph(this: ReactRenderer, text) {\n return (\n <div key={this.elementId} style={{ marginBottom: omitMargin ? '0px' : '16px' }}>\n {text}\n </div>\n );\n },\n\n codespan(this: ReactRenderer, code) {\n return (\n <CodeText key={this.elementId} variant=\"inline\">\n {code}\n </CodeText>\n );\n },\n\n code(this: ReactRenderer, code) {\n return (\n <pre key={this.elementId} style={{ margin: omitMargin ? 0 : '16px 0' }}>\n <CodeText\n style={{\n display: 'block',\n whiteSpace: 'pre',\n overflowX: 'auto',\n padding: '12px',\n }}\n >\n {code}\n </CodeText>\n </pre>\n );\n },\n\n heading(this: ReactRenderer, text, level) {\n return level === 1 ? (\n <PageTitle key={this.elementId}>{text}</PageTitle>\n ) : (\n <SectionTitle key={this.elementId}>{text}</SectionTitle>\n );\n },\n\n br(this: ReactRenderer) {\n return <br key={this.elementId} />;\n },\n\n // `text`/`strong` are deliberately NOT overridden: a wrapping Text emits a font-weight, and\n // the nested 400 beat strong's 600 — which is why bold never rendered.\n\n link(this: ReactRenderer, href, children) {\n // Keep the label for a rejected scheme rather than deleting content.\n if (!isSafeHref(href)) {\n return <React.Fragment key={this.elementId}>{children}</React.Fragment>;\n }\n if (links === 'inert') {\n return (\n <span key={this.elementId} style={INERT_LINK_STYLE}>\n {children}\n </span>\n );\n }\n if (renderLink && isRootRelative(href)) {\n return (\n <React.Fragment key={this.elementId}>\n {renderLink({ href, children, className: LINK_CLASSES })}\n </React.Fragment>\n );\n }\n return (\n // Hover is in tailwind.css: CLAUDE.md forbids JS for styling, and JS misses keyboard.\n // FOCUS_RING because a colour change alone is not a focus indicator.\n <a\n key={this.elementId}\n className={LINK_CLASSES}\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {children}\n </a>\n );\n },\n\n // Overridden only to vet `src`. Alt text stands in for a rejected image.\n image(this: ReactRenderer, src, alt, title) {\n if (!isSafeHref(src)) {\n return <span key={this.elementId}>{alt ?? ''}</span>;\n }\n return (\n <img\n key={this.elementId}\n src={src}\n alt={alt ?? ''}\n title={title ?? undefined}\n style={{ maxWidth: '100%' }}\n />\n );\n },\n\n // Preflight resets list-style/margin/padding; padding-left on the LIST is the hanging indent.\n list(this: ReactRenderer, children, ordered, start) {\n const Tag = ordered ? 'ol' : 'ul';\n return (\n <Tag\n key={this.elementId}\n // Forward `start` or `3. three` silently renumbers from 1.\n start={ordered && start !== 1 ? start : undefined}\n style={{\n listStyle: ordered ? 'decimal' : 'disc',\n paddingLeft: '1.25em',\n margin: 0,\n }}\n >\n {children}\n </Tag>\n );\n },\n\n listItem(this: ReactRenderer, children) {\n return (\n // Padding too: it pushes line boxes off ClampText's line grid, slicing the last line.\n <li key={this.elementId} style={{ padding: omitMargin ? '0px' : '2px 0px' }}>\n {children}\n </li>\n );\n },\n});\n\n/** Turns every block-level node inline for `flow`. Every block renderer must appear here, or the\n * one invariant — no block children — breaks silently. */\nconst getFlowOverrides = (): Partial<ReactRenderer> => ({\n paragraph(this: ReactRenderer, text) {\n return <span key={this.elementId}>{text} </span>;\n },\n\n // Roles are pinned: without the real <h*> the heading leaves the a11y tree entirely.\n heading(this: ReactRenderer, text, level) {\n return (\n <span\n key={this.elementId}\n role=\"heading\"\n aria-level={level}\n style={{ fontWeight: 600 }}\n >\n {text}{' '}\n </span>\n );\n },\n\n // The list owns markers and breaks — ordered numbering needs `start` plus the item index.\n // A <br> opens a line box (what -webkit-line-clamp counts); `display: block` would not.\n // Items OPEN with it, so tailwind.css suppresses the leading one on a first-child list.\n list(this: ReactRenderer, children, ordered, start) {\n const items = React.Children.toArray(children);\n return (\n <span key={this.elementId} role=\"list\" style={{ display: 'inline' }}>\n {items.map((item, index) => (\n // Index as key: one list node's rendered children never reorder.\n <span key={index} role=\"listitem\" style={{ display: 'inline' }}>\n <br />\n {/* Decoration — the role already conveys position. */}\n <span aria-hidden=\"true\">\n {ordered ? `${(start ?? 1) + index}. ` : FLOW_BULLET}\n </span>\n {item}\n </span>\n ))}\n {/* Closes the list so following prose starts on its own line. */}\n <br />\n </span>\n );\n },\n\n listItem(this: ReactRenderer, children) {\n return <React.Fragment key={this.elementId}>{children}</React.Fragment>;\n },\n\n blockquote(this: ReactRenderer, quote) {\n return <span key={this.elementId}>{quote} </span>;\n },\n\n // A hard break here is incidental wrapping; honouring it spends a clamped line on nothing.\n br(this: ReactRenderer) {\n return <span key={this.elementId}> </span>;\n },\n\n // Whitespace collapses, so a multi-line snippet becomes one run instead of the whole budget.\n code(this: ReactRenderer, code) {\n return (\n <React.Fragment key={this.elementId}>\n <CodeText variant=\"inline\">{code}</CodeText>{' '}\n </React.Fragment>\n );\n },\n\n hr(this: ReactRenderer) {\n return <span key={this.elementId}> </span>;\n },\n\n // Flattened to cell text: rows would each cost a line while carrying little at preview width.\n table(this: ReactRenderer, children) {\n return <span key={this.elementId}>{children} </span>;\n },\n tableHeader(this: ReactRenderer, children) {\n return <span key={this.elementId}>{children}</span>;\n },\n tableBody(this: ReactRenderer, children) {\n return <span key={this.elementId}>{children}</span>;\n },\n tableRow(this: ReactRenderer, children) {\n return <span key={this.elementId}>{children}</span>;\n },\n tableCell(this: ReactRenderer, children) {\n return <span key={this.elementId}>{children} </span>;\n },\n\n // Replaced content grows the line box and breaks the grid the clamp measures against.\n checkbox(this: ReactRenderer, checked) {\n return <span key={this.elementId}>{checked ? '[x] ' : '[ ] '}</span>;\n },\n\n image(this: ReactRenderer, _src, alt) {\n return <span key={this.elementId}>{alt ? `${alt} ` : ''}</span>;\n },\n});\n\n// An image would fetch a third-party URL and a break would wrap the line.\nconst getInlineOverrides = (): Partial<ReactRenderer> => ({\n image(this: ReactRenderer, _src, alt) {\n return <React.Fragment key={this.elementId}>{alt ?? ''}</React.Fragment>;\n },\n\n br(this: ReactRenderer) {\n return <React.Fragment key={this.elementId}> </React.Fragment>;\n },\n});\n\nconst dedent = (str: string) => {\n const lines = str.split('\\n');\n const indents = lines.reduce<number[]>((acc, line) => {\n if (line.trim() !== '') {\n acc.push(line.match(/^[ \\t]*(?=\\S)/gm)?.[0].length ?? 0);\n }\n return acc;\n }, []);\n // Blank-only input has no indents to measure, and Math.min() would return Infinity — which\n // reads as a literal `{Infinity}` quantifier today, but throws the moment the `u` flag is added.\n if (indents.length === 0) {\n return str;\n }\n const indent = Math.min(...indents);\n return lines.map((line) => line.replace(new RegExp(`^[ \\\\t]{${indent}}`, 'gm'), '')).join('\\n');\n};\n\ntype BlockKind = 'blank' | 'item' | 'label' | 'prose';\n\nconst FENCE = /^ {0,3}(?:```|~~~)/;\n/** Lazy continuation or indented code — either way not a new block of a different kind. */\nconst INDENTED = /^ {2,}\\S/;\n/** Belongs to the line ABOVE; a break between them demotes the heading to a thematic rule. */\nconst SETEXT_UNDERLINE = /^ {0,3}(?:=+|-+)\\s*$/;\n/** Tested before the bullet rule so `---` is not read as a list item. */\nconst THEMATIC_BREAK = /^ {0,3}(?:(?:-\\s*){3,}|(?:\\*\\s*){3,}|(?:_\\s*){3,})$/;\nconst BULLET_MARKER = /^[-*+]\\s/;\nconst ORDERED_MARKER = /^(\\d{1,9})[.)]\\s/;\n/** The colon is REQUIRED: emphasis mid-sentence looks identical, and splitting it would turn\n * one paragraph into three. */\nconst BOLD_LABEL = /^\\*\\*[^*]+:\\*\\*$|^\\*\\*[^*]+\\*\\*:$/;\n\nconst INLINE_LABEL = /\\*\\*[^*\\n]+?:\\*\\*|\\*\\*[^*\\n]+?\\*\\*:/g;\nconst LEADING_MARKER = /^\\s*(?:[-*+]|\\d{1,9}[.)])\\s+/;\n\n/** Splits a bold label that TRAILS other text onto its own line — `...support. **Pagination\n * Mode:**`. Line classification alone cannot fix it: the whole line is prose. A label that\n * OPENS its line, or a list item, is a lead-in and left alone. */\nconst splitTrailingLabels = (line: string): string[] => {\n const marker = LEADING_MARKER.exec(line)?.[0] ?? '';\n let rest = line.slice(marker.length);\n const out: string[] = [];\n let isFirst = true;\n\n while (true) {\n INLINE_LABEL.lastIndex = 0;\n let found: RegExpExecArray | null = null;\n let match = INLINE_LABEL.exec(rest);\n while (match) {\n // index 0 means the label opens the chunk — a lead-in, not a new block.\n if (match.index > 0) {\n found = match;\n break;\n }\n match = INLINE_LABEL.exec(rest);\n }\n if (!found) {\n break;\n }\n const before = rest.slice(0, found.index).trimEnd();\n if (before) {\n out.push((isFirst ? marker : '') + before);\n isFirst = false;\n }\n rest = rest.slice(found.index);\n }\n\n out.push((isFirst ? marker : '') + rest);\n return out;\n};\n\nconst classifyLine = (line: string, previous: BlockKind): BlockKind => {\n const trimmed = line.trim();\n if (!trimmed) {\n return 'blank';\n }\n if (THEMATIC_BREAK.test(line) || SETEXT_UNDERLINE.test(line)) {\n // Continues what came before, so nothing is inserted above it.\n return previous === 'blank' ? 'prose' : previous;\n }\n if (BULLET_MARKER.test(trimmed)) {\n return 'item';\n }\n const ordered = ORDERED_MARKER.exec(trimmed);\n if (ordered) {\n // Only a list starting at 1 may interrupt a paragraph, else \"Released in\\n2024.\" would\n // become `<ol start=\"2024\">`.\n if (previous !== 'prose' || ordered[1] === '1') {\n return 'item';\n }\n }\n if (BOLD_LABEL.test(trimmed)) {\n return 'label';\n }\n return 'prose';\n};\n\n/** Inserts the blank line CommonMark needs between blocks of different kinds — generated\n * descriptions separate them with a single `\\n`, which reads as lazy continuation. Only a\n * CHANGE of kind breaks, so tight lists and wrapped paragraphs survive. Skips fences, indented\n * lines and setext underlines, where a blank line changes meaning. */\nconst explicitBlocks = (text: string): string => {\n const lines = text.split('\\n');\n const out: string[] = [];\n let previous: BlockKind = 'blank';\n let inFence = false;\n\n for (const line of lines) {\n if (FENCE.test(line)) {\n inFence = !inFence;\n out.push(line);\n // A fence is its own block on both sides.\n previous = 'blank';\n continue;\n }\n if (inFence) {\n out.push(line);\n continue;\n }\n if (INDENTED.test(line)) {\n out.push(line);\n continue;\n }\n // One line can hold a block AND a trailing label, so split before classifying.\n for (const piece of splitTrailingLabels(line)) {\n previous = pushClassified(piece, previous, out);\n }\n }\n return out.join('\\n');\n};\n\nconst pushClassified = (line: string, previous: BlockKind, out: string[]): BlockKind => {\n const kind = classifyLine(line, previous);\n if (kind !== 'blank' && previous !== 'blank' && kind !== previous) {\n out.push('');\n }\n out.push(line);\n return kind;\n};\n\nconst MarkdownBlock: React.FC<MarkdownProps> = ({\n text,\n color,\n size,\n omitMargin,\n variant = 'block',\n normalizeSource = false,\n links = 'interactive',\n renderLink,\n}) => {\n // Hooks before the early return so hook order stays stable. marked-react re-lexes on every\n // render, and this can land in a virtualized Table cell.\n const source = React.useMemo(\n () => (normalizeSource ? explicitBlocks(dedent(text ?? '')) : dedent(text ?? '')),\n [text, normalizeSource],\n );\n const renderer = React.useMemo(\n () => ({\n ...getRendererFunction({ omitMargin, links, renderLink }),\n ...(variant === 'flow' ? getFlowOverrides() : {}),\n ...(variant === 'inline' ? getInlineOverrides() : {}),\n }),\n [omitMargin, variant, links, renderLink],\n );\n\n if (!text) {\n return null;\n }\n\n // `display: contents` generates no box, so this only propagates colour and size — a real box\n // would be a block child for -webkit-line-clamp to count, and a stray div inside Alert. The\n // tag still switches: `display` does not change content-model validity.\n const Root = variant === 'block' ? 'div' : 'span';\n return (\n <Root\n className={variant === 'flow' ? 'md-root md-flow' : 'md-root'}\n style={{\n display: 'contents',\n color: getTextColor(color ?? 'primary'),\n fontSize: size === 'inherit' ? 'inherit' : getFontSize(size ?? 'small'),\n }}\n >\n <Markdown\n value={source}\n renderer={renderer}\n isInline={variant === 'inline'}\n gfm={variant !== 'inline'}\n instance={variant === 'inline' ? getInlineMarked() : undefined}\n />\n </Root>\n );\n};\n\n// marked-react re-parses on every render. Every prop but `renderLink` is a primitive.\nexport default React.memo(MarkdownBlock);\n"],"names":["FLOW_BULLET","INERT_LINK_STYLE","ALLOWED_SCHEMES","isSafeHref","href","scheme","isRootRelative","LINK_CLASSES","FOCUS_RING","getRendererFunction","omitMargin","links","renderLink","quote","jsx","text","code","CodeText","level","PageTitle","SectionTitle","children","React","src","alt","title","ordered","start","getFlowOverrides","jsxs","items","item","index","checked","_src","getInlineOverrides","dedent","str","lines","indents","acc","line","_a","indent","FENCE","INDENTED","SETEXT_UNDERLINE","THEMATIC_BREAK","BULLET_MARKER","ORDERED_MARKER","BOLD_LABEL","INLINE_LABEL","LEADING_MARKER","splitTrailingLabels","marker","rest","out","isFirst","found","match","before","classifyLine","previous","trimmed","explicitBlocks","inFence","piece","pushClassified","kind","MarkdownBlock","color","size","variant","normalizeSource","source","renderer","getTextColor","getFontSize","Markdown","getInlineMarked","MarkdownBlock$1"],"mappings":"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"}
1
+ {"version":3,"file":"MarkdownBlock.js","sources":["../../../src/components/Typography/MarkdownBlock.tsx"],"sourcesContent":["import Markdown, { ReactRenderer } from 'marked-react';\nimport React from 'react';\nimport type { RenderLink } from '../PageLayout/types';\nimport { FOCUS_RING } from '../focusRing';\nimport CodeText from './CodeText';\nimport PageTitle from './PageTitle';\nimport SectionTitle from './SectionTitle';\nimport type { FontSize, TextColor } from './Text';\nimport { getInlineMarked } from './inlineMarkdown';\nimport { getFontSize, getTextColor } from './textStyles';\n\n/** `inline`: one line of inline syntax, with no autolinked emails or URLs. */\nexport type MarkdownVariant = 'block' | 'flow' | 'inline';\n\n/** `inert` draws links without an anchor, for markdown inside a <button>. */\nexport type MarkdownLinks = 'interactive' | 'inert';\n\nexport interface MarkdownProps {\n text: string;\n color?: TextColor;\n size?: FontSize | 'inherit';\n omitMargin?: boolean;\n variant?: MarkdownVariant;\n normalizeSource?: boolean;\n links?: MarkdownLinks;\n /** Renders root-relative links (`/path`) through the app's router, in the same tab. Other\n * links still open in a new tab. Pass a stable function: a new one re-parses the markdown. */\n renderLink?: RenderLink;\n}\n\nconst FLOW_BULLET = '• ';\n\n// Inline, not `md-link`, for portals outside the shadow root; the link token is unreadable on tooltips.\nconst INERT_LINK_STYLE: React.CSSProperties = {\n color: 'inherit',\n textDecoration: 'underline',\n fontWeight: 600,\n};\n\n// Third-party content, and React does not sanitize href — `javascript:` would execute.\nconst ALLOWED_SCHEMES = new Set(['http:', 'https:', 'mailto:', 'tel:']);\n\nconst isSafeHref = (href: unknown): href is string => {\n if (typeof href !== 'string') {\n return false;\n }\n const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(href.replace(/[\\u0000-\\u0020]/g, ''));\n // No scheme means relative or fragment, which has nothing to vet.\n return !scheme || ALLOWED_SCHEMES.has(`${scheme[1].toLowerCase()}:`);\n};\n\n// `//host` and `/\\host` are protocol-relative to a browser, so they leave the app.\n// URL parsing drops tab, CR and LF, so `/\\t/evil.com` would resolve off-site.\nconst isRootRelative = (href: string): boolean => /^\\/(?![/\\\\])[^\\t\\n\\r]*$/.test(href);\n\nconst LINK_CLASSES = `md-link rounded-xs ${FOCUS_RING}`;\n\n// Renderer args are always arrays: never unwrap with `x[0]`, it drops all tokens after the first.\nconst getRendererFunction = ({\n omitMargin,\n links,\n renderLink,\n}: Pick<MarkdownProps, 'omitMargin' | 'links' | 'renderLink'>): Partial<ReactRenderer> => ({\n blockquote(this: ReactRenderer, quote) {\n return (\n <div\n key={this.elementId}\n style={{\n backgroundColor: 'var(--malachite-card-dimmed-background)',\n padding: '20px',\n borderRadius: '20px',\n border: '1px solid var(--malachite-card-border)',\n margin: '16px 0',\n }}\n >\n {quote}\n </div>\n );\n },\n\n paragraph(this: ReactRenderer, text) {\n return (\n <div\n key={this.elementId}\n className=\"md-paragraph\"\n style={{ marginBottom: omitMargin ? '0px' : '16px' }}\n >\n {text}\n </div>\n );\n },\n\n codespan(this: ReactRenderer, code) {\n return (\n <CodeText key={this.elementId} variant=\"inline\">\n {code}\n </CodeText>\n );\n },\n\n code(this: ReactRenderer, code) {\n return (\n <pre key={this.elementId} style={{ margin: omitMargin ? 0 : '16px 0' }}>\n <CodeText\n style={{\n display: 'block',\n whiteSpace: 'pre',\n overflowX: 'auto',\n padding: '12px',\n }}\n >\n {code}\n </CodeText>\n </pre>\n );\n },\n\n heading(this: ReactRenderer, text, level) {\n return level === 1 ? (\n <PageTitle key={this.elementId}>{text}</PageTitle>\n ) : (\n <SectionTitle key={this.elementId}>{text}</SectionTitle>\n );\n },\n\n br(this: ReactRenderer) {\n return <br key={this.elementId} />;\n },\n\n // `text`/`strong` are deliberately NOT overridden: a wrapping Text emits a font-weight, and\n // the nested 400 beat strong's 600 — which is why bold never rendered.\n\n link(this: ReactRenderer, href, children) {\n // Keep the label for a rejected scheme rather than deleting content.\n if (!isSafeHref(href)) {\n return <React.Fragment key={this.elementId}>{children}</React.Fragment>;\n }\n if (links === 'inert') {\n return (\n <span key={this.elementId} style={INERT_LINK_STYLE}>\n {children}\n </span>\n );\n }\n if (renderLink && isRootRelative(href)) {\n return (\n <React.Fragment key={this.elementId}>\n {renderLink({ href, children, className: LINK_CLASSES })}\n </React.Fragment>\n );\n }\n return (\n // Hover is in tailwind.css: CLAUDE.md forbids JS for styling, and JS misses keyboard.\n // FOCUS_RING because a colour change alone is not a focus indicator.\n <a\n key={this.elementId}\n className={LINK_CLASSES}\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {children}\n </a>\n );\n },\n\n // Overridden only to vet `src`. Alt text stands in for a rejected image.\n image(this: ReactRenderer, src, alt, title) {\n if (!isSafeHref(src)) {\n return <span key={this.elementId}>{alt ?? ''}</span>;\n }\n return (\n <img\n key={this.elementId}\n src={src}\n alt={alt ?? ''}\n title={title ?? undefined}\n style={{ maxWidth: '100%' }}\n />\n );\n },\n\n // Preflight resets list-style/margin/padding; padding-left on the LIST is the hanging indent.\n list(this: ReactRenderer, children, ordered, start) {\n const Tag = ordered ? 'ol' : 'ul';\n return (\n <Tag\n key={this.elementId}\n // Forward `start` or `3. three` silently renumbers from 1.\n start={ordered && start !== 1 ? start : undefined}\n style={{\n listStyle: ordered ? 'decimal' : 'disc',\n paddingLeft: '1.25em',\n margin: 0,\n }}\n >\n {children}\n </Tag>\n );\n },\n\n listItem(this: ReactRenderer, children) {\n return (\n // Padding too: it pushes line boxes off ClampText's line grid, slicing the last line.\n <li key={this.elementId} style={{ padding: omitMargin ? '0px' : '2px 0px' }}>\n {children}\n </li>\n );\n },\n});\n\n/** Turns every block-level node inline for `flow`. Every block renderer must appear here, or the\n * one invariant — no block children — breaks silently. */\nconst getFlowOverrides = (): Partial<ReactRenderer> => ({\n paragraph(this: ReactRenderer, text) {\n return <span key={this.elementId}>{text} </span>;\n },\n\n // Roles are pinned: without the real <h*> the heading leaves the a11y tree entirely.\n heading(this: ReactRenderer, text, level) {\n return (\n <span\n key={this.elementId}\n role=\"heading\"\n aria-level={level}\n style={{ fontWeight: 600 }}\n >\n {text}{' '}\n </span>\n );\n },\n\n // The list owns markers and breaks — ordered numbering needs `start` plus the item index.\n // A <br> opens a line box (what -webkit-line-clamp counts); `display: block` would not.\n // Items OPEN with it, so tailwind.css suppresses the leading one on a first-child list.\n list(this: ReactRenderer, children, ordered, start) {\n const items = React.Children.toArray(children);\n return (\n <span key={this.elementId} role=\"list\" style={{ display: 'inline' }}>\n {items.map((item, index) => (\n // Index as key: one list node's rendered children never reorder.\n <span key={index} role=\"listitem\" style={{ display: 'inline' }}>\n <br />\n {/* Decoration — the role already conveys position. */}\n <span aria-hidden=\"true\">\n {ordered ? `${(start ?? 1) + index}. ` : FLOW_BULLET}\n </span>\n {item}\n </span>\n ))}\n {/* Closes the list so following prose starts on its own line. */}\n <br />\n </span>\n );\n },\n\n listItem(this: ReactRenderer, children) {\n return <React.Fragment key={this.elementId}>{children}</React.Fragment>;\n },\n\n blockquote(this: ReactRenderer, quote) {\n return <span key={this.elementId}>{quote} </span>;\n },\n\n // A hard break here is incidental wrapping; honouring it spends a clamped line on nothing.\n br(this: ReactRenderer) {\n return <span key={this.elementId}> </span>;\n },\n\n // Whitespace collapses, so a multi-line snippet becomes one run instead of the whole budget.\n code(this: ReactRenderer, code) {\n return (\n <React.Fragment key={this.elementId}>\n <CodeText variant=\"inline\">{code}</CodeText>{' '}\n </React.Fragment>\n );\n },\n\n hr(this: ReactRenderer) {\n return <span key={this.elementId}> </span>;\n },\n\n // Flattened to cell text: rows would each cost a line while carrying little at preview width.\n table(this: ReactRenderer, children) {\n return <span key={this.elementId}>{children} </span>;\n },\n tableHeader(this: ReactRenderer, children) {\n return <span key={this.elementId}>{children}</span>;\n },\n tableBody(this: ReactRenderer, children) {\n return <span key={this.elementId}>{children}</span>;\n },\n tableRow(this: ReactRenderer, children) {\n return <span key={this.elementId}>{children}</span>;\n },\n tableCell(this: ReactRenderer, children) {\n return <span key={this.elementId}>{children} </span>;\n },\n\n // Replaced content grows the line box and breaks the grid the clamp measures against.\n checkbox(this: ReactRenderer, checked) {\n return <span key={this.elementId}>{checked ? '[x] ' : '[ ] '}</span>;\n },\n\n image(this: ReactRenderer, _src, alt) {\n return <span key={this.elementId}>{alt ? `${alt} ` : ''}</span>;\n },\n});\n\n// An image would fetch a third-party URL and a break would wrap the line.\nconst getInlineOverrides = (): Partial<ReactRenderer> => ({\n image(this: ReactRenderer, _src, alt) {\n return <React.Fragment key={this.elementId}>{alt ?? ''}</React.Fragment>;\n },\n\n br(this: ReactRenderer) {\n return <React.Fragment key={this.elementId}> </React.Fragment>;\n },\n});\n\nconst dedent = (str: string) => {\n const lines = str.split('\\n');\n const indents = lines.reduce<number[]>((acc, line) => {\n if (line.trim() !== '') {\n acc.push(line.match(/^[ \\t]*(?=\\S)/gm)?.[0].length ?? 0);\n }\n return acc;\n }, []);\n // Blank-only input has no indents to measure, and Math.min() would return Infinity — which\n // reads as a literal `{Infinity}` quantifier today, but throws the moment the `u` flag is added.\n if (indents.length === 0) {\n return str;\n }\n const indent = Math.min(...indents);\n return lines.map((line) => line.replace(new RegExp(`^[ \\\\t]{${indent}}`, 'gm'), '')).join('\\n');\n};\n\ntype BlockKind = 'blank' | 'item' | 'label' | 'prose';\n\nconst FENCE = /^ {0,3}(?:```|~~~)/;\n/** Lazy continuation or indented code — either way not a new block of a different kind. */\nconst INDENTED = /^ {2,}\\S/;\n/** Belongs to the line ABOVE; a break between them demotes the heading to a thematic rule. */\nconst SETEXT_UNDERLINE = /^ {0,3}(?:=+|-+)\\s*$/;\n/** Tested before the bullet rule so `---` is not read as a list item. */\nconst THEMATIC_BREAK = /^ {0,3}(?:(?:-\\s*){3,}|(?:\\*\\s*){3,}|(?:_\\s*){3,})$/;\nconst BULLET_MARKER = /^[-*+]\\s/;\nconst ORDERED_MARKER = /^(\\d{1,9})[.)]\\s/;\n/** The colon is REQUIRED: emphasis mid-sentence looks identical, and splitting it would turn\n * one paragraph into three. */\nconst BOLD_LABEL = /^\\*\\*[^*]+:\\*\\*$|^\\*\\*[^*]+\\*\\*:$/;\n\nconst INLINE_LABEL = /\\*\\*[^*\\n]+?:\\*\\*|\\*\\*[^*\\n]+?\\*\\*:/g;\nconst LEADING_MARKER = /^\\s*(?:[-*+]|\\d{1,9}[.)])\\s+/;\n\n/** Splits a bold label that TRAILS other text onto its own line — `...support. **Pagination\n * Mode:**`. Line classification alone cannot fix it: the whole line is prose. A label that\n * OPENS its line, or a list item, is a lead-in and left alone. */\nconst splitTrailingLabels = (line: string): string[] => {\n const marker = LEADING_MARKER.exec(line)?.[0] ?? '';\n let rest = line.slice(marker.length);\n const out: string[] = [];\n let isFirst = true;\n\n while (true) {\n INLINE_LABEL.lastIndex = 0;\n let found: RegExpExecArray | null = null;\n let match = INLINE_LABEL.exec(rest);\n while (match) {\n // index 0 means the label opens the chunk — a lead-in, not a new block.\n if (match.index > 0) {\n found = match;\n break;\n }\n match = INLINE_LABEL.exec(rest);\n }\n if (!found) {\n break;\n }\n const before = rest.slice(0, found.index).trimEnd();\n if (before) {\n out.push((isFirst ? marker : '') + before);\n isFirst = false;\n }\n rest = rest.slice(found.index);\n }\n\n out.push((isFirst ? marker : '') + rest);\n return out;\n};\n\nconst classifyLine = (line: string, previous: BlockKind): BlockKind => {\n const trimmed = line.trim();\n if (!trimmed) {\n return 'blank';\n }\n if (THEMATIC_BREAK.test(line) || SETEXT_UNDERLINE.test(line)) {\n // Continues what came before, so nothing is inserted above it.\n return previous === 'blank' ? 'prose' : previous;\n }\n if (BULLET_MARKER.test(trimmed)) {\n return 'item';\n }\n const ordered = ORDERED_MARKER.exec(trimmed);\n if (ordered) {\n // Only a list starting at 1 may interrupt a paragraph, else \"Released in\\n2024.\" would\n // become `<ol start=\"2024\">`.\n if (previous !== 'prose' || ordered[1] === '1') {\n return 'item';\n }\n }\n if (BOLD_LABEL.test(trimmed)) {\n return 'label';\n }\n return 'prose';\n};\n\n/** Inserts the blank line CommonMark needs between blocks of different kinds — generated\n * descriptions separate them with a single `\\n`, which reads as lazy continuation. Only a\n * CHANGE of kind breaks, so tight lists and wrapped paragraphs survive. Skips fences, indented\n * lines and setext underlines, where a blank line changes meaning. */\nconst explicitBlocks = (text: string): string => {\n const lines = text.split('\\n');\n const out: string[] = [];\n let previous: BlockKind = 'blank';\n let inFence = false;\n\n for (const line of lines) {\n if (FENCE.test(line)) {\n inFence = !inFence;\n out.push(line);\n // A fence is its own block on both sides.\n previous = 'blank';\n continue;\n }\n if (inFence) {\n out.push(line);\n continue;\n }\n if (INDENTED.test(line)) {\n out.push(line);\n continue;\n }\n // One line can hold a block AND a trailing label, so split before classifying.\n for (const piece of splitTrailingLabels(line)) {\n previous = pushClassified(piece, previous, out);\n }\n }\n return out.join('\\n');\n};\n\nconst pushClassified = (line: string, previous: BlockKind, out: string[]): BlockKind => {\n const kind = classifyLine(line, previous);\n if (kind !== 'blank' && previous !== 'blank' && kind !== previous) {\n out.push('');\n }\n out.push(line);\n return kind;\n};\n\nconst MarkdownBlock: React.FC<MarkdownProps> = ({\n text,\n color,\n size,\n omitMargin,\n variant = 'block',\n normalizeSource = false,\n links = 'interactive',\n renderLink,\n}) => {\n // Hooks before the early return so hook order stays stable. marked-react re-lexes on every\n // render, and this can land in a virtualized Table cell.\n const source = React.useMemo(\n () => (normalizeSource ? explicitBlocks(dedent(text ?? '')) : dedent(text ?? '')),\n [text, normalizeSource],\n );\n const renderer = React.useMemo(\n () => ({\n ...getRendererFunction({ omitMargin, links, renderLink }),\n ...(variant === 'flow' ? getFlowOverrides() : {}),\n ...(variant === 'inline' ? getInlineOverrides() : {}),\n }),\n [omitMargin, variant, links, renderLink],\n );\n\n if (!text) {\n return null;\n }\n\n // `display: contents` generates no box, so this only propagates colour and size — a real box\n // would be a block child for -webkit-line-clamp to count, and a stray div inside Alert. The\n // tag still switches: `display` does not change content-model validity.\n const Root = variant === 'block' ? 'div' : 'span';\n return (\n <Root\n className={variant === 'flow' ? 'md-root md-flow' : 'md-root'}\n style={{\n display: 'contents',\n color: getTextColor(color ?? 'primary'),\n fontSize: size === 'inherit' ? 'inherit' : getFontSize(size ?? 'small'),\n }}\n >\n <Markdown\n value={source}\n renderer={renderer}\n isInline={variant === 'inline'}\n gfm={variant !== 'inline'}\n instance={variant === 'inline' ? getInlineMarked() : undefined}\n />\n </Root>\n );\n};\n\n// marked-react re-parses on every render. Every prop but `renderLink` is a primitive.\nexport default React.memo(MarkdownBlock);\n"],"names":["FLOW_BULLET","INERT_LINK_STYLE","ALLOWED_SCHEMES","isSafeHref","href","scheme","isRootRelative","LINK_CLASSES","FOCUS_RING","getRendererFunction","omitMargin","links","renderLink","quote","jsx","text","code","CodeText","level","PageTitle","SectionTitle","children","React","src","alt","title","ordered","start","getFlowOverrides","jsxs","items","item","index","checked","_src","getInlineOverrides","dedent","str","lines","indents","acc","line","_a","indent","FENCE","INDENTED","SETEXT_UNDERLINE","THEMATIC_BREAK","BULLET_MARKER","ORDERED_MARKER","BOLD_LABEL","INLINE_LABEL","LEADING_MARKER","splitTrailingLabels","marker","rest","out","isFirst","found","match","before","classifyLine","previous","trimmed","explicitBlocks","inFence","piece","pushClassified","kind","MarkdownBlock","color","size","variant","normalizeSource","source","renderer","getTextColor","getFontSize","Markdown","getInlineMarked","MarkdownBlock$1"],"mappings":"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"}
@@ -1,32 +1,34 @@
1
- import { jsx as y } from "react/jsx-runtime";
2
- import { getFontWeight as c, getFontFamily as d, getFontSize as g, getTextColor as x } from "./textStyles.js";
3
- const S = ({
4
- underline: e = !1,
1
+ import { jsx as f } from "react/jsx-runtime";
2
+ import { getFontWeight as y, getFontFamily as g, getFontSize as x, getTextColor as F } from "./textStyles.js";
3
+ const h = ({
4
+ underline: o = !1,
5
5
  children: i,
6
- color: l = "primary",
6
+ color: r = "primary",
7
7
  size: n = "small",
8
8
  font: t,
9
- fontWeight: r = "normal",
10
- tooltip: a,
11
- textAlign: o,
12
- as: s = "span",
13
- className: m = "",
14
- style: f = {}
9
+ fontWeight: a = "normal",
10
+ tooltip: l,
11
+ textAlign: e,
12
+ wrap: s = !0,
13
+ as: m = "span",
14
+ className: p = "",
15
+ style: c = {}
15
16
  }) => {
16
- const p = {
17
- color: x(l),
18
- fontSize: g(n),
17
+ const d = {
18
+ color: F(r),
19
+ fontSize: x(n),
19
20
  // Left unset when not asked for, so Text keeps inheriting whatever the surrounding context…
20
- fontFamily: t ? d(t) : void 0,
21
- fontWeight: c(r),
22
- textAlign: o,
23
- textDecoration: e ? "underline" : "none",
24
- display: o ? "block" : void 0,
25
- ...f
21
+ fontFamily: t ? g(t) : void 0,
22
+ fontWeight: y(a),
23
+ textAlign: e,
24
+ textDecoration: o ? "underline" : "none",
25
+ display: e ? "block" : void 0,
26
+ whiteSpace: s ? void 0 : "nowrap",
27
+ ...c
26
28
  };
27
- return /* @__PURE__ */ y(s, { className: m, style: p, title: a, children: i });
29
+ return /* @__PURE__ */ f(m, { className: p, style: d, title: l, children: i });
28
30
  };
29
31
  export {
30
- S as default
32
+ h as default
31
33
  };
32
34
  //# sourceMappingURL=Text.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Text.js","sources":["../../../src/components/Typography/Text.tsx"],"sourcesContent":["import React from 'react';\nimport type { FontFamily, FontSize, FontWeight, TextColor, TextElement } from './textStyles';\nimport { getFontFamily, getFontSize, getFontWeight, getTextColor } from './textStyles';\n\n// Types only: a value re-export would put the non-component exports back in this file.\nexport type { FontSize, FontFamily, FontWeight, TextColor, TextElement } from './textStyles';\n\nexport interface TextProps {\n color?: TextColor;\n size?: FontSize;\n /**\n * Typeface. Defaults to `sans` (Inter), the library's body face. `heading` is Stack Sans\n * Text and `body` is Geist — both are loaded on mount and declared in the theme.\n */\n font?: FontFamily;\n underline?: boolean;\n fontWeight?: FontWeight;\n children: React.ReactNode;\n tooltip?: string;\n textAlign?: 'left' | 'center' | 'right' | 'justify';\n /** The element it renders, for a heading or paragraph that keeps Text's look. */\n as?: TextElement;\n className?: string;\n style?: React.CSSProperties;\n}\n\nconst Text: React.FC<TextProps> = ({\n underline = false,\n children,\n color = 'primary',\n size = 'small',\n font,\n fontWeight = 'normal',\n tooltip,\n textAlign,\n as: Element = 'span',\n className = '',\n style = {},\n}) => {\n const textStyles: React.CSSProperties = {\n color: getTextColor(color),\n fontSize: getFontSize(size),\n // Left unset when not asked for, so Text keeps inheriting whatever the surrounding context…\n fontFamily: font ? getFontFamily(font) : undefined,\n fontWeight: getFontWeight(fontWeight),\n textAlign,\n textDecoration: underline ? 'underline' : 'none',\n display: textAlign ? 'block' : undefined,\n ...style,\n };\n\n return (\n <Element className={className} style={textStyles} title={tooltip}>\n {children}\n </Element>\n );\n};\n\nexport default Text;\n"],"names":["Text","underline","children","color","size","font","fontWeight","tooltip","textAlign","Element","className","style","textStyles","getTextColor","getFontSize","getFontFamily","getFontWeight"],"mappings":";;AA0BA,MAAMA,IAA4B,CAAC;AAAA,EAC/B,WAAAC,IAAY;AAAA,EACZ,UAAAC;AAAA,EACA,OAAAC,IAAQ;AAAA,EACR,MAAAC,IAAO;AAAA,EACP,MAAAC;AAAA,EACA,YAAAC,IAAa;AAAA,EACb,SAAAC;AAAA,EACA,WAAAC;AAAA,EACA,IAAIC,IAAU;AAAA,EACd,WAAAC,IAAY;AAAA,EACZ,OAAAC,IAAQ,CAAA;AACZ,MAAM;AACF,QAAMC,IAAkC;AAAA,IACpC,OAAOC,EAAaV,CAAK;AAAA,IACzB,UAAUW,EAAYV,CAAI;AAAA;AAAA,IAE1B,YAAYC,IAAOU,EAAcV,CAAI,IAAI;AAAA,IACzC,YAAYW,EAAcV,CAAU;AAAA,IACpC,WAAAE;AAAA,IACA,gBAAgBP,IAAY,cAAc;AAAA,IAC1C,SAASO,IAAY,UAAU;AAAA,IAC/B,GAAGG;AAAA,EAAA;AAGP,2BACKF,GAAA,EAAQ,WAAAC,GAAsB,OAAOE,GAAY,OAAOL,GACpD,UAAAL,GACL;AAER;"}
1
+ {"version":3,"file":"Text.js","sources":["../../../src/components/Typography/Text.tsx"],"sourcesContent":["import React from 'react';\nimport type { FontFamily, FontSize, FontWeight, TextColor, TextElement } from './textStyles';\nimport { getFontFamily, getFontSize, getFontWeight, getTextColor } from './textStyles';\n\n// Types only: a value re-export would put the non-component exports back in this file.\nexport type { FontSize, FontFamily, FontWeight, TextColor, TextElement } from './textStyles';\n\nexport interface TextProps {\n color?: TextColor;\n size?: FontSize;\n /**\n * Typeface. Defaults to `sans` (Inter), the library's body face. `heading` is Stack Sans\n * Text and `body` is Geist — both are loaded on mount and declared in the theme.\n */\n font?: FontFamily;\n underline?: boolean;\n fontWeight?: FontWeight;\n children: React.ReactNode;\n tooltip?: string;\n textAlign?: 'left' | 'center' | 'right' | 'justify';\n wrap?: boolean;\n /** The element it renders, for a heading or paragraph that keeps Text's look. */\n as?: TextElement;\n className?: string;\n style?: React.CSSProperties;\n}\n\nconst Text: React.FC<TextProps> = ({\n underline = false,\n children,\n color = 'primary',\n size = 'small',\n font,\n fontWeight = 'normal',\n tooltip,\n textAlign,\n wrap = true,\n as: Element = 'span',\n className = '',\n style = {},\n}) => {\n const textStyles: React.CSSProperties = {\n color: getTextColor(color),\n fontSize: getFontSize(size),\n // Left unset when not asked for, so Text keeps inheriting whatever the surrounding context…\n fontFamily: font ? getFontFamily(font) : undefined,\n fontWeight: getFontWeight(fontWeight),\n textAlign,\n textDecoration: underline ? 'underline' : 'none',\n display: textAlign ? 'block' : undefined,\n whiteSpace: wrap ? undefined : 'nowrap',\n ...style,\n };\n\n return (\n <Element className={className} style={textStyles} title={tooltip}>\n {children}\n </Element>\n );\n};\n\nexport default Text;\n"],"names":["Text","underline","children","color","size","font","fontWeight","tooltip","textAlign","wrap","Element","className","style","textStyles","getTextColor","getFontSize","getFontFamily","getFontWeight"],"mappings":";;AA2BA,MAAMA,IAA4B,CAAC;AAAA,EAC/B,WAAAC,IAAY;AAAA,EACZ,UAAAC;AAAA,EACA,OAAAC,IAAQ;AAAA,EACR,MAAAC,IAAO;AAAA,EACP,MAAAC;AAAA,EACA,YAAAC,IAAa;AAAA,EACb,SAAAC;AAAA,EACA,WAAAC;AAAA,EACA,MAAAC,IAAO;AAAA,EACP,IAAIC,IAAU;AAAA,EACd,WAAAC,IAAY;AAAA,EACZ,OAAAC,IAAQ,CAAA;AACZ,MAAM;AACF,QAAMC,IAAkC;AAAA,IACpC,OAAOC,EAAaX,CAAK;AAAA,IACzB,UAAUY,EAAYX,CAAI;AAAA;AAAA,IAE1B,YAAYC,IAAOW,EAAcX,CAAI,IAAI;AAAA,IACzC,YAAYY,EAAcX,CAAU;AAAA,IACpC,WAAAE;AAAA,IACA,gBAAgBP,IAAY,cAAc;AAAA,IAC1C,SAASO,IAAY,UAAU;AAAA,IAC/B,YAAYC,IAAO,SAAY;AAAA,IAC/B,GAAGG;AAAA,EAAA;AAGP,2BACKF,GAAA,EAAQ,WAAAC,GAAsB,OAAOE,GAAY,OAAON,GACpD,UAAAL,GACL;AAER;"}
@@ -1,104 +1,118 @@
1
- import { jsxs as l, jsx as e } from "react/jsx-runtime";
2
- import { useRef as b, useState as k, useLayoutEffect as $ } from "react";
3
- import { Button as j } from "./Button.js";
4
- import z from "./CustomIcons/ArrowLeftIcon.js";
5
- import { GLASS_SURFACE_STYLE as L } from "./glassSurface.js";
6
- const H = ({
7
- showBack: f = !1,
8
- onBack: x,
1
+ import { jsxs as s, jsx as e } from "react/jsx-runtime";
2
+ import { useRef as j, useState as L, useLayoutEffect as z } from "react";
3
+ import { Button as A } from "./Button.js";
4
+ import R from "./CustomIcons/ArrowLeftIcon.js";
5
+ import { GLASS_SURFACE_STYLE as B } from "./glassSurface.js";
6
+ const E = {
7
+ default: "px-6 pt-4 min-h-[var(--malachite-control-height-small,28px)]",
8
+ large: "h-[60px] px-[28px] max-sm:h-[52px] max-sm:px-[16px]"
9
+ }, F = ({
10
+ showBack: p = !1,
11
+ onBack: f,
9
12
  backDisabled: h = !1,
10
- leading: p,
11
- stepper: d,
12
- trailing: u,
13
- header: s,
14
- footer: t,
15
- footerGlass: v = !1,
16
- center: g = !1,
17
- contentWidth: i = 480,
18
- minWidth: r,
19
- children: w,
20
- className: y = ""
13
+ reserveBack: d = !0,
14
+ topBarSize: u = "default",
15
+ leading: v,
16
+ stepper: g,
17
+ trailing: w,
18
+ banner: r,
19
+ header: t,
20
+ footer: l,
21
+ footerGlass: N = !1,
22
+ footerAlign: y = "full",
23
+ center: b = !1,
24
+ contentWidth: a = 480,
25
+ minWidth: c,
26
+ children: k,
27
+ className: S = ""
21
28
  }) => {
22
- const n = b(null), [N, c] = k(0);
23
- return $(() => {
24
- const a = n.current;
25
- if (!a) {
26
- c(0);
29
+ const n = j(null), [$, o] = L(0);
30
+ return z(() => {
31
+ const i = n.current;
32
+ if (!i) {
33
+ o(0);
27
34
  return;
28
35
  }
29
- const o = () => c(a.offsetHeight);
30
- o();
31
- const m = new ResizeObserver(o);
32
- return m.observe(a), () => m.disconnect();
33
- }, [t]), /* @__PURE__ */ l(
36
+ const m = () => o(i.offsetHeight);
37
+ m();
38
+ const x = new ResizeObserver(m);
39
+ return x.observe(i), () => x.disconnect();
40
+ }, [l]), /* @__PURE__ */ s(
34
41
  "div",
35
42
  {
36
- className: `w-full h-screen flex flex-col overflow-hidden bg-[var(--malachite-card-sunken-background)] ${y}`,
37
- style: r ? { minWidth: `${r}px` } : void 0,
43
+ className: `w-full h-screen flex flex-col overflow-hidden bg-[var(--malachite-card-sunken-background)] ${S}`,
44
+ style: c ? { minWidth: `${c}px` } : void 0,
38
45
  children: [
39
- /* @__PURE__ */ l("div", { className: "w-full shrink-0 flex items-center justify-between px-6 pt-4 min-h-[var(--malachite-control-height-small,28px)]", children: [
40
- /* @__PURE__ */ l("div", { className: "flex items-center gap-[16px] min-w-0", children: [
41
- f ? /* @__PURE__ */ e(
42
- j,
43
- {
44
- variant: "outline",
45
- size: "small",
46
- iconOnly: !0,
47
- icon: /* @__PURE__ */ e(z, { size: 14 }),
48
- disabled: h,
49
- onClick: x,
50
- "aria-label": "Back",
51
- className: "w-[var(--malachite-control-height-small,28px)] h-[var(--malachite-control-height-small,28px)] shrink-0"
52
- }
53
- ) : /* @__PURE__ */ e("span", { className: "w-[var(--malachite-control-height-small,28px)] h-[var(--malachite-control-height-small,28px)] shrink-0" }),
54
- p
55
- ] }),
56
- /* @__PURE__ */ l("div", { className: "flex items-center gap-[16px]", children: [
57
- u ?? null,
58
- d ?? null
59
- ] })
60
- ] }),
61
- s ? /* @__PURE__ */ e("div", { className: "w-full shrink-0 flex flex-col items-center px-6 pt-8", children: /* @__PURE__ */ e(
46
+ /* @__PURE__ */ s(
47
+ "div",
48
+ {
49
+ className: `w-full shrink-0 flex items-center justify-between ${E[u]}`,
50
+ children: [
51
+ /* @__PURE__ */ s("div", { className: "flex items-center gap-[16px] min-w-0", children: [
52
+ p ? /* @__PURE__ */ e(
53
+ A,
54
+ {
55
+ variant: "outline",
56
+ size: "small",
57
+ iconOnly: !0,
58
+ icon: /* @__PURE__ */ e(R, { size: 14 }),
59
+ disabled: h,
60
+ onClick: f,
61
+ "aria-label": "Back",
62
+ className: "w-[var(--malachite-control-height-small,28px)] h-[var(--malachite-control-height-small,28px)] shrink-0"
63
+ }
64
+ ) : d ? /* @__PURE__ */ e("span", { className: "w-[var(--malachite-control-height-small,28px)] h-[var(--malachite-control-height-small,28px)] shrink-0" }) : null,
65
+ v
66
+ ] }),
67
+ /* @__PURE__ */ s("div", { className: "flex items-center gap-[16px]", children: [
68
+ w ?? null,
69
+ g ?? null
70
+ ] })
71
+ ]
72
+ }
73
+ ),
74
+ r ? /* @__PURE__ */ e("div", { className: "w-full shrink-0 px-[12px] pt-[12px]", children: r }) : null,
75
+ t ? /* @__PURE__ */ e("div", { className: "w-full shrink-0 flex flex-col items-center px-6 pt-8", children: /* @__PURE__ */ e(
62
76
  "div",
63
77
  {
64
78
  className: "w-full flex flex-col items-center gap-[24px]",
65
- style: { maxWidth: `${i}px` },
66
- children: s
79
+ style: { maxWidth: `${a}px` },
80
+ children: t
67
81
  }
68
82
  ) }) : null,
69
- /* @__PURE__ */ l("div", { className: "relative flex-1 min-h-0", children: [
83
+ /* @__PURE__ */ s("div", { className: "relative flex-1 min-h-0", children: [
70
84
  /* @__PURE__ */ e(
71
85
  "div",
72
86
  {
73
- className: `absolute inset-0 overflow-y-auto flex flex-col items-center px-6 ${g ? "justify-center" : "justify-start"}`,
87
+ className: `absolute inset-0 overflow-y-auto flex flex-col items-center px-6 ${b ? "justify-center" : "justify-start"}`,
74
88
  style: {
75
89
  // Reserve the overlaid footer's height plus a gap, so the last item
76
90
  // clears the footer with breathing room (not flush against it, which
77
91
  // reads as hidden content continuing underneath).
78
- paddingBottom: t ? `${N + 24}px` : "48px"
92
+ paddingBottom: l ? `${$ + 24}px` : "48px"
79
93
  },
80
94
  children: /* @__PURE__ */ e(
81
95
  "div",
82
96
  {
83
97
  className: "w-full flex flex-col items-center gap-[40px]",
84
98
  style: {
85
- maxWidth: `${i}px`,
99
+ maxWidth: `${a}px`,
86
100
  // Top padding lives on the scrolling content — NOT the scrollport —
87
101
  // so a `position: sticky` child pins flush to the top with no gap.
88
- paddingTop: s ? "32px" : "48px"
102
+ paddingTop: t ? "32px" : "48px"
89
103
  },
90
- children: w
104
+ children: k
91
105
  }
92
106
  )
93
107
  }
94
108
  ),
95
- t ? /* @__PURE__ */ e(
109
+ l ? /* @__PURE__ */ e(
96
110
  "div",
97
111
  {
98
112
  ref: n,
99
113
  className: "absolute inset-x-0 bottom-0 z-20",
100
- style: v ? L : void 0,
101
- children: t
114
+ style: N ? B : void 0,
115
+ children: y === "content" ? /* @__PURE__ */ e("div", { className: "flex justify-center px-6 pt-4 pb-3", children: /* @__PURE__ */ e("div", { className: "w-full", style: { maxWidth: `${a}px` }, children: l }) }) : l
102
116
  }
103
117
  ) : null
104
118
  ] })
@@ -107,7 +121,7 @@ const H = ({
107
121
  );
108
122
  };
109
123
  export {
110
- H as WizardLayout,
111
- H as default
124
+ F as WizardLayout,
125
+ F as default
112
126
  };
113
127
  //# sourceMappingURL=WizardLayout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WizardLayout.js","sources":["../../src/components/WizardLayout.tsx"],"sourcesContent":["import React, { useLayoutEffect, useRef, useState } from 'react';\nimport Button from './Button';\nimport ArrowLeftIcon from './CustomIcons/ArrowLeftIcon';\nimport { GLASS_SURFACE_STYLE } from './glassSurface';\n\nexport interface WizardLayoutProps {\n /** Renders the outline back button in the top-left slot. */\n showBack?: boolean;\n onBack?: () => void;\n backDisabled?: boolean;\n /** Top-left slot beside the back button — e.g. a user avatar menu. */\n leading?: React.ReactNode;\n /** Right top-bar slot (sits just before `stepper`) — e.g. a theme toggle. */\n stepper?: React.ReactNode;\n /** Far-right top-bar slot — e.g. a theme toggle. */\n trailing?: React.ReactNode;\n /**\n * Pinned below the top bar and always visible while the content scrolls —\n * e.g. connection logos + a step title. Rendered in the same centred column\n * as the content (`contentWidth`).\n */\n header?: React.ReactNode;\n /** Pinned to the bottom of the screen — typically a <FooterNav /> or a primary CTA. */\n footer?: React.ReactNode;\n /**\n * Give the pinned footer a frosted-glass treatment (translucent background +\n * backdrop blur) so scrolling content shows through it. The footer already sits\n * above sticky body content, so it reads as glass over the scrolling list.\n */\n footerGlass?: boolean;\n /** Vertically centre the content (used for terminal/success screens). */\n center?: boolean;\n /** Max width of the centered content column. */\n contentWidth?: number;\n /** Minimum width of the whole layout, below which it scrolls horizontally. */\n minWidth?: number;\n children?: React.ReactNode;\n className?: string;\n}\n\nconst WizardLayout: React.FC<WizardLayoutProps> = ({\n showBack = false,\n onBack,\n backDisabled = false,\n leading,\n stepper,\n trailing,\n header,\n footer,\n footerGlass = false,\n center = false,\n contentWidth = 480,\n minWidth,\n children,\n className = '',\n}) => {\n // The footer overlays the bottom of the scroll area (so content can glass under\n // it). Measure it so the scroll content reserves matching bottom space and the\n // last item is never hidden behind it.\n const footerRef = useRef<HTMLDivElement>(null);\n const [footerHeight, setFooterHeight] = useState(0);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: re-run when the footer node mounts/unmounts so the observer attaches to the current element.\n useLayoutEffect(() => {\n const el = footerRef.current;\n if (!el) {\n setFooterHeight(0);\n return;\n }\n const update = () => setFooterHeight(el.offsetHeight);\n update();\n const observer = new ResizeObserver(update);\n observer.observe(el);\n return () => observer.disconnect();\n }, [footer]);\n\n return (\n <div\n className={`w-full h-screen flex flex-col overflow-hidden bg-[var(--malachite-card-sunken-background)] ${className}`}\n style={minWidth ? { minWidth: `${minWidth}px` } : undefined}\n >\n <div className=\"w-full shrink-0 flex items-center justify-between px-6 pt-4 min-h-[var(--malachite-control-height-small,28px)]\">\n <div className=\"flex items-center gap-[16px] min-w-0\">\n {showBack ? (\n <Button\n variant=\"outline\"\n size=\"small\"\n iconOnly\n icon={<ArrowLeftIcon size={14} />}\n disabled={backDisabled}\n onClick={onBack}\n aria-label=\"Back\"\n className=\"w-[var(--malachite-control-height-small,28px)] h-[var(--malachite-control-height-small,28px)] shrink-0\"\n />\n ) : (\n <span className=\"w-[var(--malachite-control-height-small,28px)] h-[var(--malachite-control-height-small,28px)] shrink-0\" />\n )}\n {leading}\n </div>\n <div className=\"flex items-center gap-[16px]\">\n {trailing ?? null}\n {stepper ?? null}\n </div>\n </div>\n\n {header ? (\n <div className=\"w-full shrink-0 flex flex-col items-center px-6 pt-8\">\n <div\n className=\"w-full flex flex-col items-center gap-[24px]\"\n style={{ maxWidth: `${contentWidth}px` }}\n >\n {header}\n </div>\n </div>\n ) : null}\n\n <div className=\"relative flex-1 min-h-0\">\n <div\n className={`absolute inset-0 overflow-y-auto flex flex-col items-center px-6 ${\n center ? 'justify-center' : 'justify-start'\n }`}\n style={{\n // Reserve the overlaid footer's height plus a gap, so the last item\n // clears the footer with breathing room (not flush against it, which\n // reads as hidden content continuing underneath).\n paddingBottom: footer ? `${footerHeight + 24}px` : '48px',\n }}\n >\n <div\n className=\"w-full flex flex-col items-center gap-[40px]\"\n style={{\n maxWidth: `${contentWidth}px`,\n // Top padding lives on the scrolling content — NOT the scrollport —\n // so a `position: sticky` child pins flush to the top with no gap.\n paddingTop: header ? '32px' : '48px',\n }}\n >\n {children}\n </div>\n </div>\n\n {footer ? (\n <div\n ref={footerRef}\n // z-20 keeps the pinned footer above sticky body content (e.g. a\n // sticky Accordion header at z-10) so it can't paint over the footer.\n className=\"absolute inset-x-0 bottom-0 z-20\"\n style={footerGlass ? GLASS_SURFACE_STYLE : undefined}\n >\n {footer}\n </div>\n ) : null}\n </div>\n </div>\n );\n};\n\nexport default WizardLayout;\nexport { WizardLayout };\n"],"names":["WizardLayout","showBack","onBack","backDisabled","leading","stepper","trailing","header","footer","footerGlass","center","contentWidth","minWidth","children","className","footerRef","useRef","footerHeight","setFooterHeight","useState","useLayoutEffect","el","update","observer","jsxs","jsx","Button","ArrowLeftIcon","GLASS_SURFACE_STYLE"],"mappings":"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"}
1
+ {"version":3,"file":"WizardLayout.js","sources":["../../src/components/WizardLayout.tsx"],"sourcesContent":["import React, { useLayoutEffect, useRef, useState } from 'react';\nimport Button from './Button';\nimport ArrowLeftIcon from './CustomIcons/ArrowLeftIcon';\nimport { GLASS_SURFACE_STYLE } from './glassSurface';\n\nexport interface WizardLayoutProps {\n /** Renders the outline back button in the top-left slot. */\n showBack?: boolean;\n onBack?: () => void;\n backDisabled?: boolean;\n /** `false` drops the hidden back button's square, so `leading` sits at the same inset as `trailing`. */\n reserveBack?: boolean;\n /** `large`: a 60px bar with 28px insets (52px and 16px below 640px), items centred. */\n topBarSize?: 'default' | 'large';\n /** Top-left slot beside the back button — e.g. a user avatar menu. */\n leading?: React.ReactNode;\n /** Right top-bar slot (sits just before `stepper`) — e.g. a theme toggle. */\n stepper?: React.ReactNode;\n /** Far-right top-bar slot — e.g. a theme toggle. */\n trailing?: React.ReactNode;\n /** A page-wide notice under the top bar, above `header`. */\n banner?: React.ReactNode;\n /**\n * Pinned below the top bar and always visible while the content scrolls —\n * e.g. connection logos + a step title. Rendered in the same centred column\n * as the content (`contentWidth`).\n */\n header?: React.ReactNode;\n /** Pinned to the bottom of the screen — typically a <FooterNav /> or a primary CTA. */\n footer?: React.ReactNode;\n /**\n * Give the pinned footer a frosted-glass treatment (translucent background +\n * backdrop blur) so scrolling content shows through it. The footer already sits\n * above sticky body content, so it reads as glass over the scrolling list.\n */\n footerGlass?: boolean;\n /** `content` holds the footer to the content column, so a full-width action lines up with the list. */\n footerAlign?: 'full' | 'content';\n /** Vertically centre the content (used for terminal/success screens). */\n center?: boolean;\n /** Max width of the centered content column. */\n contentWidth?: number;\n /** Minimum width of the whole layout, below which it scrolls horizontally. */\n minWidth?: number;\n children?: React.ReactNode;\n className?: string;\n}\n\nconst TOP_BAR_CLASSES: Record<NonNullable<WizardLayoutProps['topBarSize']>, string> = {\n default: 'px-6 pt-4 min-h-[var(--malachite-control-height-small,28px)]',\n large: 'h-[60px] px-[28px] max-sm:h-[52px] max-sm:px-[16px]',\n};\n\nconst WizardLayout: React.FC<WizardLayoutProps> = ({\n showBack = false,\n onBack,\n backDisabled = false,\n reserveBack = true,\n topBarSize = 'default',\n leading,\n stepper,\n trailing,\n banner,\n header,\n footer,\n footerGlass = false,\n footerAlign = 'full',\n center = false,\n contentWidth = 480,\n minWidth,\n children,\n className = '',\n}) => {\n // The footer overlays the bottom of the scroll area (so content can glass under\n // it). Measure it so the scroll content reserves matching bottom space and the\n // last item is never hidden behind it.\n const footerRef = useRef<HTMLDivElement>(null);\n const [footerHeight, setFooterHeight] = useState(0);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: re-run when the footer node mounts/unmounts so the observer attaches to the current element.\n useLayoutEffect(() => {\n const el = footerRef.current;\n if (!el) {\n setFooterHeight(0);\n return;\n }\n const update = () => setFooterHeight(el.offsetHeight);\n update();\n const observer = new ResizeObserver(update);\n observer.observe(el);\n return () => observer.disconnect();\n }, [footer]);\n\n return (\n <div\n className={`w-full h-screen flex flex-col overflow-hidden bg-[var(--malachite-card-sunken-background)] ${className}`}\n style={minWidth ? { minWidth: `${minWidth}px` } : undefined}\n >\n <div\n className={`w-full shrink-0 flex items-center justify-between ${TOP_BAR_CLASSES[topBarSize]}`}\n >\n <div className=\"flex items-center gap-[16px] min-w-0\">\n {showBack ? (\n <Button\n variant=\"outline\"\n size=\"small\"\n iconOnly\n icon={<ArrowLeftIcon size={14} />}\n disabled={backDisabled}\n onClick={onBack}\n aria-label=\"Back\"\n className=\"w-[var(--malachite-control-height-small,28px)] h-[var(--malachite-control-height-small,28px)] shrink-0\"\n />\n ) : reserveBack ? (\n <span className=\"w-[var(--malachite-control-height-small,28px)] h-[var(--malachite-control-height-small,28px)] shrink-0\" />\n ) : null}\n {leading}\n </div>\n <div className=\"flex items-center gap-[16px]\">\n {trailing ?? null}\n {stepper ?? null}\n </div>\n </div>\n\n {banner ? <div className=\"w-full shrink-0 px-[12px] pt-[12px]\">{banner}</div> : null}\n\n {header ? (\n <div className=\"w-full shrink-0 flex flex-col items-center px-6 pt-8\">\n <div\n className=\"w-full flex flex-col items-center gap-[24px]\"\n style={{ maxWidth: `${contentWidth}px` }}\n >\n {header}\n </div>\n </div>\n ) : null}\n\n <div className=\"relative flex-1 min-h-0\">\n <div\n className={`absolute inset-0 overflow-y-auto flex flex-col items-center px-6 ${\n center ? 'justify-center' : 'justify-start'\n }`}\n style={{\n // Reserve the overlaid footer's height plus a gap, so the last item\n // clears the footer with breathing room (not flush against it, which\n // reads as hidden content continuing underneath).\n paddingBottom: footer ? `${footerHeight + 24}px` : '48px',\n }}\n >\n <div\n className=\"w-full flex flex-col items-center gap-[40px]\"\n style={{\n maxWidth: `${contentWidth}px`,\n // Top padding lives on the scrolling content — NOT the scrollport —\n // so a `position: sticky` child pins flush to the top with no gap.\n paddingTop: header ? '32px' : '48px',\n }}\n >\n {children}\n </div>\n </div>\n\n {footer ? (\n <div\n ref={footerRef}\n // z-20 keeps the pinned footer above sticky body content (e.g. a\n // sticky Accordion header at z-10) so it can't paint over the footer.\n className=\"absolute inset-x-0 bottom-0 z-20\"\n style={footerGlass ? GLASS_SURFACE_STYLE : undefined}\n >\n {footerAlign === 'content' ? (\n <div className=\"flex justify-center px-6 pt-4 pb-3\">\n <div className=\"w-full\" style={{ maxWidth: `${contentWidth}px` }}>\n {footer}\n </div>\n </div>\n ) : (\n footer\n )}\n </div>\n ) : null}\n </div>\n </div>\n );\n};\n\nexport default WizardLayout;\nexport { WizardLayout };\n"],"names":["TOP_BAR_CLASSES","WizardLayout","showBack","onBack","backDisabled","reserveBack","topBarSize","leading","stepper","trailing","banner","header","footer","footerGlass","footerAlign","center","contentWidth","minWidth","children","className","footerRef","useRef","footerHeight","setFooterHeight","useState","useLayoutEffect","el","update","observer","jsxs","jsx","Button","ArrowLeftIcon","GLASS_SURFACE_STYLE"],"mappings":"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"}