Skip to content

Commit

Permalink
Added a menu for styleguide
Browse files Browse the repository at this point in the history
  • Loading branch information
bmingles committed Oct 18, 2023
1 parent 4b98fb6 commit 676fbe1
Show file tree
Hide file tree
Showing 8 changed files with 303 additions and 104 deletions.
31 changes: 31 additions & 0 deletions packages/code-studio/src/styleguide/GotoTopButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { useCallback } from 'react';
import { Button, Icon } from '@adobe/react-spectrum';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { vsChevronUp } from '@deephaven/icons';

/**
* Button that scrolls to top of page and clears location hash.
*/
export function GotoTopButton(): JSX.Element {
const gotoTop = useCallback(() => {
window.scrollTo({
top: 0,
behavior: 'smooth',
});

// Small delay to give scrolling a chance to move smoothly to top
setTimeout(() => {
window.location.hash = '';
}, 500);
}, []);

return (
<Button variant="accent" onPress={gotoTop}>
<Icon>
<FontAwesomeIcon icon={vsChevronUp} />
</Icon>
</Button>
);
}

export default GotoTopButton;
5 changes: 4 additions & 1 deletion packages/code-studio/src/styleguide/Icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
dhSquareFilled,
dhAddSmall,
} from '@deephaven/icons';
import { Icon } from '@adobe/react-spectrum';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button } from '@deephaven/components';
import PropTypes from 'prop-types';
Expand Down Expand Up @@ -106,7 +107,9 @@ function Icons(): React.ReactElement {
});
}}
>
<FontAwesomeIcon icon={icon} className="icon" />
<Icon size="L">
<FontAwesomeIcon icon={icon} />
</Icon>

<label title={prefixedName}>{prefixedName}</label>
</Button>
Expand Down
124 changes: 124 additions & 0 deletions packages/code-studio/src/styleguide/SamplesMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import React, { Key, useCallback, useEffect, useState } from 'react';
import {
ActionButton,
Icon,
Item,
Menu,
MenuTrigger,
Section,
} from '@adobe/react-spectrum';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { vsMenu } from '@deephaven/icons';
import {
MENU_CATEGORY_DATA_ATTRIBUTE,
NO_MENU_DATA_ATTRIBUTE,
SPECTRUM_COMPONENT_SAMPLES_ID,
} from './constants';

interface Link {
id: string;
label: string;
}
type LinkCategory = { category: string; items: Link[] };

/**
* Metadata only div that provides a MENU_CATEGORY_DATA_ATTRIBUTE defining a
* menu category. These will be queried by the SamplesMenu component to build
* up the menu sections.
*/
export function SampleMenuCategory({
'data-menu-category': dataMenuCategory,
}: Record<typeof MENU_CATEGORY_DATA_ATTRIBUTE, string>): JSX.Element {
return <div data-menu-category={dataMenuCategory} />;
}

/**
* Creates a menu from h2, h3 elements on the page and assigns them each an id
* for hash navigation purposes. If the current hash matches one of the ids, it
* will scroll to that element. This handles the initial page load scenario.
* Menu sections are identified by divs with MENU_CATEGORY_DATA_ATTRIBUTE
* attributes.
*/
export function SamplesMenu(): JSX.Element {
const [links, setLinks] = useState<LinkCategory[]>([]);

useEffect(() => {
let currentSection: LinkCategory = {
category: '',
items: [],
};
const categories: LinkCategory[] = [currentSection];

const spectrumComponentsSamples = document.querySelector(
`#${SPECTRUM_COMPONENT_SAMPLES_ID}`
);

document
.querySelectorAll(`h2,h3,[${MENU_CATEGORY_DATA_ATTRIBUTE}]`)
.forEach(el => {
if (el.textContent == null || el.hasAttribute(NO_MENU_DATA_ATTRIBUTE)) {
return;
}

if (el.hasAttribute(MENU_CATEGORY_DATA_ATTRIBUTE)) {
currentSection = {
category: el.getAttribute(MENU_CATEGORY_DATA_ATTRIBUTE) ?? '',
items: [],
};
categories.push(currentSection);

return;
}

const id = `${
spectrumComponentsSamples?.contains(el) === true ? 'spectrum-' : ''
}${el.textContent}`
.toLowerCase()
.replace(/\s/g, '-');

// eslint-disable-next-line no-param-reassign
el.id = id;

currentSection.items.push({ id, label: el.textContent });

if (`#${id}` === window.location.hash) {
el.scrollIntoView();
}
});

setLinks(categories);
}, []);

const onAction = useCallback((key: Key) => {
const id = String(key);
const el = document.getElementById(id);
el?.scrollIntoView({
behavior: 'smooth',
});

// Keep hash in sync for page reloads, but give some delay to allow smooth
// scrolling above
setTimeout(() => {
window.location.hash = id;
}, 500);
}, []);

return (
<MenuTrigger>
<ActionButton>
<Icon>
<FontAwesomeIcon icon={vsMenu} />
</Icon>
</ActionButton>
<Menu items={links} onAction={onAction}>
{({ category, items }) => (
<Section key={category} items={items} title={category}>
{({ id, label }) => <Item key={id}>{label}</Item>}
</Section>
)}
</Menu>
</MenuTrigger>
);
}

export default SamplesMenu;
Loading

0 comments on commit 676fbe1

Please sign in to comment.