@ahrowe/ui 0.50.0 → 0.52.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/CHANGELOG.md +24 -0
- package/README.md +2 -1
- package/dist/cjs-types/common/actionIcon/actionIcon.types.d.ts +10 -6
- package/dist/cjs-types/common/ahroweProvider/ahroweProvider.d.ts +1 -1
- package/dist/cjs-types/common/button/button.types.d.ts +25 -8
- package/dist/cjs-types/common/chatMessageList/chatMessageList.types.d.ts +1 -1
- package/dist/cjs-types/common/configProvider/configProvider.d.ts +2 -1
- package/dist/cjs-types/common/configProvider/configProvider.types.d.ts +8 -0
- package/dist/cjs-types/common/copyButton/copyButton.types.d.ts +4 -1
- package/dist/cjs-types/common/dropdown/dropdown.types.d.ts +3 -2
- package/dist/cjs-types/common/infiniteBlock/useInfiniteScroll.d.ts +1 -1
- package/dist/cjs-types/common/listItem/index.d.ts +2 -0
- package/dist/cjs-types/common/listItem/listItem.d.ts +4 -0
- package/dist/cjs-types/common/listItem/listItem.types.d.ts +33 -0
- package/dist/cjs-types/common/menu/menu.types.d.ts +2 -2
- package/dist/cjs-types/common/menu/menuItem.d.ts +5 -2
- package/dist/cjs-types/common/themeProvider/theme.types.d.ts +4 -0
- package/dist/cjs-types/common/utils/navigation.d.ts +12 -0
- package/dist/cjs-types/index.d.ts +2 -0
- package/dist/esm/common/ahroweProvider/ahroweProvider.mjs +1 -1
- package/dist/esm/common/ahroweProvider/ahroweProvider.mjs.map +1 -1
- package/dist/esm/common/breadcrumb/breadcrumb.mjs +1 -1
- package/dist/esm/common/breadcrumb/breadcrumb.mjs.map +1 -1
- package/dist/esm/common/button/button.mjs +1 -1
- package/dist/esm/common/button/button.mjs.map +1 -1
- package/dist/esm/common/button/button.module.mjs +1 -1
- package/dist/esm/common/button/button.module.mjs.map +1 -1
- package/dist/esm/common/button/button.types.mjs.map +1 -1
- package/dist/esm/common/chatMessageList/chatMessageList.mjs +1 -1
- package/dist/esm/common/chatMessageList/chatMessageList.mjs.map +1 -1
- package/dist/esm/common/commandPalette/commandPalette.mjs +1 -1
- package/dist/esm/common/commandPalette/commandPalette.mjs.map +1 -1
- package/dist/esm/common/configProvider/configProvider.mjs +1 -1
- package/dist/esm/common/configProvider/configProvider.mjs.map +1 -1
- package/dist/esm/common/dropdown/dropdown.mjs +1 -1
- package/dist/esm/common/dropdown/dropdown.mjs.map +1 -1
- package/dist/esm/common/dropdown/dropdown.module.mjs +1 -1
- package/dist/esm/common/dropdown/dropdown.module.mjs.map +1 -1
- package/dist/esm/common/infiniteBlock/useInfiniteScroll.mjs +1 -1
- package/dist/esm/common/infiniteBlock/useInfiniteScroll.mjs.map +1 -1
- package/dist/esm/common/listItem/listItem.mjs +2 -0
- package/dist/esm/common/listItem/listItem.mjs.map +1 -0
- package/dist/esm/common/listItem/listItem.module.mjs +2 -0
- package/dist/esm/common/listItem/listItem.module.mjs.map +1 -0
- package/dist/esm/common/loader/loader.module.mjs.map +1 -1
- package/dist/esm/common/menu/menu.mjs +1 -1
- package/dist/esm/common/menu/menu.mjs.map +1 -1
- package/dist/esm/common/menu/menu.types.mjs.map +1 -1
- package/dist/esm/common/menu/menuItem.mjs +1 -1
- package/dist/esm/common/menu/menuItem.mjs.map +1 -1
- package/dist/esm/common/utils/navigation.mjs +2 -0
- package/dist/esm/common/utils/navigation.mjs.map +1 -0
- package/dist/esm/index.mjs +1 -1
- package/dist/index.cjs +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/dist/types/common/actionIcon/actionIcon.types.d.ts +10 -6
- package/dist/types/common/ahroweProvider/ahroweProvider.d.ts +1 -1
- package/dist/types/common/button/button.types.d.ts +25 -8
- package/dist/types/common/chatMessageList/chatMessageList.types.d.ts +1 -1
- package/dist/types/common/configProvider/configProvider.d.ts +2 -1
- package/dist/types/common/configProvider/configProvider.types.d.ts +8 -0
- package/dist/types/common/copyButton/copyButton.types.d.ts +4 -1
- package/dist/types/common/dropdown/dropdown.types.d.ts +3 -2
- package/dist/types/common/infiniteBlock/useInfiniteScroll.d.ts +1 -1
- package/dist/types/common/listItem/index.d.ts +2 -0
- package/dist/types/common/listItem/listItem.d.ts +4 -0
- package/dist/types/common/listItem/listItem.types.d.ts +33 -0
- package/dist/types/common/menu/menu.types.d.ts +2 -2
- package/dist/types/common/menu/menuItem.d.ts +5 -2
- package/dist/types/common/themeProvider/theme.types.d.ts +4 -0
- package/dist/types/common/utils/navigation.d.ts +12 -0
- package/dist/types/index.d.ts +2 -0
- package/docs/ActionIcon.md +7 -3
- package/docs/AhroweProvider.md +2 -1
- package/docs/Breadcrumb.md +4 -24
- package/docs/Button.md +44 -9
- package/docs/CLAUDE.md +4 -0
- package/docs/ChatMessageList.md +5 -5
- package/docs/CommandPalette.md +1 -1
- package/docs/ConfigProvider.md +34 -1
- package/docs/Dropdown.md +17 -3
- package/docs/ListItem.md +97 -0
- package/docs/Menu.md +2 -2
- package/docs/ScrollbarProvider.md +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @ahrowe/ui
|
|
2
2
|
|
|
3
|
+
## 0.52.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 076562e: Button and ActionIcon take an `href` and render as a real `<a>`, with `target`, `rel` and `download`, so a long press, Ctrl/⌘-click and a middle click work on call, e-mail and map links; disabled or loading, the link drops its `href`. See docs/Button.md
|
|
8
|
+
- 076562e: **Breaking:** `ButtonProps` and `ActionIconProps` are unions of a button and a link shape, so an interface can no longer extend them and `Omit` on them drops the attributes only one shape has. See "Migration from the `ButtonProps` interface" in docs/Button.md
|
|
9
|
+
- 076562e: `ConfigProvider` and `AhroweProvider` take a `navigate` for a client-side router: a plain click on a link of the app in Button, ActionIcon, ListItem, Breadcrumb, Menu and CommandPalette goes through it instead of loading the page, while modified and middle clicks stay with the browser. It replaces the `onClick` recipes in Breadcrumb.md and Menu.md, see docs/ConfigProvider.md
|
|
10
|
+
- 076562e: Added `ListItem`: a row for a record, with an icon, a title, a second line, `endContent` for a status or actions of its own, and a `selected` state; with `href` or `onClick` the whole row opens the record while the actions in it keep working. `--list-item-min-height` and `--list-item-selected-background` theme it. See docs/ListItem.md
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 076562e: A loading Button keeps the keyboard focus: it is marked `aria-disabled` and refuses clicks and a form submit itself, instead of being `disabled`, which in Chromium and Firefox dropped the focus to the page mid-submit
|
|
15
|
+
|
|
16
|
+
## 0.51.0
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- f26bb70: **Breaking:** `Dropdown`'s `onScroll`, which was never called, is `onLoadMore`, as on VirtualList and ChatMessageList. It loads as the end of the open list comes near, with a spinner meanwhile, and in a bottom sheet the list now scrolls itself. See "Migration from `onScroll`" in docs/Dropdown.md
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- f26bb70: `ChatMessageList` starts loading older messages shortly before the top comes into view, and no longer calls `onLoadMore` again after a load that added nothing
|
|
25
|
+
- f26bb70: Fixed `Loader`'s Spinner and Comet making a scroll container they end gain and lose a scrollbar on every turn, which pushed the loader sideways
|
|
26
|
+
|
|
3
27
|
## 0.50.0
|
|
4
28
|
|
|
5
29
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ Things that might rule the package out.
|
|
|
39
39
|
that and no polyfill ships. Two details want a newer Firefox: ScrollFade's edge fade eases from
|
|
40
40
|
128 and snaps before it, and OptionPicker rings the whole group from 121. Both still work either
|
|
41
41
|
way. CI only runs Chromium.
|
|
42
|
-
- **One stylesheet.** `@ahrowe/ui/style.css` is
|
|
42
|
+
- **One stylesheet.** `@ahrowe/ui/style.css` is 180 KB, 35 KB gzipped, and covers everything. There
|
|
43
43
|
is no per-component CSS. It sits in `@layer ahrowe-ui`, so your own unlayered CSS wins no matter
|
|
44
44
|
which loads first.
|
|
45
45
|
- **No right to left.** The stylesheets use `margin-left` and friends rather than logical
|
|
@@ -160,6 +160,7 @@ runs where `@ahrowe/ui` is installed.
|
|
|
160
160
|
| `InteractableDiv` | Accessible `<div>` that behaves like a button — click and keyboard (Enter/Space) events, with proper ARIA semantics |
|
|
161
161
|
| `KanbanBoard`, `KanbanColumn` | Drag-and-drop board with columns — task management, workflow stages, status boards (e.g. Todo / In Progress / Done) |
|
|
162
162
|
| `KlipyPicker` | GIF picker backed by the Klipy API — chat reactions, message composers, content authoring tools |
|
|
163
|
+
| `ListItem` | One row of a list that stands for a record: a customer, an invoice, a delivery note |
|
|
163
164
|
| `Loader`, `LoaderType`, `ThemeColor` | A loading indicator for something in progress with no known end: a fetch, a save, a connection |
|
|
164
165
|
| `LoadingOverlay`, `Loader`, `LoaderType` | Covering content while it reloads: a table refreshing, a form saving, a panel fetching its next page |
|
|
165
166
|
| `Menu`, `menuHotkeys` | A list of actions hanging off a trigger — a row's "more" button, a toolbar overflow, a context menu on a card |
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { IconContent } from '../types/icon.types.js';
|
|
2
2
|
import { SlotClassNames, SlotStyles } from '../types/slots.types.js';
|
|
3
|
-
import {
|
|
3
|
+
import { ButtonElementProps, ButtonLinkProps } from '../button/button.types.js';
|
|
4
4
|
export type ActionIconSlots = 'root' | 'icon';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* that describe a label or a size.
|
|
8
|
-
*/
|
|
9
|
-
export interface ActionIconProps extends Omit<ButtonProps, 'icon' | 'size' | 'children' | 'isLoading' | 'shortcut' | 'classNames' | 'styles'> {
|
|
5
|
+
type LabelAndSizeProps = 'icon' | 'size' | 'children' | 'isLoading' | 'shortcut' | 'classNames' | 'styles';
|
|
6
|
+
interface ActionIconOwnProps {
|
|
10
7
|
icon: IconContent;
|
|
11
8
|
classNames?: SlotClassNames<ActionIconSlots>;
|
|
12
9
|
styles?: SlotStyles<ActionIconSlots>;
|
|
13
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* A Button with only an icon, sized by that icon, so every Button prop applies except the ones
|
|
13
|
+
* that describe a label or a size. With `href` it is a link, as a Button is. Two branches rather
|
|
14
|
+
* than one `Omit<ButtonProps>`, which would merge them and lose `target` and the typed `onClick`.
|
|
15
|
+
*/
|
|
16
|
+
export type ActionIconProps = (Omit<ButtonElementProps, LabelAndSizeProps> & ActionIconOwnProps) | (Omit<ButtonLinkProps, LabelAndSizeProps> & ActionIconOwnProps);
|
|
17
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ReactElement } from 'react';
|
|
2
2
|
import { AhroweProviderProps } from './ahroweProvider.types.js';
|
|
3
|
-
declare function AhroweProvider({ children, themes, currentThemeId, defaultProps, presentation, toast, scrollbars, }: AhroweProviderProps): ReactElement;
|
|
3
|
+
declare function AhroweProvider({ children, themes, currentThemeId, defaultProps, presentation, navigate, toast, scrollbars, }: AhroweProviderProps): ReactElement;
|
|
4
4
|
export default AhroweProvider;
|
|
@@ -27,20 +27,14 @@ export declare enum ButtonType {
|
|
|
27
27
|
Submit = "submit"
|
|
28
28
|
}
|
|
29
29
|
export type ButtonSlots = 'background' | 'content' | 'loadingContainer' | 'label' | 'shortcut';
|
|
30
|
-
|
|
31
|
-
* `DataProps` supplies the `data-*` index signature, which `ButtonHTMLAttributes` doesn't cover.
|
|
32
|
-
* `type`, `color` and `onClick` below deliberately narrow their inherited counterparts.
|
|
33
|
-
*/
|
|
34
|
-
export interface ButtonProps extends DataProps, React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
30
|
+
interface ButtonOwnProps extends DataProps {
|
|
35
31
|
className?: string;
|
|
36
32
|
style?: CSSProperties;
|
|
37
|
-
type?: ButtonType;
|
|
38
33
|
/** Emphasis: `Filled`, `Light`, `Outlined` (default) or `Plain`. */
|
|
39
34
|
styleType?: ButtonStyleType;
|
|
40
35
|
/** A `ThemeColor` (default `Primary`) or any CSS colour, such as `'var(--brand)'`. */
|
|
41
36
|
color?: ColorValue;
|
|
42
37
|
size?: ButtonSize;
|
|
43
|
-
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => Promise<void> | void;
|
|
44
38
|
children?: React.ReactNode;
|
|
45
39
|
icon?: IconContent;
|
|
46
40
|
/**
|
|
@@ -51,7 +45,30 @@ export interface ButtonProps extends DataProps, React.ButtonHTMLAttributes<HTMLB
|
|
|
51
45
|
isLoading?: boolean;
|
|
52
46
|
disabled?: boolean;
|
|
53
47
|
title?: string;
|
|
54
|
-
ref?: React.Ref<HTMLButtonElement>;
|
|
55
48
|
classNames?: SlotClassNames<ButtonSlots>;
|
|
56
49
|
styles?: SlotStyles<ButtonSlots>;
|
|
57
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* A Button without `href`, a `<button>`. `DataProps` supplies the `data-*` index signature, which
|
|
53
|
+
* `ButtonHTMLAttributes` doesn't cover. `type` and `onClick` narrow their native counterparts.
|
|
54
|
+
*/
|
|
55
|
+
export interface ButtonElementProps extends ButtonOwnProps, Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, keyof ButtonOwnProps | 'type' | 'onClick'> {
|
|
56
|
+
href?: undefined;
|
|
57
|
+
type?: ButtonType;
|
|
58
|
+
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => Promise<void> | void;
|
|
59
|
+
ref?: React.Ref<HTMLButtonElement>;
|
|
60
|
+
}
|
|
61
|
+
/** A Button with `href`, an `<a>`: `target`, `rel` and `download` are the native ones. */
|
|
62
|
+
export interface ButtonLinkProps extends ButtonOwnProps, Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, keyof ButtonOwnProps | 'type' | 'onClick' | 'href'> {
|
|
63
|
+
/**
|
|
64
|
+
* Renders the button as a link, so a long press, Ctrl/⌘-click and a middle click work as on any
|
|
65
|
+
* other: `tel:`, `mailto:`, a maps URL, a page of the app. A plain click on a page of the app goes
|
|
66
|
+
* through `ConfigProvider`'s `navigate` when it is set.
|
|
67
|
+
*/
|
|
68
|
+
href: string;
|
|
69
|
+
type?: undefined;
|
|
70
|
+
onClick?: (event: React.MouseEvent<HTMLAnchorElement>) => Promise<void> | void;
|
|
71
|
+
ref?: React.Ref<HTMLAnchorElement>;
|
|
72
|
+
}
|
|
73
|
+
export type ButtonProps = ButtonElementProps | ButtonLinkProps;
|
|
74
|
+
export {};
|
|
@@ -15,7 +15,7 @@ export interface ChatMessageListHandle {
|
|
|
15
15
|
export interface ChatMessageListProps extends HtmlProps<HTMLDivElement> {
|
|
16
16
|
/** `ChatMessage`s, and anything between them: a `Divider` with a date, a typing indicator. Oldest first. */
|
|
17
17
|
children?: ReactNode;
|
|
18
|
-
/** Loads older messages
|
|
18
|
+
/** Loads older messages as the user nears the top. Prepend them; the view stays where it was. Leave it out once there are no more. */
|
|
19
19
|
onLoadMore?: () => Promise<void>;
|
|
20
20
|
ref?: Ref<ChatMessageListHandle>;
|
|
21
21
|
/** Overrides the text ChatMessageList renders of its own. Merged per key. */
|
|
@@ -9,5 +9,6 @@ export declare const ConfigContext: React.Context<ComponentDefaults | null>;
|
|
|
9
9
|
* consumer who missed one would get sheets in half their app.
|
|
10
10
|
*/
|
|
11
11
|
export declare const PresentationContext: React.Context<Presentation | undefined>;
|
|
12
|
-
declare
|
|
12
|
+
export declare const NavigateContext: React.Context<((path: string) => void) | undefined>;
|
|
13
|
+
declare function ConfigProvider({ children, defaultProps, presentation, navigate }: ConfigProviderProps): React.JSX.Element;
|
|
13
14
|
export default ConfigProvider;
|
|
@@ -151,4 +151,12 @@ export interface ConfigProviderProps {
|
|
|
151
151
|
* component-keyed entry in `defaultProps` overrides it, and an explicit prop overrides both.
|
|
152
152
|
*/
|
|
153
153
|
presentation?: Presentation;
|
|
154
|
+
/**
|
|
155
|
+
* The app's router, for the links components render: Button and ListItem with an `href`, and
|
|
156
|
+
* Breadcrumb, Menu and CommandPalette entries. A plain click on a link within the app calls it
|
|
157
|
+
* with the path (`/clients/7?tab=projects`) instead of loading the page. Ctrl/⌘-click, a middle
|
|
158
|
+
* click, `target`, `download` and links to anywhere else stay with the browser. Unset, every
|
|
159
|
+
* link loads its page. A nested provider that does not set it keeps the outer one's.
|
|
160
|
+
*/
|
|
161
|
+
navigate?: (path: string) => void;
|
|
154
162
|
}
|
|
@@ -11,7 +11,10 @@ export interface CopyButtonLabels {
|
|
|
11
11
|
/** Announced when the copy fails. Default: "Could not copy". */
|
|
12
12
|
failed?: string;
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
/** The `<button>` branch of ActionIcon: a copy button is never a link. */
|
|
15
|
+
export interface CopyButtonProps extends Omit<Extract<ActionIconProps, {
|
|
16
|
+
href?: undefined;
|
|
17
|
+
}>, 'href' | 'icon' | 'onCopy' | 'onError' | 'onClick' | 'ref' | 'classNames' | 'styles'> {
|
|
15
18
|
/** Text written to the clipboard on click. */
|
|
16
19
|
value: string;
|
|
17
20
|
/** How long the copied state shows, in ms. Default: 2000. */
|
|
@@ -8,7 +8,7 @@ export interface DropdownItem {
|
|
|
8
8
|
disabled?: boolean;
|
|
9
9
|
}
|
|
10
10
|
export type DropdownSlots = 'root' | 'label' | 'field' | 'control' | 'icon' | 'placeholder' | 'list' | 'option';
|
|
11
|
-
export interface DropdownProps extends Omit<HtmlProps, 'onFocus' | 'onBlur' | 'onMouseEnter' | 'onMouseLeave'> {
|
|
11
|
+
export interface DropdownProps extends Omit<HtmlProps, 'onFocus' | 'onBlur' | 'onMouseEnter' | 'onMouseLeave' | 'onScroll'> {
|
|
12
12
|
formValidator?: FormValidator | null;
|
|
13
13
|
onMouseEnter?: ((event: React.MouseEvent) => void) | null;
|
|
14
14
|
onMouseLeave?: ((event: React.MouseEvent) => void) | null;
|
|
@@ -27,7 +27,8 @@ export interface DropdownProps extends Omit<HtmlProps, 'onFocus' | 'onBlur' | 'o
|
|
|
27
27
|
isRow?: boolean;
|
|
28
28
|
disabled?: boolean;
|
|
29
29
|
readOnly?: boolean;
|
|
30
|
-
|
|
30
|
+
/** Loads more items as the end of the open list comes near. Pass `undefined` once there are no more. */
|
|
31
|
+
onLoadMore?: () => Promise<void>;
|
|
31
32
|
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
32
33
|
dropdownLabel?: string;
|
|
33
34
|
isDropdownListWider?: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
export declare function useInfiniteScroll(containerRef: React.RefObject<HTMLDivElement | null>,
|
|
2
|
+
export declare function useInfiniteScroll(containerRef: React.RefObject<HTMLDivElement | null>, onLoadMore: (() => Promise<void>) | undefined): {
|
|
3
3
|
isLoading: boolean;
|
|
4
4
|
sentinelRef: React.RefObject<HTMLDivElement | null>;
|
|
5
5
|
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
import { ListItemProps } from './listItem.types.js';
|
|
3
|
+
declare function ListItem({ icon, title, subtitle, endContent, href, target, rel, download, onClick, selected, chevron, className, style, classNames, styles: slotStyles, ...rest }: ListItemProps): ReactElement;
|
|
4
|
+
export default ListItem;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { AnchorHTMLAttributes, CSSProperties, MouseEventHandler, ReactNode } from 'react';
|
|
2
|
+
import { IconContent } from '../types/icon.types.js';
|
|
3
|
+
import { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types.js';
|
|
4
|
+
export type ListItemSlots = 'root' | 'icon' | 'labels' | 'trigger' | 'title' | 'subtitle' | 'endContent' | 'chevron';
|
|
5
|
+
export interface ListItemProps extends Omit<HtmlProps<HTMLDivElement>, 'title' | 'onClick'>, Pick<AnchorHTMLAttributes<HTMLAnchorElement>, 'target' | 'rel' | 'download'> {
|
|
6
|
+
/** Before the title: a FontAwesome icon or any element, such as an `Avatar`. */
|
|
7
|
+
icon?: IconContent;
|
|
8
|
+
/** The main line. Cut off with an ellipsis where it does not fit. */
|
|
9
|
+
title: ReactNode;
|
|
10
|
+
/** A smaller line under the title, cut off after two lines. */
|
|
11
|
+
subtitle?: ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* The trailing edge: a status `Chip`, a date, an amount, and controls of its own such as an
|
|
14
|
+
* edit button or a `Switch`, which keep working in a row that opens something.
|
|
15
|
+
*/
|
|
16
|
+
endContent?: ReactNode;
|
|
17
|
+
/**
|
|
18
|
+
* Opens something from the whole row, as a link: Ctrl/⌘-click and a middle click open a new tab.
|
|
19
|
+
* A plain click on a page of the app goes through `ConfigProvider`'s `navigate` when it is set.
|
|
20
|
+
* `target`, `rel` and `download` apply to it.
|
|
21
|
+
*/
|
|
22
|
+
href?: string;
|
|
23
|
+
/** Opens something from the whole row, as a button. With `href` as well, runs before the link is followed. */
|
|
24
|
+
onClick?: MouseEventHandler<HTMLElement>;
|
|
25
|
+
/** The row whose record is open: tinted, and the current page to a screen reader. */
|
|
26
|
+
selected?: boolean;
|
|
27
|
+
/** The chevron at the end. Shown for a row with an `href`, which takes the user somewhere. */
|
|
28
|
+
chevron?: boolean;
|
|
29
|
+
className?: string;
|
|
30
|
+
style?: CSSProperties;
|
|
31
|
+
classNames?: SlotClassNames<ListItemSlots>;
|
|
32
|
+
styles?: SlotStyles<ListItemSlots>;
|
|
33
|
+
}
|
|
@@ -26,8 +26,8 @@ export interface MenuAction {
|
|
|
26
26
|
/** Renders in the destructive colour. For deleting, discarding, revoking. */
|
|
27
27
|
danger?: boolean;
|
|
28
28
|
/**
|
|
29
|
-
* Followed after `onClick` and `onAction
|
|
30
|
-
*
|
|
29
|
+
* Followed after `onClick` and `onAction`, through `ConfigProvider`'s `navigate` when it is set.
|
|
30
|
+
* In `Menu` a real `<a>`, so Ctrl/⌘-click opens a new tab.
|
|
31
31
|
*/
|
|
32
32
|
href?: string;
|
|
33
33
|
onClick?: () => void;
|
|
@@ -6,5 +6,8 @@ export declare function renderItemIcon(icon: MenuAction['icon'], reserveCell: bo
|
|
|
6
6
|
className: string;
|
|
7
7
|
style?: CSSProperties;
|
|
8
8
|
}): ReactNode;
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Runs an action the way clicking it does: its own `onClick`, then `onAction`, then its `href`.
|
|
11
|
+
* `follow` is `useFollowHref()` where the caller can reach the router; `menuHotkeys` cannot.
|
|
12
|
+
*/
|
|
13
|
+
export declare function runMenuAction(action: MenuAction, onAction?: (id: string) => void, follow?: (href: string) => void): void;
|
|
@@ -217,6 +217,10 @@ export interface ThemeVariables {
|
|
|
217
217
|
'--multi-select-min-height'?: string;
|
|
218
218
|
/** Maximum height of the open option panel. Falls back to 260px. */
|
|
219
219
|
'--multi-select-panel-height'?: string;
|
|
220
|
+
/** Minimum height of a row. Falls back to 48px, the smallest comfortable touch target. */
|
|
221
|
+
'--list-item-min-height'?: string;
|
|
222
|
+
/** Background of a `selected` row. Falls back to a 12% tint of --primary-color. */
|
|
223
|
+
'--list-item-selected-background'?: string;
|
|
220
224
|
/** Width a column collapses to once it has no cards (see `collapseEmptyColumns`). Falls back to 140px. */
|
|
221
225
|
'--kanban-empty-column-width'?: string;
|
|
222
226
|
'--option-picker-padding'?: string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { MouseEvent } from 'react';
|
|
2
|
+
type Navigate = (path: string) => void;
|
|
3
|
+
/**
|
|
4
|
+
* The click handler for an `<a>` a component renders: a plain click on a link within the app goes
|
|
5
|
+
* to `ConfigProvider`'s `navigate`, everything else to the browser. Call it after the consumer's own
|
|
6
|
+
* `onClick`, so a `preventDefault()` there still wins.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useLinkClick(): (event: MouseEvent<HTMLAnchorElement>) => void;
|
|
9
|
+
/** Follows a link from code, where no anchor was clicked: through the router where it can. */
|
|
10
|
+
export declare function followHref(href: string, navigate?: Navigate): void;
|
|
11
|
+
export declare function useFollowHref(): (href: string) => void;
|
|
12
|
+
export {};
|
|
@@ -82,6 +82,8 @@ export { default as KanbanBoard } from './common/kanbanBoard/index.js';
|
|
|
82
82
|
export * from './common/kanbanBoard/index.js';
|
|
83
83
|
export { default as KlipyPicker } from './common/klipyPicker/index.js';
|
|
84
84
|
export * from './common/klipyPicker/index.js';
|
|
85
|
+
export { default as ListItem } from './common/listItem/index.js';
|
|
86
|
+
export * from './common/listItem/index.js';
|
|
85
87
|
export { default as Loader } from './common/loader/index.js';
|
|
86
88
|
export * from './common/loader/index.js';
|
|
87
89
|
export { default as LoadingOverlay } from './common/loadingOverlay/index.js';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import e from"../configProvider/configProvider.mjs";import t from"../scrollbarProvider/index.mjs";import n from"../themeProvider/themeProvider.mjs";import{PortalHostContext as r}from"../bodyEnd/portalHostContext.mjs";import i from"../toast/toastHost.mjs";import{useCallback as a,useContext as o,useEffect as s,useLayoutEffect as c,useState as l,useSyncExternalStore as u}from"react";import{jsx as d,jsxs as f}from"react/jsx-runtime";var p=()=>()=>{};function m({children:m,themes:h,currentThemeId:g,defaultProps:_,presentation:v,
|
|
1
|
+
"use client";import e from"../configProvider/configProvider.mjs";import t from"../scrollbarProvider/index.mjs";import n from"../themeProvider/themeProvider.mjs";import{PortalHostContext as r}from"../bodyEnd/portalHostContext.mjs";import i from"../toast/toastHost.mjs";import{useCallback as a,useContext as o,useEffect as s,useLayoutEffect as c,useState as l,useSyncExternalStore as u}from"react";import{jsx as d,jsxs as f}from"react/jsx-runtime";var p=()=>()=>{};function m({children:m,themes:h,currentThemeId:g,defaultProps:_,presentation:v,navigate:y,toast:b,scrollbars:x}){let S=o(r)!==void 0,C=u(p,()=>!0,()=>!1),[w]=l(()=>typeof document>`u`?null:document.createElement(`div`)),T=a(e=>{let t=e?.parentElement?.lastElementChild;t&&w&&w.parentNode!==t&&t.appendChild(w)},[w]),E=!S&&x!==!1;c(()=>{if(!E||!w)return;let e=getComputedStyle(w).getPropertyValue(`--background-accent-light`).trim(),t=document.documentElement.style;return t.setProperty(`--ahrowe-page-scrollbar-thumb`,e),()=>{t.removeProperty(`--ahrowe-page-scrollbar-thumb`)}},[E,w,h,g]);let D=S&&(b!==void 0||x!==void 0);s(()=>{D&&console.warn("[AhroweProvider] `toast` and `scrollbars` are read by the outermost AhroweProvider only. Set them there.")},[D]);let O=f(r.Provider,{value:C?w:null,children:[d(`template`,{ref:T}),d(i,{...b,children:m}),d(`div`,{})]});return d(e,{defaultProps:_,presentation:v,navigate:y,children:d(n,{themes:h,currentThemeId:g,children:S?m:x===!1?O:d(t,{children:O})})})}export{m as default};
|
|
2
2
|
//# sourceMappingURL=ahroweProvider.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ahroweProvider.mjs","names":[],"sources":["../../../../package/common/ahroweProvider/ahroweProvider.tsx"],"sourcesContent":["import {\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useState,\n useSyncExternalStore,\n type ReactElement,\n} from 'react';\n\nimport ConfigProvider from 'components/common/configProvider';\nimport ScrollbarProvider from 'components/common/scrollbarProvider';\nimport ThemeProvider from 'components/common/themeProvider';\nimport ToastHost from '../toast/toastHost';\nimport { PortalHostContext } from '../bodyEnd/portalHostContext';\nimport type { AhroweProviderProps } from './ahroweProvider.types';\n\nconst subscribeToNothing = () => () => {};\n\nfunction AhroweProvider({\n children,\n themes,\n currentThemeId,\n defaultProps,\n presentation,\n toast,\n scrollbars,\n}: AhroweProviderProps): ReactElement {\n // A nested provider themes and configures its section only. A second toast list would stack\n // over the first in the same corner, and a second host would split the portals in two.\n const isNested = useContext(PortalHostContext) !== undefined;\n\n // The server renders no portal, so hydration must not either: `false` there, and `true` from\n // the first render of an app that is not hydrating.\n const canPortal = useSyncExternalStore(\n subscribeToNothing,\n () => true,\n () => false,\n );\n\n // Created up front instead of rendered: a rendered host exists only after the first commit, so\n // a modal open on the first render mounted a commit late and its initial focus found nothing.\n const [host] = useState(() => (typeof document === 'undefined' ? null : document.createElement('div')));\n\n // Attached from a marker before the app, because refs attach in tree order: this way the host is\n // in the page before any layout effect runs. The slot stays last, so panels paint above the app.\n const attachHost = useCallback(\n (marker: HTMLTemplateElement | null) => {\n const slot = marker?.parentElement?.lastElementChild;\n if (!slot || !host) return;\n // No cleanup, and no move when already in place: the host leaves with its slot anyway, and\n // StrictMode's extra detach and reattach otherwise took a focused element in it along.\n if (host.parentNode !== slot) slot.appendChild(host);\n },\n [host],\n );\n\n // Read from the host rather than `themes`, for the resolved value, `var()` references included.\n const stylesPage = !isNested && scrollbars !== false;\n useLayoutEffect(() => {\n if (!stylesPage || !host) return;\n const thumb = getComputedStyle(host).getPropertyValue('--background-accent-light').trim();\n const page = document.documentElement.style;\n page.setProperty('--ahrowe-page-scrollbar-thumb', thumb);\n return () => {\n page.removeProperty('--ahrowe-page-scrollbar-thumb');\n };\n }, [stylesPage, host, themes, currentThemeId]);\n\n const ignoresRootProps = isNested && (toast !== undefined || scrollbars !== undefined);\n useEffect(() => {\n if (ignoresRootProps) {\n console.warn(\n '[AhroweProvider] `toast` and `scrollbars` are read by the outermost AhroweProvider only. Set them there.',\n );\n }\n }, [ignoresRootProps]);\n\n const root = (\n <PortalHostContext.Provider value={canPortal ? host : null}>\n <template ref={attachHost} />\n <ToastHost {...toast}>{children}</ToastHost>\n <div />\n </PortalHostContext.Provider>\n );\n\n return (\n <ConfigProvider defaultProps={defaultProps} presentation={presentation}>\n <ThemeProvider themes={themes} currentThemeId={currentThemeId}>\n {/* Around the portal slot too, so the scrollbars of the panels in it are styled as well. */}\n {isNested ? children : scrollbars === false ? root : <ScrollbarProvider>{root}</ScrollbarProvider>}\n </ThemeProvider>\n </ConfigProvider>\n );\n}\n\nexport default AhroweProvider;\n"],"mappings":"8bAiBA,IAAM,UAAiC,CAAC,EAExC,SAAS,EAAe,CACtB,WACA,SACA,iBACA,eACA,eACA,QACA,cACoC,CAGpC,IAAM,EAAW,EAAW,CAAiB,IAAM,IAAA,GAI7C,EAAY,EAChB,MACM,OACA,EACR,EAIM,CAAC,GAAQ,MAAgB,OAAO,SAAa,IAAc,KAAO,SAAS,cAAc,KAAK,CAAE,EAIhG,EAAa,EAChB,GAAuC,CACtC,IAAM,EAAO,GAAQ,eAAe,iBAC/B,GAAS,GAGV,EAAK,aAAe,GAAM,EAAK,YAAY,CAAI,CACrD,EACA,CAAC,CAAI,CACP,EAGM,EAAa,CAAC,GAAY,IAAe,GAC/C,MAAsB,CACpB,GAAI,CAAC,GAAc,CAAC,EAAM,OAC1B,IAAM,EAAQ,iBAAiB,CAAI,CAAC,CAAC,iBAAiB,2BAA2B,CAAC,CAAC,KAAK,EAClF,EAAO,SAAS,gBAAgB,MAEtC,OADA,EAAK,YAAY,gCAAiC,CAAK,MAC1C,CACX,EAAK,eAAe,+BAA+B,CACrD,CACF,EAAG,CAAC,EAAY,EAAM,EAAQ,CAAc,CAAC,EAE7C,IAAM,EAAmB,IAAa,IAAU,IAAA,IAAa,IAAe,IAAA,IAC5E,MAAgB,CACV,GACF,QAAQ,KACN,0GACF,CAEJ,EAAG,CAAC,CAAgB,CAAC,EAErB,IAAM,EACJ,EAAC,EAAkB,SAAnB,CAA4B,MAAO,EAAY,EAAO,KAAtD,SAAA,CACE,EAAC,WAAD,CAAU,IAAK,CAAa,CAAA,EAC5B,EAAC,EAAD,CAAW,GAAI,EAAQ,UAAoB,CAAA,EAC3C,EAAC,MAAD,CAAM,CAAA,CACoB,IAG9B,OACE,EAAC,EAAD,CAA8B,eAA4B,
|
|
1
|
+
{"version":3,"file":"ahroweProvider.mjs","names":[],"sources":["../../../../package/common/ahroweProvider/ahroweProvider.tsx"],"sourcesContent":["import {\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useState,\n useSyncExternalStore,\n type ReactElement,\n} from 'react';\n\nimport ConfigProvider from 'components/common/configProvider';\nimport ScrollbarProvider from 'components/common/scrollbarProvider';\nimport ThemeProvider from 'components/common/themeProvider';\nimport ToastHost from '../toast/toastHost';\nimport { PortalHostContext } from '../bodyEnd/portalHostContext';\nimport type { AhroweProviderProps } from './ahroweProvider.types';\n\nconst subscribeToNothing = () => () => {};\n\nfunction AhroweProvider({\n children,\n themes,\n currentThemeId,\n defaultProps,\n presentation,\n navigate,\n toast,\n scrollbars,\n}: AhroweProviderProps): ReactElement {\n // A nested provider themes and configures its section only. A second toast list would stack\n // over the first in the same corner, and a second host would split the portals in two.\n const isNested = useContext(PortalHostContext) !== undefined;\n\n // The server renders no portal, so hydration must not either: `false` there, and `true` from\n // the first render of an app that is not hydrating.\n const canPortal = useSyncExternalStore(\n subscribeToNothing,\n () => true,\n () => false,\n );\n\n // Created up front instead of rendered: a rendered host exists only after the first commit, so\n // a modal open on the first render mounted a commit late and its initial focus found nothing.\n const [host] = useState(() => (typeof document === 'undefined' ? null : document.createElement('div')));\n\n // Attached from a marker before the app, because refs attach in tree order: this way the host is\n // in the page before any layout effect runs. The slot stays last, so panels paint above the app.\n const attachHost = useCallback(\n (marker: HTMLTemplateElement | null) => {\n const slot = marker?.parentElement?.lastElementChild;\n if (!slot || !host) return;\n // No cleanup, and no move when already in place: the host leaves with its slot anyway, and\n // StrictMode's extra detach and reattach otherwise took a focused element in it along.\n if (host.parentNode !== slot) slot.appendChild(host);\n },\n [host],\n );\n\n // Read from the host rather than `themes`, for the resolved value, `var()` references included.\n const stylesPage = !isNested && scrollbars !== false;\n useLayoutEffect(() => {\n if (!stylesPage || !host) return;\n const thumb = getComputedStyle(host).getPropertyValue('--background-accent-light').trim();\n const page = document.documentElement.style;\n page.setProperty('--ahrowe-page-scrollbar-thumb', thumb);\n return () => {\n page.removeProperty('--ahrowe-page-scrollbar-thumb');\n };\n }, [stylesPage, host, themes, currentThemeId]);\n\n const ignoresRootProps = isNested && (toast !== undefined || scrollbars !== undefined);\n useEffect(() => {\n if (ignoresRootProps) {\n console.warn(\n '[AhroweProvider] `toast` and `scrollbars` are read by the outermost AhroweProvider only. Set them there.',\n );\n }\n }, [ignoresRootProps]);\n\n const root = (\n <PortalHostContext.Provider value={canPortal ? host : null}>\n <template ref={attachHost} />\n <ToastHost {...toast}>{children}</ToastHost>\n <div />\n </PortalHostContext.Provider>\n );\n\n return (\n <ConfigProvider defaultProps={defaultProps} presentation={presentation} navigate={navigate}>\n <ThemeProvider themes={themes} currentThemeId={currentThemeId}>\n {/* Around the portal slot too, so the scrollbars of the panels in it are styled as well. */}\n {isNested ? children : scrollbars === false ? root : <ScrollbarProvider>{root}</ScrollbarProvider>}\n </ThemeProvider>\n </ConfigProvider>\n );\n}\n\nexport default AhroweProvider;\n"],"mappings":"8bAiBA,IAAM,UAAiC,CAAC,EAExC,SAAS,EAAe,CACtB,WACA,SACA,iBACA,eACA,eACA,WACA,QACA,cACoC,CAGpC,IAAM,EAAW,EAAW,CAAiB,IAAM,IAAA,GAI7C,EAAY,EAChB,MACM,OACA,EACR,EAIM,CAAC,GAAQ,MAAgB,OAAO,SAAa,IAAc,KAAO,SAAS,cAAc,KAAK,CAAE,EAIhG,EAAa,EAChB,GAAuC,CACtC,IAAM,EAAO,GAAQ,eAAe,iBAC/B,GAAS,GAGV,EAAK,aAAe,GAAM,EAAK,YAAY,CAAI,CACrD,EACA,CAAC,CAAI,CACP,EAGM,EAAa,CAAC,GAAY,IAAe,GAC/C,MAAsB,CACpB,GAAI,CAAC,GAAc,CAAC,EAAM,OAC1B,IAAM,EAAQ,iBAAiB,CAAI,CAAC,CAAC,iBAAiB,2BAA2B,CAAC,CAAC,KAAK,EAClF,EAAO,SAAS,gBAAgB,MAEtC,OADA,EAAK,YAAY,gCAAiC,CAAK,MAC1C,CACX,EAAK,eAAe,+BAA+B,CACrD,CACF,EAAG,CAAC,EAAY,EAAM,EAAQ,CAAc,CAAC,EAE7C,IAAM,EAAmB,IAAa,IAAU,IAAA,IAAa,IAAe,IAAA,IAC5E,MAAgB,CACV,GACF,QAAQ,KACN,0GACF,CAEJ,EAAG,CAAC,CAAgB,CAAC,EAErB,IAAM,EACJ,EAAC,EAAkB,SAAnB,CAA4B,MAAO,EAAY,EAAO,KAAtD,SAAA,CACE,EAAC,WAAD,CAAU,IAAK,CAAa,CAAA,EAC5B,EAAC,EAAD,CAAW,GAAI,EAAQ,UAAoB,CAAA,EAC3C,EAAC,MAAD,CAAM,CAAA,CACoB,IAG9B,OACE,EAAC,EAAD,CAA8B,eAA4B,eAAwB,WAChF,SAAA,EAAC,EAAD,CAAuB,SAAwB,iBAE5C,SAAA,EAAW,EAAW,IAAe,GAAQ,EAAO,EAAC,EAAD,CAAA,SAAoB,CAAwB,CAAA,CACpF,CAAA,CACD,CAAA,CAEpB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{renderIcon as n}from"../utils/renderIcon.mjs";import r from"../actionIcon/actionIcon.mjs";import
|
|
1
|
+
"use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{renderIcon as n}from"../utils/renderIcon.mjs";import{useLinkClick as r}from"../utils/navigation.mjs";import i from"../actionIcon/actionIcon.mjs";import a from"../interactableDiv/index.mjs";import{PopoverAlign as o,PopoverPlacement as s}from"../popover/popover.types.mjs";import c from"../popover/popover.mjs";import l from"./breadcrumb.module.mjs";import{useState as u}from"react";import d from"classnames";import{FontAwesomeIcon as f}from"@fortawesome/react-fontawesome";import{faChevronRight as p,faEllipsis as m}from"@fortawesome/free-solid-svg-icons";import{Fragment as h,jsx as g,jsxs as _}from"react/jsx-runtime";var v={showHidden:`Show hidden breadcrumbs`};function y(e,t){let n=e.map((e,t)=>({item:e,index:t}));if(!t||e.length<=t)return{leading:n,hidden:[],trailing:[]};let r=Math.max(1,t-2),i=e.length-1-r;return i<=0?{leading:n,hidden:[],trailing:[]}:{leading:n.slice(0,1),hidden:n.slice(1,1+i),trailing:n.slice(1+i)}}function b(n){let{items:a=[],separator:h,maxItems:b,"aria-label":S=`Breadcrumb`,className:C,style:w,classNames:T,styles:E,labels:D,...O}=e(`Breadcrumb`,n),k=t(`Breadcrumb`,D,v),A=r(),[j,M]=u(!1),{leading:N,hidden:P,trailing:F}=y(a,b),I=a.length-1;function L(e){return e===I?null:g(`span`,{className:d(l.breadcrumbSeparator,T?.separator),style:E?.separator,children:h??g(f,{icon:p})})}function R({item:e,index:t}){return _(`li`,{className:d(l.breadcrumbItem,T?.item),style:E?.item,children:[x(e,t===I,l,T,E,A),L(t)]},e.id??t)}return g(`nav`,{...O,"aria-label":S,className:d(l.breadcrumb,T?.root,C),style:{...w,...E?.root},children:_(`ol`,{className:d(l.breadcrumbList,T?.list),style:E?.list,children:[N.map(R),P.length>0&&_(`li`,{className:d(l.breadcrumbItem,T?.item),style:E?.item,children:[g(c,{isOpen:j,onOpenChange:M,placement:s.Bottom,align:o.Start,constrainWidth:!1,classNames:{panel:l.breadcrumbHiddenPanel},content:g(`ul`,{className:d(l.breadcrumbHiddenList,T?.hiddenList),style:E?.hiddenList,children:P.map(({item:e,index:t})=>g(`li`,{className:d(l.breadcrumbHiddenItem,T?.hiddenItem),style:E?.hiddenItem,children:x({...e,onClick:t=>{e.onClick?.(t),M(!1)}},!1,l,T,E,A)},e.id??t))}),children:g(i,{icon:m,color:`var(--text-dark)`,title:k.showHidden,"aria-label":k.showHidden,"aria-expanded":j,className:d(l.breadcrumbEllipsis,T?.ellipsis),style:E?.ellipsis})}),L(P[P.length-1].index)]}),F.map(R)]})})}function x(e,t,r,i,o,s){let c=_(h,{children:[e.icon?n(e.icon,{className:d(r.breadcrumbIcon,i?.icon),style:o?.icon}):null,e.label]});return t?g(`span`,{"aria-current":`page`,className:d(r.breadcrumbCurrent,i?.current),style:o?.current,children:c}):e.disabled?g(`span`,{className:d(r.breadcrumbLink,r.breadcrumbLinkDisabled,i?.link),style:o?.link,children:c}):e.href?g(`a`,{href:e.href,onClick:t=>{e.onClick?.(t),s(t)},className:d(r.breadcrumbLink,i?.link),style:o?.link,children:c}):e.onClick?g(a,{onClick:e.onClick,className:d(r.breadcrumbLink,i?.link),style:o?.link,children:c}):g(`span`,{className:d(r.breadcrumbLink,r.breadcrumbLinkDisabled,i?.link),style:o?.link,children:c})}export{b as default};
|
|
2
2
|
//# sourceMappingURL=breadcrumb.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"breadcrumb.mjs","names":[],"sources":["../../../../package/common/breadcrumb/breadcrumb.tsx"],"sourcesContent":["import { useState } from 'react';\nimport type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronRight, faEllipsis } from '@fortawesome/free-solid-svg-icons';\n\nimport InteractableDiv from '../interactableDiv';\nimport ActionIcon from '../actionIcon';\nimport Popover from '../popover';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport type { BreadcrumbItem, BreadcrumbLabels, BreadcrumbProps } from './breadcrumb.types';\n\nimport styles from './breadcrumb.module.pcss';\nimport { renderIcon } from '../utils/renderIcon';\n\ninterface IndexedItem {\n item: BreadcrumbItem;\n index: number;\n}\n\nconst DEFAULT_LABELS: Required<BreadcrumbLabels> = {\n showHidden: 'Show hidden breadcrumbs',\n};\n\nfunction getGroups(items: BreadcrumbItem[], maxItems?: number) {\n const indexed = items.map((item, index) => ({ item, index }));\n if (!maxItems || items.length <= maxItems) {\n return { leading: indexed, hidden: [] as IndexedItem[], trailing: [] as IndexedItem[] };\n }\n\n const trailingCount = Math.max(1, maxItems - 2);\n const hiddenCount = items.length - 1 - trailingCount;\n if (hiddenCount <= 0) {\n return { leading: indexed, hidden: [] as IndexedItem[], trailing: [] as IndexedItem[] };\n }\n\n return {\n leading: indexed.slice(0, 1),\n hidden: indexed.slice(1, 1 + hiddenCount),\n trailing: indexed.slice(1 + hiddenCount),\n };\n}\n\nfunction Breadcrumb(props: BreadcrumbProps): ReactElement {\n const {\n items = [],\n separator,\n maxItems,\n 'aria-label': ariaLabel = 'Breadcrumb',\n className,\n style,\n classNames,\n styles: slotStyles,\n labels,\n ...rest\n } = useComponentDefaults('Breadcrumb', props);\n const text = useLabels('Breadcrumb', labels, DEFAULT_LABELS);\n\n const [isHiddenMenuOpen, setIsHiddenMenuOpen] = useState(false);\n const { leading, hidden, trailing } = getGroups(items, maxItems);\n const lastIndex = items.length - 1;\n\n function renderSeparatorAfter(index: number) {\n if (index === lastIndex) return null;\n return (\n <span className={cx(styles.breadcrumbSeparator, classNames?.separator)} style={slotStyles?.separator}>\n {separator ?? <FontAwesomeIcon icon={faChevronRight} />}\n </span>\n );\n }\n\n function renderItem({ item, index }: IndexedItem) {\n return (\n <li key={item.id ?? index} className={cx(styles.breadcrumbItem, classNames?.item)} style={slotStyles?.item}>\n {renderCrumb(item, index === lastIndex, styles, classNames, slotStyles)}\n {renderSeparatorAfter(index)}\n </li>\n );\n }\n\n return (\n <nav\n {...rest}\n aria-label={ariaLabel}\n className={cx(styles.breadcrumb, classNames?.root, className)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <ol className={cx(styles.breadcrumbList, classNames?.list)} style={slotStyles?.list}>\n {leading.map(renderItem)}\n\n {hidden.length > 0 && (\n <li className={cx(styles.breadcrumbItem, classNames?.item)} style={slotStyles?.item}>\n <Popover\n isOpen={isHiddenMenuOpen}\n onOpenChange={setIsHiddenMenuOpen}\n placement={PopoverPlacement.Bottom}\n align={PopoverAlign.Start}\n // The list brings its own padding and width.\n constrainWidth={false}\n classNames={{ panel: styles.breadcrumbHiddenPanel }}\n content={\n <ul className={cx(styles.breadcrumbHiddenList, classNames?.hiddenList)} style={slotStyles?.hiddenList}>\n {hidden.map(({ item, index }) => (\n <li\n key={item.id ?? index}\n className={cx(styles.breadcrumbHiddenItem, classNames?.hiddenItem)}\n style={slotStyles?.hiddenItem}\n >\n {renderCrumb(\n {\n ...item,\n onClick: (event) => {\n item.onClick?.(event);\n setIsHiddenMenuOpen(false);\n },\n },\n false,\n styles,\n classNames,\n slotStyles,\n )}\n </li>\n ))}\n </ul>\n }\n >\n <ActionIcon\n icon={faEllipsis}\n color='var(--text-dark)'\n title={text.showHidden}\n aria-label={text.showHidden}\n aria-expanded={isHiddenMenuOpen}\n className={cx(styles.breadcrumbEllipsis, classNames?.ellipsis)}\n style={slotStyles?.ellipsis}\n />\n </Popover>\n {renderSeparatorAfter(hidden[hidden.length - 1].index)}\n </li>\n )}\n\n {trailing.map(renderItem)}\n </ol>\n </nav>\n );\n}\n\nfunction renderCrumb(\n item: BreadcrumbItem,\n isCurrent: boolean,\n styles: Record<string, string>,\n classNames: BreadcrumbProps['classNames'],\n slotStyles: BreadcrumbProps['styles'],\n) {\n const content = (\n <>\n {item.icon\n ? renderIcon(item.icon, {\n className: cx(styles.breadcrumbIcon, classNames?.icon),\n style: slotStyles?.icon,\n })\n : null}\n {item.label}\n </>\n );\n\n if (isCurrent) {\n return (\n <span\n aria-current='page'\n className={cx(styles.breadcrumbCurrent, classNames?.current)}\n style={slotStyles?.current}\n >\n {content}\n </span>\n );\n }\n\n if (item.disabled) {\n return (\n <span\n className={cx(styles.breadcrumbLink, styles.breadcrumbLinkDisabled, classNames?.link)}\n style={slotStyles?.link}\n >\n {content}\n </span>\n );\n }\n\n if (item.href) {\n return (\n <a\n href={item.href}\n onClick={item.onClick}\n className={cx(styles.breadcrumbLink, classNames?.link)}\n style={slotStyles?.link}\n >\n {content}\n </a>\n );\n }\n\n if (item.onClick) {\n return (\n <InteractableDiv\n onClick={item.onClick}\n className={cx(styles.breadcrumbLink, classNames?.link)}\n style={slotStyles?.link}\n >\n {content}\n </InteractableDiv>\n );\n }\n\n return (\n <span\n className={cx(styles.breadcrumbLink, styles.breadcrumbLinkDisabled, classNames?.link)}\n style={slotStyles?.link}\n >\n {content}\n </span>\n );\n}\n\nexport default Breadcrumb;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"breadcrumb.mjs","names":[],"sources":["../../../../package/common/breadcrumb/breadcrumb.tsx"],"sourcesContent":["import { useState } from 'react';\nimport type { MouseEvent, ReactElement } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronRight, faEllipsis } from '@fortawesome/free-solid-svg-icons';\n\nimport InteractableDiv from '../interactableDiv';\nimport ActionIcon from '../actionIcon';\nimport Popover from '../popover';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport type { BreadcrumbItem, BreadcrumbLabels, BreadcrumbProps } from './breadcrumb.types';\n\nimport styles from './breadcrumb.module.pcss';\nimport { renderIcon } from '../utils/renderIcon';\nimport { useLinkClick } from '../utils/navigation';\n\ninterface IndexedItem {\n item: BreadcrumbItem;\n index: number;\n}\n\nconst DEFAULT_LABELS: Required<BreadcrumbLabels> = {\n showHidden: 'Show hidden breadcrumbs',\n};\n\nfunction getGroups(items: BreadcrumbItem[], maxItems?: number) {\n const indexed = items.map((item, index) => ({ item, index }));\n if (!maxItems || items.length <= maxItems) {\n return { leading: indexed, hidden: [] as IndexedItem[], trailing: [] as IndexedItem[] };\n }\n\n const trailingCount = Math.max(1, maxItems - 2);\n const hiddenCount = items.length - 1 - trailingCount;\n if (hiddenCount <= 0) {\n return { leading: indexed, hidden: [] as IndexedItem[], trailing: [] as IndexedItem[] };\n }\n\n return {\n leading: indexed.slice(0, 1),\n hidden: indexed.slice(1, 1 + hiddenCount),\n trailing: indexed.slice(1 + hiddenCount),\n };\n}\n\nfunction Breadcrumb(props: BreadcrumbProps): ReactElement {\n const {\n items = [],\n separator,\n maxItems,\n 'aria-label': ariaLabel = 'Breadcrumb',\n className,\n style,\n classNames,\n styles: slotStyles,\n labels,\n ...rest\n } = useComponentDefaults('Breadcrumb', props);\n const text = useLabels('Breadcrumb', labels, DEFAULT_LABELS);\n const followLink = useLinkClick();\n\n const [isHiddenMenuOpen, setIsHiddenMenuOpen] = useState(false);\n const { leading, hidden, trailing } = getGroups(items, maxItems);\n const lastIndex = items.length - 1;\n\n function renderSeparatorAfter(index: number) {\n if (index === lastIndex) return null;\n return (\n <span className={cx(styles.breadcrumbSeparator, classNames?.separator)} style={slotStyles?.separator}>\n {separator ?? <FontAwesomeIcon icon={faChevronRight} />}\n </span>\n );\n }\n\n function renderItem({ item, index }: IndexedItem) {\n return (\n <li key={item.id ?? index} className={cx(styles.breadcrumbItem, classNames?.item)} style={slotStyles?.item}>\n {renderCrumb(item, index === lastIndex, styles, classNames, slotStyles, followLink)}\n {renderSeparatorAfter(index)}\n </li>\n );\n }\n\n return (\n <nav\n {...rest}\n aria-label={ariaLabel}\n className={cx(styles.breadcrumb, classNames?.root, className)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <ol className={cx(styles.breadcrumbList, classNames?.list)} style={slotStyles?.list}>\n {leading.map(renderItem)}\n\n {hidden.length > 0 && (\n <li className={cx(styles.breadcrumbItem, classNames?.item)} style={slotStyles?.item}>\n <Popover\n isOpen={isHiddenMenuOpen}\n onOpenChange={setIsHiddenMenuOpen}\n placement={PopoverPlacement.Bottom}\n align={PopoverAlign.Start}\n // The list brings its own padding and width.\n constrainWidth={false}\n classNames={{ panel: styles.breadcrumbHiddenPanel }}\n content={\n <ul className={cx(styles.breadcrumbHiddenList, classNames?.hiddenList)} style={slotStyles?.hiddenList}>\n {hidden.map(({ item, index }) => (\n <li\n key={item.id ?? index}\n className={cx(styles.breadcrumbHiddenItem, classNames?.hiddenItem)}\n style={slotStyles?.hiddenItem}\n >\n {renderCrumb(\n {\n ...item,\n onClick: (event) => {\n item.onClick?.(event);\n setIsHiddenMenuOpen(false);\n },\n },\n false,\n styles,\n classNames,\n slotStyles,\n followLink,\n )}\n </li>\n ))}\n </ul>\n }\n >\n <ActionIcon\n icon={faEllipsis}\n color='var(--text-dark)'\n title={text.showHidden}\n aria-label={text.showHidden}\n aria-expanded={isHiddenMenuOpen}\n className={cx(styles.breadcrumbEllipsis, classNames?.ellipsis)}\n style={slotStyles?.ellipsis}\n />\n </Popover>\n {renderSeparatorAfter(hidden[hidden.length - 1].index)}\n </li>\n )}\n\n {trailing.map(renderItem)}\n </ol>\n </nav>\n );\n}\n\nfunction renderCrumb(\n item: BreadcrumbItem,\n isCurrent: boolean,\n styles: Record<string, string>,\n classNames: BreadcrumbProps['classNames'],\n slotStyles: BreadcrumbProps['styles'],\n followLink: (event: MouseEvent<HTMLAnchorElement>) => void,\n) {\n const content = (\n <>\n {item.icon\n ? renderIcon(item.icon, {\n className: cx(styles.breadcrumbIcon, classNames?.icon),\n style: slotStyles?.icon,\n })\n : null}\n {item.label}\n </>\n );\n\n if (isCurrent) {\n return (\n <span\n aria-current='page'\n className={cx(styles.breadcrumbCurrent, classNames?.current)}\n style={slotStyles?.current}\n >\n {content}\n </span>\n );\n }\n\n if (item.disabled) {\n return (\n <span\n className={cx(styles.breadcrumbLink, styles.breadcrumbLinkDisabled, classNames?.link)}\n style={slotStyles?.link}\n >\n {content}\n </span>\n );\n }\n\n if (item.href) {\n return (\n <a\n href={item.href}\n onClick={(event) => {\n item.onClick?.(event);\n followLink(event);\n }}\n className={cx(styles.breadcrumbLink, classNames?.link)}\n style={slotStyles?.link}\n >\n {content}\n </a>\n );\n }\n\n if (item.onClick) {\n return (\n <InteractableDiv\n onClick={item.onClick}\n className={cx(styles.breadcrumbLink, classNames?.link)}\n style={slotStyles?.link}\n >\n {content}\n </InteractableDiv>\n );\n }\n\n return (\n <span\n className={cx(styles.breadcrumbLink, styles.breadcrumbLinkDisabled, classNames?.link)}\n style={slotStyles?.link}\n >\n {content}\n </span>\n );\n}\n\nexport default Breadcrumb;\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{ThemeColor as t}from"../themeProvider/color.types.mjs";import{toneProps as n,useToneCheck as r}from"../styles/tone.mjs";import i from"./button.module.mjs";import a from"../loader/loader.mjs";import{ButtonSize as o,ButtonStyleType as s,ButtonType as c}from"./button.types.mjs";import l from"../ripple/ripple.mjs";import{renderIcon as u}from"../utils/renderIcon.mjs";import{ariaKeyshortcuts as d,formatHotkey as f}from"../hooks/useHotkeys.mjs";import{
|
|
1
|
+
"use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{ThemeColor as t}from"../themeProvider/color.types.mjs";import{toneProps as n,useToneCheck as r}from"../styles/tone.mjs";import i from"./button.module.mjs";import a from"../loader/loader.mjs";import{ButtonSize as o,ButtonStyleType as s,ButtonType as c}from"./button.types.mjs";import l from"../ripple/ripple.mjs";import{renderIcon as u}from"../utils/renderIcon.mjs";import{ariaKeyshortcuts as d,formatHotkey as f}from"../hooks/useHotkeys.mjs";import{useLinkClick as p}from"../utils/navigation.mjs";import{useRef as m}from"react";import h from"classnames";import{jsx as g,jsxs as _}from"react/jsx-runtime";var v={filled:i.buttonStyleFilled,light:i.buttonStyleLight,outlined:i.buttonStyleOutlined,plain:i.buttonStylePlain},y={small:i.buttonSizeSmall,medium:i.buttonSizeMedium,large:i.buttonSizeLarge,icon:i.buttonSizeIcon},b=`color-mix(in srgb, currentcolor 20%, transparent)`;function x(x){let{className:S,style:C,type:w=c.Button,styleType:T=s.Outlined,color:E=t.Primary,size:D=o.Medium,children:O,icon:k,shortcut:A,"aria-keyshortcuts":j=d(A),isLoading:M=!1,disabled:N=!1,title:P=void 0,classNames:F,styles:I,...L}=e(`Button`,x),R=m(null),z=p(),B=n(E);r(`Button`,[E]);let V=M||N,H={className:h(i.button,v[T],B.className,y[D],{[i.buttonLink]:L.href!==void 0,[i.buttonNotAllowed]:V,[i.buttonDisabled]:N},S),style:{...B.style,...C},title:P,"aria-keyshortcuts":j},U=g(l,{ref:R,className:i.buttonRipple,disabled:V,rippleBackground:b,children:g(`div`,{className:h(i.buttonBackground,F?.background),style:I?.background,children:_(`div`,{className:h(i.buttonContent,F?.content),style:I?.content,children:[M?g(`div`,{className:h(i.buttonLoadingContainer,F?.loadingContainer),style:I?.loadingContainer,children:g(a,{size:`1.2em`,color:t.Neutral})}):null,_(`div`,{className:h(F?.label,{[i.buttonLoading]:M,[i.buttonWithIcon]:!!k}),style:I?.label,children:[k?u(k):null,A?_(`span`,{children:[O,g(`span`,{"aria-hidden":!0,className:h(i.buttonShortcut,F?.shortcut),style:I?.shortcut,children:f(A)})]}):O]})]})})});if(L.href!==void 0){let{href:e,onClick:t,ref:n,...r}=L;return g(`a`,{...r,...H,...N?{role:`link`,tabIndex:void 0}:{href:e},"aria-disabled":V||void 0,onClick:e=>{if(V){e.preventDefault();return}e.detail===0&&R.current?.addRipple(),t?.(e),z(e)},ref:n,children:U})}let{onClick:W,ref:G,...K}=L;return g(`button`,{...K,...H,type:w,"aria-disabled":M||void 0,disabled:N,onClick:e=>{if(M){e.preventDefault();return}return e.detail===0&&R.current?.addRipple(),W?.(e)},ref:G,children:U})}x.StyleType=s,x.Size=o,x.Type=c;export{x as default};
|
|
2
2
|
//# sourceMappingURL=button.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.mjs","names":[],"sources":["../../../../package/common/button/button.tsx"],"sourcesContent":["import cx from 'classnames';\nimport { useRef, type ReactElement } from 'react';\n\nimport styles from './button.module.pcss';\nimport Loader from '../loader';\nimport { ButtonStyleType, ButtonSize, ButtonType } from './button.types';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport type { ButtonProps } from './button.types';\nimport Ripple from '../ripple';\nimport type { RippleHandle } from '../ripple';\nimport { useComponentDefaults } from '../configProvider';\nimport { renderIcon } from '../utils/renderIcon';\nimport { ariaKeyshortcuts, formatHotkey } from '../hooks/useHotkeys';\nimport { toneProps, useToneCheck } from '../styles/tone';\n\nconst CLASS_BY_STYLE: Record<ButtonStyleType, string> = {\n filled: styles.buttonStyleFilled,\n light: styles.buttonStyleLight,\n outlined: styles.buttonStyleOutlined,\n plain: styles.buttonStylePlain,\n};\n\nconst CLASS_BY_SIZE: Record<ButtonSize, string> = {\n small: styles.buttonSizeSmall,\n medium: styles.buttonSizeMedium,\n large: styles.buttonSizeLarge,\n icon: styles.buttonSizeIcon,\n};\n\n// Ripple's own default is white, which vanishes on a transparent button over a light page.\nconst RIPPLE_BACKGROUND = 'color-mix(in srgb, currentcolor 20%, transparent)';\n\nfunction Button(props: ButtonProps): ReactElement {\n const {\n className,\n style,\n type = ButtonType.Button,\n styleType = ButtonStyleType.Outlined,\n color = ThemeColor.Primary,\n size = ButtonSize.Medium,\n
|
|
1
|
+
{"version":3,"file":"button.mjs","names":[],"sources":["../../../../package/common/button/button.tsx"],"sourcesContent":["import cx from 'classnames';\nimport { useRef, type ReactElement } from 'react';\n\nimport styles from './button.module.pcss';\nimport Loader from '../loader';\nimport { ButtonStyleType, ButtonSize, ButtonType } from './button.types';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport type { ButtonProps } from './button.types';\nimport Ripple from '../ripple';\nimport type { RippleHandle } from '../ripple';\nimport { useComponentDefaults } from '../configProvider';\nimport { renderIcon } from '../utils/renderIcon';\nimport { ariaKeyshortcuts, formatHotkey } from '../hooks/useHotkeys';\nimport { toneProps, useToneCheck } from '../styles/tone';\nimport { useLinkClick } from '../utils/navigation';\n\nconst CLASS_BY_STYLE: Record<ButtonStyleType, string> = {\n filled: styles.buttonStyleFilled,\n light: styles.buttonStyleLight,\n outlined: styles.buttonStyleOutlined,\n plain: styles.buttonStylePlain,\n};\n\nconst CLASS_BY_SIZE: Record<ButtonSize, string> = {\n small: styles.buttonSizeSmall,\n medium: styles.buttonSizeMedium,\n large: styles.buttonSizeLarge,\n icon: styles.buttonSizeIcon,\n};\n\n// Ripple's own default is white, which vanishes on a transparent button over a light page.\nconst RIPPLE_BACKGROUND = 'color-mix(in srgb, currentcolor 20%, transparent)';\n\nfunction Button(props: ButtonProps): ReactElement {\n const {\n className,\n style,\n type = ButtonType.Button,\n styleType = ButtonStyleType.Outlined,\n color = ThemeColor.Primary,\n size = ButtonSize.Medium,\n children,\n icon,\n shortcut,\n // The caller's own wins: it may list a second binding the hint leaves out.\n 'aria-keyshortcuts': keyshortcuts = ariaKeyshortcuts(shortcut),\n isLoading = false,\n disabled = false,\n title = undefined,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('Button', props);\n\n const rippleRef = useRef<RippleHandle>(null);\n const followLink = useLinkClick();\n const tone = toneProps(color);\n useToneCheck('Button', [color]);\n const isInactive = isLoading || disabled;\n\n const rootProps = {\n className: cx(\n styles.button,\n CLASS_BY_STYLE[styleType],\n tone.className,\n CLASS_BY_SIZE[size],\n {\n [styles.buttonLink]: rest.href !== undefined,\n [styles.buttonNotAllowed]: isInactive,\n [styles.buttonDisabled]: disabled,\n },\n className,\n ),\n style: { ...tone.style, ...style },\n title,\n 'aria-keyshortcuts': keyshortcuts,\n };\n\n const content = (\n <Ripple ref={rippleRef} className={styles.buttonRipple} disabled={isInactive} rippleBackground={RIPPLE_BACKGROUND}>\n <div className={cx(styles.buttonBackground, classNames?.background)} style={slotStyles?.background}>\n <div className={cx(styles.buttonContent, classNames?.content)} style={slotStyles?.content}>\n {isLoading ? (\n <div\n className={cx(styles.buttonLoadingContainer, classNames?.loadingContainer)}\n style={slotStyles?.loadingContainer}\n >\n <Loader size='1.2em' color={ThemeColor.Neutral} />\n </div>\n ) : null}\n <div\n className={cx(classNames?.label, {\n [styles.buttonLoading]: isLoading,\n [styles.buttonWithIcon]: !!icon,\n })}\n style={slotStyles?.label}\n >\n {icon ? renderIcon(icon) : null}\n {shortcut ? (\n // One inline run with the label, so the hint sits on its baseline: the icon's flex\n // row would make the hint its own item and centre it instead.\n <span>\n {children}\n {/* Out of the accessible name, which would read \"Save Ctrl+S\": aria-keyshortcuts says it. */}\n <span\n aria-hidden\n className={cx(styles.buttonShortcut, classNames?.shortcut)}\n style={slotStyles?.shortcut}\n >\n {formatHotkey(shortcut)}\n </span>\n </span>\n ) : (\n children\n )}\n </div>\n </div>\n </div>\n </Ripple>\n );\n\n if (rest.href !== undefined) {\n const { href, onClick, ref, ...anchorRest } = rest;\n return (\n <a\n {...anchorRest}\n {...rootProps}\n // An <a> cannot be disabled. Without its href and tabIndex it leaves the tab order and goes\n // nowhere, and the role keeps it announced as the link it is, dimmed.\n {...(disabled ? { role: 'link', tabIndex: undefined } : { href })}\n aria-disabled={isInactive || undefined}\n onClick={(event) => {\n if (isInactive) {\n event.preventDefault();\n return;\n }\n if (event.detail === 0) rippleRef.current?.addRipple();\n void onClick?.(event);\n followLink(event);\n }}\n ref={ref}\n >\n {content}\n </a>\n );\n }\n\n const { onClick, ref, ...buttonRest } = rest;\n return (\n <button\n {...buttonRest}\n {...rootProps}\n type={type}\n // Loading is aria-disabled, not disabled, which would drop the keyboard focus mid-submit. The\n // click is cancelled instead, which also stops the form it submits.\n aria-disabled={isLoading || undefined}\n disabled={disabled}\n onClick={(event) => {\n if (isLoading) {\n event.preventDefault();\n return;\n }\n if (event.detail === 0) rippleRef.current?.addRipple();\n return onClick?.(event);\n }}\n ref={ref}\n >\n {content}\n </button>\n );\n}\n\nButton.StyleType = ButtonStyleType;\nButton.Size = ButtonSize;\nButton.Type = ButtonType;\n\nexport default Button;\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";var e=`button_wYLPJ`,t=`button-notAllowed_xGT-E`,
|
|
1
|
+
"use client";var e=`button_wYLPJ`,t=`button-link_uOwR6`,n=`button-notAllowed_xGT-E`,r=`button-disabled_XDm5w`,i=`button-content_Q-0-4`,a=`button-loading_TY6y7`,o=`button-loading-container_pMSta`,s=`button-withIcon_IXEKA`,c=`button-shortcut_0bg8S`,l=`button-style-filled_gsaJ4`,u=`button-style-light_HBi34`,d=`button-style-outlined_OSfMq`,f=`button-style-plain_CUtTy`,p=`button-background_9Vfo5`,m=`button-size-small_mn6jE`,h=`button-size-medium_gNVDM`,g=`button-size-large_xSIcS`,_=`button-size-icon_pkqwl`,v=`button-ripple_UGtt8`,y={button:e,"button-link":`button-link_uOwR6`,buttonLink:t,"button-notAllowed":`button-notAllowed_xGT-E`,buttonNotAllowed:n,"button-disabled":`button-disabled_XDm5w`,buttonDisabled:r,"button-content":`button-content_Q-0-4`,buttonContent:i,"button-loading":`button-loading_TY6y7`,buttonLoading:a,"button-loading-container":`button-loading-container_pMSta`,buttonLoadingContainer:o,"button-withIcon":`button-withIcon_IXEKA`,buttonWithIcon:s,"button-shortcut":`button-shortcut_0bg8S`,buttonShortcut:c,"button-style-filled":`button-style-filled_gsaJ4`,buttonStyleFilled:l,"button-style-light":`button-style-light_HBi34`,buttonStyleLight:u,"button-style-outlined":`button-style-outlined_OSfMq`,buttonStyleOutlined:d,"button-style-plain":`button-style-plain_CUtTy`,buttonStylePlain:f,"button-background":`button-background_9Vfo5`,buttonBackground:p,"button-size-small":`button-size-small_mn6jE`,buttonSizeSmall:m,"button-size-medium":`button-size-medium_gNVDM`,buttonSizeMedium:h,"button-size-large":`button-size-large_xSIcS`,buttonSizeLarge:g,"button-size-icon":`button-size-icon_pkqwl`,buttonSizeIcon:_,"button-ripple":`button-ripple_UGtt8`,buttonRipple:v};export{e as button,p as buttonBackground,i as buttonContent,r as buttonDisabled,t as buttonLink,a as buttonLoading,o as buttonLoadingContainer,n as buttonNotAllowed,v as buttonRipple,c as buttonShortcut,_ as buttonSizeIcon,g as buttonSizeLarge,h as buttonSizeMedium,m as buttonSizeSmall,l as buttonStyleFilled,u as buttonStyleLight,d as buttonStyleOutlined,f as buttonStylePlain,s as buttonWithIcon,y as default};
|
|
2
2
|
//# sourceMappingURL=button.module.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.module.mjs","names":[],"sources":["../../../../package/common/button/button.module.pcss"],"sourcesContent":[".button {\n appearance: none;\n -webkit-appearance: none;\n box-sizing: border-box;\n font-size: var(--default-font-size);\n background: transparent;\n border: 1px solid transparent;\n padding: 0;\n font-family: var(--default-font);\n border-radius: var(--default-border-radius);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n position: relative;\n overflow: hidden;\n min-width: min-content;\n\n /* Hover and press layer in currentcolor, so one rule fits every style. On the root, which a\n stretched button's inner element would not fill; under the Ripple wrapper, which comes later. */\n &::before {\n content: '';\n position: absolute;\n inset: 0;\n background: currentcolor;\n opacity: 0;\n transition: opacity 0.15s;\n pointer-events: none;\n }\n\n &-notAllowed {\n cursor: not-allowed;\n }\n\n /* Disabled only, not loading: a faded spinner would read as disabled rather than working. */\n &-disabled {\n opacity: 0.5;\n }\n\n /* Inside the edge: outside, a ButtonGroup neighbour with the same fill swallows the ring. A\n Filled button draws it in its text colour instead, as Pagination's active item does. */\n &:focus-visible {\n outline: 2px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -4px;\n }\n\n /* (hover: hover) because on touch a tap latches :hover until the next tap elsewhere. */\n @media (hover: hover) {\n &:hover:not(:disabled)::before {\n opacity: 0.08;\n }\n }\n\n /* After the hover rule, which it ties on specificity: a press is a hover too. */\n &:active:not(:disabled)::before {\n opacity: 0.16;\n }\n\n &-content {\n position: relative;\n white-space: break-spaces;\n }\n\n &-loading {\n visibility: hidden;\n\n &-container {\n position: absolute;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n }\n\n &-withIcon {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.5em;\n }\n\n /* A pill, because plain text right after the label reads as part of it. On the label's\n baseline rather than centred, so it follows the label wherever the font puts it. */\n &-shortcut {\n /* Not inline: Chrome trims only block containers, see the @supports below. */\n display: inline-block;\n\n /* Negative margins cancel the vertical padding in layout, so the button is no taller. */\n margin: -0.2em 0 -0.2em 0.6em;\n padding: 0.2em 0.6em;\n border-radius: 99999px;\n background: color-mix(in srgb, currentcolor 15%, transparent);\n font-size: 0.85em;\n\n /* Smaller text on a shared baseline sits lower. Raised so its capitals centre on the label's:\n (1 - 0.85) / 2 x ~0.72 cap height / 0.85. */\n vertical-align: 0.065em;\n white-space: nowrap;\n\n /* Even padding around the letters. Untrimmed, engines round the font's ascent and descent\n differently at each size, and the letters sat up to 2px off-centre. */\n @supports (text-box: trim-both cap alphabetic) {\n margin-block: -0.4em;\n padding-block: 0.4em;\n text-box: trim-both cap alphabetic;\n }\n\n /* A touch screen usually has no keyboard to press it on. */\n @media (pointer: coarse) {\n display: none;\n }\n }\n\n &-style {\n &-filled {\n background: var(--tone-fill);\n\n /* Must follow the shorthand above, which resets background-image. */\n background-image: var(--tone-gradient);\n color: var(--tone-contrast);\n\n /* After the base :focus-visible rule, which it ties on specificity. */\n &:focus-visible {\n outline-color: currentcolor;\n }\n }\n\n &-light {\n background: var(--tone-light);\n color: var(--tone-text);\n }\n\n &-outlined {\n color: var(--tone-text);\n border-color: var(--tone-outline);\n }\n\n &-plain {\n color: var(--tone-text);\n }\n }\n\n &-background {\n display: block;\n padding: 0.5em 0.75em;\n }\n\n /* Multiples of the theme's base, not em: an em on font-size resolves against the parent,\n so these would follow whatever the button sits in rather than the theme. */\n &-size {\n &-small {\n font-size: calc(var(--default-font-size) * 0.75);\n }\n\n &-medium {\n font-size: var(--default-font-size);\n }\n\n &-large {\n font-size: calc(var(--default-font-size) * 1.25);\n }\n\n /* Square, padding-free button for a lone icon; the caller sets the box size. */\n &-icon {\n display: inline-flex;\n\n /* Inherits, unlike the other sizes: a hardcoded px value would have to be out-specified\n from another stylesheet, a specificity tie decided only by bundle order. */\n font-size: inherit;\n\n .button-ripple,\n .button-background,\n .button-content {\n display: flex;\n flex: 1;\n align-items: center;\n justify-content: center;\n padding: 0;\n }\n }\n }\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"button.module.mjs","names":[],"sources":["../../../../package/common/button/button.module.pcss"],"sourcesContent":[".button {\n appearance: none;\n -webkit-appearance: none;\n box-sizing: border-box;\n font-size: var(--default-font-size);\n background: transparent;\n border: 1px solid transparent;\n padding: 0;\n font-family: var(--default-font);\n border-radius: var(--default-border-radius);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n position: relative;\n overflow: hidden;\n min-width: min-content;\n\n /* Hover and press layer in currentcolor, so one rule fits every style. On the root, which a\n stretched button's inner element would not fill; under the Ripple wrapper, which comes later. */\n &::before {\n content: '';\n position: absolute;\n inset: 0;\n background: currentcolor;\n opacity: 0;\n transition: opacity 0.15s;\n pointer-events: none;\n }\n\n /* What a <button> gets from the browser and an <a> does not. Before the sizes, so Icon's\n inline-flex wins the specificity tie. A grid, not inline-block: an inline-block that hides\n its overflow takes its bottom edge as its baseline and sits higher than a <button> beside it.\n align-content centres the label in a height set from outside, as a <button> does. */\n &-link {\n display: inline-grid;\n align-content: center;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-height: normal;\n text-align: center;\n text-decoration: none;\n text-indent: 0;\n text-shadow: none;\n text-transform: none;\n word-spacing: normal;\n }\n\n &-notAllowed {\n cursor: not-allowed;\n }\n\n /* Disabled only, not loading: a faded spinner would read as disabled rather than working. */\n &-disabled {\n opacity: 0.5;\n }\n\n /* Inside the edge: outside, a ButtonGroup neighbour with the same fill swallows the ring. A\n Filled button draws it in its text colour instead, as Pagination's active item does. */\n &:focus-visible {\n outline: 2px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -4px;\n }\n\n /* (hover: hover) because on touch a tap latches :hover until the next tap elsewhere. */\n @media (hover: hover) {\n &:hover:not(:disabled, [aria-disabled='true'])::before {\n opacity: 0.08;\n }\n }\n\n /* After the hover rule, which it ties on specificity: a press is a hover too. */\n &:active:not(:disabled, [aria-disabled='true'])::before {\n opacity: 0.16;\n }\n\n &-content {\n position: relative;\n white-space: break-spaces;\n }\n\n &-loading {\n visibility: hidden;\n\n &-container {\n position: absolute;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n }\n\n &-withIcon {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.5em;\n }\n\n /* A pill, because plain text right after the label reads as part of it. On the label's\n baseline rather than centred, so it follows the label wherever the font puts it. */\n &-shortcut {\n /* Not inline: Chrome trims only block containers, see the @supports below. */\n display: inline-block;\n\n /* Negative margins cancel the vertical padding in layout, so the button is no taller. */\n margin: -0.2em 0 -0.2em 0.6em;\n padding: 0.2em 0.6em;\n border-radius: 99999px;\n background: color-mix(in srgb, currentcolor 15%, transparent);\n font-size: 0.85em;\n\n /* Smaller text on a shared baseline sits lower. Raised so its capitals centre on the label's:\n (1 - 0.85) / 2 x ~0.72 cap height / 0.85. */\n vertical-align: 0.065em;\n white-space: nowrap;\n\n /* Even padding around the letters. Untrimmed, engines round the font's ascent and descent\n differently at each size, and the letters sat up to 2px off-centre. */\n @supports (text-box: trim-both cap alphabetic) {\n margin-block: -0.4em;\n padding-block: 0.4em;\n text-box: trim-both cap alphabetic;\n }\n\n /* A touch screen usually has no keyboard to press it on. */\n @media (pointer: coarse) {\n display: none;\n }\n }\n\n &-style {\n &-filled {\n background: var(--tone-fill);\n\n /* Must follow the shorthand above, which resets background-image. */\n background-image: var(--tone-gradient);\n color: var(--tone-contrast);\n\n /* After the base :focus-visible rule, which it ties on specificity. */\n &:focus-visible {\n outline-color: currentcolor;\n }\n }\n\n &-light {\n background: var(--tone-light);\n color: var(--tone-text);\n }\n\n &-outlined {\n color: var(--tone-text);\n border-color: var(--tone-outline);\n }\n\n &-plain {\n color: var(--tone-text);\n }\n }\n\n &-background {\n display: block;\n padding: 0.5em 0.75em;\n }\n\n /* Multiples of the theme's base, not em: an em on font-size resolves against the parent,\n so these would follow whatever the button sits in rather than the theme. */\n &-size {\n &-small {\n font-size: calc(var(--default-font-size) * 0.75);\n }\n\n &-medium {\n font-size: var(--default-font-size);\n }\n\n &-large {\n font-size: calc(var(--default-font-size) * 1.25);\n }\n\n /* Square, padding-free button for a lone icon; the caller sets the box size. */\n &-icon {\n display: inline-flex;\n\n /* Inherits, unlike the other sizes: a hardcoded px value would have to be out-specified\n from another stylesheet, a specificity tie decided only by bundle order. */\n font-size: inherit;\n\n .button-ripple,\n .button-background,\n .button-content {\n display: flex;\n flex: 1;\n align-items: center;\n justify-content: center;\n padding: 0;\n }\n }\n }\n}\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.types.mjs","names":[],"sources":["../../../../package/common/button/button.types.ts"],"sourcesContent":["import type React from 'react';\nimport type { CSSProperties } from 'react';\nimport type { IconContent } from '../types/icon.types';\nimport type { SlotClassNames, SlotStyles, DataProps } from '../types/slots.types';\nimport type { ColorValue } from '../themeProvider/color.types';\n\n/** How much the button stands out. Independent of its colour. */\nexport enum ButtonStyleType {\n /** Solid fill, for the one main action. */\n Filled = 'filled',\n /** A light tint of the colour behind text in the colour. */\n Light = 'light',\n /** Border and text in the colour, no fill. The default. */\n Outlined = 'outlined',\n /** Text in the colour, no fill and no border. For toolbar, icon and close buttons. */\n Plain = 'plain',\n}\n\nexport enum ButtonSize {\n Small = 'small',\n /** The default. */\n Medium = 'medium',\n Large = 'large',\n /** Square, padding-free button for a lone icon. Give it its own width/height. */\n Icon = 'icon',\n}\n\nexport enum ButtonType {\n Button = 'button',\n Reset = 'reset',\n Submit = 'submit',\n}\n\nexport type ButtonSlots = 'background' | 'content' | 'loadingContainer' | 'label' | 'shortcut';\n\
|
|
1
|
+
{"version":3,"file":"button.types.mjs","names":[],"sources":["../../../../package/common/button/button.types.ts"],"sourcesContent":["import type React from 'react';\nimport type { CSSProperties } from 'react';\nimport type { IconContent } from '../types/icon.types';\nimport type { SlotClassNames, SlotStyles, DataProps } from '../types/slots.types';\nimport type { ColorValue } from '../themeProvider/color.types';\n\n/** How much the button stands out. Independent of its colour. */\nexport enum ButtonStyleType {\n /** Solid fill, for the one main action. */\n Filled = 'filled',\n /** A light tint of the colour behind text in the colour. */\n Light = 'light',\n /** Border and text in the colour, no fill. The default. */\n Outlined = 'outlined',\n /** Text in the colour, no fill and no border. For toolbar, icon and close buttons. */\n Plain = 'plain',\n}\n\nexport enum ButtonSize {\n Small = 'small',\n /** The default. */\n Medium = 'medium',\n Large = 'large',\n /** Square, padding-free button for a lone icon. Give it its own width/height. */\n Icon = 'icon',\n}\n\nexport enum ButtonType {\n Button = 'button',\n Reset = 'reset',\n Submit = 'submit',\n}\n\nexport type ButtonSlots = 'background' | 'content' | 'loadingContainer' | 'label' | 'shortcut';\n\ninterface ButtonOwnProps extends DataProps {\n className?: string;\n style?: CSSProperties;\n /** Emphasis: `Filled`, `Light`, `Outlined` (default) or `Plain`. */\n styleType?: ButtonStyleType;\n /** A `ThemeColor` (default `Primary`) or any CSS colour, such as `'var(--brand)'`. */\n color?: ColorValue;\n size?: ButtonSize;\n children?: React.ReactNode;\n icon?: IconContent;\n /**\n * A shortcut drawn after the label, in `useHotkeys` spelling (`'mod+s'`): `⌘S` on Apple\n * platforms, `Ctrl+S` elsewhere. Drawn only; bind it with `useHotkeys`.\n */\n shortcut?: string;\n isLoading?: boolean;\n disabled?: boolean;\n title?: string;\n classNames?: SlotClassNames<ButtonSlots>;\n styles?: SlotStyles<ButtonSlots>;\n}\n\n/**\n * A Button without `href`, a `<button>`. `DataProps` supplies the `data-*` index signature, which\n * `ButtonHTMLAttributes` doesn't cover. `type` and `onClick` narrow their native counterparts.\n */\nexport interface ButtonElementProps\n extends\n ButtonOwnProps,\n Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, keyof ButtonOwnProps | 'type' | 'onClick'> {\n href?: undefined;\n type?: ButtonType;\n onClick?: (event: React.MouseEvent<HTMLButtonElement>) => Promise<void> | void;\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/** A Button with `href`, an `<a>`: `target`, `rel` and `download` are the native ones. */\nexport interface ButtonLinkProps\n extends\n ButtonOwnProps,\n Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, keyof ButtonOwnProps | 'type' | 'onClick' | 'href'> {\n /**\n * Renders the button as a link, so a long press, Ctrl/⌘-click and a middle click work as on any\n * other: `tel:`, `mailto:`, a maps URL, a page of the app. A plain click on a page of the app goes\n * through `ConfigProvider`'s `navigate` when it is set.\n */\n href: string;\n type?: undefined;\n onClick?: (event: React.MouseEvent<HTMLAnchorElement>) => Promise<void> | void;\n ref?: React.Ref<HTMLAnchorElement>;\n}\n\nexport type ButtonProps = ButtonElementProps | ButtonLinkProps;\n"],"mappings":"aAOA,IAAY,EAAL,SAAA,EAAA,OAEL,GAAA,OAAA,SAEA,EAAA,MAAA,QAEA,EAAA,SAAA,WAEA,EAAA,MAAA,SACF,EAAA,CAAA,CAAA,EAEY,EAAL,SAAA,EAAA,OACL,GAAA,MAAA,QAEA,EAAA,OAAA,SACA,EAAA,MAAA,QAEA,EAAA,KAAA,QACF,EAAA,CAAA,CAAA,EAEY,EAAL,SAAA,EAAA,OACL,GAAA,OAAA,SACA,EAAA,MAAA,QACA,EAAA,OAAA,UACF,EAAA,CAAA,CAAA"}
|