1.1.8 • Published 6 months ago

@hydroperx/metro v1.1.8

Weekly downloads
-
License
Apache-2.0
Repository
github
Last release
6 months ago

Metro components

A component and icon library for React using the Metro design.

Getting started

Installing required packages

Installation:

npm i @hydroperx/metro

Installing the required fonts

Installation:

npm i @fontsource/open-sans @fontsource/courier-prime

Import it into your entry point TypeScript as follows:

// Open Sans
import "@fontsource/open-sans/300.css";
import "@fontsource/open-sans/400.css";
import "@fontsource/open-sans/500.css";
import "@fontsource/open-sans/700.css";

// Courier Prime
import "@fontsource/courier-prime";

Documentation

Theming

By default, the light theme preset is used. Theme presets can be referenced in ThemePresets. You can provide a specific theme for a React section using:

import { ThemeProvider, ThemePresets } from "@hydroperx/metro";

// somewhere in React content
<ThemeProvider theme={ThemePresets.green}>
</ThemeProvider>

You can nest it as well.

Locale direction

Indicate whether a LTR layout or RTL layout is preferred through LocaleDirectionProvider:

import { LocaleDirectionProvider, LocaleDirection } from "@hydroperx/metro";

const direction: LocaleDirection = "ltr";

// somewhere in React content
<LocaleDirectionProvider value={direction}>
</LocaleDirectionProvider>

Primary colors

To opt in to using primary colors in certain components such as heading titles and checkboxes, use the PreferPrimaryContext context:

import { PreferPrimary } from "@hydroperx/metro";

// somewhere in React content
<PreferPrimary prefer={true}>
</PreferPrimary>

Icons

The Icon component is colored automatically at every state (hover, pressed) according to the computed cascading color property.

The CircleIconButton component represents a circle button consisting of an icon. For example, ArrowButton is an alias to <CircleIconButton icon="fullarrow" {...rest}/>, where the fullarrow icon fits into a square circle.

Built-in icons

Here is a list of built-in icons:

TypeDescription
bulletCorreponds to the bullet character.
checkedSomething is done or checked.
arrowA simple left arrow.
fullarrowA full left arrow, used mainly as a CircleIconButton (ArrowButton).
searchSearch or zoom.
clearClear.
gamesGame controller.
ieInternet Explorer
videoVideo.
storeGeneric store or marketplace icon.
settingsSettings.
mail(e-)mail.
userGeneric user avatar.
securitySecurity or safety.
calcCalculator.
cameraCamera.
bluetoothBluetooth.
newsNews.
bingBing search engine.
operaOpera browser.
chromeGoogle Chrome browser.
firefoxFirefox browser.
msedgeMicrosoft Edge browser.
lapisSouthwest-pointing lapis. Also used as an "edit" icon.
ideaAn upstanding lamp.
helpQuestion mark.
helpcircleQuestion mark inside a circle outline.
newA rectangle containing a plus sign.

Icon registry

Register custom icons with:

import { IconRegistry } from "@hydroperx/metro";

IconRegistry.register("iconX", { black: source, white: source });

These icons can then be used in for example Icon and CircleIconButton components.

To unregister a previously registered icon, use IconRegistry.unregister().

Retrieve a registered icon's source URI using IconRegistry.get().

Measuring points

The cascading font-size property in the <html> tag is used for determining the unit in points in the library. 1 point equals 0.25rem, where rem is the font-size pixels of the <html> tag.

If it is desired to grow or reduce all the user interface together, you may adjust the font-size of the <html> tag.

Input navigation

This library uses @hydroperx/inputaction for detecting pressed input such as keyboard arrows. You may customize the global Input.input input actions for supporting buttons other than arrow keys.

Important

Elements that may be navigated with arrow input contain the cascading class name specified by the BUTTON_NAVIGABLE constant. This is useful for applications like games for avoid duplicating focus handling by detecting that class name.

if (active_element.classList.contains(BUTTON_NAVIGABLE)) /* ignore */ return;

Context menu

Context menu (1)

Displaying context menus (typically these from a right click) is supported by the library.

import {
    useContextMenu, ContextMenu,
    ContextMenuItem, ContextMenuIcon, ContextMenuLabel, ContextMenuRight,
    ContextMenuSeparator, ContextMenuSubmenu, ContextMenuSubmenuList,
    ContextMenuSubIcon,
} from "@hydroperx/metro";

function MyComp() {
    const { id: contextMenuId, show: showContextMenu } = useContextMenu();

    function item1_onClick() {
        console.log("clicked item 1");
    }

    showContextMenu();

    return (
        <>
            <ContextMenu id={contextMenuId}>
                <ContextMenuItem click={item1_onClick}>
                    <ContextMenuIcon></ContextMenuIcon>
                    <ContextMenuLabel>Item 1</ContextMenuLabel>
                    <ContextMenuRight></ContextMenuRight>
                </ContextMenuItem>
                <ContextMenuSeparator/>
                <ContextMenuSubmenu>
                    <ContextMenuIcon></ContextMenuIcon>
                    <ContextMenuLabel>Submenu 1</ContextMenuLabel>
                    <ContextMenuRight><ContextMenuSubIcon/></ContextMenuRight>
                </ContextMenuSubmenu>
                <ContextMenuSubmenuList>
                    <ContextMenuItem disabled={true}>
                        <ContextMenuIcon></ContextMenuIcon>
                        <ContextMenuLabel>Item A</ContextMenuLabel>
                        <ContextMenuRight></ContextMenuRight>
                    </ContextMenuItem>
                </ContextMenuSubmenuList>
            </ContextMenu>
        </>
    );
}

If a context menu contains "checked" or "option" items, prepend a <ContextMenuCheck/> column to every item with the attribute state set to either none, checked or option.

(
    <ContextMenuItem>
        <ContextMenuCheck state="none"/>
        <ContextMenuIcon></ContextMenuIcon>
        <ContextMenuLabel>Item 1</ContextMenuLabel>
        <ContextMenuRight></ContextMenuRight>
    </ContextMenuItem>
    <ContextMenuItem>
        <ContextMenuCheck state="checked"/>
        <ContextMenuIcon></ContextMenuIcon>
        <ContextMenuLabel>Item 2</ContextMenuLabel>
        <ContextMenuRight></ContextMenuRight>
    </ContextMenuItem>
);

Tip: it is common, for instance, to disable opening a common context menu on buttons, as in the following code snippet:

function on_context_menu(e: MouseEvent): void {
    let p = e.target as HTMLElement | null;
    while (p) {
        if (!p.matches(":hover")) break;
        if (p instanceof HTMLButtonElement ||
            (p instanceof HTMLInputElement && p.type == "button")
        ) {
            return;
        }
        p = p.parentElement;
    }

    show_context_menu();
}

Tiles

Tiles (1)

Live tiles are supported, though partially since during dragging tiles the library will not shift tiles automatically, and "vertical" tile containers are not supported. In the future these cases might be implemented.

Here's an usage example:

const tiles_controller = new TilesController();

useEffect(() => {
    // Add tiles and their groups
    tiles_controller.addGroup({
        id: "group1",
        label: "Group 1",
    });
    
    tiles_controller.addTile({
        id: "tile1",
        group: "group1",
        x: 0,
        y: 0,
        size: "large",
        color: "#A8143A",
        icon: IconRegistry.get("video", "white"),
        label: "Video",
    });

    tiles_controller.addTile({
        id: "tile2",
        group: "group1",
        x: 0,
        y: 4,
        size: "wide",
        color: "#008000",
        icon: IconRegistry.get("games", "white"),
        label: "Games",
        livePages: [
            {
                html: `<div style='width:100%;height:100%;background: url("${xboxWallpaper}") no-repeat center 25%;background-size:cover'></div>`,
            },
        ],
    });

    tiles_controller.addGroup({
        id: "group2",
        label: "Group 2",
    });
    
    tiles_controller.addTile({
        id: "tile3",
        group: "group2",
        x: 0,
        y: 0,
        size: "small",
        color: "#2572E1",
        icon: IconRegistry.get("ie", "white"),
        label: "Internet Explorer",
    });
}, []);

return (
    <>
        <Tiles
            controller={tiles_controller}
            direction="horizontal"/>
    </>
);

Useful snippets

Disable "select all"

// Disable certain key behaviors
window.addEventListener("keydown", e => {
    // Ctrl+A
    if (e.key.toLowerCase() == "a" && e.ctrlKey && !e.shiftKey && !e.altKey) {
        if (!(
            document.activeElement instanceof HTMLInputElement ||
            document.activeElement instanceof HTMLTextAreaElement
        )) e.preventDefault();
    }
});

License

Apache 2.0

1.1.8

6 months ago

1.1.7

6 months ago

1.1.6

6 months ago

1.1.5

6 months ago

1.1.4

6 months ago

1.1.3

6 months ago

1.1.2

6 months ago

1.1.1

6 months ago

1.1.0

6 months ago