asma-ui-core 3.88.1 → 3.90.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.
Files changed (31) hide show
  1. package/Readme.md +107 -0
  2. package/dist/asma-ui-core.css +1 -1
  3. package/dist/components/feedback/minimizable-dialog/MinimizableDialog.js +6 -6
  4. package/dist/components/utils/bottom-sheet/StyledBottomSheet.js +137 -0
  5. package/dist/components/utils/bottom-sheet/StyledBottomSheet.module.js +4 -0
  6. package/dist/components/utils/bottom-sheet/formatResultsLabel.js +4 -0
  7. package/dist/components/utils/bottom-sheet/useDragToDismiss.hook.js +40 -0
  8. package/dist/components/utils/popover/PopoverAnatomy.js +46 -0
  9. package/dist/components/utils/popover/PopoverSheet.js +165 -0
  10. package/dist/hooks/useBodyScrollLock.hook.js +13 -0
  11. package/dist/hooks/useDebouncedValue.hook.js +15 -0
  12. package/dist/hooks/useMediaQuery.hook.js +10 -9
  13. package/dist/index.js +184 -178
  14. package/dist/src/components/utils/bottom-sheet/StyledBottomSheet.d.ts +59 -0
  15. package/dist/src/components/utils/bottom-sheet/formatResultsLabel.d.ts +9 -0
  16. package/dist/src/components/utils/bottom-sheet/formatResultsLabel.test.d.ts +1 -0
  17. package/dist/src/components/utils/bottom-sheet/index.d.ts +2 -0
  18. package/dist/src/components/utils/bottom-sheet/story/BottomSheetCases.d.ts +1 -0
  19. package/dist/src/components/utils/bottom-sheet/story/BottomSheetDemo.d.ts +22 -0
  20. package/dist/src/components/utils/bottom-sheet/useDragToDismiss.hook.d.ts +22 -0
  21. package/dist/src/components/utils/popover/PopoverAnatomy.d.ts +36 -0
  22. package/dist/src/components/utils/popover/PopoverSheet.d.ts +86 -0
  23. package/dist/src/components/utils/popover/index.d.ts +1 -0
  24. package/dist/src/components/utils/popover/story/PopoverCases.d.ts +1 -0
  25. package/dist/src/components/utils/popover/story/PopoverGallery.d.ts +1 -0
  26. package/dist/src/components/utils/popover/story/popoverStoryFixtures.d.ts +21 -0
  27. package/dist/src/hooks/useBodyScrollLock.hook.d.ts +7 -0
  28. package/dist/src/hooks/useDebouncedValue.hook.d.ts +2 -0
  29. package/dist/src/hooks/useMediaQuery.hook.d.ts +2 -0
  30. package/dist/src/index.d.ts +1 -0
  31. package/package.json +1 -1
@@ -0,0 +1,22 @@
1
+ import { PointerEvent, RefObject } from 'react';
2
+ interface DragHandleProps {
3
+ onPointerDown: (event: PointerEvent<HTMLElement>) => void;
4
+ onPointerMove: (event: PointerEvent<HTMLElement>) => void;
5
+ onPointerUp: (event: PointerEvent<HTMLElement>) => void;
6
+ onPointerCancel: (event: PointerEvent<HTMLElement>) => void;
7
+ }
8
+ interface DragToDismiss {
9
+ /** Spread on the drag zone — the 48px handle row. */
10
+ dragHandleProps: DragHandleProps;
11
+ /** How far the sheet is currently dragged down, in px. `0` when at rest. */
12
+ dragOffset: number;
13
+ }
14
+ /**
15
+ * Drag the sheet down past 30% of its height to dismiss it; release earlier and it snaps back.
16
+ *
17
+ * Deliberately never the only exit (WCAG 2.5.7): the close button, the scrim and Escape all do the
18
+ * same without a dragging movement. A press that starts on a button in the drag zone is left alone,
19
+ * so the close control keeps working as a plain click.
20
+ */
21
+ export declare const useDragToDismiss: (sheetRef: RefObject<HTMLElement | null>, onDismiss: () => void) => DragToDismiss;
22
+ export {};
@@ -0,0 +1,36 @@
1
+ import { ReactNode } from 'react';
2
+ /**
3
+ * The Figma "_Popover" surface chrome (node `44531:233781`): white, 1px `border/outline`, radius 8
4
+ * (`dialog` token) and the Dialogue-popup elevation — the same surface `StyledDialog` paints.
5
+ *
6
+ * `border-solid` and `box-border` are load-bearing, not noise: this package builds Tailwind with
7
+ * `preflight: false`, so without them `border` computes to `border-style: none` (no border paints at
8
+ * all) and `box-sizing` falls back to `content-box` (a 400px max-width renders 402).
9
+ *
10
+ * Positioning (`z-index`, the floating transform, the top-layer props) deliberately stays with the
11
+ * caller, so the Gallery story can render the same surface statically in a grid.
12
+ */
13
+ export declare const POPOVER_SURFACE_CLASSNAME = "box-border flex flex-col overflow-hidden rounded-lg border border-solid border-delta-300 bg-white text-delta-700 outline-none shadow-[0px_4px_40px_0px_#22213366]";
14
+ export interface PopoverAnatomyProps {
15
+ dataTest: string;
16
+ title?: ReactNode;
17
+ titleId?: string;
18
+ children: ReactNode;
19
+ /** Left slot of the Figma "Reset filter" row — the "View results (3)" affordance. */
20
+ viewResultsAction?: ReactNode;
21
+ /** Right slot of the Figma "Reset filter" row. */
22
+ resetAction?: ReactNode;
23
+ /** The Figma "Actions" row — a single right-aligned outlined button. */
24
+ footerActions?: ReactNode;
25
+ closeLabel: string;
26
+ onClose: () => void;
27
+ }
28
+ /**
29
+ * Everything inside the popover surface: the content row (title + body + close column) and the two
30
+ * optional footer rows. Split out of `PopoverSheet` so the Gallery story can render every
31
+ * Title × Reset filter × Actions combination side by side — a real popover is portalled, anchored
32
+ * and mutually exclusive, so the variants can never be seen together through the live component.
33
+ *
34
+ * Not exported from the package barrel: this is an implementation detail, not public API.
35
+ */
36
+ export declare const PopoverAnatomy: ({ dataTest, title, titleId, children, viewResultsAction, resetAction, footerActions, closeLabel, onClose, }: PopoverAnatomyProps) => JSX.Element;
@@ -0,0 +1,86 @@
1
+ import { MouseEvent, ReactNode } from 'react';
2
+ /** Spread on the trigger element. The ref is handed over separately — `StyledButton` takes `refLink`. */
3
+ export interface PopoverTriggerProps {
4
+ onClick: (event: MouseEvent<HTMLElement>) => void;
5
+ 'aria-expanded': boolean;
6
+ 'aria-controls': string | undefined;
7
+ 'aria-haspopup': 'dialog' | undefined;
8
+ 'aria-describedby': string | undefined;
9
+ }
10
+ export interface PopoverTrigger {
11
+ /** Pass to `ref` on a native element, or to `refLink` on `StyledButton`. */
12
+ ref: (node: HTMLElement | null) => void;
13
+ triggerProps: PopoverTriggerProps;
14
+ isOpen: boolean;
15
+ }
16
+ /** Content API handed to every slot that can be a render-prop. */
17
+ export interface PopoverContentApi {
18
+ close: () => void;
19
+ }
20
+ /** A slot that may need to close the surface — e.g. "View results (3)" or an Actions button. */
21
+ export type PopoverSlot = ReactNode | ((api: PopoverContentApi) => ReactNode);
22
+ /** @figmaNode wXrXt5uKNNzV2DnQCgyYZH#44531-233781 (Design-System · "_Popover") */
23
+ export interface PopoverSheetProps {
24
+ /** @figmaProp none — test hook */
25
+ dataTest: string;
26
+ /** @figmaProp none — behavioral: the consumer owns the trigger element, this wires its aria + click */
27
+ renderTrigger: (trigger: PopoverTrigger) => ReactNode;
28
+ /**
29
+ * Chosen by content, not by breakpoint.
30
+ * @figmaProp none — `info` is a read-only container referenced by `aria-describedby`;
31
+ * `action` is a `role="dialog"` with a focus trap.
32
+ */
33
+ variant?: 'info' | 'action';
34
+ /** @figmaProp Title = ReactNode→true | undefined→false */
35
+ title?: ReactNode;
36
+ /** @figmaProp Slot — the body. The render-prop form receives `close()` for the Actions pattern. */
37
+ children: PopoverSlot;
38
+ /**
39
+ * Left slot of the Figma "Reset filter" row — the "View results (3)" affordance, which reports
40
+ * the live match count and closes the surface (the filter itself already applied).
41
+ * @figmaProp Reset filter = ReactNode→true | undefined→false — the 4px footer row, left slot
42
+ */
43
+ viewResultsAction?: PopoverSlot;
44
+ /** @figmaProp Reset filter = ReactNode→true | undefined→false — the 4px footer row, right slot */
45
+ resetAction?: PopoverSlot;
46
+ /** @figmaProp Actions = ReactNode→true | undefined→false — the 12px footer row, right-aligned */
47
+ footerActions?: PopoverSlot;
48
+ /**
49
+ * Result total for the standard "Vis resultater (N)" control, rendered when no `viewResultsAction`
50
+ * is given (see `formatResultsLabel`). On mobile the sheet also announces it from its own live
51
+ * region, because the list behind is inert. `null` = total unavailable.
52
+ * @figmaProp none — behavioral
53
+ */
54
+ resultCount?: number | null;
55
+ /** @figmaProp none — a11y: accessible name of the close control */
56
+ closeLabel?: string;
57
+ /** @figmaProp none — a11y: required when `variant='action'` and no `title` is given, so the dialog has a name */
58
+ ariaLabel?: string;
59
+ /** @figmaProp none — behavioral */
60
+ onOpenChange?: (isOpen: boolean) => void;
61
+ /** @figmaProp none — style escape hatch, applied to the surface */
62
+ className?: string;
63
+ }
64
+ /**
65
+ * ASMA Design System **Popover** (ASMA-8183) — an anchored overlay with the DS surface, an optional
66
+ * title, a required close control and two optional footer rows.
67
+ *
68
+ * Two variants, chosen by content:
69
+ * - **info** — read-only. Plain container referenced by `aria-describedby`, focus moves to the
70
+ * container on open, `Tab` leaves and closes.
71
+ * - **action** — interactive. `role="dialog"`, focus trapped, focus moves to the first control.
72
+ * Covers both the Filter pattern (changes apply immediately, `Nullstill` clears) and the Actions
73
+ * pattern (a list of buttons; activation closes via the `close()` render-prop argument).
74
+ *
75
+ * There is deliberately **no `role="menu"` and no arrow-key navigation** — everything inside is
76
+ * navigated with `Tab`, and menu semantics cannot coexist with the dialog model used by Filter.
77
+ * There is also **no arrow/anchor pointer**: the surface is edge-aligned at an 8px offset and
78
+ * proximity carries the relationship.
79
+ *
80
+ * Below 744px `action` renders as a {@link StyledBottomSheet} (ASMA-8184) — same props, same content;
81
+ * `info` stays anchored at every size, because for an explanation the link to its term is the point.
82
+ *
83
+ * Separate from {@link StyledPopover}, which stays as the MUI-parity positioning primitive its eight
84
+ * internal consumers still depend on; those migrate here gradually.
85
+ */
86
+ export declare const PopoverSheet: ({ dataTest, renderTrigger, variant, title, children, viewResultsAction, resetAction, footerActions, resultCount, closeLabel, ariaLabel, onOpenChange, className, }: PopoverSheetProps) => JSX.Element;
@@ -1 +1,2 @@
1
1
  export * from './StyledPopover';
2
+ export * from './PopoverSheet';
@@ -0,0 +1 @@
1
+ export declare const PopoverCases: () => JSX.Element;
@@ -0,0 +1 @@
1
+ export declare const PopoverGallery: () => JSX.Element;
@@ -0,0 +1,21 @@
1
+ /** Sample data lifted from the Figma "Popover" frames so the stories read like the design. */
2
+ export declare const KARTLEGGING: string[];
3
+ export declare const EGENRAPPORTERING: string[];
4
+ export interface FilterState {
5
+ kartlegging: string[];
6
+ egenrapportering: string | undefined;
7
+ }
8
+ export declare const EMPTY_FILTER: FilterState;
9
+ export declare const noop: () => void;
10
+ /** How many results the mock list reports for a given number of active criteria. */
11
+ export declare const matchCountFor: (activeCount: number) => number;
12
+ export declare const countActive: (filter: FilterState) => number;
13
+ export declare const toggleInList: (list: string[], option: string) => string[];
14
+ /** The Figma "Chips select" group — a heading plus a wrapping row of checkbox/radio chips. */
15
+ export declare const ChipGroup: ({ heading, options, isSelected, onToggle, type, }: {
16
+ heading: string;
17
+ options: string[];
18
+ isSelected: (option: string) => boolean;
19
+ onToggle: (option: string) => void;
20
+ type: "checkbox" | "radio";
21
+ }) => JSX.Element;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Locks page scroll while `active` and puts the page back exactly where it was afterwards.
3
+ *
4
+ * `overflow: hidden` on both roots stops wheel and touch scrolling; the saved offset is restored on
5
+ * release because some mobile engines jump to the top when overflow is toggled back.
6
+ */
7
+ export declare const useBodyScrollLock: (active: boolean) => void;
@@ -0,0 +1,2 @@
1
+ /** `value`, but only once it has stopped changing for `delayMs`. The first value is returned at once. */
2
+ export declare const useDebouncedValue: <T>(value: T, delayMs: number) => T;
@@ -1,2 +1,4 @@
1
1
  export declare function makeMediaQueryStore(mediaQuery: string): () => boolean;
2
2
  export declare const useMobileMediaQuery: () => boolean;
3
+ /** A phone in landscape: too short for a sheet capped at 90vh to leave a usable body. */
4
+ export declare const useShortViewportMediaQuery: () => boolean;
@@ -44,6 +44,7 @@ export * from './components/navigation/listbox';
44
44
  export * from './components/utils/accordion';
45
45
  export * from './table';
46
46
  export * from './components/utils/popover';
47
+ export * from './components/utils/bottom-sheet';
47
48
  export * from './components/utils/filter-menu';
48
49
  export * from './components/utils/copy-wrapper';
49
50
  export * from './components/utils/virtual-list';
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "3.88.1",
6
+ "version": "3.90.0",
7
7
  "type": "module",
8
8
  "sideEffects": [
9
9
  "**/*.css",