@ahrowe/ui 0.40.0 → 0.41.1
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/README.md +13 -13
- package/dist/cjs-types/common/ahroweProvider/ahroweProvider.d.ts +4 -0
- package/dist/cjs-types/common/ahroweProvider/ahroweProvider.types.d.ts +12 -0
- package/dist/cjs-types/common/ahroweProvider/index.d.ts +2 -0
- package/dist/cjs-types/common/bodyEnd/portalHostContext.d.ts +6 -0
- package/dist/cjs-types/common/errorBoundary/errorBoundary.types.d.ts +2 -2
- package/dist/cjs-types/common/hooks/useOverlay.d.ts +4 -1
- package/dist/cjs-types/common/popover/popover.types.d.ts +1 -1
- package/dist/cjs-types/common/themeProvider/index.d.ts +1 -1
- package/dist/cjs-types/common/themeProvider/theme.types.d.ts +8 -0
- package/dist/cjs-types/common/themeProvider/themeProvider.d.ts +7 -9
- package/dist/cjs-types/common/themeProvider/themeVariablesContext.d.ts +2 -2
- package/dist/cjs-types/common/toast/index.d.ts +1 -2
- package/dist/cjs-types/common/toast/toast.types.d.ts +4 -4
- package/dist/cjs-types/common/toast/toastHost.d.ts +4 -0
- package/dist/cjs-types/common/toast/toastService.d.ts +1 -1
- package/dist/cjs-types/index.d.ts +2 -1
- package/dist/esm/common/ahroweProvider/ahroweProvider.mjs +2 -0
- package/dist/esm/common/ahroweProvider/ahroweProvider.mjs.map +1 -0
- package/dist/esm/common/bodyEnd/bodyEnd.mjs +1 -1
- package/dist/esm/common/bodyEnd/bodyEnd.mjs.map +1 -1
- package/dist/esm/common/bodyEnd/portalHostContext.mjs +2 -0
- package/dist/esm/common/bodyEnd/portalHostContext.mjs.map +1 -0
- package/dist/esm/common/calendar/calendar.mjs +1 -1
- package/dist/esm/common/calendar/calendar.mjs.map +1 -1
- package/dist/esm/common/carousel/carousel.module.mjs.map +1 -1
- package/dist/esm/common/errorBoundary/errorBoundary.mjs +2 -2
- package/dist/esm/common/hooks/useAnchorTracking.mjs.map +1 -1
- package/dist/esm/common/hooks/useOverlay.mjs +1 -1
- package/dist/esm/common/hooks/useOverlay.mjs.map +1 -1
- package/dist/esm/common/modal/modal.mjs +1 -1
- package/dist/esm/common/modal/modal.mjs.map +1 -1
- package/dist/esm/common/otpInput/otpInput.mjs +1 -1
- package/dist/esm/common/otpInput/otpInput.mjs.map +1 -1
- package/dist/esm/common/popover/popover.mjs +1 -1
- package/dist/esm/common/popover/popover.mjs.map +1 -1
- package/dist/esm/common/popover/popover.types.mjs.map +1 -1
- package/dist/esm/common/scrollbarProvider/scrollbarProvider.module.mjs.map +1 -1
- package/dist/esm/common/themeProvider/themeProvider.mjs +1 -1
- package/dist/esm/common/themeProvider/themeProvider.mjs.map +1 -1
- package/dist/esm/common/themeProvider/themeVariablesContext.mjs.map +1 -1
- package/dist/esm/common/timer/timer.mjs +1 -1
- package/dist/esm/common/timer/timer.mjs.map +1 -1
- package/dist/esm/common/toast/toastHost.mjs +2 -0
- package/dist/esm/common/toast/toastHost.mjs.map +1 -0
- package/dist/esm/common/toast/toastService.mjs +1 -1
- package/dist/esm/common/toast/toastService.mjs.map +1 -1
- package/dist/esm/common/tooltip/tooltip.module.mjs.map +1 -1
- package/dist/esm/common/virtualList/virtualList.mjs +1 -1
- package/dist/esm/common/virtualList/virtualList.mjs.map +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/mcp.js +2 -2
- package/dist/style.css +1 -1
- package/dist/types/common/ahroweProvider/ahroweProvider.d.ts +4 -0
- package/dist/types/common/ahroweProvider/ahroweProvider.types.d.ts +12 -0
- package/dist/types/common/ahroweProvider/index.d.ts +2 -0
- package/dist/types/common/bodyEnd/portalHostContext.d.ts +6 -0
- package/dist/types/common/errorBoundary/errorBoundary.types.d.ts +2 -2
- package/dist/types/common/hooks/useOverlay.d.ts +4 -1
- package/dist/types/common/popover/popover.types.d.ts +1 -1
- package/dist/types/common/themeProvider/index.d.ts +1 -1
- package/dist/types/common/themeProvider/theme.types.d.ts +8 -0
- package/dist/types/common/themeProvider/themeProvider.d.ts +7 -9
- package/dist/types/common/themeProvider/themeVariablesContext.d.ts +2 -2
- package/dist/types/common/toast/index.d.ts +1 -2
- package/dist/types/common/toast/toast.types.d.ts +4 -4
- package/dist/types/common/toast/toastHost.d.ts +4 -0
- package/dist/types/common/toast/toastService.d.ts +1 -1
- package/dist/types/index.d.ts +2 -1
- package/docs/AhroweProvider.md +90 -0
- package/docs/BodyEnd.md +5 -12
- package/docs/Breadcrumb.md +0 -2
- package/docs/CLAUDE.md +14 -11
- package/docs/CommandPalette.md +0 -2
- package/docs/ConfigProvider.md +11 -8
- package/docs/ConfirmModal.md +0 -2
- package/docs/ErrorBoundary.md +5 -3
- package/docs/FormValidatorGroup.md +1 -1
- package/docs/KlipyPicker.md +1 -1
- package/docs/Menu.md +0 -2
- package/docs/Modal.md +1 -3
- package/docs/MultiSelect.md +0 -2
- package/docs/OtpInput.md +0 -2
- package/docs/Popover.md +2 -4
- package/docs/RadioGroup.md +1 -1
- package/docs/ScrollbarProvider.md +4 -0
- package/docs/SplitButton.md +0 -2
- package/docs/ThemeProvider.md +13 -10
- package/docs/Toast.md +18 -17
- package/docs/Tooltip.md +1 -1
- package/docs/VirtualList.md +0 -2
- package/package.json +1 -1
- package/dist/cjs-types/common/toast/toastProvider.d.ts +0 -8
- package/dist/esm/common/toast/toastProvider.mjs +0 -2
- package/dist/esm/common/toast/toastProvider.mjs.map +0 -1
- package/dist/types/common/toast/toastProvider.d.ts +0 -8
package/README.md
CHANGED
|
@@ -14,22 +14,21 @@ npm install @ahrowe/ui
|
|
|
14
14
|
|
|
15
15
|
```tsx
|
|
16
16
|
import '@ahrowe/ui/style.css';
|
|
17
|
-
import {
|
|
17
|
+
import { AhroweProvider } from '@ahrowe/ui';
|
|
18
18
|
|
|
19
|
-
function
|
|
19
|
+
function Root() {
|
|
20
20
|
return (
|
|
21
|
-
<
|
|
22
|
-
App
|
|
23
|
-
|
|
24
|
-
<div id="bodyEnd" />
|
|
25
|
-
</ThemeProvider>
|
|
21
|
+
<AhroweProvider>
|
|
22
|
+
<App />
|
|
23
|
+
</AhroweProvider>
|
|
26
24
|
);
|
|
27
25
|
}
|
|
28
26
|
```
|
|
29
27
|
|
|
30
|
-
`
|
|
31
|
-
they render unstyled.
|
|
32
|
-
|
|
28
|
+
`AhroweProvider` is required, once, at the root. It injects the CSS variables the components paint
|
|
29
|
+
with, so without it they render unstyled. It also renders the element Modal, Tooltip, Popover, Toast
|
|
30
|
+
and Menu portal into, and shows toasts. Two themes ship, `defaultTheme` (dark) and `lightTheme`.
|
|
31
|
+
Pass your own to `themes` to replace them. See [docs/AhroweProvider.md](docs/AhroweProvider.md).
|
|
33
32
|
|
|
34
33
|
## Before you install
|
|
35
34
|
|
|
@@ -118,12 +117,13 @@ runs where `@ahrowe/ui` is installed.
|
|
|
118
117
|
| `Accordion` | Collapsible sections for progressive disclosure — FAQs, settings panels, long-form content that users expand on demand |
|
|
119
118
|
| `ActionButtons` | Standardised Submit / Cancel button pair for forms, dialogs, and drawer footers |
|
|
120
119
|
| `ActionIcon` | Clickable icon button without a label — toolbar actions, table row actions, icon-only controls |
|
|
120
|
+
| `AhroweProvider` | The one wrapper an app needs, at its root: it applies the theme, sets default props for every component, shows toasts and renders the element that Modal, Tooltip, Popover, Menu and Toast portal into |
|
|
121
121
|
| `Alert`, `AlertStyleType` | A persistent inline status message, used for form-level errors, page/section banners, empty-config warnings, and "here's what changed" notices |
|
|
122
122
|
| `AnimatedLogo` | A self-drawing SVG mark for splash/loading screens, empty states, or anywhere a brand or icon mark should feel alive rather than static |
|
|
123
123
|
| `AnimatedText`, `AnimatedTextVariant`, `AnimatedTextUnit` | Decorative text entrance or emphasis effect, for hero headlines, empty-state messages, "premium"/"new" callouts, loading/decoding states |
|
|
124
124
|
| `Avatar`, `AvatarGroup` | User or entity image — profile pictures, comment authors, member lists |
|
|
125
125
|
| `Badge`, `BadgeStyleType`, `BadgePosition` | Small count or status indicator — unread counts, notification dots, "new" markers |
|
|
126
|
-
| `BodyEnd` | Renders children
|
|
126
|
+
| `BodyEnd` | Renders children through a React portal, to the end of the app |
|
|
127
127
|
| `Breadcrumb` | A trail showing the current page's position within a hierarchy, with links back to each ancestor, such as a file browser path, a category/subcategory drill-down, or a multi-level settings page |
|
|
128
128
|
| `Button`, `ButtonStyleType`, `ButtonSize`, `ButtonType` | Primary interactive action trigger |
|
|
129
129
|
| `ButtonGroup`, `ButtonGroupOrientation` | Joins a row (or column) of buttons into a single seamless control, merging their borders and rounding only the outer corners |
|
|
@@ -186,12 +186,12 @@ runs where `@ahrowe/ui` is installed.
|
|
|
186
186
|
| `Switch` | Sliding boolean toggle — settings panels, feature flags, preference lists |
|
|
187
187
|
| `TabHeader` | A horizontal row of tabs to switch between sibling views or sections — settings panels, dashboards, detail pages |
|
|
188
188
|
| `Textarea` | Multiline text input — comments, descriptions, notes, code snippets, long-form content |
|
|
189
|
-
| `ThemeProvider` |
|
|
189
|
+
| `ThemeProvider` | Applies a theme to its subtree, injecting its CSS custom properties as inline styles so every component inside can use them |
|
|
190
190
|
| `Tilt`, `TiltGroup` | Wrap content that should tilt toward the cursor in 3D |
|
|
191
191
|
| `TimeInput` | Time-of-day selection — appointment times, opening hours, shift starts, reminders |
|
|
192
192
|
| `Timeline`, `TimelineAlignment`, `TimelineItemStatus` | A vertical list of dated events, connected by a line, for audit logs, activity feeds, order status history, and deploy logs |
|
|
193
193
|
| `Timer` | A countdown timer with a depleting ring, add/remove-time and pause/cancel controls, and an imperative ref API |
|
|
194
|
-
| `
|
|
194
|
+
| `showToast` | Non-blocking notifications — save confirmations, error alerts, info messages, warnings |
|
|
195
195
|
| `Tooltip` | Contextual help, info icons, field explanations, error pointers |
|
|
196
196
|
| `Tree`, `flattenTree` | A hierarchical list the user can expand, collapse and pick from: categories, nested locations, folder structures, org charts |
|
|
197
197
|
| `VirtualList` | Efficiently render large lists or tables — thousands of rows rendered with a virtualised scroll window so only visible rows are in the DOM |
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
import { AhroweProviderProps } from './ahroweProvider.types.js';
|
|
3
|
+
declare function AhroweProvider({ children, themes, currentThemeId, defaultProps, presentation, toast, scrollbars, }: AhroweProviderProps): ReactElement;
|
|
4
|
+
export default AhroweProvider;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ConfigProviderProps } from '../configProvider/configProvider.types.js';
|
|
2
|
+
import { ThemeProviderProps } from '../themeProvider/theme.types.js';
|
|
3
|
+
import { ToastContextConfig } from '../toast/toast.types.js';
|
|
4
|
+
export interface AhroweProviderProps extends ConfigProviderProps, ThemeProviderProps {
|
|
5
|
+
/** Defaults for every toast `showToast` shows. Read by the outermost provider only. */
|
|
6
|
+
toast?: ToastContextConfig;
|
|
7
|
+
/**
|
|
8
|
+
* Styles every scrollbar inside the app, portalled panels included, in the theme's colours.
|
|
9
|
+
* Defaults to `true`. Read by the outermost provider only.
|
|
10
|
+
*/
|
|
11
|
+
scrollbars?: boolean;
|
|
12
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The element `AhroweProvider` portals into. `undefined` means there is no provider, so BodyEnd
|
|
3
|
+
* falls back to `#bodyEnd`; `null` means a provider that cannot portal yet, on the server and
|
|
4
|
+
* while hydrating, and must not be reported missing.
|
|
5
|
+
*/
|
|
6
|
+
export declare const PortalHostContext: import('react').Context<HTMLElement | null | undefined>;
|
|
@@ -19,9 +19,9 @@ export interface ErrorBoundaryLabels {
|
|
|
19
19
|
copied?: string;
|
|
20
20
|
/** Reset button label. Default: "Try again". */
|
|
21
21
|
reset?: string;
|
|
22
|
-
/** Toast shown after a successful copy
|
|
22
|
+
/** Toast shown after a successful copy, when the boundary is inside an `AhroweProvider`. */
|
|
23
23
|
copySuccessToast?: string;
|
|
24
|
-
/** Toast shown if the copy fails
|
|
24
|
+
/** Toast shown if the copy fails, when the boundary is inside an `AhroweProvider`. */
|
|
25
25
|
copyErrorToast?: string;
|
|
26
26
|
}
|
|
27
27
|
export interface ErrorBoundaryProps extends Omit<HtmlProps, 'title' | 'onError'> {
|
|
@@ -6,7 +6,10 @@ export interface UseOverlayArgs {
|
|
|
6
6
|
containerRef: RefObject<HTMLElement | null>;
|
|
7
7
|
/** Called when Escape is pressed while this is the topmost overlay. Omit to not handle Escape. */
|
|
8
8
|
onEscape?: () => void;
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Keep Tab inside the panel and restore focus to the previously focused element on close, unless
|
|
11
|
+
* an overlay opened after this one is still open.
|
|
12
|
+
*/
|
|
10
13
|
trapFocus?: boolean;
|
|
11
14
|
/** Prevent the page behind the overlay from scrolling. */
|
|
12
15
|
lockScroll?: boolean;
|
|
@@ -53,7 +53,7 @@ export interface PopoverProps extends Omit<HtmlProps, 'content'> {
|
|
|
53
53
|
offset?: number;
|
|
54
54
|
/** Render the arrow pointing at the trigger (default `true`). */
|
|
55
55
|
withArrow?: boolean;
|
|
56
|
-
/** Close
|
|
56
|
+
/** Close on a click outside, or when focus leaves the trigger and panel (default `true`). */
|
|
57
57
|
closeOnOutsideClick?: boolean;
|
|
58
58
|
/** Close when pressing Escape (default `true`). */
|
|
59
59
|
closeOnEscape?: boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { default } from './themeProvider.js';
|
|
2
2
|
export { defaultTheme } from './defaultTheme.js';
|
|
3
3
|
export { lightTheme } from './lightTheme.js';
|
|
4
|
-
export type { Theme, ThemeVariables } from './theme.types.js';
|
|
4
|
+
export type { Theme, ThemeProviderProps, ThemeVariables } from './theme.types.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
1
2
|
/**
|
|
2
3
|
* All CSS custom properties used by the component library.
|
|
3
4
|
* Every key is optional so a theme only needs to declare the variables it overrides.
|
|
@@ -278,3 +279,10 @@ export interface Theme {
|
|
|
278
279
|
baseThemeId?: string;
|
|
279
280
|
variables: ThemeVariables;
|
|
280
281
|
}
|
|
282
|
+
export interface ThemeProviderProps {
|
|
283
|
+
children: ReactNode;
|
|
284
|
+
/** The themes to choose from. Defaults to `[defaultTheme]`. */
|
|
285
|
+
themes?: Theme[];
|
|
286
|
+
/** The `id` of the theme to apply, from `themes`. */
|
|
287
|
+
currentThemeId?: string;
|
|
288
|
+
}
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { default as React, Component } from 'react';
|
|
2
|
-
import { Theme } from './theme.types.js';
|
|
3
|
-
|
|
4
|
-
children: React.ReactNode;
|
|
5
|
-
themes?: Theme[];
|
|
6
|
-
currentThemeId?: string;
|
|
7
|
-
}
|
|
2
|
+
import { Theme, ThemeProviderProps } from './theme.types.js';
|
|
3
|
+
import { ThemeVariablesContext } from './themeVariablesContext.js';
|
|
8
4
|
interface State {
|
|
9
5
|
resolvedVariables: Record<string, string>;
|
|
10
6
|
colorFadeActive: boolean;
|
|
@@ -15,15 +11,17 @@ interface State {
|
|
|
15
11
|
currentThemeId: string | undefined;
|
|
16
12
|
};
|
|
17
13
|
}
|
|
18
|
-
declare class ThemeProvider extends Component<
|
|
14
|
+
declare class ThemeProvider extends Component<ThemeProviderProps, State> {
|
|
15
|
+
static contextType: React.Context<Record<string, string> | null>;
|
|
16
|
+
context: React.ContextType<typeof ThemeVariablesContext>;
|
|
19
17
|
state: State;
|
|
20
18
|
private colorFadeTimeout;
|
|
21
19
|
private wrapperRef;
|
|
22
20
|
private themeColorMeta;
|
|
23
21
|
private createdThemeColorMeta;
|
|
24
|
-
static getDerivedStateFromProps(props:
|
|
22
|
+
static getDerivedStateFromProps(props: ThemeProviderProps, state: State): Partial<State> | null;
|
|
25
23
|
componentDidMount(): void;
|
|
26
|
-
componentDidUpdate(prevProps:
|
|
24
|
+
componentDidUpdate(prevProps: ThemeProviderProps, prevState: State): void;
|
|
27
25
|
componentWillUnmount(): void;
|
|
28
26
|
/**
|
|
29
27
|
* Replaces the former react-helmet usage: keep a single
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The nearest ThemeProvider's resolved variables, for content that renders outside its DOM
|
|
3
3
|
* subtree. Theme variables reach a component by CSS inheritance, which a portal breaks: a
|
|
4
|
-
* `BodyEnd` child lands wherever
|
|
5
|
-
*
|
|
4
|
+
* `BodyEnd` child lands wherever the portal target is, so it picks up whatever provider wraps
|
|
5
|
+
* *that* node rather than the one it was written under.
|
|
6
6
|
*
|
|
7
7
|
* Deliberately its own module rather than a member of `themeProvider.tsx`: BodyEnd needs the
|
|
8
8
|
* context and nothing else, and importing the component for it would pull the whole
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
export { default } from './toastProvider.js';
|
|
2
1
|
export { showToast } from './toastService.js';
|
|
3
|
-
export type {
|
|
2
|
+
export type { ToastContextConfig, ToastLabels, ToastOptions, ToastType, ToastPosition, ToastSlots, } from './toast.types.js';
|
|
@@ -19,17 +19,17 @@ export interface ToastContextConfig {
|
|
|
19
19
|
/** Pause the auto-dismiss timer while the toast is hovered or focused. */
|
|
20
20
|
pauseOnHover?: boolean;
|
|
21
21
|
/**
|
|
22
|
-
* Overrides the text a toast renders of its own. Set
|
|
23
|
-
* the whole app, or per toast through `showToast` options.
|
|
22
|
+
* Overrides the text a toast renders of its own. Set in `AhroweProvider`'s
|
|
23
|
+
* `toast` for the whole app, or per toast through `showToast` options.
|
|
24
24
|
*/
|
|
25
25
|
labels?: ToastLabels;
|
|
26
26
|
}
|
|
27
|
-
export interface ToastOptions extends ToastContextConfig {
|
|
27
|
+
export interface ToastOptions extends Omit<ToastContextConfig, 'allowStacking'> {
|
|
28
28
|
type?: ToastType;
|
|
29
29
|
classNames?: SlotClassNames<ToastSlots>;
|
|
30
30
|
styles?: SlotStyles<ToastSlots>;
|
|
31
31
|
}
|
|
32
|
-
export interface
|
|
32
|
+
export interface ToastHostProps extends ToastContextConfig {
|
|
33
33
|
children: ReactNode;
|
|
34
34
|
}
|
|
35
35
|
export interface ToastItem extends Pick<ToastOptions, 'labels'> {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { ToastHostProps } from './toast.types.js';
|
|
3
|
+
declare function ToastHost({ children, position, duration, showRemainingTime, showCloseButton, allowStacking, pauseOnHover, labels, }: ToastHostProps): React.JSX.Element;
|
|
4
|
+
export default ToastHost;
|
|
@@ -2,6 +2,6 @@ import { ReactNode } from 'react';
|
|
|
2
2
|
import { ToastOptions } from './toast.types.js';
|
|
3
3
|
type ShowToastFn = (content: ReactNode, options?: ToastOptions) => void;
|
|
4
4
|
export declare function _registerToastHandler(fn: ShowToastFn): void;
|
|
5
|
-
export declare function _unregisterToastHandler(): void;
|
|
5
|
+
export declare function _unregisterToastHandler(fn: ShowToastFn): void;
|
|
6
6
|
export declare function showToast(content: ReactNode, options?: ToastOptions): void;
|
|
7
7
|
export {};
|
|
@@ -3,6 +3,8 @@ export * from './common/accordion/index.js';
|
|
|
3
3
|
export * from './common/actionButtons/index.js';
|
|
4
4
|
export { default as ActionIcon } from './common/actionIcon/index.js';
|
|
5
5
|
export * from './common/actionIcon/index.js';
|
|
6
|
+
export { default as AhroweProvider } from './common/ahroweProvider/index.js';
|
|
7
|
+
export * from './common/ahroweProvider/index.js';
|
|
6
8
|
export { default as Alert } from './common/alert/index.js';
|
|
7
9
|
export * from './common/alert/index.js';
|
|
8
10
|
export { default as AnimatedLogo } from './common/animatedLogo/index.js';
|
|
@@ -144,7 +146,6 @@ export { default as Timeline } from './common/timeline/index.js';
|
|
|
144
146
|
export * from './common/timeline/index.js';
|
|
145
147
|
export { default as Timer } from './common/timer/index.js';
|
|
146
148
|
export * from './common/timer/index.js';
|
|
147
|
-
export { default as ToastProvider } from './common/toast/index.js';
|
|
148
149
|
export * from './common/toast/index.js';
|
|
149
150
|
export { default as Tooltip } from './common/tooltip/index.js';
|
|
150
151
|
export * from './common/tooltip/index.js';
|
|
@@ -0,0 +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,toast:y,scrollbars:b}){let x=o(r)!==void 0,S=u(p,()=>!0,()=>!1),[C]=l(()=>typeof document>`u`?null:document.createElement(`div`)),w=a(e=>{let t=e?.parentElement?.lastElementChild;t&&C&&C.parentNode!==t&&t.appendChild(C)},[C]),T=!x&&b!==!1;c(()=>{if(!T||!C)return;let e=getComputedStyle(C).getPropertyValue(`--background-accent-light`).trim(),t=document.documentElement.style;return t.setProperty(`--ahrowe-page-scrollbar-thumb`,e),()=>{t.removeProperty(`--ahrowe-page-scrollbar-thumb`)}},[T,C,h,g]);let E=x&&(y!==void 0||b!==void 0);s(()=>{E&&console.warn("[AhroweProvider] `toast` and `scrollbars` are read by the outermost AhroweProvider only. Set them there.")},[E]);let D=f(r.Provider,{value:S?C:null,children:[d(`template`,{ref:w}),d(i,{...y,children:m}),d(`div`,{})]});return d(e,{defaultProps:_,presentation:v,children:d(n,{themes:h,currentThemeId:g,children:x?m:b===!1?D:d(t,{children:D})})})}export{m as default};
|
|
2
|
+
//# sourceMappingURL=ahroweProvider.mjs.map
|
|
@@ -0,0 +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,eACxD,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{ThemeVariablesContext as e}from"../themeProvider/themeVariablesContext.mjs";import{useContext as
|
|
1
|
+
"use client";import{ThemeVariablesContext as e}from"../themeProvider/themeVariablesContext.mjs";import t from"../themeProvider/theme.module.mjs";import{PortalHostContext as n}from"./portalHostContext.mjs";import{useContext as r}from"react";import{jsx as i}from"react/jsx-runtime";import{createPortal as a}from"react-dom";function o({children:o=null}){let s=r(e),c=r(n),l=c===void 0?document.getElementById(`bodyEnd`):c;return l?a(s?i(`div`,{className:t.themeRoot,style:{display:`contents`,...s},children:o}):o,l):null}export{o as default};
|
|
2
2
|
//# sourceMappingURL=bodyEnd.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bodyEnd.mjs","names":[],"sources":["../../../../package/common/bodyEnd/bodyEnd.tsx"],"sourcesContent":["import { createPortal } from 'react-dom';\nimport { useContext, type CSSProperties, type ReactElement } from 'react';\n\nimport { ThemeVariablesContext } from '../themeProvider/themeVariablesContext';\nimport type { BodyEndProps } from './bodyEnd.types';\n\n// Module-scoped ambient typing for the bundler-replaced NODE_ENV guard below.\n// Browsers don't define `process`, so the value is read defensively at runtime;\n// bundlers (Vite/webpack) still statically replace the `process.env.NODE_ENV`\n// token. Declared at module scope (not globally), it isn't emitted into the\n// .d.ts and can't clash with a consumer's @types/node.\ndeclare const process: { env: { NODE_ENV?: string } };\n\nfunction BodyEnd({ children = null }: BodyEndProps): ReactElement | null {\n const themeVariables = useContext(ThemeVariablesContext);\n const target = document.getElementById('bodyEnd');\n if (!target) {\n if (typeof process !== 'undefined' && process.env.NODE_ENV !== 'production') {\n console.error('[BodyEnd]
|
|
1
|
+
{"version":3,"file":"bodyEnd.mjs","names":[],"sources":["../../../../package/common/bodyEnd/bodyEnd.tsx"],"sourcesContent":["import { createPortal } from 'react-dom';\nimport { useContext, type CSSProperties, type ReactElement } from 'react';\n\nimport { ThemeVariablesContext } from '../themeProvider/themeVariablesContext';\nimport themeStyles from '../themeProvider/theme.module.pcss';\nimport { PortalHostContext } from './portalHostContext';\nimport type { BodyEndProps } from './bodyEnd.types';\n\n// Module-scoped ambient typing for the bundler-replaced NODE_ENV guard below.\n// Browsers don't define `process`, so the value is read defensively at runtime;\n// bundlers (Vite/webpack) still statically replace the `process.env.NODE_ENV`\n// token. Declared at module scope (not globally), it isn't emitted into the\n// .d.ts and can't clash with a consumer's @types/node.\ndeclare const process: { env: { NODE_ENV?: string } };\n\nfunction BodyEnd({ children = null }: BodyEndProps): ReactElement | null {\n const themeVariables = useContext(ThemeVariablesContext);\n const host = useContext(PortalHostContext);\n const target = host === undefined ? document.getElementById('bodyEnd') : host;\n if (!target) {\n if (host === undefined && typeof process !== 'undefined' && process.env.NODE_ENV !== 'production') {\n console.error('[BodyEnd] Nowhere to render. Wrap your app in <AhroweProvider>, or add <div id=\"bodyEnd\"></div>.');\n }\n return null;\n }\n /*\n * Carry the nearest theme through the portal. `display: contents` is what makes this safe:\n * the wrapper generates no box, so positioning, stacking and clipping are untouched —\n * escaping clipping ancestors is the whole point of this component — while custom\n * properties still inherit, because inheritance follows the element tree, not the box tree.\n *\n * Variables this theme leaves unset still fall through to whatever wraps the target, which\n * is the same cascade the children would have seen rendered in place. `themeRoot` sets the\n * base typography again, which the target would otherwise pass down from its own theme.\n */\n if (!themeVariables) return createPortal(children, target);\n return createPortal(\n <div className={themeStyles.themeRoot} style={{ display: 'contents', ...themeVariables } as CSSProperties}>\n {children}\n </div>,\n target,\n );\n}\n\nexport default BodyEnd;\n"],"mappings":"iUAeA,SAAS,EAAQ,CAAE,WAAW,MAA2C,CACvE,IAAM,EAAiB,EAAW,CAAqB,EACjD,EAAO,EAAW,CAAiB,EACnC,EAAS,IAAS,IAAA,GAAY,SAAS,eAAe,SAAS,EAAI,EAkBzE,OAjBK,EAiBE,EADF,EAEH,EAAC,MAAD,CAAK,UAAW,EAAY,UAAW,MAAO,CAAE,QAAS,WAAY,GAAG,CAAe,EACpF,UACE,CAAA,EAJkC,EAKvC,CALuD,EAZhD,IAmBX"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portalHostContext.mjs","names":[],"sources":["../../../../package/common/bodyEnd/portalHostContext.ts"],"sourcesContent":["import { createContext } from 'react';\n\n/**\n * The element `AhroweProvider` portals into. `undefined` means there is no provider, so BodyEnd\n * falls back to `#bodyEnd`; `null` means a provider that cannot portal yet, on the server and\n * while hydrating, and must not be reported missing.\n */\nexport const PortalHostContext = createContext<HTMLElement | null | undefined>(undefined);\n"],"mappings":"mDAOA,IAAa,EAAoB,EAA8C,IAAA,EAAS"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{mergeRefs as n}from"../utils/mergeRefs.mjs";import{asDay as r,dateToDay as i,dayToDate as a}from"../utils/day.mjs";import{CalendarView as o}from"./calendar.types.mjs";import{addDays as
|
|
1
|
+
"use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{mergeRefs as n}from"../utils/mergeRefs.mjs";import{asDay as r,dateToDay as i,dayToDate as a}from"../utils/day.mjs";import{CalendarView as o}from"./calendar.types.mjs";import{addDays as ee,dayDiff as s,hiddenDaySet as te,periodRange as ne,rangeWithNeighbours as re,shownDay as ie,shownDays as c,startOfDay as l,stepDate as u}from"./dates.mjs";import{formatPeriodTitle as d,localeWeekStart as ae}from"./format.mjs";import oe from"./components/agendaList/agendaList.mjs";import se from"./components/calendarHeader/calendarHeader.mjs";import ce from"./components/compactMonth/compactMonth.mjs";import le from"./components/monthView/monthView.mjs";import ue from"./components/timeGrid/timeGrid.mjs";import de from"./calendar.module.mjs";import{useEffect as f,useLayoutEffect as p,useRef as m,useState as h}from"react";import fe from"classnames";import{jsx as g,jsxs as pe}from"react/jsx-runtime";var me=[o.Month,o.Week,o.Day,o.Agenda],he=600,ge={previous:`Previous`,next:`Next`,today:`Today`,viewSwitcher:`Calendar view`,month:`Month`,week:`Week`,day:`Day`,agenda:`Agenda`,allDay:`All day`,more:e=>`+${e} more`,eventCount:e=>e===1?`1 event`:`${e} events`,noEvents:`No events`,newEvent:`New event`};function _(_){let{events:v,date:y,defaultDate:_e,onDateChange:ve,view:b,defaultView:ye=o.Month,onViewChange:be,views:x=me,locale:S,weekStartsOn:xe,slotMinutes:Se=15,hiddenDays:Ce,scrollToHour:we,onRangeChange:C,onEventClick:w,onEventChange:T,onSlotCreate:E,renderEvent:D,labels:O,ref:k,className:A,style:Te,classNames:j,styles:M,...Ee}=e(`Calendar`,_),N=t(`Calendar`,O,ge),P=xe??ae(S),F=te(Ce),I=m(null),De=m(null),[L,Oe]=h(0),[ke,Ae]=h(1),[je,Me]=h(()=>{let e=r(_e);return e?a(e):l(new Date)}),R=r(y),z=ie(R?a(R):je,F),[Ne,Pe]=h(ye),B=b??Ne;p(()=>{let e=I.current;if(!e)return;let t=()=>Oe(e.getBoundingClientRect().width);t();let n=new ResizeObserver(t);return n.observe(e),()=>n.disconnect()},[]);let V=L>0&&L<he,H=re(B,z,P,F),U=`${i(H.start)}/${i(H.end)}`,W=m({range:H,onRangeChange:C}),G=m(null);p(()=>{W.current={range:H,onRangeChange:C}}),f(()=>{G.current!==U&&(G.current=U,W.current.onRangeChange?.(W.current.range))},[U]);function K(e){let t=l(e);i(t)!==i(z)&&(Ae(t>z?1:-1),y===void 0&&Me(t),ve?.(i(t)))}function q(e){e!==B&&(b===void 0&&Pe(e),be?.(e))}let[J,Y]=h(()=>new Map);function Fe(e){let t=T?.(e);if(typeof t?.then!=`function`)return;let n=String(e.event.id),r={start:e.start,end:e.end};Y(e=>new Map(e).set(n,r));let i=()=>Y(e=>{if(e.get(n)!==r)return e;let t=new Map(e);return t.delete(n),t});t.then(i,i)}let X=J.size===0?v:v.map(e=>{let t=J.get(String(e.id));return t?{...e,...t}:e}),Z={events:X,date:z,view:B,locale:S,weekStartsOn:P,slotMinutes:Se,hidden:F,scrollToHour:we,text:N,onDateChange:K,onOpenDay:x.includes(o.Day)?e=>{K(e),q(o.Day)}:void 0,onEventClick:w,onEventChange:T&&Fe,onSlotCreate:E,renderEvent:D,classNames:j,slotStyles:M},Q=ne(B,z,P),$=c(Q.start,s(Q.start,Q.end),F),Ie={start:$[0],end:ee($[$.length-1],1)};function Le(){switch(B){case o.Week:case o.Day:return g(ue,{...Z,zoomRef:De});case o.Agenda:return g(oe,{events:X,start:Q.start,days:s(Q.start,Q.end),hidden:F,scrollTo:z,showDayHeaders:!0,locale:S,text:N,onEventClick:w,renderEvent:D,classNames:j,slotStyles:M});default:return g(V?ce:le,{...Z})}}return pe(`div`,{...Ee,ref:n(I,k),className:fe(de.calendar,A,j?.root),style:{...Te,...M?.root},children:[g(se,{className:j?.header,style:M?.header,compact:V,title:d(B,z,Ie,S),direction:ke,view:B,views:x,text:N,onStep:e=>K(u(B,z,e,F)),onToday:()=>K(new Date),onViewChange:q}),Le()]})}export{_ as default};
|
|
2
2
|
//# sourceMappingURL=calendar.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calendar.mjs","names":[],"sources":["../../../../package/common/calendar/calendar.tsx"],"sourcesContent":["import { useEffect, useLayoutEffect, useRef, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { mergeRefs } from '../utils/mergeRefs';\nimport { asDay, dayToDate } from '../utils/day';\nimport {\n CalendarView,\n type CalendarEvent,\n type CalendarEventChange,\n type CalendarLabels,\n type CalendarProps,\n} from './calendar.types';\nimport {\n dayDiff,\n dateToDay,\n addDays,\n hiddenDaySet,\n periodRange,\n rangeWithNeighbours,\n shownDay,\n shownDays,\n startOfDay,\n stepDate,\n} from './dates';\nimport { formatPeriodTitle, localeWeekStart } from './format';\nimport AgendaList from './components/agendaList';\nimport CalendarHeader from './components/calendarHeader';\nimport CompactMonth from './components/compactMonth';\nimport MonthView from './components/monthView';\nimport TimeGrid from './components/timeGrid';\nimport type { TimeGridZoom } from './components/timeGrid/timeGrid.types';\nimport type { CalendarViewProps } from './view.types';\n\nimport styles from './calendar.module.pcss';\n\nconst ALL_VIEWS = [CalendarView.Month, CalendarView.Week, CalendarView.Day, CalendarView.Agenda];\n\n/** Below this width the month view folds into the compact month with a day list. */\nconst COMPACT_WIDTH = 600;\n\nconst DEFAULT_LABELS: Required<CalendarLabels> = {\n previous: 'Previous',\n next: 'Next',\n today: 'Today',\n viewSwitcher: 'Calendar view',\n month: 'Month',\n week: 'Week',\n day: 'Day',\n agenda: 'Agenda',\n allDay: 'All day',\n more: (count) => `+${count} more`,\n eventCount: (count) => (count === 1 ? '1 event' : `${count} events`),\n noEvents: 'No events',\n newEvent: 'New event',\n};\n\nfunction Calendar<E extends CalendarEvent = CalendarEvent>(props: CalendarProps<E>): ReactElement {\n const {\n events,\n date: controlledDate,\n defaultDate,\n onDateChange,\n view: controlledView,\n defaultView = CalendarView.Month,\n onViewChange,\n views = ALL_VIEWS,\n locale,\n weekStartsOn: weekStartsOnProp,\n slotMinutes = 15,\n hiddenDays,\n scrollToHour,\n onRangeChange,\n onEventClick,\n onEventChange,\n onSlotCreate,\n renderEvent,\n labels,\n ref,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('Calendar', props);\n const text = useLabels('Calendar', labels, DEFAULT_LABELS);\n const weekStartsOn = weekStartsOnProp ?? localeWeekStart(locale);\n const hidden = hiddenDaySet(hiddenDays);\n\n const rootRef = useRef<HTMLDivElement>(null);\n const zoomRef = useRef<TimeGridZoom | null>(null);\n const [width, setWidth] = useState(0);\n const [direction, setDirection] = useState<1 | -1>(1);\n\n // `date` is a day, the same wherever the calendar is opened, and read as its first local moment.\n const [internalDate, setInternalDate] = useState(() => {\n const initial = asDay(defaultDate);\n return initial ? dayToDate(initial) : startOfDay(new Date());\n });\n const controlledDay = asDay(controlledDate);\n // Only ever a day the views show. The selection itself is left alone: a hidden day in `date` is\n // the parent's, and stepping on from it lands where the parent would expect.\n const date = shownDay(controlledDay ? dayToDate(controlledDay) : internalDate, hidden);\n const [internalView, setInternalView] = useState(defaultView);\n const view = controlledView ?? internalView;\n\n // The layout switch follows the calendar's own width rather than the viewport's, so it also\n // happens in a sidebar on a wide screen. Width 0 means no layout (server, jsdom): stay wide.\n useLayoutEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n // The border box, both times: an observer's `contentRect` leaves the border out, and 600px\n // wide would then read as 598.\n const measure = () => setWidth(root.getBoundingClientRect().width);\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(root);\n return () => observer.disconnect();\n }, []);\n const compact = width > 0 && width < COMPACT_WIDTH;\n\n // Reported per range, not per render: `date` is a new object on every render of a controlled\n // parent. Not `useEffectEvent` for the latest callback, which React only has from 19.2.\n const range = rangeWithNeighbours(view, date, weekStartsOn, hidden);\n const rangeKey = `${dateToDay(range.start)}/${dateToDay(range.end)}`;\n const latest = useRef({ range, onRangeChange });\n useLayoutEffect(() => {\n latest.current = { range, onRangeChange };\n });\n useEffect(() => {\n latest.current.onRangeChange?.(latest.current.range);\n }, [rangeKey]);\n\n function changeDate(next: Date) {\n const day = startOfDay(next);\n if (dateToDay(day) === dateToDay(date)) return;\n setDirection(day > date ? 1 : -1);\n if (controlledDate === undefined) setInternalDate(day);\n onDateChange?.(dateToDay(day));\n }\n\n function changeView(next: CalendarView) {\n if (next === view) return;\n if (controlledView === undefined) setInternalView(next);\n onViewChange?.(next);\n }\n\n // Moves whose promise has not settled yet, drawn where they were dropped meanwhile.\n const [pending, setPending] = useState<ReadonlyMap<string, { start: Date; end: Date }>>(() => new Map());\n function changeEvent(change: CalendarEventChange<E>) {\n const result = onEventChange?.(change);\n if (typeof (result as Promise<unknown> | undefined)?.then !== 'function') return;\n const key = String(change.event.id);\n const entry = { start: change.start, end: change.end };\n setPending((current) => new Map(current).set(key, entry));\n // A later move of the same event owns the entry by then, and keeps it.\n const settle = () =>\n setPending((current) => {\n if (current.get(key) !== entry) return current;\n const next = new Map(current);\n next.delete(key);\n return next;\n });\n (result as Promise<unknown>).then(settle, settle);\n }\n const shownEvents =\n pending.size === 0\n ? events\n : events.map((event) => {\n const moved = pending.get(String(event.id));\n return moved ? { ...event, ...moved } : event;\n });\n\n const viewProps: CalendarViewProps<E> = {\n events: shownEvents,\n date,\n view,\n locale,\n weekStartsOn,\n slotMinutes,\n hidden,\n scrollToHour,\n text,\n onDateChange: changeDate,\n onOpenDay: views.includes(CalendarView.Day)\n ? (day) => {\n changeDate(day);\n changeView(CalendarView.Day);\n }\n : undefined,\n onEventClick,\n onEventChange: onEventChange && changeEvent,\n onSlotCreate,\n renderEvent,\n classNames,\n slotStyles,\n };\n\n // The week's title names the days it shows, not the ones it hides at either end.\n const period = periodRange(view, date, weekStartsOn);\n const columns = shownDays(period.start, dayDiff(period.start, period.end), hidden);\n const titleRange = { start: columns[0], end: addDays(columns[columns.length - 1], 1) };\n\n function renderView() {\n switch (view) {\n case CalendarView.Week:\n case CalendarView.Day:\n return <TimeGrid {...viewProps} zoomRef={zoomRef} />;\n case CalendarView.Agenda: {\n return (\n <AgendaList\n events={shownEvents}\n start={period.start}\n days={dayDiff(period.start, period.end)}\n hidden={hidden}\n scrollTo={date}\n showDayHeaders\n locale={locale}\n text={text}\n onEventClick={onEventClick}\n renderEvent={renderEvent}\n classNames={classNames}\n slotStyles={slotStyles}\n />\n );\n }\n default:\n return compact ? <CompactMonth {...viewProps} /> : <MonthView {...viewProps} />;\n }\n }\n\n return (\n <div\n {...rest}\n ref={mergeRefs(rootRef, ref)}\n className={cx(styles.calendar, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <CalendarHeader\n className={classNames?.header}\n style={slotStyles?.header}\n compact={compact}\n title={formatPeriodTitle(view, date, titleRange, locale)}\n direction={direction}\n view={view}\n views={views}\n text={text}\n onStep={(step) => changeDate(stepDate(view, date, step, hidden))}\n onToday={() => changeDate(new Date())}\n onViewChange={changeView}\n />\n {renderView()}\n </div>\n );\n}\n\nexport default Calendar;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"calendar.mjs","names":[],"sources":["../../../../package/common/calendar/calendar.tsx"],"sourcesContent":["import { useEffect, useLayoutEffect, useRef, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { mergeRefs } from '../utils/mergeRefs';\nimport { asDay, dayToDate } from '../utils/day';\nimport {\n CalendarView,\n type CalendarEvent,\n type CalendarEventChange,\n type CalendarLabels,\n type CalendarProps,\n} from './calendar.types';\nimport {\n dayDiff,\n dateToDay,\n addDays,\n hiddenDaySet,\n periodRange,\n rangeWithNeighbours,\n shownDay,\n shownDays,\n startOfDay,\n stepDate,\n} from './dates';\nimport { formatPeriodTitle, localeWeekStart } from './format';\nimport AgendaList from './components/agendaList';\nimport CalendarHeader from './components/calendarHeader';\nimport CompactMonth from './components/compactMonth';\nimport MonthView from './components/monthView';\nimport TimeGrid from './components/timeGrid';\nimport type { TimeGridZoom } from './components/timeGrid/timeGrid.types';\nimport type { CalendarViewProps } from './view.types';\n\nimport styles from './calendar.module.pcss';\n\nconst ALL_VIEWS = [CalendarView.Month, CalendarView.Week, CalendarView.Day, CalendarView.Agenda];\n\n/** Below this width the month view folds into the compact month with a day list. */\nconst COMPACT_WIDTH = 600;\n\nconst DEFAULT_LABELS: Required<CalendarLabels> = {\n previous: 'Previous',\n next: 'Next',\n today: 'Today',\n viewSwitcher: 'Calendar view',\n month: 'Month',\n week: 'Week',\n day: 'Day',\n agenda: 'Agenda',\n allDay: 'All day',\n more: (count) => `+${count} more`,\n eventCount: (count) => (count === 1 ? '1 event' : `${count} events`),\n noEvents: 'No events',\n newEvent: 'New event',\n};\n\nfunction Calendar<E extends CalendarEvent = CalendarEvent>(props: CalendarProps<E>): ReactElement {\n const {\n events,\n date: controlledDate,\n defaultDate,\n onDateChange,\n view: controlledView,\n defaultView = CalendarView.Month,\n onViewChange,\n views = ALL_VIEWS,\n locale,\n weekStartsOn: weekStartsOnProp,\n slotMinutes = 15,\n hiddenDays,\n scrollToHour,\n onRangeChange,\n onEventClick,\n onEventChange,\n onSlotCreate,\n renderEvent,\n labels,\n ref,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('Calendar', props);\n const text = useLabels('Calendar', labels, DEFAULT_LABELS);\n const weekStartsOn = weekStartsOnProp ?? localeWeekStart(locale);\n const hidden = hiddenDaySet(hiddenDays);\n\n const rootRef = useRef<HTMLDivElement>(null);\n const zoomRef = useRef<TimeGridZoom | null>(null);\n const [width, setWidth] = useState(0);\n const [direction, setDirection] = useState<1 | -1>(1);\n\n // `date` is a day, the same wherever the calendar is opened, and read as its first local moment.\n const [internalDate, setInternalDate] = useState(() => {\n const initial = asDay(defaultDate);\n return initial ? dayToDate(initial) : startOfDay(new Date());\n });\n const controlledDay = asDay(controlledDate);\n // Only ever a day the views show. The selection itself is left alone: a hidden day in `date` is\n // the parent's, and stepping on from it lands where the parent would expect.\n const date = shownDay(controlledDay ? dayToDate(controlledDay) : internalDate, hidden);\n const [internalView, setInternalView] = useState(defaultView);\n const view = controlledView ?? internalView;\n\n // The layout switch follows the calendar's own width rather than the viewport's, so it also\n // happens in a sidebar on a wide screen. Width 0 means no layout (server, jsdom): stay wide.\n useLayoutEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n // The border box, both times: an observer's `contentRect` leaves the border out, and 600px\n // wide would then read as 598.\n const measure = () => setWidth(root.getBoundingClientRect().width);\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(root);\n return () => observer.disconnect();\n }, []);\n const compact = width > 0 && width < COMPACT_WIDTH;\n\n // Reported per range, not per render: `date` is a new object on every render of a controlled\n // parent. Not `useEffectEvent` for the latest callback, which React only has from 19.2.\n // An effect, since controlled props move the range too; `reported` keeps StrictMode's re-run\n // from reporting a range twice, which a consumer fetching in the callback pays for.\n const range = rangeWithNeighbours(view, date, weekStartsOn, hidden);\n const rangeKey = `${dateToDay(range.start)}/${dateToDay(range.end)}`;\n const latest = useRef({ range, onRangeChange });\n const reported = useRef<string | null>(null);\n useLayoutEffect(() => {\n latest.current = { range, onRangeChange };\n });\n useEffect(() => {\n if (reported.current === rangeKey) return;\n reported.current = rangeKey;\n latest.current.onRangeChange?.(latest.current.range);\n }, [rangeKey]);\n\n function changeDate(next: Date) {\n const day = startOfDay(next);\n if (dateToDay(day) === dateToDay(date)) return;\n setDirection(day > date ? 1 : -1);\n if (controlledDate === undefined) setInternalDate(day);\n onDateChange?.(dateToDay(day));\n }\n\n function changeView(next: CalendarView) {\n if (next === view) return;\n if (controlledView === undefined) setInternalView(next);\n onViewChange?.(next);\n }\n\n // Moves whose promise has not settled yet, drawn where they were dropped meanwhile.\n const [pending, setPending] = useState<ReadonlyMap<string, { start: Date; end: Date }>>(() => new Map());\n function changeEvent(change: CalendarEventChange<E>) {\n const result = onEventChange?.(change);\n if (typeof (result as Promise<unknown> | undefined)?.then !== 'function') return;\n const key = String(change.event.id);\n const entry = { start: change.start, end: change.end };\n setPending((current) => new Map(current).set(key, entry));\n // A later move of the same event owns the entry by then, and keeps it.\n const settle = () =>\n setPending((current) => {\n if (current.get(key) !== entry) return current;\n const next = new Map(current);\n next.delete(key);\n return next;\n });\n (result as Promise<unknown>).then(settle, settle);\n }\n const shownEvents =\n pending.size === 0\n ? events\n : events.map((event) => {\n const moved = pending.get(String(event.id));\n return moved ? { ...event, ...moved } : event;\n });\n\n const viewProps: CalendarViewProps<E> = {\n events: shownEvents,\n date,\n view,\n locale,\n weekStartsOn,\n slotMinutes,\n hidden,\n scrollToHour,\n text,\n onDateChange: changeDate,\n onOpenDay: views.includes(CalendarView.Day)\n ? (day) => {\n changeDate(day);\n changeView(CalendarView.Day);\n }\n : undefined,\n onEventClick,\n onEventChange: onEventChange && changeEvent,\n onSlotCreate,\n renderEvent,\n classNames,\n slotStyles,\n };\n\n // The week's title names the days it shows, not the ones it hides at either end.\n const period = periodRange(view, date, weekStartsOn);\n const columns = shownDays(period.start, dayDiff(period.start, period.end), hidden);\n const titleRange = { start: columns[0], end: addDays(columns[columns.length - 1], 1) };\n\n function renderView() {\n switch (view) {\n case CalendarView.Week:\n case CalendarView.Day:\n return <TimeGrid {...viewProps} zoomRef={zoomRef} />;\n case CalendarView.Agenda: {\n return (\n <AgendaList\n events={shownEvents}\n start={period.start}\n days={dayDiff(period.start, period.end)}\n hidden={hidden}\n scrollTo={date}\n showDayHeaders\n locale={locale}\n text={text}\n onEventClick={onEventClick}\n renderEvent={renderEvent}\n classNames={classNames}\n slotStyles={slotStyles}\n />\n );\n }\n default:\n return compact ? <CompactMonth {...viewProps} /> : <MonthView {...viewProps} />;\n }\n }\n\n return (\n <div\n {...rest}\n ref={mergeRefs(rootRef, ref)}\n className={cx(styles.calendar, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <CalendarHeader\n className={classNames?.header}\n style={slotStyles?.header}\n compact={compact}\n title={formatPeriodTitle(view, date, titleRange, locale)}\n direction={direction}\n view={view}\n views={views}\n text={text}\n onStep={(step) => changeDate(stepDate(view, date, step, hidden))}\n onToday={() => changeDate(new Date())}\n onViewChange={changeView}\n />\n {renderView()}\n </div>\n );\n}\n\nexport default Calendar;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"carousel.module.mjs","names":[],"sources":["../../../../package/common/carousel/carousel.module.pcss"],"sourcesContent":[".carousel {\n position: relative;\n overflow: hidden;\n border-radius: var(--default-border-radius);\n\n &-adaptive {\n transition: height 0.4s ease;\n }\n\n &-track {\n display: flex;\n align-items: stretch;\n transition: transform 0.4s ease;\n touch-action: pan-y;\n user-select: none;\n }\n\n &-trackAdaptive {\n align-items: flex-start;\n }\n\n &-slide {\n flex-shrink: 0;\n box-sizing: border-box;\n }\n\n &-prevButton,\n &-nextButton {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n z-index: 1;\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n background: var(--background-accent);\n color: var(--text-color);\n box-shadow: var(--card-shadow);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition:\n opacity 0.15s,\n background 0.15s;\n\n &:hover {\n background: var(--background-accent-light);\n }\n\n &:focus-visible {\n outline: none;\n box-shadow:\n var(--card-shadow),\n 0 0 0 2px var(--primary-lighter);\n }\n }\n\n &-buttonDisabled {\n opacity: 0.35;\n pointer-events: none;\n }\n\n &-prevButton {\n left: 12px;\n }\n\n &-nextButton {\n right: 12px;\n }\n\n /* A scroll strip rather than a row that shrinks, so the dots keep their size at any slide count.\n The padding holds the outer dots clear of the fade until the strip has somewhere to scroll. */\n &-dots {\n position: absolute;\n bottom: 4px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 1;\n display: flex;\n box-sizing: border-box;\n width: max-content;\n max-width: 100%;\n padding-inline: 16px;\n overflow-x: auto;\n overscroll-behavior-x: contain;\n scroll-behavior: smooth;\n\n /* Matches the strip's own `padding-inline`, so the first and last dot start out clear of the\n fade. The shared sheet's private length, not `--scroll-fade-length`, which would inherit. */\n --custom-scroll-fade-length: 16px;\n\n
|
|
1
|
+
{"version":3,"file":"carousel.module.mjs","names":[],"sources":["../../../../package/common/carousel/carousel.module.pcss"],"sourcesContent":[".carousel {\n position: relative;\n overflow: hidden;\n border-radius: var(--default-border-radius);\n\n &-adaptive {\n transition: height 0.4s ease;\n }\n\n &-track {\n display: flex;\n align-items: stretch;\n transition: transform 0.4s ease;\n touch-action: pan-y;\n user-select: none;\n }\n\n &-trackAdaptive {\n align-items: flex-start;\n }\n\n &-slide {\n flex-shrink: 0;\n box-sizing: border-box;\n }\n\n &-prevButton,\n &-nextButton {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n z-index: 1;\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n background: var(--background-accent);\n color: var(--text-color);\n box-shadow: var(--card-shadow);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition:\n opacity 0.15s,\n background 0.15s;\n\n &:hover {\n background: var(--background-accent-light);\n }\n\n &:focus-visible {\n outline: none;\n box-shadow:\n var(--card-shadow),\n 0 0 0 2px var(--primary-lighter);\n }\n }\n\n &-buttonDisabled {\n opacity: 0.35;\n pointer-events: none;\n }\n\n &-prevButton {\n left: 12px;\n }\n\n &-nextButton {\n right: 12px;\n }\n\n /* A scroll strip rather than a row that shrinks, so the dots keep their size at any slide count.\n The padding holds the outer dots clear of the fade until the strip has somewhere to scroll. */\n &-dots {\n position: absolute;\n bottom: 4px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 1;\n display: flex;\n box-sizing: border-box;\n width: max-content;\n max-width: 100%;\n padding-inline: 16px;\n overflow-x: auto;\n overscroll-behavior-x: contain;\n scroll-behavior: smooth;\n\n /* Matches the strip's own `padding-inline`, so the first and last dot start out clear of the\n fade. The shared sheet's private length, not `--scroll-fade-length`, which would inherit. */\n --custom-scroll-fade-length: 16px;\n\n scrollbar-width: none;\n\n &::-webkit-scrollbar {\n display: none;\n }\n\n @media (prefers-reduced-motion: reduce) {\n scroll-behavior: auto;\n }\n }\n\n /* The dot stays 8px, its padding carries the hit area out to the 24px minimum. */\n &-dot {\n box-sizing: content-box;\n\n /* The strip is width-capped, and shrinking would pull the content box, the only part\n `background-clip` paints, down to nothing. */\n flex-shrink: 0;\n width: 8px;\n height: 8px;\n padding: 8px;\n border-radius: 2000px;\n background: var(--background-accent);\n background-clip: content-box;\n opacity: 0.6;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition:\n opacity 0.15s,\n background 0.15s,\n width 0.15s;\n\n &:hover {\n opacity: 0.85;\n }\n\n &:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--primary-lighter);\n }\n\n &-active {\n background: var(--primary-color);\n background-clip: content-box;\n opacity: 1;\n width: 20px;\n }\n }\n}\n"],"mappings":""}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use client";import e from"../accordion/accordion.mjs";import t from"../button/button.mjs";import n
|
|
1
|
+
"use client";import e from"../accordion/accordion.mjs";import t from"../button/button.mjs";import{showToast as n}from"../toast/toastService.mjs";import r from"../copyButton/copyButton.mjs";import i from"./errorBoundary.module.mjs";import a from"react";import o from"classnames";import{FontAwesomeIcon as s}from"@fortawesome/react-fontawesome";import{faArrowRotateRight as c,faTriangleExclamation as l}from"@fortawesome/free-solid-svg-icons";import{jsx as u,jsxs as d}from"react/jsx-runtime";var f={stackTrace:`Stack trace`,copy:`Copy`,copied:`Copied`,reset:`Try again`,copySuccessToast:`Stack trace copied to clipboard`,copyErrorToast:`Could not copy stack trace`};function p(e,t){let n=[e.stack??`${e.name}: ${e.message}`];return t?.componentStack&&n.push(`Component stack:${t.componentStack}`),n.join(`
|
|
2
2
|
|
|
3
|
-
`)}var m=class extends a.Component{constructor(...e){super(...e),this.state={error:null,errorInfo:null},this.reset=()=>{this.setState({error:null,errorInfo:null}),this.props.onReset?.()}}static getDerivedStateFromError(e){return{error:e}}componentDidCatch(e,t){this.setState({errorInfo:t}),this.props.onError?.(e,t)}componentDidUpdate(e){if(!this.state.error)return;let t=e.resetKeys??[],n=this.props.resetKeys??[];(n.length!==t.length||n.some((e,n)=>e!==t[n]))&&this.reset()}render(){let{error:a,errorInfo:m}=this.state,{children:h=null,title:g=`Something went wrong`,description:_,labels:v,fallback:y,hideStack:b=!1,className:x,style:S,classNames:C,styles:w,onError:T,onReset:E,resetKeys:D,role:O=`alert`,...k}=this.props;if(!a)return h;let A={...f,...v};return y?typeof y==`function`?y({error:a,errorInfo:m,resetErrorBoundary:this.reset}):y:d(`div`,{...k,role:O,className:o(i.errorBoundary,x,C?.root),style:{...S,...w?.root},children:[d(`div`,{className:o(i.errorBoundaryHeader,C?.header),style:w?.header,children:[u(`div`,{className:o(i.errorBoundaryBadge,C?.badge),style:w?.badge,children:u(s,{icon:l,className:o(i.errorBoundaryIcon,C?.icon),style:w?.icon})}),u(`div`,{className:o(i.errorBoundaryTitle,C?.title),style:w?.title,children:g}),u(`div`,{className:o(i.errorBoundaryDescription,C?.description),style:w?.description,children:_??a.message})]}),!b&&d(e,{title:A.stackTrace,className:o(i.errorBoundaryStack,C?.stack),style:w?.stack,children:[d(`div`,{className:o(i.errorBoundaryStackHeader,C?.stackHeader),style:w?.stackHeader,children:[u(`span`,{className:o(i.errorBoundaryStackLabel,C?.stackLabel),style:w?.stackLabel,children:a.name||`Error`}),u(
|
|
3
|
+
`)}var m=class extends a.Component{constructor(...e){super(...e),this.state={error:null,errorInfo:null},this.reset=()=>{this.setState({error:null,errorInfo:null}),this.props.onReset?.()}}static getDerivedStateFromError(e){return{error:e}}componentDidCatch(e,t){this.setState({errorInfo:t}),this.props.onError?.(e,t)}componentDidUpdate(e){if(!this.state.error)return;let t=e.resetKeys??[],n=this.props.resetKeys??[];(n.length!==t.length||n.some((e,n)=>e!==t[n]))&&this.reset()}render(){let{error:a,errorInfo:m}=this.state,{children:h=null,title:g=`Something went wrong`,description:_,labels:v,fallback:y,hideStack:b=!1,className:x,style:S,classNames:C,styles:w,onError:T,onReset:E,resetKeys:D,role:O=`alert`,...k}=this.props;if(!a)return h;let A={...f,...v};return y?typeof y==`function`?y({error:a,errorInfo:m,resetErrorBoundary:this.reset}):y:d(`div`,{...k,role:O,className:o(i.errorBoundary,x,C?.root),style:{...S,...w?.root},children:[d(`div`,{className:o(i.errorBoundaryHeader,C?.header),style:w?.header,children:[u(`div`,{className:o(i.errorBoundaryBadge,C?.badge),style:w?.badge,children:u(s,{icon:l,className:o(i.errorBoundaryIcon,C?.icon),style:w?.icon})}),u(`div`,{className:o(i.errorBoundaryTitle,C?.title),style:w?.title,children:g}),u(`div`,{className:o(i.errorBoundaryDescription,C?.description),style:w?.description,children:_??a.message})]}),!b&&d(e,{title:A.stackTrace,className:o(i.errorBoundaryStack,C?.stack),style:w?.stack,children:[d(`div`,{className:o(i.errorBoundaryStackHeader,C?.stackHeader),style:w?.stackHeader,children:[u(`span`,{className:o(i.errorBoundaryStackLabel,C?.stackLabel),style:w?.stackLabel,children:a.name||`Error`}),u(r,{value:p(a,m),labels:{copy:A.copy,copied:A.copied},onCopy:()=>n(A.copySuccessToast,{type:`success`}),onError:()=>n(A.copyErrorToast,{type:`error`}),className:o(i.errorBoundaryCopyButton,C?.copyButton),style:w?.copyButton})]}),u(`pre`,{className:o(i.errorBoundaryStackTrace,C?.stackTrace),style:w?.stackTrace,children:p(a,m)})]}),u(`div`,{className:o(i.errorBoundaryActions,C?.actions),style:w?.actions,children:u(t,{onClick:this.reset,icon:c,className:o(i.errorBoundaryResetButton,C?.resetButton),style:w?.resetButton,children:A.reset})})]})}};export{m as default};
|
|
4
4
|
//# sourceMappingURL=errorBoundary.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAnchorTracking.mjs","names":[],"sources":["../../../../package/common/hooks/useAnchorTracking.ts"],"sourcesContent":["import { useLayoutEffect, useRef, type RefObject } from 'react';\nimport { getScrollableAncestors } from 'components/common/utils/scrollAncestors';\n\nexport interface UseAnchorTrackingArgs {\n isOpen: boolean;\n /** The element the popup is anchored to. Its scrollable ancestors are what get tracked. */\n triggerRef: RefObject<HTMLElement | null>;\n /**\n * Every element the positioning maths needs. Tracking starts only once all of them, plus the\n * trigger, are mounted. Read fresh on each setup, so an inline array literal is fine.\n */\n elementRefs: RefObject<HTMLElement | null>[];\n /**\n * Positions the popup. Called once on setup and then on every scroll, resize and tracked size\n * change. Reads the caller's own refs for everything beyond the two arguments.\n */\n update: (trigger: HTMLElement, ancestors: HTMLElement[]) => void;\n /**\n * Elements whose own size changes must re-run `update`: a popup whose content changes size\n * while open (a filtered list shrinking, an async list loading in) otherwise keeps coordinates\n * computed for its old height and ends up detached from the trigger. Re-read on every update,\n * since a panel can mount a commit later than the element it hangs off.\n */\n resizeRefs?: RefObject<HTMLElement | null>[];\n /** Hands the popup's elements back as they were found. Runs when tracking stops. */\n teardown?: () => void;\n /** Values that must restart tracking, and so re-run `update`, when they change. */\n deps?: readonly unknown[];\n}\n\n/**\n * The lifecycle half of anchoring a portaled, `position: fixed` popup to a trigger: waits for the\n * elements to mount, resolves the trigger's scrolling ancestors once, and re-runs the caller's\n * positioning on every scroll, viewport resize and tracked size change until it closes.\n *\n * Callers supply only the geometry (`update`) and the inline styles to clear again (`teardown`);\n * `usePopoverPosition` is built on this.\n *\n * Position is deliberately written straight to the DOM by the caller rather than through React\n * state: going through setState -> re-render -> commit adds a round-trip that lags behind the\n * browser's own scroll painting by at least a frame.\n */\nexport function useAnchorTracking({\n isOpen,\n triggerRef,\n elementRefs,\n update,\n resizeRefs,\n teardown,\n deps = [],\n}: UseAnchorTrackingArgs) {\n // Latest-value refs, so the effect restarts only for `deps` the caller names, never merely\n // because these were passed as fresh array/function literals this render.\n const latest = useRef({ elementRefs, update, resizeRefs, teardown });\n latest.current = { elementRefs, update, resizeRefs, teardown };\n\n useLayoutEffect(() => {\n if (!isOpen) return;\n\n let rafId: number | undefined;\n let stop: (() => void) | undefined;\n\n // The elements may not be mounted yet on the very first invocation of this effect: a portal\n // target
|
|
1
|
+
{"version":3,"file":"useAnchorTracking.mjs","names":[],"sources":["../../../../package/common/hooks/useAnchorTracking.ts"],"sourcesContent":["import { useLayoutEffect, useRef, type RefObject } from 'react';\nimport { getScrollableAncestors } from 'components/common/utils/scrollAncestors';\n\nexport interface UseAnchorTrackingArgs {\n isOpen: boolean;\n /** The element the popup is anchored to. Its scrollable ancestors are what get tracked. */\n triggerRef: RefObject<HTMLElement | null>;\n /**\n * Every element the positioning maths needs. Tracking starts only once all of them, plus the\n * trigger, are mounted. Read fresh on each setup, so an inline array literal is fine.\n */\n elementRefs: RefObject<HTMLElement | null>[];\n /**\n * Positions the popup. Called once on setup and then on every scroll, resize and tracked size\n * change. Reads the caller's own refs for everything beyond the two arguments.\n */\n update: (trigger: HTMLElement, ancestors: HTMLElement[]) => void;\n /**\n * Elements whose own size changes must re-run `update`: a popup whose content changes size\n * while open (a filtered list shrinking, an async list loading in) otherwise keeps coordinates\n * computed for its old height and ends up detached from the trigger. Re-read on every update,\n * since a panel can mount a commit later than the element it hangs off.\n */\n resizeRefs?: RefObject<HTMLElement | null>[];\n /** Hands the popup's elements back as they were found. Runs when tracking stops. */\n teardown?: () => void;\n /** Values that must restart tracking, and so re-run `update`, when they change. */\n deps?: readonly unknown[];\n}\n\n/**\n * The lifecycle half of anchoring a portaled, `position: fixed` popup to a trigger: waits for the\n * elements to mount, resolves the trigger's scrolling ancestors once, and re-runs the caller's\n * positioning on every scroll, viewport resize and tracked size change until it closes.\n *\n * Callers supply only the geometry (`update`) and the inline styles to clear again (`teardown`);\n * `usePopoverPosition` is built on this.\n *\n * Position is deliberately written straight to the DOM by the caller rather than through React\n * state: going through setState -> re-render -> commit adds a round-trip that lags behind the\n * browser's own scroll painting by at least a frame.\n */\nexport function useAnchorTracking({\n isOpen,\n triggerRef,\n elementRefs,\n update,\n resizeRefs,\n teardown,\n deps = [],\n}: UseAnchorTrackingArgs) {\n // Latest-value refs, so the effect restarts only for `deps` the caller names, never merely\n // because these were passed as fresh array/function literals this render.\n const latest = useRef({ elementRefs, update, resizeRefs, teardown });\n latest.current = { elementRefs, update, resizeRefs, teardown };\n\n useLayoutEffect(() => {\n if (!isOpen) return;\n\n let rafId: number | undefined;\n let stop: (() => void) | undefined;\n\n // The elements may not be mounted yet on the very first invocation of this effect: a portal\n // target can still be missing (a `#bodyEnd` the app renders, or any while hydrating), and then\n // the portaled content and its ref commits one render later. Since refs never change\n // identity, that later commit doesn't re-trigger this effect on its own, so retry each frame.\n function trySetup() {\n const trigger = triggerRef.current;\n const elements = latest.current.elementRefs;\n if (!trigger || elements.some((ref) => !ref.current)) {\n rafId = requestAnimationFrame(trySetup);\n return;\n }\n const ancestors = getScrollableAncestors(trigger);\n\n const resizeObserver = new ResizeObserver(() => runUpdate());\n const observed = new Set<Element>();\n\n function runUpdate() {\n latest.current.update(trigger!, ancestors);\n // After the update, so an element that only mounts during it is picked up too. Once\n // observed this is a handful of Set lookups, so it costs nothing on the scroll path.\n latest.current.resizeRefs?.forEach(({ current }) => {\n if (!current || observed.has(current)) return;\n observed.add(current);\n resizeObserver.observe(current);\n });\n }\n\n runUpdate();\n\n const scrollTargets: (Window | HTMLElement)[] = [window, ...ancestors];\n scrollTargets.forEach((t) => t.addEventListener('scroll', runUpdate, { passive: true }));\n window.addEventListener('resize', runUpdate);\n // `window.resize` doesn't reliably fire when a mobile keyboard opens/closes — visualViewport\n // does, and is what the callers' flip/clamp maths is measured against.\n window.visualViewport?.addEventListener('resize', runUpdate);\n window.visualViewport?.addEventListener('scroll', runUpdate);\n\n stop = () => {\n scrollTargets.forEach((t) => t.removeEventListener('scroll', runUpdate));\n window.removeEventListener('resize', runUpdate);\n window.visualViewport?.removeEventListener('resize', runUpdate);\n window.visualViewport?.removeEventListener('scroll', runUpdate);\n resizeObserver.disconnect();\n latest.current.teardown?.();\n };\n }\n\n trySetup();\n\n return () => {\n if (rafId !== undefined) cancelAnimationFrame(rafId);\n stop?.();\n };\n // Spread rather than listed: `deps` is the caller's own set of geometry inputs, and changing\n // any of them has to restart tracking so the new values get applied immediately.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen, triggerRef, ...deps]);\n}\n"],"mappings":"uIA0CA,SAAgB,EAAkB,CAChC,SACA,aACA,cACA,SACA,aACA,WACA,OAAO,CAAC,GACgB,CAGxB,IAAM,EAAS,EAAO,CAAE,cAAa,SAAQ,aAAY,UAAS,CAAC,EACnE,EAAO,QAAU,CAAE,cAAa,SAAQ,aAAY,UAAS,EAE7D,MAAsB,CACpB,GAAI,CAAC,EAAQ,OAEb,IAAI,EACA,EAMJ,SAAS,GAAW,CAClB,IAAM,EAAU,EAAW,QACrB,EAAW,EAAO,QAAQ,YAChC,GAAI,CAAC,GAAW,EAAS,KAAM,GAAQ,CAAC,EAAI,OAAO,EAAG,CACpD,EAAQ,sBAAsB,CAAQ,EACtC,MACF,CACA,IAAM,EAAY,EAAuB,CAAO,EAE1C,EAAiB,IAAI,mBAAqB,EAAU,CAAC,EACrD,EAAW,IAAI,IAErB,SAAS,GAAY,CACnB,EAAO,QAAQ,OAAO,EAAU,CAAS,EAGzC,EAAO,QAAQ,YAAY,SAAS,CAAE,aAAc,CAC7C,GAAW,GAAS,IAAI,CAAO,IACpC,EAAS,IAAI,CAAO,EACpB,EAAe,QAAQ,CAAO,EAChC,CAAC,CACH,CAEA,EAAU,EAEV,IAAM,EAA0C,CAAC,OAAQ,GAAG,CAAS,EACrE,EAAc,QAAS,GAAM,EAAE,iBAAiB,SAAU,EAAW,CAAE,QAAS,EAAK,CAAC,CAAC,EACvF,OAAO,iBAAiB,SAAU,CAAS,EAG3C,OAAO,gBAAgB,iBAAiB,SAAU,CAAS,EAC3D,OAAO,gBAAgB,iBAAiB,SAAU,CAAS,EAE3D,MAAa,CACX,EAAc,QAAS,GAAM,EAAE,oBAAoB,SAAU,CAAS,CAAC,EACvE,OAAO,oBAAoB,SAAU,CAAS,EAC9C,OAAO,gBAAgB,oBAAoB,SAAU,CAAS,EAC9D,OAAO,gBAAgB,oBAAoB,SAAU,CAAS,EAC9D,EAAe,WAAW,EAC1B,EAAO,QAAQ,WAAW,CAC5B,CACF,CAIA,OAFA,EAAS,MAEI,CACP,IAAU,IAAA,IAAW,qBAAqB,CAAK,EACnD,IAAO,CACT,CAIF,EAAG,CAAC,EAAQ,EAAY,GAAG,CAAI,CAAC,CAClC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{tabbablesIn as e}from"./useFocusBoundary.mjs";import{useEffect as t,useRef as n}from"react";function r({isActive:r,containerRef:o,onEscape:s,trapFocus:c=!0,lockScroll:d=!0}){let f=n(s);t(()=>{f.current=s});let p=n(0);r&&p.current===0&&(p.current=i++),!r&&p.current!==0&&(p.current=0),t(()=>{if(!r)return;let t=p.current;a.add(t);let n=()=>t===Math.max(...a),i=document.activeElement;if(c){let t=o.current;(e(t)[0]??t)?.focus()}function s(t){if(!n())return;if(t.key===`Escape`&&f.current){t.preventDefault(),f.current();return}if(t.key!==`Tab`||!c)return;let r=e(o.current);if(r.length===0){t.preventDefault();return}let i=r[0],a=r[r.length-1],s=document.activeElement;o.current?.contains(s)?!t.shiftKey&&s===a?(t.preventDefault(),i.focus()):t.shiftKey&&s===i&&(t.preventDefault(),a.focus()):(t.preventDefault(),i.focus())}return window.addEventListener(`keydown`,s),d&&l(),()=>{window.removeEventListener(`keydown`,s),d&&u(),a.delete(t)
|
|
1
|
+
"use client";import{tabbablesIn as e}from"./useFocusBoundary.mjs";import{useEffect as t,useRef as n}from"react";function r({isActive:r,containerRef:o,onEscape:s,trapFocus:c=!0,lockScroll:d=!0}){let f=n(s);t(()=>{f.current=s});let p=n(0);r&&p.current===0&&(p.current=i++),!r&&p.current!==0&&(p.current=0),t(()=>{if(!r)return;let t=p.current;a.add(t);let n=()=>t===Math.max(...a),i=document.activeElement;if(c){let t=o.current;(e(t)[0]??t)?.focus()}function s(t){if(!n())return;if(t.key===`Escape`&&f.current){t.preventDefault(),f.current();return}if(t.key!==`Tab`||!c)return;let r=e(o.current);if(r.length===0){t.preventDefault();return}let i=r[0],a=r[r.length-1],s=document.activeElement;o.current?.contains(s)?!t.shiftKey&&s===a?(t.preventDefault(),i.focus()):t.shiftKey&&s===i&&(t.preventDefault(),a.focus()):(t.preventDefault(),i.focus())}let m=new Set,h=e=>e.code||e.key;function g(e){e.repeat?!m.has(h(e))&&o.current?.contains(e.target)&&(e.preventDefault(),e.stopPropagation()):m.add(h(e))}function _(e){m.delete(h(e))}return window.addEventListener(`keydown`,s),window.addEventListener(`keydown`,g,!0),window.addEventListener(`keyup`,_,!0),d&&l(),()=>{window.removeEventListener(`keydown`,s),window.removeEventListener(`keydown`,g,!0),window.removeEventListener(`keyup`,_,!0),d&&u(),a.delete(t);let e=[...a].some(e=>e>t);c&&!e&&i?.focus()}},[r,c,d,o])}var i=1,a=new Set,o=0,s=``,c=``;function l(){let{body:e}=document;if(o===0){s=e.style.overflow,c=e.style.paddingRight;let t=window.innerWidth-document.documentElement.clientWidth;if(t>0){let n=parseFloat(window.getComputedStyle(e).paddingRight)||0;e.style.paddingRight=`${n+t}px`}e.style.overflow=`hidden`}o+=1}function u(){--o,!(o>0)&&(o=0,document.body.style.overflow=s,document.body.style.paddingRight=c)}export{r as useOverlay};
|
|
2
2
|
//# sourceMappingURL=useOverlay.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useOverlay.mjs","names":[],"sources":["../../../../package/common/hooks/useOverlay.ts"],"sourcesContent":["import { useEffect, useRef, type RefObject } from 'react';\nimport { tabbablesIn } from './useFocusBoundary';\n\nexport interface UseOverlayArgs {\n /** Whether the overlay is currently visible. Everything below is set up while this is true. */\n isActive: boolean;\n /** The overlay panel. Focus is trapped inside it and Escape is scoped to it. */\n containerRef: RefObject<HTMLElement | null>;\n /** Called when Escape is pressed while this is the topmost overlay. Omit to not handle Escape. */\n onEscape?: () => void;\n
|
|
1
|
+
{"version":3,"file":"useOverlay.mjs","names":[],"sources":["../../../../package/common/hooks/useOverlay.ts"],"sourcesContent":["import { useEffect, useRef, type RefObject } from 'react';\nimport { tabbablesIn } from './useFocusBoundary';\n\nexport interface UseOverlayArgs {\n /** Whether the overlay is currently visible. Everything below is set up while this is true. */\n isActive: boolean;\n /** The overlay panel. Focus is trapped inside it and Escape is scoped to it. */\n containerRef: RefObject<HTMLElement | null>;\n /** Called when Escape is pressed while this is the topmost overlay. Omit to not handle Escape. */\n onEscape?: () => void;\n /**\n * Keep Tab inside the panel and restore focus to the previously focused element on close, unless\n * an overlay opened after this one is still open.\n */\n trapFocus?: boolean;\n /** Prevent the page behind the overlay from scrolling. */\n lockScroll?: boolean;\n}\n\n/**\n * The three behaviours every blocking overlay needs: Escape to dismiss, a focus trap, and a\n * scroll lock on the page behind it.\n *\n * They share one module-level stack because they share one question: overlays nest (a\n * ConfirmModal opened from inside a Modal), every instance listens on `document`, and only the\n * innermost one may react. Without the stack, Escape would close every open layer at once and\n * the outer trap would yank focus out of the inner panel on the next Tab.\n *\n * The scroll lock is reference-counted for the same reason: the page stays locked until the last\n * overlay closes, and the body's original inline styles are restored rather than cleared.\n */\nexport function useOverlay({ isActive, containerRef, onEscape, trapFocus = true, lockScroll = true }: UseOverlayArgs) {\n const onEscapeRef = useRef(onEscape);\n useEffect(() => {\n onEscapeRef.current = onEscape;\n });\n\n // The layer's position in the stack is claimed during render, not in the effect below, because\n // effects run child-first: an overlay opened from *inside* another one (a menu in a modal) has\n // its effect run before its own parent's and would come out as the lower layer. Render runs\n // parent-first, which is the order the layers are actually nested in. DOM order is no help\n // either — React commits the inner portal ahead of the outer one.\n const orderRef = useRef(0);\n if (isActive && orderRef.current === 0) orderRef.current = nextLayerOrder++;\n if (!isActive && orderRef.current !== 0) orderRef.current = 0;\n\n useEffect(() => {\n if (!isActive) return;\n\n const order = orderRef.current;\n activeLayers.add(order);\n const isTopmost = () => order === Math.max(...activeLayers);\n\n const previouslyFocused = document.activeElement as HTMLElement | null;\n if (trapFocus) {\n const container = containerRef.current;\n (tabbablesIn(container)[0] ?? container)?.focus();\n }\n\n function onKeyDown(event: KeyboardEvent) {\n if (!isTopmost()) return;\n\n if (event.key === 'Escape' && onEscapeRef.current) {\n event.preventDefault();\n onEscapeRef.current();\n return;\n }\n\n if (event.key !== 'Tab' || !trapFocus) return;\n const focusables = tabbablesIn(containerRef.current);\n // Nothing to move to inside the panel, but Tab must still not escape it.\n if (focusables.length === 0) {\n event.preventDefault();\n return;\n }\n const first = focusables[0];\n const last = focusables[focusables.length - 1];\n const active = document.activeElement;\n if (!containerRef.current?.contains(active)) {\n event.preventDefault();\n first.focus();\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n } else if (event.shiftKey && active === first) {\n event.preventDefault();\n last.focus();\n }\n }\n\n // A key held as this opened would repeat into what it focused: a held Enter pressed Close.\n // Outside the panel it still repeats, since suggestions open while the user types.\n const pressedSinceOpen = new Set<string>();\n const keyId = (event: KeyboardEvent) => event.code || event.key;\n function onKeyDownCapture(event: KeyboardEvent) {\n if (!event.repeat) pressedSinceOpen.add(keyId(event));\n else if (!pressedSinceOpen.has(keyId(event)) && containerRef.current?.contains(event.target as Node)) {\n event.preventDefault();\n event.stopPropagation();\n }\n }\n function onKeyUp(event: KeyboardEvent) {\n pressedSinceOpen.delete(keyId(event));\n }\n\n // `window`, not `document`: a key event on a focused element reaches both, but a synthetic\n // one dispatched straight at `window` (as tests and some consumers do) never reaches\n // `document`, since it sits above it in the propagation path.\n window.addEventListener('keydown', onKeyDown);\n window.addEventListener('keydown', onKeyDownCapture, true);\n window.addEventListener('keyup', onKeyUp, true);\n if (lockScroll) acquireScrollLock();\n\n return () => {\n window.removeEventListener('keydown', onKeyDown);\n window.removeEventListener('keydown', onKeyDownCapture, true);\n window.removeEventListener('keyup', onKeyUp, true);\n if (lockScroll) releaseScrollLock();\n activeLayers.delete(order);\n // Modal closes a commit late, so a Popover opened by the same click already holds the focus.\n const coveredByLaterLayer = [...activeLayers].some((other) => other > order);\n if (trapFocus && !coveredByLaterLayer) previouslyFocused?.focus();\n };\n }, [isActive, trapFocus, lockScroll, containerRef]);\n}\n\n/** Monotonic, so a layer opened later always outranks one opened earlier. */\nlet nextLayerOrder = 1;\nconst activeLayers = new Set<number>();\n\nlet scrollLockCount = 0;\nlet previousOverflow = '';\nlet previousPaddingRight = '';\n\nfunction acquireScrollLock() {\n const { body } = document;\n if (scrollLockCount === 0) {\n previousOverflow = body.style.overflow;\n previousPaddingRight = body.style.paddingRight;\n // Removing the page's scrollbar reclaims its width, so everything behind the overlay would\n // shift right by that amount without the compensating padding.\n const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;\n if (scrollbarWidth > 0) {\n const current = parseFloat(window.getComputedStyle(body).paddingRight) || 0;\n body.style.paddingRight = `${current + scrollbarWidth}px`;\n }\n body.style.overflow = 'hidden';\n }\n scrollLockCount += 1;\n}\n\nfunction releaseScrollLock() {\n scrollLockCount -= 1;\n if (scrollLockCount > 0) return;\n scrollLockCount = 0;\n document.body.style.overflow = previousOverflow;\n document.body.style.paddingRight = previousPaddingRight;\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{usePresentationDefault as n}from"../configProvider/usePresentationDefault.mjs";import r from"../bodyEnd/bodyEnd.mjs";import i from"../styles/sheet.module.mjs";import{useVisualViewportSize as a}from"../hooks/useVisualViewportSize.mjs";import o from"../hooks/useSheet.mjs";import{useOverlay as s}from"../hooks/useOverlay.mjs";import c from"../styles/scrollFade.module.mjs";import l from"./modal.module.mjs";import{useScrollEdges as u}from"../hooks/useScrollEdges.mjs";import{useEffect as d,useId as f,useRef as p,useState as m}from"react";import h from"classnames";import{FontAwesomeIcon as g}from"@fortawesome/react-fontawesome";import{faXmark as _}from"@fortawesome/free-solid-svg-icons";import{jsx as v,jsxs as y}from"react/jsx-runtime";var b={close:`Close`};function x(x){let{children:S=null,title:C,isOpen:w=!1,onClose:T=()=>null,footer:E=null,className:D=``,style:O,hideHeader:k=!1,closeOnEscape:A=!0,presentation:j,classNames:M,styles:N,labels:P,...F}=e(`Modal`,x),I=t(`Modal`,P,b),[L,R]=m(w),[z,B]=m(w),V=p(null),H=p(null),U=f(),{height:W,offsetTop:G}=a(),K=n(),{isSheet:q,sheetStyle:J}=o(j??K);d(()=>{w
|
|
1
|
+
"use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{usePresentationDefault as n}from"../configProvider/usePresentationDefault.mjs";import r from"../bodyEnd/bodyEnd.mjs";import i from"../styles/sheet.module.mjs";import{useVisualViewportSize as a}from"../hooks/useVisualViewportSize.mjs";import o from"../hooks/useSheet.mjs";import{useOverlay as s}from"../hooks/useOverlay.mjs";import c from"../styles/scrollFade.module.mjs";import l from"./modal.module.mjs";import{useScrollEdges as u}from"../hooks/useScrollEdges.mjs";import{useEffect as d,useId as f,useRef as p,useState as m}from"react";import h from"classnames";import{FontAwesomeIcon as g}from"@fortawesome/react-fontawesome";import{faXmark as _}from"@fortawesome/free-solid-svg-icons";import{jsx as v,jsxs as y}from"react/jsx-runtime";var b={close:`Close`};function x(x){let{children:S=null,title:C,isOpen:w=!1,onClose:T=()=>null,footer:E=null,className:D=``,style:O,hideHeader:k=!1,closeOnEscape:A=!0,presentation:j,classNames:M,styles:N,labels:P,...F}=e(`Modal`,x),I=t(`Modal`,P,b),[L,R]=m(w),[z,B]=m(w),V=p(null),H=p(null),U=f(),{height:W,offsetTop:G}=a(),K=n(),{isSheet:q,sheetStyle:J}=o(j??K);d(()=>{if(w){R(!0);let e=setTimeout(()=>B(!0),1);return()=>clearTimeout(e)}B(!1);let e=setTimeout(()=>R(!1),300);return()=>clearTimeout(e)},[w]),s({isActive:L&&z,containerRef:V,onEscape:A?T:void 0});let{hasStart:Y,hasEnd:X}=u(H,{isEnabled:L&&z});if(!L)return null;let Z=!k&&!!C;return v(r,{children:y(`div`,{className:h(l.modalContainer,M?.container),style:{height:W,top:G,...N?.container},children:[v(`div`,{className:h(l.modalWrapper,z&&l.modalWrapperShown,M?.backdrop),style:N?.backdrop,onClick:T}),z&&y(`div`,{...F,ref:V,role:`dialog`,"aria-modal":`true`,"aria-labelledby":Z&&!F[`aria-label`]?U:void 0,tabIndex:-1,className:h(l.modal,z&&l.modalShown,q&&[i.sheet,i.sheetSurface,l.modalSheet],D,M?.modal),style:{...q?J:void 0,...O,...N?.modal},onClick:e=>e.stopPropagation(),children:[!k&&y(`div`,{className:h(l.modalHeader,M?.header),style:N?.header,children:[v(`div`,{id:U,className:M?.title,style:N?.title,children:C}),v(`button`,{type:`button`,onClick:T,"aria-label":I.close,className:h(l.modalCloseButton,M?.closeButton),style:N?.closeButton,children:v(g,{icon:_})})]}),v(`div`,{ref:H,className:h(l.modalBody,c.scrollFade,c.scrollFadeY,Y&&c.scrollFadeFadedStart,X&&c.scrollFadeFadedEnd,M?.body),style:N?.body,children:S}),E&&v(`div`,{className:h(l.modalFooter,M?.footer),style:N?.footer,children:E})]})]})})}export{x as default};
|
|
2
2
|
//# sourceMappingURL=modal.mjs.map
|