jvetrau-ds 0.2.46 → 0.2.48
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/components/course-outline/course-outline.css +3 -1
- package/components/dropdown/dropdown.cjs +34 -5
- package/components/dropdown/dropdown.cjs.map +1 -1
- package/components/dropdown/dropdown.js +34 -5
- package/components/dropdown/dropdown.js.map +1 -1
- package/components/index.d.ts +1 -1
- package/components/markdown/index.d.ts +1 -1
- package/components/markdown/markdown.cjs +13 -2
- package/components/markdown/markdown.cjs.map +1 -1
- package/components/markdown/markdown.d.ts +2 -0
- package/components/markdown/markdown.js +13 -2
- package/components/markdown/markdown.js.map +1 -1
- package/index.d.ts +1 -1
- package/layouts/base/base.css +1 -1
- package/layouts/foundation/foundation.css +1 -1
- package/package.json +1 -1
- package/styles.css +5 -3
- package/themes/base/desktop.css +5 -1
- package/themes/base/desktop.md +5 -1
- package/themes/base/desktop.scoped.css +5 -1
- package/themes/base/diagram.css +5 -1
- package/themes/base/diagram.md +5 -1
- package/themes/base/diagram.scoped.css +5 -1
- package/themes/base/mobile.css +5 -1
- package/themes/base/mobile.md +5 -1
- package/themes/base/mobile.scoped.css +5 -1
- package/themes/base/responsive.css +379 -0
- package/themes/brandcopilot/desktop.css +5 -1
- package/themes/brandcopilot/desktop.md +5 -1
- package/themes/brandcopilot/desktop.scoped.css +5 -1
- package/themes/brandcopilot/diagram.css +5 -1
- package/themes/brandcopilot/diagram.md +5 -1
- package/themes/brandcopilot/diagram.scoped.css +5 -1
- package/themes/brandcopilot/mobile.css +5 -1
- package/themes/brandcopilot/mobile.md +5 -1
- package/themes/brandcopilot/mobile.scoped.css +5 -1
- package/themes/brandcopilot/responsive.css +379 -0
- package/themes/dmpatterns/desktop.css +5 -1
- package/themes/dmpatterns/desktop.md +5 -1
- package/themes/dmpatterns/desktop.scoped.css +5 -1
- package/themes/dmpatterns/diagram.css +5 -1
- package/themes/dmpatterns/diagram.md +5 -1
- package/themes/dmpatterns/diagram.scoped.css +5 -1
- package/themes/dmpatterns/mobile.css +5 -1
- package/themes/dmpatterns/mobile.md +5 -1
- package/themes/dmpatterns/mobile.scoped.css +5 -1
- package/themes/dmpatterns/responsive.css +379 -0
- package/themes/pdigest/desktop.css +5 -1
- package/themes/pdigest/desktop.md +5 -1
- package/themes/pdigest/desktop.scoped.css +5 -1
- package/themes/pdigest/diagram.css +5 -1
- package/themes/pdigest/diagram.md +5 -1
- package/themes/pdigest/diagram.scoped.css +5 -1
- package/themes/pdigest/mobile.css +5 -1
- package/themes/pdigest/mobile.md +5 -1
- package/themes/pdigest/mobile.scoped.css +5 -1
- package/themes/pdigest/responsive.css +379 -0
- package/themes/retrack/desktop.css +5 -1
- package/themes/retrack/desktop.md +5 -1
- package/themes/retrack/desktop.scoped.css +5 -1
- package/themes/retrack/diagram.css +5 -1
- package/themes/retrack/diagram.md +5 -1
- package/themes/retrack/diagram.scoped.css +5 -1
- package/themes/retrack/mobile.css +5 -1
- package/themes/retrack/mobile.md +5 -1
- package/themes/retrack/mobile.scoped.css +5 -1
- package/themes/retrack/responsive.css +379 -0
- package/tokens/runtime/tokens.base.desktop.runtime.cjs +4 -0
- package/tokens/runtime/tokens.base.desktop.runtime.cjs.map +1 -1
- package/tokens/runtime/tokens.base.desktop.runtime.d.ts +4 -0
- package/tokens/runtime/tokens.base.desktop.runtime.js +4 -0
- package/tokens/runtime/tokens.base.desktop.runtime.js.map +1 -1
- package/tokens/runtime/tokens.base.diagram.runtime.cjs +4 -0
- package/tokens/runtime/tokens.base.diagram.runtime.cjs.map +1 -1
- package/tokens/runtime/tokens.base.diagram.runtime.d.ts +4 -0
- package/tokens/runtime/tokens.base.diagram.runtime.js +4 -0
- package/tokens/runtime/tokens.base.diagram.runtime.js.map +1 -1
- package/tokens/runtime/tokens.base.mobile.runtime.cjs +4 -0
- package/tokens/runtime/tokens.base.mobile.runtime.cjs.map +1 -1
- package/tokens/runtime/tokens.base.mobile.runtime.d.ts +4 -0
- package/tokens/runtime/tokens.base.mobile.runtime.js +4 -0
- package/tokens/runtime/tokens.base.mobile.runtime.js.map +1 -1
- package/tokens/runtime/tokens.brandcopilot.desktop.runtime.cjs +4 -0
- package/tokens/runtime/tokens.brandcopilot.desktop.runtime.cjs.map +1 -1
- package/tokens/runtime/tokens.brandcopilot.desktop.runtime.d.ts +4 -0
- package/tokens/runtime/tokens.brandcopilot.desktop.runtime.js +4 -0
- package/tokens/runtime/tokens.brandcopilot.desktop.runtime.js.map +1 -1
- package/tokens/runtime/tokens.brandcopilot.diagram.runtime.cjs +4 -0
- package/tokens/runtime/tokens.brandcopilot.diagram.runtime.cjs.map +1 -1
- package/tokens/runtime/tokens.brandcopilot.diagram.runtime.d.ts +4 -0
- package/tokens/runtime/tokens.brandcopilot.diagram.runtime.js +4 -0
- package/tokens/runtime/tokens.brandcopilot.diagram.runtime.js.map +1 -1
- package/tokens/runtime/tokens.brandcopilot.mobile.runtime.cjs +4 -0
- package/tokens/runtime/tokens.brandcopilot.mobile.runtime.cjs.map +1 -1
- package/tokens/runtime/tokens.brandcopilot.mobile.runtime.d.ts +4 -0
- package/tokens/runtime/tokens.brandcopilot.mobile.runtime.js +4 -0
- package/tokens/runtime/tokens.brandcopilot.mobile.runtime.js.map +1 -1
- package/tokens/runtime/tokens.dmpatterns.desktop.runtime.cjs +4 -0
- package/tokens/runtime/tokens.dmpatterns.desktop.runtime.cjs.map +1 -1
- package/tokens/runtime/tokens.dmpatterns.desktop.runtime.d.ts +4 -0
- package/tokens/runtime/tokens.dmpatterns.desktop.runtime.js +4 -0
- package/tokens/runtime/tokens.dmpatterns.desktop.runtime.js.map +1 -1
- package/tokens/runtime/tokens.dmpatterns.diagram.runtime.cjs +4 -0
- package/tokens/runtime/tokens.dmpatterns.diagram.runtime.cjs.map +1 -1
- package/tokens/runtime/tokens.dmpatterns.diagram.runtime.d.ts +4 -0
- package/tokens/runtime/tokens.dmpatterns.diagram.runtime.js +4 -0
- package/tokens/runtime/tokens.dmpatterns.diagram.runtime.js.map +1 -1
- package/tokens/runtime/tokens.dmpatterns.mobile.runtime.cjs +4 -0
- package/tokens/runtime/tokens.dmpatterns.mobile.runtime.cjs.map +1 -1
- package/tokens/runtime/tokens.dmpatterns.mobile.runtime.d.ts +4 -0
- package/tokens/runtime/tokens.dmpatterns.mobile.runtime.js +4 -0
- package/tokens/runtime/tokens.dmpatterns.mobile.runtime.js.map +1 -1
- package/tokens/runtime/tokens.pdigest.desktop.runtime.cjs +4 -0
- package/tokens/runtime/tokens.pdigest.desktop.runtime.cjs.map +1 -1
- package/tokens/runtime/tokens.pdigest.desktop.runtime.d.ts +4 -0
- package/tokens/runtime/tokens.pdigest.desktop.runtime.js +4 -0
- package/tokens/runtime/tokens.pdigest.desktop.runtime.js.map +1 -1
- package/tokens/runtime/tokens.pdigest.diagram.runtime.cjs +4 -0
- package/tokens/runtime/tokens.pdigest.diagram.runtime.cjs.map +1 -1
- package/tokens/runtime/tokens.pdigest.diagram.runtime.d.ts +4 -0
- package/tokens/runtime/tokens.pdigest.diagram.runtime.js +4 -0
- package/tokens/runtime/tokens.pdigest.diagram.runtime.js.map +1 -1
- package/tokens/runtime/tokens.pdigest.mobile.runtime.cjs +4 -0
- package/tokens/runtime/tokens.pdigest.mobile.runtime.cjs.map +1 -1
- package/tokens/runtime/tokens.pdigest.mobile.runtime.d.ts +4 -0
- package/tokens/runtime/tokens.pdigest.mobile.runtime.js +4 -0
- package/tokens/runtime/tokens.pdigest.mobile.runtime.js.map +1 -1
- package/tokens/runtime/tokens.retrack.desktop.runtime.cjs +4 -0
- package/tokens/runtime/tokens.retrack.desktop.runtime.cjs.map +1 -1
- package/tokens/runtime/tokens.retrack.desktop.runtime.d.ts +4 -0
- package/tokens/runtime/tokens.retrack.desktop.runtime.js +4 -0
- package/tokens/runtime/tokens.retrack.desktop.runtime.js.map +1 -1
- package/tokens/runtime/tokens.retrack.diagram.runtime.cjs +4 -0
- package/tokens/runtime/tokens.retrack.diagram.runtime.cjs.map +1 -1
- package/tokens/runtime/tokens.retrack.diagram.runtime.d.ts +4 -0
- package/tokens/runtime/tokens.retrack.diagram.runtime.js +4 -0
- package/tokens/runtime/tokens.retrack.diagram.runtime.js.map +1 -1
- package/tokens/runtime/tokens.retrack.mobile.runtime.cjs +4 -0
- package/tokens/runtime/tokens.retrack.mobile.runtime.cjs.map +1 -1
- package/tokens/runtime/tokens.retrack.mobile.runtime.d.ts +4 -0
- package/tokens/runtime/tokens.retrack.mobile.runtime.js +4 -0
- package/tokens/runtime/tokens.retrack.mobile.runtime.js.map +1 -1
- package/tokens/themes/base/dist/desktop.json +33 -1
- package/tokens/themes/base/dist/diagram.json +33 -1
- package/tokens/themes/base/dist/mobile.json +33 -1
- package/tokens/themes/brandcopilot/dist/desktop.json +33 -1
- package/tokens/themes/brandcopilot/dist/diagram.json +33 -1
- package/tokens/themes/brandcopilot/dist/mobile.json +33 -1
- package/tokens/themes/dmpatterns/dist/desktop.json +33 -1
- package/tokens/themes/dmpatterns/dist/diagram.json +33 -1
- package/tokens/themes/dmpatterns/dist/mobile.json +33 -1
- package/tokens/themes/pdigest/dist/desktop.json +33 -1
- package/tokens/themes/pdigest/dist/diagram.json +33 -1
- package/tokens/themes/pdigest/dist/mobile.json +33 -1
- package/tokens/themes/retrack/dist/desktop.json +33 -1
- package/tokens/themes/retrack/dist/diagram.json +33 -1
- package/tokens/themes/retrack/dist/mobile.json +33 -1
|
@@ -5,6 +5,7 @@ var react = require('react');
|
|
|
5
5
|
require('./dropdown.css');
|
|
6
6
|
var classnames = require('../helpers/classnames');
|
|
7
7
|
|
|
8
|
+
const HOVER_CLOSE_DELAY_MS = 150;
|
|
8
9
|
const Dropdown = ({
|
|
9
10
|
options,
|
|
10
11
|
value,
|
|
@@ -22,6 +23,7 @@ const Dropdown = ({
|
|
|
22
23
|
...props
|
|
23
24
|
}) => {
|
|
24
25
|
const dropdownRef = react.useRef(null);
|
|
26
|
+
const hoverCloseTimeoutRef = react.useRef(null);
|
|
25
27
|
const [isOpen, setIsOpen] = react.useState(false);
|
|
26
28
|
const [
|
|
27
29
|
internalValue,
|
|
@@ -29,6 +31,26 @@ const Dropdown = ({
|
|
|
29
31
|
] = react.useState(defaultValue);
|
|
30
32
|
const selectedValue = value ?? internalValue;
|
|
31
33
|
const isHoverInteraction = interaction === "hover";
|
|
34
|
+
const clearHoverCloseTimeout = () => {
|
|
35
|
+
if (hoverCloseTimeoutRef.current === null) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
clearTimeout(
|
|
39
|
+
hoverCloseTimeoutRef.current
|
|
40
|
+
);
|
|
41
|
+
hoverCloseTimeoutRef.current = null;
|
|
42
|
+
};
|
|
43
|
+
const closeDropdown = () => {
|
|
44
|
+
clearHoverCloseTimeout();
|
|
45
|
+
setIsOpen(false);
|
|
46
|
+
};
|
|
47
|
+
const scheduleHoverClose = () => {
|
|
48
|
+
clearHoverCloseTimeout();
|
|
49
|
+
hoverCloseTimeoutRef.current = setTimeout(() => {
|
|
50
|
+
hoverCloseTimeoutRef.current = null;
|
|
51
|
+
setIsOpen(false);
|
|
52
|
+
}, HOVER_CLOSE_DELAY_MS);
|
|
53
|
+
};
|
|
32
54
|
const classes = classnames.classnames(
|
|
33
55
|
"dropdown",
|
|
34
56
|
className,
|
|
@@ -72,23 +94,25 @@ const Dropdown = ({
|
|
|
72
94
|
setInternalValue(option.value);
|
|
73
95
|
}
|
|
74
96
|
onChange?.(option.value);
|
|
75
|
-
|
|
97
|
+
closeDropdown();
|
|
76
98
|
};
|
|
77
99
|
const handleMouseEnter = (event) => {
|
|
78
100
|
onMouseEnter?.(event);
|
|
79
101
|
if (isHoverInteraction) {
|
|
102
|
+
clearHoverCloseTimeout();
|
|
80
103
|
setIsOpen(true);
|
|
81
104
|
}
|
|
82
105
|
};
|
|
83
106
|
const handleMouseLeave = (event) => {
|
|
84
107
|
onMouseLeave?.(event);
|
|
85
108
|
if (isHoverInteraction) {
|
|
86
|
-
|
|
109
|
+
scheduleHoverClose();
|
|
87
110
|
}
|
|
88
111
|
};
|
|
89
112
|
const handleFocus = (event) => {
|
|
90
113
|
onFocus?.(event);
|
|
91
114
|
if (isHoverInteraction) {
|
|
115
|
+
clearHoverCloseTimeout();
|
|
92
116
|
setIsOpen(true);
|
|
93
117
|
}
|
|
94
118
|
};
|
|
@@ -103,8 +127,13 @@ const Dropdown = ({
|
|
|
103
127
|
)) {
|
|
104
128
|
return;
|
|
105
129
|
}
|
|
106
|
-
|
|
130
|
+
closeDropdown();
|
|
107
131
|
};
|
|
132
|
+
react.useEffect(() => {
|
|
133
|
+
return () => {
|
|
134
|
+
clearHoverCloseTimeout();
|
|
135
|
+
};
|
|
136
|
+
}, []);
|
|
108
137
|
react.useEffect(() => {
|
|
109
138
|
if (!isOpen) {
|
|
110
139
|
return;
|
|
@@ -116,11 +145,11 @@ const Dropdown = ({
|
|
|
116
145
|
)) {
|
|
117
146
|
return;
|
|
118
147
|
}
|
|
119
|
-
|
|
148
|
+
closeDropdown();
|
|
120
149
|
};
|
|
121
150
|
const handleDocumentKeyDown = (event) => {
|
|
122
151
|
if (event.key === "Escape") {
|
|
123
|
-
|
|
152
|
+
closeDropdown();
|
|
124
153
|
}
|
|
125
154
|
};
|
|
126
155
|
document.addEventListener(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/dropdown/dropdown.tsx"],"names":["useRef","useState","classnames","cloneElement","useEffect","jsxs","jsx"],"mappings":"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","file":"dropdown.cjs","sourcesContent":["import React, {\n cloneElement,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nimport './dropdown.css';\nimport { classnames } from '../helpers/classnames';\n\nexport interface DropdownOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport type DropdownInteraction =\n | 'click'\n | 'hover';\n\ntype DropdownTriggerProps = {\n className?: string;\n onClick?: (\n event: React.MouseEvent,\n ) => void;\n 'aria-haspopup'?: string;\n 'aria-expanded'?: boolean;\n};\n\ntype DropdownTriggerElement =\n React.ReactElement<DropdownTriggerProps>;\n\nexport interface DropdownProps\n extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onChange'\n > {\n options: DropdownOption[];\n value?: string;\n defaultValue?: string;\n label?: string;\n align?:\n | 'top-start'\n | 'top-end'\n | 'bottom-start'\n | 'bottom-end';\n interaction?: DropdownInteraction;\n trigger: DropdownTriggerElement;\n onChange?: (\n value: string,\n ) => void;\n}\n\nconst Dropdown: React.FC<DropdownProps> = ({\n options,\n value,\n defaultValue,\n label = 'Dropdown',\n align = 'bottom-start',\n interaction = 'click',\n trigger,\n onChange,\n className = '',\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ...props\n}) => {\n const dropdownRef =\n useRef<HTMLDivElement>(null);\n\n const [isOpen, setIsOpen] =\n useState(false);\n\n const [\n internalValue,\n setInternalValue,\n ] = useState(defaultValue);\n\n const selectedValue =\n value ?? internalValue;\n\n const isHoverInteraction =\n interaction === 'hover';\n\n const classes = classnames(\n 'dropdown',\n className,\n {\n 'dropdown--open': isOpen,\n 'dropdown--top-start':\n align === 'top-start',\n 'dropdown--top-end':\n align === 'top-end',\n 'dropdown--bottom-start':\n align === 'bottom-start',\n 'dropdown--bottom-end':\n align === 'bottom-end',\n },\n );\n\n const triggerElement =\n trigger as DropdownTriggerElement;\n\n const triggerClasses = classnames(\n 'dropdown__trigger',\n triggerElement.props.className,\n );\n\n const resolvedTrigger = cloneElement(\n triggerElement,\n {\n className: triggerClasses,\n 'aria-haspopup': 'menu',\n 'aria-expanded': isOpen,\n\n onClick: (\n event: React.MouseEvent,\n ) => {\n triggerElement.props.onClick?.(\n event,\n );\n\n if (isHoverInteraction) {\n return;\n }\n\n setIsOpen(\n (currentValue) =>\n !currentValue,\n );\n },\n },\n );\n\n const handleOptionClick = (\n option: DropdownOption,\n ) => {\n if (option.disabled) {\n return;\n }\n\n if (value === undefined) {\n setInternalValue(option.value);\n }\n\n onChange?.(option.value);\n setIsOpen(false);\n };\n\n const handleMouseEnter = (\n event: React.MouseEvent<HTMLDivElement>,\n ) => {\n onMouseEnter?.(event);\n\n if (isHoverInteraction) {\n setIsOpen(true);\n }\n };\n\n const handleMouseLeave = (\n event: React.MouseEvent<HTMLDivElement>,\n ) => {\n onMouseLeave?.(event);\n\n if (isHoverInteraction) {\n setIsOpen(false);\n }\n };\n\n const handleFocus = (\n event: React.FocusEvent<HTMLDivElement>,\n ) => {\n onFocus?.(event);\n\n if (isHoverInteraction) {\n setIsOpen(true);\n }\n };\n\n const handleBlur = (\n event: React.FocusEvent<HTMLDivElement>,\n ) => {\n onBlur?.(event);\n\n if (!isHoverInteraction) {\n return;\n }\n\n const nextTarget =\n event.relatedTarget;\n\n if (\n nextTarget instanceof Node &&\n event.currentTarget.contains(\n nextTarget,\n )\n ) {\n return;\n }\n\n setIsOpen(false);\n };\n\n useEffect(() => {\n if (!isOpen) {\n return;\n }\n\n const handleDocumentClick = (\n event: MouseEvent,\n ) => {\n const dropdown =\n dropdownRef.current;\n\n if (\n !dropdown ||\n dropdown.contains(\n event.target as Node,\n )\n ) {\n return;\n }\n\n setIsOpen(false);\n };\n\n const handleDocumentKeyDown = (\n event: KeyboardEvent,\n ) => {\n if (event.key === 'Escape') {\n setIsOpen(false);\n }\n };\n\n document.addEventListener(\n 'mousedown',\n handleDocumentClick,\n );\n\n document.addEventListener(\n 'keydown',\n handleDocumentKeyDown,\n );\n\n return () => {\n document.removeEventListener(\n 'mousedown',\n handleDocumentClick,\n );\n\n document.removeEventListener(\n 'keydown',\n handleDocumentKeyDown,\n );\n };\n }, [isOpen]);\n\n return (\n <div\n ref={dropdownRef}\n className={classes}\n onMouseEnter={\n handleMouseEnter\n }\n onMouseLeave={\n handleMouseLeave\n }\n onFocus={handleFocus}\n onBlur={handleBlur}\n {...props}\n >\n {resolvedTrigger}\n\n {isOpen && (\n <div\n className=\"dropdown__menu\"\n role=\"menu\"\n aria-label={label}\n >\n {options.map((option) => {\n const isSelected =\n option.value ===\n selectedValue;\n\n const itemClasses =\n classnames(\n 'dropdown__item',\n {\n 'dropdown__item--selected':\n isSelected,\n 'dropdown__item--disabled':\n option.disabled,\n },\n );\n\n return (\n <button\n key={option.value}\n className={\n itemClasses\n }\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={\n isSelected\n }\n aria-disabled={\n option.disabled\n }\n disabled={\n option.disabled\n }\n onClick={() =>\n handleOptionClick(\n option,\n )\n }\n >\n {option.label}\n </button>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n\nexport default Dropdown;\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/dropdown/dropdown.tsx"],"names":["useRef","useState","classnames","cloneElement","useEffect","jsxs","jsx"],"mappings":"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","file":"dropdown.cjs","sourcesContent":["import React, {\n cloneElement,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nimport './dropdown.css';\nimport { classnames } from '../helpers/classnames';\n\nconst HOVER_CLOSE_DELAY_MS = 150;\n\nexport interface DropdownOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport type DropdownInteraction =\n | 'click'\n | 'hover';\n\ntype DropdownTriggerProps = {\n className?: string;\n onClick?: (\n event: React.MouseEvent,\n ) => void;\n 'aria-haspopup'?: string;\n 'aria-expanded'?: boolean;\n};\n\ntype DropdownTriggerElement =\n React.ReactElement<DropdownTriggerProps>;\n\nexport interface DropdownProps\n extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onChange'\n > {\n options: DropdownOption[];\n value?: string;\n defaultValue?: string;\n label?: string;\n align?:\n | 'top-start'\n | 'top-end'\n | 'bottom-start'\n | 'bottom-end';\n interaction?: DropdownInteraction;\n trigger: DropdownTriggerElement;\n onChange?: (\n value: string,\n ) => void;\n}\n\nconst Dropdown: React.FC<DropdownProps> = ({\n options,\n value,\n defaultValue,\n label = 'Dropdown',\n align = 'bottom-start',\n interaction = 'click',\n trigger,\n onChange,\n className = '',\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ...props\n}) => {\n const dropdownRef =\n useRef<HTMLDivElement>(null);\n\n const hoverCloseTimeoutRef =\n useRef<ReturnType<\n typeof setTimeout\n > | null>(null);\n\n const [isOpen, setIsOpen] =\n useState(false);\n\n const [\n internalValue,\n setInternalValue,\n ] = useState(defaultValue);\n\n const selectedValue =\n value ?? internalValue;\n\n const isHoverInteraction =\n interaction === 'hover';\n\n const clearHoverCloseTimeout =\n () => {\n if (\n hoverCloseTimeoutRef.current ===\n null\n ) {\n return;\n }\n\n clearTimeout(\n hoverCloseTimeoutRef.current,\n );\n\n hoverCloseTimeoutRef.current =\n null;\n };\n\n const closeDropdown = () => {\n clearHoverCloseTimeout();\n setIsOpen(false);\n };\n\n const scheduleHoverClose = () => {\n clearHoverCloseTimeout();\n\n hoverCloseTimeoutRef.current =\n setTimeout(() => {\n hoverCloseTimeoutRef.current =\n null;\n setIsOpen(false);\n }, HOVER_CLOSE_DELAY_MS);\n };\n\n const classes = classnames(\n 'dropdown',\n className,\n {\n 'dropdown--open': isOpen,\n 'dropdown--top-start':\n align === 'top-start',\n 'dropdown--top-end':\n align === 'top-end',\n 'dropdown--bottom-start':\n align === 'bottom-start',\n 'dropdown--bottom-end':\n align === 'bottom-end',\n },\n );\n\n const triggerElement =\n trigger as DropdownTriggerElement;\n\n const triggerClasses = classnames(\n 'dropdown__trigger',\n triggerElement.props.className,\n );\n\n const resolvedTrigger = cloneElement(\n triggerElement,\n {\n className: triggerClasses,\n 'aria-haspopup': 'menu',\n 'aria-expanded': isOpen,\n\n onClick: (\n event: React.MouseEvent,\n ) => {\n triggerElement.props.onClick?.(\n event,\n );\n\n if (isHoverInteraction) {\n return;\n }\n\n setIsOpen(\n (currentValue) =>\n !currentValue,\n );\n },\n },\n );\n\n const handleOptionClick = (\n option: DropdownOption,\n ) => {\n if (option.disabled) {\n return;\n }\n\n if (value === undefined) {\n setInternalValue(option.value);\n }\n\n onChange?.(option.value);\n closeDropdown();\n };\n\n const handleMouseEnter = (\n event: React.MouseEvent<HTMLDivElement>,\n ) => {\n onMouseEnter?.(event);\n\n if (isHoverInteraction) {\n clearHoverCloseTimeout();\n setIsOpen(true);\n }\n };\n\n const handleMouseLeave = (\n event: React.MouseEvent<HTMLDivElement>,\n ) => {\n onMouseLeave?.(event);\n\n if (isHoverInteraction) {\n scheduleHoverClose();\n }\n };\n\n const handleFocus = (\n event: React.FocusEvent<HTMLDivElement>,\n ) => {\n onFocus?.(event);\n\n if (isHoverInteraction) {\n clearHoverCloseTimeout();\n setIsOpen(true);\n }\n };\n\n const handleBlur = (\n event: React.FocusEvent<HTMLDivElement>,\n ) => {\n onBlur?.(event);\n\n if (!isHoverInteraction) {\n return;\n }\n\n const nextTarget =\n event.relatedTarget;\n\n if (\n nextTarget instanceof Node &&\n event.currentTarget.contains(\n nextTarget,\n )\n ) {\n return;\n }\n\n closeDropdown();\n };\n\n useEffect(() => {\n return () => {\n clearHoverCloseTimeout();\n };\n }, []);\n\n useEffect(() => {\n if (!isOpen) {\n return;\n }\n\n const handleDocumentClick = (\n event: MouseEvent,\n ) => {\n const dropdown =\n dropdownRef.current;\n\n if (\n !dropdown ||\n dropdown.contains(\n event.target as Node,\n )\n ) {\n return;\n }\n\n closeDropdown();\n };\n\n const handleDocumentKeyDown = (\n event: KeyboardEvent,\n ) => {\n if (event.key === 'Escape') {\n closeDropdown();\n }\n };\n\n document.addEventListener(\n 'mousedown',\n handleDocumentClick,\n );\n\n document.addEventListener(\n 'keydown',\n handleDocumentKeyDown,\n );\n\n return () => {\n document.removeEventListener(\n 'mousedown',\n handleDocumentClick,\n );\n\n document.removeEventListener(\n 'keydown',\n handleDocumentKeyDown,\n );\n };\n }, [isOpen]);\n\n return (\n <div\n ref={dropdownRef}\n className={classes}\n onMouseEnter={\n handleMouseEnter\n }\n onMouseLeave={\n handleMouseLeave\n }\n onFocus={handleFocus}\n onBlur={handleBlur}\n {...props}\n >\n {resolvedTrigger}\n\n {isOpen && (\n <div\n className=\"dropdown__menu\"\n role=\"menu\"\n aria-label={label}\n >\n {options.map((option) => {\n const isSelected =\n option.value ===\n selectedValue;\n\n const itemClasses =\n classnames(\n 'dropdown__item',\n {\n 'dropdown__item--selected':\n isSelected,\n 'dropdown__item--disabled':\n option.disabled,\n },\n );\n\n return (\n <button\n key={option.value}\n className={\n itemClasses\n }\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={\n isSelected\n }\n aria-disabled={\n option.disabled\n }\n disabled={\n option.disabled\n }\n onClick={() =>\n handleOptionClick(\n option,\n )\n }\n >\n {option.label}\n </button>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n\nexport default Dropdown;\n"]}
|
|
@@ -3,6 +3,7 @@ import { useRef, useState, cloneElement, useEffect } from 'react';
|
|
|
3
3
|
import './dropdown.css';
|
|
4
4
|
import { classnames } from '../helpers/classnames';
|
|
5
5
|
|
|
6
|
+
const HOVER_CLOSE_DELAY_MS = 150;
|
|
6
7
|
const Dropdown = ({
|
|
7
8
|
options,
|
|
8
9
|
value,
|
|
@@ -20,6 +21,7 @@ const Dropdown = ({
|
|
|
20
21
|
...props
|
|
21
22
|
}) => {
|
|
22
23
|
const dropdownRef = useRef(null);
|
|
24
|
+
const hoverCloseTimeoutRef = useRef(null);
|
|
23
25
|
const [isOpen, setIsOpen] = useState(false);
|
|
24
26
|
const [
|
|
25
27
|
internalValue,
|
|
@@ -27,6 +29,26 @@ const Dropdown = ({
|
|
|
27
29
|
] = useState(defaultValue);
|
|
28
30
|
const selectedValue = value ?? internalValue;
|
|
29
31
|
const isHoverInteraction = interaction === "hover";
|
|
32
|
+
const clearHoverCloseTimeout = () => {
|
|
33
|
+
if (hoverCloseTimeoutRef.current === null) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
clearTimeout(
|
|
37
|
+
hoverCloseTimeoutRef.current
|
|
38
|
+
);
|
|
39
|
+
hoverCloseTimeoutRef.current = null;
|
|
40
|
+
};
|
|
41
|
+
const closeDropdown = () => {
|
|
42
|
+
clearHoverCloseTimeout();
|
|
43
|
+
setIsOpen(false);
|
|
44
|
+
};
|
|
45
|
+
const scheduleHoverClose = () => {
|
|
46
|
+
clearHoverCloseTimeout();
|
|
47
|
+
hoverCloseTimeoutRef.current = setTimeout(() => {
|
|
48
|
+
hoverCloseTimeoutRef.current = null;
|
|
49
|
+
setIsOpen(false);
|
|
50
|
+
}, HOVER_CLOSE_DELAY_MS);
|
|
51
|
+
};
|
|
30
52
|
const classes = classnames(
|
|
31
53
|
"dropdown",
|
|
32
54
|
className,
|
|
@@ -70,23 +92,25 @@ const Dropdown = ({
|
|
|
70
92
|
setInternalValue(option.value);
|
|
71
93
|
}
|
|
72
94
|
onChange?.(option.value);
|
|
73
|
-
|
|
95
|
+
closeDropdown();
|
|
74
96
|
};
|
|
75
97
|
const handleMouseEnter = (event) => {
|
|
76
98
|
onMouseEnter?.(event);
|
|
77
99
|
if (isHoverInteraction) {
|
|
100
|
+
clearHoverCloseTimeout();
|
|
78
101
|
setIsOpen(true);
|
|
79
102
|
}
|
|
80
103
|
};
|
|
81
104
|
const handleMouseLeave = (event) => {
|
|
82
105
|
onMouseLeave?.(event);
|
|
83
106
|
if (isHoverInteraction) {
|
|
84
|
-
|
|
107
|
+
scheduleHoverClose();
|
|
85
108
|
}
|
|
86
109
|
};
|
|
87
110
|
const handleFocus = (event) => {
|
|
88
111
|
onFocus?.(event);
|
|
89
112
|
if (isHoverInteraction) {
|
|
113
|
+
clearHoverCloseTimeout();
|
|
90
114
|
setIsOpen(true);
|
|
91
115
|
}
|
|
92
116
|
};
|
|
@@ -101,8 +125,13 @@ const Dropdown = ({
|
|
|
101
125
|
)) {
|
|
102
126
|
return;
|
|
103
127
|
}
|
|
104
|
-
|
|
128
|
+
closeDropdown();
|
|
105
129
|
};
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
return () => {
|
|
132
|
+
clearHoverCloseTimeout();
|
|
133
|
+
};
|
|
134
|
+
}, []);
|
|
106
135
|
useEffect(() => {
|
|
107
136
|
if (!isOpen) {
|
|
108
137
|
return;
|
|
@@ -114,11 +143,11 @@ const Dropdown = ({
|
|
|
114
143
|
)) {
|
|
115
144
|
return;
|
|
116
145
|
}
|
|
117
|
-
|
|
146
|
+
closeDropdown();
|
|
118
147
|
};
|
|
119
148
|
const handleDocumentKeyDown = (event) => {
|
|
120
149
|
if (event.key === "Escape") {
|
|
121
|
-
|
|
150
|
+
closeDropdown();
|
|
122
151
|
}
|
|
123
152
|
};
|
|
124
153
|
document.addEventListener(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/dropdown/dropdown.tsx"],"names":[],"mappings":"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","file":"dropdown.js","sourcesContent":["import React, {\n cloneElement,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nimport './dropdown.css';\nimport { classnames } from '../helpers/classnames';\n\nexport interface DropdownOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport type DropdownInteraction =\n | 'click'\n | 'hover';\n\ntype DropdownTriggerProps = {\n className?: string;\n onClick?: (\n event: React.MouseEvent,\n ) => void;\n 'aria-haspopup'?: string;\n 'aria-expanded'?: boolean;\n};\n\ntype DropdownTriggerElement =\n React.ReactElement<DropdownTriggerProps>;\n\nexport interface DropdownProps\n extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onChange'\n > {\n options: DropdownOption[];\n value?: string;\n defaultValue?: string;\n label?: string;\n align?:\n | 'top-start'\n | 'top-end'\n | 'bottom-start'\n | 'bottom-end';\n interaction?: DropdownInteraction;\n trigger: DropdownTriggerElement;\n onChange?: (\n value: string,\n ) => void;\n}\n\nconst Dropdown: React.FC<DropdownProps> = ({\n options,\n value,\n defaultValue,\n label = 'Dropdown',\n align = 'bottom-start',\n interaction = 'click',\n trigger,\n onChange,\n className = '',\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ...props\n}) => {\n const dropdownRef =\n useRef<HTMLDivElement>(null);\n\n const [isOpen, setIsOpen] =\n useState(false);\n\n const [\n internalValue,\n setInternalValue,\n ] = useState(defaultValue);\n\n const selectedValue =\n value ?? internalValue;\n\n const isHoverInteraction =\n interaction === 'hover';\n\n const classes = classnames(\n 'dropdown',\n className,\n {\n 'dropdown--open': isOpen,\n 'dropdown--top-start':\n align === 'top-start',\n 'dropdown--top-end':\n align === 'top-end',\n 'dropdown--bottom-start':\n align === 'bottom-start',\n 'dropdown--bottom-end':\n align === 'bottom-end',\n },\n );\n\n const triggerElement =\n trigger as DropdownTriggerElement;\n\n const triggerClasses = classnames(\n 'dropdown__trigger',\n triggerElement.props.className,\n );\n\n const resolvedTrigger = cloneElement(\n triggerElement,\n {\n className: triggerClasses,\n 'aria-haspopup': 'menu',\n 'aria-expanded': isOpen,\n\n onClick: (\n event: React.MouseEvent,\n ) => {\n triggerElement.props.onClick?.(\n event,\n );\n\n if (isHoverInteraction) {\n return;\n }\n\n setIsOpen(\n (currentValue) =>\n !currentValue,\n );\n },\n },\n );\n\n const handleOptionClick = (\n option: DropdownOption,\n ) => {\n if (option.disabled) {\n return;\n }\n\n if (value === undefined) {\n setInternalValue(option.value);\n }\n\n onChange?.(option.value);\n setIsOpen(false);\n };\n\n const handleMouseEnter = (\n event: React.MouseEvent<HTMLDivElement>,\n ) => {\n onMouseEnter?.(event);\n\n if (isHoverInteraction) {\n setIsOpen(true);\n }\n };\n\n const handleMouseLeave = (\n event: React.MouseEvent<HTMLDivElement>,\n ) => {\n onMouseLeave?.(event);\n\n if (isHoverInteraction) {\n setIsOpen(false);\n }\n };\n\n const handleFocus = (\n event: React.FocusEvent<HTMLDivElement>,\n ) => {\n onFocus?.(event);\n\n if (isHoverInteraction) {\n setIsOpen(true);\n }\n };\n\n const handleBlur = (\n event: React.FocusEvent<HTMLDivElement>,\n ) => {\n onBlur?.(event);\n\n if (!isHoverInteraction) {\n return;\n }\n\n const nextTarget =\n event.relatedTarget;\n\n if (\n nextTarget instanceof Node &&\n event.currentTarget.contains(\n nextTarget,\n )\n ) {\n return;\n }\n\n setIsOpen(false);\n };\n\n useEffect(() => {\n if (!isOpen) {\n return;\n }\n\n const handleDocumentClick = (\n event: MouseEvent,\n ) => {\n const dropdown =\n dropdownRef.current;\n\n if (\n !dropdown ||\n dropdown.contains(\n event.target as Node,\n )\n ) {\n return;\n }\n\n setIsOpen(false);\n };\n\n const handleDocumentKeyDown = (\n event: KeyboardEvent,\n ) => {\n if (event.key === 'Escape') {\n setIsOpen(false);\n }\n };\n\n document.addEventListener(\n 'mousedown',\n handleDocumentClick,\n );\n\n document.addEventListener(\n 'keydown',\n handleDocumentKeyDown,\n );\n\n return () => {\n document.removeEventListener(\n 'mousedown',\n handleDocumentClick,\n );\n\n document.removeEventListener(\n 'keydown',\n handleDocumentKeyDown,\n );\n };\n }, [isOpen]);\n\n return (\n <div\n ref={dropdownRef}\n className={classes}\n onMouseEnter={\n handleMouseEnter\n }\n onMouseLeave={\n handleMouseLeave\n }\n onFocus={handleFocus}\n onBlur={handleBlur}\n {...props}\n >\n {resolvedTrigger}\n\n {isOpen && (\n <div\n className=\"dropdown__menu\"\n role=\"menu\"\n aria-label={label}\n >\n {options.map((option) => {\n const isSelected =\n option.value ===\n selectedValue;\n\n const itemClasses =\n classnames(\n 'dropdown__item',\n {\n 'dropdown__item--selected':\n isSelected,\n 'dropdown__item--disabled':\n option.disabled,\n },\n );\n\n return (\n <button\n key={option.value}\n className={\n itemClasses\n }\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={\n isSelected\n }\n aria-disabled={\n option.disabled\n }\n disabled={\n option.disabled\n }\n onClick={() =>\n handleOptionClick(\n option,\n )\n }\n >\n {option.label}\n </button>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n\nexport default Dropdown;\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/dropdown/dropdown.tsx"],"names":[],"mappings":"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","file":"dropdown.js","sourcesContent":["import React, {\n cloneElement,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nimport './dropdown.css';\nimport { classnames } from '../helpers/classnames';\n\nconst HOVER_CLOSE_DELAY_MS = 150;\n\nexport interface DropdownOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport type DropdownInteraction =\n | 'click'\n | 'hover';\n\ntype DropdownTriggerProps = {\n className?: string;\n onClick?: (\n event: React.MouseEvent,\n ) => void;\n 'aria-haspopup'?: string;\n 'aria-expanded'?: boolean;\n};\n\ntype DropdownTriggerElement =\n React.ReactElement<DropdownTriggerProps>;\n\nexport interface DropdownProps\n extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onChange'\n > {\n options: DropdownOption[];\n value?: string;\n defaultValue?: string;\n label?: string;\n align?:\n | 'top-start'\n | 'top-end'\n | 'bottom-start'\n | 'bottom-end';\n interaction?: DropdownInteraction;\n trigger: DropdownTriggerElement;\n onChange?: (\n value: string,\n ) => void;\n}\n\nconst Dropdown: React.FC<DropdownProps> = ({\n options,\n value,\n defaultValue,\n label = 'Dropdown',\n align = 'bottom-start',\n interaction = 'click',\n trigger,\n onChange,\n className = '',\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ...props\n}) => {\n const dropdownRef =\n useRef<HTMLDivElement>(null);\n\n const hoverCloseTimeoutRef =\n useRef<ReturnType<\n typeof setTimeout\n > | null>(null);\n\n const [isOpen, setIsOpen] =\n useState(false);\n\n const [\n internalValue,\n setInternalValue,\n ] = useState(defaultValue);\n\n const selectedValue =\n value ?? internalValue;\n\n const isHoverInteraction =\n interaction === 'hover';\n\n const clearHoverCloseTimeout =\n () => {\n if (\n hoverCloseTimeoutRef.current ===\n null\n ) {\n return;\n }\n\n clearTimeout(\n hoverCloseTimeoutRef.current,\n );\n\n hoverCloseTimeoutRef.current =\n null;\n };\n\n const closeDropdown = () => {\n clearHoverCloseTimeout();\n setIsOpen(false);\n };\n\n const scheduleHoverClose = () => {\n clearHoverCloseTimeout();\n\n hoverCloseTimeoutRef.current =\n setTimeout(() => {\n hoverCloseTimeoutRef.current =\n null;\n setIsOpen(false);\n }, HOVER_CLOSE_DELAY_MS);\n };\n\n const classes = classnames(\n 'dropdown',\n className,\n {\n 'dropdown--open': isOpen,\n 'dropdown--top-start':\n align === 'top-start',\n 'dropdown--top-end':\n align === 'top-end',\n 'dropdown--bottom-start':\n align === 'bottom-start',\n 'dropdown--bottom-end':\n align === 'bottom-end',\n },\n );\n\n const triggerElement =\n trigger as DropdownTriggerElement;\n\n const triggerClasses = classnames(\n 'dropdown__trigger',\n triggerElement.props.className,\n );\n\n const resolvedTrigger = cloneElement(\n triggerElement,\n {\n className: triggerClasses,\n 'aria-haspopup': 'menu',\n 'aria-expanded': isOpen,\n\n onClick: (\n event: React.MouseEvent,\n ) => {\n triggerElement.props.onClick?.(\n event,\n );\n\n if (isHoverInteraction) {\n return;\n }\n\n setIsOpen(\n (currentValue) =>\n !currentValue,\n );\n },\n },\n );\n\n const handleOptionClick = (\n option: DropdownOption,\n ) => {\n if (option.disabled) {\n return;\n }\n\n if (value === undefined) {\n setInternalValue(option.value);\n }\n\n onChange?.(option.value);\n closeDropdown();\n };\n\n const handleMouseEnter = (\n event: React.MouseEvent<HTMLDivElement>,\n ) => {\n onMouseEnter?.(event);\n\n if (isHoverInteraction) {\n clearHoverCloseTimeout();\n setIsOpen(true);\n }\n };\n\n const handleMouseLeave = (\n event: React.MouseEvent<HTMLDivElement>,\n ) => {\n onMouseLeave?.(event);\n\n if (isHoverInteraction) {\n scheduleHoverClose();\n }\n };\n\n const handleFocus = (\n event: React.FocusEvent<HTMLDivElement>,\n ) => {\n onFocus?.(event);\n\n if (isHoverInteraction) {\n clearHoverCloseTimeout();\n setIsOpen(true);\n }\n };\n\n const handleBlur = (\n event: React.FocusEvent<HTMLDivElement>,\n ) => {\n onBlur?.(event);\n\n if (!isHoverInteraction) {\n return;\n }\n\n const nextTarget =\n event.relatedTarget;\n\n if (\n nextTarget instanceof Node &&\n event.currentTarget.contains(\n nextTarget,\n )\n ) {\n return;\n }\n\n closeDropdown();\n };\n\n useEffect(() => {\n return () => {\n clearHoverCloseTimeout();\n };\n }, []);\n\n useEffect(() => {\n if (!isOpen) {\n return;\n }\n\n const handleDocumentClick = (\n event: MouseEvent,\n ) => {\n const dropdown =\n dropdownRef.current;\n\n if (\n !dropdown ||\n dropdown.contains(\n event.target as Node,\n )\n ) {\n return;\n }\n\n closeDropdown();\n };\n\n const handleDocumentKeyDown = (\n event: KeyboardEvent,\n ) => {\n if (event.key === 'Escape') {\n closeDropdown();\n }\n };\n\n document.addEventListener(\n 'mousedown',\n handleDocumentClick,\n );\n\n document.addEventListener(\n 'keydown',\n handleDocumentKeyDown,\n );\n\n return () => {\n document.removeEventListener(\n 'mousedown',\n handleDocumentClick,\n );\n\n document.removeEventListener(\n 'keydown',\n handleDocumentKeyDown,\n );\n };\n }, [isOpen]);\n\n return (\n <div\n ref={dropdownRef}\n className={classes}\n onMouseEnter={\n handleMouseEnter\n }\n onMouseLeave={\n handleMouseLeave\n }\n onFocus={handleFocus}\n onBlur={handleBlur}\n {...props}\n >\n {resolvedTrigger}\n\n {isOpen && (\n <div\n className=\"dropdown__menu\"\n role=\"menu\"\n aria-label={label}\n >\n {options.map((option) => {\n const isSelected =\n option.value ===\n selectedValue;\n\n const itemClasses =\n classnames(\n 'dropdown__item',\n {\n 'dropdown__item--selected':\n isSelected,\n 'dropdown__item--disabled':\n option.disabled,\n },\n );\n\n return (\n <button\n key={option.value}\n className={\n itemClasses\n }\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={\n isSelected\n }\n aria-disabled={\n option.disabled\n }\n disabled={\n option.disabled\n }\n onClick={() =>\n handleOptionClick(\n option,\n )\n }\n >\n {option.label}\n </button>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n\nexport default Dropdown;\n"]}
|
package/components/index.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export type { CardGroupProps } from './card-group/card-group';
|
|
|
50
50
|
export { default as Breadcrumbs } from './breadcrumbs/breadcrumbs';
|
|
51
51
|
export type { BreadcrumbItem, BreadcrumbsPresentation, BreadcrumbsProps, } from './breadcrumbs/breadcrumbs';
|
|
52
52
|
export { default as Markdown } from './markdown/markdown';
|
|
53
|
-
export type { MarkdownEmbed, MarkdownProps, } from './markdown/markdown';
|
|
53
|
+
export type { MarkdownEmbed, MarkdownLinkProps, MarkdownProps, } from './markdown/markdown';
|
|
54
54
|
export { default as Discussion } from './discussion/discussion';
|
|
55
55
|
export type { DiscussionItem, DiscussionProps, } from './discussion/discussion';
|
|
56
56
|
export { default as CourseOutline } from './course-outline/course-outline';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default } from './markdown';
|
|
2
|
-
export type { MarkdownEmbed, MarkdownProps, } from './markdown';
|
|
2
|
+
export type { MarkdownEmbed, MarkdownLinkProps, MarkdownProps, } from './markdown';
|
|
@@ -199,7 +199,7 @@ const MarkdownHeading = ({
|
|
|
199
199
|
return /* @__PURE__ */ jsxRuntime.jsx("h6", { ...props, children });
|
|
200
200
|
}
|
|
201
201
|
};
|
|
202
|
-
function createDocumentComponents(headingLevelOffset, renderEmbed) {
|
|
202
|
+
function createDocumentComponents(headingLevelOffset, renderEmbed, renderLink) {
|
|
203
203
|
return {
|
|
204
204
|
h1: ({ node: _node, ...headingProps }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
205
205
|
MarkdownHeading,
|
|
@@ -249,6 +249,15 @@ function createDocumentComponents(headingLevelOffset, renderEmbed) {
|
|
|
249
249
|
offset: headingLevelOffset
|
|
250
250
|
}
|
|
251
251
|
),
|
|
252
|
+
a: ({
|
|
253
|
+
node: _node,
|
|
254
|
+
...linkProps
|
|
255
|
+
}) => {
|
|
256
|
+
if (renderLink) {
|
|
257
|
+
return renderLink(linkProps);
|
|
258
|
+
}
|
|
259
|
+
return /* @__PURE__ */ jsxRuntime.jsx("a", { ...linkProps });
|
|
260
|
+
},
|
|
252
261
|
div: ({
|
|
253
262
|
node: _node,
|
|
254
263
|
...divProps
|
|
@@ -277,6 +286,7 @@ const Markdown = ({
|
|
|
277
286
|
excerpt = false,
|
|
278
287
|
headingLevelOffset = 0,
|
|
279
288
|
renderEmbed,
|
|
289
|
+
renderLink,
|
|
280
290
|
className = "",
|
|
281
291
|
...props
|
|
282
292
|
}) => {
|
|
@@ -309,7 +319,8 @@ const Markdown = ({
|
|
|
309
319
|
const normalizedHeadingLevelOffset = normalizeHeadingLevelOffset(headingLevelOffset);
|
|
310
320
|
const documentComponents = createDocumentComponents(
|
|
311
321
|
normalizedHeadingLevelOffset,
|
|
312
|
-
renderEmbed
|
|
322
|
+
renderEmbed,
|
|
323
|
+
renderLink
|
|
313
324
|
);
|
|
314
325
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
315
326
|
"div",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/markdown/markdown.tsx"],"names":["jsx","classnames","ReactMarkdown","remarkGfm","remarkDirective"],"mappings":"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","file":"markdown.cjs","sourcesContent":["import React from 'react';\nimport ReactMarkdown, { type Components } from 'react-markdown';\nimport remarkDirective from 'remark-directive';\nimport remarkGfm from 'remark-gfm';\nimport type { Plugin } from 'unified';\n\nimport './markdown.css';\nimport { classnames } from '../helpers/classnames';\n\nexport interface MarkdownEmbed {\n src: string;\n label: string;\n}\n\nexport interface MarkdownProps\n extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'children' | 'dangerouslySetInnerHTML'\n > {\n content: string;\n excerpt?: boolean;\n headingLevelOffset?: number;\n renderEmbed?: (\n embed: MarkdownEmbed,\n ) => React.ReactNode;\n}\n\ninterface MarkdownHeadingProps\n extends React.HTMLAttributes<HTMLHeadingElement> {\n sourceLevel: number;\n offset: number;\n}\n\ntype MarkdownNodeData = {\n hName?: string;\n hProperties?: Record<string, unknown>;\n hChildren?: MarkdownNode[];\n [key: string]: unknown;\n};\n\ntype MarkdownNode = {\n type: string;\n name?: string;\n value?: string;\n alt?: string | null;\n attributes?: Record<\n string,\n string | null | undefined\n >;\n data?: MarkdownNodeData;\n children?: MarkdownNode[];\n [key: string]: unknown;\n};\n\ntype MarkdownRoot = {\n type: 'root';\n children: MarkdownNode[];\n};\n\ntype MarkdownEmbedDivProps =\n React.HTMLAttributes<HTMLDivElement> & {\n 'data-markdown-embed'?: string;\n 'data-markdown-embed-src'?: string;\n 'data-markdown-embed-label'?: string;\n };\n\nfunction normalizeHeadingLevelOffset(value: number | undefined) {\n if (value === undefined || !Number.isFinite(value)) {\n return 0;\n }\n\n return Math.max(0, Math.floor(value));\n}\n\nfunction getHeadingLevel(\n sourceLevel: number,\n offset: number,\n) {\n return Math.min(6, sourceLevel + offset);\n}\n\nfunction createTextNode(value: string): MarkdownNode {\n return {\n type: 'text',\n value,\n };\n}\n\nfunction appendNodesWithSeparator(\n target: MarkdownNode[],\n nodes: MarkdownNode[],\n) {\n if (nodes.length === 0) {\n return;\n }\n\n if (target.length > 0) {\n target.push(createTextNode(' '));\n }\n\n target.push(...nodes);\n}\n\nfunction getNodeText(node: MarkdownNode): string {\n if (typeof node.value === 'string') {\n return node.value;\n }\n\n return (node.children ?? [])\n .map(getNodeText)\n .join('');\n}\n\nfunction getEmbed(\n node: MarkdownNode,\n): MarkdownEmbed | null {\n if (\n node.type !== 'leafDirective' ||\n node.name !== 'embed'\n ) {\n return null;\n }\n\n const src = node.attributes?.src ?? '';\n const label = getNodeText(node);\n\n if (!src.trim() || !label.trim()) {\n return null;\n }\n\n return {\n src,\n label,\n };\n}\n\nfunction transformEmbedNodes(\n nodes: MarkdownNode[],\n) {\n for (const node of nodes) {\n if (\n node.type === 'leafDirective' &&\n node.name === 'embed'\n ) {\n const embed = getEmbed(node);\n\n node.data = {\n ...node.data,\n hName: 'div',\n hProperties: embed\n ? {\n 'data-markdown-embed': 'true',\n 'data-markdown-embed-src':\n embed.src,\n 'data-markdown-embed-label':\n embed.label,\n }\n : {\n 'data-markdown-embed':\n 'invalid',\n },\n hChildren: [],\n };\n\n continue;\n }\n\n if (node.children) {\n transformEmbedNodes(node.children);\n }\n }\n}\n\nconst remarkEmbed: Plugin = () => {\n return (tree) => {\n const root = tree as MarkdownRoot;\n\n transformEmbedNodes(root.children);\n };\n};\n\nfunction flattenInlineNode(\n node: MarkdownNode,\n): MarkdownNode[] {\n switch (node.type) {\n case 'html':\n return [];\n\n case 'break':\n return [\n createTextNode(' '),\n ];\n\n case 'image':\n case 'imageReference':\n return node.alt\n ? [\n createTextNode(node.alt),\n ]\n : [];\n\n default:\n if (!node.children) {\n return [\n node,\n ];\n }\n\n return [\n {\n ...node,\n children: node.children.flatMap(\n flattenInlineNode,\n ),\n },\n ];\n }\n}\n\nfunction flattenBlockNode(\n node: MarkdownNode,\n): MarkdownNode[] {\n switch (node.type) {\n case 'paragraph':\n case 'heading':\n return (node.children ?? []).flatMap(\n flattenInlineNode,\n );\n\n case 'tableCell':\n return (node.children ?? []).flatMap(\n flattenInlineNode,\n );\n\n case 'code':\n return node.value\n ? [\n {\n type: 'inlineCode',\n value: node.value,\n },\n ]\n : [];\n\n case 'leafDirective': {\n const embed = getEmbed(node);\n\n return embed\n ? [\n createTextNode(embed.label),\n ]\n : [];\n }\n\n case 'html':\n case 'definition':\n case 'footnoteDefinition':\n case 'thematicBreak':\n case 'yaml':\n return [];\n\n case 'blockquote':\n case 'list':\n case 'listItem':\n case 'table':\n case 'tableRow':\n return flattenBlockNodes(node.children ?? []);\n\n default:\n return node.children\n ? flattenBlockNodes(node.children)\n : [];\n }\n}\n\nfunction flattenBlockNodes(\n nodes: MarkdownNode[],\n): MarkdownNode[] {\n const result: MarkdownNode[] = [];\n\n for (const node of nodes) {\n appendNodesWithSeparator(\n result,\n flattenBlockNode(node),\n );\n }\n\n return result;\n}\n\nconst remarkExcerpt: Plugin = () => {\n return (tree) => {\n const root = tree as MarkdownRoot;\n const children = flattenBlockNodes(root.children);\n\n root.children = children.length > 0\n ? [\n {\n type: 'paragraph',\n children,\n },\n ]\n : [];\n };\n};\n\nconst MarkdownHeading: React.FC<MarkdownHeadingProps> = ({\n sourceLevel,\n offset,\n children,\n ...props\n}) => {\n const level = getHeadingLevel(sourceLevel, offset);\n\n switch (level) {\n case 1:\n return <h1 {...props}>{children}</h1>;\n\n case 2:\n return <h2 {...props}>{children}</h2>;\n\n case 3:\n return <h3 {...props}>{children}</h3>;\n\n case 4:\n return <h4 {...props}>{children}</h4>;\n\n case 5:\n return <h5 {...props}>{children}</h5>;\n\n default:\n return <h6 {...props}>{children}</h6>;\n }\n};\n\nfunction createDocumentComponents(\n headingLevelOffset: number,\n renderEmbed:\n | ((\n embed: MarkdownEmbed,\n ) => React.ReactNode)\n | undefined,\n): Components {\n return {\n h1: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={1}\n offset={headingLevelOffset}\n />\n ),\n\n h2: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={2}\n offset={headingLevelOffset}\n />\n ),\n\n h3: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={3}\n offset={headingLevelOffset}\n />\n ),\n\n h4: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={4}\n offset={headingLevelOffset}\n />\n ),\n\n h5: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={5}\n offset={headingLevelOffset}\n />\n ),\n\n h6: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={6}\n offset={headingLevelOffset}\n />\n ),\n\n div: ({\n node: _node,\n ...divProps\n }) => {\n const {\n 'data-markdown-embed':\n embedMarker,\n 'data-markdown-embed-src':\n embedSrc,\n 'data-markdown-embed-label':\n embedLabel,\n ...props\n } = divProps as MarkdownEmbedDivProps;\n\n if (embedMarker !== undefined) {\n if (\n embedMarker !== 'true' ||\n !embedSrc ||\n !embedLabel ||\n !renderEmbed\n ) {\n return null;\n }\n\n return renderEmbed({\n src: embedSrc,\n label: embedLabel,\n });\n }\n\n return <div {...props} />;\n },\n };\n}\n\nconst Markdown: React.FC<MarkdownProps> = ({\n content,\n excerpt = false,\n headingLevelOffset = 0,\n renderEmbed,\n className = '',\n ...props\n}) => {\n if (!content.trim()) {\n return null;\n }\n\n const classes = classnames('markdown', className, {\n 'markdown--excerpt': excerpt,\n });\n\n if (excerpt) {\n return (\n <div\n className={classes}\n {...props}\n >\n <ReactMarkdown\n remarkPlugins={[\n remarkGfm,\n remarkDirective,\n remarkExcerpt,\n ]}\n >\n {content}\n </ReactMarkdown>\n </div>\n );\n }\n\n const normalizedHeadingLevelOffset =\n normalizeHeadingLevelOffset(headingLevelOffset);\n\n const documentComponents = createDocumentComponents(\n normalizedHeadingLevelOffset,\n renderEmbed,\n );\n\n return (\n <div\n className={classes}\n {...props}\n >\n <ReactMarkdown\n remarkPlugins={[\n remarkGfm,\n remarkDirective,\n remarkEmbed,\n ]}\n components={documentComponents}\n >\n {content}\n </ReactMarkdown>\n </div>\n );\n};\n\nexport default Markdown;\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/markdown/markdown.tsx"],"names":["jsx","classnames","ReactMarkdown","remarkGfm","remarkDirective"],"mappings":"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","file":"markdown.cjs","sourcesContent":["import React from 'react';\nimport ReactMarkdown, { type Components } from 'react-markdown';\nimport remarkDirective from 'remark-directive';\nimport remarkGfm from 'remark-gfm';\nimport type { Plugin } from 'unified';\n\nimport './markdown.css';\nimport { classnames } from '../helpers/classnames';\n\nexport interface MarkdownEmbed {\n src: string;\n label: string;\n}\n\nexport type MarkdownLinkProps =\n React.AnchorHTMLAttributes<HTMLAnchorElement>;\n\nexport interface MarkdownProps\n extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'children' | 'dangerouslySetInnerHTML'\n > {\n content: string;\n excerpt?: boolean;\n headingLevelOffset?: number;\n renderEmbed?: (\n embed: MarkdownEmbed,\n ) => React.ReactNode;\n renderLink?: (\n props: MarkdownLinkProps,\n ) => React.ReactNode;\n}\n\ninterface MarkdownHeadingProps\n extends React.HTMLAttributes<HTMLHeadingElement> {\n sourceLevel: number;\n offset: number;\n}\n\ntype MarkdownNodeData = {\n hName?: string;\n hProperties?: Record<string, unknown>;\n hChildren?: MarkdownNode[];\n [key: string]: unknown;\n};\n\ntype MarkdownNode = {\n type: string;\n name?: string;\n value?: string;\n alt?: string | null;\n attributes?: Record<\n string,\n string | null | undefined\n >;\n data?: MarkdownNodeData;\n children?: MarkdownNode[];\n [key: string]: unknown;\n};\n\ntype MarkdownRoot = {\n type: 'root';\n children: MarkdownNode[];\n};\n\ntype MarkdownEmbedDivProps =\n React.HTMLAttributes<HTMLDivElement> & {\n 'data-markdown-embed'?: string;\n 'data-markdown-embed-src'?: string;\n 'data-markdown-embed-label'?: string;\n };\n\nfunction normalizeHeadingLevelOffset(value: number | undefined) {\n if (value === undefined || !Number.isFinite(value)) {\n return 0;\n }\n\n return Math.max(0, Math.floor(value));\n}\n\nfunction getHeadingLevel(\n sourceLevel: number,\n offset: number,\n) {\n return Math.min(6, sourceLevel + offset);\n}\n\nfunction createTextNode(value: string): MarkdownNode {\n return {\n type: 'text',\n value,\n };\n}\n\nfunction appendNodesWithSeparator(\n target: MarkdownNode[],\n nodes: MarkdownNode[],\n) {\n if (nodes.length === 0) {\n return;\n }\n\n if (target.length > 0) {\n target.push(createTextNode(' '));\n }\n\n target.push(...nodes);\n}\n\nfunction getNodeText(node: MarkdownNode): string {\n if (typeof node.value === 'string') {\n return node.value;\n }\n\n return (node.children ?? [])\n .map(getNodeText)\n .join('');\n}\n\nfunction getEmbed(\n node: MarkdownNode,\n): MarkdownEmbed | null {\n if (\n node.type !== 'leafDirective' ||\n node.name !== 'embed'\n ) {\n return null;\n }\n\n const src = node.attributes?.src ?? '';\n const label = getNodeText(node);\n\n if (!src.trim() || !label.trim()) {\n return null;\n }\n\n return {\n src,\n label,\n };\n}\n\nfunction transformEmbedNodes(\n nodes: MarkdownNode[],\n) {\n for (const node of nodes) {\n if (\n node.type === 'leafDirective' &&\n node.name === 'embed'\n ) {\n const embed = getEmbed(node);\n\n node.data = {\n ...node.data,\n hName: 'div',\n hProperties: embed\n ? {\n 'data-markdown-embed': 'true',\n 'data-markdown-embed-src':\n embed.src,\n 'data-markdown-embed-label':\n embed.label,\n }\n : {\n 'data-markdown-embed':\n 'invalid',\n },\n hChildren: [],\n };\n\n continue;\n }\n\n if (node.children) {\n transformEmbedNodes(node.children);\n }\n }\n}\n\nconst remarkEmbed: Plugin = () => {\n return (tree) => {\n const root = tree as MarkdownRoot;\n\n transformEmbedNodes(root.children);\n };\n};\n\nfunction flattenInlineNode(\n node: MarkdownNode,\n): MarkdownNode[] {\n switch (node.type) {\n case 'html':\n return [];\n\n case 'break':\n return [\n createTextNode(' '),\n ];\n\n case 'image':\n case 'imageReference':\n return node.alt\n ? [\n createTextNode(node.alt),\n ]\n : [];\n\n default:\n if (!node.children) {\n return [\n node,\n ];\n }\n\n return [\n {\n ...node,\n children: node.children.flatMap(\n flattenInlineNode,\n ),\n },\n ];\n }\n}\n\nfunction flattenBlockNode(\n node: MarkdownNode,\n): MarkdownNode[] {\n switch (node.type) {\n case 'paragraph':\n case 'heading':\n return (node.children ?? []).flatMap(\n flattenInlineNode,\n );\n\n case 'tableCell':\n return (node.children ?? []).flatMap(\n flattenInlineNode,\n );\n\n case 'code':\n return node.value\n ? [\n {\n type: 'inlineCode',\n value: node.value,\n },\n ]\n : [];\n\n case 'leafDirective': {\n const embed = getEmbed(node);\n\n return embed\n ? [\n createTextNode(embed.label),\n ]\n : [];\n }\n\n case 'html':\n case 'definition':\n case 'footnoteDefinition':\n case 'thematicBreak':\n case 'yaml':\n return [];\n\n case 'blockquote':\n case 'list':\n case 'listItem':\n case 'table':\n case 'tableRow':\n return flattenBlockNodes(node.children ?? []);\n\n default:\n return node.children\n ? flattenBlockNodes(node.children)\n : [];\n }\n}\n\nfunction flattenBlockNodes(\n nodes: MarkdownNode[],\n): MarkdownNode[] {\n const result: MarkdownNode[] = [];\n\n for (const node of nodes) {\n appendNodesWithSeparator(\n result,\n flattenBlockNode(node),\n );\n }\n\n return result;\n}\n\nconst remarkExcerpt: Plugin = () => {\n return (tree) => {\n const root = tree as MarkdownRoot;\n const children = flattenBlockNodes(root.children);\n\n root.children = children.length > 0\n ? [\n {\n type: 'paragraph',\n children,\n },\n ]\n : [];\n };\n};\n\nconst MarkdownHeading: React.FC<MarkdownHeadingProps> = ({\n sourceLevel,\n offset,\n children,\n ...props\n}) => {\n const level = getHeadingLevel(sourceLevel, offset);\n\n switch (level) {\n case 1:\n return <h1 {...props}>{children}</h1>;\n\n case 2:\n return <h2 {...props}>{children}</h2>;\n\n case 3:\n return <h3 {...props}>{children}</h3>;\n\n case 4:\n return <h4 {...props}>{children}</h4>;\n\n case 5:\n return <h5 {...props}>{children}</h5>;\n\n default:\n return <h6 {...props}>{children}</h6>;\n }\n};\n\nfunction createDocumentComponents(\n headingLevelOffset: number,\n renderEmbed:\n | ((\n embed: MarkdownEmbed,\n ) => React.ReactNode)\n | undefined,\n renderLink:\n | ((\n props: MarkdownLinkProps,\n ) => React.ReactNode)\n | undefined,\n): Components {\n return {\n h1: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={1}\n offset={headingLevelOffset}\n />\n ),\n\n h2: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={2}\n offset={headingLevelOffset}\n />\n ),\n\n h3: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={3}\n offset={headingLevelOffset}\n />\n ),\n\n h4: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={4}\n offset={headingLevelOffset}\n />\n ),\n\n h5: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={5}\n offset={headingLevelOffset}\n />\n ),\n\n h6: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={6}\n offset={headingLevelOffset}\n />\n ),\n\n a: ({\n node: _node,\n ...linkProps\n }) => {\n if (renderLink) {\n return renderLink(linkProps);\n }\n\n return <a {...linkProps} />;\n },\n\n div: ({\n node: _node,\n ...divProps\n }) => {\n const {\n 'data-markdown-embed':\n embedMarker,\n 'data-markdown-embed-src':\n embedSrc,\n 'data-markdown-embed-label':\n embedLabel,\n ...props\n } = divProps as MarkdownEmbedDivProps;\n\n if (embedMarker !== undefined) {\n if (\n embedMarker !== 'true' ||\n !embedSrc ||\n !embedLabel ||\n !renderEmbed\n ) {\n return null;\n }\n\n return renderEmbed({\n src: embedSrc,\n label: embedLabel,\n });\n }\n\n return <div {...props} />;\n },\n };\n}\n\nconst Markdown: React.FC<MarkdownProps> = ({\n content,\n excerpt = false,\n headingLevelOffset = 0,\n renderEmbed,\n renderLink,\n className = '',\n ...props\n}) => {\n if (!content.trim()) {\n return null;\n }\n\n const classes = classnames('markdown', className, {\n 'markdown--excerpt': excerpt,\n });\n\n if (excerpt) {\n return (\n <div\n className={classes}\n {...props}\n >\n <ReactMarkdown\n remarkPlugins={[\n remarkGfm,\n remarkDirective,\n remarkExcerpt,\n ]}\n >\n {content}\n </ReactMarkdown>\n </div>\n );\n }\n\n const normalizedHeadingLevelOffset =\n normalizeHeadingLevelOffset(headingLevelOffset);\n\n const documentComponents = createDocumentComponents(\n normalizedHeadingLevelOffset,\n renderEmbed,\n renderLink,\n );\n\n return (\n <div\n className={classes}\n {...props}\n >\n <ReactMarkdown\n remarkPlugins={[\n remarkGfm,\n remarkDirective,\n remarkEmbed,\n ]}\n components={documentComponents}\n >\n {content}\n </ReactMarkdown>\n </div>\n );\n};\n\nexport default Markdown;\n"]}
|
|
@@ -4,11 +4,13 @@ export interface MarkdownEmbed {
|
|
|
4
4
|
src: string;
|
|
5
5
|
label: string;
|
|
6
6
|
}
|
|
7
|
+
export type MarkdownLinkProps = React.AnchorHTMLAttributes<HTMLAnchorElement>;
|
|
7
8
|
export interface MarkdownProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children' | 'dangerouslySetInnerHTML'> {
|
|
8
9
|
content: string;
|
|
9
10
|
excerpt?: boolean;
|
|
10
11
|
headingLevelOffset?: number;
|
|
11
12
|
renderEmbed?: (embed: MarkdownEmbed) => React.ReactNode;
|
|
13
|
+
renderLink?: (props: MarkdownLinkProps) => React.ReactNode;
|
|
12
14
|
}
|
|
13
15
|
declare const Markdown: React.FC<MarkdownProps>;
|
|
14
16
|
export default Markdown;
|
|
@@ -191,7 +191,7 @@ const MarkdownHeading = ({
|
|
|
191
191
|
return /* @__PURE__ */ jsx("h6", { ...props, children });
|
|
192
192
|
}
|
|
193
193
|
};
|
|
194
|
-
function createDocumentComponents(headingLevelOffset, renderEmbed) {
|
|
194
|
+
function createDocumentComponents(headingLevelOffset, renderEmbed, renderLink) {
|
|
195
195
|
return {
|
|
196
196
|
h1: ({ node: _node, ...headingProps }) => /* @__PURE__ */ jsx(
|
|
197
197
|
MarkdownHeading,
|
|
@@ -241,6 +241,15 @@ function createDocumentComponents(headingLevelOffset, renderEmbed) {
|
|
|
241
241
|
offset: headingLevelOffset
|
|
242
242
|
}
|
|
243
243
|
),
|
|
244
|
+
a: ({
|
|
245
|
+
node: _node,
|
|
246
|
+
...linkProps
|
|
247
|
+
}) => {
|
|
248
|
+
if (renderLink) {
|
|
249
|
+
return renderLink(linkProps);
|
|
250
|
+
}
|
|
251
|
+
return /* @__PURE__ */ jsx("a", { ...linkProps });
|
|
252
|
+
},
|
|
244
253
|
div: ({
|
|
245
254
|
node: _node,
|
|
246
255
|
...divProps
|
|
@@ -269,6 +278,7 @@ const Markdown = ({
|
|
|
269
278
|
excerpt = false,
|
|
270
279
|
headingLevelOffset = 0,
|
|
271
280
|
renderEmbed,
|
|
281
|
+
renderLink,
|
|
272
282
|
className = "",
|
|
273
283
|
...props
|
|
274
284
|
}) => {
|
|
@@ -301,7 +311,8 @@ const Markdown = ({
|
|
|
301
311
|
const normalizedHeadingLevelOffset = normalizeHeadingLevelOffset(headingLevelOffset);
|
|
302
312
|
const documentComponents = createDocumentComponents(
|
|
303
313
|
normalizedHeadingLevelOffset,
|
|
304
|
-
renderEmbed
|
|
314
|
+
renderEmbed,
|
|
315
|
+
renderLink
|
|
305
316
|
);
|
|
306
317
|
return /* @__PURE__ */ jsx(
|
|
307
318
|
"div",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/markdown/markdown.tsx"],"names":[],"mappings":"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","file":"markdown.js","sourcesContent":["import React from 'react';\nimport ReactMarkdown, { type Components } from 'react-markdown';\nimport remarkDirective from 'remark-directive';\nimport remarkGfm from 'remark-gfm';\nimport type { Plugin } from 'unified';\n\nimport './markdown.css';\nimport { classnames } from '../helpers/classnames';\n\nexport interface MarkdownEmbed {\n src: string;\n label: string;\n}\n\nexport interface MarkdownProps\n extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'children' | 'dangerouslySetInnerHTML'\n > {\n content: string;\n excerpt?: boolean;\n headingLevelOffset?: number;\n renderEmbed?: (\n embed: MarkdownEmbed,\n ) => React.ReactNode;\n}\n\ninterface MarkdownHeadingProps\n extends React.HTMLAttributes<HTMLHeadingElement> {\n sourceLevel: number;\n offset: number;\n}\n\ntype MarkdownNodeData = {\n hName?: string;\n hProperties?: Record<string, unknown>;\n hChildren?: MarkdownNode[];\n [key: string]: unknown;\n};\n\ntype MarkdownNode = {\n type: string;\n name?: string;\n value?: string;\n alt?: string | null;\n attributes?: Record<\n string,\n string | null | undefined\n >;\n data?: MarkdownNodeData;\n children?: MarkdownNode[];\n [key: string]: unknown;\n};\n\ntype MarkdownRoot = {\n type: 'root';\n children: MarkdownNode[];\n};\n\ntype MarkdownEmbedDivProps =\n React.HTMLAttributes<HTMLDivElement> & {\n 'data-markdown-embed'?: string;\n 'data-markdown-embed-src'?: string;\n 'data-markdown-embed-label'?: string;\n };\n\nfunction normalizeHeadingLevelOffset(value: number | undefined) {\n if (value === undefined || !Number.isFinite(value)) {\n return 0;\n }\n\n return Math.max(0, Math.floor(value));\n}\n\nfunction getHeadingLevel(\n sourceLevel: number,\n offset: number,\n) {\n return Math.min(6, sourceLevel + offset);\n}\n\nfunction createTextNode(value: string): MarkdownNode {\n return {\n type: 'text',\n value,\n };\n}\n\nfunction appendNodesWithSeparator(\n target: MarkdownNode[],\n nodes: MarkdownNode[],\n) {\n if (nodes.length === 0) {\n return;\n }\n\n if (target.length > 0) {\n target.push(createTextNode(' '));\n }\n\n target.push(...nodes);\n}\n\nfunction getNodeText(node: MarkdownNode): string {\n if (typeof node.value === 'string') {\n return node.value;\n }\n\n return (node.children ?? [])\n .map(getNodeText)\n .join('');\n}\n\nfunction getEmbed(\n node: MarkdownNode,\n): MarkdownEmbed | null {\n if (\n node.type !== 'leafDirective' ||\n node.name !== 'embed'\n ) {\n return null;\n }\n\n const src = node.attributes?.src ?? '';\n const label = getNodeText(node);\n\n if (!src.trim() || !label.trim()) {\n return null;\n }\n\n return {\n src,\n label,\n };\n}\n\nfunction transformEmbedNodes(\n nodes: MarkdownNode[],\n) {\n for (const node of nodes) {\n if (\n node.type === 'leafDirective' &&\n node.name === 'embed'\n ) {\n const embed = getEmbed(node);\n\n node.data = {\n ...node.data,\n hName: 'div',\n hProperties: embed\n ? {\n 'data-markdown-embed': 'true',\n 'data-markdown-embed-src':\n embed.src,\n 'data-markdown-embed-label':\n embed.label,\n }\n : {\n 'data-markdown-embed':\n 'invalid',\n },\n hChildren: [],\n };\n\n continue;\n }\n\n if (node.children) {\n transformEmbedNodes(node.children);\n }\n }\n}\n\nconst remarkEmbed: Plugin = () => {\n return (tree) => {\n const root = tree as MarkdownRoot;\n\n transformEmbedNodes(root.children);\n };\n};\n\nfunction flattenInlineNode(\n node: MarkdownNode,\n): MarkdownNode[] {\n switch (node.type) {\n case 'html':\n return [];\n\n case 'break':\n return [\n createTextNode(' '),\n ];\n\n case 'image':\n case 'imageReference':\n return node.alt\n ? [\n createTextNode(node.alt),\n ]\n : [];\n\n default:\n if (!node.children) {\n return [\n node,\n ];\n }\n\n return [\n {\n ...node,\n children: node.children.flatMap(\n flattenInlineNode,\n ),\n },\n ];\n }\n}\n\nfunction flattenBlockNode(\n node: MarkdownNode,\n): MarkdownNode[] {\n switch (node.type) {\n case 'paragraph':\n case 'heading':\n return (node.children ?? []).flatMap(\n flattenInlineNode,\n );\n\n case 'tableCell':\n return (node.children ?? []).flatMap(\n flattenInlineNode,\n );\n\n case 'code':\n return node.value\n ? [\n {\n type: 'inlineCode',\n value: node.value,\n },\n ]\n : [];\n\n case 'leafDirective': {\n const embed = getEmbed(node);\n\n return embed\n ? [\n createTextNode(embed.label),\n ]\n : [];\n }\n\n case 'html':\n case 'definition':\n case 'footnoteDefinition':\n case 'thematicBreak':\n case 'yaml':\n return [];\n\n case 'blockquote':\n case 'list':\n case 'listItem':\n case 'table':\n case 'tableRow':\n return flattenBlockNodes(node.children ?? []);\n\n default:\n return node.children\n ? flattenBlockNodes(node.children)\n : [];\n }\n}\n\nfunction flattenBlockNodes(\n nodes: MarkdownNode[],\n): MarkdownNode[] {\n const result: MarkdownNode[] = [];\n\n for (const node of nodes) {\n appendNodesWithSeparator(\n result,\n flattenBlockNode(node),\n );\n }\n\n return result;\n}\n\nconst remarkExcerpt: Plugin = () => {\n return (tree) => {\n const root = tree as MarkdownRoot;\n const children = flattenBlockNodes(root.children);\n\n root.children = children.length > 0\n ? [\n {\n type: 'paragraph',\n children,\n },\n ]\n : [];\n };\n};\n\nconst MarkdownHeading: React.FC<MarkdownHeadingProps> = ({\n sourceLevel,\n offset,\n children,\n ...props\n}) => {\n const level = getHeadingLevel(sourceLevel, offset);\n\n switch (level) {\n case 1:\n return <h1 {...props}>{children}</h1>;\n\n case 2:\n return <h2 {...props}>{children}</h2>;\n\n case 3:\n return <h3 {...props}>{children}</h3>;\n\n case 4:\n return <h4 {...props}>{children}</h4>;\n\n case 5:\n return <h5 {...props}>{children}</h5>;\n\n default:\n return <h6 {...props}>{children}</h6>;\n }\n};\n\nfunction createDocumentComponents(\n headingLevelOffset: number,\n renderEmbed:\n | ((\n embed: MarkdownEmbed,\n ) => React.ReactNode)\n | undefined,\n): Components {\n return {\n h1: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={1}\n offset={headingLevelOffset}\n />\n ),\n\n h2: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={2}\n offset={headingLevelOffset}\n />\n ),\n\n h3: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={3}\n offset={headingLevelOffset}\n />\n ),\n\n h4: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={4}\n offset={headingLevelOffset}\n />\n ),\n\n h5: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={5}\n offset={headingLevelOffset}\n />\n ),\n\n h6: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={6}\n offset={headingLevelOffset}\n />\n ),\n\n div: ({\n node: _node,\n ...divProps\n }) => {\n const {\n 'data-markdown-embed':\n embedMarker,\n 'data-markdown-embed-src':\n embedSrc,\n 'data-markdown-embed-label':\n embedLabel,\n ...props\n } = divProps as MarkdownEmbedDivProps;\n\n if (embedMarker !== undefined) {\n if (\n embedMarker !== 'true' ||\n !embedSrc ||\n !embedLabel ||\n !renderEmbed\n ) {\n return null;\n }\n\n return renderEmbed({\n src: embedSrc,\n label: embedLabel,\n });\n }\n\n return <div {...props} />;\n },\n };\n}\n\nconst Markdown: React.FC<MarkdownProps> = ({\n content,\n excerpt = false,\n headingLevelOffset = 0,\n renderEmbed,\n className = '',\n ...props\n}) => {\n if (!content.trim()) {\n return null;\n }\n\n const classes = classnames('markdown', className, {\n 'markdown--excerpt': excerpt,\n });\n\n if (excerpt) {\n return (\n <div\n className={classes}\n {...props}\n >\n <ReactMarkdown\n remarkPlugins={[\n remarkGfm,\n remarkDirective,\n remarkExcerpt,\n ]}\n >\n {content}\n </ReactMarkdown>\n </div>\n );\n }\n\n const normalizedHeadingLevelOffset =\n normalizeHeadingLevelOffset(headingLevelOffset);\n\n const documentComponents = createDocumentComponents(\n normalizedHeadingLevelOffset,\n renderEmbed,\n );\n\n return (\n <div\n className={classes}\n {...props}\n >\n <ReactMarkdown\n remarkPlugins={[\n remarkGfm,\n remarkDirective,\n remarkEmbed,\n ]}\n components={documentComponents}\n >\n {content}\n </ReactMarkdown>\n </div>\n );\n};\n\nexport default Markdown;\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/markdown/markdown.tsx"],"names":[],"mappings":"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","file":"markdown.js","sourcesContent":["import React from 'react';\nimport ReactMarkdown, { type Components } from 'react-markdown';\nimport remarkDirective from 'remark-directive';\nimport remarkGfm from 'remark-gfm';\nimport type { Plugin } from 'unified';\n\nimport './markdown.css';\nimport { classnames } from '../helpers/classnames';\n\nexport interface MarkdownEmbed {\n src: string;\n label: string;\n}\n\nexport type MarkdownLinkProps =\n React.AnchorHTMLAttributes<HTMLAnchorElement>;\n\nexport interface MarkdownProps\n extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'children' | 'dangerouslySetInnerHTML'\n > {\n content: string;\n excerpt?: boolean;\n headingLevelOffset?: number;\n renderEmbed?: (\n embed: MarkdownEmbed,\n ) => React.ReactNode;\n renderLink?: (\n props: MarkdownLinkProps,\n ) => React.ReactNode;\n}\n\ninterface MarkdownHeadingProps\n extends React.HTMLAttributes<HTMLHeadingElement> {\n sourceLevel: number;\n offset: number;\n}\n\ntype MarkdownNodeData = {\n hName?: string;\n hProperties?: Record<string, unknown>;\n hChildren?: MarkdownNode[];\n [key: string]: unknown;\n};\n\ntype MarkdownNode = {\n type: string;\n name?: string;\n value?: string;\n alt?: string | null;\n attributes?: Record<\n string,\n string | null | undefined\n >;\n data?: MarkdownNodeData;\n children?: MarkdownNode[];\n [key: string]: unknown;\n};\n\ntype MarkdownRoot = {\n type: 'root';\n children: MarkdownNode[];\n};\n\ntype MarkdownEmbedDivProps =\n React.HTMLAttributes<HTMLDivElement> & {\n 'data-markdown-embed'?: string;\n 'data-markdown-embed-src'?: string;\n 'data-markdown-embed-label'?: string;\n };\n\nfunction normalizeHeadingLevelOffset(value: number | undefined) {\n if (value === undefined || !Number.isFinite(value)) {\n return 0;\n }\n\n return Math.max(0, Math.floor(value));\n}\n\nfunction getHeadingLevel(\n sourceLevel: number,\n offset: number,\n) {\n return Math.min(6, sourceLevel + offset);\n}\n\nfunction createTextNode(value: string): MarkdownNode {\n return {\n type: 'text',\n value,\n };\n}\n\nfunction appendNodesWithSeparator(\n target: MarkdownNode[],\n nodes: MarkdownNode[],\n) {\n if (nodes.length === 0) {\n return;\n }\n\n if (target.length > 0) {\n target.push(createTextNode(' '));\n }\n\n target.push(...nodes);\n}\n\nfunction getNodeText(node: MarkdownNode): string {\n if (typeof node.value === 'string') {\n return node.value;\n }\n\n return (node.children ?? [])\n .map(getNodeText)\n .join('');\n}\n\nfunction getEmbed(\n node: MarkdownNode,\n): MarkdownEmbed | null {\n if (\n node.type !== 'leafDirective' ||\n node.name !== 'embed'\n ) {\n return null;\n }\n\n const src = node.attributes?.src ?? '';\n const label = getNodeText(node);\n\n if (!src.trim() || !label.trim()) {\n return null;\n }\n\n return {\n src,\n label,\n };\n}\n\nfunction transformEmbedNodes(\n nodes: MarkdownNode[],\n) {\n for (const node of nodes) {\n if (\n node.type === 'leafDirective' &&\n node.name === 'embed'\n ) {\n const embed = getEmbed(node);\n\n node.data = {\n ...node.data,\n hName: 'div',\n hProperties: embed\n ? {\n 'data-markdown-embed': 'true',\n 'data-markdown-embed-src':\n embed.src,\n 'data-markdown-embed-label':\n embed.label,\n }\n : {\n 'data-markdown-embed':\n 'invalid',\n },\n hChildren: [],\n };\n\n continue;\n }\n\n if (node.children) {\n transformEmbedNodes(node.children);\n }\n }\n}\n\nconst remarkEmbed: Plugin = () => {\n return (tree) => {\n const root = tree as MarkdownRoot;\n\n transformEmbedNodes(root.children);\n };\n};\n\nfunction flattenInlineNode(\n node: MarkdownNode,\n): MarkdownNode[] {\n switch (node.type) {\n case 'html':\n return [];\n\n case 'break':\n return [\n createTextNode(' '),\n ];\n\n case 'image':\n case 'imageReference':\n return node.alt\n ? [\n createTextNode(node.alt),\n ]\n : [];\n\n default:\n if (!node.children) {\n return [\n node,\n ];\n }\n\n return [\n {\n ...node,\n children: node.children.flatMap(\n flattenInlineNode,\n ),\n },\n ];\n }\n}\n\nfunction flattenBlockNode(\n node: MarkdownNode,\n): MarkdownNode[] {\n switch (node.type) {\n case 'paragraph':\n case 'heading':\n return (node.children ?? []).flatMap(\n flattenInlineNode,\n );\n\n case 'tableCell':\n return (node.children ?? []).flatMap(\n flattenInlineNode,\n );\n\n case 'code':\n return node.value\n ? [\n {\n type: 'inlineCode',\n value: node.value,\n },\n ]\n : [];\n\n case 'leafDirective': {\n const embed = getEmbed(node);\n\n return embed\n ? [\n createTextNode(embed.label),\n ]\n : [];\n }\n\n case 'html':\n case 'definition':\n case 'footnoteDefinition':\n case 'thematicBreak':\n case 'yaml':\n return [];\n\n case 'blockquote':\n case 'list':\n case 'listItem':\n case 'table':\n case 'tableRow':\n return flattenBlockNodes(node.children ?? []);\n\n default:\n return node.children\n ? flattenBlockNodes(node.children)\n : [];\n }\n}\n\nfunction flattenBlockNodes(\n nodes: MarkdownNode[],\n): MarkdownNode[] {\n const result: MarkdownNode[] = [];\n\n for (const node of nodes) {\n appendNodesWithSeparator(\n result,\n flattenBlockNode(node),\n );\n }\n\n return result;\n}\n\nconst remarkExcerpt: Plugin = () => {\n return (tree) => {\n const root = tree as MarkdownRoot;\n const children = flattenBlockNodes(root.children);\n\n root.children = children.length > 0\n ? [\n {\n type: 'paragraph',\n children,\n },\n ]\n : [];\n };\n};\n\nconst MarkdownHeading: React.FC<MarkdownHeadingProps> = ({\n sourceLevel,\n offset,\n children,\n ...props\n}) => {\n const level = getHeadingLevel(sourceLevel, offset);\n\n switch (level) {\n case 1:\n return <h1 {...props}>{children}</h1>;\n\n case 2:\n return <h2 {...props}>{children}</h2>;\n\n case 3:\n return <h3 {...props}>{children}</h3>;\n\n case 4:\n return <h4 {...props}>{children}</h4>;\n\n case 5:\n return <h5 {...props}>{children}</h5>;\n\n default:\n return <h6 {...props}>{children}</h6>;\n }\n};\n\nfunction createDocumentComponents(\n headingLevelOffset: number,\n renderEmbed:\n | ((\n embed: MarkdownEmbed,\n ) => React.ReactNode)\n | undefined,\n renderLink:\n | ((\n props: MarkdownLinkProps,\n ) => React.ReactNode)\n | undefined,\n): Components {\n return {\n h1: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={1}\n offset={headingLevelOffset}\n />\n ),\n\n h2: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={2}\n offset={headingLevelOffset}\n />\n ),\n\n h3: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={3}\n offset={headingLevelOffset}\n />\n ),\n\n h4: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={4}\n offset={headingLevelOffset}\n />\n ),\n\n h5: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={5}\n offset={headingLevelOffset}\n />\n ),\n\n h6: ({ node: _node, ...headingProps }) => (\n <MarkdownHeading\n {...headingProps}\n sourceLevel={6}\n offset={headingLevelOffset}\n />\n ),\n\n a: ({\n node: _node,\n ...linkProps\n }) => {\n if (renderLink) {\n return renderLink(linkProps);\n }\n\n return <a {...linkProps} />;\n },\n\n div: ({\n node: _node,\n ...divProps\n }) => {\n const {\n 'data-markdown-embed':\n embedMarker,\n 'data-markdown-embed-src':\n embedSrc,\n 'data-markdown-embed-label':\n embedLabel,\n ...props\n } = divProps as MarkdownEmbedDivProps;\n\n if (embedMarker !== undefined) {\n if (\n embedMarker !== 'true' ||\n !embedSrc ||\n !embedLabel ||\n !renderEmbed\n ) {\n return null;\n }\n\n return renderEmbed({\n src: embedSrc,\n label: embedLabel,\n });\n }\n\n return <div {...props} />;\n },\n };\n}\n\nconst Markdown: React.FC<MarkdownProps> = ({\n content,\n excerpt = false,\n headingLevelOffset = 0,\n renderEmbed,\n renderLink,\n className = '',\n ...props\n}) => {\n if (!content.trim()) {\n return null;\n }\n\n const classes = classnames('markdown', className, {\n 'markdown--excerpt': excerpt,\n });\n\n if (excerpt) {\n return (\n <div\n className={classes}\n {...props}\n >\n <ReactMarkdown\n remarkPlugins={[\n remarkGfm,\n remarkDirective,\n remarkExcerpt,\n ]}\n >\n {content}\n </ReactMarkdown>\n </div>\n );\n }\n\n const normalizedHeadingLevelOffset =\n normalizeHeadingLevelOffset(headingLevelOffset);\n\n const documentComponents = createDocumentComponents(\n normalizedHeadingLevelOffset,\n renderEmbed,\n renderLink,\n );\n\n return (\n <div\n className={classes}\n {...props}\n >\n <ReactMarkdown\n remarkPlugins={[\n remarkGfm,\n remarkDirective,\n remarkEmbed,\n ]}\n components={documentComponents}\n >\n {content}\n </ReactMarkdown>\n </div>\n );\n};\n\nexport default Markdown;\n"]}
|
package/index.d.ts
CHANGED
|
@@ -58,7 +58,7 @@ export type { CardGroupProps } from './components/card-group';
|
|
|
58
58
|
export { default as Breadcrumbs } from './components/breadcrumbs';
|
|
59
59
|
export type { BreadcrumbItem, BreadcrumbsPresentation, BreadcrumbsProps, } from './components/breadcrumbs';
|
|
60
60
|
export { default as Markdown } from './components/markdown';
|
|
61
|
-
export type { MarkdownEmbed, MarkdownProps, } from './components/markdown';
|
|
61
|
+
export type { MarkdownEmbed, MarkdownLinkProps, MarkdownProps, } from './components/markdown';
|
|
62
62
|
export { default as Discussion } from './components/discussion';
|
|
63
63
|
export type { DiscussionItem, DiscussionProps, } from './components/discussion';
|
|
64
64
|
export { default as CourseOutline } from './components/course-outline';
|