panelui-native 0.65.0 → 0.68.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/README.md +21 -1
- package/lib/module/components/area-chart/index.js +18 -1
- package/lib/module/components/area-chart/index.js.map +1 -1
- package/lib/module/components/bar-chart/index.js +18 -1
- package/lib/module/components/bar-chart/index.js.map +1 -1
- package/lib/module/components/bottom-sheet/index.js.map +1 -1
- package/lib/module/components/candlestick-chart/index.js +18 -1
- package/lib/module/components/candlestick-chart/index.js.map +1 -1
- package/lib/module/components/carousel/carousel-lifecycle.js +51 -0
- package/lib/module/components/carousel/carousel-lifecycle.js.map +1 -0
- package/lib/module/components/carousel/index.js +61 -24
- package/lib/module/components/carousel/index.js.map +1 -1
- package/lib/module/components/context-menu/context-menu-invocation.js +23 -0
- package/lib/module/components/context-menu/context-menu-invocation.js.map +1 -0
- package/lib/module/components/context-menu/index.js +78 -9
- package/lib/module/components/context-menu/index.js.map +1 -1
- package/lib/module/components/dialog/index.js.map +1 -1
- package/lib/module/components/drawer/index.js.map +1 -1
- package/lib/module/components/flow/flow-accessibility.js +18 -2
- package/lib/module/components/flow/flow-accessibility.js.map +1 -1
- package/lib/module/components/flow/flow-identifiers.js +81 -0
- package/lib/module/components/flow/flow-identifiers.js.map +1 -0
- package/lib/module/components/flow/index.js +12 -18
- package/lib/module/components/flow/index.js.map +1 -1
- package/lib/module/components/form/index.js +7 -5
- package/lib/module/components/form/index.js.map +1 -1
- package/lib/module/components/form/typed-form.js +11 -0
- package/lib/module/components/form/typed-form.js.map +1 -0
- package/lib/module/components/form/use-form.js +4 -0
- package/lib/module/components/form/use-form.js.map +1 -1
- package/lib/module/components/heatmap-chart/index.js +27 -1
- package/lib/module/components/heatmap-chart/index.js.map +1 -1
- package/lib/module/components/line-chart/index.js +18 -1
- package/lib/module/components/line-chart/index.js.map +1 -1
- package/lib/module/components/message-scroller/index.js +187 -17
- package/lib/module/components/message-scroller/index.js.map +1 -1
- package/lib/module/components/message-scroller/message-scroller-math.js +43 -0
- package/lib/module/components/message-scroller/message-scroller-math.js.map +1 -0
- package/lib/module/components/meter/index.js +250 -0
- package/lib/module/components/meter/index.js.map +1 -0
- package/lib/module/components/meter/meter-scale.js +88 -0
- package/lib/module/components/meter/meter-scale.js.map +1 -0
- package/lib/module/components/planner/index.js +714 -0
- package/lib/module/components/planner/index.js.map +1 -0
- package/lib/module/components/planner/planner-entries.js +124 -0
- package/lib/module/components/planner/planner-entries.js.map +1 -0
- package/lib/module/components/popover/index.js.map +1 -1
- package/lib/module/components/radar-chart/index.js +18 -1
- package/lib/module/components/radar-chart/index.js.map +1 -1
- package/lib/module/components/scatter-chart/index.js +18 -1
- package/lib/module/components/scatter-chart/index.js.map +1 -1
- package/lib/module/components/selection-mode/index.js +26 -31
- package/lib/module/components/selection-mode/index.js.map +1 -1
- package/lib/module/components/selection-mode/selection-mode-contracts.js +48 -0
- package/lib/module/components/selection-mode/selection-mode-contracts.js.map +1 -0
- package/lib/module/components/signature/index.js +62 -20
- package/lib/module/components/signature/index.js.map +1 -1
- package/lib/module/components/signature/signature-accessibility.js +58 -0
- package/lib/module/components/signature/signature-accessibility.js.map +1 -0
- package/lib/module/components/skeleton/index.js +45 -7
- package/lib/module/components/skeleton/index.js.map +1 -1
- package/lib/module/components/slider/index.js +22 -18
- package/lib/module/components/slider/index.js.map +1 -1
- package/lib/module/components/spinner/index.js +63 -14
- package/lib/module/components/spinner/index.js.map +1 -1
- package/lib/module/components/time-picker/index.js +46 -21
- package/lib/module/components/time-picker/index.js.map +1 -1
- package/lib/module/components/time-picker/ruler-window.js +25 -0
- package/lib/module/components/time-picker/ruler-window.js.map +1 -0
- package/lib/module/hooks/breakpoint-contract.js +29 -0
- package/lib/module/hooks/breakpoint-contract.js.map +1 -0
- package/lib/module/hooks/index.js +1 -1
- package/lib/module/hooks/index.js.map +1 -1
- package/lib/module/hooks/use-breakpoint.js +46 -1
- package/lib/module/hooks/use-breakpoint.js.map +1 -1
- package/lib/module/index.js +3 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/primitives/chart-accessibility.js +68 -0
- package/lib/module/primitives/chart-accessibility.js.map +1 -0
- package/lib/module/primitives/scrim.js +94 -3
- package/lib/module/primitives/scrim.js.map +1 -1
- package/lib/typescript/src/components/area-chart/index.d.ts +2 -1
- package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/bar-chart/index.d.ts +2 -1
- package/lib/typescript/src/components/bar-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/bottom-sheet/index.d.ts +3 -0
- package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
- package/lib/typescript/src/components/candlestick-chart/index.d.ts +2 -1
- package/lib/typescript/src/components/candlestick-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/carousel/carousel-lifecycle.d.ts +23 -0
- package/lib/typescript/src/components/carousel/carousel-lifecycle.d.ts.map +1 -0
- package/lib/typescript/src/components/carousel/index.d.ts +5 -2
- package/lib/typescript/src/components/carousel/index.d.ts.map +1 -1
- package/lib/typescript/src/components/context-menu/context-menu-invocation.d.ts +20 -0
- package/lib/typescript/src/components/context-menu/context-menu-invocation.d.ts.map +1 -0
- package/lib/typescript/src/components/context-menu/index.d.ts +18 -8
- package/lib/typescript/src/components/context-menu/index.d.ts.map +1 -1
- package/lib/typescript/src/components/dialog/index.d.ts +3 -0
- package/lib/typescript/src/components/dialog/index.d.ts.map +1 -1
- package/lib/typescript/src/components/drawer/index.d.ts +3 -0
- package/lib/typescript/src/components/drawer/index.d.ts.map +1 -1
- package/lib/typescript/src/components/flow/flow-accessibility.d.ts +8 -0
- package/lib/typescript/src/components/flow/flow-accessibility.d.ts.map +1 -1
- package/lib/typescript/src/components/flow/flow-identifiers.d.ts +24 -0
- package/lib/typescript/src/components/flow/flow-identifiers.d.ts.map +1 -0
- package/lib/typescript/src/components/flow/index.d.ts +6 -5
- package/lib/typescript/src/components/flow/index.d.ts.map +1 -1
- package/lib/typescript/src/components/form/index.d.ts +18 -5
- package/lib/typescript/src/components/form/index.d.ts.map +1 -1
- package/lib/typescript/src/components/form/typed-form.d.ts +7 -0
- package/lib/typescript/src/components/form/typed-form.d.ts.map +1 -0
- package/lib/typescript/src/components/form/use-form.d.ts +4 -0
- package/lib/typescript/src/components/form/use-form.d.ts.map +1 -1
- package/lib/typescript/src/components/heatmap-chart/index.d.ts +2 -1
- package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/line-chart/index.d.ts +2 -1
- package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/message-scroller/index.d.ts +30 -2
- package/lib/typescript/src/components/message-scroller/index.d.ts.map +1 -1
- package/lib/typescript/src/components/message-scroller/message-scroller-math.d.ts +18 -0
- package/lib/typescript/src/components/message-scroller/message-scroller-math.d.ts.map +1 -0
- package/lib/typescript/src/components/meter/index.d.ts +270 -0
- package/lib/typescript/src/components/meter/index.d.ts.map +1 -0
- package/lib/typescript/src/components/meter/meter-scale.d.ts +57 -0
- package/lib/typescript/src/components/meter/meter-scale.d.ts.map +1 -0
- package/lib/typescript/src/components/planner/index.d.ts +274 -0
- package/lib/typescript/src/components/planner/index.d.ts.map +1 -0
- package/lib/typescript/src/components/planner/planner-entries.d.ts +75 -0
- package/lib/typescript/src/components/planner/planner-entries.d.ts.map +1 -0
- package/lib/typescript/src/components/popover/index.d.ts +3 -0
- package/lib/typescript/src/components/popover/index.d.ts.map +1 -1
- package/lib/typescript/src/components/radar-chart/index.d.ts +4 -0
- package/lib/typescript/src/components/radar-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/scatter-chart/index.d.ts +2 -1
- package/lib/typescript/src/components/scatter-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/selection-mode/index.d.ts +6 -4
- package/lib/typescript/src/components/selection-mode/index.d.ts.map +1 -1
- package/lib/typescript/src/components/selection-mode/selection-mode-contracts.d.ts +18 -0
- package/lib/typescript/src/components/selection-mode/selection-mode-contracts.d.ts.map +1 -0
- package/lib/typescript/src/components/signature/index.d.ts +6 -0
- package/lib/typescript/src/components/signature/index.d.ts.map +1 -1
- package/lib/typescript/src/components/signature/signature-accessibility.d.ts +27 -0
- package/lib/typescript/src/components/signature/signature-accessibility.d.ts.map +1 -0
- package/lib/typescript/src/components/skeleton/index.d.ts +25 -2
- package/lib/typescript/src/components/skeleton/index.d.ts.map +1 -1
- package/lib/typescript/src/components/slider/index.d.ts.map +1 -1
- package/lib/typescript/src/components/spinner/index.d.ts +25 -2
- package/lib/typescript/src/components/spinner/index.d.ts.map +1 -1
- package/lib/typescript/src/components/time-picker/index.d.ts +5 -6
- package/lib/typescript/src/components/time-picker/index.d.ts.map +1 -1
- package/lib/typescript/src/components/time-picker/ruler-window.d.ts +12 -0
- package/lib/typescript/src/components/time-picker/ruler-window.d.ts.map +1 -0
- package/lib/typescript/src/hooks/breakpoint-contract.d.ts +6 -0
- package/lib/typescript/src/hooks/breakpoint-contract.d.ts.map +1 -0
- package/lib/typescript/src/hooks/index.d.ts +1 -1
- package/lib/typescript/src/hooks/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/use-breakpoint.d.ts +22 -0
- package/lib/typescript/src/hooks/use-breakpoint.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +4 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/primitives/chart-accessibility.d.ts +33 -0
- package/lib/typescript/src/primitives/chart-accessibility.d.ts.map +1 -0
- package/lib/typescript/src/primitives/scrim.d.ts +5 -3
- package/lib/typescript/src/primitives/scrim.d.ts.map +1 -1
- package/lib/typescript/types/form-api.type-test.d.ts +2 -0
- package/lib/typescript/types/form-api.type-test.d.ts.map +1 -0
- package/package.json +34 -5
- package/src/components/area-chart/index.tsx +27 -2
- package/src/components/bar-chart/index.tsx +27 -2
- package/src/components/bottom-sheet/index.tsx +3 -0
- package/src/components/candlestick-chart/index.tsx +30 -2
- package/src/components/carousel/carousel-lifecycle.ts +74 -0
- package/src/components/carousel/index.tsx +80 -25
- package/src/components/context-menu/context-menu-invocation.ts +44 -0
- package/src/components/context-menu/index.tsx +113 -13
- package/src/components/dialog/index.tsx +3 -0
- package/src/components/drawer/index.tsx +3 -0
- package/src/components/flow/flow-accessibility.ts +16 -2
- package/src/components/flow/flow-identifiers.ts +98 -0
- package/src/components/flow/index.tsx +29 -19
- package/src/components/form/index.tsx +32 -5
- package/src/components/form/typed-form.ts +13 -0
- package/src/components/form/use-form.ts +9 -0
- package/src/components/heatmap-chart/index.tsx +49 -3
- package/src/components/line-chart/index.tsx +27 -2
- package/src/components/message-scroller/index.tsx +273 -13
- package/src/components/message-scroller/message-scroller-math.ts +62 -0
- package/src/components/meter/index.tsx +318 -0
- package/src/components/meter/meter-scale.ts +109 -0
- package/src/components/planner/index.tsx +886 -0
- package/src/components/planner/planner-entries.ts +146 -0
- package/src/components/popover/index.tsx +3 -0
- package/src/components/radar-chart/index.tsx +24 -0
- package/src/components/scatter-chart/index.tsx +27 -2
- package/src/components/selection-mode/index.tsx +37 -30
- package/src/components/selection-mode/selection-mode-contracts.ts +53 -0
- package/src/components/signature/index.tsx +107 -20
- package/src/components/signature/signature-accessibility.ts +67 -0
- package/src/components/skeleton/index.tsx +59 -5
- package/src/components/slider/index.tsx +28 -18
- package/src/components/spinner/index.tsx +80 -13
- package/src/components/time-picker/index.tsx +57 -29
- package/src/components/time-picker/ruler-window.ts +24 -0
- package/src/hooks/breakpoint-contract.ts +31 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/use-breakpoint.ts +61 -10
- package/src/index.ts +32 -0
- package/src/primitives/chart-accessibility.ts +102 -0
- package/src/primitives/scrim.tsx +127 -7
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import { createContext, createElement, useContext, type ReactNode } from 'react';
|
|
1
2
|
import { useWindowDimensions } from 'react-native';
|
|
3
|
+
import {
|
|
4
|
+
breakpointAt,
|
|
5
|
+
breakpointEntries,
|
|
6
|
+
type BreakpointDefinition,
|
|
7
|
+
} from './breakpoint-contract';
|
|
2
8
|
|
|
3
9
|
/** Tailwind's breakpoints, minus the ones no phone or tablet reaches. */
|
|
4
10
|
export const BREAKPOINTS = { sm: 640, md: 768, lg: 1024, xl: 1280 } as const;
|
|
11
|
+
const DEFAULT_ENTRIES = breakpointEntries(BREAKPOINTS);
|
|
5
12
|
|
|
6
13
|
export type Breakpoint = keyof typeof BREAKPOINTS;
|
|
7
14
|
|
|
@@ -15,6 +22,59 @@ export interface UseBreakpointResult {
|
|
|
15
22
|
isLandscape: boolean;
|
|
16
23
|
}
|
|
17
24
|
|
|
25
|
+
export interface BreakpointResult<Name extends string> {
|
|
26
|
+
current: Name | 'base';
|
|
27
|
+
isAtLeast: (breakpoint: Name) => boolean;
|
|
28
|
+
width: number;
|
|
29
|
+
height: number;
|
|
30
|
+
isLandscape: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface BreakpointProviderProps {
|
|
34
|
+
children?: ReactNode;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface BreakpointContract<Name extends string> {
|
|
38
|
+
Provider: (props: BreakpointProviderProps) => ReactNode;
|
|
39
|
+
useBreakpoint: () => BreakpointResult<Name>;
|
|
40
|
+
breakpoints: Readonly<Record<Name, number>>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Create app-specific responsive names. One Provider owns the window
|
|
45
|
+
* subscription; every bound hook below it reads the same dimensions.
|
|
46
|
+
*/
|
|
47
|
+
export function createBreakpoints<const T extends BreakpointDefinition>(
|
|
48
|
+
definition: T
|
|
49
|
+
): BreakpointContract<keyof T & string> {
|
|
50
|
+
type Name = keyof T & string;
|
|
51
|
+
const breakpoints = Object.freeze({ ...definition }) as Readonly<T>;
|
|
52
|
+
const entries = breakpointEntries(breakpoints);
|
|
53
|
+
const Context = createContext<{ width: number; height: number } | null>(null);
|
|
54
|
+
|
|
55
|
+
function Provider({ children }: BreakpointProviderProps) {
|
|
56
|
+
const dimensions = useWindowDimensions();
|
|
57
|
+
return createElement(Context.Provider, { value: dimensions }, children);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function useBoundBreakpoint(): BreakpointResult<Name> {
|
|
61
|
+
const dimensions = useContext(Context);
|
|
62
|
+
if (!dimensions) {
|
|
63
|
+
throw new Error('This breakpoint hook must be used inside its Breakpoint Provider.');
|
|
64
|
+
}
|
|
65
|
+
const { width, height } = dimensions;
|
|
66
|
+
return {
|
|
67
|
+
current: breakpointAt(entries, width),
|
|
68
|
+
isAtLeast: (name) => width >= breakpoints[name]!,
|
|
69
|
+
width,
|
|
70
|
+
height,
|
|
71
|
+
isLandscape: width > height,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { Provider, useBreakpoint: useBoundBreakpoint, breakpoints };
|
|
76
|
+
}
|
|
77
|
+
|
|
18
78
|
/**
|
|
19
79
|
* Responsive state from the window size — the React Native answer to a media
|
|
20
80
|
* query, which has no equivalent here.
|
|
@@ -31,16 +91,7 @@ export interface UseBreakpointResult {
|
|
|
31
91
|
export function useBreakpoint(): UseBreakpointResult {
|
|
32
92
|
const { width, height } = useWindowDimensions();
|
|
33
93
|
|
|
34
|
-
const current
|
|
35
|
-
width >= BREAKPOINTS.xl
|
|
36
|
-
? 'xl'
|
|
37
|
-
: width >= BREAKPOINTS.lg
|
|
38
|
-
? 'lg'
|
|
39
|
-
: width >= BREAKPOINTS.md
|
|
40
|
-
? 'md'
|
|
41
|
-
: width >= BREAKPOINTS.sm
|
|
42
|
-
? 'sm'
|
|
43
|
-
: 'base';
|
|
94
|
+
const current = breakpointAt(DEFAULT_ENTRIES, width);
|
|
44
95
|
|
|
45
96
|
return {
|
|
46
97
|
current,
|
package/src/index.ts
CHANGED
|
@@ -209,6 +209,8 @@ export {
|
|
|
209
209
|
type FlowControlsProps,
|
|
210
210
|
type FlowMiniMapProps,
|
|
211
211
|
type FlowConnection,
|
|
212
|
+
type FlowEndpoint,
|
|
213
|
+
type FlowEndpointReference,
|
|
212
214
|
type FlowViewport,
|
|
213
215
|
type FlowNodePosition,
|
|
214
216
|
type FlowEdgeVariant,
|
|
@@ -483,10 +485,14 @@ export {
|
|
|
483
485
|
} from './components/field';
|
|
484
486
|
export {
|
|
485
487
|
Form,
|
|
488
|
+
createForm,
|
|
486
489
|
useForm,
|
|
487
490
|
useField,
|
|
488
491
|
type FormProps,
|
|
489
492
|
type FormFieldProps,
|
|
493
|
+
type TypedForm,
|
|
494
|
+
type TypedFormProps,
|
|
495
|
+
type TypedFormFieldProps,
|
|
490
496
|
type FormApi,
|
|
491
497
|
type FieldErrors,
|
|
492
498
|
type FieldTouched,
|
|
@@ -740,6 +746,12 @@ export {
|
|
|
740
746
|
type MenuItemVariant,
|
|
741
747
|
type MenuRadioIndicator,
|
|
742
748
|
} from './components/menu';
|
|
749
|
+
export {
|
|
750
|
+
Meter,
|
|
751
|
+
type MeterProps,
|
|
752
|
+
type MeterColor,
|
|
753
|
+
type MeterThreshold,
|
|
754
|
+
} from './components/meter';
|
|
743
755
|
export {
|
|
744
756
|
MessageScroller,
|
|
745
757
|
useMessageScroller,
|
|
@@ -777,6 +789,26 @@ export {
|
|
|
777
789
|
type PopoverAnchorRect,
|
|
778
790
|
type PopoverAnchorControls,
|
|
779
791
|
} from './components/popover';
|
|
792
|
+
export {
|
|
793
|
+
Planner,
|
|
794
|
+
type PlannerProps,
|
|
795
|
+
type PlannerEntry,
|
|
796
|
+
type PlannerCategory,
|
|
797
|
+
type PlannerHeaderProps,
|
|
798
|
+
type PlannerTitleProps,
|
|
799
|
+
type PlannerTodayProps,
|
|
800
|
+
type PlannerNavProps,
|
|
801
|
+
type PlannerActionProps,
|
|
802
|
+
type PlannerGridProps,
|
|
803
|
+
type PlannerDayProps,
|
|
804
|
+
type PlannerDayState,
|
|
805
|
+
type PlannerDayRenderer,
|
|
806
|
+
type PlannerLegendProps,
|
|
807
|
+
type PlannerSummaryProps,
|
|
808
|
+
type PlannerFooterProps,
|
|
809
|
+
type PlannerDetailsProps,
|
|
810
|
+
type PlannerCountedCategory,
|
|
811
|
+
} from './components/planner';
|
|
780
812
|
export {
|
|
781
813
|
Post,
|
|
782
814
|
type PostProps,
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createElement } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface ChartAccessibilityProps<Datum> {
|
|
4
|
+
/** Spoken summary. Defaults to the chart family and number of data entries. */
|
|
5
|
+
accessibilityLabel?: string;
|
|
6
|
+
/** Additional guidance spoken after the chart summary. */
|
|
7
|
+
accessibilityHint?: string;
|
|
8
|
+
/** Overrides the spoken label for one data entry. */
|
|
9
|
+
accessibilityLabelForDatum?: (datum: Datum, index: number) => string;
|
|
10
|
+
/** Makes each data entry actionable without making the SVG geometry focusable. */
|
|
11
|
+
onAccessibilityDatumPress?: (datum: Datum, index: number) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ChartAccessibilityItem<Datum> {
|
|
15
|
+
datum: Datum;
|
|
16
|
+
index: number;
|
|
17
|
+
label: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function spoken(value: unknown): string | null {
|
|
21
|
+
if (value instanceof Date) return value.toLocaleDateString();
|
|
22
|
+
if (typeof value === 'string' || typeof value === 'number') return String(value);
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Pure semantic projection, shared by tests and the native accessibility tree. */
|
|
27
|
+
export function chartAccessibilityModel<Datum>(
|
|
28
|
+
chart: string,
|
|
29
|
+
data: Datum[],
|
|
30
|
+
valueOf: (datum: Datum) => [string, unknown][],
|
|
31
|
+
labelForDatum?: (datum: Datum, index: number) => string
|
|
32
|
+
): { summary: string; items: ChartAccessibilityItem<Datum>[] } {
|
|
33
|
+
return {
|
|
34
|
+
summary: `${chart}, ${data.length} ${data.length === 1 ? 'data entry' : 'data entries'}`,
|
|
35
|
+
items: data.map((datum, index) => ({
|
|
36
|
+
datum,
|
|
37
|
+
index,
|
|
38
|
+
label:
|
|
39
|
+
labelForDatum?.(datum, index) ??
|
|
40
|
+
valueOf(datum)
|
|
41
|
+
.map(([label, value]) => {
|
|
42
|
+
const text = spoken(value);
|
|
43
|
+
return text === null ? null : `${label}, ${text}`;
|
|
44
|
+
})
|
|
45
|
+
.filter((part): part is string => part !== null)
|
|
46
|
+
.join('. '),
|
|
47
|
+
})),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ChartAccessibilityDataProps<Datum>
|
|
52
|
+
extends ChartAccessibilityProps<Datum> {
|
|
53
|
+
chart: string;
|
|
54
|
+
data: Datum[];
|
|
55
|
+
valueOf: (datum: Datum) => [string, unknown][];
|
|
56
|
+
disabled?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A compact semantic sibling to the visual plot. The SVG remains one
|
|
61
|
+
* decorative subtree while screen readers receive one summary and one entry
|
|
62
|
+
* per datum, rather than every path, grid line and marker.
|
|
63
|
+
*/
|
|
64
|
+
export function ChartAccessibilityData<Datum>({
|
|
65
|
+
chart,
|
|
66
|
+
data,
|
|
67
|
+
valueOf,
|
|
68
|
+
disabled = false,
|
|
69
|
+
accessibilityLabel,
|
|
70
|
+
accessibilityHint,
|
|
71
|
+
accessibilityLabelForDatum,
|
|
72
|
+
onAccessibilityDatumPress,
|
|
73
|
+
}: ChartAccessibilityDataProps<Datum>) {
|
|
74
|
+
if (disabled) return null;
|
|
75
|
+
// Keep the pure semantic projection importable by Node tests without asking
|
|
76
|
+
// Node to parse React Native's Flow entry point.
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
78
|
+
const { Pressable, View } = require('react-native') as typeof import('react-native');
|
|
79
|
+
const model = chartAccessibilityModel(chart, data, valueOf, accessibilityLabelForDatum);
|
|
80
|
+
|
|
81
|
+
return createElement(
|
|
82
|
+
View,
|
|
83
|
+
{ style: { position: 'absolute', left: -10_000, width: 1, height: 1 } },
|
|
84
|
+
createElement(View, {
|
|
85
|
+
accessible: true,
|
|
86
|
+
accessibilityRole: 'image',
|
|
87
|
+
accessibilityLabel: accessibilityLabel ?? model.summary,
|
|
88
|
+
accessibilityHint,
|
|
89
|
+
}),
|
|
90
|
+
...model.items.map((item) =>
|
|
91
|
+
createElement(Pressable, {
|
|
92
|
+
key: item.index,
|
|
93
|
+
accessible: true,
|
|
94
|
+
accessibilityRole: onAccessibilityDatumPress ? 'button' : 'text',
|
|
95
|
+
accessibilityLabel: item.label,
|
|
96
|
+
onPress: onAccessibilityDatumPress
|
|
97
|
+
? () => onAccessibilityDatumPress(item.datum, item.index)
|
|
98
|
+
: undefined,
|
|
99
|
+
})
|
|
100
|
+
)
|
|
101
|
+
);
|
|
102
|
+
}
|
package/src/primitives/scrim.tsx
CHANGED
|
@@ -10,17 +10,20 @@
|
|
|
10
10
|
* that never blurs anything installs nothing. When it is asked for and the
|
|
11
11
|
* package is missing, this falls back to a dim rather than failing: a blur you
|
|
12
12
|
* cannot draw is better shown as a darkened screen than as a crash.
|
|
13
|
+
* Reduce Transparency replaces the blur with an opaque, tint-aware surface.
|
|
13
14
|
*
|
|
14
15
|
* It fills its parent and does not intercept touches itself — the overlay layers
|
|
15
16
|
* its own dismiss `Pressable` over it — so it is purely the visual backdrop.
|
|
16
17
|
*/
|
|
17
|
-
import { type ComponentType } from 'react';
|
|
18
|
-
import { StyleSheet, View, type ViewProps } from 'react-native';
|
|
18
|
+
import { useEffect, useState, type ComponentType } from 'react';
|
|
19
|
+
import { AccessibilityInfo, StyleSheet, View, type ViewProps } from 'react-native';
|
|
19
20
|
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
|
|
20
21
|
|
|
22
|
+
type BlurTint = 'light' | 'dark' | 'default' | 'systemMaterial';
|
|
23
|
+
|
|
21
24
|
interface BlurViewProps {
|
|
22
25
|
intensity?: number;
|
|
23
|
-
tint?:
|
|
26
|
+
tint?: BlurTint;
|
|
24
27
|
style?: unknown;
|
|
25
28
|
children?: React.ReactNode;
|
|
26
29
|
}
|
|
@@ -43,16 +46,112 @@ const BlurView: ComponentType<BlurViewProps> | null = (() => {
|
|
|
43
46
|
/** True when a real blur can be drawn — for a caller that wants to know. */
|
|
44
47
|
export const hasBlur = BlurView !== null;
|
|
45
48
|
|
|
49
|
+
type ReduceTransparencySource = {
|
|
50
|
+
isReduceTransparencyEnabled?: () => Promise<boolean>;
|
|
51
|
+
addEventListener?: (
|
|
52
|
+
event: 'reduceTransparencyChanged',
|
|
53
|
+
listener: (enabled: boolean) => void
|
|
54
|
+
) => { remove?: () => void } | undefined;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
function observeReduceTransparency(
|
|
58
|
+
source: ReduceTransparencySource,
|
|
59
|
+
onChange: (enabled: boolean) => void
|
|
60
|
+
) {
|
|
61
|
+
let active = true;
|
|
62
|
+
let changed = false;
|
|
63
|
+
const update = (enabled: boolean) => {
|
|
64
|
+
changed = true;
|
|
65
|
+
if (active) onChange(Boolean(enabled));
|
|
66
|
+
};
|
|
67
|
+
/*
|
|
68
|
+
* A platform that cannot report the preference has no preference to respect,
|
|
69
|
+
* so blur stays available. A platform that has the API and failed to answer
|
|
70
|
+
* might have it switched on, and that is the case worth being careful about.
|
|
71
|
+
*/
|
|
72
|
+
const fallback = (queryable: boolean) => () => {
|
|
73
|
+
if (active && !changed) onChange(queryable);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
let subscription: { remove?: () => void } | undefined;
|
|
77
|
+
try {
|
|
78
|
+
subscription = source.addEventListener?.('reduceTransparencyChanged', update);
|
|
79
|
+
} catch {
|
|
80
|
+
// Some web and test shims expose AccessibilityInfo without every event.
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
if (typeof source.isReduceTransparencyEnabled !== 'function') fallback(false)();
|
|
85
|
+
else {
|
|
86
|
+
void source.isReduceTransparencyEnabled().then((enabled) => {
|
|
87
|
+
// A preference event that arrived while the query was pending is newer.
|
|
88
|
+
if (active && !changed) update(enabled);
|
|
89
|
+
}, fallback(true));
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
fallback(true)();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return () => {
|
|
96
|
+
active = false;
|
|
97
|
+
try {
|
|
98
|
+
subscription?.remove?.();
|
|
99
|
+
} catch {
|
|
100
|
+
// Cleanup stays safe for partial platform shims.
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The last answer the platform gave, shared by every scrim in the process.
|
|
107
|
+
*
|
|
108
|
+
* The query is asynchronous and the preference belongs to the device, not to
|
|
109
|
+
* any one overlay. Without this, each overlay opens not knowing it and draws
|
|
110
|
+
* opaque until the answer arrives — a flash on every dialog for the many people
|
|
111
|
+
* who have the preference off. Remembered here, only the first overlay of a
|
|
112
|
+
* launch can see it.
|
|
113
|
+
*/
|
|
114
|
+
let knownReduceTransparency: boolean | null = null;
|
|
115
|
+
|
|
116
|
+
function useReduceTransparency() {
|
|
117
|
+
// Not knowing is deliberately conservative: draw opaque until the platform
|
|
118
|
+
// says blur is allowed, rather than flashing blur at someone who opted out.
|
|
119
|
+
const [enabled, setEnabled] = useState<boolean | null>(knownReduceTransparency);
|
|
120
|
+
useEffect(
|
|
121
|
+
() =>
|
|
122
|
+
observeReduceTransparency(AccessibilityInfo, (next) => {
|
|
123
|
+
knownReduceTransparency = next;
|
|
124
|
+
setEnabled(next);
|
|
125
|
+
}),
|
|
126
|
+
[]
|
|
127
|
+
);
|
|
128
|
+
return enabled;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
type ScrimMode = 'blur' | 'dim' | 'opaque';
|
|
132
|
+
|
|
133
|
+
function scrimMode(blur: boolean, canBlur: boolean, reduceTransparency: boolean | null): ScrimMode {
|
|
134
|
+
if (!blur) return 'dim';
|
|
135
|
+
if (reduceTransparency !== false) return 'opaque';
|
|
136
|
+
return canBlur ? 'blur' : 'dim';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function opaqueClassName(tint: BlurTint) {
|
|
140
|
+
if (tint === 'light') return 'bg-white';
|
|
141
|
+
if (tint === 'dark') return 'bg-black';
|
|
142
|
+
return 'bg-background';
|
|
143
|
+
}
|
|
144
|
+
|
|
46
145
|
export interface ScrimProps extends Omit<ViewProps, 'children'> {
|
|
47
146
|
/** Frost the backdrop instead of dimming it. Falls back to a dim if it can't. */
|
|
48
147
|
blur?: boolean;
|
|
49
148
|
/** Blur strength, 0–100. */
|
|
50
149
|
intensity?: number;
|
|
51
150
|
/** Which way the blur tints. */
|
|
52
|
-
tint?:
|
|
151
|
+
tint?: BlurTint;
|
|
53
152
|
/**
|
|
54
|
-
* The dim used when not blurring
|
|
55
|
-
*
|
|
153
|
+
* The dim used when not blurring, when blur is unavailable, and over the
|
|
154
|
+
* opaque Reduce Transparency fallback. A popover passes a lighter one than a dialog.
|
|
56
155
|
*/
|
|
57
156
|
dimClassName?: string;
|
|
58
157
|
}
|
|
@@ -65,7 +164,10 @@ export function Scrim({
|
|
|
65
164
|
style,
|
|
66
165
|
...props
|
|
67
166
|
}: ScrimProps) {
|
|
68
|
-
|
|
167
|
+
const reduceTransparency = useReduceTransparency();
|
|
168
|
+
const mode = scrimMode(blur, BlurView !== null, reduceTransparency);
|
|
169
|
+
|
|
170
|
+
if (mode === 'blur' && BlurView) {
|
|
69
171
|
return (
|
|
70
172
|
<Animated.View
|
|
71
173
|
entering={FadeIn.duration(180)}
|
|
@@ -81,6 +183,24 @@ export function Scrim({
|
|
|
81
183
|
);
|
|
82
184
|
}
|
|
83
185
|
|
|
186
|
+
if (mode === 'opaque') {
|
|
187
|
+
return (
|
|
188
|
+
<Animated.View
|
|
189
|
+
entering={FadeIn.duration(180)}
|
|
190
|
+
exiting={FadeOut.duration(150)}
|
|
191
|
+
style={[StyleSheet.absoluteFill, style]}
|
|
192
|
+
{...props}
|
|
193
|
+
>
|
|
194
|
+
<View
|
|
195
|
+
pointerEvents="none"
|
|
196
|
+
style={StyleSheet.absoluteFill}
|
|
197
|
+
className={opaqueClassName(tint)}
|
|
198
|
+
/>
|
|
199
|
+
<View pointerEvents="none" style={StyleSheet.absoluteFill} className={dimClassName} />
|
|
200
|
+
</Animated.View>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
84
204
|
return (
|
|
85
205
|
<Animated.View
|
|
86
206
|
entering={FadeIn.duration(150)}
|