diff --git a/packages/code-studio/src/styleguide/GotoTopButton.tsx b/packages/code-studio/src/styleguide/GotoTopButton.tsx new file mode 100644 index 0000000000..361e908b86 --- /dev/null +++ b/packages/code-studio/src/styleguide/GotoTopButton.tsx @@ -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 ( + + ); +} + +export default GotoTopButton; diff --git a/packages/code-studio/src/styleguide/Icons.tsx b/packages/code-studio/src/styleguide/Icons.tsx index efd25ac9cf..ce2e3f0b32 100644 --- a/packages/code-studio/src/styleguide/Icons.tsx +++ b/packages/code-studio/src/styleguide/Icons.tsx @@ -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'; @@ -106,7 +107,9 @@ function Icons(): React.ReactElement { }); }} > - + + + diff --git a/packages/code-studio/src/styleguide/SamplesMenu.tsx b/packages/code-studio/src/styleguide/SamplesMenu.tsx new file mode 100644 index 0000000000..e348ca4222 --- /dev/null +++ b/packages/code-studio/src/styleguide/SamplesMenu.tsx @@ -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): JSX.Element { + return
; +} + +/** + * 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([]); + + 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 ( + + + + + + + + {({ category, items }) => ( +
+ {({ id, label }) => {label}} +
+ )} +
+
+ ); +} + +export default SamplesMenu; diff --git a/packages/code-studio/src/styleguide/SpectrumComponents.tsx b/packages/code-studio/src/styleguide/SpectrumComponents.tsx index 8a3b46cc3d..697fb87f9c 100644 --- a/packages/code-studio/src/styleguide/SpectrumComponents.tsx +++ b/packages/code-studio/src/styleguide/SpectrumComponents.tsx @@ -30,43 +30,42 @@ import { Well, } from '@adobe/react-spectrum'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { vsEmptyWindow, vsStarFull } from '@deephaven/icons'; +import { dh } from '@deephaven/icons'; +import { SPECTRUM_COMPONENT_SAMPLES_ID } from './constants'; export function SpectrumComponents(): JSX.Element { return ( - <> -

Spectrum Components

+
+

+ Spectrum Components +

Buttons

-

Forms

- -
- -

Icons

- +

Collections

+
-

Illustrated Message

+

Content

-

Contextual Help

- +

Forms

+
-

Table View

- +

Overlays

+

Wells

This is a well.
- +
); } @@ -76,11 +75,12 @@ function ButtonsSample(): JSX.Element { return ( - + @@ -93,11 +93,17 @@ function ButtonsSample(): JSX.Element { + + - + @@ -110,28 +116,51 @@ function ButtonsSample(): JSX.Element { + + Normal - Quiet + + Quiet + + Static Black + Static White Disabled + + + Normal + Quiet + + Emphasized + + Static Black + Static White + Disabled ); } function ContextualHelpSample(): JSX.Element { return ( - - Need help? - - - This is a helpful description of the thing you need help with. - - - + <> + Contextual Help + + Need help? + + + This is a helpful description of the thing you need help with. + + + + ); } @@ -146,64 +175,61 @@ function FormsSample(): JSX.Element { Three Checkbox + + Switch ); } -function IconSample(): JSX.Element { - return ( - - - - ); -} - function IllustratedMessageSample(): JSX.Element { return ( - + - No results - Try another search + Illustrated Message + This is the content of the message. ); } function TableViewSample(): JSX.Element { return ( - - - - Name - Age - - - City - State - - - - - John - 42 - San Francisco - CA - - - Jane - 38 - San Francisco - CA - - - Becky - 12 - San Francisco - CA - - - + <> + + + + + Name + Age + + + City + State + + + + + John + 42 + San Francisco + CA + + + Jane + 38 + San Francisco + CA + + + Becky + 12 + San Francisco + CA + + + + ); } diff --git a/packages/code-studio/src/styleguide/StyleGuide.scss b/packages/code-studio/src/styleguide/StyleGuide.scss index 58d270d8e2..3f77914e82 100644 --- a/packages/code-studio/src/styleguide/StyleGuide.scss +++ b/packages/code-studio/src/styleguide/StyleGuide.scss @@ -125,11 +125,6 @@ h5.sub-title { } } - .icon, - .icon .svg-inline--fa { - font-size: 36px; - } - .card label { max-width: 100%; font-size: 0.8rem; diff --git a/packages/code-studio/src/styleguide/StyleGuide.tsx b/packages/code-studio/src/styleguide/StyleGuide.tsx index e5ff92c47b..d4db65af1d 100644 --- a/packages/code-studio/src/styleguide/StyleGuide.tsx +++ b/packages/code-studio/src/styleguide/StyleGuide.tsx @@ -1,4 +1,6 @@ +/* eslint-disable react/jsx-props-no-spreading */ import React from 'react'; +import { Flex } from '@adobe/react-spectrum'; import { ContextMenuRoot } from '@deephaven/components'; import Alerts from './Alerts'; @@ -23,56 +25,63 @@ import DraggableLists from './DraggableLists'; import Navigations from './Navigations'; import ThemeColors from './ThemeColors'; import SpectrumComponents from './SpectrumComponents'; +import SamplesMenu, { SampleMenuCategory } from './SamplesMenu'; +import GotoTopButton from './GotoTopButton'; + +const stickyProps = { + position: 'sticky', + justifyContent: 'end', + zIndex: 1, +} as const; function StyleGuide(): React.ReactElement { return (
-
+

Deephaven UI Components

+ + + + + + + - + - - - - - - - - - - - - - - - - - +
); diff --git a/packages/code-studio/src/styleguide/Typography.tsx b/packages/code-studio/src/styleguide/Typography.tsx index 747e2ec79e..a9255d2f55 100644 --- a/packages/code-studio/src/styleguide/Typography.tsx +++ b/packages/code-studio/src/styleguide/Typography.tsx @@ -3,15 +3,23 @@ import React from 'react'; function Typography(): React.ReactElement { return (
-

Typograpy

+

Typography

-

h1. Unused

-

h2. Unused

-

h3. Unused

-

h4. Standard Heading

-
h5. Small Heading
-
h6. Unused
+

+ h1. Unused +

+

+ h2. Unused +

+

+ h3. Unused +

+

h4. Standard Heading

+
h5. Small Heading
+
+ h6. Unused +
diff --git a/packages/code-studio/src/styleguide/constants.ts b/packages/code-studio/src/styleguide/constants.ts new file mode 100644 index 0000000000..af5b342f11 --- /dev/null +++ b/packages/code-studio/src/styleguide/constants.ts @@ -0,0 +1,3 @@ +export const MENU_CATEGORY_DATA_ATTRIBUTE = 'data-menu-category'; +export const NO_MENU_DATA_ATTRIBUTE = 'data-no-menu'; +export const SPECTRUM_COMPONENT_SAMPLES_ID = 'spectrum-component-samples';