@hjmds/react 1.12.0 → 1.13.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/dist/actions.d.ts +4 -0
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +4 -1
- package/dist/actions.js.map +1 -1
- package/dist/activity-heatmap.d.ts +4 -1
- package/dist/activity-heatmap.d.ts.map +1 -1
- package/dist/activity-heatmap.js +1 -1
- package/dist/activity-heatmap.js.map +1 -1
- package/dist/advanced-display.d.ts +31 -3
- package/dist/advanced-display.d.ts.map +1 -1
- package/dist/advanced-display.js +26 -27
- package/dist/advanced-display.js.map +1 -1
- package/dist/advanced-forms.d.ts +27 -2
- package/dist/advanced-forms.d.ts.map +1 -1
- package/dist/advanced-forms.js +11 -7
- package/dist/advanced-forms.js.map +1 -1
- package/dist/affix.d.ts +4 -1
- package/dist/affix.d.ts.map +1 -1
- package/dist/affix.js +2 -2
- package/dist/affix.js.map +1 -1
- package/dist/agreement.d.ts +3 -0
- package/dist/agreement.d.ts.map +1 -1
- package/dist/agreement.js +14 -3
- package/dist/agreement.js.map +1 -1
- package/dist/anchor.d.ts +5 -0
- package/dist/anchor.d.ts.map +1 -1
- package/dist/anchor.js +2 -2
- package/dist/anchor.js.map +1 -1
- package/dist/asset.d.ts +8 -1
- package/dist/asset.d.ts.map +1 -1
- package/dist/asset.js +4 -4
- package/dist/asset.js.map +1 -1
- package/dist/auth-screen.d.ts +5 -0
- package/dist/auth-screen.d.ts.map +1 -1
- package/dist/auth-screen.js +2 -1
- package/dist/auth-screen.js.map +1 -1
- package/dist/bottom-cta.d.ts +5 -0
- package/dist/bottom-cta.d.ts.map +1 -1
- package/dist/bottom-cta.js +2 -2
- package/dist/bottom-cta.js.map +1 -1
- package/dist/bottom-info.d.ts +5 -0
- package/dist/bottom-info.d.ts.map +1 -1
- package/dist/bottom-info.js +2 -2
- package/dist/bottom-info.js.map +1 -1
- package/dist/bottom-navigation.d.ts +3 -0
- package/dist/bottom-navigation.d.ts.map +1 -1
- package/dist/bottom-navigation.js +2 -1
- package/dist/bottom-navigation.js.map +1 -1
- package/dist/breadcrumb.d.ts +3 -0
- package/dist/breadcrumb.d.ts.map +1 -1
- package/dist/breadcrumb.js +2 -2
- package/dist/breadcrumb.js.map +1 -1
- package/dist/calendar.d.ts +4 -1
- package/dist/calendar.d.ts.map +1 -1
- package/dist/calendar.js +2 -2
- package/dist/calendar.js.map +1 -1
- package/dist/carousel-motion.d.ts +3 -0
- package/dist/carousel-motion.d.ts.map +1 -1
- package/dist/carousel-motion.js +1 -1
- package/dist/carousel-motion.js.map +1 -1
- package/dist/carousel.d.ts +3 -0
- package/dist/carousel.d.ts.map +1 -1
- package/dist/carousel.js +2 -2
- package/dist/carousel.js.map +1 -1
- package/dist/celebration.d.ts.map +1 -1
- package/dist/celebration.js.map +1 -1
- package/dist/clipboard.d.ts.map +1 -1
- package/dist/clipboard.js +6 -2
- package/dist/clipboard.js.map +1 -1
- package/dist/code-block.d.ts +4 -1
- package/dist/code-block.d.ts.map +1 -1
- package/dist/code-block.js +1 -1
- package/dist/code-block.js.map +1 -1
- package/dist/collapsible.d.ts +3 -0
- package/dist/collapsible.d.ts.map +1 -1
- package/dist/collapsible.js +2 -2
- package/dist/collapsible.js.map +1 -1
- package/dist/color-picker.d.ts +4 -1
- package/dist/color-picker.d.ts.map +1 -1
- package/dist/color-picker.js +2 -2
- package/dist/color-picker.js.map +1 -1
- package/dist/command-palette.d.ts +6 -0
- package/dist/command-palette.d.ts.map +1 -1
- package/dist/command-palette.js +49 -15
- package/dist/command-palette.js.map +1 -1
- package/dist/composition-style.d.ts +14 -1
- package/dist/composition-style.d.ts.map +1 -1
- package/dist/composition-style.js.map +1 -1
- package/dist/content-transition.d.ts +6 -2
- package/dist/content-transition.d.ts.map +1 -1
- package/dist/content-transition.js +4 -4
- package/dist/content-transition.js.map +1 -1
- package/dist/context-menu.d.ts +4 -1
- package/dist/context-menu.d.ts.map +1 -1
- package/dist/context-menu.js +2 -2
- package/dist/context-menu.js.map +1 -1
- package/dist/data-table.d.ts +3 -0
- package/dist/data-table.d.ts.map +1 -1
- package/dist/data-table.js +31 -11
- package/dist/data-table.js.map +1 -1
- package/dist/date-picker.d.ts +4 -1
- package/dist/date-picker.d.ts.map +1 -1
- package/dist/date-picker.js +2 -2
- package/dist/date-picker.js.map +1 -1
- package/dist/date-range.d.ts +4 -1
- package/dist/date-range.d.ts.map +1 -1
- package/dist/date-range.js +2 -2
- package/dist/date-range.js.map +1 -1
- package/dist/display.d.ts +4 -0
- package/dist/display.d.ts.map +1 -1
- package/dist/display.js.map +1 -1
- package/dist/duration-field.d.ts +4 -2
- package/dist/duration-field.d.ts.map +1 -1
- package/dist/duration-field.js +2 -2
- package/dist/duration-field.js.map +1 -1
- package/dist/effect-surface.d.ts +4 -2
- package/dist/effect-surface.d.ts.map +1 -1
- package/dist/effect-surface.js +2 -2
- package/dist/effect-surface.js.map +1 -1
- package/dist/evidence.d.ts +1 -1
- package/dist/evidence.js +1 -1
- package/dist/evidence.js.map +1 -1
- package/dist/feedback.d.ts +24 -13
- package/dist/feedback.d.ts.map +1 -1
- package/dist/feedback.js +17 -15
- package/dist/feedback.js.map +1 -1
- package/dist/file-picker.d.ts +5 -0
- package/dist/file-picker.d.ts.map +1 -1
- package/dist/file-picker.js +2 -2
- package/dist/file-picker.js.map +1 -1
- package/dist/floating-action-button.d.ts.map +1 -1
- package/dist/floating-action-button.js.map +1 -1
- package/dist/folder-preview.d.ts +4 -1
- package/dist/folder-preview.d.ts.map +1 -1
- package/dist/folder-preview.js +2 -2
- package/dist/folder-preview.js.map +1 -1
- package/dist/forms.d.ts +35 -3
- package/dist/forms.d.ts.map +1 -1
- package/dist/forms.js +55 -36
- package/dist/forms.js.map +1 -1
- package/dist/gravity-letters.d.ts +4 -2
- package/dist/gravity-letters.d.ts.map +1 -1
- package/dist/gravity-letters.js +2 -2
- package/dist/gravity-letters.js.map +1 -1
- package/dist/grid-reveal.d.ts +4 -2
- package/dist/grid-reveal.d.ts.map +1 -1
- package/dist/grid-reveal.js +2 -2
- package/dist/grid-reveal.js.map +1 -1
- package/dist/heading.d.ts +5 -0
- package/dist/heading.d.ts.map +1 -1
- package/dist/heading.js +8 -1
- package/dist/heading.js.map +1 -1
- package/dist/inline-confirm.d.ts +3 -0
- package/dist/inline-confirm.d.ts.map +1 -1
- package/dist/inline-confirm.js +3 -3
- package/dist/inline-confirm.js.map +1 -1
- package/dist/internal/message-reactions.d.ts +16 -0
- package/dist/internal/message-reactions.d.ts.map +1 -0
- package/dist/internal/message-reactions.js +42 -0
- package/dist/internal/message-reactions.js.map +1 -0
- package/dist/internal/spinner.d.ts +18 -0
- package/dist/internal/spinner.d.ts.map +1 -0
- package/dist/internal/spinner.js +10 -0
- package/dist/internal/spinner.js.map +1 -0
- package/dist/layout.d.ts +12 -0
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +6 -6
- package/dist/layout.js.map +1 -1
- package/dist/masonry.d.ts +4 -1
- package/dist/masonry.d.ts.map +1 -1
- package/dist/masonry.js +5 -3
- package/dist/masonry.js.map +1 -1
- package/dist/mentions.d.ts.map +1 -1
- package/dist/mentions.js +12 -3
- package/dist/mentions.js.map +1 -1
- package/dist/menu-morph.d.ts +8 -1
- package/dist/menu-morph.d.ts.map +1 -1
- package/dist/menu-morph.js +3 -3
- package/dist/menu-morph.js.map +1 -1
- package/dist/menubar.d.ts +4 -1
- package/dist/menubar.d.ts.map +1 -1
- package/dist/menubar.js +4 -3
- package/dist/menubar.js.map +1 -1
- package/dist/modal.d.ts.map +1 -1
- package/dist/modal.js +4 -1
- package/dist/modal.js.map +1 -1
- package/dist/navigation-bar.d.ts +4 -1
- package/dist/navigation-bar.d.ts.map +1 -1
- package/dist/navigation-bar.js +2 -2
- package/dist/navigation-bar.js.map +1 -1
- package/dist/navigation.d.ts +5 -0
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +6 -6
- package/dist/navigation.js.map +1 -1
- package/dist/notification-bell.d.ts +4 -1
- package/dist/notification-bell.d.ts.map +1 -1
- package/dist/notification-bell.js +5 -3
- package/dist/notification-bell.js.map +1 -1
- package/dist/number-field.d.ts +11 -0
- package/dist/number-field.d.ts.map +1 -1
- package/dist/number-field.js +8 -3
- package/dist/number-field.js.map +1 -1
- package/dist/overlay-stack.d.ts.map +1 -1
- package/dist/overlay-stack.js.map +1 -1
- package/dist/overlays.d.ts +5 -0
- package/dist/overlays.d.ts.map +1 -1
- package/dist/overlays.js +35 -11
- package/dist/overlays.js.map +1 -1
- package/dist/pagination.d.ts +5 -0
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +2 -2
- package/dist/pagination.js.map +1 -1
- package/dist/popover.d.ts.map +1 -1
- package/dist/popover.js +5 -4
- package/dist/popover.js.map +1 -1
- package/dist/portal.d.ts.map +1 -1
- package/dist/portal.js +2 -1
- package/dist/portal.js.map +1 -1
- package/dist/provider-button.d.ts +5 -0
- package/dist/provider-button.d.ts.map +1 -1
- package/dist/provider-button.js +2 -2
- package/dist/provider-button.js.map +1 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js.map +1 -1
- package/dist/qr-code.d.ts +4 -1
- package/dist/qr-code.d.ts.map +1 -1
- package/dist/qr-code.js +2 -2
- package/dist/qr-code.js.map +1 -1
- package/dist/reaction-picker.d.ts +6 -2
- package/dist/reaction-picker.d.ts.map +1 -1
- package/dist/reaction-picker.js +20 -4
- package/dist/reaction-picker.js.map +1 -1
- package/dist/saved-items.d.ts +33 -0
- package/dist/saved-items.d.ts.map +1 -0
- package/dist/saved-items.js +19 -0
- package/dist/saved-items.js.map +1 -0
- package/dist/screen-flows.d.ts +323 -0
- package/dist/screen-flows.d.ts.map +1 -0
- package/dist/screen-flows.js +212 -0
- package/dist/screen-flows.js.map +1 -0
- package/dist/screens.d.ts +120 -0
- package/dist/screens.d.ts.map +1 -0
- package/dist/screens.js +66 -0
- package/dist/screens.js.map +1 -0
- package/dist/select.d.ts +6 -0
- package/dist/select.d.ts.map +1 -1
- package/dist/select.js +13 -3
- package/dist/select.js.map +1 -1
- package/dist/selection.d.ts +28 -1
- package/dist/selection.d.ts.map +1 -1
- package/dist/selection.js +12 -12
- package/dist/selection.js.map +1 -1
- package/dist/side-panel.d.ts.map +1 -1
- package/dist/side-panel.js.map +1 -1
- package/dist/sidebar.d.ts +3 -0
- package/dist/sidebar.d.ts.map +1 -1
- package/dist/sidebar.js +2 -2
- package/dist/sidebar.js.map +1 -1
- package/dist/skip-nav.d.ts.map +1 -1
- package/dist/skip-nav.js.map +1 -1
- package/dist/slider.d.ts +5 -0
- package/dist/slider.d.ts.map +1 -1
- package/dist/slider.js +2 -1
- package/dist/slider.js.map +1 -1
- package/dist/sortable.d.ts +3 -0
- package/dist/sortable.d.ts.map +1 -1
- package/dist/sortable.js +1 -1
- package/dist/sortable.js.map +1 -1
- package/dist/splitter.d.ts +5 -0
- package/dist/splitter.d.ts.map +1 -1
- package/dist/splitter.js +28 -3
- package/dist/splitter.js.map +1 -1
- package/dist/step-player.d.ts +4 -1
- package/dist/step-player.d.ts.map +1 -1
- package/dist/step-player.js +2 -2
- package/dist/step-player.js.map +1 -1
- package/dist/steps.d.ts +5 -0
- package/dist/steps.d.ts.map +1 -1
- package/dist/steps.js +2 -2
- package/dist/steps.js.map +1 -1
- package/dist/styles.css +245 -65
- package/dist/styles.layered.css +245 -65
- package/dist/supplemental-display.d.ts +4 -0
- package/dist/supplemental-display.d.ts.map +1 -1
- package/dist/supplemental-display.js +7 -4
- package/dist/supplemental-display.js.map +1 -1
- package/dist/supplemental-navigation.d.ts +5 -0
- package/dist/supplemental-navigation.d.ts.map +1 -1
- package/dist/supplemental-navigation.js +2 -2
- package/dist/supplemental-navigation.js.map +1 -1
- package/dist/swipe-actions.d.ts +4 -1
- package/dist/swipe-actions.d.ts.map +1 -1
- package/dist/swipe-actions.js +2 -2
- package/dist/swipe-actions.js.map +1 -1
- package/dist/tags-input.d.ts +5 -0
- package/dist/tags-input.d.ts.map +1 -1
- package/dist/tags-input.js +2 -2
- package/dist/tags-input.js.map +1 -1
- package/dist/task-list.d.ts +4 -1
- package/dist/task-list.d.ts.map +1 -1
- package/dist/task-list.js +3 -3
- package/dist/task-list.js.map +1 -1
- package/dist/text-formats.d.ts +5 -0
- package/dist/text-formats.d.ts.map +1 -1
- package/dist/text-formats.js +2 -1
- package/dist/text-formats.js.map +1 -1
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +22 -2
- package/dist/theme.js.map +1 -1
- package/dist/thinking-orb.d.ts +4 -1
- package/dist/thinking-orb.d.ts.map +1 -1
- package/dist/thinking-orb.js +2 -2
- package/dist/thinking-orb.js.map +1 -1
- package/dist/toast.d.ts.map +1 -1
- package/dist/toast.js +10 -4
- package/dist/toast.js.map +1 -1
- package/dist/toggle-group.d.ts +3 -0
- package/dist/toggle-group.d.ts.map +1 -1
- package/dist/toggle-group.js +2 -1
- package/dist/toggle-group.js.map +1 -1
- package/dist/top-bar.d.ts +5 -0
- package/dist/top-bar.d.ts.map +1 -1
- package/dist/top-bar.js +2 -2
- package/dist/top-bar.js.map +1 -1
- package/dist/top.d.ts +5 -0
- package/dist/top.d.ts.map +1 -1
- package/dist/top.js +2 -2
- package/dist/top.js.map +1 -1
- package/dist/tour.d.ts.map +1 -1
- package/dist/tour.js +3 -2
- package/dist/tour.js.map +1 -1
- package/dist/transfer-list.d.ts +3 -0
- package/dist/transfer-list.d.ts.map +1 -1
- package/dist/transfer-list.js +2 -2
- package/dist/transfer-list.js.map +1 -1
- package/dist/tree.d.ts +3 -0
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +17 -5
- package/dist/tree.js.map +1 -1
- package/dist/upload-item.d.ts +5 -0
- package/dist/upload-item.d.ts.map +1 -1
- package/dist/upload-item.js +2 -2
- package/dist/upload-item.js.map +1 -1
- package/dist/virtual-list.d.ts +4 -1
- package/dist/virtual-list.d.ts.map +1 -1
- package/dist/virtual-list.js +2 -2
- package/dist/virtual-list.js.map +1 -1
- package/dist/voice-note.d.ts +4 -1
- package/dist/voice-note.d.ts.map +1 -1
- package/dist/voice-note.js +2 -2
- package/dist/voice-note.js.map +1 -1
- package/dist/watermark.d.ts +4 -1
- package/dist/watermark.d.ts.map +1 -1
- package/dist/watermark.js +2 -2
- package/dist/watermark.js.map +1 -1
- package/package.json +18 -3
package/dist/top-bar.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type HTMLAttributes, type MouseEventHandler, type ReactNode } from "react";
|
|
2
|
+
import type { HjmCompositionStyleProp } from "./composition-style.js";
|
|
2
3
|
export type TopBarProps = Omit<HTMLAttributes<HTMLDivElement>, "title" | "children"> & Readonly<{
|
|
3
4
|
title?: string;
|
|
4
5
|
titleLeading?: ReactNode;
|
|
@@ -10,6 +11,8 @@ export type TopBarProps = Omit<HTMLAttributes<HTMLDivElement>, "title" | "childr
|
|
|
10
11
|
actions?: ReactNode;
|
|
11
12
|
centered?: boolean;
|
|
12
13
|
safeAreaTop?: number;
|
|
14
|
+
/** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */
|
|
15
|
+
layoutStyle?: HjmCompositionStyleProp;
|
|
13
16
|
}>;
|
|
14
17
|
/** Screen chrome stays composable inside pages and dialogs, without adding a second banner landmark. */
|
|
15
18
|
export declare const TopBar: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "title" | "children"> & Readonly<{
|
|
@@ -23,5 +26,7 @@ export declare const TopBar: import("react").ForwardRefExoticComponent<Omit<HTML
|
|
|
23
26
|
actions?: ReactNode;
|
|
24
27
|
centered?: boolean;
|
|
25
28
|
safeAreaTop?: number;
|
|
29
|
+
/** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */
|
|
30
|
+
layoutStyle?: HjmCompositionStyleProp;
|
|
26
31
|
}> & import("react").RefAttributes<HTMLDivElement>>;
|
|
27
32
|
//# sourceMappingURL=top-bar.d.ts.map
|
package/dist/top-bar.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"top-bar.d.ts","sourceRoot":"","sources":["../src/top-bar.tsx"],"names":[],"mappings":"AACA,OAAO,EAAkC,KAAK,cAAc,EAAE,KAAK,iBAAiB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"top-bar.d.ts","sourceRoot":"","sources":["../src/top-bar.tsx"],"names":[],"mappings":"AACA,OAAO,EAAkC,KAAK,cAAc,EAAE,KAAK,iBAAiB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIpH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC,GAAG,QAAQ,CAAC;IAC9F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,YAAY,CAAC,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IACpD,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACrC,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAEH,wGAAwG;AACxG,eAAO,MAAM,MAAM;YAfT,MAAM;mBACC,SAAS;mBACT,iBAAiB,CAAC,iBAAiB,CAAC;8BACzB,MAAM;mBACjB,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC;cAC1B,SAAS;eACR,SAAS;cACV,SAAS;eACR,OAAO;kBACJ,MAAM;IACpB,gGAAgG;kBAClF,uBAAuB;mDAmCrC,CAAC"}
|
package/dist/top-bar.js
CHANGED
|
@@ -5,7 +5,7 @@ import { Button } from "./actions.js";
|
|
|
5
5
|
import { classNames } from "./internal.js";
|
|
6
6
|
import { useOptionalHjmTheme } from "./provider.js";
|
|
7
7
|
/** Screen chrome stays composable inside pages and dialogs, without adding a second banner landmark. */
|
|
8
|
-
export const TopBar = forwardRef(function TopBar({ title, titleLeading, onTitleClick, titleAccessibilityLabel, headingLevel = 1, leading, trailing, actions, centered = topBarRecipe.defaults.centered, safeAreaTop = 0, className, style, ...props }, ref) {
|
|
8
|
+
export const TopBar = forwardRef(function TopBar({ title, titleLeading, onTitleClick, titleAccessibilityLabel, headingLevel = 1, leading, trailing, actions, centered = topBarRecipe.defaults.centered, safeAreaTop = 0, className, style, layoutStyle, ...props }, ref) {
|
|
9
9
|
const theme = useOptionalHjmTheme();
|
|
10
10
|
if (!Number.isFinite(safeAreaTop) || safeAreaTop < 0)
|
|
11
11
|
throw new RangeError("TopBar safeAreaTop must be non-negative");
|
|
@@ -23,7 +23,7 @@ export const TopBar = forwardRef(function TopBar({ title, titleLeading, onTitleC
|
|
|
23
23
|
"--hjm-top-bar-min-height": `${topBarRecipe.minHeight}px`,
|
|
24
24
|
"--hjm-top-bar-padding": `${topBarRecipe.paddingHorizontal}px`,
|
|
25
25
|
"--hjm-top-bar-gap": `${topBarRecipe.gap}px`,
|
|
26
|
-
"--hjm-top-bar-safe-area": `${safeAreaTop}px`, ...style,
|
|
26
|
+
"--hjm-top-bar-safe-area": `${safeAreaTop}px`, ...style, ...layoutStyle,
|
|
27
27
|
}, children: [_jsx("div", { className: "hjm-top-bar__leading", children: leading }), _jsx("div", { className: "hjm-top-bar__title", children: title === undefined ? null : _jsx(Heading, { className: "hjm-top-bar__heading", children: onTitleClick ? _jsx(Button, { tone: "ghost", onClick: onTitleClick, "aria-label": titleAccessibilityLabel, leading: titleLeading, children: title })
|
|
28
28
|
: _jsxs("span", { "aria-label": titleAccessibilityLabel, children: [titleLeading, title] }) }) }), _jsx("div", { className: "hjm-top-bar__trailing", children: actions ?? trailing })] });
|
|
29
29
|
});
|
package/dist/top-bar.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"top-bar.js","sourceRoot":"","sources":["../src/top-bar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAmF,MAAM,OAAO,CAAC;AACpH,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"top-bar.js","sourceRoot":"","sources":["../src/top-bar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAmF,MAAM,OAAO,CAAC;AACpH,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAkBpD,wGAAwG;AACxG,MAAM,CAAC,MAAM,MAAM,GAAG,UAAU,CAA8B,SAAS,MAAM,CAAC,EAC5E,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,uBAAuB,EAAE,YAAY,GAAG,CAAC,EAC5E,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,QAAQ,EAAE,WAAW,GAAG,CAAC,EACtF,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,KAAK,EACxC,EAAE,GAAG;IACJ,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAC;IACpC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,WAAW,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,yCAAyC,CAAC,CAAC;IACtH,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;QAAE,MAAM,IAAI,SAAS,CAAC,gCAAgC,CAAC,CAAC;IAChG,IAAI,uBAAuB,KAAK,SAAS,IAAI,CAAC,uBAAuB,CAAC,IAAI,EAAE;QAAE,MAAM,IAAI,SAAS,CAAC,kDAAkD,CAAC,CAAC;IACtJ,IAAI,OAAO,KAAK,SAAS,IAAI,QAAQ,KAAK,SAAS;QAAE,MAAM,IAAI,SAAS,CAAC,2CAA2C,CAAC,CAAC;IACtH,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,YAAY,KAAK,SAAS,IAAI,YAAY,KAAK,SAAS,IAAI,uBAAuB,KAAK,SAAS,CAAC,EAAE,CAAC;QAC/H,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,CAAC;IAClE,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,YAAY,EAA6C,CAAC;IAC9E,OAAO,kBAAS,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,UAAU,CAAC,aAAa,EAAE,SAAS,CAAC,mBAC/D,QAAQ,qBAAmB,CAAC,KAAK,EAAE,WAAW,CAAC,SAAS,IAAI,CAAC,CAAC,IAAI,YAAY,CAAC,kBAAkB,EAChH,KAAK,EAAE;YACL,0BAA0B,EAAE,GAAG,YAAY,CAAC,SAAS,IAAI;YACzD,uBAAuB,EAAE,GAAG,YAAY,CAAC,iBAAiB,IAAI;YAC9D,mBAAmB,EAAE,GAAG,YAAY,CAAC,GAAG,IAAI;YAC5C,yBAAyB,EAAE,GAAG,WAAW,IAAI,EAAE,GAAG,KAAK,EAAE,GAAG,WAAW;SACvD,aAClB,cAAK,SAAS,EAAC,sBAAsB,YAAE,OAAO,GAAO,EACrD,cAAK,SAAS,EAAC,oBAAoB,YAChC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAC,OAAO,IAAC,SAAS,EAAC,sBAAsB,YACpE,YAAY,CAAC,CAAC,CAAC,KAAC,MAAM,IAAC,IAAI,EAAC,OAAO,EAAC,OAAO,EAAE,YAAY,gBAAc,uBAAuB,EAAE,OAAO,EAAE,YAAY,YAAG,KAAK,GAAU;wBACtI,CAAC,CAAC,8BAAkB,uBAAuB,aAAG,YAAY,EAAE,KAAK,IAAQ,GACnE,GACN,EACN,cAAK,SAAS,EAAC,uBAAuB,YAAE,OAAO,IAAI,QAAQ,GAAO,IAC9D,CAAC;AACT,CAAC,CAAC,CAAC","sourcesContent":["import { topBarRecipe } from \"@hjmds/design-contracts/recipes\";\nimport { forwardRef, type CSSProperties, type HTMLAttributes, type MouseEventHandler, type ReactNode } from \"react\";\nimport { Button } from \"./actions.js\";\nimport { classNames } from \"./internal.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type TopBarProps = Omit<HTMLAttributes<HTMLDivElement>, \"title\" | \"children\"> & Readonly<{\n title?: string;\n titleLeading?: ReactNode;\n onTitleClick?: MouseEventHandler<HTMLButtonElement>;\n titleAccessibilityLabel?: string;\n headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;\n leading?: ReactNode;\n trailing?: ReactNode;\n actions?: ReactNode;\n centered?: boolean;\n safeAreaTop?: number;\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\n/** Screen chrome stays composable inside pages and dialogs, without adding a second banner landmark. */\nexport const TopBar = forwardRef<HTMLDivElement, TopBarProps>(function TopBar({\n title, titleLeading, onTitleClick, titleAccessibilityLabel, headingLevel = 1,\n leading, trailing, actions, centered = topBarRecipe.defaults.centered, safeAreaTop = 0,\n className, style, layoutStyle, ...props\n}, ref) {\n const theme = useOptionalHjmTheme();\n if (!Number.isFinite(safeAreaTop) || safeAreaTop < 0) throw new RangeError(\"TopBar safeAreaTop must be non-negative\");\n if (title !== undefined && !title.trim()) throw new TypeError(\"TopBar title must not be empty\");\n if (titleAccessibilityLabel !== undefined && !titleAccessibilityLabel.trim()) throw new TypeError(\"TopBar titleAccessibilityLabel must not be empty\");\n if (actions !== undefined && trailing !== undefined) throw new TypeError(\"TopBar accepts either trailing or actions\");\n if (title === undefined && (titleLeading !== undefined || onTitleClick !== undefined || titleAccessibilityLabel !== undefined)) {\n throw new TypeError(\"TopBar title affordances require a title\");\n }\n const Heading = `h${headingLevel}` as \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\";\n return <div {...props} ref={ref} className={classNames(\"hjm-top-bar\", className)}\n data-centered={centered} data-large-text={(theme?.environment.textScale ?? 1) >= topBarRecipe.largeTextThreshold}\n style={{\n \"--hjm-top-bar-min-height\": `${topBarRecipe.minHeight}px`,\n \"--hjm-top-bar-padding\": `${topBarRecipe.paddingHorizontal}px`,\n \"--hjm-top-bar-gap\": `${topBarRecipe.gap}px`,\n \"--hjm-top-bar-safe-area\": `${safeAreaTop}px`, ...style, ...layoutStyle,\n } as CSSProperties}>\n <div className=\"hjm-top-bar__leading\">{leading}</div>\n <div className=\"hjm-top-bar__title\">\n {title === undefined ? null : <Heading className=\"hjm-top-bar__heading\">\n {onTitleClick ? <Button tone=\"ghost\" onClick={onTitleClick} aria-label={titleAccessibilityLabel} leading={titleLeading}>{title}</Button>\n : <span aria-label={titleAccessibilityLabel}>{titleLeading}{title}</span>}\n </Heading>}\n </div>\n <div className=\"hjm-top-bar__trailing\">{actions ?? trailing}</div>\n </div>;\n});\n"]}
|
package/dist/top.d.ts
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { type TopDescriptor } from "@hjmds/design-contracts/components/top";
|
|
2
2
|
import { type ReactNode } from "react";
|
|
3
|
+
import type { HjmCompositionStyleProp } from "./composition-style.js";
|
|
3
4
|
export type TopProps = Readonly<{
|
|
4
5
|
descriptor: TopDescriptor;
|
|
5
6
|
/** Secondary action sharing the title row; drops below it when space runs out. */
|
|
6
7
|
trailing?: ReactNode;
|
|
7
8
|
className?: string;
|
|
9
|
+
/** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */
|
|
10
|
+
layoutStyle?: HjmCompositionStyleProp;
|
|
8
11
|
}>;
|
|
9
12
|
export declare const Top: import("react").ForwardRefExoticComponent<Readonly<{
|
|
10
13
|
descriptor: TopDescriptor;
|
|
11
14
|
/** Secondary action sharing the title row; drops below it when space runs out. */
|
|
12
15
|
trailing?: ReactNode;
|
|
13
16
|
className?: string;
|
|
17
|
+
/** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */
|
|
18
|
+
layoutStyle?: HjmCompositionStyleProp;
|
|
14
19
|
}> & import("react").RefAttributes<HTMLElement>>;
|
|
15
20
|
//# sourceMappingURL=top.d.ts.map
|
package/dist/top.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"top.d.ts","sourceRoot":"","sources":["../src/top.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAiD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"top.d.ts","sourceRoot":"","sources":["../src/top.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAiD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC;IAC9B,UAAU,EAAE,aAAa,CAAC;IAC1B,kFAAkF;IAClF,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAEH,eAAO,MAAM,GAAG;gBARF,aAAa;IACzB,kFAAkF;eACvE,SAAS;gBACR,MAAM;IAClB,gGAAgG;kBAClF,uBAAuB;gDAoCrC,CAAC"}
|
package/dist/top.js
CHANGED
|
@@ -2,12 +2,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { topDefaults, topRecipe, validateTopDescriptor, } from "@hjmds/design-contracts/components/top";
|
|
3
3
|
import { forwardRef, createElement } from "react";
|
|
4
4
|
import { classNames } from "./internal.js";
|
|
5
|
-
export const Top = forwardRef(function Top({ descriptor, trailing, className }, forwardedRef) {
|
|
5
|
+
export const Top = forwardRef(function Top({ descriptor, trailing, className, layoutStyle }, forwardedRef) {
|
|
6
6
|
validateTopDescriptor(descriptor);
|
|
7
7
|
const size = descriptor.size ?? topDefaults.size;
|
|
8
8
|
const level = descriptor.headingLevel ?? topDefaults.headingLevel;
|
|
9
9
|
const metrics = topRecipe.sizes[size];
|
|
10
|
-
return (_jsxs("header", { ref: forwardedRef, className: classNames("hjm-top", className), "data-size": size, style: {
|
|
10
|
+
return (_jsxs("header", { ref: forwardedRef, className: classNames("hjm-top", className), "data-size": size, style: { ...layoutStyle,
|
|
11
11
|
"--hjm-top-title-size": `${metrics.title.fontSize}px`,
|
|
12
12
|
"--hjm-top-title-line-height": `${metrics.title.lineHeight}px`,
|
|
13
13
|
"--hjm-top-title-weight": metrics.title.fontWeight,
|
package/dist/top.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"top.js","sourceRoot":"","sources":["../src/top.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,WAAW,EACX,SAAS,EACT,qBAAqB,GAEtB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,aAAa,EAAsC,MAAM,OAAO,CAAC;AACtF,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"top.js","sourceRoot":"","sources":["../src/top.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,WAAW,EACX,SAAS,EACT,qBAAqB,GAEtB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,aAAa,EAAsC,MAAM,OAAO,CAAC;AACtF,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAY3C,MAAM,CAAC,MAAM,GAAG,GAAG,UAAU,CAAwB,SAAS,GAAG,CAC/D,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,EAChD,YAAY;IAEZ,qBAAqB,CAAC,UAAU,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,IAAI,WAAW,CAAC,IAAI,CAAC;IACjD,MAAM,KAAK,GAAG,UAAU,CAAC,YAAY,IAAI,WAAW,CAAC,YAAY,CAAC;IAClE,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO,CACL,kBACE,GAAG,EAAE,YAAY,EACjB,SAAS,EAAE,UAAU,CAAC,SAAS,EAAE,SAAS,CAAC,eAChC,IAAI,EACf,KAAK,EAAE,EAAE,GAAG,WAAW;YACrB,sBAAsB,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,IAAI;YACrD,6BAA6B,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,IAAI;YAC9D,wBAAwB,EAAE,OAAO,CAAC,KAAK,CAAC,UAAU;YAClD,uBAAuB,EAAE,GAAG,OAAO,CAAC,UAAU,IAAI;YAClD,0BAA0B,EAAE,GAAG,OAAO,CAAC,aAAa,IAAI;SACxC,aAEjB,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,YAAG,SAAS,EAAC,kBAAkB,YAAE,UAAU,CAAC,OAAO,GAAK,CAAC,CAAC,CAAC,IAAI,EACrF,eAAK,SAAS,EAAC,cAAc,aAK1B,aAAa,CAAC,IAAI,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,gBAAgB,EAAE,EAAE,UAAU,CAAC,KAAK,CAAC,EAC7E,QAAQ,CAAC,CAAC,CAAC,cAAK,SAAS,EAAC,mBAAmB,YAAE,QAAQ,GAAO,CAAC,CAAC,CAAC,IAAI,IAClE,EACL,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,YAAG,SAAS,EAAC,sBAAsB,YAAE,UAAU,CAAC,WAAW,GAAK,CAAC,CAAC,CAAC,IAAI,IAC1F,CACV,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["import {\n topDefaults,\n topRecipe,\n validateTopDescriptor,\n type TopDescriptor,\n} from \"@hjmds/design-contracts/components/top\";\nimport { forwardRef, createElement, type CSSProperties, type ReactNode } from \"react\";\nimport { classNames } from \"./internal.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type TopProps = Readonly<{\n descriptor: TopDescriptor;\n /** Secondary action sharing the title row; drops below it when space runs out. */\n trailing?: ReactNode;\n className?: string;\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nexport const Top = forwardRef<HTMLElement, TopProps>(function Top(\n { descriptor, trailing, className, layoutStyle },\n forwardedRef,\n) {\n validateTopDescriptor(descriptor);\n const size = descriptor.size ?? topDefaults.size;\n const level = descriptor.headingLevel ?? topDefaults.headingLevel;\n const metrics = topRecipe.sizes[size];\n return (\n <header\n ref={forwardedRef}\n className={classNames(\"hjm-top\", className)}\n data-size={size}\n style={{ ...layoutStyle,\n \"--hjm-top-title-size\": `${metrics.title.fontSize}px`,\n \"--hjm-top-title-line-height\": `${metrics.title.lineHeight}px`,\n \"--hjm-top-title-weight\": metrics.title.fontWeight,\n \"--hjm-top-padding-top\": `${metrics.paddingTop}px`,\n \"--hjm-top-padding-bottom\": `${metrics.paddingBottom}px`,\n } as CSSProperties}\n >\n {descriptor.eyebrow ? <p className=\"hjm-top__eyebrow\">{descriptor.eyebrow}</p> : null}\n <div className=\"hjm-top__row\">\n {/*\n A real heading element at the declared level, not styled text: this\n block is what a screen reader lands on when it skips to the content.\n */}\n {createElement(`h${level}`, { className: \"hjm-top__title\" }, descriptor.title)}\n {trailing ? <div className=\"hjm-top__trailing\">{trailing}</div> : null}\n </div>\n {descriptor.description ? <p className=\"hjm-top__description\">{descriptor.description}</p> : null}\n </header>\n );\n});\n"]}
|
package/dist/tour.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tour.d.ts","sourceRoot":"","sources":["../src/tour.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,2BAA2B,EAGhC,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC3B,MAAM,yCAAyC,CAAC;AAajD,OAAO,EAA0D,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"tour.d.ts","sourceRoot":"","sources":["../src/tour.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,2BAA2B,EAGhC,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC3B,MAAM,yCAAyC,CAAC;AAajD,OAAO,EAA0D,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAGzG,MAAM,MAAM,SAAS,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IAC3D,UAAU,EAAE,cAAc,CAAC,EAAE,CAAC,CAAC;IAC/B,wEAAwE;IACxE,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,WAAW,GAAG,IAAI,CAAC;IACxD,mBAAmB,EAAE,2BAA2B,CAAC;IACjD,YAAY,EAAE,qBAAqB,CAAC,EAAE,CAAC,CAAC;IACxC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;QAAE,MAAM,EAAE,cAAc,CAAA;KAAE,CAAC,KAAK,IAAI,CAAC;IACrF,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,eAAe,CAAC,EAAE,WAAW,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AA2BH,eAAO,MAAM,IAAI,EAmKX,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,EAC/B,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;CAAE,KACvD,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC"}
|
package/dist/tour.js
CHANGED
|
@@ -59,7 +59,8 @@ export const Tour = forwardRef(function Tour({ descriptor, resolveAnchor, compos
|
|
|
59
59
|
const advance = (reason) => {
|
|
60
60
|
const outcome = resolveTourAdvance(descriptor, reason);
|
|
61
61
|
// `no-op` is Previous on the first step: the contract makes that a decision,
|
|
62
|
-
// not a
|
|
62
|
+
// not a removed control. The button renders `aria-disabled` (below) and this
|
|
63
|
+
// branch simply ignores it.
|
|
63
64
|
if (outcome.type === "step")
|
|
64
65
|
onStepChange(outcome.stepId, reason);
|
|
65
66
|
else if (outcome.type === "close")
|
|
@@ -117,6 +118,6 @@ export const Tour = forwardRef(function Tour({ descriptor, resolveAnchor, compos
|
|
|
117
118
|
insetInlineStart: anchorRect.left,
|
|
118
119
|
inlineSize: anchorRect.width,
|
|
119
120
|
blockSize: anchorRect.height,
|
|
120
|
-
} })) : null }) }), _jsx(AnchoredPortal, { anchorRef: anchorRef, ...(portalContainer ? { container: portalContainer } : {}), children: _jsxs("div", { ref: cardRefs, id: id, role: "dialog", "aria-modal": "true", "aria-label": resolved.accessibilityLabel, "aria-describedby": `${id}-announcement`, tabIndex: -1, "data-hjm-modal-content": "", "data-placement": position.placement, "data-align": position.align, className: classNames("hjm-tour", className), style: { ...position.style, maxInlineSize: tourRecipe.maxWidth }, children: [_jsx("span", { id: `${id}-announcement`, className: "hjm-visually-hidden", children: step.announcement }), _jsxs("div", { "aria-hidden": "true", children: [_jsxs("p", { className: "hjm-tour__counter", children: [step.position, " / ", step.total] }), _jsx("h2", { className: "hjm-tour__title", children: step.title }), _jsx("p", { className: "hjm-tour__description", children: step.description })] }), _jsxs("div", { className: "hjm-tour__actions", children: [_jsx(Button, { tone: "ghost", onClick: () => close("skip"), children: descriptor.labels.skip }), _jsx(Button, { tone: "secondary", disabled: resolved.isFirstStep, onClick: () => advance("previous"), children: descriptor.labels.previous }), _jsx(Button, { onClick: () => advance("next"), children: resolved.isLastStep ? descriptor.labels.done : descriptor.labels.next })] })] }) })] }));
|
|
121
|
+
} })) : null }) }), _jsx(AnchoredPortal, { anchorRef: anchorRef, ...(portalContainer ? { container: portalContainer } : {}), children: _jsxs("div", { ref: cardRefs, id: id, role: "dialog", "aria-modal": "true", "aria-label": resolved.accessibilityLabel, "aria-describedby": `${id}-announcement`, tabIndex: -1, "data-hjm-modal-content": "", "data-placement": position.placement, "data-align": position.align, className: classNames("hjm-tour", className), style: { ...position.style, maxInlineSize: tourRecipe.maxWidth }, children: [_jsx("span", { id: `${id}-announcement`, className: "hjm-visually-hidden", children: step.announcement }), _jsxs("div", { "aria-hidden": "true", children: [_jsxs("p", { className: "hjm-tour__counter", children: [step.position, " / ", step.total] }), _jsx("h2", { className: "hjm-tour__title", children: step.title }), _jsx("p", { className: "hjm-tour__description", children: step.description })] }), _jsxs("div", { className: "hjm-tour__actions", children: [_jsx(Button, { tone: "ghost", onClick: () => close("skip"), children: descriptor.labels.skip }), _jsx(Button, { tone: "secondary", "aria-disabled": resolved.isFirstStep || undefined, onClick: () => advance("previous"), children: descriptor.labels.previous }), _jsx(Button, { onClick: () => advance("next"), children: resolved.isLastStep ? descriptor.labels.done : descriptor.labels.next })] })] }) })] }));
|
|
121
122
|
});
|
|
122
123
|
//# sourceMappingURL=tour.js.map
|
package/dist/tour.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tour.js","sourceRoot":"","sources":["../src/tour.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n resolveTourAdvance,\n resolveTourDescriptor,\n tourRecipe,\n validateTourOpenState,\n type ComposeTourStepAnnouncement,\n type TourAdvanceReason,\n type TourCloseReason,\n type TourDescriptor,\n type TourOpenReason,\n type TourStepChangeHandler,\n} from \"@hjmds/design-contracts/components/tour\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type CSSProperties,\n} from \"react\";\nimport { Button } from \"./actions.js\";\nimport { classNames, composeRefs } from \"./internal.js\";\nimport { AnchoredPortal, useAnchoredPopup } from \"./portal.js\";\nimport { getModalLayer, HjmPortal, renderTrigger, useModalFocus, type OverlayTrigger } from \"./modal.js\";\n\nexport type TourProps<Id extends string = string> = Readonly<{\n descriptor: TourDescriptor<Id>;\n /** Resolves the product-owned anchor key to the element to point at. */\n resolveAnchor: (anchorId: string) => HTMLElement | null;\n composeAnnouncement: ComposeTourStepAnnouncement;\n onStepChange: TourStepChangeHandler<Id>;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean, detail: Readonly<{ reason: TourOpenReason }>) => void;\n trigger?: OverlayTrigger;\n portalContainer?: HTMLElement;\n className?: string;\n}>;\n\n/*\n The contract hands renderers an opaque `anchorId`, never a node, so the\n product owns what each step points at. This component therefore keeps its own\n element box: it re-reads the anchor on every step and on layout changes rather\n than storing a rect the product could invalidate by re-rendering the page.\n*/\nfunction useAnchorRect(anchor: HTMLElement | null, active: boolean): DOMRect | null {\n const [rect, setRect] = useState<DOMRect | null>(null);\n useEffect(() => {\n if (!active || !anchor) { setRect(null); return; }\n const measure = () => setRect(anchor.getBoundingClientRect());\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(anchor);\n window.addEventListener(\"scroll\", measure, true);\n window.addEventListener(\"resize\", measure);\n return () => {\n observer.disconnect();\n window.removeEventListener(\"scroll\", measure, true);\n window.removeEventListener(\"resize\", measure);\n };\n }, [anchor, active]);\n return rect;\n}\n\nexport const Tour = forwardRef(function Tour<Id extends string = string>(\n {\n descriptor,\n resolveAnchor,\n composeAnnouncement,\n onStepChange,\n open: openProp,\n defaultOpen,\n onOpenChange,\n trigger,\n portalContainer,\n className,\n }: TourProps<Id>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n validateTourOpenState({\n ...(openProp === undefined ? {} : { open: openProp }),\n ...(defaultOpen === undefined ? {} : { defaultOpen }),\n ...(onOpenChange === undefined ? {} : { onOpenChange }),\n } as Parameters<typeof validateTourOpenState>[0]);\n const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false);\n const open = openProp ?? internalOpen;\n const resolved = resolveTourDescriptor(descriptor, { composeAnnouncement });\n const step = resolved.currentStep;\n\n const cardRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLElement>(null);\n const anchorRef = useRef<HTMLElement | null>(null);\n const [card, setCard] = useState<HTMLDivElement | null>(null);\n const id = `${useId().replaceAll(\":\", \"\")}-tour`;\n const anchor = open ? resolveAnchor(step.anchorId) : null;\n anchorRef.current = anchor;\n const anchorRect = useAnchorRect(anchor, open);\n\n const change = useCallback((next: boolean, reason: TourOpenReason) => {\n if (openProp === undefined) setInternalOpen(next);\n onOpenChange?.(next, { reason });\n }, [openProp, onOpenChange]);\n const close = (reason: TourCloseReason) => change(false, reason);\n const advance = (reason: TourAdvanceReason) => {\n const outcome = resolveTourAdvance(descriptor, reason);\n // `no-op` is Previous on the first step: the contract makes that a decision,\n // not a disabled button, so the control stays focusable and simply does nothing.\n if (outcome.type === \"step\") onStepChange(outcome.stepId, reason);\n else if (outcome.type === \"close\") close(outcome.reason);\n };\n\n const openRef = useRef(open);\n openRef.current = open;\n const settleRef = useRef(close);\n settleRef.current = close;\n const epochRef = useRef(0);\n useEffect(() => {\n const epoch = epochRef.current + 1;\n epochRef.current = epoch;\n return () => {\n queueMicrotask(() => {\n // StrictMode re-runs setup right after its probe cleanup; only a real\n // unmount has no later epoch. An open tour torn down that way was\n // interrupted — the user never skipped, escaped, or completed it.\n if (epochRef.current !== epoch || !openRef.current) return;\n settleRef.current(\"interrupted\");\n });\n };\n }, []);\n\n useModalFocus({\n active: open,\n contentRef: cardRef,\n // The card itself, not its first button: it carries the step announcement,\n // which is the whole substitute for a sighted user's visual pointer.\n initialFocusRef: cardRef,\n ...(trigger === undefined ? {} : { fallbackReturnRef: triggerRef }),\n onEscape: () => close(\"escape\"),\n });\n useEffect(() => {\n // Focus follows the card on every step change, never the anchor: the anchor\n // is inert background while the tour runs.\n if (open) cardRef.current?.focus({ preventScroll: true });\n }, [open, step.id]);\n\n const position = useAnchoredPopup(anchorRef, open ? card : null, {\n ...(step.placement === undefined ? {} : { placement: step.placement }),\n ...(step.align === undefined ? {} : { align: step.align }),\n gap: tourRecipe.sideOffset,\n zIndex: getModalLayer(0) + 1,\n fallbackAxis: true,\n });\n const cardRefs = useCallback(composeRefs(setCard, cardRef, forwardedRef), [forwardedRef]);\n\n const renderedTrigger = trigger === undefined\n ? null\n : renderTrigger(trigger, triggerRef, open, id, \"dialog\", () => change(true, \"trigger\"));\n if (!open) return renderedTrigger;\n return (\n <>\n {renderedTrigger}\n <HjmPortal {...(portalContainer === undefined ? {} : { container: portalContainer })}>\n {/*\n No dismiss handler on the veil: the contract has no `outside` close\n reason, so an accidental pointer must not end a walkthrough.\n */}\n <div className=\"hjm-tour-backdrop\" data-state=\"open\" style={{ zIndex: getModalLayer(0) }}>\n {anchorRect ? (\n <div\n className=\"hjm-tour-highlight\"\n aria-hidden=\"true\"\n style={{\n insetBlockStart: anchorRect.top,\n insetInlineStart: anchorRect.left,\n inlineSize: anchorRect.width,\n blockSize: anchorRect.height,\n } as CSSProperties}\n />\n ) : null}\n </div>\n </HjmPortal>\n <AnchoredPortal anchorRef={anchorRef} {...(portalContainer ? { container: portalContainer } : {})}>\n <div\n ref={cardRefs}\n id={id}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={resolved.accessibilityLabel}\n aria-describedby={`${id}-announcement`}\n tabIndex={-1}\n data-hjm-modal-content=\"\"\n data-placement={position.placement}\n data-align={position.align}\n className={classNames(\"hjm-tour\", className)}\n style={{ ...position.style, maxInlineSize: tourRecipe.maxWidth } as CSSProperties}\n >\n {/*\n The composed announcement already carries title, description, and\n position, so the visible copy is hidden from assistive technology\n instead of being read twice.\n */}\n <span id={`${id}-announcement`} className=\"hjm-visually-hidden\">{step.announcement}</span>\n <div aria-hidden=\"true\">\n <p className=\"hjm-tour__counter\">{step.position} / {step.total}</p>\n <h2 className=\"hjm-tour__title\">{step.title}</h2>\n <p className=\"hjm-tour__description\">{step.description}</p>\n </div>\n <div className=\"hjm-tour__actions\">\n <Button tone=\"ghost\" onClick={() => close(\"skip\")}>{descriptor.labels.skip}</Button>\n <Button tone=\"secondary\" disabled={resolved.isFirstStep} onClick={() => advance(\"previous\")}>\n {descriptor.labels.previous}\n </Button>\n <Button onClick={() => advance(\"next\")}>\n {resolved.isLastStep ? descriptor.labels.done : descriptor.labels.next}\n </Button>\n </div>\n </div>\n </AnchoredPortal>\n </>\n );\n}) as <Id extends string = string>(\n props: TourProps<Id> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\n"]}
|
|
1
|
+
{"version":3,"file":"tour.js","sourceRoot":"","sources":["../src/tour.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n resolveTourAdvance,\n resolveTourDescriptor,\n tourRecipe,\n validateTourOpenState,\n type ComposeTourStepAnnouncement,\n type TourAdvanceReason,\n type TourCloseReason,\n type TourDescriptor,\n type TourOpenReason,\n type TourStepChangeHandler,\n} from \"@hjmds/design-contracts/components/tour\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type CSSProperties,\n} from \"react\";\nimport { Button } from \"./actions.js\";\nimport { classNames, composeRefs } from \"./internal.js\";\nimport { AnchoredPortal, useAnchoredPopup } from \"./portal.js\";\nimport { getModalLayer, HjmPortal, renderTrigger, useModalFocus, type OverlayTrigger } from \"./modal.js\";\n\n// No `layoutStyle`: the card is an anchored portal that follows each step's anchor; only the caller's trigger is in flow.\nexport type TourProps<Id extends string = string> = Readonly<{\n descriptor: TourDescriptor<Id>;\n /** Resolves the product-owned anchor key to the element to point at. */\n resolveAnchor: (anchorId: string) => HTMLElement | null;\n composeAnnouncement: ComposeTourStepAnnouncement;\n onStepChange: TourStepChangeHandler<Id>;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean, detail: Readonly<{ reason: TourOpenReason }>) => void;\n trigger?: OverlayTrigger;\n portalContainer?: HTMLElement;\n className?: string;\n}>;\n\n/*\n The contract hands renderers an opaque `anchorId`, never a node, so the\n product owns what each step points at. This component therefore keeps its own\n element box: it re-reads the anchor on every step and on layout changes rather\n than storing a rect the product could invalidate by re-rendering the page.\n*/\nfunction useAnchorRect(anchor: HTMLElement | null, active: boolean): DOMRect | null {\n const [rect, setRect] = useState<DOMRect | null>(null);\n useEffect(() => {\n if (!active || !anchor) { setRect(null); return; }\n const measure = () => setRect(anchor.getBoundingClientRect());\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(anchor);\n window.addEventListener(\"scroll\", measure, true);\n window.addEventListener(\"resize\", measure);\n return () => {\n observer.disconnect();\n window.removeEventListener(\"scroll\", measure, true);\n window.removeEventListener(\"resize\", measure);\n };\n }, [anchor, active]);\n return rect;\n}\n\nexport const Tour = forwardRef(function Tour<Id extends string = string>(\n {\n descriptor,\n resolveAnchor,\n composeAnnouncement,\n onStepChange,\n open: openProp,\n defaultOpen,\n onOpenChange,\n trigger,\n portalContainer,\n className,\n }: TourProps<Id>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n validateTourOpenState({\n ...(openProp === undefined ? {} : { open: openProp }),\n ...(defaultOpen === undefined ? {} : { defaultOpen }),\n ...(onOpenChange === undefined ? {} : { onOpenChange }),\n } as Parameters<typeof validateTourOpenState>[0]);\n const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false);\n const open = openProp ?? internalOpen;\n const resolved = resolveTourDescriptor(descriptor, { composeAnnouncement });\n const step = resolved.currentStep;\n\n const cardRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLElement>(null);\n const anchorRef = useRef<HTMLElement | null>(null);\n const [card, setCard] = useState<HTMLDivElement | null>(null);\n const id = `${useId().replaceAll(\":\", \"\")}-tour`;\n const anchor = open ? resolveAnchor(step.anchorId) : null;\n anchorRef.current = anchor;\n const anchorRect = useAnchorRect(anchor, open);\n\n const change = useCallback((next: boolean, reason: TourOpenReason) => {\n if (openProp === undefined) setInternalOpen(next);\n onOpenChange?.(next, { reason });\n }, [openProp, onOpenChange]);\n const close = (reason: TourCloseReason) => change(false, reason);\n const advance = (reason: TourAdvanceReason) => {\n const outcome = resolveTourAdvance(descriptor, reason);\n // `no-op` is Previous on the first step: the contract makes that a decision,\n // not a removed control. The button renders `aria-disabled` (below) and this\n // branch simply ignores it.\n if (outcome.type === \"step\") onStepChange(outcome.stepId, reason);\n else if (outcome.type === \"close\") close(outcome.reason);\n };\n\n const openRef = useRef(open);\n openRef.current = open;\n const settleRef = useRef(close);\n settleRef.current = close;\n const epochRef = useRef(0);\n useEffect(() => {\n const epoch = epochRef.current + 1;\n epochRef.current = epoch;\n return () => {\n queueMicrotask(() => {\n // StrictMode re-runs setup right after its probe cleanup; only a real\n // unmount has no later epoch. An open tour torn down that way was\n // interrupted — the user never skipped, escaped, or completed it.\n if (epochRef.current !== epoch || !openRef.current) return;\n settleRef.current(\"interrupted\");\n });\n };\n }, []);\n\n useModalFocus({\n active: open,\n contentRef: cardRef,\n // The card itself, not its first button: it carries the step announcement,\n // which is the whole substitute for a sighted user's visual pointer.\n initialFocusRef: cardRef,\n ...(trigger === undefined ? {} : { fallbackReturnRef: triggerRef }),\n onEscape: () => close(\"escape\"),\n });\n useEffect(() => {\n // Focus follows the card on every step change, never the anchor: the anchor\n // is inert background while the tour runs.\n if (open) cardRef.current?.focus({ preventScroll: true });\n }, [open, step.id]);\n\n const position = useAnchoredPopup(anchorRef, open ? card : null, {\n ...(step.placement === undefined ? {} : { placement: step.placement }),\n ...(step.align === undefined ? {} : { align: step.align }),\n gap: tourRecipe.sideOffset,\n zIndex: getModalLayer(0) + 1,\n fallbackAxis: true,\n });\n const cardRefs = useCallback(composeRefs(setCard, cardRef, forwardedRef), [forwardedRef]);\n\n const renderedTrigger = trigger === undefined\n ? null\n : renderTrigger(trigger, triggerRef, open, id, \"dialog\", () => change(true, \"trigger\"));\n if (!open) return renderedTrigger;\n return (\n <>\n {renderedTrigger}\n <HjmPortal {...(portalContainer === undefined ? {} : { container: portalContainer })}>\n {/*\n No dismiss handler on the veil: the contract has no `outside` close\n reason, so an accidental pointer must not end a walkthrough.\n */}\n <div className=\"hjm-tour-backdrop\" data-state=\"open\" style={{ zIndex: getModalLayer(0) }}>\n {anchorRect ? (\n <div\n className=\"hjm-tour-highlight\"\n aria-hidden=\"true\"\n style={{\n insetBlockStart: anchorRect.top,\n insetInlineStart: anchorRect.left,\n inlineSize: anchorRect.width,\n blockSize: anchorRect.height,\n } as CSSProperties}\n />\n ) : null}\n </div>\n </HjmPortal>\n <AnchoredPortal anchorRef={anchorRef} {...(portalContainer ? { container: portalContainer } : {})}>\n <div\n ref={cardRefs}\n id={id}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={resolved.accessibilityLabel}\n aria-describedby={`${id}-announcement`}\n tabIndex={-1}\n data-hjm-modal-content=\"\"\n data-placement={position.placement}\n data-align={position.align}\n className={classNames(\"hjm-tour\", className)}\n style={{ ...position.style, maxInlineSize: tourRecipe.maxWidth } as CSSProperties}\n >\n {/*\n The composed announcement already carries title, description, and\n position, so the visible copy is hidden from assistive technology\n instead of being read twice.\n */}\n <span id={`${id}-announcement`} className=\"hjm-visually-hidden\">{step.announcement}</span>\n <div aria-hidden=\"true\">\n <p className=\"hjm-tour__counter\">{step.position} / {step.total}</p>\n <h2 className=\"hjm-tour__title\">{step.title}</h2>\n <p className=\"hjm-tour__description\">{step.description}</p>\n </div>\n <div className=\"hjm-tour__actions\">\n <Button tone=\"ghost\" onClick={() => close(\"skip\")}>{descriptor.labels.skip}</Button>\n {/*\n `aria-disabled`, not `disabled`: a native disabled button drops focus to\n <body> when the user arrives back on the first step with Previous, and\n leaves a hole in the card's tab order. Button keeps an aria-disabled\n control focusable and swallows the press (the Carousel boundary pattern).\n */}\n <Button tone=\"secondary\" aria-disabled={resolved.isFirstStep || undefined} onClick={() => advance(\"previous\")}>\n {descriptor.labels.previous}\n </Button>\n <Button onClick={() => advance(\"next\")}>\n {resolved.isLastStep ? descriptor.labels.done : descriptor.labels.next}\n </Button>\n </div>\n </div>\n </AnchoredPortal>\n </>\n );\n}) as <Id extends string = string>(\n props: TourProps<Id> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\n"]}
|
package/dist/transfer-list.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type TransferListMoveDirection } from "@hjmds/design-contracts/components/transfer-list";
|
|
2
2
|
import type { SelectItemDescriptor } from "@hjmds/design-contracts/behaviors";
|
|
3
|
+
import type { HjmCompositionStyleProp } from "./composition-style.js";
|
|
3
4
|
export type TransferListLabels = Readonly<{
|
|
4
5
|
source: string;
|
|
5
6
|
target: string;
|
|
@@ -17,6 +18,8 @@ export type TransferListProps<Id extends string = string> = Readonly<{
|
|
|
17
18
|
/** Receives which ids moved, in origin-panel order, so the product announces it. */
|
|
18
19
|
onMove?: (movedIds: readonly Id[], direction: TransferListMoveDirection) => void;
|
|
19
20
|
className?: string;
|
|
21
|
+
/** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */
|
|
22
|
+
layoutStyle?: HjmCompositionStyleProp;
|
|
20
23
|
}>;
|
|
21
24
|
export declare const TransferList: <Id extends string = string>(props: TransferListProps<Id> & {
|
|
22
25
|
ref?: React.Ref<HTMLDivElement>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transfer-list.d.ts","sourceRoot":"","sources":["../src/transfer-list.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,yBAAyB,EAG/B,MAAM,kDAAkD,CAAC;AAC1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"transfer-list.d.ts","sourceRoot":"","sources":["../src/transfer-list.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,yBAAyB,EAG/B,MAAM,kDAAkD,CAAC;AAC1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAU9E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAAC;AAEH,MAAM,MAAM,iBAAiB,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IACnE,KAAK,EAAE,SAAS,oBAAoB,CAAC,EAAE,CAAC,EAAE,CAAC;IAC3C,MAAM,EAAE,kBAAkB,CAAC;IAC3B,UAAU,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7B,iBAAiB,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IACpC,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC;IACrD,oFAAoF;IACpF,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAOH,eAAO,MAAM,YAAY,EA2JnB,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,EAC/B,KAAK,EAAE,iBAAiB,CAAC,EAAE,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;CAAE,KAC/D,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC"}
|
package/dist/transfer-list.js
CHANGED
|
@@ -7,7 +7,7 @@ const emptySelection = () => ({
|
|
|
7
7
|
source: new Set(),
|
|
8
8
|
target: new Set(),
|
|
9
9
|
});
|
|
10
|
-
export const TransferList = forwardRef(function TransferList({ items, labels, targetKeys: controlledTargetKeys, defaultTargetKeys, onTargetKeysChange, onMove, className, }, forwardedRef) {
|
|
10
|
+
export const TransferList = forwardRef(function TransferList({ items, labels, targetKeys: controlledTargetKeys, defaultTargetKeys, onTargetKeysChange, onMove, className, layoutStyle, }, forwardedRef) {
|
|
11
11
|
const [targetKeys, setTargetKeys] = useControllableState({
|
|
12
12
|
...(controlledTargetKeys === undefined ? {} : { value: controlledTargetKeys }),
|
|
13
13
|
defaultValue: defaultTargetKeys ?? new Set(),
|
|
@@ -104,6 +104,6 @@ export const TransferList = forwardRef(function TransferList({ items, labels, ta
|
|
|
104
104
|
};
|
|
105
105
|
return (_jsxs("div", { className: "hjm-transfer-list__panel", role: "group", "aria-label": labels[panel], children: [_jsxs("div", { className: "hjm-transfer-list__header", children: [_jsx("span", { className: "hjm-transfer-list__title", children: labels[panel] }), _jsx("button", { type: "button", role: "checkbox", "aria-checked": selectAllState === "mixed" ? "mixed" : String(selectAllState === true), "aria-label": `${labels[panel]} ${labels.selectAll}`, className: "hjm-transfer-list__select-all", onClick: () => setSelection(toggleTransferListSelectAll(descriptor, selection, panel)), children: selectAllState === true ? "✓" : selectAllState === "mixed" ? "–" : "" })] }), panelItems.length === 0 ? (_jsx("p", { className: "hjm-transfer-list__empty", "data-hjm-transfer-empty": panel, tabIndex: -1, children: labels.empty })) : (_jsx("div", { role: "listbox", "aria-multiselectable": "true", "aria-label": labels[panel], className: "hjm-transfer-list__options", children: panelItems.map((item) => (_jsxs("div", { role: "option", "data-hjm-transfer-row": `${panel}:${item.id}`, "aria-selected": selected.has(item.id), "aria-disabled": item.disabled || undefined, tabIndex: item.id === activeId ? 0 : -1, className: "hjm-transfer-list__option", onFocus: () => setFocused((previous) => ({ ...previous, [panel]: item.id })), onClick: () => setSelection(toggleTransferListSelection(descriptor, selection, panel, item.id)), onKeyDown: (event) => onKeyDown(event, item), children: [_jsx("span", { "aria-hidden": "true", className: "hjm-transfer-list__mark", "data-checked": selected.has(item.id) || undefined, children: selected.has(item.id) ? "✓" : "" }), _jsx("span", { children: item.label })] }, item.id))) }))] }));
|
|
106
106
|
};
|
|
107
|
-
return (_jsxs("div", { ref: composeRefs(rootRef, forwardedRef), className: classNames("hjm-transfer-list", className), children: [renderPanel("source"), _jsxs("div", { className: "hjm-transfer-list__actions", children: [_jsx(Button, { tone: "secondary", disabled: selection.source.size === 0, onClick: () => move("toTarget"), children: labels.toTarget }), _jsx(Button, { tone: "secondary", disabled: selection.target.size === 0, onClick: () => move("toSource"), children: labels.toSource })] }), renderPanel("target")] }));
|
|
107
|
+
return (_jsxs("div", { ref: composeRefs(rootRef, forwardedRef), style: layoutStyle, className: classNames("hjm-transfer-list", className), children: [renderPanel("source"), _jsxs("div", { className: "hjm-transfer-list__actions", children: [_jsx(Button, { tone: "secondary", disabled: selection.source.size === 0, onClick: () => move("toTarget"), children: labels.toTarget }), _jsx(Button, { tone: "secondary", disabled: selection.target.size === 0, onClick: () => move("toSource"), children: labels.toSource })] }), renderPanel("target")] }));
|
|
108
108
|
});
|
|
109
109
|
//# sourceMappingURL=transfer-list.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transfer-list.js","sourceRoot":"","sources":["../src/transfer-list.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n moveTransferListSelection,\n reconcileTransferListSelection,\n resolveTransferListFocusAfterMove,\n resolveTransferListPanels,\n resolveTransferListSelectAllState,\n toggleTransferListSelectAll,\n toggleTransferListSelection,\n type TransferListMoveDirection,\n type TransferListPanel,\n type TransferListSelection,\n} from \"@hjmds/design-contracts/components/transfer-list\";\nimport type { SelectItemDescriptor } from \"@hjmds/design-contracts/behaviors\";\nimport {\n forwardRef,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n} from \"react\";\nimport { Button } from \"./actions.js\";\nimport { classNames, composeRefs, useControllableState } from \"./internal.js\";\n\nexport type TransferListLabels = Readonly<{\n source: string;\n target: string;\n toTarget: string;\n toSource: string;\n selectAll: string;\n empty: string;\n}>;\n\nexport type TransferListProps<Id extends string = string> = Readonly<{\n items: readonly SelectItemDescriptor<Id>[];\n labels: TransferListLabels;\n targetKeys?: ReadonlySet<Id>;\n defaultTargetKeys?: ReadonlySet<Id>;\n onTargetKeysChange?: (keys: ReadonlySet<Id>) => void;\n /** Receives which ids moved, in origin-panel order, so the product announces it. */\n onMove?: (movedIds: readonly Id[], direction: TransferListMoveDirection) => void;\n className?: string;\n}>;\n\nconst emptySelection = <Id extends string>(): TransferListSelection<Id> => ({\n source: new Set<Id>(),\n target: new Set<Id>(),\n});\n\nexport const TransferList = forwardRef(function TransferList<Id extends string = string>(\n {\n items,\n labels,\n targetKeys: controlledTargetKeys,\n defaultTargetKeys,\n onTargetKeysChange,\n onMove,\n className,\n }: TransferListProps<Id>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n const [targetKeys, setTargetKeys] = useControllableState<ReadonlySet<Id>>({\n ...(controlledTargetKeys === undefined ? {} : { value: controlledTargetKeys }),\n defaultValue: defaultTargetKeys ?? new Set<Id>(),\n ...(onTargetKeysChange === undefined ? {} : { onChange: onTargetKeysChange }),\n });\n const [rawSelection, setSelection] = useState<TransferListSelection<Id>>(emptySelection<Id>);\n const descriptor = useMemo(() => ({ items, targetKeys }), [items, targetKeys]);\n // A row that left the list entirely must not keep a pending check mark.\n const selection = useMemo(\n () => reconcileTransferListSelection(descriptor, rawSelection),\n [descriptor, rawSelection],\n );\n const panels = resolveTransferListPanels(descriptor);\n const rootRef = useRef<HTMLDivElement>(null);\n const [focused, setFocused] = useState<Readonly<Record<TransferListPanel, Id | null>>>({ source: null, target: null });\n\n const focusRow = (panel: TransferListPanel, id: Id | null) => {\n setFocused((previous) => ({ ...previous, [panel]: id }));\n if (id === null) return;\n rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-row=\"${panel}:${CSS.escape(id)}\"]`)?.focus();\n };\n const move = (direction: TransferListMoveDirection) => {\n const from: TransferListPanel = direction === \"toTarget\" ? \"source\" : \"target\";\n const before = panels[from];\n const result = moveTransferListSelection(descriptor, selection, direction);\n if (result.movedIds.length === 0) return;\n const removedIndex = before.findIndex((item) => item.id === result.movedIds[0]);\n const remaining = before.filter((item) => !result.movedIds.includes(item.id));\n setTargetKeys(result.targetKeys);\n setSelection(result.selection);\n onMove?.(result.movedIds, direction);\n // Focus lands on the row that slid into the first removed position, or on\n // the panel's empty state — never on the document body.\n const next = resolveTransferListFocusAfterMove(remaining, removedIndex);\n queueMicrotask(() => {\n if (next !== null) focusRow(from, next);\n else rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-empty=\"${from}\"]`)?.focus();\n });\n };\n const moveSingle = (panel: TransferListPanel, id: Id) => {\n // A focused row moves on its own: a single move must not require first\n // building a multi-selection.\n const single = { ...emptySelection<Id>(), [panel]: new Set<Id>([id]) } as TransferListSelection<Id>;\n const direction: TransferListMoveDirection = panel === \"source\" ? \"toTarget\" : \"toSource\";\n const result = moveTransferListSelection(descriptor, single, direction);\n if (result.movedIds.length === 0) return;\n const before = panels[panel];\n const removedIndex = before.findIndex((item) => item.id === id);\n const remaining = before.filter((item) => item.id !== id);\n setTargetKeys(result.targetKeys);\n setSelection((current) => ({ ...current, [panel]: new Set([...current[panel]].filter((key) => key !== id)) }));\n onMove?.(result.movedIds, direction);\n const next = resolveTransferListFocusAfterMove(remaining, removedIndex);\n queueMicrotask(() => {\n if (next !== null) focusRow(panel, next);\n else rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-empty=\"${panel}\"]`)?.focus();\n });\n };\n\n const renderPanel = (panel: TransferListPanel) => {\n const panelItems = panels[panel];\n const selected = selection[panel];\n const selectAllState = resolveTransferListSelectAllState(descriptor, selection, panel);\n const activeId = panelItems.some((item) => item.id === focused[panel])\n ? focused[panel]\n : panelItems.find((item) => !item.disabled)?.id ?? panelItems[0]?.id ?? null;\n const onKeyDown = (event: KeyboardEvent<HTMLElement>, item: SelectItemDescriptor<Id>) => {\n const index = panelItems.findIndex((candidate) => candidate.id === item.id);\n const goto = (nextIndex: number) => {\n const target = panelItems[Math.min(Math.max(nextIndex, 0), panelItems.length - 1)];\n if (target) { event.preventDefault(); focusRow(panel, target.id); }\n };\n if (event.key === \"ArrowDown\") goto(index + 1);\n else if (event.key === \"ArrowUp\") goto(index - 1);\n else if (event.key === \"Home\") goto(0);\n else if (event.key === \"End\") goto(panelItems.length - 1);\n else if (event.key === \" \") {\n event.preventDefault();\n setSelection(toggleTransferListSelection(descriptor, selection, panel, item.id));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n moveSingle(panel, item.id);\n }\n };\n return (\n <div className=\"hjm-transfer-list__panel\" role=\"group\" aria-label={labels[panel]}>\n <div className=\"hjm-transfer-list__header\">\n <span className=\"hjm-transfer-list__title\">{labels[panel]}</span>\n <button\n type=\"button\"\n role=\"checkbox\"\n aria-checked={selectAllState === \"mixed\" ? \"mixed\" : String(selectAllState === true) as \"true\" | \"false\"}\n aria-label={`${labels[panel]} ${labels.selectAll}`}\n className=\"hjm-transfer-list__select-all\"\n onClick={() => setSelection(toggleTransferListSelectAll(descriptor, selection, panel))}\n >\n {selectAllState === true ? \"✓\" : selectAllState === \"mixed\" ? \"–\" : \"\"}\n </button>\n </div>\n {panelItems.length === 0 ? (\n <p className=\"hjm-transfer-list__empty\" data-hjm-transfer-empty={panel} tabIndex={-1}>{labels.empty}</p>\n ) : (\n <div role=\"listbox\" aria-multiselectable=\"true\" aria-label={labels[panel]} className=\"hjm-transfer-list__options\">\n {panelItems.map((item) => (\n <div\n key={item.id}\n role=\"option\"\n data-hjm-transfer-row={`${panel}:${item.id}`}\n aria-selected={selected.has(item.id)}\n aria-disabled={item.disabled || undefined}\n tabIndex={item.id === activeId ? 0 : -1}\n className=\"hjm-transfer-list__option\"\n onFocus={() => setFocused((previous) => ({ ...previous, [panel]: item.id }))}\n onClick={() => setSelection(toggleTransferListSelection(descriptor, selection, panel, item.id))}\n onKeyDown={(event) => onKeyDown(event, item)}\n >\n <span aria-hidden=\"true\" className=\"hjm-transfer-list__mark\" data-checked={selected.has(item.id) || undefined}>\n {selected.has(item.id) ? \"✓\" : \"\"}\n </span>\n <span>{item.label}</span>\n </div>\n ))}\n </div>\n )}\n </div>\n );\n };\n\n return (\n <div ref={composeRefs(rootRef, forwardedRef)} className={classNames(\"hjm-transfer-list\", className)}>\n {renderPanel(\"source\")}\n <div className=\"hjm-transfer-list__actions\">\n <Button tone=\"secondary\" disabled={selection.source.size === 0} onClick={() => move(\"toTarget\")}>\n {labels.toTarget}\n </Button>\n <Button tone=\"secondary\" disabled={selection.target.size === 0} onClick={() => move(\"toSource\")}>\n {labels.toSource}\n </Button>\n </div>\n {renderPanel(\"target\")}\n </div>\n );\n}) as <Id extends string = string>(\n props: TransferListProps<Id> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\n"]}
|
|
1
|
+
{"version":3,"file":"transfer-list.js","sourceRoot":"","sources":["../src/transfer-list.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n moveTransferListSelection,\n reconcileTransferListSelection,\n resolveTransferListFocusAfterMove,\n resolveTransferListPanels,\n resolveTransferListSelectAllState,\n toggleTransferListSelectAll,\n toggleTransferListSelection,\n type TransferListMoveDirection,\n type TransferListPanel,\n type TransferListSelection,\n} from \"@hjmds/design-contracts/components/transfer-list\";\nimport type { SelectItemDescriptor } from \"@hjmds/design-contracts/behaviors\";\nimport {\n forwardRef,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n} from \"react\";\nimport { Button } from \"./actions.js\";\nimport { classNames, composeRefs, useControllableState } from \"./internal.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type TransferListLabels = Readonly<{\n source: string;\n target: string;\n toTarget: string;\n toSource: string;\n selectAll: string;\n empty: string;\n}>;\n\nexport type TransferListProps<Id extends string = string> = Readonly<{\n items: readonly SelectItemDescriptor<Id>[];\n labels: TransferListLabels;\n targetKeys?: ReadonlySet<Id>;\n defaultTargetKeys?: ReadonlySet<Id>;\n onTargetKeysChange?: (keys: ReadonlySet<Id>) => void;\n /** Receives which ids moved, in origin-panel order, so the product announces it. */\n onMove?: (movedIds: readonly Id[], direction: TransferListMoveDirection) => void;\n className?: string;\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nconst emptySelection = <Id extends string>(): TransferListSelection<Id> => ({\n source: new Set<Id>(),\n target: new Set<Id>(),\n});\n\nexport const TransferList = forwardRef(function TransferList<Id extends string = string>(\n {\n items,\n labels,\n targetKeys: controlledTargetKeys,\n defaultTargetKeys,\n onTargetKeysChange,\n onMove,\n className,\n layoutStyle,\n }: TransferListProps<Id>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n const [targetKeys, setTargetKeys] = useControllableState<ReadonlySet<Id>>({\n ...(controlledTargetKeys === undefined ? {} : { value: controlledTargetKeys }),\n defaultValue: defaultTargetKeys ?? new Set<Id>(),\n ...(onTargetKeysChange === undefined ? {} : { onChange: onTargetKeysChange }),\n });\n const [rawSelection, setSelection] = useState<TransferListSelection<Id>>(emptySelection<Id>);\n const descriptor = useMemo(() => ({ items, targetKeys }), [items, targetKeys]);\n // A row that left the list entirely must not keep a pending check mark.\n const selection = useMemo(\n () => reconcileTransferListSelection(descriptor, rawSelection),\n [descriptor, rawSelection],\n );\n const panels = resolveTransferListPanels(descriptor);\n const rootRef = useRef<HTMLDivElement>(null);\n const [focused, setFocused] = useState<Readonly<Record<TransferListPanel, Id | null>>>({ source: null, target: null });\n\n const focusRow = (panel: TransferListPanel, id: Id | null) => {\n setFocused((previous) => ({ ...previous, [panel]: id }));\n if (id === null) return;\n rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-row=\"${panel}:${CSS.escape(id)}\"]`)?.focus();\n };\n const move = (direction: TransferListMoveDirection) => {\n const from: TransferListPanel = direction === \"toTarget\" ? \"source\" : \"target\";\n const before = panels[from];\n const result = moveTransferListSelection(descriptor, selection, direction);\n if (result.movedIds.length === 0) return;\n const removedIndex = before.findIndex((item) => item.id === result.movedIds[0]);\n const remaining = before.filter((item) => !result.movedIds.includes(item.id));\n setTargetKeys(result.targetKeys);\n setSelection(result.selection);\n onMove?.(result.movedIds, direction);\n // Focus lands on the row that slid into the first removed position, or on\n // the panel's empty state — never on the document body.\n const next = resolveTransferListFocusAfterMove(remaining, removedIndex);\n queueMicrotask(() => {\n if (next !== null) focusRow(from, next);\n else rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-empty=\"${from}\"]`)?.focus();\n });\n };\n const moveSingle = (panel: TransferListPanel, id: Id) => {\n // A focused row moves on its own: a single move must not require first\n // building a multi-selection.\n const single = { ...emptySelection<Id>(), [panel]: new Set<Id>([id]) } as TransferListSelection<Id>;\n const direction: TransferListMoveDirection = panel === \"source\" ? \"toTarget\" : \"toSource\";\n const result = moveTransferListSelection(descriptor, single, direction);\n if (result.movedIds.length === 0) return;\n const before = panels[panel];\n const removedIndex = before.findIndex((item) => item.id === id);\n const remaining = before.filter((item) => item.id !== id);\n setTargetKeys(result.targetKeys);\n setSelection((current) => ({ ...current, [panel]: new Set([...current[panel]].filter((key) => key !== id)) }));\n onMove?.(result.movedIds, direction);\n const next = resolveTransferListFocusAfterMove(remaining, removedIndex);\n queueMicrotask(() => {\n if (next !== null) focusRow(panel, next);\n else rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-empty=\"${panel}\"]`)?.focus();\n });\n };\n\n const renderPanel = (panel: TransferListPanel) => {\n const panelItems = panels[panel];\n const selected = selection[panel];\n const selectAllState = resolveTransferListSelectAllState(descriptor, selection, panel);\n const activeId = panelItems.some((item) => item.id === focused[panel])\n ? focused[panel]\n : panelItems.find((item) => !item.disabled)?.id ?? panelItems[0]?.id ?? null;\n const onKeyDown = (event: KeyboardEvent<HTMLElement>, item: SelectItemDescriptor<Id>) => {\n const index = panelItems.findIndex((candidate) => candidate.id === item.id);\n const goto = (nextIndex: number) => {\n const target = panelItems[Math.min(Math.max(nextIndex, 0), panelItems.length - 1)];\n if (target) { event.preventDefault(); focusRow(panel, target.id); }\n };\n if (event.key === \"ArrowDown\") goto(index + 1);\n else if (event.key === \"ArrowUp\") goto(index - 1);\n else if (event.key === \"Home\") goto(0);\n else if (event.key === \"End\") goto(panelItems.length - 1);\n else if (event.key === \" \") {\n event.preventDefault();\n setSelection(toggleTransferListSelection(descriptor, selection, panel, item.id));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n moveSingle(panel, item.id);\n }\n };\n return (\n <div className=\"hjm-transfer-list__panel\" role=\"group\" aria-label={labels[panel]}>\n <div className=\"hjm-transfer-list__header\">\n <span className=\"hjm-transfer-list__title\">{labels[panel]}</span>\n <button\n type=\"button\"\n role=\"checkbox\"\n aria-checked={selectAllState === \"mixed\" ? \"mixed\" : String(selectAllState === true) as \"true\" | \"false\"}\n aria-label={`${labels[panel]} ${labels.selectAll}`}\n className=\"hjm-transfer-list__select-all\"\n onClick={() => setSelection(toggleTransferListSelectAll(descriptor, selection, panel))}\n >\n {selectAllState === true ? \"✓\" : selectAllState === \"mixed\" ? \"–\" : \"\"}\n </button>\n </div>\n {panelItems.length === 0 ? (\n <p className=\"hjm-transfer-list__empty\" data-hjm-transfer-empty={panel} tabIndex={-1}>{labels.empty}</p>\n ) : (\n <div role=\"listbox\" aria-multiselectable=\"true\" aria-label={labels[panel]} className=\"hjm-transfer-list__options\">\n {panelItems.map((item) => (\n <div\n key={item.id}\n role=\"option\"\n data-hjm-transfer-row={`${panel}:${item.id}`}\n aria-selected={selected.has(item.id)}\n aria-disabled={item.disabled || undefined}\n tabIndex={item.id === activeId ? 0 : -1}\n className=\"hjm-transfer-list__option\"\n onFocus={() => setFocused((previous) => ({ ...previous, [panel]: item.id }))}\n onClick={() => setSelection(toggleTransferListSelection(descriptor, selection, panel, item.id))}\n onKeyDown={(event) => onKeyDown(event, item)}\n >\n <span aria-hidden=\"true\" className=\"hjm-transfer-list__mark\" data-checked={selected.has(item.id) || undefined}>\n {selected.has(item.id) ? \"✓\" : \"\"}\n </span>\n <span>{item.label}</span>\n </div>\n ))}\n </div>\n )}\n </div>\n );\n };\n\n return (\n <div ref={composeRefs(rootRef, forwardedRef)} style={layoutStyle} className={classNames(\"hjm-transfer-list\", className)}>\n {renderPanel(\"source\")}\n <div className=\"hjm-transfer-list__actions\">\n <Button tone=\"secondary\" disabled={selection.source.size === 0} onClick={() => move(\"toTarget\")}>\n {labels.toTarget}\n </Button>\n <Button tone=\"secondary\" disabled={selection.target.size === 0} onClick={() => move(\"toSource\")}>\n {labels.toSource}\n </Button>\n </div>\n {renderPanel(\"target\")}\n </div>\n );\n}) as <Id extends string = string>(\n props: TransferListProps<Id> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\n"]}
|
package/dist/tree.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ComposeTreeAccessibleName, type TreeAsyncState, type TreeNodeDescriptor, type TreeSelectionModel } from "@hjmds/design-contracts/components/tree";
|
|
2
2
|
import type { CheckboxState } from "@hjmds/design-contracts/behaviors";
|
|
3
3
|
import { type ReactNode } from "react";
|
|
4
|
+
import type { HjmCompositionStyleProp } from "./composition-style.js";
|
|
4
5
|
export type TreeProps<Id extends string = string> = Readonly<{
|
|
5
6
|
label: string;
|
|
6
7
|
nodes: readonly TreeNodeDescriptor<Id>[];
|
|
@@ -22,6 +23,8 @@ export type TreeProps<Id extends string = string> = Readonly<{
|
|
|
22
23
|
expanded: boolean;
|
|
23
24
|
}>) => ReactNode;
|
|
24
25
|
className?: string;
|
|
26
|
+
/** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */
|
|
27
|
+
layoutStyle?: HjmCompositionStyleProp;
|
|
25
28
|
}>;
|
|
26
29
|
export declare const Tree: <Id extends string = string>(props: TreeProps<Id> & {
|
|
27
30
|
ref?: React.Ref<HTMLDivElement>;
|
package/dist/tree.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../src/tree.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,yBAAyB,EAE9B,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACxB,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,mCAAmC,CAAC;AACvF,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../src/tree.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,yBAAyB,EAE9B,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACxB,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,mCAAmC,CAAC;AACvF,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,SAAS,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC;IACzC,qBAAqB,EAAE,yBAAyB,CAAC;IACjD,YAAY,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IAC/B,mBAAmB,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IACtC,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC;IACvD,SAAS,CAAC,EAAE,kBAAkB,CAAC,EAAE,CAAC,CAAC;IACnC;;;;OAIG;IACH,aAAa,CAAC,EAAE,WAAW,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;IAC/C,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,IAAI,CAAC;IACnC,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,sFAAsF;IACtF,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,KAAK,SAAS,CAAC;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAaH,eAAO,MAAM,IAAI,EAyKX,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,EAC/B,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;CAAE,KACvD,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC"}
|
package/dist/tree.js
CHANGED
|
@@ -13,7 +13,7 @@ function selectedIds(selection) {
|
|
|
13
13
|
}
|
|
14
14
|
return selection.selectedKeys ?? selection.defaultSelectedKeys ?? new Set();
|
|
15
15
|
}
|
|
16
|
-
export const Tree = forwardRef(function Tree({ label, nodes, composeAccessibleName, expandedKeys: controlledExpanded, defaultExpandedKeys, onExpandedKeysChange, selection, checkedStates, onCheckedToggle, asyncState = { status: "idle" }, renderToggle, className, }, forwardedRef) {
|
|
16
|
+
export const Tree = forwardRef(function Tree({ label, nodes, composeAccessibleName, expandedKeys: controlledExpanded, defaultExpandedKeys, onExpandedKeysChange, selection, checkedStates, onCheckedToggle, asyncState = { status: "idle" }, renderToggle, className, layoutStyle, }, forwardedRef) {
|
|
17
17
|
const [expandedKeys, setExpandedKeys] = useControllableState({
|
|
18
18
|
...(controlledExpanded === undefined ? {} : { value: controlledExpanded }),
|
|
19
19
|
defaultValue: defaultExpandedKeys ?? new Set(),
|
|
@@ -25,7 +25,15 @@ export const Tree = forwardRef(function Tree({ label, nodes, composeAccessibleNa
|
|
|
25
25
|
const liveExpanded = useMemo(() => reconcileTreeExpansion(nodes, expandedKeys), [nodes, expandedKeys]);
|
|
26
26
|
const resolved = useMemo(() => resolveTreeDescriptor(nodes, liveExpanded, { composeAccessibleName }), [nodes, liveExpanded, composeAccessibleName]);
|
|
27
27
|
const visible = useMemo(() => resolved.filter((node) => node.visible), [resolved]);
|
|
28
|
-
|
|
28
|
+
// `defaultSelectedKey(s)` used to be re-read on every render, so an
|
|
29
|
+
// uncontrolled tree reported the click to `onSelectionChange` but kept showing
|
|
30
|
+
// the default. Keep the uncontrolled value in state like `useControllableState`
|
|
31
|
+
// does for expansion; a controlled `selectedKey(s)` (including `null`) still wins.
|
|
32
|
+
const selectionControlled = selection?.mode === "single"
|
|
33
|
+
? selection.selectedKey !== undefined
|
|
34
|
+
: selection?.mode === "multiple" ? selection.selectedKeys !== undefined : true;
|
|
35
|
+
const [uncontrolledSelected, setUncontrolledSelected] = useState(() => selectedIds(selection));
|
|
36
|
+
const selected = selectionControlled ? selectedIds(selection) : uncontrolledSelected;
|
|
29
37
|
const [focusedId, setFocusedId] = useState(null);
|
|
30
38
|
// Roving tab stop: one node is tabbable, and it must stay a real visible node
|
|
31
39
|
// after a collapse removed the previously focused descendant.
|
|
@@ -48,16 +56,20 @@ export const Tree = forwardRef(function Tree({ label, nodes, composeAccessibleNa
|
|
|
48
56
|
if (node.disabled || !selection || selection.mode === "none")
|
|
49
57
|
return;
|
|
50
58
|
if (selection.mode === "single") {
|
|
51
|
-
const current =
|
|
59
|
+
const current = selected.has(node.id) ? node.id : null;
|
|
52
60
|
const next = current === node.id && selection.disallowEmptySelection !== true ? null : node.id;
|
|
61
|
+
if (!selectionControlled)
|
|
62
|
+
setUncontrolledSelected(next === null ? new Set() : new Set([next]));
|
|
53
63
|
selection.onSelectionChange?.(next);
|
|
54
64
|
return;
|
|
55
65
|
}
|
|
56
|
-
const next = new Set(
|
|
66
|
+
const next = new Set(selected);
|
|
57
67
|
if (next.has(node.id))
|
|
58
68
|
next.delete(node.id);
|
|
59
69
|
else
|
|
60
70
|
next.add(node.id);
|
|
71
|
+
if (!selectionControlled)
|
|
72
|
+
setUncontrolledSelected(next);
|
|
61
73
|
selection.onSelectionChange?.(next);
|
|
62
74
|
};
|
|
63
75
|
const onKeyDown = (event, node) => {
|
|
@@ -104,7 +116,7 @@ export const Tree = forwardRef(function Tree({ label, nodes, composeAccessibleNa
|
|
|
104
116
|
focusNode(match);
|
|
105
117
|
}
|
|
106
118
|
};
|
|
107
|
-
return (_jsxs("div", { ref: composeRefs(rootRef, forwardedRef), role: "tree", "aria-label": label, "aria-busy": asyncState.status === "loading" || asyncState.status === "loadingMore" || undefined, "aria-multiselectable": selection?.mode === "multiple" || undefined, className: classNames("hjm-tree", className), style: { "--hjm-tree-indent": `${treeRecipe.indentPerLevel}px` }, children: [asyncState.status === "empty" || asyncState.status === "error" || asyncState.status === "loading" ? (_jsx("p", { className: "hjm-tree__state", role: asyncState.status === "error" ? "alert" : "status", children: asyncState.message })) : null, visible.map((node) => (_jsxs("div", { role: "treeitem", "data-hjm-tree-node": node.id, "aria-level": node.depth, "aria-posinset": node.position, "aria-setsize": node.siblingCount, "aria-expanded": node.hasChildren ? node.expanded : undefined, "aria-selected": selection && selection.mode !== "none" ? selected.has(node.id) : undefined, "aria-checked": checkedStates ? (checkedStates.get(node.id) === "mixed" ? "mixed" : String(checkedStates.get(node.id) === true)) : undefined, "aria-disabled": node.disabled || undefined, "aria-label": node.accessibleName,
|
|
119
|
+
return (_jsxs("div", { ref: composeRefs(rootRef, forwardedRef), role: "tree", "aria-label": label, "aria-busy": asyncState.status === "loading" || asyncState.status === "loadingMore" || undefined, "aria-multiselectable": selection?.mode === "multiple" || undefined, className: classNames("hjm-tree", className), style: { ...layoutStyle, "--hjm-tree-indent": `${treeRecipe.indentPerLevel}px` }, children: [asyncState.status === "empty" || asyncState.status === "error" || asyncState.status === "loading" ? (_jsx("p", { className: "hjm-tree__state", role: asyncState.status === "error" ? "alert" : "status", children: asyncState.message })) : null, visible.map((node) => (_jsxs("div", { role: "treeitem", "data-hjm-tree-node": node.id, "aria-level": node.depth, "aria-posinset": node.position, "aria-setsize": node.siblingCount, "aria-expanded": node.hasChildren ? node.expanded : undefined, "aria-selected": selection && selection.mode !== "none" ? selected.has(node.id) : undefined, "aria-checked": checkedStates ? (checkedStates.get(node.id) === "mixed" ? "mixed" : String(checkedStates.get(node.id) === true)) : undefined, "aria-disabled": node.disabled || undefined, "aria-label": node.accessibleName,
|
|
108
120
|
// One tab stop for the whole tree; the glyph is decorative, not a
|
|
109
121
|
// nested control, so a node is the only focusable thing in a row.
|
|
110
122
|
tabIndex: node.id === activeId ? 0 : -1, className: "hjm-tree__node", style: { "--hjm-tree-depth": node.depth - 1 }, onClick: (event) => {
|