npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@uwu/shelter-ui

v0.0.2

Published

Discord components remade for Shelter

Downloads

4

Readme

shelter UI

A set of UI components designed to look identical to Discord's, built entirely in solid.

The API signatures are not identical to Discord's react components.

These components are only expected to work inside Discord. For use outside of Discord, a solution may or may not be implemented.

ToC:

Accessibility

All efforts are taken to support users of accessibility technologies, including screen readers and keyboard navigation.

This entails a few blanket behaviours across shelter-ui:

  • All interactive elements (button, link, switch, etc.) have a focus ring
  • All interactive elements take an aria-label prop to override the default label
    • Labels are usually sensibly picked, eg the button and switchitem children prop
  • Relevant interactive elements (checkboxes, switches, textboxes) take an id prop to use with a <label>.

Utils

Not components, but UI utils used in shelter.

<ReactiveRoot>

ReactiveRoot creates a solid reactive root, to ensure that onCleanup works, and fix some reactivity bugs.

elem.append(<ReactiveRoot>{/* ... */}</ReactiveRoot>);

injectCss

injectCss, as the name says, injects CSS.

It returns a callback which, if passed another string, changes the injected CSS. If this callback is passed undefined, the CSS is removed from the page. At this point, you cannot pass another string into this callback to re-add some styles.

const modify = injectCss(" .myClass { color: red } ");
modify(" .myClass { color: green } "); // modifies the css
modify(); // removes the css
modify(" .myClass { color: blue } "); // no-op

cleanupCss is also exported, which removes all injected css.

genId

genId generates a random ID.

This is useful in cases where you need to link elements together by ID, but don't actually need a meaningful ID.

export default () => {
  const id = genId();

  return (
    <>
      <label for={id}>A useful input</label>
      <input id={id} />
    </>
  );
};

openModal

openModal opens the given component in a fullscreen popup modal.

It passes your component one prop, close, which is a function that closes the modal.

It returns a function that removes your modal.

const remove = openModal((p) => <button onclick={p}>Hi!</button>);
remove();

<ReactInSolidBridge />

Renders a React component in Solid.

// this function comes from discord
function Component({ className }) {
  return React.createElement("div", { className }, "yeah uh its a div");
}

<ReactInSolidBridge comp={Component} props={{ className: "reactelem" }} />;

SolidInReactBridge

Renders a Solid component in React. Using this is not recommended as you will need to provide your own React.

function Component(props) {
  return <div class={props.className}>yeah uh its a div</div>;
}

React.createElement(SolidInReactBridge, {
  comp: Component,
  props: { className: "solidelem" },
});

renderSolidInReact

Just a wrapper to React.createElement(SolidInReactBridge, {comp, props})

function Component(props) {
  return <div class={props.className}>yeah uh its a div</div>;
}

// Get a Discord component using React fiber

component.render = () => {
  return renderSolidInReact(Component, { className: "solidelem" });
};

<ErrorBoundary />

Safely catches any errors that occur, displays the error, and has a button to retry.

showToast

Shows a toast.

// all of these props are optional!
showToast({
  title: "title!",
  content: "a cool toast",
  onClick() {},
  class: "my-cool-toast",
  duration: 3000,
});

niceScrollbarsClass

A getter that gets a class to add to an element to give it a nice scrollbar.

<div class={`myclass myclass2 ${niceScrollbarsClass()}`} />

use:focusring

Adds a visible ring around your element when focused via keyboard (tab key), to aid with accessibility.

Optionally takes a border radius.

!!! Be careful - some tooling incorrectly tree shakes this if imported using ESM. You can use false && focusring to prevent the tree shaking (it will be minified away).

The focusring is included for you on interactive shelter-ui components, so if you are using those this is not necessary.

focusring must be in scope, either via an import from shelter-ui, or otherwise (e.g. const { focusring } = shelter.ui).

<button use:focusring>do a thing</button>
<input use:focusring={6} type="checkbox" />

use:tooltip

Shows a Discord-style tooltip when you hover over the element.

Same scope rules apply as focusring.

You can pass any JSX element type, including strings and elements.

If you pass undefined, it will do nothing.

<button use:tooltip="Delete"><DeleteIcon /></button>

Components

<Text>

Text just renders some text, using Discord's current text colour.

<Text>This is some text</Text>

<Header>

Header is, well, a header. It has three styles, chosen by the tag prop.

  • H1: A nice big header - like the ones at the top of user settings sections.
  • H2: A slightly smaller header, with allcaps text.
  • H3: A smaller again header - like "Gifts you purchased" in settings.
  • H4: Smaller again, allcaps text.
  • H5: Small, allcaps text, default - like "sms backup authentication" in settings.
<Header tag={HeaderTags.H1}>My cool page</Header>

<Divider />

Divider renders a grey horizontal divider line.

The mt and mb props control the top and bottom margin.

By default, there are no margins. When a string is provided, that is the margin value. When set true, 20px is used.

<Divider mt mb="30px" />

<Button>

Button is a, well, button, using Discord's styles. The props are as follows:

  • look: A button style from ButtonLooks - filled/inverted/outlined - defaults to filled
  • color: The colour of the button from ButtonColors - defaults to brand
  • size: The size of the button from ButtonSizes - defaults to small
  • grow: When set true, width: auto
  • disabled: When true, the button cannot be clicked and is greyed out
  • type: button, reset, submit - defaults to button
  • style: optionally an object of custom styles to apply
  • class: optionally some classes to apply
  • onClick: callback when button is clicked
  • onDoubleClick: callback when button is double-clicked
  • children: the button text
  • tooltip: the tooltip text, if any

<LinkButton>

A link (<a>) that fits with Discord's UI.

It will open the href in a new tab / in your system browser.

<Switch />

A toggle switch.

The id prop sets the id of the <input>.

checked, disabled, onChange should be pretty self-explanatory.

tooltip, if set, adds a tooltip.

const [switchState, setSwitchState] = createSignal(false);
<Switch checked={switchState} onChange={setSwitchState} />;

<SwitchItem>

An item with an option name, a switch, and optionally some extra info.

value sets the value of the switch, disabled and onChange work as you'd expect.

note, if passed, sets the extra info to be displayed under the title and switch.

Unless hideBorder is set, a <Divider /> is rendered under the component.

The child elements of the component is the title displayed next to the switch.

tooltip, if set, adds a tooltip.

<SwitchItem note="Does cool things" value={/*...*/}>A cool option</SwitchItem>

<Checkbox />

Like <Switch /> but its a checkbox.

<CheckboxItem>

Like <SwitchItem> but its a checkbox.

<ModalRoot>

The root component of a discord-styled modal.

Takes a size from ModalSizes and some child elements.

size defaults to ModalSizes.SMALL.

All provided child parts of the modal (header, body, footer) are optional.

<ModalRoot size={ModalSizes.SMALL}>
  <ModalHeader /* noClose */ close={closeFn}>My cool modal</ModalHeader>
  <ModalBody>Look mom! I'm on the shelter-ui modal!</ModalBody>
  <ModalFooter>Uhhhhh idk this is the footer ig, its a good place for buttons!</ModalFooter>
</ModalRoot>

<ModalHeader>

The header of a discord-styled modal.

Takes a prop, close, which is the function that closes the modal.

Also has an optional boolean prop noClose which hides the close button.

<ModalBody>

The body of a discord-styled modal.

Has nice discord scrollbars and plays well with the header and footer when overflowed.

<ModalFooter>

Takes no props.

The footer of a discord-styled-modal, good for buttons!

<TextBox />

A discord style textbox.

Takes value, placeholder, maxLength, and onInput.

All optional. onInput called every keystroke and passed the full current value.

<TextArea />

Like <TextBox /> but its a textarea.

Also takes width, height, resize-x, resize-y, and mono.

<Slider />

A discord-style slider. Takes value and returns in onInput as number,

Set min and max as needed.

step controls the stepping of the folder and steps (plural) controls the text ticks that show.

<Space />

A spacebar character that will never be collapsed. Useful in flexboxes etc.