@transferwise/components 46.157.0 → 46.158.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/build/Markup/Markup.js +15 -14
- package/build/Markup/Markup.js.map +1 -1
- package/build/Markup/Markup.mjs +15 -14
- package/build/Markup/Markup.mjs.map +1 -1
- package/build/Popover/Popover.js +4 -0
- package/build/Popover/Popover.js.map +1 -1
- package/build/Popover/Popover.mjs +4 -0
- package/build/Popover/Popover.mjs.map +1 -1
- package/build/common/Panel/Panel.js +13 -4
- package/build/common/Panel/Panel.js.map +1 -1
- package/build/common/Panel/Panel.mjs +14 -5
- package/build/common/Panel/Panel.mjs.map +1 -1
- package/build/common/PropsValues/position.js +29 -0
- package/build/common/PropsValues/position.js.map +1 -1
- package/build/common/PropsValues/position.mjs +28 -1
- package/build/common/PropsValues/position.mjs.map +1 -1
- package/build/main.css +14 -0
- package/build/styles/Typeahead/Typeahead.css +10 -0
- package/build/styles/main.css +14 -0
- package/build/types/Markup/Markup.d.ts.map +1 -1
- package/build/types/Markup/Markup.types.d.ts +16 -5
- package/build/types/Markup/Markup.types.d.ts.map +1 -1
- package/build/types/Markup/index.d.ts +1 -1
- package/build/types/Markup/index.d.ts.map +1 -1
- package/build/types/Popover/Popover.d.ts +1 -1
- package/build/types/Popover/Popover.d.ts.map +1 -1
- package/build/types/common/Panel/Panel.d.ts +6 -6
- package/build/types/common/Panel/Panel.d.ts.map +1 -1
- package/build/types/common/PropsValues/position.d.ts +18 -1
- package/build/types/common/PropsValues/position.d.ts.map +1 -1
- package/build/types/common/ResponsivePanel/ResponsivePanel.d.ts +2 -2
- package/build/types/index.d.ts +1 -1
- package/build/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Markup/Markup.test.tsx +8 -7
- package/src/Markup/Markup.tsx +18 -15
- package/src/Markup/Markup.types.ts +16 -6
- package/src/Markup/_stories/Markup.docs.mdx +60 -0
- package/src/Markup/_stories/Markup.story.tsx +7 -8
- package/src/Markup/index.ts +6 -1
- package/src/Popover/Popover.tsx +9 -2
- package/src/Typeahead/Typeahead.css +10 -0
- package/src/Typeahead/Typeahead.less +6 -0
- package/src/common/Panel/Panel.test.tsx +17 -0
- package/src/common/Panel/Panel.tsx +17 -8
- package/src/common/Panel/_stories/Panel.story.tsx +111 -0
- package/src/common/PropsValues/position.ts +54 -0
- package/src/i18n/en.json +1 -0
- package/src/index.ts +1 -1
- package/src/main.css +14 -0
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
-
import type { LinkProps } from '../Link';
|
|
4
|
-
|
|
5
3
|
export type MarkupNode =
|
|
6
4
|
| { type: 'text'; content: string }
|
|
7
5
|
| { type: 'newline' }
|
|
@@ -20,9 +18,20 @@ export type MarkupNode =
|
|
|
20
18
|
children: MarkupNode[];
|
|
21
19
|
};
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Callback signature for the `onAction` prop. Receives the action name from
|
|
23
|
+
* `<link action="name">` when clicked.
|
|
24
|
+
*/
|
|
25
|
+
export type MarkupActionHandler = (actionId: string) => void;
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Per-prop Markup configuration that host components can accept via a `markupOptions`
|
|
29
|
+
* prop keyed by their text prop names (e.g. `{ title?: MarkupTextOptions; description?: MarkupTextOptions }`).
|
|
30
|
+
*/
|
|
31
|
+
export type MarkupTextOptions = {
|
|
32
|
+
data?: Record<string, string | number>;
|
|
33
|
+
allowLinks?: 'internal' | 'all' | 'none';
|
|
34
|
+
};
|
|
26
35
|
|
|
27
36
|
export interface MarkupProps {
|
|
28
37
|
/**
|
|
@@ -45,9 +54,10 @@ export interface MarkupProps {
|
|
|
45
54
|
* */
|
|
46
55
|
allowLinks?: 'internal' | 'all' | 'none';
|
|
47
56
|
/**
|
|
48
|
-
*
|
|
57
|
+
* Callback invoked when a `<link action="name">` button-mode link is clicked.
|
|
58
|
+
* Receives the action name as argument.
|
|
49
59
|
* */
|
|
50
|
-
|
|
60
|
+
onAction?: MarkupActionHandler;
|
|
51
61
|
/**
|
|
52
62
|
* Wrapper element. Defaults to `div` when the parsed tree contains `<paragraph>` nodes,
|
|
53
63
|
* `span` otherwise. Set explicitly to override auto-detection.
|
|
@@ -4,6 +4,66 @@ import { Meta, Source } from '@storybook/addon-docs/blocks';
|
|
|
4
4
|
|
|
5
5
|
# Developer notes
|
|
6
6
|
|
|
7
|
+
## Integrating Markup in host components
|
|
8
|
+
|
|
9
|
+
To integrate any component with Markup (e.g. `title`, `description`), follow this pattern:
|
|
10
|
+
|
|
11
|
+
1. **Add `onMarkupAction`** — if the component is expected to accept actions, add a single callback prop that the
|
|
12
|
+
component forwards to every internal `<Markup onAction={onMarkupAction}>` instance. The name of this prop must
|
|
13
|
+
remain consistent across components.
|
|
14
|
+
|
|
15
|
+
2. **Add `markupOptions`** — an optional prop keyed by the text prop names, carrying per-prop Markup configuration
|
|
16
|
+
(`data`, `allowLinks`). `MarkupTextOptions` type is exposed by the component.
|
|
17
|
+
|
|
18
|
+
<Source dark language="tsx" code={`
|
|
19
|
+
import Markup, { type MarkupActionHandler, type MarkupTextOptions } from '../Markup';
|
|
20
|
+
|
|
21
|
+
type MyComponentProps = {
|
|
22
|
+
title: ReactNode;
|
|
23
|
+
description?: ReactNode;
|
|
24
|
+
onMarkupAction?: MarkupActionHandler;
|
|
25
|
+
markupOptions?: {
|
|
26
|
+
title?: MarkupTextOptions;
|
|
27
|
+
description?: MarkupTextOptions;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function MyComponent({ title, description, onMarkupAction, markupOptions }: MyComponentProps) {
|
|
32
|
+
return (
|
|
33
|
+
<div>
|
|
34
|
+
<Markup onAction={onMarkupAction} {...markupOptions?.title}>
|
|
35
|
+
{title}
|
|
36
|
+
</Markup>
|
|
37
|
+
|
|
38
|
+
{description && (
|
|
39
|
+
<Markup onAction={onMarkupAction} {...markupOptions?.description}>
|
|
40
|
+
{description}
|
|
41
|
+
</Markup>
|
|
42
|
+
)}
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
`} />
|
|
47
|
+
|
|
48
|
+
Consumer usage:
|
|
49
|
+
|
|
50
|
+
<Source
|
|
51
|
+
dark
|
|
52
|
+
language="tsx"
|
|
53
|
+
code={`
|
|
54
|
+
<MyComponent
|
|
55
|
+
title='Something went wrong. <link action="retry">Try again</link>.'
|
|
56
|
+
description='Your balance is {{amount}}.'
|
|
57
|
+
onMarkupAction={(actionId) => {
|
|
58
|
+
if (actionId === 'retry') refetch();
|
|
59
|
+
}}
|
|
60
|
+
markupOptions={{
|
|
61
|
+
description: { data: { amount: '£100.00' }, allowLinks: 'all' }
|
|
62
|
+
}}
|
|
63
|
+
/>
|
|
64
|
+
`}
|
|
65
|
+
/>
|
|
66
|
+
|
|
7
67
|
## How parsing works
|
|
8
68
|
|
|
9
69
|
The parser runs in two phases: **tokenise** then **parse**.
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
3
|
-
import { action } from 'storybook/actions';
|
|
4
3
|
|
|
5
4
|
import {
|
|
6
5
|
createSandboxStory,
|
|
@@ -9,7 +8,6 @@ import {
|
|
|
9
8
|
import { storySourceWithoutNoise } from '../../../.storybook/helpers';
|
|
10
9
|
|
|
11
10
|
import { Markup } from '../Markup';
|
|
12
|
-
import type { MarkupActionHandler } from '../Markup.types';
|
|
13
11
|
|
|
14
12
|
export default {
|
|
15
13
|
component: Markup,
|
|
@@ -58,8 +56,8 @@ export const WithLinks: Story = {
|
|
|
58
56
|
};
|
|
59
57
|
|
|
60
58
|
/**
|
|
61
|
-
* Button-mode link using the `action` attribute. The handler is provided via the `
|
|
62
|
-
* If
|
|
59
|
+
* Button-mode link using the `action` attribute. The handler is provided via the `onAction` prop.
|
|
60
|
+
* If `onAction` is not provided, the link degrades to plain text.
|
|
63
61
|
* */
|
|
64
62
|
export const WithLinksAsButtons = storySourceWithoutNoise<Story>({
|
|
65
63
|
args: {
|
|
@@ -68,14 +66,15 @@ export const WithLinksAsButtons = storySourceWithoutNoise<Story>({
|
|
|
68
66
|
render: function Render(args) {
|
|
69
67
|
const [count, setCount] = useState(0);
|
|
70
68
|
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
const handleAction = (actionId: string) => {
|
|
70
|
+
if (actionId === 'increment') {
|
|
71
|
+
setCount((prev) => prev + 1);
|
|
72
|
+
}
|
|
74
73
|
};
|
|
75
74
|
|
|
76
75
|
return (
|
|
77
76
|
<>
|
|
78
|
-
<Markup
|
|
77
|
+
<Markup onAction={handleAction}>{args.children}</Markup>
|
|
79
78
|
<output>{count}</output>
|
|
80
79
|
</>
|
|
81
80
|
);
|
package/src/Markup/index.ts
CHANGED
package/src/Popover/Popover.tsx
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { useTheme } from '@wise/components-theming';
|
|
2
1
|
import { clsx } from 'clsx';
|
|
3
2
|
import { useRef, useState, cloneElement, useEffect, isValidElement, useId } from 'react';
|
|
4
3
|
|
|
@@ -9,10 +8,14 @@ import { logActionRequired } from '../utilities';
|
|
|
9
8
|
|
|
10
9
|
/** @deprecated Use `"top" | "bottom"` instead. */
|
|
11
10
|
type PopoverPreferredPlacementDeprecated =
|
|
11
|
+
| `${Position.TOP_LEFT}`
|
|
12
|
+
| `${Position.TOP_RIGHT}`
|
|
12
13
|
| `${Position.LEFT_TOP}`
|
|
13
14
|
| `${Position.RIGHT_TOP}`
|
|
14
15
|
| `${Position.BOTTOM_RIGHT}`
|
|
15
|
-
| `${Position.BOTTOM_LEFT}
|
|
16
|
+
| `${Position.BOTTOM_LEFT}`
|
|
17
|
+
| `${Position.LEFT_BOTTOM}`
|
|
18
|
+
| `${Position.RIGHT_BOTTOM}`;
|
|
16
19
|
|
|
17
20
|
export type PopoverPreferredPlacement =
|
|
18
21
|
| `${Position.TOP}`
|
|
@@ -35,11 +38,15 @@ export interface PopoverProps {
|
|
|
35
38
|
|
|
36
39
|
function resolvePlacement(preferredPlacement: PopoverPreferredPlacement) {
|
|
37
40
|
switch (preferredPlacement) {
|
|
41
|
+
case 'top-left':
|
|
42
|
+
case 'top-right':
|
|
38
43
|
case 'left-top':
|
|
39
44
|
case 'right-top':
|
|
40
45
|
return 'top';
|
|
41
46
|
case 'bottom-left':
|
|
42
47
|
case 'bottom-right':
|
|
48
|
+
case 'left-bottom':
|
|
49
|
+
case 'right-bottom':
|
|
43
50
|
return 'bottom';
|
|
44
51
|
default:
|
|
45
52
|
return preferredPlacement;
|
|
@@ -7,10 +7,20 @@
|
|
|
7
7
|
.typeahead .dropdown-menu {
|
|
8
8
|
max-width: 100%;
|
|
9
9
|
width: 100%;
|
|
10
|
+
margin-top: 8px;
|
|
11
|
+
margin-top: var(--size-8);
|
|
10
12
|
scroll-padding-top: 8px;
|
|
11
13
|
scroll-padding-top: var(--size-8);
|
|
12
14
|
scroll-padding-bottom: 8px;
|
|
13
15
|
scroll-padding-bottom: var(--size-8);
|
|
16
|
+
--ring-outline-offset: calc(-1 * var(--ring-outline-width));
|
|
17
|
+
}
|
|
18
|
+
.typeahead .dropdown-menu:focus {
|
|
19
|
+
outline: none;
|
|
20
|
+
}
|
|
21
|
+
.typeahead .dropdown-menu:focus-visible {
|
|
22
|
+
outline: var(--ring-outline-color) solid var(--ring-outline-width);
|
|
23
|
+
outline-offset: var(--ring-outline-offset);
|
|
14
24
|
}
|
|
15
25
|
.np-theme-personal--forest-green .typeahead .dropdown-menu,
|
|
16
26
|
.np-theme-personal--bright-green .typeahead .dropdown-menu,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
@import (reference) "./../styles/variables/legacy-variables.less";
|
|
2
2
|
@import (reference) "./../styles/less/mixins/_logical-properties.less";
|
|
3
|
+
@import (reference) "./../styles/less/ring.less";
|
|
3
4
|
|
|
4
5
|
@animation-timing-function: ease-in-out;
|
|
5
6
|
@animation-duration: 0.03s;
|
|
@@ -18,8 +19,13 @@
|
|
|
18
19
|
.dropdown-menu {
|
|
19
20
|
max-width: 100%;
|
|
20
21
|
width: 100%;
|
|
22
|
+
margin-top: var(--size-8);
|
|
21
23
|
scroll-padding-top: var(--size-8);
|
|
22
24
|
scroll-padding-bottom: var(--size-8);
|
|
25
|
+
|
|
26
|
+
.focus-ring();
|
|
27
|
+
--ring-outline-offset: calc(-1 * var(--ring-outline-width));
|
|
28
|
+
|
|
23
29
|
.np-theme-personal--forest-green &,
|
|
24
30
|
.np-theme-personal--bright-green &,
|
|
25
31
|
.np-theme-personal--dark & {
|
|
@@ -76,4 +76,21 @@ describe('Panel', () => {
|
|
|
76
76
|
|
|
77
77
|
const getPanel = () => document.querySelector('.np-panel--open');
|
|
78
78
|
const getArrow = () => document.querySelector('.np-panel__arrow');
|
|
79
|
+
|
|
80
|
+
describe('aligned placements', () => {
|
|
81
|
+
it.each<[PanelProps['position'], string]>([
|
|
82
|
+
[Position.TOP_LEFT, 'top-start'],
|
|
83
|
+
[Position.TOP_RIGHT, 'top-end'],
|
|
84
|
+
[Position.RIGHT_TOP, 'right-start'],
|
|
85
|
+
[Position.RIGHT_BOTTOM, 'right-end'],
|
|
86
|
+
[Position.BOTTOM_RIGHT, 'bottom-end'],
|
|
87
|
+
[Position.BOTTOM_LEFT, 'bottom-start'],
|
|
88
|
+
[Position.LEFT_BOTTOM, 'left-end'],
|
|
89
|
+
[Position.LEFT_TOP, 'left-start'],
|
|
90
|
+
])('converts %s to floating-ui placement %s', (position, expectedPlacement) => {
|
|
91
|
+
render(<Panel {...props} position={position} />);
|
|
92
|
+
|
|
93
|
+
expect(getPanel()).toHaveAttribute('data-popper-placement', expectedPlacement);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
79
96
|
});
|
|
@@ -12,15 +12,15 @@ import { clsx } from 'clsx';
|
|
|
12
12
|
import {
|
|
13
13
|
CSSProperties,
|
|
14
14
|
HTMLAttributes,
|
|
15
|
-
MutableRefObject,
|
|
16
15
|
PropsWithChildren,
|
|
16
|
+
RefObject,
|
|
17
17
|
SyntheticEvent,
|
|
18
18
|
forwardRef,
|
|
19
19
|
useContext,
|
|
20
20
|
useRef,
|
|
21
21
|
} from 'react';
|
|
22
22
|
|
|
23
|
-
import { Position,
|
|
23
|
+
import { Position, type FloatingPlacement, positionToPlacement } from '..';
|
|
24
24
|
import Dimmer from '../../Dimmer';
|
|
25
25
|
import { OverlayIdContext } from '../../Provider/overlay/OverlayIdProvider';
|
|
26
26
|
|
|
@@ -29,11 +29,19 @@ const POPOVER_OFFSET = 16;
|
|
|
29
29
|
// By default the flip positioning explores only the opposite alternative. So if left is passed and there's no enough space
|
|
30
30
|
// the right one gets chosen. If there's no space on both sides popover goes back to the initially chosen one left.
|
|
31
31
|
// This mapping forces popover to try the four available positions before going back to the initial chosen one.
|
|
32
|
-
const fallbackPlacements: Record<
|
|
32
|
+
const fallbackPlacements: Record<Placement, Placement[]> = {
|
|
33
33
|
[Position.TOP]: [Position.BOTTOM, Position.RIGHT, Position.LEFT],
|
|
34
34
|
[Position.BOTTOM]: [Position.TOP, Position.RIGHT, Position.LEFT],
|
|
35
35
|
[Position.LEFT]: [Position.RIGHT, Position.TOP, Position.BOTTOM],
|
|
36
36
|
[Position.RIGHT]: [Position.LEFT, Position.TOP, Position.BOTTOM],
|
|
37
|
+
'top-start': ['bottom-start', 'top-end', 'bottom-end'],
|
|
38
|
+
'top-end': ['bottom-end', 'top-start', 'bottom-start'],
|
|
39
|
+
'bottom-start': ['top-start', 'bottom-end', 'top-end'],
|
|
40
|
+
'bottom-end': ['top-end', 'bottom-start', 'top-start'],
|
|
41
|
+
'left-start': ['right-start', 'left-end', 'right-end'],
|
|
42
|
+
'left-end': ['right-end', 'left-start', 'right-start'],
|
|
43
|
+
'right-start': ['left-start', 'right-end', 'left-end'],
|
|
44
|
+
'right-end': ['left-end', 'right-start', 'left-start'],
|
|
37
45
|
};
|
|
38
46
|
|
|
39
47
|
export type PanelProps = PropsWithChildren<{
|
|
@@ -42,8 +50,8 @@ export type PanelProps = PropsWithChildren<{
|
|
|
42
50
|
altAxis?: boolean;
|
|
43
51
|
open?: boolean;
|
|
44
52
|
onClose?: (event: Event | SyntheticEvent) => void;
|
|
45
|
-
position?:
|
|
46
|
-
anchorRef:
|
|
53
|
+
position?: FloatingPlacement;
|
|
54
|
+
anchorRef: RefObject<Element | null>;
|
|
47
55
|
anchorWidth?: boolean;
|
|
48
56
|
considerHeight?: boolean;
|
|
49
57
|
}> &
|
|
@@ -66,6 +74,7 @@ const Panel = forwardRef<HTMLDivElement, PanelProps>(function Panel(
|
|
|
66
74
|
reference,
|
|
67
75
|
) {
|
|
68
76
|
const arrowRef = useRef<HTMLDivElement | null>(null);
|
|
77
|
+
const floatingPlacement = positionToPlacement(position);
|
|
69
78
|
|
|
70
79
|
const middleware = [];
|
|
71
80
|
|
|
@@ -73,10 +82,10 @@ const Panel = forwardRef<HTMLDivElement, PanelProps>(function Panel(
|
|
|
73
82
|
middleware.push(offset(POPOVER_OFFSET));
|
|
74
83
|
}
|
|
75
84
|
|
|
76
|
-
if (flip && fallbackPlacements[
|
|
85
|
+
if (flip && fallbackPlacements[floatingPlacement]) {
|
|
77
86
|
middleware.push(
|
|
78
87
|
flipMiddleware({
|
|
79
|
-
fallbackPlacements: fallbackPlacements[
|
|
88
|
+
fallbackPlacements: fallbackPlacements[floatingPlacement],
|
|
80
89
|
}),
|
|
81
90
|
);
|
|
82
91
|
}
|
|
@@ -104,7 +113,7 @@ const Panel = forwardRef<HTMLDivElement, PanelProps>(function Panel(
|
|
|
104
113
|
}
|
|
105
114
|
|
|
106
115
|
const { refs, floatingStyles, placement, middlewareData } = useFloating({
|
|
107
|
-
placement:
|
|
116
|
+
placement: floatingPlacement,
|
|
108
117
|
middleware,
|
|
109
118
|
elements: {
|
|
110
119
|
reference: anchorRef.current,
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
|
+
import { useRef, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import Button from '../../../Button';
|
|
5
|
+
import { Position } from '../../PropsValues/position';
|
|
6
|
+
import Panel from '../Panel';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
component: Panel,
|
|
10
|
+
title: 'Dialogs/Panel',
|
|
11
|
+
parameters: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: {
|
|
14
|
+
component:
|
|
15
|
+
'Panel is a low-level floating component used by Popover and ResponsivePanel. It supports all floating-ui placements.',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
} satisfies Meta<typeof Panel>;
|
|
20
|
+
|
|
21
|
+
type Story = StoryObj<typeof Panel>;
|
|
22
|
+
|
|
23
|
+
const PanelDemo = ({
|
|
24
|
+
position,
|
|
25
|
+
label,
|
|
26
|
+
}: {
|
|
27
|
+
position: Parameters<typeof Panel>[0]['position'];
|
|
28
|
+
label: string;
|
|
29
|
+
}) => {
|
|
30
|
+
const anchorRef = useRef<HTMLButtonElement>(null);
|
|
31
|
+
const [open, setOpen] = useState(false);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div style={{ display: 'inline-block' }}>
|
|
35
|
+
<Button ref={anchorRef} onClick={() => setOpen((prev) => !prev)}>
|
|
36
|
+
{label}
|
|
37
|
+
</Button>
|
|
38
|
+
<Panel
|
|
39
|
+
open={open}
|
|
40
|
+
position={position}
|
|
41
|
+
anchorRef={anchorRef}
|
|
42
|
+
onClose={() => setOpen(false)}
|
|
43
|
+
aria-label={`Panel positioned ${label}`}
|
|
44
|
+
>
|
|
45
|
+
<div style={{ padding: '16px', maxWidth: '200px' }}>
|
|
46
|
+
Panel content positioned at <strong>{label}</strong>
|
|
47
|
+
</div>
|
|
48
|
+
</Panel>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const CardinalPositions: Story = {
|
|
54
|
+
render: () => (
|
|
55
|
+
<div
|
|
56
|
+
style={{
|
|
57
|
+
display: 'flex',
|
|
58
|
+
flexWrap: 'wrap',
|
|
59
|
+
gap: '48px',
|
|
60
|
+
justifyContent: 'center',
|
|
61
|
+
alignItems: 'center',
|
|
62
|
+
padding: '120px 80px',
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
<PanelDemo position={Position.TOP} label="top" />
|
|
66
|
+
<PanelDemo position={Position.RIGHT} label="right" />
|
|
67
|
+
<PanelDemo position={Position.BOTTOM} label="bottom" />
|
|
68
|
+
<PanelDemo position={Position.LEFT} label="left" />
|
|
69
|
+
</div>
|
|
70
|
+
),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const VerticalAlignedPositions: Story = {
|
|
74
|
+
render: () => (
|
|
75
|
+
<div
|
|
76
|
+
style={{
|
|
77
|
+
display: 'flex',
|
|
78
|
+
flexWrap: 'wrap',
|
|
79
|
+
gap: '48px',
|
|
80
|
+
justifyContent: 'center',
|
|
81
|
+
alignItems: 'center',
|
|
82
|
+
padding: '120px 80px',
|
|
83
|
+
}}
|
|
84
|
+
>
|
|
85
|
+
<PanelDemo position={Position.TOP_LEFT} label="top-left" />
|
|
86
|
+
<PanelDemo position={Position.TOP_RIGHT} label="top-right" />
|
|
87
|
+
<PanelDemo position={Position.BOTTOM_LEFT} label="bottom-left" />
|
|
88
|
+
<PanelDemo position={Position.BOTTOM_RIGHT} label="bottom-right" />
|
|
89
|
+
</div>
|
|
90
|
+
),
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const HorizontalAlignedPositions: Story = {
|
|
94
|
+
render: () => (
|
|
95
|
+
<div
|
|
96
|
+
style={{
|
|
97
|
+
display: 'flex',
|
|
98
|
+
flexWrap: 'wrap',
|
|
99
|
+
gap: '48px',
|
|
100
|
+
justifyContent: 'center',
|
|
101
|
+
alignItems: 'center',
|
|
102
|
+
padding: '120px 80px',
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
<PanelDemo position={Position.LEFT_TOP} label="left-top" />
|
|
106
|
+
<PanelDemo position={Position.LEFT_BOTTOM} label="left-bottom" />
|
|
107
|
+
<PanelDemo position={Position.RIGHT_TOP} label="right-top" />
|
|
108
|
+
<PanelDemo position={Position.RIGHT_BOTTOM} label="right-bottom" />
|
|
109
|
+
</div>
|
|
110
|
+
),
|
|
111
|
+
};
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
+
import type { Placement } from '@floating-ui/react';
|
|
2
|
+
|
|
1
3
|
export enum Position {
|
|
2
4
|
BOTTOM = 'bottom',
|
|
3
5
|
BOTTOM_LEFT = 'bottom-left',
|
|
4
6
|
BOTTOM_RIGHT = 'bottom-right',
|
|
5
7
|
CENTER = 'center',
|
|
6
8
|
LEFT = 'left',
|
|
9
|
+
LEFT_BOTTOM = 'left-bottom',
|
|
7
10
|
LEFT_TOP = 'left-top',
|
|
8
11
|
RIGHT = 'right',
|
|
12
|
+
RIGHT_BOTTOM = 'right-bottom',
|
|
9
13
|
RIGHT_TOP = 'right-top',
|
|
10
14
|
TOP = 'top',
|
|
15
|
+
TOP_LEFT = 'top-left',
|
|
16
|
+
TOP_RIGHT = 'top-right',
|
|
11
17
|
}
|
|
12
18
|
|
|
13
19
|
export type PositionCenter = 'center';
|
|
@@ -15,3 +21,51 @@ export type PositionTop = 'top';
|
|
|
15
21
|
export type PositionRight = 'right';
|
|
16
22
|
export type PositionBottom = 'bottom';
|
|
17
23
|
export type PositionLeft = 'left';
|
|
24
|
+
|
|
25
|
+
export type PositionTopLeft = 'top-left';
|
|
26
|
+
export type PositionTopRight = 'top-right';
|
|
27
|
+
export type PositionBottomLeft = 'bottom-left';
|
|
28
|
+
export type PositionBottomRight = 'bottom-right';
|
|
29
|
+
export type PositionLeftTop = 'left-top';
|
|
30
|
+
export type PositionLeftBottom = 'left-bottom';
|
|
31
|
+
export type PositionRightTop = 'right-top';
|
|
32
|
+
export type PositionRightBottom = 'right-bottom';
|
|
33
|
+
|
|
34
|
+
/** Neptune position values that can be converted to floating-ui Placement */
|
|
35
|
+
export type FloatingPlacement =
|
|
36
|
+
| PositionTop
|
|
37
|
+
| PositionRight
|
|
38
|
+
| PositionBottom
|
|
39
|
+
| PositionLeft
|
|
40
|
+
| PositionTopLeft
|
|
41
|
+
| PositionTopRight
|
|
42
|
+
| PositionRightTop
|
|
43
|
+
| PositionRightBottom
|
|
44
|
+
| PositionBottomLeft
|
|
45
|
+
| PositionBottomRight
|
|
46
|
+
| PositionLeftTop
|
|
47
|
+
| PositionLeftBottom;
|
|
48
|
+
|
|
49
|
+
/** Maps Neptune position values to floating-ui Placement */
|
|
50
|
+
export const positionToPlacement = (position: FloatingPlacement): Placement => {
|
|
51
|
+
switch (position) {
|
|
52
|
+
case 'top-left':
|
|
53
|
+
return 'top-start';
|
|
54
|
+
case 'top-right':
|
|
55
|
+
return 'top-end';
|
|
56
|
+
case 'bottom-left':
|
|
57
|
+
return 'bottom-start';
|
|
58
|
+
case 'bottom-right':
|
|
59
|
+
return 'bottom-end';
|
|
60
|
+
case 'left-top':
|
|
61
|
+
return 'left-start';
|
|
62
|
+
case 'left-bottom':
|
|
63
|
+
return 'left-end';
|
|
64
|
+
case 'right-top':
|
|
65
|
+
return 'right-start';
|
|
66
|
+
case 'right-bottom':
|
|
67
|
+
return 'right-end';
|
|
68
|
+
default:
|
|
69
|
+
return position;
|
|
70
|
+
}
|
|
71
|
+
};
|
package/src/i18n/en.json
CHANGED
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"neptune.Table.loaded": "Table data has loaded",
|
|
51
51
|
"neptune.Table.loading": "Table data is loading",
|
|
52
52
|
"neptune.Table.refreshPage": "Refresh page",
|
|
53
|
+
"neptune.Typeahead.suggestionsLabel": "Suggestions",
|
|
53
54
|
"neptune.Upload.csButtonText": "Upload another file?",
|
|
54
55
|
"neptune.Upload.csFailureText": "Upload failed. Please, try again",
|
|
55
56
|
"neptune.Upload.csSuccessText": "Upload complete!",
|
package/src/index.ts
CHANGED
|
@@ -95,7 +95,7 @@ export type { LegacyListItemProps } from './_deprecated/LegacyListItem';
|
|
|
95
95
|
export type { LinkProps } from './Link';
|
|
96
96
|
export type { LoaderProps } from './Loader';
|
|
97
97
|
export type { MarkdownProps } from './Markdown';
|
|
98
|
-
export type { MarkupActionHandler,
|
|
98
|
+
export type { MarkupActionHandler, MarkupNode, MarkupProps, MarkupTextOptions } from './Markup';
|
|
99
99
|
export type { ModalProps } from './Modal';
|
|
100
100
|
export type { MoneyProps } from './Money';
|
|
101
101
|
export type {
|
package/src/main.css
CHANGED
|
@@ -34202,10 +34202,24 @@ html:not([dir="rtl"]) .np-navigation-option {
|
|
|
34202
34202
|
.typeahead .dropdown-menu {
|
|
34203
34203
|
max-width: 100%;
|
|
34204
34204
|
width: 100%;
|
|
34205
|
+
margin-top: 8px;
|
|
34206
|
+
margin-top: var(--size-8);
|
|
34205
34207
|
scroll-padding-top: 8px;
|
|
34206
34208
|
scroll-padding-top: var(--size-8);
|
|
34207
34209
|
scroll-padding-bottom: 8px;
|
|
34208
34210
|
scroll-padding-bottom: var(--size-8);
|
|
34211
|
+
--ring-outline-offset: calc(-1 * var(--ring-outline-width));
|
|
34212
|
+
}
|
|
34213
|
+
|
|
34214
|
+
.typeahead .dropdown-menu:focus {
|
|
34215
|
+
outline: none;
|
|
34216
|
+
}
|
|
34217
|
+
|
|
34218
|
+
.typeahead .dropdown-menu:focus-visible {
|
|
34219
|
+
outline: #37517e solid 2px;
|
|
34220
|
+
outline: var(--ring-outline-color) solid var(--ring-outline-width);
|
|
34221
|
+
outline-offset: 2px;
|
|
34222
|
+
outline-offset: var(--ring-outline-offset);
|
|
34209
34223
|
}
|
|
34210
34224
|
|
|
34211
34225
|
.np-theme-personal--forest-green .typeahead .dropdown-menu,
|