@vertigis/react-ui 9.2.1 → 9.3.1

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.
@@ -0,0 +1,39 @@
1
+ import { FC } from "react";
2
+ import { BoxProps } from "../Box";
3
+ /**
4
+ * Properties for the `Markdown` component.
5
+ */
6
+ export interface MarkdownProps extends BoxProps {
7
+ /**
8
+ * The markdown text to render.
9
+ */
10
+ markdown: string;
11
+ /**
12
+ * A caller-supplied sanitization function to ensure that the resulting HTML
13
+ * is safe to insert into the DOM. Required.
14
+ */
15
+ sanitize: (unsafeHtml: string) => string;
16
+ /**
17
+ * If specified, produces HTML that is valid for contexts where only inline
18
+ * content is allowed (e.g. inside `<h1>`>` or `<span>` tags). HTML tags for
19
+ * block-level elements will be removed, but any text content will be
20
+ * preserved.
21
+ */
22
+ inline?: boolean;
23
+ /**
24
+ * If true, any HTML in the markdown will be escaped rather than passed
25
+ * through directly to the output as normal (since Markdown is a superset of
26
+ * HTML). This is useful in situations where you wish to only support pure
27
+ * Markdown syntax without also supporting HTML syntax. The default is
28
+ * `false`.
29
+ *
30
+ * IMPORTANT: The resulting HTML still needs to be sanitized whether or not
31
+ * this option is set.
32
+ */
33
+ escapeHtml?: boolean;
34
+ }
35
+ /**
36
+ * A component that renders markdown as HTML.
37
+ */
38
+ declare const Markdown: FC<MarkdownProps>;
39
+ export default Markdown;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import clsx from "clsx";
3
+ import Box from "../Box";
4
+ import { markdownToHtml } from "../utils/markdown";
5
+ /**
6
+ * A component that renders markdown as HTML.
7
+ */
8
+ const Markdown = ({ markdown, inline, escapeHtml, sanitize, className, ...otherProps }) => {
9
+ return (_jsx(Box, Object.assign({ component: inline ? "span" : "div", className: clsx("GcxMarkdown", className), dangerouslySetInnerHTML: {
10
+ __html: sanitize(markdownToHtml(markdown, { inline, escapeHtml })),
11
+ } }, otherProps), void 0));
12
+ };
13
+ export default Markdown;
@@ -0,0 +1,2 @@
1
+ export * from "./Markdown";
2
+ export { default } from "./Markdown";
@@ -0,0 +1,2 @@
1
+ export * from "./Markdown";
2
+ export { default } from "./Markdown";
@@ -0,0 +1,11 @@
1
+ import { FC } from "react";
2
+ import { ListItemSecondaryActionProps } from "../ListItemSecondaryAction";
3
+ /**
4
+ * For use within a MenuList (or other list that implements arrow key
5
+ * navigation). The standard ListItemSecondaryAction will break keyboard
6
+ * navigation in this type of list.
7
+ *
8
+ * @param props The component properties.
9
+ */
10
+ declare const MenuItemSecondaryAction: FC<ListItemSecondaryActionProps>;
11
+ export default MenuItemSecondaryAction;
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useRef } from "react";
3
+ import GcxListItemSecondaryAction from "../ListItemSecondaryAction";
4
+ import { styled } from "../styles";
5
+ const StyledListItemSecondaryAction = styled(GcxListItemSecondaryAction)({
6
+ display: "flex",
7
+ flexShrink: 0,
8
+ });
9
+ /**
10
+ * For use within a MenuList (or other list that implements arrow key
11
+ * navigation). The standard ListItemSecondaryAction will break keyboard
12
+ * navigation in this type of list.
13
+ *
14
+ * @param props The component properties.
15
+ */
16
+ const MenuItemSecondaryAction = (props) => {
17
+ const ref = useRef(null);
18
+ const { className, ...otherProps } = props;
19
+ const onKeyDown = (event) => {
20
+ var _a, _b;
21
+ const { key } = event;
22
+ if (key === "ArrowUp" || key === "ArrowDown" || key === "Home" || key === "End") {
23
+ // Switch focus to the parent list item, then let the key event
24
+ // propagate to get the normal behavior of pressing these keys.
25
+ (_b = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.parentElement) === null || _b === void 0 ? void 0 : _b.focus();
26
+ }
27
+ else if (key !== "Escape") {
28
+ // Prevent propagation to the primary action.
29
+ event.stopPropagation();
30
+ }
31
+ };
32
+ return (_jsx(StyledListItemSecondaryAction, Object.assign({ className: className, onClick: (event) => {
33
+ event.stopPropagation();
34
+ }, onKeyDown: onKeyDown, onMouseDown: (event) => {
35
+ // This will stop child ripples from propagating.
36
+ event.stopPropagation();
37
+ }, ref: ref }, otherProps), void 0));
38
+ };
39
+ export default MenuItemSecondaryAction;
@@ -0,0 +1,2 @@
1
+ export { default } from "./MenuItemSecondaryAction";
2
+ export * from "./MenuItemSecondaryAction";
@@ -0,0 +1,2 @@
1
+ export { default } from "./MenuItemSecondaryAction";
2
+ export * from "./MenuItemSecondaryAction";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertigis/react-ui",
3
- "version": "9.2.1",
3
+ "version": "9.3.1",
4
4
  "description": "Utilities and React components used in VertiGIS applications.",
5
5
  "keywords": [
6
6
  "vertigis",
@@ -22,6 +22,7 @@
22
22
  "@mui/material": "5.2.8",
23
23
  "clsx": "^1.1.1",
24
24
  "lodash": "^4.17.21",
25
+ "marked": "^4.0.12",
25
26
  "tslib": "^2.1.0"
26
27
  },
27
28
  "devDependencies": {
@@ -29,6 +30,7 @@
29
30
  "@testing-library/react": "^12.1.2",
30
31
  "@testing-library/user-event": "^13.5.0",
31
32
  "@types/lodash": "^4.14.168",
33
+ "@types/marked": "^4.0.2",
32
34
  "@types/react": "^17.0.34",
33
35
  "@types/react-dom": "^17.0.11",
34
36
  "react": "^17.0.2",
@@ -465,6 +465,31 @@ function getOverrides(theme) {
465
465
  },
466
466
  },
467
467
  MuiListItem: {
468
+ styleOverrides: {
469
+ root: {
470
+ "&.MuiListItem-secondaryAction, &>.MuiListItemButton-root": {
471
+ // Disable the right padding of 48px that MUI uses
472
+ // to make room for secondary actions, since we use
473
+ // flex for those rather than absolute positioning
474
+ // (see MuiListItemSecondaryAction overrides). Note
475
+ // that there are two distinct cases we need to
476
+ // handle, depending on whether a
477
+ // <ListItemSecondaryAction> element is used
478
+ // explicitly, or whether the ListItem's
479
+ // `secondaryAction` prop is used.
480
+ paddingRight: spacing(1),
481
+ },
482
+ },
483
+ container: {
484
+ // A wrapper element with this class is added around a
485
+ // list item when a <ListItemSecondaryAction> child is
486
+ // present. Use flex to position the secondary actions
487
+ // next to the list item content rather than absolute
488
+ // positioning so that nothing overlaps.
489
+ display: "flex",
490
+ alignItems: "center",
491
+ },
492
+ },
468
493
  variants: [
469
494
  {
470
495
  // HACK: The button prop is deprecated in MUI 5 in favor
@@ -478,6 +503,18 @@ function getOverrides(theme) {
478
503
  },
479
504
  ],
480
505
  },
506
+ MuiListItemSecondaryAction: {
507
+ styleOverrides: {
508
+ root: {
509
+ // We use flex instead of absolute positioning so that
510
+ // secondary buttons don't overlap the item text.
511
+ position: "static",
512
+ transform: "unset",
513
+ flex: "0 0 auto",
514
+ paddingRight: spacing(1),
515
+ },
516
+ },
517
+ },
481
518
  MuiListItemText: {
482
519
  styleOverrides: {
483
520
  primary: {
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Options for the markdownToHtml() function.
3
+ */
4
+ export interface MarkdownToHtmlOptions {
5
+ /**
6
+ * If specified, produces HTML that is valid to place within an HTML element
7
+ * that only supports inline content (called "phrasing content" in the HTML5
8
+ * spec). Example elements that only support phrasing content are <span> and
9
+ * <h2>. In this mode, any block-level elements resulting from the Markdown
10
+ * conversion will be stripped. The default is `false`.
11
+ */
12
+ inline?: boolean;
13
+ /**
14
+ * If true, any HTML in the markdown will be escaped rather than passed
15
+ * through directly to the output (since Markdown is a superset of HTML).
16
+ * This is useful in situations where you wish to only support pure Markdown
17
+ * syntax without also supporting HTML syntax. The default is `false`.
18
+ *
19
+ * IMPORTANT: The resulting HTML still needs to be sanitized whether or not
20
+ * this option is set.
21
+ */
22
+ escapeHtml?: boolean;
23
+ }
24
+ /**
25
+ * Converts markdown text into HTML.
26
+ *
27
+ * NOTE: The resulting HTML is NOT sanitized by this function. It is the
28
+ * caller's responsibility to sanitize the resulting HTML prior to inserting it
29
+ * into the DOM. You must do this even if `escapeHtml` is set to true.
30
+ *
31
+ * @param markdown The markdown text to convert.
32
+ * @param options Options that affect how the text is converted.
33
+ */
34
+ export declare function markdownToHtml(markdown: string | undefined, options?: MarkdownToHtmlOptions): string;
@@ -0,0 +1,44 @@
1
+ import escapeHtml from "lodash/escape";
2
+ import { marked } from "marked";
3
+ /**
4
+ * A custom marked renderer that escapes HTML in the original markdown.
5
+ */
6
+ class EscapeHtmlRenderer extends marked.Renderer {
7
+ html(html) {
8
+ return escapeHtml(html);
9
+ }
10
+ }
11
+ EscapeHtmlRenderer.instance = new EscapeHtmlRenderer();
12
+ /**
13
+ * Converts markdown text into HTML.
14
+ *
15
+ * NOTE: The resulting HTML is NOT sanitized by this function. It is the
16
+ * caller's responsibility to sanitize the resulting HTML prior to inserting it
17
+ * into the DOM. You must do this even if `escapeHtml` is set to true.
18
+ *
19
+ * @param markdown The markdown text to convert.
20
+ * @param options Options that affect how the text is converted.
21
+ */
22
+ export function markdownToHtml(markdown, options) {
23
+ if (!markdown) {
24
+ return "";
25
+ }
26
+ const { inline, escapeHtml } = { inline: false, escapeHtml: false, ...options };
27
+ const markedOptions = {
28
+ ...(escapeHtml && {
29
+ renderer: EscapeHtmlRenderer.instance,
30
+ }),
31
+ };
32
+ const html = marked(markdown, markedOptions);
33
+ return inline ? stripBlockElements(html) : html;
34
+ }
35
+ /**
36
+ * Strips all HTML elements that are not suitable for placing within an inline
37
+ * HTML element.
38
+ *
39
+ * @param html The HTML to process.
40
+ */
41
+ function stripBlockElements(html) {
42
+ // Derived from here: https://html.spec.whatwg.org/multipage/dom.html#phrasing-content
43
+ return html.replace(/(?:<(\/?))(?!(?:\/|a|abbr|area|audio|b|bdi|bdo|br|button|canvas|cite|code|data|datalist|del|dfn|em|embed|i|iframe|img|input|ins|kbd|label|link|map|mark|math|meta|meter|noscript|object|output|picture|progress|q|ruby|s|samp|script|select|slot|small|span|strong|sub|sup|svg|template|textarea|time|u|var|video|wbr)\b)[^>]*>/gi, (match, s1) => (s1 === "/" ? " " : ""));
44
+ }