@juspay/blend-design-system 0.0.37-beta.7 → 0.0.37-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CodeEditor/types.d.ts +22 -2
- package/dist/components/CodeEditorV2/CodeEditorV2.d.ts +1 -1
- package/dist/components/CodeEditorV2/codeEditorV2.types.d.ts +23 -2
- package/dist/components/MenuV2/MenuV2Content.d.ts +1 -0
- package/dist/components/MenuV2/menuV2.types.d.ts +5 -0
- package/dist/components/MenuV2/menuV2.utils.d.ts +10 -2
- package/dist/components/MultiSelect/MultiSelect.d.ts +1 -1
- package/dist/components/MultiSelect/MultiSelectMenu.d.ts +1 -1
- package/dist/components/MultiSelect/types.d.ts +2 -0
- package/dist/components/MultiSelectV2/MultiSelectV2.d.ts +1 -1
- package/dist/components/MultiSelectV2/MultiSelectV2Menu.d.ts +1 -1
- package/dist/components/MultiSelectV2/multiSelectV2.types.d.ts +2 -0
- package/dist/components/SingleSelect/SingleSelect.d.ts +1 -1
- package/dist/components/SingleSelect/SingleSelectMenu.d.ts +2 -1
- package/dist/components/SingleSelect/types.d.ts +1 -0
- package/dist/components/SingleSelectV2/SingleSelectV2.d.ts +1 -1
- package/dist/components/SingleSelectV2/SingleSelectV2Menu.d.ts +1 -1
- package/dist/components/SingleSelectV2/singleSelectV2.tokens.types.d.ts +8 -0
- package/dist/components/SingleSelectV2/singleSelectV2.types.d.ts +2 -0
- package/dist/components/Snackbar/Snackbar.d.ts +1 -1
- package/dist/components/Snackbar/snackbar.tokens.d.ts +1 -0
- package/dist/components/Snackbar/types.d.ts +2 -0
- package/dist/main.js +18043 -17940
- package/dist/{node-C_DmV86X.js → node-CovJEwot.js} +16 -0
- package/dist/node.js +1 -1
- package/dist/{tokens.js → token-engine.js} +1 -1
- package/lib/components/CodeEditor/MonacoEditorWrapper.tsx +6 -2
- package/lib/components/CodeEditor/types.ts +52 -2
- package/lib/components/CodeEditorV2/MonacoEditor/MonacoEditorWrapper.tsx +6 -2
- package/lib/components/CodeEditorV2/codeEditorV2.types.ts +44 -5
- package/lib/components/DateRangePicker/CalendarGrid.tsx +3 -0
- package/lib/components/DateRangePicker/DateRangePicker.tsx +5 -1
- package/lib/components/MenuV2/MenuV2.tsx +9 -2
- package/lib/components/MenuV2/MenuV2Content.tsx +6 -0
- package/lib/components/MenuV2/MenuV2SubMenu.tsx +16 -2
- package/lib/components/MenuV2/menuV2.types.ts +11 -0
- package/lib/components/MenuV2/menuV2.utils.ts +45 -3
- package/lib/components/MultiSelect/MultiSelect.tsx +2 -0
- package/lib/components/MultiSelect/MultiSelectMenu.tsx +8 -0
- package/lib/components/MultiSelect/types.ts +2 -0
- package/lib/components/MultiSelectV2/MultiSelectV2.tsx +2 -0
- package/lib/components/MultiSelectV2/MultiSelectV2Menu.tsx +8 -0
- package/lib/components/MultiSelectV2/multiSelectV2.types.ts +2 -0
- package/lib/components/SingleSelect/SingleSelect.tsx +2 -0
- package/lib/components/SingleSelect/SingleSelectMenu.tsx +171 -138
- package/lib/components/SingleSelect/types.ts +1 -0
- package/lib/components/SingleSelectV2/SingleSelectV2.tsx +2 -0
- package/lib/components/SingleSelectV2/SingleSelectV2Menu.tsx +84 -58
- package/lib/components/SingleSelectV2/SingleSelectV2Search.tsx +1 -0
- package/lib/components/SingleSelectV2/singleSelectV2.dark.tokens.ts +8 -0
- package/lib/components/SingleSelectV2/singleSelectV2.light.tokens.ts +8 -0
- package/lib/components/SingleSelectV2/singleSelectV2.tokens.types.ts +8 -0
- package/lib/components/SingleSelectV2/singleSelectV2.types.ts +2 -0
- package/lib/components/Snackbar/Snackbar.tsx +18 -8
- package/lib/components/Snackbar/snackbar.tokens.ts +6 -0
- package/lib/components/Snackbar/types.ts +2 -0
- package/lib/components/StatCard/StatCard.tsx +29 -0
- package/package.json +5 -4
- package/dist/tokens.d.ts +0 -2
|
@@ -7082,6 +7082,14 @@ const f = (r) => {
|
|
|
7082
7082
|
color: r.colors.gray[600]
|
|
7083
7083
|
},
|
|
7084
7084
|
iconColor: r.colors.gray[400]
|
|
7085
|
+
},
|
|
7086
|
+
footer: {
|
|
7087
|
+
paddingTop: r.unit[16],
|
|
7088
|
+
paddingRight: r.unit[16],
|
|
7089
|
+
paddingBottom: r.unit[16],
|
|
7090
|
+
paddingLeft: r.unit[16],
|
|
7091
|
+
backgroundColor: r.colors.gray[0],
|
|
7092
|
+
borderTop: `${r.border.width[1]} solid ${r.colors.gray[200]}`
|
|
7085
7093
|
}
|
|
7086
7094
|
},
|
|
7087
7095
|
mobilePanel: {
|
|
@@ -7428,6 +7436,14 @@ const f = (r) => {
|
|
|
7428
7436
|
color: r.colors.gray[200]
|
|
7429
7437
|
},
|
|
7430
7438
|
iconColor: r.colors.gray[500]
|
|
7439
|
+
},
|
|
7440
|
+
footer: {
|
|
7441
|
+
paddingTop: r.unit[16],
|
|
7442
|
+
paddingRight: r.unit[16],
|
|
7443
|
+
paddingBottom: r.unit[16],
|
|
7444
|
+
paddingLeft: r.unit[16],
|
|
7445
|
+
backgroundColor: r.colors.gray[800],
|
|
7446
|
+
borderTop: `${r.border.width[1]} solid ${r.colors.gray[700]}`
|
|
7431
7447
|
}
|
|
7432
7448
|
},
|
|
7433
7449
|
mobilePanel: {
|
package/dist/node.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as a, T as o, y as t, x as T, w as n, v as g, z as k, u as V, t as r, s as i, r as l, q as c, g as b, p as d, o as u, n as S, m as p, a as h, l as m, j as v, i as x, k as A, h as C, f as M, e as B, d as E, c as N, b as P } from "./node-
|
|
1
|
+
import { F as a, T as o, y as t, x as T, w as n, v as g, z as k, u as V, t as r, s as i, r as l, q as c, g as b, p as d, o as u, n as S, m as p, a as h, l as m, j as v, i as x, k as A, h as C, f as M, e as B, d as E, c as N, b as P } from "./node-CovJEwot.js";
|
|
2
2
|
export {
|
|
3
3
|
a as FOUNDATION_THEME,
|
|
4
4
|
o as Theme,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as u, T as d, g as k, a as E, b as V, c as g, d as v, e as p, f as S, h, i as R, j as A, k as y, l as C, m as N, n as I, o as P, p as m, q as _, r as B, s as L, t as w, u as M, v as j, w as U, x as D, y as G, z as H } from "./node-
|
|
1
|
+
import { F as u, T as d, g as k, a as E, b as V, c as g, d as v, e as p, f as S, h, i as R, j as A, k as y, l as C, m as N, n as I, o as P, p as m, q as _, r as B, s as L, t as w, u as M, v as j, w as U, x as D, y as G, z as H } from "./node-CovJEwot.js";
|
|
2
2
|
import { BRANCH_COLLECTION as ie, BRANCH_ID_PATTERN as ae, PRESETS as ce, PRESET_BLEND_DEFAULT as fe, PRESET_GREEN as le, PRESET_JUSPAY as Te, PRESET_ORANGE as ue, PRESET_PURPLE as de, RADIUS_PRESETS as Oe, SNAPSHOT_SUBCOLLECTION as ke, STORAGE_KEYS as Ee, TEAM_ROLE_PERMISSIONS as Ve, VERSION_PATTERN as ge, VERSION_SUBCOLLECTION as ve, analyzeContrast as pe, canUserPerformAction as Se, diffBrandConfigs as he, extractOverridePaths as Re, generateBranchId as Ae, generateColorScale as ye, getContrastRatio as Ce, getContrastRatioHex as Ne, getDefaultOnboardingState as Ie, getDefaultPreferences as Pe, getPreset as me, hexToRgb as _e, incrementVersion as Be, isValidHexColor as Le, listPresets as we, meetsWCAG as Me, parseBranchId as je, relativeLuminance as Ue, resolveWithInheritance as De, snapshotsPath as Ge, suggestForeground as He, validateAgainstLocks as xe, validateBranchId as Ke, validateBrandConfig as Fe, validatePaletteContrast as Je, validateVersion as $e, versionsPath as Ye } from "./tokens-server.js";
|
|
3
3
|
function O(e) {
|
|
4
4
|
const o = typeof structuredClone == "function" ? structuredClone(u) : JSON.parse(JSON.stringify(u));
|
|
@@ -5,9 +5,13 @@ import Block from '../Primitives/Block/Block'
|
|
|
5
5
|
import type { CodeBlockTokenType } from '../CodeBlock/codeBlock.token'
|
|
6
6
|
import './monaco-editor.css'
|
|
7
7
|
|
|
8
|
+
// Monaco registers only `javascript`/`typescript` (VS Code's
|
|
9
|
+
// `javascriptreact`/`typescriptreact` IDs are not registered, so they would
|
|
10
|
+
// fall back to plaintext with no highlighting). Monaco's JS/TS tokenizers
|
|
11
|
+
// handle JSX/TSX syntax fine, so map the ergonomic aliases onto them.
|
|
8
12
|
const LANGUAGE_MAP: Record<string, string> = {
|
|
9
|
-
jsx: '
|
|
10
|
-
tsx: '
|
|
13
|
+
jsx: 'javascript',
|
|
14
|
+
tsx: 'typescript',
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
const mapLanguage = (value: string) => LANGUAGE_MAP[value] ?? value
|
|
@@ -1,11 +1,61 @@
|
|
|
1
1
|
import type { ReactNode } from 'react'
|
|
2
|
-
import type { SupportedLanguage } from '../CodeBlock/types'
|
|
3
2
|
|
|
4
3
|
export enum CodeEditorVariant {
|
|
5
4
|
DEFAULT = 'default',
|
|
6
5
|
NO_GUTTER = 'no-gutter',
|
|
7
6
|
}
|
|
8
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Common Monaco language IDs, provided purely for IDE autocomplete on the
|
|
10
|
+
* `language` prop — NOT a runtime constraint. Every entry is verified against
|
|
11
|
+
* the language IDs monaco-editor actually registers (Monaco does not export a
|
|
12
|
+
* static union of its built-in IDs; they are only enumerable at runtime via
|
|
13
|
+
* `monaco.languages.getLanguages()`). Note Monaco's plain-text ID is
|
|
14
|
+
* `plaintext` and shell scripts are `shell` (there is no `bash` ID). Monaco
|
|
15
|
+
* also has no distinct `jsx`/`tsx` IDs — those still work through the open
|
|
16
|
+
* string arm of {@link CodeEditorLanguage}: the editor maps them onto the
|
|
17
|
+
* `javascript`/`typescript` tokenizers.
|
|
18
|
+
*/
|
|
19
|
+
export type KnownCodeEditorLanguage =
|
|
20
|
+
| 'javascript'
|
|
21
|
+
| 'typescript'
|
|
22
|
+
| 'json'
|
|
23
|
+
| 'css'
|
|
24
|
+
| 'scss'
|
|
25
|
+
| 'less'
|
|
26
|
+
| 'html'
|
|
27
|
+
| 'xml'
|
|
28
|
+
| 'markdown'
|
|
29
|
+
| 'yaml'
|
|
30
|
+
| 'ini'
|
|
31
|
+
| 'graphql'
|
|
32
|
+
| 'sql'
|
|
33
|
+
| 'python'
|
|
34
|
+
| 'rust'
|
|
35
|
+
| 'go'
|
|
36
|
+
| 'java'
|
|
37
|
+
| 'kotlin'
|
|
38
|
+
| 'swift'
|
|
39
|
+
| 'c'
|
|
40
|
+
| 'cpp'
|
|
41
|
+
| 'csharp'
|
|
42
|
+
| 'php'
|
|
43
|
+
| 'ruby'
|
|
44
|
+
| 'shell'
|
|
45
|
+
| 'powershell'
|
|
46
|
+
| 'dockerfile'
|
|
47
|
+
| 'plaintext'
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Language accepted by {@link CodeEditorProps.language}: autocomplete for
|
|
51
|
+
* {@link KnownCodeEditorLanguage}, while `(string & {})` keeps the prop open
|
|
52
|
+
* to every other Monaco language ID (the component forwards to Monaco, whose
|
|
53
|
+
* own `language` option is a plain `string`). Decoupled from `CodeBlock`'s
|
|
54
|
+
* `SupportedLanguage`, which is scoped to CodeBlock's hand-rolled tokenizer
|
|
55
|
+
* rather than Monaco.
|
|
56
|
+
*/
|
|
57
|
+
export type CodeEditorLanguage = KnownCodeEditorLanguage | (string & {})
|
|
58
|
+
|
|
9
59
|
export type CodeEditorProps = {
|
|
10
60
|
value: string
|
|
11
61
|
onChange?: (value: string) => void
|
|
@@ -30,7 +80,7 @@ export type CodeEditorProps = {
|
|
|
30
80
|
*/
|
|
31
81
|
showLeftIcon?: boolean
|
|
32
82
|
showCopyButton?: boolean
|
|
33
|
-
language?:
|
|
83
|
+
language?: CodeEditorLanguage
|
|
34
84
|
placeholder?: string
|
|
35
85
|
readOnly?: boolean
|
|
36
86
|
disabled?: boolean
|
|
@@ -25,9 +25,13 @@ import { CSSObject } from 'styled-components'
|
|
|
25
25
|
// Language mapping
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
27
27
|
|
|
28
|
+
// Monaco registers only `javascript`/`typescript` (VS Code's
|
|
29
|
+
// `javascriptreact`/`typescriptreact` IDs are not registered, so they would
|
|
30
|
+
// fall back to plaintext with no highlighting). Monaco's JS/TS tokenizers
|
|
31
|
+
// handle JSX/TSX syntax fine, so map the ergonomic aliases onto them.
|
|
28
32
|
const LANGUAGE_MAP: Record<string, string> = {
|
|
29
|
-
jsx: '
|
|
30
|
-
tsx: '
|
|
33
|
+
jsx: 'javascript',
|
|
34
|
+
tsx: 'typescript',
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
const mapLanguage = (lang: string) => LANGUAGE_MAP[lang] ?? lang
|
|
@@ -21,19 +21,58 @@ export type CodeEditorV2DiffLine = {
|
|
|
21
21
|
type: CodeEditorV2DiffLineType
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Common Monaco language IDs, provided purely for IDE autocomplete on the
|
|
26
|
+
* `language` prop — NOT a runtime constraint. Every entry is verified against
|
|
27
|
+
* the language IDs monaco-editor actually registers (Monaco does not export a
|
|
28
|
+
* static union of its built-in IDs; they are only enumerable at runtime via
|
|
29
|
+
* `monaco.languages.getLanguages()`). Note Monaco's plain-text ID is
|
|
30
|
+
* `plaintext` and shell scripts are `shell` (there is no `bash` ID). Monaco
|
|
31
|
+
* also has no distinct `jsx`/`tsx` IDs — those still work through the open
|
|
32
|
+
* string arm of {@link CodeEditorV2Language}: the editor maps them onto the
|
|
33
|
+
* `javascript`/`typescript` tokenizers.
|
|
34
|
+
*/
|
|
35
|
+
export type KnownCodeEditorV2Language =
|
|
25
36
|
| 'javascript'
|
|
26
37
|
| 'typescript'
|
|
27
|
-
| 'jsx'
|
|
28
|
-
| 'tsx'
|
|
29
38
|
| 'json'
|
|
30
39
|
| 'css'
|
|
40
|
+
| 'scss'
|
|
41
|
+
| 'less'
|
|
31
42
|
| 'html'
|
|
43
|
+
| 'xml'
|
|
32
44
|
| 'markdown'
|
|
33
45
|
| 'yaml'
|
|
46
|
+
| 'ini'
|
|
47
|
+
| 'graphql'
|
|
48
|
+
| 'sql'
|
|
34
49
|
| 'python'
|
|
35
50
|
| 'rust'
|
|
36
|
-
| '
|
|
51
|
+
| 'go'
|
|
52
|
+
| 'java'
|
|
53
|
+
| 'kotlin'
|
|
54
|
+
| 'swift'
|
|
55
|
+
| 'c'
|
|
56
|
+
| 'cpp'
|
|
57
|
+
| 'csharp'
|
|
58
|
+
| 'php'
|
|
59
|
+
| 'ruby'
|
|
60
|
+
| 'shell'
|
|
61
|
+
| 'powershell'
|
|
62
|
+
| 'dockerfile'
|
|
63
|
+
| 'plaintext'
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Language accepted by {@link CodeEditorV2Props.language}: autocomplete for
|
|
67
|
+
* {@link KnownCodeEditorV2Language}, while `(string & {})` keeps the prop open
|
|
68
|
+
* to every other Monaco language ID (matching the internal
|
|
69
|
+
* `MonacoEditorWrapper`'s `language: string` — Monaco itself types the option
|
|
70
|
+
* as a plain `string`).
|
|
71
|
+
*/
|
|
72
|
+
export type CodeEditorV2Language = KnownCodeEditorV2Language | (string & {})
|
|
73
|
+
|
|
74
|
+
/** @deprecated Use {@link CodeEditorV2Language} instead. */
|
|
75
|
+
export type CodeEditorV2SupportedLanguage = CodeEditorV2Language
|
|
37
76
|
|
|
38
77
|
export type CodeEditorV2Dimensions = {
|
|
39
78
|
width?: CSSObject['width']
|
|
@@ -56,7 +95,7 @@ export type CodeEditorV2Props = {
|
|
|
56
95
|
rightSlot?: ReactNode
|
|
57
96
|
showCopyButton?: boolean
|
|
58
97
|
}
|
|
59
|
-
language?:
|
|
98
|
+
language?: CodeEditorV2Language
|
|
60
99
|
placeholder?: string
|
|
61
100
|
readOnly?: boolean
|
|
62
101
|
disabled?: boolean
|
|
@@ -129,6 +129,7 @@ const CalendarSkeleton = ({
|
|
|
129
129
|
<Block
|
|
130
130
|
style={{
|
|
131
131
|
maxHeight: CONTAINER_HEIGHT,
|
|
132
|
+
height: '100%',
|
|
132
133
|
padding: FOUNDATION_THEME.unit[16],
|
|
133
134
|
}}
|
|
134
135
|
>
|
|
@@ -918,6 +919,7 @@ const CalendarGrid = forwardRef<HTMLDivElement, CalendarGridProps>(
|
|
|
918
919
|
<MotionBlock
|
|
919
920
|
style={{
|
|
920
921
|
maxHeight: CONTAINER_HEIGHT,
|
|
922
|
+
height: '100%',
|
|
921
923
|
overflowY: 'auto',
|
|
922
924
|
overflow: 'auto',
|
|
923
925
|
position: 'relative',
|
|
@@ -985,6 +987,7 @@ const CalendarGrid = forwardRef<HTMLDivElement, CalendarGridProps>(
|
|
|
985
987
|
}}
|
|
986
988
|
style={{
|
|
987
989
|
maxHeight: CONTAINER_HEIGHT,
|
|
990
|
+
height: '100%',
|
|
988
991
|
overflowY: 'auto',
|
|
989
992
|
overflow: 'auto',
|
|
990
993
|
position: 'relative',
|
|
@@ -292,7 +292,7 @@ const CalendarSection: React.FC<
|
|
|
292
292
|
isSingleDatePicker,
|
|
293
293
|
maxYearOffset,
|
|
294
294
|
}) => (
|
|
295
|
-
<Block>
|
|
295
|
+
<Block flexGrow={1} minHeight={0} overflow="auto">
|
|
296
296
|
<CalendarGrid
|
|
297
297
|
selectedRange={selectedRange}
|
|
298
298
|
onDateSelect={onDateSelect}
|
|
@@ -1228,6 +1228,10 @@ const DateRangePicker = forwardRef<HTMLDivElement, DateRangePickerProps>(
|
|
|
1228
1228
|
style={{
|
|
1229
1229
|
...calendarToken.calendar,
|
|
1230
1230
|
}}
|
|
1231
|
+
maxHeight="var(--radix-popper-available-height)"
|
|
1232
|
+
display="flex"
|
|
1233
|
+
flexDirection="column"
|
|
1234
|
+
overflow="hidden"
|
|
1231
1235
|
>
|
|
1232
1236
|
{showDateInput && (
|
|
1233
1237
|
<DateInputsSection
|
|
@@ -23,6 +23,8 @@ const MenuV2 = React.forwardRef<HTMLDivElement, MenuV2Props>(
|
|
|
23
23
|
dimensions = {} as MenuV2Dimensions,
|
|
24
24
|
enableSearch = false,
|
|
25
25
|
searchPlaceholder = 'Search',
|
|
26
|
+
searchSortFn,
|
|
27
|
+
onEnter,
|
|
26
28
|
enableVirtualScrolling = false,
|
|
27
29
|
virtualScrolling,
|
|
28
30
|
open: controlledOpen,
|
|
@@ -61,10 +63,14 @@ const MenuV2 = React.forwardRef<HTMLDivElement, MenuV2Props>(
|
|
|
61
63
|
)
|
|
62
64
|
|
|
63
65
|
const filteredItems = useMemo(
|
|
64
|
-
() => filterMenuV2Groups(items, searchText),
|
|
65
|
-
[items, searchText]
|
|
66
|
+
() => filterMenuV2Groups(items, searchText, searchSortFn),
|
|
67
|
+
[items, searchText, searchSortFn]
|
|
66
68
|
)
|
|
67
69
|
|
|
70
|
+
const handleSearchEnter = useCallback(() => {
|
|
71
|
+
onEnter?.(searchText, filteredItems)
|
|
72
|
+
}, [onEnter, searchText, filteredItems])
|
|
73
|
+
|
|
68
74
|
const handleInteractOutside = useCallback((e: unknown) => {
|
|
69
75
|
const event = e as {
|
|
70
76
|
target: HTMLElement | null
|
|
@@ -104,6 +110,7 @@ const MenuV2 = React.forwardRef<HTMLDivElement, MenuV2Props>(
|
|
|
104
110
|
searchPlaceholder={searchPlaceholder}
|
|
105
111
|
searchText={searchText}
|
|
106
112
|
onSearchTextChange={setSearchText}
|
|
113
|
+
onEnter={handleSearchEnter}
|
|
107
114
|
maxHeight={
|
|
108
115
|
dimensions.maxHeight as CSSObject['maxHeight']
|
|
109
116
|
}
|
|
@@ -104,6 +104,7 @@ export type MenuV2ContentProps = {
|
|
|
104
104
|
searchPlaceholder: string
|
|
105
105
|
searchText: string
|
|
106
106
|
onSearchTextChange: (value: string) => void
|
|
107
|
+
onEnter?: () => void
|
|
107
108
|
maxHeight?: CSSObject['maxHeight']
|
|
108
109
|
minHeight?: CSSObject['minHeight']
|
|
109
110
|
minWidth?: CSSObject['minWidth']
|
|
@@ -130,6 +131,7 @@ const MenuV2Content = React.forwardRef<HTMLDivElement, MenuV2ContentProps>(
|
|
|
130
131
|
searchPlaceholder,
|
|
131
132
|
searchText,
|
|
132
133
|
onSearchTextChange,
|
|
134
|
+
onEnter,
|
|
133
135
|
maxHeight,
|
|
134
136
|
minHeight,
|
|
135
137
|
minWidth: minWidthProp,
|
|
@@ -250,6 +252,10 @@ const MenuV2Content = React.forwardRef<HTMLDivElement, MenuV2ContentProps>(
|
|
|
250
252
|
onChange={(e) => onSearchTextChange(e.target.value)}
|
|
251
253
|
onKeyDown={(e) => {
|
|
252
254
|
e.stopPropagation()
|
|
255
|
+
if (e.key === 'Enter') {
|
|
256
|
+
e.preventDefault()
|
|
257
|
+
onEnter?.()
|
|
258
|
+
}
|
|
253
259
|
}}
|
|
254
260
|
aria-label={
|
|
255
261
|
searchPlaceholder
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
getMenuItemDescriptionColor,
|
|
15
15
|
filterMenuV2Item,
|
|
16
16
|
getItemSlots,
|
|
17
|
+
defaultSearchSortFn,
|
|
17
18
|
} from './menuV2.utils'
|
|
18
19
|
import { menuV2SubmenuContentAnimations } from './menuV2.animations'
|
|
19
20
|
import { useResponsiveTokens } from '../../hooks/useResponsiveTokens'
|
|
@@ -64,12 +65,15 @@ const MenuV2SubMenu = ({ item, index, maxHeight }: MenuV2SubMenuProps) => {
|
|
|
64
65
|
|
|
65
66
|
const lower = searchText.toLowerCase()
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
const matched = item.subMenu.reduce<MenuV2ItemType[]>((acc, sub) => {
|
|
68
69
|
const result = filterMenuV2Item(sub, lower)
|
|
69
70
|
if (result) acc.push(result)
|
|
70
71
|
return acc
|
|
71
72
|
}, [])
|
|
72
|
-
|
|
73
|
+
|
|
74
|
+
const sortFn = item.subMenuSearchSortFn ?? defaultSearchSortFn
|
|
75
|
+
return sortFn(matched, searchText)
|
|
76
|
+
}, [item.subMenu, searchText, item.subMenuSearchSortFn])
|
|
73
77
|
|
|
74
78
|
const {
|
|
75
79
|
bgDefault,
|
|
@@ -262,6 +266,16 @@ const MenuV2SubMenu = ({ item, index, maxHeight }: MenuV2SubMenuProps) => {
|
|
|
262
266
|
}
|
|
263
267
|
value={searchText}
|
|
264
268
|
onChange={(e) => setSearchText(e.target.value)}
|
|
269
|
+
onKeyDown={(e) => {
|
|
270
|
+
e.stopPropagation()
|
|
271
|
+
if (e.key === 'Enter') {
|
|
272
|
+
e.preventDefault()
|
|
273
|
+
item.onSubMenuSearchEnter?.(
|
|
274
|
+
searchText,
|
|
275
|
+
filteredSubMenuItems
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
}}
|
|
265
279
|
aria-label="Search submenu"
|
|
266
280
|
/>
|
|
267
281
|
</Block>
|
|
@@ -38,6 +38,10 @@ export type MenuV2ItemLabel = {
|
|
|
38
38
|
text: string
|
|
39
39
|
leftSlot?: React.ReactElement
|
|
40
40
|
}
|
|
41
|
+
export type MenuV2SearchSortFn = (
|
|
42
|
+
items: MenuV2ItemType[],
|
|
43
|
+
searchText: string
|
|
44
|
+
) => MenuV2ItemType[]
|
|
41
45
|
export type MenuV2ItemType = {
|
|
42
46
|
id?: string
|
|
43
47
|
label: MenuV2ItemLabel
|
|
@@ -49,6 +53,11 @@ export type MenuV2ItemType = {
|
|
|
49
53
|
subMenu?: MenuV2ItemType[]
|
|
50
54
|
enableSubMenuSearch?: boolean
|
|
51
55
|
subMenuSearchPlaceholder?: string
|
|
56
|
+
subMenuSearchSortFn?: MenuV2SearchSortFn
|
|
57
|
+
onSubMenuSearchEnter?: (
|
|
58
|
+
searchText: string,
|
|
59
|
+
filteredResults: MenuV2ItemType[]
|
|
60
|
+
) => void
|
|
52
61
|
tooltip?: string | ReactNode
|
|
53
62
|
tooltipProps?: MenuV2ItemTooltipProps
|
|
54
63
|
}
|
|
@@ -82,6 +91,8 @@ export type MenuV2Props = {
|
|
|
82
91
|
dimensions?: MenuV2Dimensions
|
|
83
92
|
enableSearch?: boolean
|
|
84
93
|
searchPlaceholder?: string
|
|
94
|
+
searchSortFn?: MenuV2SearchSortFn
|
|
95
|
+
onEnter?: (searchText: string, filteredGroups: MenuV2GroupType[]) => void
|
|
85
96
|
enableVirtualScrolling?: boolean
|
|
86
97
|
virtualScrolling?: MenuV2VirtualScrollingConfig
|
|
87
98
|
open?: boolean
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { ReactNode } from 'react'
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
MenuV2GroupType,
|
|
4
|
+
MenuV2ItemType,
|
|
5
|
+
MenuV2SearchSortFn,
|
|
6
|
+
} from './menuV2.types'
|
|
3
7
|
import type { MenuV2TokensType } from './menuV2.tokens'
|
|
4
8
|
|
|
5
9
|
import { MenuV2ItemActionType, MenuV2ItemVariant } from './menuV2.types'
|
|
@@ -11,6 +15,43 @@ export const getItemSlots = (item: MenuV2ItemType): [ReactNode?] => {
|
|
|
11
15
|
return [item.label.leftSlot]
|
|
12
16
|
}
|
|
13
17
|
|
|
18
|
+
export enum MenuV2MatchRank {
|
|
19
|
+
EXACT = 0,
|
|
20
|
+
PREFIX = 1,
|
|
21
|
+
SUBSTRING = 2,
|
|
22
|
+
NONE = 3,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const getFieldMatchRank = (
|
|
26
|
+
fieldValue: string | undefined,
|
|
27
|
+
lower: string
|
|
28
|
+
): MenuV2MatchRank => {
|
|
29
|
+
if (!fieldValue) return MenuV2MatchRank.NONE
|
|
30
|
+
const value = fieldValue.toLowerCase()
|
|
31
|
+
if (value === lower) return MenuV2MatchRank.EXACT
|
|
32
|
+
if (value.startsWith(lower)) return MenuV2MatchRank.PREFIX
|
|
33
|
+
if (value.includes(lower)) return MenuV2MatchRank.SUBSTRING
|
|
34
|
+
return MenuV2MatchRank.NONE
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const getItemMatchRank = (
|
|
38
|
+
item: MenuV2ItemType,
|
|
39
|
+
lower: string
|
|
40
|
+
): MenuV2MatchRank => {
|
|
41
|
+
return Math.min(
|
|
42
|
+
getFieldMatchRank(item.label.text, lower),
|
|
43
|
+
getFieldMatchRank(item.subLabel, lower)
|
|
44
|
+
) as MenuV2MatchRank
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const defaultSearchSortFn: MenuV2SearchSortFn = (items, searchText) => {
|
|
48
|
+
if (!searchText.trim()) return items
|
|
49
|
+
const lower = searchText.toLowerCase()
|
|
50
|
+
return [...items].sort(
|
|
51
|
+
(a, b) => getItemMatchRank(a, lower) - getItemMatchRank(b, lower)
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
14
55
|
export const filterMenuV2Item = (
|
|
15
56
|
item: MenuV2ItemType,
|
|
16
57
|
lower: string
|
|
@@ -140,7 +181,8 @@ export const flattenMenuV2Groups = (
|
|
|
140
181
|
|
|
141
182
|
export const filterMenuV2Groups = (
|
|
142
183
|
groups: MenuV2GroupType[],
|
|
143
|
-
searchText: string
|
|
184
|
+
searchText: string,
|
|
185
|
+
searchSortFn: MenuV2SearchSortFn = defaultSearchSortFn
|
|
144
186
|
): MenuV2GroupType[] => {
|
|
145
187
|
if (!searchText) return groups
|
|
146
188
|
const lower = searchText.toLowerCase()
|
|
@@ -150,7 +192,7 @@ export const filterMenuV2Groups = (
|
|
|
150
192
|
.map((item) => filterMenuV2Item(item, lower))
|
|
151
193
|
.filter(Boolean) as MenuV2ItemType[]
|
|
152
194
|
if (filteredItems.length === 0) return null
|
|
153
|
-
return { ...group, items: filteredItems }
|
|
195
|
+
return { ...group, items: searchSortFn(filteredItems, searchText) }
|
|
154
196
|
})
|
|
155
197
|
.filter(Boolean) as MenuV2GroupType[]
|
|
156
198
|
}
|
|
@@ -101,6 +101,7 @@ const MultiSelect = ({
|
|
|
101
101
|
onClearAllClick,
|
|
102
102
|
onOpenChange,
|
|
103
103
|
multiSelectGroupPosition,
|
|
104
|
+
menuFooter,
|
|
104
105
|
...rest
|
|
105
106
|
}: MultiSelectProps) => {
|
|
106
107
|
const { onFocus, onBlur, ...buttonRest } = rest
|
|
@@ -356,6 +357,7 @@ const MultiSelect = ({
|
|
|
356
357
|
hasMore={hasMore}
|
|
357
358
|
loadingComponent={loadingComponent}
|
|
358
359
|
menuId={menuId}
|
|
360
|
+
menuFooter={menuFooter}
|
|
359
361
|
trigger={
|
|
360
362
|
customTrigger || (
|
|
361
363
|
<PrimitiveButton
|
|
@@ -86,6 +86,10 @@ const FixedActionButtons = styled(Block)(() => ({
|
|
|
86
86
|
flexShrink: 0,
|
|
87
87
|
}))
|
|
88
88
|
|
|
89
|
+
const MenuFooter = styled(Block)(() => ({
|
|
90
|
+
flexShrink: 0,
|
|
91
|
+
}))
|
|
92
|
+
|
|
89
93
|
type FlattenedMultiSelectItem = VirtualListItem & {
|
|
90
94
|
type: 'item' | 'label' | 'separator'
|
|
91
95
|
item?: MultiSelectMenuItemType
|
|
@@ -171,6 +175,7 @@ const MultiSelectMenu = ({
|
|
|
171
175
|
allowCustomValue = false,
|
|
172
176
|
customValueLabel = 'Specify',
|
|
173
177
|
menuId,
|
|
178
|
+
menuFooter,
|
|
174
179
|
}: MultiSelectMenuProps) => {
|
|
175
180
|
const multiSelectTokens =
|
|
176
181
|
useResponsiveTokens<MultiSelectTokensType>('MULTI_SELECT')
|
|
@@ -892,6 +897,9 @@ const MultiSelectMenu = ({
|
|
|
892
897
|
)}
|
|
893
898
|
</FixedActionButtons>
|
|
894
899
|
)}
|
|
900
|
+
{menuFooter && (
|
|
901
|
+
<MenuFooter>{menuFooter}</MenuFooter>
|
|
902
|
+
)}
|
|
895
903
|
</>
|
|
896
904
|
)}
|
|
897
905
|
</Content>
|
|
@@ -157,6 +157,7 @@ export type MultiSelectProps = {
|
|
|
157
157
|
showClearButton?: boolean
|
|
158
158
|
onClearAllClick?: () => void
|
|
159
159
|
multiSelectGroupPosition?: 'center' | 'left' | 'right'
|
|
160
|
+
menuFooter?: React.ReactNode
|
|
160
161
|
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'slot' | 'onChange'>
|
|
161
162
|
|
|
162
163
|
// Multi Select Menu Dropdpown
|
|
@@ -225,4 +226,5 @@ export type MultiSelectMenuProps = {
|
|
|
225
226
|
customValueLabel?: string
|
|
226
227
|
menuId?: string
|
|
227
228
|
collisionBoundary?: Element | null | Array<Element | null>
|
|
229
|
+
menuFooter?: React.ReactNode
|
|
228
230
|
}
|
|
@@ -91,6 +91,7 @@ const MultiSelectV2 = ({
|
|
|
91
91
|
onClearAllClick,
|
|
92
92
|
onOpenChange,
|
|
93
93
|
multiSelectGroupPosition,
|
|
94
|
+
menuFooter,
|
|
94
95
|
...rest
|
|
95
96
|
}: MultiSelectV2Props) => {
|
|
96
97
|
const { disabled, name, ...buttonRest } = rest as {
|
|
@@ -366,6 +367,7 @@ const MultiSelectV2 = ({
|
|
|
366
367
|
}
|
|
367
368
|
allowCustomValue={allowCustomValue}
|
|
368
369
|
customValueLabel={customValueLabel}
|
|
370
|
+
menuFooter={menuFooter}
|
|
369
371
|
/>
|
|
370
372
|
|
|
371
373
|
{shouldShowClearButton && (
|
|
@@ -69,6 +69,10 @@ const ScrollableContent = styled(Block)(() => ({
|
|
|
69
69
|
},
|
|
70
70
|
}))
|
|
71
71
|
|
|
72
|
+
const MenuFooter = styled(Block)`
|
|
73
|
+
flex-shrink: 0;
|
|
74
|
+
`
|
|
75
|
+
|
|
72
76
|
const MultiSelectV2Menu = ({
|
|
73
77
|
items,
|
|
74
78
|
selected,
|
|
@@ -104,6 +108,7 @@ const MultiSelectV2Menu = ({
|
|
|
104
108
|
allowCustomValue = false,
|
|
105
109
|
customValueLabel = 'Specify',
|
|
106
110
|
menuId,
|
|
111
|
+
menuFooter,
|
|
107
112
|
}: MultiSelectV2MenuProps) => {
|
|
108
113
|
const multiSelectTokens =
|
|
109
114
|
useResponsiveTokens<MultiSelectV2TokensType>('MULTI_SELECT_V2')
|
|
@@ -467,6 +472,9 @@ const MultiSelectV2Menu = ({
|
|
|
467
472
|
onClose={() => handleOpenChange(false)}
|
|
468
473
|
/>
|
|
469
474
|
)}
|
|
475
|
+
{menuFooter && (
|
|
476
|
+
<MenuFooter>{menuFooter}</MenuFooter>
|
|
477
|
+
)}
|
|
470
478
|
</>
|
|
471
479
|
)}
|
|
472
480
|
</Content>
|
|
@@ -97,6 +97,7 @@ export type MultiSelectV2MenuProps = {
|
|
|
97
97
|
allowCustomValue?: boolean
|
|
98
98
|
customValueLabel?: string
|
|
99
99
|
menuId?: string
|
|
100
|
+
menuFooter?: ReactNode
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
export type MultiSelectV2MenuRootProps = SelectV2MenuRootPropsBase & {
|
|
@@ -170,4 +171,5 @@ export type MultiSelectV2Props = Omit<
|
|
|
170
171
|
showClearButton?: boolean
|
|
171
172
|
onClearAllClick?: () => void
|
|
172
173
|
multiSelectGroupPosition?: 'center' | 'left' | 'right'
|
|
174
|
+
menuFooter?: ReactNode
|
|
173
175
|
}
|
|
@@ -104,6 +104,7 @@ const SingleSelect = ({
|
|
|
104
104
|
customValueLabel = 'Specify',
|
|
105
105
|
singleSelectGroupPosition,
|
|
106
106
|
allowDeselect = false,
|
|
107
|
+
menuFooter,
|
|
107
108
|
...rest
|
|
108
109
|
}: SingleSelectProps) => {
|
|
109
110
|
const { onFocus, onBlur, ...buttonRest } = rest
|
|
@@ -298,6 +299,7 @@ const SingleSelect = ({
|
|
|
298
299
|
allowCustomValue={allowCustomValue}
|
|
299
300
|
customValueLabel={customValueLabel}
|
|
300
301
|
menuId={menuId}
|
|
302
|
+
menuFooter={menuFooter}
|
|
301
303
|
trigger={
|
|
302
304
|
customTrigger || (
|
|
303
305
|
<PrimitiveButton
|