@ultimat3/ui 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CATALOG.md +848 -0
- package/README.md +87 -3
- package/package.json +8 -6
- package/src/catalog/build-catalog.ts +32 -0
- package/src/catalog/parse-component.ts +142 -0
- package/src/catalog/render-catalog.ts +116 -0
- package/src/components/AppShell.module.scss +110 -0
- package/src/components/AppShell.tsx +61 -0
- package/src/components/Button.module.scss +2 -46
- package/src/components/Dialog.module.scss +1 -4
- package/src/components/Drawer.module.scss +1 -3
- package/src/components/IconButton.module.scss +4 -34
- package/src/components/PageHeader.module.scss +47 -0
- package/src/components/PageHeader.tsx +46 -0
- package/src/components/Section.module.scss +38 -0
- package/src/components/Section.tsx +55 -0
- package/src/components/Tabs.module.scss +1 -4
- package/src/components/Toolbar.module.scss +26 -0
- package/src/components/Toolbar.tsx +48 -0
- package/src/components/app-shell-view.ts +35 -0
- package/src/components/heading-level.ts +25 -0
- package/src/errors.ts +30 -3
- package/src/i18n-keys.ts +4 -0
- package/src/index.ts +27 -1
- package/src/theme/brand.ts +155 -0
- package/src/tokens/_colors.scss +11 -7
- package/src/tokens/_mixins.scss +84 -0
- package/src/tokens/contrast.ts +63 -0
- package/src/tokens/tokens.ts +9 -7
|
@@ -10,10 +10,7 @@
|
|
|
10
10
|
color: t.role('fg-muted');
|
|
11
11
|
cursor: pointer;
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
opacity: 0.55;
|
|
15
|
-
cursor: not-allowed;
|
|
16
|
-
}
|
|
13
|
+
@include t.disabled;
|
|
17
14
|
}
|
|
18
15
|
|
|
19
16
|
.round {
|
|
@@ -44,39 +41,12 @@
|
|
|
44
41
|
font-size: t.text(lg);
|
|
45
42
|
}
|
|
46
43
|
|
|
47
|
-
.tone-
|
|
48
|
-
--tone: #{t.role('fg-strong')};
|
|
49
|
-
--tone-soft: #{t.role('bg-soft')};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.tone-accent {
|
|
53
|
-
--tone: #{t.role('accent')};
|
|
54
|
-
--tone-soft: #{t.role('accent', 0.12)};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.tone-success {
|
|
58
|
-
--tone: #{t.role('success')};
|
|
59
|
-
--tone-soft: #{t.role('success-soft')};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.tone-warning {
|
|
63
|
-
--tone: #{t.role('warning')};
|
|
64
|
-
--tone-soft: #{t.role('warning-soft')};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.tone-danger {
|
|
68
|
-
--tone: #{t.role('danger')};
|
|
69
|
-
--tone-soft: #{t.role('danger-soft')};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.tone-info {
|
|
73
|
-
--tone: #{t.role('info')};
|
|
74
|
-
--tone-soft: #{t.role('info-soft')};
|
|
75
|
-
}
|
|
44
|
+
@include t.tone-classes;
|
|
76
45
|
|
|
77
46
|
.variant-primary {
|
|
78
47
|
background: var(--tone);
|
|
79
|
-
|
|
48
|
+
// `--tone-fg` rather than a hardcoded accent-fg: a danger IconButton needs danger's on-colour.
|
|
49
|
+
color: var(--tone-fg);
|
|
80
50
|
}
|
|
81
51
|
|
|
82
52
|
.variant-secondary {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
@use '../tokens' as t;
|
|
2
|
+
|
|
3
|
+
.pageHeader {
|
|
4
|
+
@include t.column(t.space(3));
|
|
5
|
+
padding-block-end: t.space(5);
|
|
6
|
+
border-block-end: 1px solid t.role('line');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Wraps rather than shrinking: a long title must never squeeze the actions off the row.
|
|
10
|
+
.bar {
|
|
11
|
+
@include t.row(t.space(4), flex-start, space-between);
|
|
12
|
+
flex-wrap: wrap;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.titles {
|
|
16
|
+
@include t.row(t.space(3), flex-start);
|
|
17
|
+
min-inline-size: 0;
|
|
18
|
+
flex: 1 1 20rem;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.media {
|
|
22
|
+
display: inline-flex;
|
|
23
|
+
flex: none;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.text {
|
|
27
|
+
@include t.column(t.space(1));
|
|
28
|
+
min-inline-size: 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.title {
|
|
32
|
+
color: t.role('fg-strong');
|
|
33
|
+
font-size: t.text(2xl);
|
|
34
|
+
line-height: t.leading(tight);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.description {
|
|
38
|
+
color: t.role('fg-muted');
|
|
39
|
+
font-size: t.text(sm);
|
|
40
|
+
max-inline-size: 68ch;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.actions {
|
|
44
|
+
@include t.row(t.space(2), center, flex-end);
|
|
45
|
+
flex-wrap: wrap;
|
|
46
|
+
flex: none;
|
|
47
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// The top of a screen: breadcrumbs, the page's one heading, a description, and the actions that
|
|
2
|
+
// belong to the page rather than to a row in it. Hand-rolled, this is where the heading level,
|
|
3
|
+
// the landmark and the action alignment go wrong; here it is one component with one shape.
|
|
4
|
+
|
|
5
|
+
import type { JSX } from 'solid-js';
|
|
6
|
+
import { cx } from '../cx';
|
|
7
|
+
import { Breadcrumb, type BreadcrumbItem } from './Breadcrumb';
|
|
8
|
+
import { type HeadingLevel, headingTag } from './heading-level';
|
|
9
|
+
import styles from './PageHeader.module.scss';
|
|
10
|
+
|
|
11
|
+
export interface PageHeaderProps {
|
|
12
|
+
/** Already-translated page title. Rendered as the heading, once. */
|
|
13
|
+
title: string;
|
|
14
|
+
/** Already-translated supporting line under the title. */
|
|
15
|
+
description?: string | undefined;
|
|
16
|
+
/** Trailing controls — a Button, a Toolbar. Wraps under the title on narrow viewports. */
|
|
17
|
+
actions?: JSX.Element | undefined;
|
|
18
|
+
breadcrumbs?: readonly BreadcrumbItem[] | undefined;
|
|
19
|
+
/** Heading level. 1 by default: a screen has exactly one h1, and this is it. */
|
|
20
|
+
level?: HeadingLevel | undefined;
|
|
21
|
+
/** Rendered before the title — an avatar, an icon, a status dot. */
|
|
22
|
+
media?: JSX.Element | undefined;
|
|
23
|
+
class?: string | undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function PageHeader(props: PageHeaderProps): JSX.Element {
|
|
27
|
+
const Heading = headingTag(props.level ?? 1);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<header class={cx(styles['pageHeader'], props.class)}>
|
|
31
|
+
{props.breadcrumbs === undefined ? null : <Breadcrumb items={props.breadcrumbs} />}
|
|
32
|
+
<div class={styles['bar']}>
|
|
33
|
+
<div class={styles['titles']}>
|
|
34
|
+
{props.media === undefined ? null : <div class={styles['media']}>{props.media}</div>}
|
|
35
|
+
<div class={styles['text']}>
|
|
36
|
+
<Heading class={styles['title']}>{props.title}</Heading>
|
|
37
|
+
{props.description === undefined ? null : (
|
|
38
|
+
<p class={styles['description']}>{props.description}</p>
|
|
39
|
+
)}
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
{props.actions === undefined ? null : <div class={styles['actions']}>{props.actions}</div>}
|
|
43
|
+
</div>
|
|
44
|
+
</header>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
@use '../tokens' as t;
|
|
2
|
+
|
|
3
|
+
.section {
|
|
4
|
+
@include t.column(t.space(4));
|
|
5
|
+
min-inline-size: 0;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.head {
|
|
9
|
+
@include t.row(t.space(4), flex-end, space-between);
|
|
10
|
+
flex-wrap: wrap;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.text {
|
|
14
|
+
@include t.column(t.space(1));
|
|
15
|
+
min-inline-size: 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.title {
|
|
19
|
+
color: t.role('fg-strong');
|
|
20
|
+
font-size: t.text(lg);
|
|
21
|
+
line-height: t.leading(snug);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.description {
|
|
25
|
+
color: t.role('fg-muted');
|
|
26
|
+
font-size: t.text(sm);
|
|
27
|
+
max-inline-size: 68ch;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.actions {
|
|
31
|
+
@include t.row(t.space(2), center, flex-end);
|
|
32
|
+
flex-wrap: wrap;
|
|
33
|
+
flex: none;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.body {
|
|
37
|
+
min-inline-size: 0;
|
|
38
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// A titled block inside a page. Exists so a screen's second-level structure is a labelled
|
|
2
|
+
// landmark with a real heading — `aria-labelledby` wired to the heading it renders — instead of
|
|
3
|
+
// a <div> with bold text, which is what an unassisted layout always becomes.
|
|
4
|
+
|
|
5
|
+
import type { JSX } from 'solid-js';
|
|
6
|
+
import { useId } from '../a11y';
|
|
7
|
+
import { cx } from '../cx';
|
|
8
|
+
import { type HeadingLevel, headingTag } from './heading-level';
|
|
9
|
+
import styles from './Section.module.scss';
|
|
10
|
+
|
|
11
|
+
export interface SectionProps {
|
|
12
|
+
children: JSX.Element;
|
|
13
|
+
/** Already-translated section title. Omit for an unlabelled grouping. */
|
|
14
|
+
title?: string | undefined;
|
|
15
|
+
/** Already-translated supporting line under the title. */
|
|
16
|
+
description?: string | undefined;
|
|
17
|
+
/** Controls that act on this section only. */
|
|
18
|
+
actions?: JSX.Element | undefined;
|
|
19
|
+
/** Heading level. 2 by default — the level under a PageHeader's h1. */
|
|
20
|
+
level?: HeadingLevel | undefined;
|
|
21
|
+
as?: 'section' | 'article' | 'aside' | undefined;
|
|
22
|
+
class?: string | undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function Section(props: SectionProps): JSX.Element {
|
|
26
|
+
const Tag = props.as ?? 'section';
|
|
27
|
+
const Heading = headingTag(props.level ?? 2);
|
|
28
|
+
const titleId = useId('section');
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Tag
|
|
32
|
+
class={cx(styles['section'], props.class)}
|
|
33
|
+
aria-labelledby={props.title === undefined ? undefined : titleId}
|
|
34
|
+
>
|
|
35
|
+
{props.title === undefined && props.actions === undefined ? null : (
|
|
36
|
+
<div class={styles['head']}>
|
|
37
|
+
<div class={styles['text']}>
|
|
38
|
+
{props.title === undefined ? null : (
|
|
39
|
+
<Heading id={titleId} class={styles['title']}>
|
|
40
|
+
{props.title}
|
|
41
|
+
</Heading>
|
|
42
|
+
)}
|
|
43
|
+
{props.description === undefined ? null : (
|
|
44
|
+
<p class={styles['description']}>{props.description}</p>
|
|
45
|
+
)}
|
|
46
|
+
</div>
|
|
47
|
+
{props.actions === undefined ? null : (
|
|
48
|
+
<div class={styles['actions']}>{props.actions}</div>
|
|
49
|
+
)}
|
|
50
|
+
</div>
|
|
51
|
+
)}
|
|
52
|
+
<div class={styles['body']}>{props.children}</div>
|
|
53
|
+
</Tag>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
@use '../tokens' as t;
|
|
2
|
+
|
|
3
|
+
.toolbar {
|
|
4
|
+
@include t.row(t.space(3), center, space-between);
|
|
5
|
+
flex-wrap: wrap;
|
|
6
|
+
min-inline-size: 0;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.surface {
|
|
10
|
+
@include t.surface('surface', 'xs');
|
|
11
|
+
padding-block: t.space(3);
|
|
12
|
+
padding-inline: t.space(4);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.start {
|
|
16
|
+
@include t.row(t.space(2), center);
|
|
17
|
+
flex: 1 1 auto;
|
|
18
|
+
flex-wrap: wrap;
|
|
19
|
+
min-inline-size: 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.end {
|
|
23
|
+
@include t.row(t.space(2), center, flex-end);
|
|
24
|
+
flex: none;
|
|
25
|
+
flex-wrap: wrap;
|
|
26
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// The control strip above a table or a list: filters and search at the inline start, actions at
|
|
2
|
+
// the inline end. `role="toolbar"` with the same roving-tabindex helper Tabs uses, so arrow keys
|
|
3
|
+
// move between controls and the strip costs one Tab stop instead of a dozen.
|
|
4
|
+
|
|
5
|
+
import type { JSX } from 'solid-js';
|
|
6
|
+
import { createRovingTabindex, focusableWithin } from '../a11y';
|
|
7
|
+
import { cx } from '../cx';
|
|
8
|
+
import { useUi } from '../theme/context';
|
|
9
|
+
import styles from './Toolbar.module.scss';
|
|
10
|
+
|
|
11
|
+
export interface ToolbarProps {
|
|
12
|
+
/** Leading controls — search, filters, a Select. */
|
|
13
|
+
children: JSX.Element;
|
|
14
|
+
/** Trailing controls, pushed to the inline end. */
|
|
15
|
+
actions?: JSX.Element | undefined;
|
|
16
|
+
/** Already-translated accessible name. Required: an unnamed toolbar is an unnamed group. */
|
|
17
|
+
label: string;
|
|
18
|
+
/** Renders the strip on a raised, bordered surface. */
|
|
19
|
+
surface?: boolean | undefined;
|
|
20
|
+
class?: string | undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function Toolbar(props: ToolbarProps): JSX.Element {
|
|
24
|
+
const ui = useUi();
|
|
25
|
+
let strip: HTMLDivElement | undefined;
|
|
26
|
+
|
|
27
|
+
// Arrows follow the writing direction; `focusableWithin` skips anything hidden or disabled.
|
|
28
|
+
const onKeyDown = createRovingTabindex(
|
|
29
|
+
() => (strip === undefined ? [] : focusableWithin(strip)),
|
|
30
|
+
{ orientation: 'horizontal', dir: ui.dir, loop: false },
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div
|
|
35
|
+
ref={(el: HTMLDivElement) => {
|
|
36
|
+
strip = el;
|
|
37
|
+
}}
|
|
38
|
+
class={cx(styles['toolbar'], props.surface === true && styles['surface'], props.class)}
|
|
39
|
+
role="toolbar"
|
|
40
|
+
aria-label={props.label}
|
|
41
|
+
aria-orientation="horizontal"
|
|
42
|
+
onKeyDown={onKeyDown}
|
|
43
|
+
>
|
|
44
|
+
<div class={styles['start']}>{props.children}</div>
|
|
45
|
+
{props.actions === undefined ? null : <div class={styles['end']}>{props.actions}</div>}
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// AppShell's two rules, kept away from the markup: the id the skip link points at is derived
|
|
2
|
+
// from the same place the <main> gets it, and which landmarks the frame emits in which order.
|
|
3
|
+
// Both are silent failures when wrong — a skip link to nowhere, or two <main> elements.
|
|
4
|
+
|
|
5
|
+
export type ShellLandmark = 'banner' | 'navigation' | 'main' | 'contentinfo';
|
|
6
|
+
|
|
7
|
+
export interface ShellIds {
|
|
8
|
+
readonly mainId: string;
|
|
9
|
+
/** Always `#` + `mainId`. One derivation, so the link and the target cannot drift. */
|
|
10
|
+
readonly skipHref: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ShellSlots {
|
|
14
|
+
readonly header: boolean;
|
|
15
|
+
readonly sidebar: boolean;
|
|
16
|
+
readonly footer: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function shellIds(base: string): ShellIds {
|
|
20
|
+
const mainId = `${base}-main`;
|
|
21
|
+
return { mainId, skipHref: `#${mainId}` };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* DOM order, which is also screen-reader order: banner, then navigation, then main, then
|
|
26
|
+
* contentinfo. `main` is unconditional — a shell with no main region is not a page.
|
|
27
|
+
*/
|
|
28
|
+
export function shellLandmarks(slots: ShellSlots): readonly ShellLandmark[] {
|
|
29
|
+
const out: ShellLandmark[] = [];
|
|
30
|
+
if (slots.header) out.push('banner');
|
|
31
|
+
if (slots.sidebar) out.push('navigation');
|
|
32
|
+
out.push('main');
|
|
33
|
+
if (slots.footer) out.push('contentinfo');
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Which heading element a composite renders. A rule, not markup: PageHeader and Section both
|
|
2
|
+
// need it, and a page whose headings skip a level is a screen-reader outline with holes — so the
|
|
3
|
+
// level is a prop with one mapping, never a hardcoded <h2> inside a component.
|
|
4
|
+
|
|
5
|
+
import { invalidValueError } from '../errors';
|
|
6
|
+
|
|
7
|
+
export const HEADING_LEVELS = [1, 2, 3, 4, 5, 6] as const;
|
|
8
|
+
export type HeadingLevel = (typeof HEADING_LEVELS)[number];
|
|
9
|
+
export type HeadingTag = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
10
|
+
|
|
11
|
+
/** `2` → `'h2'`. Throws `X_UI_INVALID_VALUE` for anything off the scale. */
|
|
12
|
+
export function headingTag(level: HeadingLevel): HeadingTag {
|
|
13
|
+
if (!(HEADING_LEVELS as readonly number[]).includes(level)) {
|
|
14
|
+
throw invalidValueError('heading', level, 'a heading level from 1 to 6');
|
|
15
|
+
}
|
|
16
|
+
return `h${level}` as HeadingTag;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The level for content nested under a heading. Clamped at 6 rather than throwing: a deeply
|
|
21
|
+
* nested Section is a layout the app is allowed to build, and HTML has no `<h7>`.
|
|
22
|
+
*/
|
|
23
|
+
export function nextHeadingLevel(level: HeadingLevel): HeadingLevel {
|
|
24
|
+
return (level < 6 ? level + 1 : 6) as HeadingLevel;
|
|
25
|
+
}
|
package/src/errors.ts
CHANGED
|
@@ -35,12 +35,39 @@ export class UiError extends UltimateError {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
/**
|
|
39
|
-
|
|
38
|
+
/**
|
|
39
|
+
* A component asked for a token role that the SCSS source does not define. `source` is the
|
|
40
|
+
* partial that declares the scale — defaulted, because most kinds pluralise (`colors`), and
|
|
41
|
+
* named explicitly by the ones that do not (`radius`), so the `fix:` is always a real path.
|
|
42
|
+
*/
|
|
43
|
+
export function unknownTokenError(
|
|
44
|
+
kind: string,
|
|
45
|
+
name: string,
|
|
46
|
+
known: readonly string[],
|
|
47
|
+
source = `_${kind}s.scss`,
|
|
48
|
+
): UiError {
|
|
40
49
|
return new UiError({
|
|
41
50
|
code: UI_ERROR_CODES.tokenUnknown,
|
|
42
51
|
cause: `unknown ${kind} token "${name}"; known roles: ${known.join(', ')}`,
|
|
43
|
-
fix: `use one of the ${kind} roles above, or add "${name}" to packages/ui/src/tokens
|
|
52
|
+
fix: `use one of the ${kind} roles above, or add "${name}" to packages/ui/src/tokens/${source} and mirror it in tokens.ts`,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* A `defineTheme()` override held something that is not a token value. Strict on purpose: the
|
|
58
|
+
* result is interpolated into a `<style>` element, so a value carrying `;`, `}` or `</style>` is
|
|
59
|
+
* a CSS injection, not a typo.
|
|
60
|
+
*/
|
|
61
|
+
export function invalidBrandTokenError(
|
|
62
|
+
scope: string,
|
|
63
|
+
name: string,
|
|
64
|
+
value: unknown,
|
|
65
|
+
expected: string,
|
|
66
|
+
): UiError {
|
|
67
|
+
return new UiError({
|
|
68
|
+
code: UI_ERROR_CODES.invalidValue,
|
|
69
|
+
cause: `defineTheme() override ${scope}.${name} is ${JSON.stringify(value)}, which is not ${expected}`,
|
|
70
|
+
fix: `pass ${expected} to defineTheme(), e.g. defineTheme({ colors: { light: { accent: '31 110 178' } } })`,
|
|
44
71
|
});
|
|
45
72
|
}
|
|
46
73
|
|
package/src/i18n-keys.ts
CHANGED
|
@@ -17,6 +17,10 @@ export const UI_KEYS = {
|
|
|
17
17
|
sortAscending: 'ui.sort.ascending',
|
|
18
18
|
sortDescending: 'ui.sort.descending',
|
|
19
19
|
breadcrumb: 'ui.breadcrumb',
|
|
20
|
+
/** AppShell's skip link — the first Tab stop on every page. */
|
|
21
|
+
skip: 'ui.skip',
|
|
22
|
+
/** AppShell's sidebar landmark name. */
|
|
23
|
+
navigation: 'ui.navigation',
|
|
20
24
|
menu: 'ui.menu',
|
|
21
25
|
more: 'ui.more',
|
|
22
26
|
required: 'ui.required',
|
package/src/index.ts
CHANGED
|
@@ -21,8 +21,12 @@ export {
|
|
|
21
21
|
export type { AlertProps } from './components/Alert';
|
|
22
22
|
// --- components --------------------------------------------------------------
|
|
23
23
|
export { Alert } from './components/Alert';
|
|
24
|
+
export type { AppShellProps } from './components/AppShell';
|
|
25
|
+
export { AppShell } from './components/AppShell';
|
|
24
26
|
export type { AvatarProps } from './components/Avatar';
|
|
25
27
|
export { Avatar, initialsOf } from './components/Avatar';
|
|
28
|
+
export type { ShellIds, ShellLandmark, ShellSlots } from './components/app-shell-view';
|
|
29
|
+
export { shellIds, shellLandmarks } from './components/app-shell-view';
|
|
26
30
|
export type { BadgeProps } from './components/Badge';
|
|
27
31
|
export { Badge } from './components/Badge';
|
|
28
32
|
export type { BreadcrumbItem, BreadcrumbProps } from './components/Breadcrumb';
|
|
@@ -63,6 +67,8 @@ export type { FormProps } from './components/Form';
|
|
|
63
67
|
export { Form } from './components/Form';
|
|
64
68
|
export type { GridProps } from './components/Grid';
|
|
65
69
|
export { Grid } from './components/Grid';
|
|
70
|
+
export type { HeadingLevel, HeadingTag } from './components/heading-level';
|
|
71
|
+
export { HEADING_LEVELS, headingTag, nextHeadingLevel } from './components/heading-level';
|
|
66
72
|
export type { IconButtonProps } from './components/IconButton';
|
|
67
73
|
export { IconButton } from './components/IconButton';
|
|
68
74
|
export type { ImageProps } from './components/Image';
|
|
@@ -82,6 +88,8 @@ export { Money } from './components/Money';
|
|
|
82
88
|
export type { MoneyFormatter, MoneyInput, MoneyViewOptions } from './components/money-view';
|
|
83
89
|
// --- formatting cores (pure, renderer-free) ----------------------------------
|
|
84
90
|
export { moneyText, toMoney } from './components/money-view';
|
|
91
|
+
export type { PageHeaderProps } from './components/PageHeader';
|
|
92
|
+
export { PageHeader } from './components/PageHeader';
|
|
85
93
|
export type { PaginationProps } from './components/Pagination';
|
|
86
94
|
export { Pagination } from './components/Pagination';
|
|
87
95
|
export type { Placement, PopoverProps } from './components/Popover';
|
|
@@ -92,6 +100,8 @@ export type { RelativeTimeProps } from './components/RelativeTime';
|
|
|
92
100
|
export { RelativeTime } from './components/RelativeTime';
|
|
93
101
|
export type { RelativeTimeOptions } from './components/relative-time-view';
|
|
94
102
|
export { relativeTimeText } from './components/relative-time-view';
|
|
103
|
+
export type { SectionProps } from './components/Section';
|
|
104
|
+
export { Section } from './components/Section';
|
|
95
105
|
export type { SelectOption, SelectProps } from './components/Select';
|
|
96
106
|
export { Select } from './components/Select';
|
|
97
107
|
export type { SkeletonProps } from './components/Skeleton';
|
|
@@ -116,6 +126,8 @@ export type { ThemeChoice, ThemeToggleProps } from './components/ThemeToggle';
|
|
|
116
126
|
export { ThemeToggle } from './components/ThemeToggle';
|
|
117
127
|
export type { ToastProps, ToastRegionProps } from './components/Toast';
|
|
118
128
|
export { Toast, ToastRegion } from './components/Toast';
|
|
129
|
+
export type { ToolbarProps } from './components/Toolbar';
|
|
130
|
+
export { Toolbar } from './components/Toolbar';
|
|
119
131
|
export type { TooltipProps } from './components/Tooltip';
|
|
120
132
|
export { Tooltip } from './components/Tooltip';
|
|
121
133
|
export type { Align, ButtonVariant, Size, SpaceStep, Tone } from './components/variants';
|
|
@@ -126,6 +138,7 @@ export type { ClassValue } from './cx';
|
|
|
126
138
|
export { cx } from './cx';
|
|
127
139
|
export type { UiErrorCode } from './errors';
|
|
128
140
|
export {
|
|
141
|
+
invalidBrandTokenError,
|
|
129
142
|
invalidThemeError,
|
|
130
143
|
invalidValueError,
|
|
131
144
|
runtimeMissingError,
|
|
@@ -135,6 +148,8 @@ export {
|
|
|
135
148
|
} from './errors';
|
|
136
149
|
export type { UiKey } from './i18n-keys';
|
|
137
150
|
export { UI_KEYS } from './i18n-keys';
|
|
151
|
+
export type { Brand, BrandInput, FontSlot } from './theme/brand';
|
|
152
|
+
export { brandStyleTag, defineTheme, FONT_SLOTS } from './theme/brand';
|
|
138
153
|
export type { Direction, UiContextValue } from './theme/context';
|
|
139
154
|
export {
|
|
140
155
|
defaultUiContext,
|
|
@@ -177,7 +192,18 @@ export {
|
|
|
177
192
|
toggleTheme,
|
|
178
193
|
watchOsTheme,
|
|
179
194
|
} from './theme/theme';
|
|
180
|
-
export type {
|
|
195
|
+
export type { Channels } from './tokens/contrast';
|
|
196
|
+
export {
|
|
197
|
+
AA_LARGE,
|
|
198
|
+
AA_TEXT,
|
|
199
|
+
CHANNELS_PATTERN,
|
|
200
|
+
contrastRatio,
|
|
201
|
+
meetsContrast,
|
|
202
|
+
parseChannels,
|
|
203
|
+
relativeLuminance,
|
|
204
|
+
roleContrast,
|
|
205
|
+
} from './tokens/contrast';
|
|
206
|
+
export type { ColorRole, RadiusName, Theme } from './tokens/tokens';
|
|
181
207
|
// --- tokens ------------------------------------------------------------------
|
|
182
208
|
export {
|
|
183
209
|
assertColorRole,
|