Skip to content

Commit

Permalink
docs: Add matching details section in the docs (#72)
Browse files Browse the repository at this point in the history
## Description
Add matching details section in the docs

### Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [x] Documentation update (improves or adds clarity to existing
documentation)

### Checklist
- [x] I have performed a self-review of my code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have updated the documentation accordingly
- [x] My changes generate no new warnings
  • Loading branch information
jakmro authored Dec 19, 2024
1 parent 98cbe68 commit e120d2b
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 0 deletions.
7 changes: 7 additions & 0 deletions docs/src/theme/MDXComponents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Import the original mapper
import MDXComponents from '@theme-original/MDXComponents';

export default {
// Re-use the default mapping
...MDXComponents,
};
20 changes: 20 additions & 0 deletions docs/src/theme/MDXComponents/Details.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import DetailsStyling from '@site/src/theme/MDXComponents/DetailsStyling';

const MDXDetails = (props) => {
const items = React.Children.toArray(props.children);
// Split summary item from the rest to pass it as a separate prop to the
// Details theme component
const summary = items.find(
(item) => React.isValidElement(item) && item.props?.mdxType === 'summary'
);

const children = <>{items.filter((item) => item !== summary)}</>;
return (
<DetailsStyling {...props} summary={summary}>
{children}
</DetailsStyling>
);
};

export default MDXDetails;
101 changes: 101 additions & 0 deletions docs/src/theme/MDXComponents/DetailsStyling.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { useCollapsible, Collapsible } from '@docusaurus/theme-common';

import clsx from 'clsx';
import React from 'react';
import { useRef, useState } from 'react';

import styles from './styles.module.css';
import useIsBrowser from '@docusaurus/useIsBrowser';
import ThemedImage from '@theme/ThemedImage';
import useBaseUrl from '@docusaurus/useBaseUrl';

const DetailsStyling = ({ summary, children, ...props }): JSX.Element => {
const isBrowser = useIsBrowser();
const { collapsed, setCollapsed } = useCollapsible({
initialState: !props.open,
});

const arrowIcon = {
light: useBaseUrl('/img/Arrow.svg'),
dark: useBaseUrl('img/Arrow-dark.svg'),
};

const detailsRef = useRef<HTMLDetailsElement>(null);
const [open, setOpen] = useState(props.open);

// As we need to modify our own summary, we need to extract original content of summary
const extractedSummaryElement = summary.props.children;

return (
<details
{...props}
ref={detailsRef}
open={open}
data-collapsed={collapsed}
className={clsx(
styles.details,
isBrowser && styles.isBrowser,
props.className
)}
onMouseDown={(e) => {
const target = e.target as HTMLElement;
// Prevent a double-click to highlight summary text
if (isInSummary(target) && e.detail > 1) {
e.preventDefault();
}
}}
onClick={(e) => {
e.stopPropagation(); // For isolation of multiple nested details/summary
const target = e.target as HTMLElement;
const shouldToggle =
isInSummary(target) && hasParent(target, detailsRef.current!);
if (!shouldToggle) {
return;
}
e.preventDefault();
if (collapsed) {
setCollapsed(false);
setOpen(true);
} else {
setCollapsed(true);
// Don't do this, it breaks close animation!
// setOpen(false);
}
}}
>
<summary>
<ThemedImage sources={arrowIcon} className={styles.arrow} />

<p>{extractedSummaryElement}</p>
</summary>

<Collapsible
lazy={false}
collapsed={collapsed}
disableSSRStyle
onCollapseTransitionEnd={(newCollapsed) => {
setCollapsed(newCollapsed);
setOpen(!newCollapsed);
}}
>
<div className={styles.collapsibleContent}>{children}</div>
</Collapsible>
</details>
);
};

function isInSummary(node: HTMLElement | null): boolean {
if (!node) {
return false;
}
return node.tagName === 'SUMMARY' || isInSummary(node.parentElement);
}

function hasParent(node: HTMLElement | null, parent: HTMLElement): boolean {
if (!node) {
return false;
}
return node === parent || hasParent(node.parentElement, parent);
}

export default DetailsStyling;
50 changes: 50 additions & 0 deletions docs/src/theme/MDXComponents/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
.details {
background-color: var(--swm-details-foreground);
box-shadow: -8px 8px 0 var(--swm-details-background);

color: var(--swm-details-color);
}

.details a {
color: var(--swm-details-color);
}

.details > summary {
display: flex;
align-items: center;
cursor: pointer;
list-style: none;
padding: 1.5em 2em;
}

.details > summary > p {
margin: 0;
}

/* TODO: deprecation, need to remove this after Safari will support `::marker` */
.details > summary::-webkit-details-marker {
display: none;
}

.arrow {
height: 12px;
width: 12px;
margin-right: 1.5rem;
left: 0;

transition: var(--swm-expandable-transition);
}

.details[open]:not(.isBrowser) > summary > .arrow,
/* When JS works: we use the data-attribute for arrow animation */
.details[data-collapsed='false'].isBrowser > summary > .arrow {
transform: rotate(180deg);
}

.collapsibleContent {
padding: 0 2em 1.5em 2em;
}

.collapsibleContent > *:last-child {
margin-bottom: 0;
}
3 changes: 3 additions & 0 deletions docs/static/img/Arrow-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/static/img/Arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e120d2b

Please sign in to comment.