@jc-times/business-ui 0.2.43

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.
Files changed (154) hide show
  1. package/AGENTS.md +6 -0
  2. package/CHANGELOG.md +267 -0
  3. package/README.md +55 -0
  4. package/THIRD_PARTY_NOTICES.md +29 -0
  5. package/bin/business-ui-gate.mjs +171 -0
  6. package/dist/Alert.js +47 -0
  7. package/dist/AsyncDirectoryPicker.js +134 -0
  8. package/dist/AsyncSearchPicker.js +155 -0
  9. package/dist/AutoComplete.js +34 -0
  10. package/dist/Badge.js +14 -0
  11. package/dist/Button.js +54 -0
  12. package/dist/Cascader.js +141 -0
  13. package/dist/Combobox.js +106 -0
  14. package/dist/ConfirmDialog.js +48 -0
  15. package/dist/DataTableHorizontalScrollbar.js +38 -0
  16. package/dist/DataTableIcons.js +37 -0
  17. package/dist/DataTableMergedRows.js +45 -0
  18. package/dist/DataTableTheme.js +135 -0
  19. package/dist/DateInput.js +223 -0
  20. package/dist/DropdownMenu.js +130 -0
  21. package/dist/EmptyState.js +27 -0
  22. package/dist/ExplainableButton.js +68 -0
  23. package/dist/FileDropzone.js +70 -0
  24. package/dist/FilePreparationDialog.js +33 -0
  25. package/dist/FloatingControls.js +120 -0
  26. package/dist/FormField.js +143 -0
  27. package/dist/MetricCard.js +38 -0
  28. package/dist/ModalShell.js +139 -0
  29. package/dist/MultiSelect.js +137 -0
  30. package/dist/NumericInput.js +97 -0
  31. package/dist/OverlayContainer.js +9 -0
  32. package/dist/PageHeader.js +36 -0
  33. package/dist/Pagination.js +115 -0
  34. package/dist/PreparedFileDropzone.js +30 -0
  35. package/dist/RecoverableAsyncState.js +35 -0
  36. package/dist/RovingTabs.js +54 -0
  37. package/dist/SectionCard.js +35 -0
  38. package/dist/SegmentedControl.js +46 -0
  39. package/dist/Select.js +73 -0
  40. package/dist/Skeleton.js +25 -0
  41. package/dist/SortableList.js +55 -0
  42. package/dist/StructuredAddressInput.js +157 -0
  43. package/dist/Toast.js +100 -0
  44. package/dist/ToggleSwitch.js +34 -0
  45. package/dist/TreeSelect.js +444 -0
  46. package/dist/amount-format.js +20 -0
  47. package/dist/business-ui.js +44 -0
  48. package/dist/chinese-address-regions.js +15 -0
  49. package/dist/classNames.js +6 -0
  50. package/dist/data-table.js +257 -0
  51. package/dist/document-render-budget.js +22 -0
  52. package/dist/document-viewer/ContinuousPdfViewer.js +145 -0
  53. package/dist/document-viewer/DocumentViewer.js +123 -0
  54. package/dist/document-viewer/DocumentViewerViewport.js +36 -0
  55. package/dist/document-viewer/ImagePage.js +39 -0
  56. package/dist/document-viewer/PdfCanvasPage.js +26 -0
  57. package/dist/document-viewer/PdfThumbnailRail.js +116 -0
  58. package/dist/document-viewer/PdfToolbar.js +61 -0
  59. package/dist/document-viewer/PdfWorkspace.js +42 -0
  60. package/dist/document-viewer/pdf-render-budget.js +7 -0
  61. package/dist/document-viewer/usePdfCanvas.js +69 -0
  62. package/dist/document-viewer/usePdfViewport.js +48 -0
  63. package/dist/document-viewer-v2/DocumentViewerV2.js +171 -0
  64. package/dist/document-viewer-v2/HeadlessPdfViewer.js +420 -0
  65. package/dist/document-viewer-v2/HeadlessSearch.js +66 -0
  66. package/dist/document-viewer-v2.js +13 -0
  67. package/dist/document-viewer.js +12 -0
  68. package/dist/pdfium-viewer.js +18 -0
  69. package/dist/styles.css +2 -0
  70. package/dist/types/Alert.d.ts +11 -0
  71. package/dist/types/AmountFormat.d.ts +15 -0
  72. package/dist/types/AsyncDirectoryPicker.d.ts +32 -0
  73. package/dist/types/AsyncSearchPicker.d.ts +26 -0
  74. package/dist/types/AutoComplete.d.ts +9 -0
  75. package/dist/types/Badge.d.ts +13 -0
  76. package/dist/types/Button.d.ts +36 -0
  77. package/dist/types/Cascader.d.ts +57 -0
  78. package/dist/types/ChineseAddressRegions.d.ts +4 -0
  79. package/dist/types/Combobox.d.ts +37 -0
  80. package/dist/types/ConfirmDialog.d.ts +20 -0
  81. package/dist/types/DataTable.d.ts +68 -0
  82. package/dist/types/DataTableHorizontalScrollbar.d.ts +8 -0
  83. package/dist/types/DataTableIcons.d.ts +19 -0
  84. package/dist/types/DataTableMergedRows.d.ts +11 -0
  85. package/dist/types/DataTableTheme.d.ts +4 -0
  86. package/dist/types/DateInput.d.ts +25 -0
  87. package/dist/types/DocumentRenderBudget.d.ts +9 -0
  88. package/dist/types/DocumentViewer.d.ts +11 -0
  89. package/dist/types/DocumentViewerV2.d.ts +10 -0
  90. package/dist/types/DropdownMenu.d.ts +73 -0
  91. package/dist/types/EmptyState.d.ts +9 -0
  92. package/dist/types/ExplainableButton.d.ts +7 -0
  93. package/dist/types/FileDropzone.d.ts +20 -0
  94. package/dist/types/FilePreparationDialog.d.ts +12 -0
  95. package/dist/types/FloatingControls.d.ts +39 -0
  96. package/dist/types/FormField.d.ts +35 -0
  97. package/dist/types/MetricCard.d.ts +17 -0
  98. package/dist/types/ModalShell.d.ts +33 -0
  99. package/dist/types/MultiSelect.d.ts +12 -0
  100. package/dist/types/NumericInput.d.ts +54 -0
  101. package/dist/types/OverlayContainer.d.ts +2 -0
  102. package/dist/types/PageHeader.d.ts +23 -0
  103. package/dist/types/Pagination.d.ts +14 -0
  104. package/dist/types/PdfiumViewer.d.ts +5 -0
  105. package/dist/types/PreparedFileDropzone.d.ts +7 -0
  106. package/dist/types/RecoverableAsyncState.d.ts +11 -0
  107. package/dist/types/RovingTabs.d.ts +21 -0
  108. package/dist/types/SectionCard.d.ts +18 -0
  109. package/dist/types/SegmentedControl.d.ts +26 -0
  110. package/dist/types/Select.d.ts +31 -0
  111. package/dist/types/Skeleton.d.ts +11 -0
  112. package/dist/types/SortableList.d.ts +17 -0
  113. package/dist/types/StructuredAddressInput.d.ts +57 -0
  114. package/dist/types/Toast.d.ts +22 -0
  115. package/dist/types/ToggleSwitch.d.ts +15 -0
  116. package/dist/types/TreeSelect.d.ts +58 -0
  117. package/dist/types/classNames.d.ts +1 -0
  118. package/dist/types/document-viewer/ContinuousPdfViewer.d.ts +16 -0
  119. package/dist/types/document-viewer/DocumentViewer.d.ts +31 -0
  120. package/dist/types/document-viewer/DocumentViewerViewport.d.ts +23 -0
  121. package/dist/types/document-viewer/ImagePage.d.ts +9 -0
  122. package/dist/types/document-viewer/PdfCanvasPage.d.ts +7 -0
  123. package/dist/types/document-viewer/PdfThumbnailRail.d.ts +12 -0
  124. package/dist/types/document-viewer/PdfToolbar.d.ts +13 -0
  125. package/dist/types/document-viewer/PdfWorkspace.d.ts +5 -0
  126. package/dist/types/document-viewer/pdf-render-budget.d.ts +2 -0
  127. package/dist/types/document-viewer/usePdfCanvas.d.ts +22 -0
  128. package/dist/types/document-viewer/usePdfViewport.d.ts +14 -0
  129. package/dist/types/document-viewer-v2/DocumentViewerV2.d.ts +50 -0
  130. package/dist/types/document-viewer-v2/HeadlessPdfViewer.d.ts +39 -0
  131. package/dist/types/document-viewer-v2/HeadlessSearch.d.ts +9 -0
  132. package/dist/types/index.d.ts +41 -0
  133. package/dist/types/useDebouncedValue.d.ts +2 -0
  134. package/dist/types/useFilePreparation.d.ts +28 -0
  135. package/dist/types/useModalFocusTrap.d.ts +3 -0
  136. package/dist/useDebouncedValue.js +16 -0
  137. package/dist/useFilePreparation.js +51 -0
  138. package/dist/useModalFocusTrap.js +71 -0
  139. package/docs/agent-guide.md +145 -0
  140. package/docs/api.md +220 -0
  141. package/docs/cascader.md +56 -0
  142. package/docs/component-strategy.md +94 -0
  143. package/docs/data-table.md +123 -0
  144. package/docs/dependencies.md +57 -0
  145. package/docs/document-viewer-v2.md +259 -0
  146. package/docs/document-viewer.md +28 -0
  147. package/docs/file-preparation.md +11 -0
  148. package/docs/maturity-migration.md +62 -0
  149. package/docs/modal-shell-migration.md +43 -0
  150. package/docs/mrt-poc.md +205 -0
  151. package/docs/release.md +32 -0
  152. package/docs/viewport-qa.md +99 -0
  153. package/licenses/rc-cascader-MIT.txt +21 -0
  154. package/package.json +163 -0
@@ -0,0 +1,5 @@
1
+ import { type HTMLAttributes, type ReactNode } from "react";
2
+ export declare const useThumbnailRailCollapsed: () => boolean;
3
+ export declare function PdfWorkspace({ rail, children, className, ...props }: HTMLAttributes<HTMLDivElement> & {
4
+ rail: ReactNode;
5
+ }): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ export * from "../DocumentRenderBudget";
2
+ export declare function pdfRenderBudgetForWindow(): import("./pdf-render-budget").PdfRenderBudget;
@@ -0,0 +1,22 @@
1
+ import type { PDFDocumentProxy } from "pdfjs-dist";
2
+ type PageOptions = {
3
+ kind: "page";
4
+ document: PDFDocumentProxy;
5
+ page: number;
6
+ renderWidth: number;
7
+ zoom: number;
8
+ rotation: number;
9
+ };
10
+ type ThumbnailOptions = {
11
+ kind: "thumbnail";
12
+ document: PDFDocumentProxy;
13
+ page: number;
14
+ renderWidth: number;
15
+ };
16
+ /** Serializes PDF.js work for one canvas so cancelled renders cannot race replacements. */
17
+ export declare function usePdfCanvas(options: PageOptions | ThumbnailOptions): {
18
+ canvasRef: import("react").RefObject<HTMLCanvasElement | null>;
19
+ ratio: number;
20
+ error: string;
21
+ };
22
+ export {};
@@ -0,0 +1,14 @@
1
+ export declare function usePdfViewport(documentKey: string): {
2
+ containerRef: import("react").RefObject<HTMLDivElement | null>;
3
+ zoom: number;
4
+ setZoom: import("react").Dispatch<import("react").SetStateAction<number>>;
5
+ rotation: number;
6
+ setRotation: import("react").Dispatch<import("react").SetStateAction<number>>;
7
+ fullscreen: boolean;
8
+ canZoomOut: boolean;
9
+ canZoomIn: boolean;
10
+ zoomOut: () => void;
11
+ zoomIn: () => void;
12
+ rotate: () => void;
13
+ toggleFullscreen: () => undefined;
14
+ };
@@ -0,0 +1,50 @@
1
+ import { type CSSProperties } from "react";
2
+ import type { LoadDocumentBufferOptions, LoadDocumentUrlOptions, PDFViewerConfig, PDFViewerProps } from "@embedpdf/react-pdf-viewer";
3
+ import { type DocumentViewerProps, type DocumentViewerSource } from '../document-viewer/DocumentViewer';
4
+ import type { DocumentViewerV2HeadlessProps } from './HeadlessPdfViewer';
5
+ export declare const DOCUMENT_VIEWER_V2_DISABLED_CATEGORIES: readonly ["annotation", "redaction", "insert", "form"];
6
+ export type DocumentViewerV2UrlSource = Omit<LoadDocumentUrlOptions, "documentId" | "autoActivate"> & {
7
+ id: string;
8
+ kind: "url";
9
+ };
10
+ export type DocumentViewerV2BufferSource = Omit<LoadDocumentBufferOptions, "documentId" | "autoActivate"> & {
11
+ id: string;
12
+ kind: "buffer";
13
+ };
14
+ export type DocumentViewerV2PdfSource = DocumentViewerV2UrlSource | DocumentViewerV2BufferSource;
15
+ export type DocumentViewerV2ImageSource = Extract<DocumentViewerSource, {
16
+ kind: 'image';
17
+ }>;
18
+ export type DocumentViewerV2Source = DocumentViewerV2PdfSource | DocumentViewerSource;
19
+ export type DocumentViewerV2Config = Omit<PDFViewerConfig, "src" | "documentManager"> & {
20
+ documentManager?: Omit<NonNullable<PDFViewerConfig["documentManager"]>, "initialDocuments">;
21
+ };
22
+ type SharedViewerProps = {
23
+ className?: string;
24
+ style?: CSSProperties;
25
+ ariaLabel?: string;
26
+ };
27
+ export type DocumentViewerV2NativeProps = SharedViewerProps & Pick<DocumentViewerProps, 'title' | 'toolbar' | 'footer'> & {
28
+ renderer?: 'viewer';
29
+ source: DocumentViewerV2PdfSource;
30
+ config?: DocumentViewerV2Config;
31
+ onInit?: PDFViewerProps["onInit"];
32
+ onReady?: PDFViewerProps["onReady"];
33
+ currentPage?: never;
34
+ onCurrentPageChange?: never;
35
+ pageMode?: never;
36
+ renderPage?: never;
37
+ };
38
+ export type DocumentViewerV2Props = DocumentViewerV2NativeProps | DocumentViewerV2HeadlessProps | (DocumentViewerProps & SharedViewerProps & {
39
+ renderer?: never;
40
+ config?: never;
41
+ onInit?: never;
42
+ onReady?: never;
43
+ });
44
+ export declare const DOCUMENT_VIEWER_V2_TOOLBAR_STYLE_ATTRIBUTE = "data-business-ui-document-viewer-v2-toolbar";
45
+ export declare function applyDocumentViewerV2ToolbarStyles(container: {
46
+ shadowRoot: ShadowRoot | null;
47
+ }): void;
48
+ export declare function createDocumentViewerV2Config(source: DocumentViewerV2PdfSource, config?: DocumentViewerV2Config): PDFViewerConfig;
49
+ export declare function DocumentViewerV2(props: DocumentViewerV2Props): import("react").JSX.Element;
50
+ export {};
@@ -0,0 +1,39 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type PluginRegistry } from '@embedpdf/core';
3
+ import { usePdfiumEngine } from '@embedpdf/engines/react';
4
+ import type { DocumentViewerProps } from '../document-viewer/DocumentViewer';
5
+ import type { DocumentViewerV2PdfSource } from './DocumentViewerV2';
6
+ /** Coordinates use the unscaled page, origin at its top-left. Rotation/scale are applied by the viewer. */
7
+ export type DocumentViewerV2OverlayContext = {
8
+ documentId: string;
9
+ page: number;
10
+ pageIndex: number;
11
+ width: number;
12
+ height: number;
13
+ zoom: number;
14
+ rotation: number;
15
+ };
16
+ export type DocumentViewerV2HeadlessProps = Omit<DocumentViewerProps, 'source'> & {
17
+ renderer: 'headless';
18
+ zoom?: number;
19
+ initialZoom?: number | "fit-width";
20
+ rotation?: number;
21
+ showToolbar?: boolean;
22
+ focusTarget?: {
23
+ page: number;
24
+ y: number;
25
+ token: string;
26
+ } | null;
27
+ onZoomChange?: (zoom: number) => void;
28
+ onRotationChange?: (rotation: number) => void;
29
+ features?: {
30
+ search?: boolean;
31
+ textSelection?: boolean;
32
+ pinchZoom?: boolean;
33
+ };
34
+ source: DocumentViewerV2PdfSource;
35
+ renderPageOverlay?: (context: DocumentViewerV2OverlayContext) => ReactNode;
36
+ engineOptions?: Parameters<typeof usePdfiumEngine>[0];
37
+ onReady?: (registry: PluginRegistry) => void;
38
+ };
39
+ export default function HeadlessPdfViewer(props: DocumentViewerV2HeadlessProps): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ export declare function HeadlessSearch({ documentId, onPageChange, enabled, onClear }: {
2
+ documentId: string;
3
+ onPageChange(page: number, point?: {
4
+ x: number;
5
+ y: number;
6
+ }): void;
7
+ enabled: boolean;
8
+ onClear?(): void;
9
+ }): import("react").JSX.Element;
@@ -0,0 +1,41 @@
1
+ import "./styles.css";
2
+ export * from "./Button";
3
+ export * from "./Alert";
4
+ export * from "./EmptyState";
5
+ export * from "./Skeleton";
6
+ export * from "./FloatingControls";
7
+ export * from "./Select";
8
+ export * from "./Combobox";
9
+ export * from "./AutoComplete";
10
+ export * from "./Toast";
11
+ export * from "./DataTable";
12
+ export * from "./ExplainableButton";
13
+ export * from "./ModalShell";
14
+ export * from "./FormField";
15
+ export * from "./DateInput";
16
+ export * from "./Pagination";
17
+ export * from "./RovingTabs";
18
+ export * from "./ToggleSwitch";
19
+ export * from "./SegmentedControl";
20
+ export * from "./RecoverableAsyncState";
21
+ export * from "./useModalFocusTrap";
22
+ export * from "./AsyncSearchPicker";
23
+ export * from "./AsyncDirectoryPicker";
24
+ export * from "./DropdownMenu";
25
+ export * from "./ConfirmDialog";
26
+ export * from "./useDebouncedValue";
27
+ export * from "./Badge";
28
+ export * from "./MetricCard";
29
+ export * from "./PageHeader";
30
+ export * from "./SectionCard";
31
+ export * from "./SortableList";
32
+ export * from "./TreeSelect";
33
+ export * from "./StructuredAddressInput";
34
+ export * from "./FileDropzone";
35
+ export * from "./useFilePreparation";
36
+ export * from "./FilePreparationDialog";
37
+ export * from "./PreparedFileDropzone";
38
+ export * from "./NumericInput";
39
+ export * from "./AmountFormat";
40
+ export * from "./MultiSelect";
41
+ export * from "./Cascader";
@@ -0,0 +1,2 @@
1
+ /** Returns the latest value after it has remained unchanged for the requested delay. */
2
+ export declare function useDebouncedValue<T>(value: T, delay: number): T;
@@ -0,0 +1,28 @@
1
+ export type FilePreparationState = {
2
+ kind: "idle";
3
+ } | {
4
+ kind: "preparing";
5
+ fileName: string;
6
+ index: number;
7
+ total: number;
8
+ } | {
9
+ kind: "failed";
10
+ fileName: string;
11
+ message: string;
12
+ };
13
+ export type FilePreparationOptions = {
14
+ /** Include every resource identity/version that changes the meaning of the selection. */
15
+ ownerKey: string;
16
+ disabled?: boolean;
17
+ prepareFile(file: File, signal: AbortSignal): Promise<File>;
18
+ onFilesSelected(files: readonly File[]): void;
19
+ errorMessage(error: unknown): string;
20
+ };
21
+ /** Prepares a whole selection before invoking the business callback once. */
22
+ export declare function useFilePreparation(options: FilePreparationOptions): {
23
+ state: FilePreparationState;
24
+ busy: boolean;
25
+ cancel: () => void;
26
+ selectFiles: (files: readonly File[]) => Promise<void>;
27
+ };
28
+ export type FilePreparation = ReturnType<typeof useFilePreparation>;
@@ -0,0 +1,3 @@
1
+ import { type RefObject } from "react";
2
+ export type FocusTarget = HTMLElement | null | RefObject<HTMLElement | null>;
3
+ export declare function useModalFocusTrap(active: boolean, dialogRef?: RefObject<HTMLElement | null>, returnFocus?: FocusTarget | false, initialFocus?: FocusTarget): void;
@@ -0,0 +1,16 @@
1
+ import { useEffect as e, useState as t } from "react";
2
+ //#region src/useDebouncedValue.ts
3
+ function n(n, r) {
4
+ let [i, a] = t(n);
5
+ return e(() => {
6
+ let e = Number.isFinite(r) ? Math.max(0, r) : 0;
7
+ if (e === 0) {
8
+ a(n);
9
+ return;
10
+ }
11
+ let t = window.setTimeout(() => a(n), e);
12
+ return () => window.clearTimeout(t);
13
+ }, [r, n]), i;
14
+ }
15
+ //#endregion
16
+ export { n as useDebouncedValue };
@@ -0,0 +1,51 @@
1
+ import { useCallback as e, useLayoutEffect as t, useRef as n, useState as r } from "react";
2
+ //#region src/useFilePreparation.ts
3
+ function i(i) {
4
+ let a = n(i);
5
+ a.current = i;
6
+ let o = n(null), [s, c] = r({ kind: "idle" }), l = e(() => {
7
+ o.current?.abort(), o.current = null, c({ kind: "idle" });
8
+ }, []);
9
+ t(() => (l(), () => {
10
+ o.current?.abort(), o.current = null;
11
+ }), [
12
+ i.ownerKey,
13
+ i.disabled,
14
+ l
15
+ ]);
16
+ let u = e(async (e) => {
17
+ if (!e.length || a.current.disabled || o.current) return;
18
+ let t = a.current.ownerKey, n = new AbortController();
19
+ o.current = n;
20
+ let r = () => o.current === n && !n.signal.aborted && a.current.ownerKey === t && !a.current.disabled, i = [], s = e[0].name;
21
+ try {
22
+ for (let [t, o] of e.entries()) {
23
+ s = o.name, c({
24
+ kind: "preparing",
25
+ fileName: s,
26
+ index: t + 1,
27
+ total: e.length
28
+ });
29
+ let l = await a.current.prepareFile(o, n.signal);
30
+ if (!r()) return;
31
+ i.push(l);
32
+ }
33
+ } catch (e) {
34
+ r() && (o.current = null, c({
35
+ kind: "failed",
36
+ fileName: s,
37
+ message: a.current.errorMessage(e)
38
+ }));
39
+ return;
40
+ }
41
+ r() && (o.current = null, c({ kind: "idle" }), a.current.onFilesSelected(i));
42
+ }, []);
43
+ return {
44
+ state: s,
45
+ busy: s.kind === "preparing",
46
+ cancel: l,
47
+ selectFiles: u
48
+ };
49
+ }
50
+ //#endregion
51
+ export { i as useFilePreparation };
@@ -0,0 +1,71 @@
1
+ import { useEffect as e } from "react";
2
+ //#region src/useModalFocusTrap.ts
3
+ var t = [
4
+ "button:not([disabled])",
5
+ "a[href]",
6
+ "input:not([disabled])",
7
+ "select:not([disabled])",
8
+ "textarea:not([disabled])",
9
+ "[contenteditable]:not([contenteditable='false'])",
10
+ "[tabindex]:not([tabindex='-1'])"
11
+ ].join(",");
12
+ function n(n, r, i, a) {
13
+ e(() => {
14
+ if (!n || r?.current?.hasAttribute("data-ui-focus-managed")) return;
15
+ let e = o(), d = i === !1 ? null : s(i) ?? (document.activeElement instanceof HTMLElement ? document.activeElement : null), f = () => {
16
+ let e = c(r), n = s(a);
17
+ e && l(e) && !e.contains(document.activeElement) && (n?.isConnected && !n.hasAttribute("disabled") ? n : e.querySelector(t) ?? e).focus();
18
+ };
19
+ f();
20
+ let p = requestAnimationFrame(f), m = (e) => {
21
+ if (e.key !== "Tab") return;
22
+ let n = c(r);
23
+ if (!n || !l(n)) return;
24
+ let i = [...n.querySelectorAll(t)], a = i.filter((e) => e.getClientRects().length > 0), o = a.length ? a : i;
25
+ if (!o.length) return;
26
+ let s = o[0], u = o.at(-1);
27
+ e.shiftKey && document.activeElement === s ? (e.preventDefault(), u.focus()) : !e.shiftKey && document.activeElement === u && (e.preventDefault(), s.focus());
28
+ }, h = (e) => {
29
+ let n = c(r);
30
+ n && l(n) && !n.contains(e.target) && (n.querySelector(t) ?? n).focus();
31
+ };
32
+ return document.addEventListener("keydown", m), document.addEventListener("focusin", h), () => {
33
+ cancelAnimationFrame(p), document.removeEventListener("keydown", m), document.removeEventListener("focusin", h), e();
34
+ let t = u(r?.current);
35
+ d && (!t || t.contains(d)) && d.focus();
36
+ };
37
+ }, [
38
+ n,
39
+ r,
40
+ i,
41
+ a
42
+ ]);
43
+ }
44
+ var r = 0, i = "", a = "";
45
+ function o() {
46
+ if (r === 0) {
47
+ i = document.body.style.overflow, a = document.body.style.paddingRight;
48
+ let e = document.documentElement.clientWidth, t = e > 0 ? Math.max(0, window.innerWidth - e) : 0;
49
+ document.body.style.overflow = "hidden", t > 0 && (document.body.style.paddingRight = `${t}px`);
50
+ }
51
+ r += 1;
52
+ let e = !1;
53
+ return () => {
54
+ e || (e = !0, r = Math.max(0, r - 1), r === 0 && (document.body.style.overflow = i, document.body.style.paddingRight = a));
55
+ };
56
+ }
57
+ function s(e) {
58
+ return e && "current" in e ? e.current : e || null;
59
+ }
60
+ function c(e) {
61
+ let t = e?.current ?? u();
62
+ return !t || t.getAttribute("aria-modal") !== "true" || t.inert ? null : t;
63
+ }
64
+ function l(e) {
65
+ return u() === e;
66
+ }
67
+ function u(e) {
68
+ return [...document.querySelectorAll("[role='dialog'][aria-modal='true'],[role='alertdialog'][aria-modal='true']")].filter((t) => t !== e && !t.inert && t.getAttribute("aria-hidden") !== "true").at(-1);
69
+ }
70
+ //#endregion
71
+ export { n as useModalFocusTrap };
@@ -0,0 +1,145 @@
1
+ # Agent 公共组件使用指南
2
+
3
+ 本指南面向在业务应用中使用 `@jc-times/business-ui` 的 agent,也适用于维护本库的 agent。先按场景选型,再按需阅读 API 和专题文档,不需要通读历史审计记录。
4
+
5
+ ## 开始工作前
6
+
7
+ 依赖升级、兼容版本与消费迁移边界见 [依赖维护](dependencies.md)。
8
+
9
+ 1. 查看消费项目的 `AGENTS.md`、`package.json` 和锁文件,确认实际安装的精确版本、现有业务包装和主题入口。仓库当前源码不代表消费项目已安装该能力。
10
+ 2. 在下表选择公共组件;已有组件能覆盖时优先复用,业务差异通过 props、插槽或薄包装表达。不要复制本库源码或另写同类焦点、菜单、日期及选择交互。
11
+ 3. 阅读 [API 与使用示例](api.md) 及相关专题。完整参数和导出以实际安装包的 `dist/types` 类型声明为准;在本仓库维护时核对 `src/index.ts`、`package.json` 的 `exports` 和组件源码。不要根据其他组件库猜测本包参数。
12
+ 4. 主入口导入常规组件,应用统一导入 `@jc-times/business-ui/styles.css`;通过 `--ui-*` 令牌接入主题。文档查看、行政区划和服务端金额格式使用独立子入口。
13
+ 5. 核对新能力所需版本及 peer dependencies;升级使用已发布的精确版本并提交锁文件。未发布工作树、文档中出现的历史版本号均不能作为安装最新版的依据。
14
+
15
+ ## 按需求选择组件
16
+
17
+ ### 页面、信息与反馈
18
+
19
+ | 需求 | 使用组件 | 边界与选择依据 |
20
+ | --- | --- | --- |
21
+ | 页面标题、说明、操作区 | `PageHeader` | 输出页面级 h1;不要用于每个小区块 |
22
+ | 带标题的内容区域 | `SectionCard` | 按页面大纲设置 headingLevel;业务内容放 children |
23
+ | 状态标签 / 单项统计 | `Badge` / `MetricCard` | 业务状态映射、数值计算和格式由应用提供 |
24
+ | 常规操作 / 解释不可用原因 | `Button` / `ExplainableButton` | 图标按钮必须有 aria-label;禁用条件由业务判断 |
25
+ | 导航 / 文件下载 | `LinkButton` | 必须提供 href;输出真实链接并保留浏览器下载语义;跨域/鉴权下载先由业务层取得签名地址或 Blob URL,不用临时 DOM 点击模拟 |
26
+ | 持续提示 / 无数据 / 加载占位 | `Alert` / `EmptyState` / `Skeleton` | 区分错误、空结果和加载,不用空状态掩盖请求失败 |
27
+ | 加载与可重试错误 | `RecoverableAsyncState`(别名 `AsyncState`) | 页面提供状态、文案和重试回调 |
28
+ | 操作完成后的临时通知 | `ToastProvider` 与 `useToast` | 统一队列;关键表单错误仍应在字段或页面呈现 |
29
+ | 切换内容面板 | `RovingTabList` + `RovingTabPanel` | 配对 groupId/value;不要把字段枚举选项当内容页签 |
30
+
31
+ ### 表单与选项
32
+
33
+ | 需求 | 使用组件 | 边界与选择依据 |
34
+ | --- | --- | --- |
35
+ | 文本 / 多行文本 | `TextInput` / `TextArea` | 自带 label、helpText、error;自定义控件用 `FormField` 关联标签 |
36
+ | 勾选 / 开关 | `Checkbox` / `ToggleSwitch` | 分别表达勾选项与开启/关闭;请求和持久化留在应用 |
37
+ | 少量互斥选项,全部平铺可见 | [SegmentedControl](api.md#分段控制器-segmentedcontrol) | 受控字符串 value/options/onChange;支持实色/浅底、图标、纵向和收缩宽度;原生 radio 语义,须提供 aria-label,新增参数先核对安装版本 |
38
+ | 数量、精确十进制 | `NumericInput` | value 为字符串;提交用 onValueCommit,不能用草稿计算 |
39
+ | 金额等以最小单位整数存储的数值 | `ScaledNumericInput` | value 为安全整数或 null;scale=100 表示分,不是元 |
40
+ | 原生下拉单选 | `SelectInput` | 简单固定候选,使用浏览器原生选择体验 |
41
+ | 定制单选浮层 | `Select` | 需要公共浮层、定制选项或手机抽屉体验 |
42
+ | 搜索已有选项 / 允许自由文本 | `Combobox` / `AutoComplete` | 必须选已有项用前者;允许新文本用后者 |
43
+ | 多选与已选标签 | `MultiSelect` | values/onValuesChange;异步候选之外的标签通过 selectedItems 补足 |
44
+ | 部门、分类、省市区等多列级联选择 | `Cascader` | RC 内核;单选路径、多选路径数组;默认父子独立多选;扁平数据用 buildCascaderOptions 转换 |
45
+ | 部门、分类等树形单选或多选 | `TreeSelect` | 应用映射 id/label/parentId;父子独立选择,不自动级联 |
46
+ | 远程目录搜索,需防抖、取消、缓存 | `AsyncDirectoryPicker` | 提供 loadItems(query, signal) 和 ownerKey;网络适配器必须传递 signal |
47
+ | 页面已有查询状态和请求生命周期 | `AsyncSearchPicker` | 页面提供 query/items/status/onSearch/onSelect;组件不发请求 |
48
+ | 日期输入与日历 | `DateInput` | 用 minDate/maxDate/isDateUnavailable 约束;业务时区和日期转换由应用负责 |
49
+ | 省市区与详细地址 | `StructuredAddressInput` | 默认无地址类型,海外作为顶级地区;内部复用 Cascader;独立行政区划入口提供数据;地址解析、拼接和字段映射由应用负责 |
50
+
51
+ ### 弹窗、浮层与操作
52
+
53
+ | 需求 | 使用组件 | 边界与选择依据 |
54
+ | --- | --- | --- |
55
+ | 编辑表单、详情等模态内容 | `ModalShell`(别名 `Dialog`) | 正文用 children,固定操作区用 footer;不重复安装焦点陷阱 |
56
+ | 独立、强层级确认 | `ConfirmDialog` | 例如删除确认;应用决定危险级别、busy 和提交逻辑 |
57
+ | 触发器旁的轻量确认 | `Popconfirm` | 依附锚点;长表单使用 ModalShell |
58
+ | 更多操作、分组操作、可勾选菜单 | `DropdownMenu`(别名 `Menu`) | 统一 menu 键盘行为;导航当前位置用 current,选中配置用 selectionMode |
59
+ | 补充信息或小型表单浮层 | `Popover` | 不代替操作菜单 |
60
+ | 简短悬浮解释 | `Tooltip` | 不承载必填信息或交互表单 |
61
+
62
+ ### 表格、排序、文件与查看
63
+
64
+ | 需求 | 使用组件或入口 | 边界与选择依据 |
65
+ | --- | --- | --- |
66
+ | 列表表格、排序、行选择 | [DataTable](data-table.md) | 当前源码已采用MRT;旧参数兼容,完整配置用options或useDataTable/DataTableView;长页面可按需开启底部吸附横向滚动条 |
67
+ | 评估筛选、列管理、主从明细行合并或展开 | [表格能力与 MRT 样例](mrt-poc.md) | productDisplay由调用方选择;同行用rowSpan且按合同分页;仅本地验证,不是已发布API |
68
+ | 页码导航 | `Pagination` | 页面负责取数;窄屏自动简化 |
69
+ | 卡片拖动排序 | `SortableList` | 提供稳定唯一 getKey、getTextValue、renderItem 和 onReorder;应用保存顺序 |
70
+ | 点击、键盘或拖入选择文件 | `FileDropzone` | accept/maxFileSize/maxFiles 与拒绝反馈;不负责上传请求 |
71
+ | 上传前异步转换、校验或解密适配 | `PreparedFileDropzone` | prepareFile(file, signal),整批成功后提交;必须提供 ownerKey |
72
+ | 非文件选择器入口需要同一前处理流程 | `useFilePreparation` + `FilePreparationDialog` | 复用取消、进度、错误处理;密钥和服务端策略不进入 UI 包 |
73
+ | 图片、已有 PDF.js 对象或旧业务叠层 | V2 兼容 `DocumentViewer`,`document-viewer-v2` 子入口 | 未发布候选保留全部 V1 参数及底层组合;PDF 加载和销毁仍由调用方负责;旧包继续原入口 |
74
+ | PDFium/WASM PDF 查看迁移 | `DocumentViewerV2`,`document-viewer-v2` 子入口 | URL/Buffer 使用 PDFium;兼容分支不是引擎替换,核对安装版本与专题文档 |
75
+ | PDFium 原生 React 叠层、受控页码、单页显示 | V2 的 `renderer="headless"` | 未发布候选;用 `renderPageOverlay` 追加业务层,基础工具栏与默认成品 UI 的能力不同 |
76
+ | 服务端或文档中的金额格式化 | `amount-format` 子入口 | 无 React 依赖;formatDecimalAmount/formatScaledMoney 不负责汇率或中文大写 |
77
+ | 中国省市区数据 | `chinese-address-regions` 子入口 | 按需加载,核对数据版本;不跨仓库引用源码 |
78
+
79
+ ## 组合示例
80
+
81
+ 以下示例可用于已有 React 页面;请求、权限和保存由页面外层业务代码管理。
82
+
83
+ ```tsx
84
+ import { useId, useState } from "react";
85
+ import { Button, ModalShell, NumericInput, TextInput } from "@jc-times/business-ui";
86
+ import "@jc-times/business-ui/styles.css";
87
+
88
+ export function EditQuantity() {
89
+ const titleId = useId();
90
+ const [open, setOpen] = useState(false);
91
+ const [name, setName] = useState("");
92
+ const [quantity, setQuantity] = useState("");
93
+ return <>
94
+ <Button onClick={() => setOpen(true)}>编辑数量</Button>
95
+ {open && <ModalShell titleId={titleId} closeLabel="关闭" onClose={() => setOpen(false)} title="编辑数量"
96
+ footer={<Button onClick={() => setOpen(false)}>关闭</Button>}>
97
+ <TextInput label="名称" value={name}
98
+ onChange={event => setName(event.currentTarget.value)} />
99
+ <NumericInput label="数量" value={quantity} onValueCommit={setQuantity}
100
+ decimalPlaces={3} min="0" />
101
+ </ModalShell>}
102
+ </>;
103
+ }
104
+ ```
105
+
106
+ 更多数字和输入装饰示例见 [API](api.md);多选、日期与服务端表格见 [成熟度迁移](maturity-migration.md);文件前处理见 [上传前处理](file-preparation.md);查看器分别见 [现有入口](document-viewer.md) 和 [V2 迁移](document-viewer-v2.md)。
107
+
108
+ ## 必须保持的契约
109
+
110
+ - 通用交互缺陷、无障碍和响应式问题在公共包修复;权限、接口、领域状态、业务文案在消费项目组合。只有一个项目需要的领域封装不进入公共包。
111
+ - 默认控件高度 40px,粗指针触屏最小 44px。统一使用主题令牌,不单独为金额框、搜索框重写高度、字体或 padding。
112
+ - 使用公开插槽、ref 和原生属性透传,不依赖未承诺的内部 DOM 层级;弹窗内浮层保持在最近 ModalShell 的 portal 容器。
113
+ - 数字空值保留为空字符串或 null,空值归零由应用决定;原生 FormData 可能包含格式化显示文本,业务提交使用受控数值状态。
114
+ - ownerKey 包含会改变数据范围或上传目标的身份;上下文变化时更新。目录查询和文件适配器传递 AbortSignal,避免旧请求覆盖新页面。
115
+ - 大列表提供稳定数据/回调引用和唯一 ID,更新数据时使用新引用。不能假设所有组件都有虚拟化;用真实规模验证长列表、长 PDF 和大量已选项。
116
+ - 客户端文件限制和隐藏按钮不能替代服务端校验及权限控制。
117
+
118
+ ## 完成接入前的验证
119
+
120
+ 1. 运行消费项目要求的类型检查、构建及相关测试,验证实际业务回调、空值、禁用、错误、重试和资源切换。
121
+ 2. 检查桌面与手机视口、长文字、弹窗嵌套下拉、键盘操作和焦点归还;需要软键盘的流程做真实手机验收。视口模拟不等于真实手机通过。
122
+ 3. 已配置 `business-ui-gate` 的项目运行现有门禁;不要扩大 baseline 掩盖新增手写组件。首次接入的基线按项目规则生成并审核。
123
+ 4. 修改公共实现时运行本库相关检查,并按 [多视口验收](viewport-qa.md) 和 [发布流程](release.md) 处理。区分本地验证、包已发布、消费方升级与生产部署状态。
124
+
125
+ ## 文档分工与维护
126
+
127
+ - [README](../README.md):包入口、安装与文档导航。
128
+ - 本指南:按场景选型、组合原则和接入检查。
129
+ - [API](api.md):公共行为、参数说明和代码示例;完整类型由安装包提供。
130
+ - [组件策略](component-strategy.md):设计依据与公共能力边界,历史选型不作为当前参数契约。
131
+ - 专题与迁移文档:弹窗、成熟交互、文件处理、查看器的详细契约。
132
+ - [变更记录](../CHANGELOG.md):按版本查变化;历史审计是当时证据,不作为当前能力清单。
133
+
134
+ 新增或改变公共能力时,同步选型表、对应 API/专题及发布文件清单,避免在多处复制完整参数表。消费项目可在自己的 AGENTS.md 中加入:
135
+
136
+ > 修改 UI 前,先阅读已安装的 @jc-times/business-ui 包中的 docs/agent-guide.md,并按需阅读其 API 和专题文档;优先复用公共组件,按安装版本的类型声明实现。
137
+
138
+ PDFium Headless 0.2.33 支持搜索、选择、双指缩放及外部受控缩放/旋转;详见 V2 专题,业务叠层控件标记 data-pdf-business-control。
139
+
140
+
141
+ ### PDFium 专用构建入口(0.2.34)
142
+
143
+ 只使用原生 PDF 叠层的页面可从 `@jc-times/business-ui/pdfium-viewer` 导入 `PdfiumViewer`,参数与 Headless 模式一致,但省略 `renderer`。该入口不引入 PDF.js 兼容渲染或完整查看器外壳,适用于独立公开查看页面;引擎仍按需加载。通过 `engineOptions.wasmUrl` 提供本地资源的绝对 URL,保证 Blob worker 可解析;同一 `source.kind/id` 维持会话,文件版本改变时必须改变 `id`。
144
+
145
+ PDFium 专用入口支持 `initialZoom="fit-width"`(0.2.37):无受控 zoom 时由官方插件适配页面宽度;旧默认 100% 保持不变。