diff --git a/.storybook-react/main.ts b/.storybook-react/main.ts new file mode 100644 index 00000000..3a88cd7c --- /dev/null +++ b/.storybook-react/main.ts @@ -0,0 +1,16 @@ +import type { StorybookConfig } from "@storybook/react-vite"; + +const config: StorybookConfig = { + stories: ["./stories/**/*.stories.ts"], + staticDirs: ["./static"], + addons: ["@storybook/addon-links", "@storybook/addon-essentials"], + framework: { + name: "@storybook/react-vite", + options: {}, + }, + core: { + disableTelemetry: true, + } +}; +export default config; + diff --git a/.storybook-react/preview.ts b/.storybook-react/preview.ts new file mode 100644 index 00000000..d7088557 --- /dev/null +++ b/.storybook-react/preview.ts @@ -0,0 +1,15 @@ +import type { Preview } from "@storybook/react"; + +const preview: Preview = { + parameters: { + actions: { argTypesRegex: "^on[A-Z].*" }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, + }, +}; + +export default preview; \ No newline at end of file diff --git a/.storybook-react/static/assets/icons/check2-circle.svg b/.storybook-react/static/assets/icons/check2-circle.svg new file mode 100644 index 00000000..13569621 --- /dev/null +++ b/.storybook-react/static/assets/icons/check2-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.storybook-react/static/assets/icons/exclamation-octagon.svg b/.storybook-react/static/assets/icons/exclamation-octagon.svg new file mode 100644 index 00000000..0bc23130 --- /dev/null +++ b/.storybook-react/static/assets/icons/exclamation-octagon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.storybook-react/stories/button.stories.ts b/.storybook-react/stories/button.stories.ts new file mode 100644 index 00000000..0b372edb --- /dev/null +++ b/.storybook-react/stories/button.stories.ts @@ -0,0 +1,66 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { Button } from '../../components/react'; + +const meta = { + title: 'Button', + component: Button, +} satisfies Meta; + +export default meta; + + +export const Template: StoryObj = { + args: { + innerHTML: "Click me", + variant: "primary", + disabled: false, + onClick: () => console.log("Click!") + }, + argTypes: { + variant: { + options: ["default", "primary", "success", "neutral", "warning", "danger", "text"], + control: { + type: "select", + }, + }, + size: { + options: ["small", "medium", "large"], + control: { + type: "select", + }, + }, + outline: { + options: [true, false], + control: { + type: "radio", + }, + }, + pill: { + options: [true, false], + control: { + type: "radio", + }, + }, + disabled: { + options: [true, false], + control: { + type: "radio", + }, + }, + caret: { + options: [true, false], + control: { + type: "radio", + }, + }, + loading: { + options: [true, false], + control: { + type: "radio", + }, + }, + }, +}; + + diff --git a/.storybook-react/stories/header.stories.ts b/.storybook-react/stories/header.stories.ts new file mode 100644 index 00000000..48ef84d3 --- /dev/null +++ b/.storybook-react/stories/header.stories.ts @@ -0,0 +1,79 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { hrefTo } from '@storybook/addon-links'; +import { Header } from '../../components/react/index'; + +const defaultLogo = ""; + +const twoTab = [ + { + label: 'GO TO HEADER PAGE', + clickEvent: async () => { + const url = await hrefTo('Button', 'Template'); + window.location.href = url; + } + }, + { + label: 'GO TO TRACKING PAGE', + clickEvent: async () => { + const url = await hrefTo('Tracking', 'Template'); + window.location.href = url; + } + } +]; + +const meta = { + title: 'Header', + component: Header, + args: { + size: "small", + title: "", + logo: defaultLogo, + drawer: true, + tabs: twoTab, + }, + argTypes: { + size: { + options: ["large", "medium", "small"], + control: { + type: "select", + }, + }, + title: { + options: ["", "Tasking Manager", "FMTM", "Drone Tasking Manager"], + control: { + type: "select", + }, + }, + logo: { + options: [defaultLogo, "data:image/png;base64,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"], + control: { + type: "select", + }, + }, + drawer: { + options: [true, false], + control: { + type: "radio", + }, + }, + tabs: { + options: { + '2 Tabs': twoTab, + }, + control: { + type: "select", + }, + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Template: Story = { + parameters: { + layout: 'fullscreen', + }, + args: {}, +}; + diff --git a/.storybook-react/stories/logo.ts b/.storybook-react/stories/logo.ts new file mode 100644 index 00000000..4739677f --- /dev/null +++ b/.storybook-react/stories/logo.ts @@ -0,0 +1,18 @@ + +import type { Meta, StoryObj } from '@storybook/react'; + +import { Logo } from '../../components/react/index'; + +const meta = { + title: 'Logo', + component: Logo, +} satisfies Meta; + +export default meta; + +export const Template: StoryObj = { + args: {}, + argTypes: {}, +}; + + diff --git a/.storybook-react/stories/toolbar.stories.ts b/.storybook-react/stories/toolbar.stories.ts new file mode 100644 index 00000000..8560729e --- /dev/null +++ b/.storybook-react/stories/toolbar.stories.ts @@ -0,0 +1,17 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { Toolbar } from '../../components/react/index'; + +const meta = { + title: 'Toolbar', + component: Toolbar, + argTypes: {}, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Template: Story = { + args: {}, +}; + diff --git a/.storybook/main.ts b/.storybook/main.ts index dbb9cbc6..8bfc6dd4 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -8,6 +8,8 @@ const config: StorybookConfig = { name: "@storybook/web-components-vite", options: {}, }, - docs: {}, + core: { + disableTelemetry: true, + } }; export default config; diff --git a/.storybook/stories/button.stories.ts b/.storybook/stories/button.stories.ts index 9fc217f5..0820a2fb 100644 --- a/.storybook/stories/button.stories.ts +++ b/.storybook/stories/button.stories.ts @@ -4,6 +4,10 @@ import type { Meta, StoryObj } from "@storybook/web-components"; import "../../components/index"; import { html } from "lit"; +import registerBundledIcons from "../../components/icons"; + +registerBundledIcons(); + const meta: Meta = { title: "Button", component: "hot-button", @@ -14,45 +18,100 @@ export const Template: StoryObj = { args: { text: "Click Me", variant: "default", + size: "medium", disabled: false, + style: "", + href: "", }, argTypes: { variant: { - options: ["default", "primary", "success", "neutral", "warning", "danger"], + options: ["default", "primary", "success", "neutral", "warning", "danger", "text"], + control: { + type: "select", + }, + }, + size: { + options: ["small", "medium", "large"], control: { type: "select", }, }, + outline: { + options: [true, false], + control: { + type: "radio", + }, + }, + pill: { + options: [true, false], + control: { + type: "radio", + }, + }, + prefix: { + options: [true, false], + control: { + type: "radio", + }, + }, + suffix: { + options: [true, false], + control: { + type: "radio", + }, + }, + icon: { + options: [true, false], + control: { + type: "radio", + }, + }, disabled: { options: [true, false], control: { type: "radio", }, }, - }, - parameters: { - showToast: () => { - const alert = document.getElementById("click-toast"); - if (alert) { - alert.show(); - } + circle: { + options: [true, false], + control: { + type: "radio", + }, + }, + caret: { + options: [true, false], + control: { + type: "radio", + }, + }, + loading: { + options: [true, false], + control: { + type: "radio", + }, }, }, render: (args, { parameters }) => { return html` -

Button

{parameters.showToast()}} + ?outline="${args.outline}" + size="${args.size}" + ?pill="${args.pill}" + ?circle="${args.circle}" + @click=${() => {console.log("click!")}} ?disabled=${args.disabled} - >${args.text} - -
-
- - - You clicked the button. - + style=${args.style} + ?prefix=${args.prefix} + ?caret=${args.caret} + ?loading=${args.loading} + href="${args.href}" + target="_blank" + > + ${args.prefix ? html`` : ""} + ${args.suffix ? html`` : ""} + ${args.icon ? html`` : args.text} + `; }, }; diff --git a/.storybook/stories/header.stories.ts b/.storybook/stories/header.stories.ts index bbe3a994..f61cce86 100644 --- a/.storybook/stories/header.stories.ts +++ b/.storybook/stories/header.stories.ts @@ -44,16 +44,20 @@ const fiveTab = Array.from({ length: 5 }, (_, index) => ({ })); export const Template: StoryObj = { + parameters: { + layout: 'fullscreen', + }, args: { - size: "large", + size: "small", title: "", logo: defaultLogo, drawer: true, tabs: twoTab, + borderBottom: true, }, argTypes: { size: { - options: ["large", "small"], + options: ["large", "medium", "small"], control: { type: "select", }, @@ -76,6 +80,12 @@ export const Template: StoryObj = { type: "radio", }, }, + borderBottom: { + options: [true, false], + control: { + type: "radio", + }, + }, tabs: { options: { '1 Tab': oneTab, @@ -94,22 +104,11 @@ export const Template: StoryObj = { - -

- Page Content -

-

- Lorem ipsum dolor sit amet, consectetur adipiscing - elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, - quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure - dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur - sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est - laborum. -

`; }, }; diff --git a/.storybook/stories/toolbar.stories.ts b/.storybook/stories/toolbar.stories.ts index 8d68c94c..b4224e01 100644 --- a/.storybook/stories/toolbar.stories.ts +++ b/.storybook/stories/toolbar.stories.ts @@ -22,9 +22,6 @@ export const Template: StoryObj = { }, render: (args) => { return html` -

Toolbar

-
-
{alert("Redo Clicked")}} diff --git a/.storybook/stories/tracking.stories.ts b/.storybook/stories/tracking.stories.ts index fb39d5dd..3ce49d6e 100644 --- a/.storybook/stories/tracking.stories.ts +++ b/.storybook/stories/tracking.stories.ts @@ -50,13 +50,6 @@ export const Template: StoryObj = { }, render: (args, { parameters }) => { return html` -

Matomo Tracking Banner

- The banner is disabled if a local storage key is set. -
- Click the buttons below to enable/disable and refresh the page. -
-
- { parameters.removeKeyLocalStorage(args.siteId) }}>Re-Enable Banner diff --git a/README.md b/README.md index 0d36744d..d8305a8c 100644 --- a/README.md +++ b/README.md @@ -1,207 +1,145 @@ -# HOT Shared UI - - -

- HOT -

-

- Shared Web Components with theming for use across HOTOSM tools. -

-

- - Publish - - - CDN Deploy - - - Publish Docs - - - Package version - - - Downloads - - - License - -

+# HOT UI -📖 **Documentation**: https://hotosm.github.io/ui/ +## Shared UI components with HOT theming -🖥️ **Source Code**: https://github.com/hotosm/ui +HOT themed UI components to reduce code duplication and make live easier for developers, available as [Web Components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) and witn first-class React support -🎯 **Roadmap / Tasks**: https://github.com/orgs/hotosm/projects/37/views/3 +The main goal of this project is not to re-invent the wheel, or add an extra burden of development and maintenance. ---- + - +[![Release](https://github.com/hotosm/ui/actions/workflows/publish.yml/badge.svg?event=release)](https://github.com/hotosm/ui/actions/workflows/publish.yml/) +[![CDN Deploy](https://github.com/hotosm/ui/actions/workflows/cdn_deploy.yml/badge.svg?branch=main)](https://github.com/hotosm/ui/actions/workflows/cdn_deploy.yml) +[![Docs](https://github.com/hotosm/ui/actions/workflows/docs.yml/badge.svg)](https://github.com/hotosm/ui/actions/workflows/docs.yml) +[![Package Version](https://img.shields.io/npm/v/%40hotosm/ui?color=334D058)](https://www.npmjs.com/package/@hotosm/ui) +[![Downloads](https://img.shields.io/npm/dm/%40hotosm%2Fui)](https://npmtrends.com/@hotosm/ui) +[![License](https://img.shields.io/github/license/hotosm/ui.svg)](https://github.com/hotosm/ui/blob/main/LICENSE.md) -Shared UI components with theming for use across HOTOSM tools, -to reduce code duplication. +📖 **Documentation**: https://hotosm.github.io/ui/ -The components are -[Web Components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components), -currently written in **Lit**, using TypeScript. +🖥️ **Source Code**: https://github.com/hotosm/ui -The main goal of this project is not to re-invent the wheel, or add an extra -burden of development and maintenance. +🎯 **Roadmap / Tasks**: https://github.com/orgs/hotosm/projects/37/views/3 -Primarily we want to have: +--- -- Low level components exported from the excellent Shoelace web component - library, simply re-exported with our default styling / CSS overrides. -- A few composite components (header, sidebar, etc): - - Consistent styling across most of our tools where it counts. - - Reduction in duplicated logic, such as user management, OAuth login, etc. -- Improved developer experience, reduced development time for new tools, while - maintaining consistency in look and feel of applications. +## Quick start -## Install +There are two options: NPM and Components Bundle. -There are two options for install: +### NPM -- **NPM**: appropriate for applications that have installable dependencies. -- **CDN**: appropriate for HTML / Markdown / HTMX. +Appropriate for applications that have installable dependencies -### Components Bundle +`npm install @hotosm/ui` -- This is the compiled JavaScript bundle generated from the TypeScript code. -- The components require no additional dependencies and are minified. - -#### Via NPM - -- Install package `@hotosm/ui` as a dependency in your `package.json`. -- Import the components. +Import the library in your project and use the components. ```html +``` -// Use the components in your templates - +```html + ``` -#### Via CDN +#### React + +```js + import { Logo } from '@hotosm/ui/components'; +``` + +```jsx + +``` + +### Components Bundle + +- This is the compiled JavaScript bundle generated from the TypeScript code. +- The components require no additional dependencies and are minified. + +Appropriate for HTML / Markdown / HTMX. ```html -// Import the styles (or create your own) -// Import the components - Ideally the version of Shoelace installed should match the version used in - > hotosm/ui. +Shoelace is an UI library that is exported directly from `@hotosm/ui`. -Example: +To access the low-level components, such as buttons, dropdowns, modals, etc, +simply import the component of the same name from the [Shoelace docs] +(): ```js -import '@hotosm/ui/components/header/header'; +import '@hotosm/ui/components/button/button'; +``` -// Then in your template - +```html +Can't Click Me ``` ### React -Versions of React below v19 require a specific 'wrapper' component to use the -web components. - -Use these instead of the standard `@hotosm/ui/components/xxx`: - -```bash -pnpm install @hotosm/ui +```js +import { Button } from '@hotosm/ui/components'; ``` -```js -import { Button } from '@hotosm/ui/react/Header' - -const HomePage = ({}) => { - return ( -
-
-
-
-
- ); -}; - -export default HomePage; +```html + ``` -> Note that while web components must always have a closing tag, this is not -> required for the React wrappers. +### Examples -## Using Extra Shoelace Components +You can found examples for HTML and also all common frameworks (React, Svelte, Vue) under `/examples`. -The UI library contains many composite components, such as headers, sidebars, -tracking banners, etc, and does not re-invent the wheel for low-level components. +### Development -Shoelace is an excellent UI library that is exported directly from `@hotosm/ui`. +HOT UI is developed in TypeScript, using Lit and @lit/react. -To access the low-level components, such as buttons, dropdowns, modals, etc, -simply import the component of the same name from the [Shoelace docs] -(): +Primarily we want to have: -```js -import '@hotosm/ui/components/button/button'; +- Low level components exported from the Shoelace web component + library, simply re-exported with our default styling / CSS overrides. +- A few composite components (header, sidebar, etc): + - Consistent styling across most of our tools where it counts. + - Reduction in duplicated logic, such as user management, OAuth login, etc. +- Improved developer experience, reduced development time for new tools, while + maintaining consistency in look and feel of applications. -// Then in your template -Can't Click Me -``` +### How to contribute -If you are using a bundler, you must bundle the (icon) assets yourself, -described in the Shoelace docs. +- Clone the project `git clone git@github.com:hotosm/ui.git` +- Install dependencies `pnpm install` +- Run the storybook `pnpm run dev` +- Write code! -### Example of bundling assets +There's also a React storybook that you can use for testing: -- To include the Shoelace assets in your final bundle (dist), you could add - the following to your `package.json`: +- Run the React storybook `pnpm run dev-react` -```json - "scripts": { - "clean-icons": "rm -rf public/assets/icons", - "get-icons": "cp -r node_modules/@shoelace-style/shoelace/dist/assets/icons public/", - "setup-dist": "pnpm run clean-icons && pnpm run get-icons", - } -``` +For **styling**, we have 2 important files under `/theme`: + +- `hot-sl.css` has a Shoelace theme, re-defining some variables +- `hot.css` has custom styles for eveything else, specially composited components + +### License -- Now the Shoelace assets will be bundled with your dist, under `/shoelace`. -- Following the example, also add `public/assets/icons` to your `.gitignore` file. +HOT UI is free and open source software! you may use any HOT UI project under the terms of the GNU Affero General Public License (AGPL) Version 3. diff --git a/components/header/header.styles.ts b/components/header/header.styles.ts new file mode 100644 index 00000000..e98945a0 --- /dev/null +++ b/components/header/header.styles.ts @@ -0,0 +1,103 @@ +import { css } from 'lit'; +import { cva } from "class-variance-authority"; + +export const headerVariants = cva( + // Defaults to include in all variants + ` + header + `, + { + variants: { + size: { + small: "header--size-small", + medium: "header--size-medium", + large: "header--size-large", + }, + borderBottom: { + true: "border-bottom", + } + }, + } +); + +export type sizes = "small" | "medium" | "large"; + +export const styles = css` + .header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 var(--sl-spacing-small); + } + + .header.border-bottom { + border-bottom: var(--hot-divider-border-bottom) solid var(--sl-color-neutral-50); + } + + .header--size-small { + height: var(--hot-height-small); + } + + .header--size-medium { + height: var(--hot-height-medium); + } + + .header--size-large { + height: var(--hot-height-large); + } + + .header--link { + text-decoration: none; + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--sl-spacing-small); + } + + .header--title { + color: var(--sl-color-neutral-950); + font-family: var(--sl-font-sans); + font-size: var(--sl-font-size-x-large); + } + + .header--tab-group { + flex-direction: column; + } + + .header--tab::part(base) { + font-size: var(--sl-font-size-medium); + font-weight: var(--sl-font-weight-normal); + color: var(--sl-color-neutral-950); + padding: var(--sl-spacing-small) var(--sl-spacing-small); + } + + .header--tab-group::part(base) { + --track-color: transparent; + --indicator-color: var(--sl-color-neutral-950); + } + + .header--nav { + justify-content: space-between; + justify-items: center; + gap: var(--sl-spacing-medium); + font-weight: var(--sl-font-weight-semibold); + } + + .header--nav-mobile { + } + + .header--person-circle { + font-size: var(--sl-font-size-x-large); + } + + .header--drawer { + font-size: var(--sl-font-size-x-large) + } + + .header--right-section { + } + + .header--logo-img { + } +} +` diff --git a/components/header/header.ts b/components/header/header.ts index 3eaa82c4..019d80b1 100644 --- a/components/header/header.ts +++ b/components/header/header.ts @@ -1,3 +1,4 @@ +import "../../theme/hot-sl.css"; import "../../theme/hot.css"; import "@shoelace-style/shoelace/dist/components/icon-button/icon-button.js"; @@ -6,8 +7,9 @@ import "@shoelace-style/shoelace/dist/components/tab-group/tab-group.js"; import "@shoelace-style/shoelace/dist/components/tab/tab.js"; import { LitElement, html } from "lit"; -import { property, state } from "lit/decorators.js"; -import { headerVariants, type sizes, styles} from "./styles"; +import { property } from "lit/decorators.js"; +import { headerVariants, type sizes, styles } from './header.styles.js'; +import type { CSSResultGroup } from 'lit'; import registerBundledIcons from "../../components/icons"; @@ -20,26 +22,36 @@ interface MenuItem { export class Header extends LitElement { - @property() name = "hot-header"; + static styles: CSSResultGroup = [styles]; + + name = "hot-header"; /** Use a text-based title in the header. */ - @property({ type: String }) title: string = ""; + @property({ type: String }) + accessor title: string = ""; /** Display a logo on the left of the header. */ - @property({ type: String }) logo: string | URL = ""; + @property({ type: String }) + accessor logo: string | URL = ""; /** Add a drawer icon with a click event to e.g. open a sidebar. */ - @property({ type: Boolean }) drawer: boolean = true; + @property({ type: Boolean }) + accessor drawer: boolean = true; /** Array of menu items to include as navigation tabs. */ - @property({ type: Array }) tabs: MenuItem[] = []; + @property({ type: Array }) + accessor tabs: MenuItem[] = []; /** Size of toolbar vertically. */ - @property({ type: String }) size: sizes = "large"; - - @state() private selectedTab: number = 0; + @property({ type: String }) + accessor size: sizes = "small"; - static styles = styles; + /** Border bottom. */ + @property({ type: Boolean }) + accessor borderBottom: boolean = true; + + @property() + accessor selectedTab: number = 0; protected render() { const logoSrc = @@ -50,26 +62,29 @@ export class Header extends LitElement { : ""; return html` -
-
${logoSrc.length > 0 ? html` ` : html` - `} ${this.title.length > 0 ? html` -

+

${this.title}

` @@ -78,12 +93,13 @@ export class Header extends LitElement { ${/* Navigation bar for desktop, hide on mobile */ ""}