@economic/taco 14.1.0-EC-79752-breadcrumbs.1 → 14.1.0-EC-86083-poc-array-return.3
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/charts/components/Pie/util.cjs +2 -1
- package/dist/charts/components/Pie/util.cjs.map +1 -1
- package/dist/charts/components/Pie/util.js +2 -1
- package/dist/charts/components/Pie/util.js.map +1 -1
- package/dist/components/Combobox/useCombobox.cjs +2 -2
- package/dist/components/Combobox/useCombobox.cjs.map +1 -1
- package/dist/components/Combobox/useCombobox.js +2 -2
- package/dist/components/Combobox/useCombobox.js.map +1 -1
- package/dist/components/Listbox/useListbox.cjs +2 -2
- package/dist/components/Listbox/useListbox.cjs.map +1 -1
- package/dist/components/Listbox/useListbox.js +2 -2
- package/dist/components/Listbox/useListbox.js.map +1 -1
- package/dist/components/Listbox/useMultiListbox.cjs +2 -2
- package/dist/components/Listbox/useMultiListbox.cjs.map +1 -1
- package/dist/components/Listbox/useMultiListbox.js +2 -2
- package/dist/components/Listbox/useMultiListbox.js.map +1 -1
- package/dist/components/Provider/Localization.cjs +0 -3
- package/dist/components/Provider/Localization.cjs.map +1 -1
- package/dist/components/Provider/Localization.d.ts +1 -7
- package/dist/components/Provider/Localization.js +0 -3
- package/dist/components/Provider/Localization.js.map +1 -1
- package/dist/components/Select/useSelect.cjs +2 -2
- package/dist/components/Select/useSelect.cjs.map +1 -1
- package/dist/components/Select/useSelect.js +2 -2
- package/dist/components/Select/useSelect.js.map +1 -1
- package/dist/components/Select2/components/Edit.cjs +2 -1
- package/dist/components/Select2/components/Edit.cjs.map +1 -1
- package/dist/components/Select2/components/Edit.js +2 -1
- package/dist/components/Select2/components/Edit.js.map +1 -1
- package/dist/components/Table3/Table3.cjs.map +1 -1
- package/dist/components/Table3/Table3.d.ts +1 -1
- package/dist/components/Table3/Table3.js.map +1 -1
- package/dist/components/Table3/components/Row/Row.cjs +5 -3
- package/dist/components/Table3/components/Row/Row.cjs.map +1 -1
- package/dist/components/Table3/components/Row/Row.js +5 -3
- package/dist/components/Table3/components/Row/Row.js.map +1 -1
- package/dist/components/Table3/features/useEditingState.cjs +2 -2
- package/dist/components/Table3/features/useEditingState.cjs.map +1 -1
- package/dist/components/Table3/features/useEditingState.js +2 -2
- package/dist/components/Table3/features/useEditingState.js.map +1 -1
- package/dist/components/Table3/types.d.ts +2 -1
- package/dist/components/Table3/useTable3.cjs +46 -38
- package/dist/components/Table3/useTable3.cjs.map +1 -1
- package/dist/components/Table3/useTable3.js +46 -38
- package/dist/components/Table3/useTable3.js.map +1 -1
- package/dist/components/Tabs/Tabs.cjs +2 -1
- package/dist/components/Tabs/Tabs.cjs.map +1 -1
- package/dist/components/Tabs/Tabs.js +2 -1
- package/dist/components/Tabs/Tabs.js.map +1 -1
- package/dist/components/Toast/Toaster.cjs +3 -3
- package/dist/components/Toast/Toaster.cjs.map +1 -1
- package/dist/components/Toast/Toaster.js +3 -3
- package/dist/components/Toast/Toaster.js.map +1 -1
- package/dist/components/actions/Link/Link.cjs.map +1 -1
- package/dist/components/actions/Link/Link.d.ts +2 -1
- package/dist/components/actions/Link/Link.js.map +1 -1
- package/dist/components/index.d.ts +0 -1
- package/dist/hooks/useId.cjs +4 -3
- package/dist/hooks/useId.cjs.map +1 -1
- package/dist/hooks/useId.d.ts +1 -1
- package/dist/hooks/useId.js +4 -3
- package/dist/hooks/useId.js.map +1 -1
- package/dist/index.cjs +0 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/primitives/Table/Core/components/Columns/Cell/BuiltIns/GroupedCell.cjs +4 -3
- package/dist/primitives/Table/Core/components/Columns/Cell/BuiltIns/GroupedCell.cjs.map +1 -1
- package/dist/primitives/Table/Core/components/Columns/Cell/BuiltIns/GroupedCell.js +2 -1
- package/dist/primitives/Table/Core/components/Columns/Cell/BuiltIns/GroupedCell.js.map +1 -1
- package/dist/primitives/Table/Core/components/Columns/Internal/Actions.cjs +42 -17
- package/dist/primitives/Table/Core/components/Columns/Internal/Actions.cjs.map +1 -1
- package/dist/primitives/Table/Core/components/Columns/Internal/Actions.d.ts +2 -2
- package/dist/primitives/Table/Core/components/Columns/Internal/Actions.js +42 -17
- package/dist/primitives/Table/Core/components/Columns/Internal/Actions.js.map +1 -1
- package/dist/primitives/Table/types.cjs.map +1 -1
- package/dist/primitives/Table/types.d.ts +49 -3
- package/dist/primitives/Table/types.js.map +1 -1
- package/dist/primitives/Table/useTableManager/features/useTableRowActions.cjs +2 -1
- package/dist/primitives/Table/useTableManager/features/useTableRowActions.cjs.map +1 -1
- package/dist/primitives/Table/useTableManager/features/useTableRowActions.d.ts +3 -3
- package/dist/primitives/Table/useTableManager/features/useTableRowActions.js +2 -1
- package/dist/primitives/Table/useTableManager/features/useTableRowActions.js.map +1 -1
- package/dist/primitives/Table/useTableManager/features/useTableRowGroups.cjs.map +1 -1
- package/dist/primitives/Table/useTableManager/features/useTableRowGroups.d.ts +3 -3
- package/dist/primitives/Table/useTableManager/features/useTableRowGroups.js.map +1 -1
- package/dist/primitives/Table/useTableManager/util/presets.cjs +2 -1
- package/dist/primitives/Table/useTableManager/util/presets.cjs.map +1 -1
- package/dist/primitives/Table/useTableManager/util/presets.js +2 -1
- package/dist/primitives/Table/useTableManager/util/presets.js.map +1 -1
- package/dist/primitives/Table/useTableManager/util/rowActions.cjs +7 -0
- package/dist/primitives/Table/useTableManager/util/rowActions.cjs.map +1 -0
- package/dist/primitives/Table/useTableManager/util/rowActions.d.ts +1 -0
- package/dist/primitives/Table/useTableManager/util/rowActions.js +7 -0
- package/dist/primitives/Table/useTableManager/util/rowActions.js.map +1 -0
- package/dist/primitives/Table/useTableManager/util/setup.cjs +2 -1
- package/dist/primitives/Table/useTableManager/util/setup.cjs.map +1 -1
- package/dist/primitives/Table/useTableManager/util/setup.js +2 -1
- package/dist/primitives/Table/useTableManager/util/setup.js.map +1 -1
- package/dist/taco.css +0 -4
- package/dist/utils/generateId.cjs +12 -0
- package/dist/utils/generateId.cjs.map +1 -0
- package/dist/utils/generateId.d.ts +1 -0
- package/dist/utils/generateId.js +12 -0
- package/dist/utils/generateId.js.map +1 -0
- package/dist/utils/storyutils.d.ts +0 -1
- package/package.json +6 -8
- package/dist/components/Breadcrumbs/Breadcrumbs.cjs +0 -25
- package/dist/components/Breadcrumbs/Breadcrumbs.cjs.map +0 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +0 -26
- package/dist/components/Breadcrumbs/Breadcrumbs.js +0 -25
- package/dist/components/Breadcrumbs/Breadcrumbs.js.map +0 -1
|
@@ -31,7 +31,8 @@ function usePieChart(children, options) {
|
|
|
31
31
|
const pieProps = {
|
|
32
32
|
activeIndex,
|
|
33
33
|
activeShape: (props) => /* @__PURE__ */ React.createElement(ActiveShape, { ...props }),
|
|
34
|
-
|
|
34
|
+
// dataKey falls back to the segment label, which can repeat — the index keeps keys unique
|
|
35
|
+
children: shapes.map((shape, index) => /* @__PURE__ */ React.createElement(RechartsPrimitive__namespace.Cell, { key: `${shape.dataKey}-${index}`, color: shape.color, fill: shape.fill })),
|
|
35
36
|
// data props
|
|
36
37
|
data: items.filter((item) => !item.isHidden),
|
|
37
38
|
dataKey: "value",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.cjs","sources":["../../../../src/charts/components/Pie/util.tsx"],"sourcesContent":["import React from 'react';\nimport * as RechartsPrimitive from 'recharts';\nimport type { PieSectorDataItem } from 'recharts/types/polar/Pie';\n\nimport type { useChartOptions } from '../useChartData';\nimport { useChart } from '../useChartData';\n\nconst PIE_ACTIVE_WIDTH = 10;\nconst PIE_ACTIVE_WIDTH_OFFSET = 2;\n\nexport function usePieChart<TData extends object>(children: React.ReactNode, options?: useChartOptions) {\n const { items, shapes, activeIndex, setActiveIndex, globalFormatter, toggleItem } = useChart<TData>(children, options);\n const [radius, _setRadius] = React.useState<number>(0);\n\n function setRadius(width: number) {\n _setRadius((width - 2 * (PIE_ACTIVE_WIDTH + PIE_ACTIVE_WIDTH_OFFSET)) / 2);\n }\n\n const pieProps = {\n activeIndex,\n activeShape: (props: PieSectorDataItem) => <ActiveShape {...props} />,\n children: shapes.map(shape => <RechartsPrimitive.Cell key={shape.dataKey} color={shape.color} fill={shape.fill}
|
|
1
|
+
{"version":3,"file":"util.cjs","sources":["../../../../src/charts/components/Pie/util.tsx"],"sourcesContent":["import React from 'react';\nimport * as RechartsPrimitive from 'recharts';\nimport type { PieSectorDataItem } from 'recharts/types/polar/Pie';\n\nimport type { useChartOptions } from '../useChartData';\nimport { useChart } from '../useChartData';\n\nconst PIE_ACTIVE_WIDTH = 10;\nconst PIE_ACTIVE_WIDTH_OFFSET = 2;\n\nexport function usePieChart<TData extends object>(children: React.ReactNode, options?: useChartOptions) {\n const { items, shapes, activeIndex, setActiveIndex, globalFormatter, toggleItem } = useChart<TData>(children, options);\n const [radius, _setRadius] = React.useState<number>(0);\n\n function setRadius(width: number) {\n _setRadius((width - 2 * (PIE_ACTIVE_WIDTH + PIE_ACTIVE_WIDTH_OFFSET)) / 2);\n }\n\n const pieProps = {\n activeIndex,\n activeShape: (props: PieSectorDataItem) => <ActiveShape {...props} />,\n // dataKey falls back to the segment label, which can repeat — the index keeps keys unique\n children: shapes.map((shape, index) => (\n <RechartsPrimitive.Cell key={`${shape.dataKey}-${index}`} color={shape.color} fill={shape.fill} />\n )),\n // data props\n data: items.filter(item => !item.isHidden),\n dataKey: 'value',\n nameKey: 'label',\n // style props\n isAnimationActive: false,\n startAngle: 90,\n endAngle: -270,\n stroke: '0',\n labelLine: false,\n outerRadius: radius,\n };\n\n return { pieProps, radius, setRadius, items, activeIndex, setActiveIndex, globalFormatter, toggleItem, shapes };\n}\n\nconst PIE_CHART_HOVER = 10;\nconst PIE_CHART_HOVER_OFFSET = 2;\n\nfunction ActiveShape(props: PieSectorDataItem) {\n const { outerRadius = 0, ...attributes } = props;\n\n return (\n <>\n <RechartsPrimitive.Sector {...attributes} outerRadius={outerRadius} />\n <RechartsPrimitive.Sector\n {...attributes}\n innerRadius={outerRadius + PIE_CHART_HOVER_OFFSET}\n outerRadius={outerRadius + PIE_CHART_HOVER}\n opacity={0.4}\n />\n </>\n );\n}\n"],"names":["useChart","RechartsPrimitive"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAOA,MAAM,mBAAmB;AACzB,MAAM,0BAA0B;AAEzB,SAAS,YAAkC,UAA2B,SAA2B;AACpG,QAAM,EAAE,OAAO,QAAQ,aAAa,gBAAgB,iBAAiB,eAAeA,aAAAA,SAAgB,UAAU,OAAO;AACrH,QAAM,CAAC,QAAQ,UAAU,IAAI,MAAM,SAAiB,CAAC;AAErD,WAAS,UAAU,OAAe;AAC9B,gBAAY,QAAQ,KAAK,mBAAmB,4BAA4B,CAAC;AAAA,EAC7E;AAEA,QAAM,WAAW;AAAA,IACb;AAAA,IACA,aAAa,CAAC,UAA6B,sBAAA,cAAC,aAAA,EAAa,GAAG,OAAO;AAAA;AAAA,IAEnE,UAAU,OAAO,IAAI,CAAC,OAAO,UACzB,sBAAA,cAACC,6BAAkB,MAAlB,EAAuB,KAAK,GAAG,MAAM,OAAO,IAAI,KAAK,IAAI,OAAO,MAAM,OAAO,MAAM,MAAM,KAAA,CAAM,CACnG;AAAA;AAAA,IAED,MAAM,MAAM,OAAO,CAAA,SAAQ,CAAC,KAAK,QAAQ;AAAA,IACzC,SAAS;AAAA,IACT,SAAS;AAAA;AAAA,IAET,mBAAmB;AAAA,IACnB,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,aAAa;AAAA,EAAA;AAGjB,SAAO,EAAE,UAAU,QAAQ,WAAW,OAAO,aAAa,gBAAgB,iBAAiB,YAAY,OAAA;AAC3G;AAEA,MAAM,kBAAkB;AACxB,MAAM,yBAAyB;AAE/B,SAAS,YAAY,OAA0B;AAC3C,QAAM,EAAE,cAAc,GAAG,GAAG,eAAe;AAE3C,SACI,sBAAA,cAAA,MAAA,UAAA,0CACKA,6BAAkB,QAAlB,EAA0B,GAAG,YAAY,aAA0B,GACpE,sBAAA;AAAA,IAACA,6BAAkB;AAAA,IAAlB;AAAA,MACI,GAAG;AAAA,MACJ,aAAa,cAAc;AAAA,MAC3B,aAAa,cAAc;AAAA,MAC3B,SAAS;AAAA,IAAA;AAAA,EAAA,CAEjB;AAER;;"}
|
|
@@ -12,7 +12,8 @@ function usePieChart(children, options) {
|
|
|
12
12
|
const pieProps = {
|
|
13
13
|
activeIndex,
|
|
14
14
|
activeShape: (props) => /* @__PURE__ */ React__default.createElement(ActiveShape, { ...props }),
|
|
15
|
-
|
|
15
|
+
// dataKey falls back to the segment label, which can repeat — the index keeps keys unique
|
|
16
|
+
children: shapes.map((shape, index) => /* @__PURE__ */ React__default.createElement(RechartsPrimitive.Cell, { key: `${shape.dataKey}-${index}`, color: shape.color, fill: shape.fill })),
|
|
16
17
|
// data props
|
|
17
18
|
data: items.filter((item) => !item.isHidden),
|
|
18
19
|
dataKey: "value",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sources":["../../../../src/charts/components/Pie/util.tsx"],"sourcesContent":["import React from 'react';\nimport * as RechartsPrimitive from 'recharts';\nimport type { PieSectorDataItem } from 'recharts/types/polar/Pie';\n\nimport type { useChartOptions } from '../useChartData';\nimport { useChart } from '../useChartData';\n\nconst PIE_ACTIVE_WIDTH = 10;\nconst PIE_ACTIVE_WIDTH_OFFSET = 2;\n\nexport function usePieChart<TData extends object>(children: React.ReactNode, options?: useChartOptions) {\n const { items, shapes, activeIndex, setActiveIndex, globalFormatter, toggleItem } = useChart<TData>(children, options);\n const [radius, _setRadius] = React.useState<number>(0);\n\n function setRadius(width: number) {\n _setRadius((width - 2 * (PIE_ACTIVE_WIDTH + PIE_ACTIVE_WIDTH_OFFSET)) / 2);\n }\n\n const pieProps = {\n activeIndex,\n activeShape: (props: PieSectorDataItem) => <ActiveShape {...props} />,\n children: shapes.map(shape => <RechartsPrimitive.Cell key={shape.dataKey} color={shape.color} fill={shape.fill}
|
|
1
|
+
{"version":3,"file":"util.js","sources":["../../../../src/charts/components/Pie/util.tsx"],"sourcesContent":["import React from 'react';\nimport * as RechartsPrimitive from 'recharts';\nimport type { PieSectorDataItem } from 'recharts/types/polar/Pie';\n\nimport type { useChartOptions } from '../useChartData';\nimport { useChart } from '../useChartData';\n\nconst PIE_ACTIVE_WIDTH = 10;\nconst PIE_ACTIVE_WIDTH_OFFSET = 2;\n\nexport function usePieChart<TData extends object>(children: React.ReactNode, options?: useChartOptions) {\n const { items, shapes, activeIndex, setActiveIndex, globalFormatter, toggleItem } = useChart<TData>(children, options);\n const [radius, _setRadius] = React.useState<number>(0);\n\n function setRadius(width: number) {\n _setRadius((width - 2 * (PIE_ACTIVE_WIDTH + PIE_ACTIVE_WIDTH_OFFSET)) / 2);\n }\n\n const pieProps = {\n activeIndex,\n activeShape: (props: PieSectorDataItem) => <ActiveShape {...props} />,\n // dataKey falls back to the segment label, which can repeat — the index keeps keys unique\n children: shapes.map((shape, index) => (\n <RechartsPrimitive.Cell key={`${shape.dataKey}-${index}`} color={shape.color} fill={shape.fill} />\n )),\n // data props\n data: items.filter(item => !item.isHidden),\n dataKey: 'value',\n nameKey: 'label',\n // style props\n isAnimationActive: false,\n startAngle: 90,\n endAngle: -270,\n stroke: '0',\n labelLine: false,\n outerRadius: radius,\n };\n\n return { pieProps, radius, setRadius, items, activeIndex, setActiveIndex, globalFormatter, toggleItem, shapes };\n}\n\nconst PIE_CHART_HOVER = 10;\nconst PIE_CHART_HOVER_OFFSET = 2;\n\nfunction ActiveShape(props: PieSectorDataItem) {\n const { outerRadius = 0, ...attributes } = props;\n\n return (\n <>\n <RechartsPrimitive.Sector {...attributes} outerRadius={outerRadius} />\n <RechartsPrimitive.Sector\n {...attributes}\n innerRadius={outerRadius + PIE_CHART_HOVER_OFFSET}\n outerRadius={outerRadius + PIE_CHART_HOVER}\n opacity={0.4}\n />\n </>\n );\n}\n"],"names":["React"],"mappings":";;;AAOA,MAAM,mBAAmB;AACzB,MAAM,0BAA0B;AAEzB,SAAS,YAAkC,UAA2B,SAA2B;AACpG,QAAM,EAAE,OAAO,QAAQ,aAAa,gBAAgB,iBAAiB,eAAe,SAAgB,UAAU,OAAO;AACrH,QAAM,CAAC,QAAQ,UAAU,IAAIA,eAAM,SAAiB,CAAC;AAErD,WAAS,UAAU,OAAe;AAC9B,gBAAY,QAAQ,KAAK,mBAAmB,4BAA4B,CAAC;AAAA,EAC7E;AAEA,QAAM,WAAW;AAAA,IACb;AAAA,IACA,aAAa,CAAC,UAA6BA,+BAAA,cAAC,aAAA,EAAa,GAAG,OAAO;AAAA;AAAA,IAEnE,UAAU,OAAO,IAAI,CAAC,OAAO,UACzBA,+BAAA,cAAC,kBAAkB,MAAlB,EAAuB,KAAK,GAAG,MAAM,OAAO,IAAI,KAAK,IAAI,OAAO,MAAM,OAAO,MAAM,MAAM,KAAA,CAAM,CACnG;AAAA;AAAA,IAED,MAAM,MAAM,OAAO,CAAA,SAAQ,CAAC,KAAK,QAAQ;AAAA,IACzC,SAAS;AAAA,IACT,SAAS;AAAA;AAAA,IAET,mBAAmB;AAAA,IACnB,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,aAAa;AAAA,EAAA;AAGjB,SAAO,EAAE,UAAU,QAAQ,WAAW,OAAO,aAAa,gBAAgB,iBAAiB,YAAY,OAAA;AAC3G;AAEA,MAAM,kBAAkB;AACxB,MAAM,yBAAyB;AAE/B,SAAS,YAAY,OAA0B;AAC3C,QAAM,EAAE,cAAc,GAAG,GAAG,eAAe;AAE3C,SACIA,+BAAA,cAAAA,eAAA,UAAA,mDACK,kBAAkB,QAAlB,EAA0B,GAAG,YAAY,aAA0B,GACpEA,+BAAA;AAAA,IAAC,kBAAkB;AAAA,IAAlB;AAAA,MACI,GAAG;AAAA,MACJ,aAAa,cAAc;AAAA,MAC3B,aAAa,cAAc;AAAA,MAC3B,SAAS;AAAA,IAAA;AAAA,EAAA,CAEjB;AAER;"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const debounce = require("lodash/debounce");
|
|
4
|
-
const nanoid = require("nanoid");
|
|
5
4
|
const React = require("react");
|
|
6
5
|
const ScrollableList = require("../Listbox/ScrollableList.cjs");
|
|
7
6
|
const util = require("../Listbox/util.cjs");
|
|
7
|
+
const useId = require("../../hooks/useId.cjs");
|
|
8
8
|
const useMergedRef = require("../../hooks/useMergedRef.cjs");
|
|
9
9
|
const dom = require("../../utils/dom.cjs");
|
|
10
10
|
const input = require("../../utils/input.cjs");
|
|
@@ -50,7 +50,7 @@ const useCombobox = (props, ref) => {
|
|
|
50
50
|
const buttonRef = React__namespace.useRef(null);
|
|
51
51
|
const listRef = React__namespace.useRef(null);
|
|
52
52
|
const [open, setOpen] = React__namespace.useState(false);
|
|
53
|
-
const listId =
|
|
53
|
+
const listId = useId.useId();
|
|
54
54
|
const flattenedData = util.useFlattenedData(unfilteredData);
|
|
55
55
|
const convertToInputValue = React__namespace.useCallback(
|
|
56
56
|
(val) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCombobox.cjs","sources":["../../../src/components/Combobox/useCombobox.tsx"],"sourcesContent":["import debounce from 'lodash/debounce';\nimport { nanoid } from 'nanoid';\nimport * as React from 'react';\n\nimport type { InputProps } from '@/components';\nimport type {\n ScrollableListItem,\n ScrollableListItemValue,\n ScrollableListPropsWithRef,\n} from '@/components/Listbox/ScrollableList';\nimport { getId } from '@/components/Listbox/ScrollableList';\nimport {\n setInputValueByRef,\n getIndexFromValue,\n findByValue,\n useFlattenedData,\n sanitizeItem,\n getOptionParents,\n filterData,\n} from '@/components/Listbox/util';\nimport { useMergedRef } from '@/hooks/useMergedRef';\nimport { isElementInsideTable3OrReport } from '@/utils/dom';\nimport { createCustomKeyboardEvent } from '@/utils/input';\n\nimport type { ComboboxProps } from './Combobox';\n\nconst debouncer = debounce(f => f(), 200);\n\nconst defaultConvertToInputValue = (value: ScrollableListItemValue | undefined) => String(value ?? '');\n\ntype useCombobox = Omit<React.HTMLAttributes<HTMLDivElement>, 'popover'> & {\n combobox: React.HTMLAttributes<HTMLSpanElement>;\n input: Omit<InputProps, 'defaultValue'> & { ref: React.RefObject<HTMLInputElement> };\n list: ScrollableListPropsWithRef;\n button: { ref: React.LegacyRef<HTMLButtonElement> };\n popover: { open: boolean; onOpenChange: (open: boolean) => void };\n};\n\nexport const useCombobox = (props: Omit<ComboboxProps, 'dialog'>, ref: React.Ref<HTMLInputElement>): useCombobox => {\n const {\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n data: unfilteredData = [],\n defaultValue,\n disabled,\n id: _nativeId,\n inline,\n loading: __,\n onChange,\n onClick,\n onKeyDown,\n onSearch,\n readOnly,\n value,\n valueRenderer,\n ...otherProps\n } = props;\n const inputRef = useMergedRef<HTMLInputElement>(ref);\n const buttonRef = React.useRef<HTMLButtonElement>(null);\n const listRef = React.useRef<HTMLUListElement>(null);\n const [open, setOpen] = React.useState(false);\n const listId = React.useMemo(() => nanoid(), []);\n const flattenedData = useFlattenedData(unfilteredData);\n\n const convertToInputValue = React.useCallback(\n (val: ScrollableListItemValue | undefined): string => {\n const option = findByValue(flattenedData, val);\n if (typeof valueRenderer === 'function' && option) {\n return valueRenderer(option);\n }\n return defaultConvertToInputValue(val);\n },\n [valueRenderer, flattenedData]\n );\n\n const [inputValue, setInputValue] = React.useState<string>(convertToInputValue(value));\n const shouldFilterData = !onSearch && (!inline || (inline && inputValue !== convertToInputValue(value)));\n const data = React.useMemo(\n () => (shouldFilterData ? filterData(flattenedData, inputValue) : flattenedData),\n [shouldFilterData, inputValue, flattenedData]\n );\n // listbox/select change value _with_ the index, but combobox changes on select of an index (click/enter), so we need state\n const [currentIndex, setCurrentIndex] = React.useState<number | undefined>(\n inputValue === undefined ? undefined : getIndexFromValue(data, inputValue)\n );\n\n const setInputValueByIndex = (index: number | undefined, event?: string): void => {\n if (index !== undefined) {\n const option = data[index];\n\n if (option && !option.disabled) {\n const eventType = event ?? 'focusout';\n setInputValueByRef(inputRef.current, option.value, eventType);\n }\n }\n };\n\n const setCurrentValue = (index: number | undefined, event?: string) => {\n if (index === undefined) {\n return;\n }\n\n const option = data[index];\n\n if (option?.value === value) {\n // if the selected option is already selected, refill input with its value\n setInputValue(convertToInputValue(value));\n } else {\n // if the selected option is not already selected, trigger blur event\n setInputValueByIndex(index, event);\n }\n };\n\n // ensure the external value is synced with the internal value when mounting, e.g. incase a default value was set\n React.useEffect(() => {\n if (defaultValue && !value) {\n setInputValueByIndex(getIndexFromValue(data, defaultValue));\n }\n }, [data]);\n\n // Sync input text when `value` changes externally, during render (not in an effect) so it's correct before the\n // \"show listbox\" effect below — otherwise a one-render stale-text window can open the popover on reset.\n const [prevValue, setPrevValue] = React.useState(value);\n if (value !== prevValue) {\n setPrevValue(value);\n\n if (value !== undefined) {\n // Compare rendered values to handle valueRenderer (e.g. \"1000\" renders as \"1000 - Account Name\").\n const renderedValue = convertToInputValue(value);\n if (renderedValue !== inputValue) {\n setInputValue(renderedValue);\n }\n } else if (prevValue !== undefined && inputValue === convertToInputValue(prevValue) && inputValue !== '') {\n // `value` was cleared externally and the input still shows its previous rendered text (not user-typed),\n // so clear it. Text the user typed never matches the previous render, so it's preserved.\n setInputValue('');\n }\n }\n\n React.useEffect(() => {\n if (onSearch) {\n debouncer(() => {\n onSearch(inputValue);\n });\n }\n }, [inputValue]);\n\n // show listbox based on input value\n React.useEffect(() => {\n // don't show the popover if the internal (input) value already is the current value\n // this prevents the popover showing after selecting a value or pressing escape\n const isCurrentValue = value !== undefined && value !== null && inputValue === convertToInputValue(value);\n\n if (inputValue && data.length && !isCurrentValue) {\n setCurrentIndex(getIndexFromValue(data, inputValue) || 0);\n\n if (!open) {\n setOpen(true);\n }\n } else {\n setOpen(false);\n }\n // `value` commits one render after the input on Tab/select; without it in the deps the guard\n // reopens the popover with a stale `value` and never re-runs to close it. EC-83328\n }, [inputValue, data, value]);\n\n React.useEffect(() => {\n if (open) {\n setCurrentIndex(getIndexFromValue(data, inputValue) || 0);\n } else {\n setCurrentIndex(undefined);\n }\n }, [open]);\n\n const findOption = React.useCallback(\n (inputVal: string): ScrollableListItem | undefined => {\n // First try to find by raw value\n const itemByValue = findByValue(flattenedData, inputVal);\n if (itemByValue) {\n return itemByValue;\n }\n\n // If valueRenderer is provided, also try to match against rendered values\n if (valueRenderer) {\n return flattenedData.find(option => valueRenderer(option) === inputVal);\n }\n\n return undefined;\n },\n [flattenedData, valueRenderer]\n );\n\n // event handlers\n const handleInputBlur = (event: React.FocusEvent<HTMLInputElement>): void => {\n if (listRef.current && event.relatedTarget === listRef.current) {\n event.preventDefault();\n return;\n }\n\n const item = findOption(event.target.value);\n\n // If the found item's value equals the current value, just update the display to rendered format\n // This handles the case where user types the raw value but valueRenderer shows something different\n // (e.g., user types \"1000\" but display should be \"1000 - Account Name\")\n if (item && item.value === value) {\n setInputValue(convertToInputValue(value));\n if (props.onBlur) {\n props.onBlur(event);\n }\n return;\n }\n\n // event.target.value is always a string so it is important to cast value to a string (convertToInputValue) before checking the equality\n if (onChange && event.target.value !== convertToInputValue(value)) {\n (event as any).detail = sanitizeItem(item);\n\n const parents = getOptionParents(flattenedData, item?.path);\n\n if (parents !== null && parents.length > 0) {\n (event as any).detail.parents = parents;\n }\n\n onChange(event);\n }\n\n if (props.onBlur) {\n props.onBlur(event);\n }\n };\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n setInputValue(event.target.value);\n };\n\n const handleInputClick = (event: React.MouseEvent<HTMLInputElement>): void => {\n if (inline || (!open && inputValue && data.length)) {\n setOpen(true);\n }\n\n if (onClick) {\n onClick(event);\n }\n };\n\n const handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n // Explicitly stop propagation for arrow keys when Combobox is in open state and inside Table3\n // This ensures Table3 won't react to these keys when using the Combobox\n if (\n isElementInsideTable3OrReport(event.currentTarget) &&\n (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&\n open\n ) {\n event.stopPropagation();\n }\n\n if (!event.ctrlKey && !event.metaKey) {\n switch (event.key) {\n case 'Backspace': {\n return;\n }\n\n case 'Escape': {\n event.preventDefault();\n if (open) {\n // as focus is on input, need to prevent propagation to not close dialog.\n event.stopPropagation();\n }\n setInputValue(convertToInputValue(value));\n setOpen(false);\n return;\n }\n\n case 'Tab': {\n // default focusout will trigger onBlur and we don't want to trigger it twice\n setCurrentValue(currentIndex, 'change');\n setOpen(false);\n return;\n }\n\n case 'Enter': {\n event.preventDefault();\n\n if (isElementInsideTable3OrReport(event.currentTarget)) {\n if (inline && !open) {\n setOpen(true);\n } else if (buttonRef.current && !open) {\n buttonRef.current.click();\n }\n }\n\n if (open) {\n // we need to focusout to trigger onBlur as we are not actually blurring the input\n setCurrentValue(currentIndex, 'focusout');\n setOpen(false);\n }\n\n return;\n }\n\n case 'ArrowDown':\n if (open) {\n event.preventDefault();\n } else if (!inline && buttonRef.current && !isElementInsideTable3OrReport(event.currentTarget)) {\n buttonRef.current.click();\n }\n break;\n\n case 'ArrowUp':\n case 'Home':\n case 'End': {\n if (open) {\n event.preventDefault();\n }\n break;\n }\n\n default:\n }\n\n // we aren't focused on the list, so manually forward the keydown event to it\n if (listRef.current) {\n listRef.current.dispatchEvent(createCustomKeyboardEvent(event));\n }\n\n if (\n inline &&\n !open &&\n (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&\n !isElementInsideTable3OrReport(event.currentTarget)\n ) {\n event.preventDefault();\n const initialIndex = event.key === 'ArrowUp' ? data.length - 1 : 0;\n setCurrentIndex(currentIndex === undefined ? initialIndex : currentIndex);\n setOpen(true);\n }\n }\n\n if (!event.isDefaultPrevented() && onKeyDown) {\n onKeyDown(event);\n }\n };\n\n const handleListboxChange = (index: number): void => {\n setCurrentIndex(index);\n };\n\n const handleListboxClick = (event: React.MouseEvent<HTMLLIElement>, index: number): void => {\n event.preventDefault();\n setCurrentValue(index);\n setOpen(false);\n };\n\n const combobox = {\n 'aria-expanded': open,\n 'aria-owns': listId,\n 'aria-haspopup': 'listbox' as const,\n role: 'combobox',\n };\n\n const input = {\n ...otherProps,\n 'aria-controls': listId,\n // Indicates that the autocomplete behavior of the text input is to suggest a list of possible values in a popup and that the suggestions\n // are related to the string that is present in the textbox\n 'aria-autocomplete': 'list' as const,\n // Enables assistive technologies to know which element the application regards as focused while DOM focus remains on the input element\n 'aria-activedescendant':\n currentIndex !== undefined && data[currentIndex] ? getId(listId, String(data[currentIndex].value)) : undefined,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n disabled,\n onBlur: !disabled && !readOnly ? handleInputBlur : undefined,\n onChange: !disabled && !readOnly ? handleInputChange : undefined,\n onClick: !disabled && !readOnly ? handleInputClick : undefined,\n onKeyDownCapture: !disabled && !readOnly ? handleInputKeyDown : undefined,\n readOnly,\n ref: inputRef,\n type: 'text',\n value: inputValue ?? '',\n };\n\n const list: ScrollableListPropsWithRef = {\n 'aria-labelledby': ariaLabelledBy,\n data,\n disabled,\n id: listId,\n onChange: handleListboxChange,\n onClick: handleListboxClick,\n ref: listRef,\n scrollOnFocus: false,\n tabIndex: -1,\n value: currentIndex,\n };\n\n const button = {\n ref: buttonRef,\n };\n\n return {\n combobox,\n input,\n list,\n button,\n popover: {\n open,\n onOpenChange: setOpen,\n //visible: !data.length ? false : open,\n },\n };\n};\n"],"names":["useMergedRef","React","nanoid","useFlattenedData","findByValue","filterData","getIndexFromValue","setInputValueByRef","sanitizeItem","getOptionParents","isElementInsideTable3OrReport","createCustomKeyboardEvent","input","getId"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useCombobox.cjs","sources":["../../../src/components/Combobox/useCombobox.tsx"],"sourcesContent":["import debounce from 'lodash/debounce';\nimport * as React from 'react';\n\nimport type { InputProps } from '@/components';\nimport type {\n ScrollableListItem,\n ScrollableListItemValue,\n ScrollableListPropsWithRef,\n} from '@/components/Listbox/ScrollableList';\nimport { getId } from '@/components/Listbox/ScrollableList';\nimport {\n setInputValueByRef,\n getIndexFromValue,\n findByValue,\n useFlattenedData,\n sanitizeItem,\n getOptionParents,\n filterData,\n} from '@/components/Listbox/util';\nimport { useId } from '@/hooks/useId';\nimport { useMergedRef } from '@/hooks/useMergedRef';\nimport { isElementInsideTable3OrReport } from '@/utils/dom';\nimport { createCustomKeyboardEvent } from '@/utils/input';\n\nimport type { ComboboxProps } from './Combobox';\n\nconst debouncer = debounce(f => f(), 200);\n\nconst defaultConvertToInputValue = (value: ScrollableListItemValue | undefined) => String(value ?? '');\n\ntype useCombobox = Omit<React.HTMLAttributes<HTMLDivElement>, 'popover'> & {\n combobox: React.HTMLAttributes<HTMLSpanElement>;\n input: Omit<InputProps, 'defaultValue'> & { ref: React.RefObject<HTMLInputElement> };\n list: ScrollableListPropsWithRef;\n button: { ref: React.LegacyRef<HTMLButtonElement> };\n popover: { open: boolean; onOpenChange: (open: boolean) => void };\n};\n\nexport const useCombobox = (props: Omit<ComboboxProps, 'dialog'>, ref: React.Ref<HTMLInputElement>): useCombobox => {\n const {\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n data: unfilteredData = [],\n defaultValue,\n disabled,\n id: _nativeId,\n inline,\n loading: __,\n onChange,\n onClick,\n onKeyDown,\n onSearch,\n readOnly,\n value,\n valueRenderer,\n ...otherProps\n } = props;\n const inputRef = useMergedRef<HTMLInputElement>(ref);\n const buttonRef = React.useRef<HTMLButtonElement>(null);\n const listRef = React.useRef<HTMLUListElement>(null);\n const [open, setOpen] = React.useState(false);\n const listId = useId();\n const flattenedData = useFlattenedData(unfilteredData);\n\n const convertToInputValue = React.useCallback(\n (val: ScrollableListItemValue | undefined): string => {\n const option = findByValue(flattenedData, val);\n if (typeof valueRenderer === 'function' && option) {\n return valueRenderer(option);\n }\n return defaultConvertToInputValue(val);\n },\n [valueRenderer, flattenedData]\n );\n\n const [inputValue, setInputValue] = React.useState<string>(convertToInputValue(value));\n const shouldFilterData = !onSearch && (!inline || (inline && inputValue !== convertToInputValue(value)));\n const data = React.useMemo(\n () => (shouldFilterData ? filterData(flattenedData, inputValue) : flattenedData),\n [shouldFilterData, inputValue, flattenedData]\n );\n // listbox/select change value _with_ the index, but combobox changes on select of an index (click/enter), so we need state\n const [currentIndex, setCurrentIndex] = React.useState<number | undefined>(\n inputValue === undefined ? undefined : getIndexFromValue(data, inputValue)\n );\n\n const setInputValueByIndex = (index: number | undefined, event?: string): void => {\n if (index !== undefined) {\n const option = data[index];\n\n if (option && !option.disabled) {\n const eventType = event ?? 'focusout';\n setInputValueByRef(inputRef.current, option.value, eventType);\n }\n }\n };\n\n const setCurrentValue = (index: number | undefined, event?: string) => {\n if (index === undefined) {\n return;\n }\n\n const option = data[index];\n\n if (option?.value === value) {\n // if the selected option is already selected, refill input with its value\n setInputValue(convertToInputValue(value));\n } else {\n // if the selected option is not already selected, trigger blur event\n setInputValueByIndex(index, event);\n }\n };\n\n // ensure the external value is synced with the internal value when mounting, e.g. incase a default value was set\n React.useEffect(() => {\n if (defaultValue && !value) {\n setInputValueByIndex(getIndexFromValue(data, defaultValue));\n }\n }, [data]);\n\n // Sync input text when `value` changes externally, during render (not in an effect) so it's correct before the\n // \"show listbox\" effect below — otherwise a one-render stale-text window can open the popover on reset.\n const [prevValue, setPrevValue] = React.useState(value);\n if (value !== prevValue) {\n setPrevValue(value);\n\n if (value !== undefined) {\n // Compare rendered values to handle valueRenderer (e.g. \"1000\" renders as \"1000 - Account Name\").\n const renderedValue = convertToInputValue(value);\n if (renderedValue !== inputValue) {\n setInputValue(renderedValue);\n }\n } else if (prevValue !== undefined && inputValue === convertToInputValue(prevValue) && inputValue !== '') {\n // `value` was cleared externally and the input still shows its previous rendered text (not user-typed),\n // so clear it. Text the user typed never matches the previous render, so it's preserved.\n setInputValue('');\n }\n }\n\n React.useEffect(() => {\n if (onSearch) {\n debouncer(() => {\n onSearch(inputValue);\n });\n }\n }, [inputValue]);\n\n // show listbox based on input value\n React.useEffect(() => {\n // don't show the popover if the internal (input) value already is the current value\n // this prevents the popover showing after selecting a value or pressing escape\n const isCurrentValue = value !== undefined && value !== null && inputValue === convertToInputValue(value);\n\n if (inputValue && data.length && !isCurrentValue) {\n setCurrentIndex(getIndexFromValue(data, inputValue) || 0);\n\n if (!open) {\n setOpen(true);\n }\n } else {\n setOpen(false);\n }\n // `value` commits one render after the input on Tab/select; without it in the deps the guard\n // reopens the popover with a stale `value` and never re-runs to close it. EC-83328\n }, [inputValue, data, value]);\n\n React.useEffect(() => {\n if (open) {\n setCurrentIndex(getIndexFromValue(data, inputValue) || 0);\n } else {\n setCurrentIndex(undefined);\n }\n }, [open]);\n\n const findOption = React.useCallback(\n (inputVal: string): ScrollableListItem | undefined => {\n // First try to find by raw value\n const itemByValue = findByValue(flattenedData, inputVal);\n if (itemByValue) {\n return itemByValue;\n }\n\n // If valueRenderer is provided, also try to match against rendered values\n if (valueRenderer) {\n return flattenedData.find(option => valueRenderer(option) === inputVal);\n }\n\n return undefined;\n },\n [flattenedData, valueRenderer]\n );\n\n // event handlers\n const handleInputBlur = (event: React.FocusEvent<HTMLInputElement>): void => {\n if (listRef.current && event.relatedTarget === listRef.current) {\n event.preventDefault();\n return;\n }\n\n const item = findOption(event.target.value);\n\n // If the found item's value equals the current value, just update the display to rendered format\n // This handles the case where user types the raw value but valueRenderer shows something different\n // (e.g., user types \"1000\" but display should be \"1000 - Account Name\")\n if (item && item.value === value) {\n setInputValue(convertToInputValue(value));\n if (props.onBlur) {\n props.onBlur(event);\n }\n return;\n }\n\n // event.target.value is always a string so it is important to cast value to a string (convertToInputValue) before checking the equality\n if (onChange && event.target.value !== convertToInputValue(value)) {\n (event as any).detail = sanitizeItem(item);\n\n const parents = getOptionParents(flattenedData, item?.path);\n\n if (parents !== null && parents.length > 0) {\n (event as any).detail.parents = parents;\n }\n\n onChange(event);\n }\n\n if (props.onBlur) {\n props.onBlur(event);\n }\n };\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n setInputValue(event.target.value);\n };\n\n const handleInputClick = (event: React.MouseEvent<HTMLInputElement>): void => {\n if (inline || (!open && inputValue && data.length)) {\n setOpen(true);\n }\n\n if (onClick) {\n onClick(event);\n }\n };\n\n const handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n // Explicitly stop propagation for arrow keys when Combobox is in open state and inside Table3\n // This ensures Table3 won't react to these keys when using the Combobox\n if (\n isElementInsideTable3OrReport(event.currentTarget) &&\n (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&\n open\n ) {\n event.stopPropagation();\n }\n\n if (!event.ctrlKey && !event.metaKey) {\n switch (event.key) {\n case 'Backspace': {\n return;\n }\n\n case 'Escape': {\n event.preventDefault();\n if (open) {\n // as focus is on input, need to prevent propagation to not close dialog.\n event.stopPropagation();\n }\n setInputValue(convertToInputValue(value));\n setOpen(false);\n return;\n }\n\n case 'Tab': {\n // default focusout will trigger onBlur and we don't want to trigger it twice\n setCurrentValue(currentIndex, 'change');\n setOpen(false);\n return;\n }\n\n case 'Enter': {\n event.preventDefault();\n\n if (isElementInsideTable3OrReport(event.currentTarget)) {\n if (inline && !open) {\n setOpen(true);\n } else if (buttonRef.current && !open) {\n buttonRef.current.click();\n }\n }\n\n if (open) {\n // we need to focusout to trigger onBlur as we are not actually blurring the input\n setCurrentValue(currentIndex, 'focusout');\n setOpen(false);\n }\n\n return;\n }\n\n case 'ArrowDown':\n if (open) {\n event.preventDefault();\n } else if (!inline && buttonRef.current && !isElementInsideTable3OrReport(event.currentTarget)) {\n buttonRef.current.click();\n }\n break;\n\n case 'ArrowUp':\n case 'Home':\n case 'End': {\n if (open) {\n event.preventDefault();\n }\n break;\n }\n\n default:\n }\n\n // we aren't focused on the list, so manually forward the keydown event to it\n if (listRef.current) {\n listRef.current.dispatchEvent(createCustomKeyboardEvent(event));\n }\n\n if (\n inline &&\n !open &&\n (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&\n !isElementInsideTable3OrReport(event.currentTarget)\n ) {\n event.preventDefault();\n const initialIndex = event.key === 'ArrowUp' ? data.length - 1 : 0;\n setCurrentIndex(currentIndex === undefined ? initialIndex : currentIndex);\n setOpen(true);\n }\n }\n\n if (!event.isDefaultPrevented() && onKeyDown) {\n onKeyDown(event);\n }\n };\n\n const handleListboxChange = (index: number): void => {\n setCurrentIndex(index);\n };\n\n const handleListboxClick = (event: React.MouseEvent<HTMLLIElement>, index: number): void => {\n event.preventDefault();\n setCurrentValue(index);\n setOpen(false);\n };\n\n const combobox = {\n 'aria-expanded': open,\n 'aria-owns': listId,\n 'aria-haspopup': 'listbox' as const,\n role: 'combobox',\n };\n\n const input = {\n ...otherProps,\n 'aria-controls': listId,\n // Indicates that the autocomplete behavior of the text input is to suggest a list of possible values in a popup and that the suggestions\n // are related to the string that is present in the textbox\n 'aria-autocomplete': 'list' as const,\n // Enables assistive technologies to know which element the application regards as focused while DOM focus remains on the input element\n 'aria-activedescendant':\n currentIndex !== undefined && data[currentIndex] ? getId(listId, String(data[currentIndex].value)) : undefined,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n disabled,\n onBlur: !disabled && !readOnly ? handleInputBlur : undefined,\n onChange: !disabled && !readOnly ? handleInputChange : undefined,\n onClick: !disabled && !readOnly ? handleInputClick : undefined,\n onKeyDownCapture: !disabled && !readOnly ? handleInputKeyDown : undefined,\n readOnly,\n ref: inputRef,\n type: 'text',\n value: inputValue ?? '',\n };\n\n const list: ScrollableListPropsWithRef = {\n 'aria-labelledby': ariaLabelledBy,\n data,\n disabled,\n id: listId,\n onChange: handleListboxChange,\n onClick: handleListboxClick,\n ref: listRef,\n scrollOnFocus: false,\n tabIndex: -1,\n value: currentIndex,\n };\n\n const button = {\n ref: buttonRef,\n };\n\n return {\n combobox,\n input,\n list,\n button,\n popover: {\n open,\n onOpenChange: setOpen,\n //visible: !data.length ? false : open,\n },\n };\n};\n"],"names":["useMergedRef","React","useId","useFlattenedData","findByValue","filterData","getIndexFromValue","setInputValueByRef","sanitizeItem","getOptionParents","isElementInsideTable3OrReport","createCustomKeyboardEvent","input","getId"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import debounce from "lodash/debounce";
|
|
2
|
-
import { nanoid } from "nanoid";
|
|
3
2
|
import * as React from "react";
|
|
4
3
|
import { getId } from "../Listbox/ScrollableList.js";
|
|
5
4
|
import { useFlattenedData, findByValue, filterData, getIndexFromValue, setInputValueByRef, sanitizeItem, getOptionParents } from "../Listbox/util.js";
|
|
5
|
+
import { useId } from "../../hooks/useId.js";
|
|
6
6
|
import { useMergedRef } from "../../hooks/useMergedRef.js";
|
|
7
7
|
import { isElementInsideTable3OrReport } from "../../utils/dom.js";
|
|
8
8
|
import { createCustomKeyboardEvent } from "../../utils/input.js";
|
|
@@ -31,7 +31,7 @@ const useCombobox = (props, ref) => {
|
|
|
31
31
|
const buttonRef = React.useRef(null);
|
|
32
32
|
const listRef = React.useRef(null);
|
|
33
33
|
const [open, setOpen] = React.useState(false);
|
|
34
|
-
const listId =
|
|
34
|
+
const listId = useId();
|
|
35
35
|
const flattenedData = useFlattenedData(unfilteredData);
|
|
36
36
|
const convertToInputValue = React.useCallback(
|
|
37
37
|
(val) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCombobox.js","sources":["../../../src/components/Combobox/useCombobox.tsx"],"sourcesContent":["import debounce from 'lodash/debounce';\nimport { nanoid } from 'nanoid';\nimport * as React from 'react';\n\nimport type { InputProps } from '@/components';\nimport type {\n ScrollableListItem,\n ScrollableListItemValue,\n ScrollableListPropsWithRef,\n} from '@/components/Listbox/ScrollableList';\nimport { getId } from '@/components/Listbox/ScrollableList';\nimport {\n setInputValueByRef,\n getIndexFromValue,\n findByValue,\n useFlattenedData,\n sanitizeItem,\n getOptionParents,\n filterData,\n} from '@/components/Listbox/util';\nimport { useMergedRef } from '@/hooks/useMergedRef';\nimport { isElementInsideTable3OrReport } from '@/utils/dom';\nimport { createCustomKeyboardEvent } from '@/utils/input';\n\nimport type { ComboboxProps } from './Combobox';\n\nconst debouncer = debounce(f => f(), 200);\n\nconst defaultConvertToInputValue = (value: ScrollableListItemValue | undefined) => String(value ?? '');\n\ntype useCombobox = Omit<React.HTMLAttributes<HTMLDivElement>, 'popover'> & {\n combobox: React.HTMLAttributes<HTMLSpanElement>;\n input: Omit<InputProps, 'defaultValue'> & { ref: React.RefObject<HTMLInputElement> };\n list: ScrollableListPropsWithRef;\n button: { ref: React.LegacyRef<HTMLButtonElement> };\n popover: { open: boolean; onOpenChange: (open: boolean) => void };\n};\n\nexport const useCombobox = (props: Omit<ComboboxProps, 'dialog'>, ref: React.Ref<HTMLInputElement>): useCombobox => {\n const {\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n data: unfilteredData = [],\n defaultValue,\n disabled,\n id: _nativeId,\n inline,\n loading: __,\n onChange,\n onClick,\n onKeyDown,\n onSearch,\n readOnly,\n value,\n valueRenderer,\n ...otherProps\n } = props;\n const inputRef = useMergedRef<HTMLInputElement>(ref);\n const buttonRef = React.useRef<HTMLButtonElement>(null);\n const listRef = React.useRef<HTMLUListElement>(null);\n const [open, setOpen] = React.useState(false);\n const listId = React.useMemo(() => nanoid(), []);\n const flattenedData = useFlattenedData(unfilteredData);\n\n const convertToInputValue = React.useCallback(\n (val: ScrollableListItemValue | undefined): string => {\n const option = findByValue(flattenedData, val);\n if (typeof valueRenderer === 'function' && option) {\n return valueRenderer(option);\n }\n return defaultConvertToInputValue(val);\n },\n [valueRenderer, flattenedData]\n );\n\n const [inputValue, setInputValue] = React.useState<string>(convertToInputValue(value));\n const shouldFilterData = !onSearch && (!inline || (inline && inputValue !== convertToInputValue(value)));\n const data = React.useMemo(\n () => (shouldFilterData ? filterData(flattenedData, inputValue) : flattenedData),\n [shouldFilterData, inputValue, flattenedData]\n );\n // listbox/select change value _with_ the index, but combobox changes on select of an index (click/enter), so we need state\n const [currentIndex, setCurrentIndex] = React.useState<number | undefined>(\n inputValue === undefined ? undefined : getIndexFromValue(data, inputValue)\n );\n\n const setInputValueByIndex = (index: number | undefined, event?: string): void => {\n if (index !== undefined) {\n const option = data[index];\n\n if (option && !option.disabled) {\n const eventType = event ?? 'focusout';\n setInputValueByRef(inputRef.current, option.value, eventType);\n }\n }\n };\n\n const setCurrentValue = (index: number | undefined, event?: string) => {\n if (index === undefined) {\n return;\n }\n\n const option = data[index];\n\n if (option?.value === value) {\n // if the selected option is already selected, refill input with its value\n setInputValue(convertToInputValue(value));\n } else {\n // if the selected option is not already selected, trigger blur event\n setInputValueByIndex(index, event);\n }\n };\n\n // ensure the external value is synced with the internal value when mounting, e.g. incase a default value was set\n React.useEffect(() => {\n if (defaultValue && !value) {\n setInputValueByIndex(getIndexFromValue(data, defaultValue));\n }\n }, [data]);\n\n // Sync input text when `value` changes externally, during render (not in an effect) so it's correct before the\n // \"show listbox\" effect below — otherwise a one-render stale-text window can open the popover on reset.\n const [prevValue, setPrevValue] = React.useState(value);\n if (value !== prevValue) {\n setPrevValue(value);\n\n if (value !== undefined) {\n // Compare rendered values to handle valueRenderer (e.g. \"1000\" renders as \"1000 - Account Name\").\n const renderedValue = convertToInputValue(value);\n if (renderedValue !== inputValue) {\n setInputValue(renderedValue);\n }\n } else if (prevValue !== undefined && inputValue === convertToInputValue(prevValue) && inputValue !== '') {\n // `value` was cleared externally and the input still shows its previous rendered text (not user-typed),\n // so clear it. Text the user typed never matches the previous render, so it's preserved.\n setInputValue('');\n }\n }\n\n React.useEffect(() => {\n if (onSearch) {\n debouncer(() => {\n onSearch(inputValue);\n });\n }\n }, [inputValue]);\n\n // show listbox based on input value\n React.useEffect(() => {\n // don't show the popover if the internal (input) value already is the current value\n // this prevents the popover showing after selecting a value or pressing escape\n const isCurrentValue = value !== undefined && value !== null && inputValue === convertToInputValue(value);\n\n if (inputValue && data.length && !isCurrentValue) {\n setCurrentIndex(getIndexFromValue(data, inputValue) || 0);\n\n if (!open) {\n setOpen(true);\n }\n } else {\n setOpen(false);\n }\n // `value` commits one render after the input on Tab/select; without it in the deps the guard\n // reopens the popover with a stale `value` and never re-runs to close it. EC-83328\n }, [inputValue, data, value]);\n\n React.useEffect(() => {\n if (open) {\n setCurrentIndex(getIndexFromValue(data, inputValue) || 0);\n } else {\n setCurrentIndex(undefined);\n }\n }, [open]);\n\n const findOption = React.useCallback(\n (inputVal: string): ScrollableListItem | undefined => {\n // First try to find by raw value\n const itemByValue = findByValue(flattenedData, inputVal);\n if (itemByValue) {\n return itemByValue;\n }\n\n // If valueRenderer is provided, also try to match against rendered values\n if (valueRenderer) {\n return flattenedData.find(option => valueRenderer(option) === inputVal);\n }\n\n return undefined;\n },\n [flattenedData, valueRenderer]\n );\n\n // event handlers\n const handleInputBlur = (event: React.FocusEvent<HTMLInputElement>): void => {\n if (listRef.current && event.relatedTarget === listRef.current) {\n event.preventDefault();\n return;\n }\n\n const item = findOption(event.target.value);\n\n // If the found item's value equals the current value, just update the display to rendered format\n // This handles the case where user types the raw value but valueRenderer shows something different\n // (e.g., user types \"1000\" but display should be \"1000 - Account Name\")\n if (item && item.value === value) {\n setInputValue(convertToInputValue(value));\n if (props.onBlur) {\n props.onBlur(event);\n }\n return;\n }\n\n // event.target.value is always a string so it is important to cast value to a string (convertToInputValue) before checking the equality\n if (onChange && event.target.value !== convertToInputValue(value)) {\n (event as any).detail = sanitizeItem(item);\n\n const parents = getOptionParents(flattenedData, item?.path);\n\n if (parents !== null && parents.length > 0) {\n (event as any).detail.parents = parents;\n }\n\n onChange(event);\n }\n\n if (props.onBlur) {\n props.onBlur(event);\n }\n };\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n setInputValue(event.target.value);\n };\n\n const handleInputClick = (event: React.MouseEvent<HTMLInputElement>): void => {\n if (inline || (!open && inputValue && data.length)) {\n setOpen(true);\n }\n\n if (onClick) {\n onClick(event);\n }\n };\n\n const handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n // Explicitly stop propagation for arrow keys when Combobox is in open state and inside Table3\n // This ensures Table3 won't react to these keys when using the Combobox\n if (\n isElementInsideTable3OrReport(event.currentTarget) &&\n (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&\n open\n ) {\n event.stopPropagation();\n }\n\n if (!event.ctrlKey && !event.metaKey) {\n switch (event.key) {\n case 'Backspace': {\n return;\n }\n\n case 'Escape': {\n event.preventDefault();\n if (open) {\n // as focus is on input, need to prevent propagation to not close dialog.\n event.stopPropagation();\n }\n setInputValue(convertToInputValue(value));\n setOpen(false);\n return;\n }\n\n case 'Tab': {\n // default focusout will trigger onBlur and we don't want to trigger it twice\n setCurrentValue(currentIndex, 'change');\n setOpen(false);\n return;\n }\n\n case 'Enter': {\n event.preventDefault();\n\n if (isElementInsideTable3OrReport(event.currentTarget)) {\n if (inline && !open) {\n setOpen(true);\n } else if (buttonRef.current && !open) {\n buttonRef.current.click();\n }\n }\n\n if (open) {\n // we need to focusout to trigger onBlur as we are not actually blurring the input\n setCurrentValue(currentIndex, 'focusout');\n setOpen(false);\n }\n\n return;\n }\n\n case 'ArrowDown':\n if (open) {\n event.preventDefault();\n } else if (!inline && buttonRef.current && !isElementInsideTable3OrReport(event.currentTarget)) {\n buttonRef.current.click();\n }\n break;\n\n case 'ArrowUp':\n case 'Home':\n case 'End': {\n if (open) {\n event.preventDefault();\n }\n break;\n }\n\n default:\n }\n\n // we aren't focused on the list, so manually forward the keydown event to it\n if (listRef.current) {\n listRef.current.dispatchEvent(createCustomKeyboardEvent(event));\n }\n\n if (\n inline &&\n !open &&\n (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&\n !isElementInsideTable3OrReport(event.currentTarget)\n ) {\n event.preventDefault();\n const initialIndex = event.key === 'ArrowUp' ? data.length - 1 : 0;\n setCurrentIndex(currentIndex === undefined ? initialIndex : currentIndex);\n setOpen(true);\n }\n }\n\n if (!event.isDefaultPrevented() && onKeyDown) {\n onKeyDown(event);\n }\n };\n\n const handleListboxChange = (index: number): void => {\n setCurrentIndex(index);\n };\n\n const handleListboxClick = (event: React.MouseEvent<HTMLLIElement>, index: number): void => {\n event.preventDefault();\n setCurrentValue(index);\n setOpen(false);\n };\n\n const combobox = {\n 'aria-expanded': open,\n 'aria-owns': listId,\n 'aria-haspopup': 'listbox' as const,\n role: 'combobox',\n };\n\n const input = {\n ...otherProps,\n 'aria-controls': listId,\n // Indicates that the autocomplete behavior of the text input is to suggest a list of possible values in a popup and that the suggestions\n // are related to the string that is present in the textbox\n 'aria-autocomplete': 'list' as const,\n // Enables assistive technologies to know which element the application regards as focused while DOM focus remains on the input element\n 'aria-activedescendant':\n currentIndex !== undefined && data[currentIndex] ? getId(listId, String(data[currentIndex].value)) : undefined,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n disabled,\n onBlur: !disabled && !readOnly ? handleInputBlur : undefined,\n onChange: !disabled && !readOnly ? handleInputChange : undefined,\n onClick: !disabled && !readOnly ? handleInputClick : undefined,\n onKeyDownCapture: !disabled && !readOnly ? handleInputKeyDown : undefined,\n readOnly,\n ref: inputRef,\n type: 'text',\n value: inputValue ?? '',\n };\n\n const list: ScrollableListPropsWithRef = {\n 'aria-labelledby': ariaLabelledBy,\n data,\n disabled,\n id: listId,\n onChange: handleListboxChange,\n onClick: handleListboxClick,\n ref: listRef,\n scrollOnFocus: false,\n tabIndex: -1,\n value: currentIndex,\n };\n\n const button = {\n ref: buttonRef,\n };\n\n return {\n combobox,\n input,\n list,\n button,\n popover: {\n open,\n onOpenChange: setOpen,\n //visible: !data.length ? false : open,\n },\n };\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useCombobox.js","sources":["../../../src/components/Combobox/useCombobox.tsx"],"sourcesContent":["import debounce from 'lodash/debounce';\nimport * as React from 'react';\n\nimport type { InputProps } from '@/components';\nimport type {\n ScrollableListItem,\n ScrollableListItemValue,\n ScrollableListPropsWithRef,\n} from '@/components/Listbox/ScrollableList';\nimport { getId } from '@/components/Listbox/ScrollableList';\nimport {\n setInputValueByRef,\n getIndexFromValue,\n findByValue,\n useFlattenedData,\n sanitizeItem,\n getOptionParents,\n filterData,\n} from '@/components/Listbox/util';\nimport { useId } from '@/hooks/useId';\nimport { useMergedRef } from '@/hooks/useMergedRef';\nimport { isElementInsideTable3OrReport } from '@/utils/dom';\nimport { createCustomKeyboardEvent } from '@/utils/input';\n\nimport type { ComboboxProps } from './Combobox';\n\nconst debouncer = debounce(f => f(), 200);\n\nconst defaultConvertToInputValue = (value: ScrollableListItemValue | undefined) => String(value ?? '');\n\ntype useCombobox = Omit<React.HTMLAttributes<HTMLDivElement>, 'popover'> & {\n combobox: React.HTMLAttributes<HTMLSpanElement>;\n input: Omit<InputProps, 'defaultValue'> & { ref: React.RefObject<HTMLInputElement> };\n list: ScrollableListPropsWithRef;\n button: { ref: React.LegacyRef<HTMLButtonElement> };\n popover: { open: boolean; onOpenChange: (open: boolean) => void };\n};\n\nexport const useCombobox = (props: Omit<ComboboxProps, 'dialog'>, ref: React.Ref<HTMLInputElement>): useCombobox => {\n const {\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n data: unfilteredData = [],\n defaultValue,\n disabled,\n id: _nativeId,\n inline,\n loading: __,\n onChange,\n onClick,\n onKeyDown,\n onSearch,\n readOnly,\n value,\n valueRenderer,\n ...otherProps\n } = props;\n const inputRef = useMergedRef<HTMLInputElement>(ref);\n const buttonRef = React.useRef<HTMLButtonElement>(null);\n const listRef = React.useRef<HTMLUListElement>(null);\n const [open, setOpen] = React.useState(false);\n const listId = useId();\n const flattenedData = useFlattenedData(unfilteredData);\n\n const convertToInputValue = React.useCallback(\n (val: ScrollableListItemValue | undefined): string => {\n const option = findByValue(flattenedData, val);\n if (typeof valueRenderer === 'function' && option) {\n return valueRenderer(option);\n }\n return defaultConvertToInputValue(val);\n },\n [valueRenderer, flattenedData]\n );\n\n const [inputValue, setInputValue] = React.useState<string>(convertToInputValue(value));\n const shouldFilterData = !onSearch && (!inline || (inline && inputValue !== convertToInputValue(value)));\n const data = React.useMemo(\n () => (shouldFilterData ? filterData(flattenedData, inputValue) : flattenedData),\n [shouldFilterData, inputValue, flattenedData]\n );\n // listbox/select change value _with_ the index, but combobox changes on select of an index (click/enter), so we need state\n const [currentIndex, setCurrentIndex] = React.useState<number | undefined>(\n inputValue === undefined ? undefined : getIndexFromValue(data, inputValue)\n );\n\n const setInputValueByIndex = (index: number | undefined, event?: string): void => {\n if (index !== undefined) {\n const option = data[index];\n\n if (option && !option.disabled) {\n const eventType = event ?? 'focusout';\n setInputValueByRef(inputRef.current, option.value, eventType);\n }\n }\n };\n\n const setCurrentValue = (index: number | undefined, event?: string) => {\n if (index === undefined) {\n return;\n }\n\n const option = data[index];\n\n if (option?.value === value) {\n // if the selected option is already selected, refill input with its value\n setInputValue(convertToInputValue(value));\n } else {\n // if the selected option is not already selected, trigger blur event\n setInputValueByIndex(index, event);\n }\n };\n\n // ensure the external value is synced with the internal value when mounting, e.g. incase a default value was set\n React.useEffect(() => {\n if (defaultValue && !value) {\n setInputValueByIndex(getIndexFromValue(data, defaultValue));\n }\n }, [data]);\n\n // Sync input text when `value` changes externally, during render (not in an effect) so it's correct before the\n // \"show listbox\" effect below — otherwise a one-render stale-text window can open the popover on reset.\n const [prevValue, setPrevValue] = React.useState(value);\n if (value !== prevValue) {\n setPrevValue(value);\n\n if (value !== undefined) {\n // Compare rendered values to handle valueRenderer (e.g. \"1000\" renders as \"1000 - Account Name\").\n const renderedValue = convertToInputValue(value);\n if (renderedValue !== inputValue) {\n setInputValue(renderedValue);\n }\n } else if (prevValue !== undefined && inputValue === convertToInputValue(prevValue) && inputValue !== '') {\n // `value` was cleared externally and the input still shows its previous rendered text (not user-typed),\n // so clear it. Text the user typed never matches the previous render, so it's preserved.\n setInputValue('');\n }\n }\n\n React.useEffect(() => {\n if (onSearch) {\n debouncer(() => {\n onSearch(inputValue);\n });\n }\n }, [inputValue]);\n\n // show listbox based on input value\n React.useEffect(() => {\n // don't show the popover if the internal (input) value already is the current value\n // this prevents the popover showing after selecting a value or pressing escape\n const isCurrentValue = value !== undefined && value !== null && inputValue === convertToInputValue(value);\n\n if (inputValue && data.length && !isCurrentValue) {\n setCurrentIndex(getIndexFromValue(data, inputValue) || 0);\n\n if (!open) {\n setOpen(true);\n }\n } else {\n setOpen(false);\n }\n // `value` commits one render after the input on Tab/select; without it in the deps the guard\n // reopens the popover with a stale `value` and never re-runs to close it. EC-83328\n }, [inputValue, data, value]);\n\n React.useEffect(() => {\n if (open) {\n setCurrentIndex(getIndexFromValue(data, inputValue) || 0);\n } else {\n setCurrentIndex(undefined);\n }\n }, [open]);\n\n const findOption = React.useCallback(\n (inputVal: string): ScrollableListItem | undefined => {\n // First try to find by raw value\n const itemByValue = findByValue(flattenedData, inputVal);\n if (itemByValue) {\n return itemByValue;\n }\n\n // If valueRenderer is provided, also try to match against rendered values\n if (valueRenderer) {\n return flattenedData.find(option => valueRenderer(option) === inputVal);\n }\n\n return undefined;\n },\n [flattenedData, valueRenderer]\n );\n\n // event handlers\n const handleInputBlur = (event: React.FocusEvent<HTMLInputElement>): void => {\n if (listRef.current && event.relatedTarget === listRef.current) {\n event.preventDefault();\n return;\n }\n\n const item = findOption(event.target.value);\n\n // If the found item's value equals the current value, just update the display to rendered format\n // This handles the case where user types the raw value but valueRenderer shows something different\n // (e.g., user types \"1000\" but display should be \"1000 - Account Name\")\n if (item && item.value === value) {\n setInputValue(convertToInputValue(value));\n if (props.onBlur) {\n props.onBlur(event);\n }\n return;\n }\n\n // event.target.value is always a string so it is important to cast value to a string (convertToInputValue) before checking the equality\n if (onChange && event.target.value !== convertToInputValue(value)) {\n (event as any).detail = sanitizeItem(item);\n\n const parents = getOptionParents(flattenedData, item?.path);\n\n if (parents !== null && parents.length > 0) {\n (event as any).detail.parents = parents;\n }\n\n onChange(event);\n }\n\n if (props.onBlur) {\n props.onBlur(event);\n }\n };\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n setInputValue(event.target.value);\n };\n\n const handleInputClick = (event: React.MouseEvent<HTMLInputElement>): void => {\n if (inline || (!open && inputValue && data.length)) {\n setOpen(true);\n }\n\n if (onClick) {\n onClick(event);\n }\n };\n\n const handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n // Explicitly stop propagation for arrow keys when Combobox is in open state and inside Table3\n // This ensures Table3 won't react to these keys when using the Combobox\n if (\n isElementInsideTable3OrReport(event.currentTarget) &&\n (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&\n open\n ) {\n event.stopPropagation();\n }\n\n if (!event.ctrlKey && !event.metaKey) {\n switch (event.key) {\n case 'Backspace': {\n return;\n }\n\n case 'Escape': {\n event.preventDefault();\n if (open) {\n // as focus is on input, need to prevent propagation to not close dialog.\n event.stopPropagation();\n }\n setInputValue(convertToInputValue(value));\n setOpen(false);\n return;\n }\n\n case 'Tab': {\n // default focusout will trigger onBlur and we don't want to trigger it twice\n setCurrentValue(currentIndex, 'change');\n setOpen(false);\n return;\n }\n\n case 'Enter': {\n event.preventDefault();\n\n if (isElementInsideTable3OrReport(event.currentTarget)) {\n if (inline && !open) {\n setOpen(true);\n } else if (buttonRef.current && !open) {\n buttonRef.current.click();\n }\n }\n\n if (open) {\n // we need to focusout to trigger onBlur as we are not actually blurring the input\n setCurrentValue(currentIndex, 'focusout');\n setOpen(false);\n }\n\n return;\n }\n\n case 'ArrowDown':\n if (open) {\n event.preventDefault();\n } else if (!inline && buttonRef.current && !isElementInsideTable3OrReport(event.currentTarget)) {\n buttonRef.current.click();\n }\n break;\n\n case 'ArrowUp':\n case 'Home':\n case 'End': {\n if (open) {\n event.preventDefault();\n }\n break;\n }\n\n default:\n }\n\n // we aren't focused on the list, so manually forward the keydown event to it\n if (listRef.current) {\n listRef.current.dispatchEvent(createCustomKeyboardEvent(event));\n }\n\n if (\n inline &&\n !open &&\n (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&\n !isElementInsideTable3OrReport(event.currentTarget)\n ) {\n event.preventDefault();\n const initialIndex = event.key === 'ArrowUp' ? data.length - 1 : 0;\n setCurrentIndex(currentIndex === undefined ? initialIndex : currentIndex);\n setOpen(true);\n }\n }\n\n if (!event.isDefaultPrevented() && onKeyDown) {\n onKeyDown(event);\n }\n };\n\n const handleListboxChange = (index: number): void => {\n setCurrentIndex(index);\n };\n\n const handleListboxClick = (event: React.MouseEvent<HTMLLIElement>, index: number): void => {\n event.preventDefault();\n setCurrentValue(index);\n setOpen(false);\n };\n\n const combobox = {\n 'aria-expanded': open,\n 'aria-owns': listId,\n 'aria-haspopup': 'listbox' as const,\n role: 'combobox',\n };\n\n const input = {\n ...otherProps,\n 'aria-controls': listId,\n // Indicates that the autocomplete behavior of the text input is to suggest a list of possible values in a popup and that the suggestions\n // are related to the string that is present in the textbox\n 'aria-autocomplete': 'list' as const,\n // Enables assistive technologies to know which element the application regards as focused while DOM focus remains on the input element\n 'aria-activedescendant':\n currentIndex !== undefined && data[currentIndex] ? getId(listId, String(data[currentIndex].value)) : undefined,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n disabled,\n onBlur: !disabled && !readOnly ? handleInputBlur : undefined,\n onChange: !disabled && !readOnly ? handleInputChange : undefined,\n onClick: !disabled && !readOnly ? handleInputClick : undefined,\n onKeyDownCapture: !disabled && !readOnly ? handleInputKeyDown : undefined,\n readOnly,\n ref: inputRef,\n type: 'text',\n value: inputValue ?? '',\n };\n\n const list: ScrollableListPropsWithRef = {\n 'aria-labelledby': ariaLabelledBy,\n data,\n disabled,\n id: listId,\n onChange: handleListboxChange,\n onClick: handleListboxClick,\n ref: listRef,\n scrollOnFocus: false,\n tabIndex: -1,\n value: currentIndex,\n };\n\n const button = {\n ref: buttonRef,\n };\n\n return {\n combobox,\n input,\n list,\n button,\n popover: {\n open,\n onOpenChange: setOpen,\n //visible: !data.length ? false : open,\n },\n };\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const nanoid = require("nanoid");
|
|
4
3
|
const React = require("react");
|
|
4
|
+
const useId = require("../../hooks/useId.cjs");
|
|
5
5
|
const useMergedRef = require("../../hooks/useMergedRef.cjs");
|
|
6
6
|
const useTypeahead = require("./useTypeahead.cjs");
|
|
7
7
|
const util = require("./util.cjs");
|
|
@@ -37,7 +37,7 @@ const useListbox = ({
|
|
|
37
37
|
...otherProps
|
|
38
38
|
}, ref) => {
|
|
39
39
|
const data = util.useFlattenedData(emptyValue === void 0 ? externalData : [{ text: "", value: emptyValue }, ...externalData]);
|
|
40
|
-
const id =
|
|
40
|
+
const id = useId.useId(nativeId);
|
|
41
41
|
const inputRef = useMergedRef.useMergedRef(ref);
|
|
42
42
|
const currentIndex = value === void 0 ? void 0 : util.getIndexFromValue(data, value);
|
|
43
43
|
const { getNextIndex } = useTypeahead.useTypeahead({ data, currentIndex });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useListbox.cjs","sources":["../../../src/components/Listbox/useListbox.tsx"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"useListbox.cjs","sources":["../../../src/components/Listbox/useListbox.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { useId } from '@/hooks/useId';\nimport { useMergedRef } from '@/hooks/useMergedRef';\n\nimport type { ListboxProps } from './Listbox';\nimport type { ScrollableListProps } from './ScrollableList';\nimport { useTypeahead } from './useTypeahead';\nimport { setInputValueByRef, getIndexFromValue, findByValue, getActiveDescendant, useFlattenedData, sanitizeItem } from './util';\n\ntype useListbox = {\n list: ScrollableListProps;\n input: Omit<React.HTMLAttributes<HTMLInputElement>, 'defaultValue'>;\n};\n\nexport const useListbox = (\n {\n data: externalData = [],\n defaultValue,\n disabled,\n emptyValue,\n id: nativeId,\n invalid,\n name,\n onChange,\n onFocus,\n onKeyDown,\n value = emptyValue,\n ...otherProps\n }: ListboxProps,\n ref: React.Ref<HTMLInputElement>\n): useListbox => {\n const data = useFlattenedData(emptyValue === undefined ? externalData : [{ text: '', value: emptyValue }, ...externalData]);\n const id = useId(nativeId);\n const inputRef = useMergedRef<HTMLInputElement>(ref);\n const currentIndex = value === undefined ? undefined : getIndexFromValue(data, value);\n const { getNextIndex } = useTypeahead({ data, currentIndex });\n\n const setInputValueByIndex = (index: number | undefined): void => {\n if (index !== undefined) {\n const option = data[index];\n\n if (option && !option.disabled) {\n setInputValueByRef(inputRef.current, option.value);\n }\n }\n };\n\n const handleListboxChange = (index: number): void => {\n setInputValueByIndex(index);\n };\n\n React.useEffect(() => {\n if (data.length && currentIndex === undefined) {\n if (defaultValue === undefined) {\n setInputValueByIndex(0);\n } else {\n const defaultValueIndex = getIndexFromValue(data, defaultValue);\n\n if (defaultValueIndex !== undefined) {\n setInputValueByIndex(defaultValueIndex);\n }\n }\n }\n }, [data]);\n\n const handleListboxKeyDown = (event: React.KeyboardEvent<HTMLElement>): void => {\n const charKey = event.key;\n\n // event.key always returns the whole string (String.fromCharCode(event.keyCode) does not)\n // We need to filter out only alphabetical or numeric letters, that's why that condition has been changed\n if (charKey.length === 1 && /(\\w)/gu.test(charKey)) {\n const nextIndex = getNextIndex(charKey);\n\n if (nextIndex > -1 && nextIndex !== currentIndex) {\n setInputValueByIndex(nextIndex);\n }\n return;\n }\n\n if (onKeyDown) {\n event.persist();\n onKeyDown(event);\n }\n };\n\n const handleListboxFocus = (event: React.FocusEvent<HTMLElement>): void => {\n if (currentIndex === undefined && data.length) {\n setInputValueByIndex(0);\n }\n\n if (onFocus) {\n event.persist();\n onFocus(event);\n }\n };\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n event.persist();\n\n if (onChange) {\n const item = findByValue(data, event.target.value);\n (event as any).detail = sanitizeItem(item);\n\n const indexes = item?.path?.split('.') ?? [];\n\n if (indexes.length > 1) {\n // we don't want to map the current item\n indexes.pop();\n // we need to rebuild the path as we map\n let lastPath: string;\n\n (event as any).detail.parents = indexes.map(i => {\n lastPath = lastPath ? [lastPath, i].join('.') : i;\n return sanitizeItem(data.find(i => i.path === lastPath));\n });\n }\n\n onChange(event);\n }\n };\n\n const list: ScrollableListProps = {\n ...otherProps,\n 'aria-activedescendant': getActiveDescendant(data, currentIndex, id),\n data,\n disabled,\n id,\n invalid,\n onChange: handleListboxChange,\n onFocus: handleListboxFocus,\n onKeyDown: handleListboxKeyDown,\n scrollOnFocus: true,\n tabIndex: disabled ? -1 : otherProps.tabIndex ? otherProps.tabIndex : 0,\n value: currentIndex,\n };\n\n const input = {\n name,\n onChange: handleInputChange,\n ref: inputRef,\n tabIndex: -1,\n value: value ?? '',\n };\n\n return { list, input };\n};\n"],"names":["useFlattenedData","useId","useMergedRef","getIndexFromValue","useTypeahead","setInputValueByRef","React","findByValue","sanitizeItem","i","getActiveDescendant"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { nanoid } from "nanoid";
|
|
2
1
|
import * as React from "react";
|
|
2
|
+
import { useId } from "../../hooks/useId.js";
|
|
3
3
|
import { useMergedRef } from "../../hooks/useMergedRef.js";
|
|
4
4
|
import { useTypeahead } from "./useTypeahead.js";
|
|
5
5
|
import { useFlattenedData, getIndexFromValue, getActiveDescendant, setInputValueByRef, findByValue, sanitizeItem } from "./util.js";
|
|
@@ -18,7 +18,7 @@ const useListbox = ({
|
|
|
18
18
|
...otherProps
|
|
19
19
|
}, ref) => {
|
|
20
20
|
const data = useFlattenedData(emptyValue === void 0 ? externalData : [{ text: "", value: emptyValue }, ...externalData]);
|
|
21
|
-
const id =
|
|
21
|
+
const id = useId(nativeId);
|
|
22
22
|
const inputRef = useMergedRef(ref);
|
|
23
23
|
const currentIndex = value === void 0 ? void 0 : getIndexFromValue(data, value);
|
|
24
24
|
const { getNextIndex } = useTypeahead({ data, currentIndex });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useListbox.js","sources":["../../../src/components/Listbox/useListbox.tsx"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"useListbox.js","sources":["../../../src/components/Listbox/useListbox.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { useId } from '@/hooks/useId';\nimport { useMergedRef } from '@/hooks/useMergedRef';\n\nimport type { ListboxProps } from './Listbox';\nimport type { ScrollableListProps } from './ScrollableList';\nimport { useTypeahead } from './useTypeahead';\nimport { setInputValueByRef, getIndexFromValue, findByValue, getActiveDescendant, useFlattenedData, sanitizeItem } from './util';\n\ntype useListbox = {\n list: ScrollableListProps;\n input: Omit<React.HTMLAttributes<HTMLInputElement>, 'defaultValue'>;\n};\n\nexport const useListbox = (\n {\n data: externalData = [],\n defaultValue,\n disabled,\n emptyValue,\n id: nativeId,\n invalid,\n name,\n onChange,\n onFocus,\n onKeyDown,\n value = emptyValue,\n ...otherProps\n }: ListboxProps,\n ref: React.Ref<HTMLInputElement>\n): useListbox => {\n const data = useFlattenedData(emptyValue === undefined ? externalData : [{ text: '', value: emptyValue }, ...externalData]);\n const id = useId(nativeId);\n const inputRef = useMergedRef<HTMLInputElement>(ref);\n const currentIndex = value === undefined ? undefined : getIndexFromValue(data, value);\n const { getNextIndex } = useTypeahead({ data, currentIndex });\n\n const setInputValueByIndex = (index: number | undefined): void => {\n if (index !== undefined) {\n const option = data[index];\n\n if (option && !option.disabled) {\n setInputValueByRef(inputRef.current, option.value);\n }\n }\n };\n\n const handleListboxChange = (index: number): void => {\n setInputValueByIndex(index);\n };\n\n React.useEffect(() => {\n if (data.length && currentIndex === undefined) {\n if (defaultValue === undefined) {\n setInputValueByIndex(0);\n } else {\n const defaultValueIndex = getIndexFromValue(data, defaultValue);\n\n if (defaultValueIndex !== undefined) {\n setInputValueByIndex(defaultValueIndex);\n }\n }\n }\n }, [data]);\n\n const handleListboxKeyDown = (event: React.KeyboardEvent<HTMLElement>): void => {\n const charKey = event.key;\n\n // event.key always returns the whole string (String.fromCharCode(event.keyCode) does not)\n // We need to filter out only alphabetical or numeric letters, that's why that condition has been changed\n if (charKey.length === 1 && /(\\w)/gu.test(charKey)) {\n const nextIndex = getNextIndex(charKey);\n\n if (nextIndex > -1 && nextIndex !== currentIndex) {\n setInputValueByIndex(nextIndex);\n }\n return;\n }\n\n if (onKeyDown) {\n event.persist();\n onKeyDown(event);\n }\n };\n\n const handleListboxFocus = (event: React.FocusEvent<HTMLElement>): void => {\n if (currentIndex === undefined && data.length) {\n setInputValueByIndex(0);\n }\n\n if (onFocus) {\n event.persist();\n onFocus(event);\n }\n };\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n event.persist();\n\n if (onChange) {\n const item = findByValue(data, event.target.value);\n (event as any).detail = sanitizeItem(item);\n\n const indexes = item?.path?.split('.') ?? [];\n\n if (indexes.length > 1) {\n // we don't want to map the current item\n indexes.pop();\n // we need to rebuild the path as we map\n let lastPath: string;\n\n (event as any).detail.parents = indexes.map(i => {\n lastPath = lastPath ? [lastPath, i].join('.') : i;\n return sanitizeItem(data.find(i => i.path === lastPath));\n });\n }\n\n onChange(event);\n }\n };\n\n const list: ScrollableListProps = {\n ...otherProps,\n 'aria-activedescendant': getActiveDescendant(data, currentIndex, id),\n data,\n disabled,\n id,\n invalid,\n onChange: handleListboxChange,\n onFocus: handleListboxFocus,\n onKeyDown: handleListboxKeyDown,\n scrollOnFocus: true,\n tabIndex: disabled ? -1 : otherProps.tabIndex ? otherProps.tabIndex : 0,\n value: currentIndex,\n };\n\n const input = {\n name,\n onChange: handleInputChange,\n ref: inputRef,\n tabIndex: -1,\n value: value ?? '',\n };\n\n return { list, input };\n};\n"],"names":["i"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const nanoid = require("nanoid");
|
|
4
3
|
const React = require("react");
|
|
4
|
+
const useId = require("../../hooks/useId.cjs");
|
|
5
5
|
const useMergedRef = require("../../hooks/useMergedRef.cjs");
|
|
6
6
|
const useTypeahead = require("./useTypeahead.cjs");
|
|
7
7
|
const util = require("./util.cjs");
|
|
@@ -38,7 +38,7 @@ const useMultiListbox = ({
|
|
|
38
38
|
...otherProps
|
|
39
39
|
}, ref) => {
|
|
40
40
|
const { texts } = Localization.useLocalization();
|
|
41
|
-
const id =
|
|
41
|
+
const id = useId.useId(nativeId);
|
|
42
42
|
const inputRef = useMergedRef.useMergedRef(ref);
|
|
43
43
|
const data = React__namespace.useMemo(() => {
|
|
44
44
|
return [{ text: texts.listbox.allOption, value: "#ALL-OPTIONS#" }, ...externalData];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMultiListbox.cjs","sources":["../../../src/components/Listbox/useMultiListbox.tsx"],"sourcesContent":["import { nanoid } from 'nanoid';\nimport * as React from 'react';\n\nimport { useLocalization } from '@/components';\nimport { useMergedRef } from '@/hooks/useMergedRef';\n\nimport type { ListboxProps } from './Listbox';\nimport type { ScrollableListProps, ScrollableListItem, ScrollableListItemValue } from './ScrollableList';\nimport { useTypeahead } from './useTypeahead';\nimport { setInputValueByRef, getIndexFromValue, getSelectedIndexesFromValue, findByValue, getActiveDescendant } from './util';\n\ntype useListbox = {\n list: ScrollableListProps;\n input: Omit<React.HTMLAttributes<HTMLInputElement>, 'defaultValue'>;\n};\n\nexport const useMultiListbox = (\n {\n data: externalData = [],\n defaultValue,\n disabled,\n emptyValue: _,\n id: nativeId,\n name,\n onClick,\n onChange,\n onFocus,\n onKeyDown,\n value,\n ...otherProps\n }: ListboxProps,\n ref: React.Ref<HTMLInputElement>\n): useListbox => {\n const { texts } = useLocalization();\n const id = React.useMemo(() => nativeId || nanoid(), [nativeId]);\n const inputRef = useMergedRef<HTMLInputElement>(ref);\n const data = React.useMemo(() => {\n return [{ text: texts.listbox.allOption, value: '#ALL-OPTIONS#' }, ...externalData];\n }, [externalData]);\n const [currentIndex, setCurrentIndex] = React.useState(getIndexFromValue(data, value) || 0);\n const [allOptionsSelected, setAllOptionsSelected] = React.useState(false);\n const { getNextIndex } = useTypeahead({ data, currentIndex });\n const [selectedIndexes, setSelectedIndexes] = React.useState(getSelectedIndexesFromValue(data, value));\n\n React.useEffect(() => {\n if (defaultValue && !value) {\n setInputValueByRef(inputRef.current, defaultValue.toString());\n }\n }, []);\n\n React.useEffect(() => {\n const selectableItemsCount = data.filter(item => !item.disabled).length - 1;\n\n if (selectableItemsCount === selectedIndexes.length) {\n setAllOptionsSelected(true);\n } else {\n setAllOptionsSelected(false);\n }\n }, [selectedIndexes, data]);\n\n const setInputValueByIndex = (index: number | undefined): void => {\n if (index !== undefined) {\n const option = data[index];\n\n if (option && !option.disabled) {\n let newInputValue: ScrollableListItemValue = '';\n const currentInputValue = inputRef.current?.value;\n const currentValuesArray = currentInputValue?.split(',') || [];\n const optionAlreadySelected = currentValuesArray.some(val => val === String(option.value));\n\n // Handling the select_all option\n if (option.value === '#ALL-OPTIONS#') {\n if (allOptionsSelected) {\n newInputValue = '';\n } else {\n newInputValue = data\n .filter((option, index) => index !== 0 && !option.disabled)\n .map(option => option.value)\n .join(',');\n }\n } else if (optionAlreadySelected) {\n newInputValue = currentValuesArray.filter(val => val !== String(option.value)).join(',');\n } else {\n newInputValue = currentInputValue ? `${currentInputValue},${option.value}` : option.value;\n }\n\n setInputValueByRef(inputRef.current, newInputValue);\n }\n }\n };\n\n const handleListboxClick = (event: React.MouseEvent<HTMLLIElement>, index: number): void => {\n event.preventDefault();\n\n if (data[index] && !data[index].disabled) {\n setCurrentIndex(index);\n setInputValueByIndex(index);\n }\n\n if (onClick) {\n onClick(event);\n }\n };\n\n const handleListboxKeyDown = (event: React.KeyboardEvent<HTMLElement>, index?: number): void => {\n const charKey = event.key;\n\n // event.key always returns the whole string (String.fromCharCode(event.keyCode) does not)\n // We need to filter out only alphabetical or numeric letters, that's why that condition has been changed\n if (charKey.length === 1 && /(\\w)/gu.test(charKey)) {\n const nextIndex = getNextIndex(charKey);\n\n if (nextIndex > -1 && nextIndex !== currentIndex) {\n setCurrentIndex(nextIndex);\n }\n return;\n }\n\n switch (event.key) {\n case ' ': {\n event.preventDefault();\n setInputValueByIndex(index);\n break;\n }\n\n case 'Enter': {\n setInputValueByIndex(index);\n break;\n }\n\n default:\n }\n setCurrentIndex(index === undefined ? 0 : index);\n\n if (onKeyDown) {\n event.persist();\n onKeyDown(event);\n }\n };\n\n const handleListboxFocus = (event: React.FocusEvent<HTMLElement>): void => {\n if (onFocus) {\n event.persist();\n onFocus(event);\n }\n };\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n event.persist();\n\n setSelectedIndexes(getSelectedIndexesFromValue(data, event.target.value));\n\n if (onChange) {\n const detail: ScrollableListItem[] = [];\n const valuesArray = event.target.value.split(',');\n\n valuesArray.forEach(val => {\n const item = findByValue(data, val);\n if (item) {\n detail.push(item);\n }\n });\n (event as any).detail = detail;\n\n onChange(event);\n }\n };\n\n const list = {\n ...otherProps,\n 'aria-activedescendant': getActiveDescendant(data, currentIndex, id),\n 'aria-multiselectable': true,\n data,\n disabled,\n id,\n onChange: () => null,\n onClick: handleListboxClick,\n onFocus: handleListboxFocus,\n onKeyDown: handleListboxKeyDown,\n scrollOnFocus: false,\n tabIndex: disabled ? -1 : otherProps.tabIndex ? otherProps.tabIndex : 0,\n value: currentIndex,\n selectedIndexes,\n multiselect: true,\n allOptionsSelected,\n };\n\n const input = {\n name,\n onChange: handleInputChange,\n ref: inputRef,\n tabIndex: -1,\n value: value ?? '',\n };\n\n return { list, input };\n};\n"],"names":["useLocalization","React","nanoid","useMergedRef","getIndexFromValue","useTypeahead","getSelectedIndexesFromValue","setInputValueByRef","option","index","findByValue","getActiveDescendant"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useMultiListbox.cjs","sources":["../../../src/components/Listbox/useMultiListbox.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { useLocalization } from '@/components';\nimport { useId } from '@/hooks/useId';\nimport { useMergedRef } from '@/hooks/useMergedRef';\n\nimport type { ListboxProps } from './Listbox';\nimport type { ScrollableListProps, ScrollableListItem, ScrollableListItemValue } from './ScrollableList';\nimport { useTypeahead } from './useTypeahead';\nimport { setInputValueByRef, getIndexFromValue, getSelectedIndexesFromValue, findByValue, getActiveDescendant } from './util';\n\ntype useListbox = {\n list: ScrollableListProps;\n input: Omit<React.HTMLAttributes<HTMLInputElement>, 'defaultValue'>;\n};\n\nexport const useMultiListbox = (\n {\n data: externalData = [],\n defaultValue,\n disabled,\n emptyValue: _,\n id: nativeId,\n name,\n onClick,\n onChange,\n onFocus,\n onKeyDown,\n value,\n ...otherProps\n }: ListboxProps,\n ref: React.Ref<HTMLInputElement>\n): useListbox => {\n const { texts } = useLocalization();\n const id = useId(nativeId);\n const inputRef = useMergedRef<HTMLInputElement>(ref);\n const data = React.useMemo(() => {\n return [{ text: texts.listbox.allOption, value: '#ALL-OPTIONS#' }, ...externalData];\n }, [externalData]);\n const [currentIndex, setCurrentIndex] = React.useState(getIndexFromValue(data, value) || 0);\n const [allOptionsSelected, setAllOptionsSelected] = React.useState(false);\n const { getNextIndex } = useTypeahead({ data, currentIndex });\n const [selectedIndexes, setSelectedIndexes] = React.useState(getSelectedIndexesFromValue(data, value));\n\n React.useEffect(() => {\n if (defaultValue && !value) {\n setInputValueByRef(inputRef.current, defaultValue.toString());\n }\n }, []);\n\n React.useEffect(() => {\n const selectableItemsCount = data.filter(item => !item.disabled).length - 1;\n\n if (selectableItemsCount === selectedIndexes.length) {\n setAllOptionsSelected(true);\n } else {\n setAllOptionsSelected(false);\n }\n }, [selectedIndexes, data]);\n\n const setInputValueByIndex = (index: number | undefined): void => {\n if (index !== undefined) {\n const option = data[index];\n\n if (option && !option.disabled) {\n let newInputValue: ScrollableListItemValue = '';\n const currentInputValue = inputRef.current?.value;\n const currentValuesArray = currentInputValue?.split(',') || [];\n const optionAlreadySelected = currentValuesArray.some(val => val === String(option.value));\n\n // Handling the select_all option\n if (option.value === '#ALL-OPTIONS#') {\n if (allOptionsSelected) {\n newInputValue = '';\n } else {\n newInputValue = data\n .filter((option, index) => index !== 0 && !option.disabled)\n .map(option => option.value)\n .join(',');\n }\n } else if (optionAlreadySelected) {\n newInputValue = currentValuesArray.filter(val => val !== String(option.value)).join(',');\n } else {\n newInputValue = currentInputValue ? `${currentInputValue},${option.value}` : option.value;\n }\n\n setInputValueByRef(inputRef.current, newInputValue);\n }\n }\n };\n\n const handleListboxClick = (event: React.MouseEvent<HTMLLIElement>, index: number): void => {\n event.preventDefault();\n\n if (data[index] && !data[index].disabled) {\n setCurrentIndex(index);\n setInputValueByIndex(index);\n }\n\n if (onClick) {\n onClick(event);\n }\n };\n\n const handleListboxKeyDown = (event: React.KeyboardEvent<HTMLElement>, index?: number): void => {\n const charKey = event.key;\n\n // event.key always returns the whole string (String.fromCharCode(event.keyCode) does not)\n // We need to filter out only alphabetical or numeric letters, that's why that condition has been changed\n if (charKey.length === 1 && /(\\w)/gu.test(charKey)) {\n const nextIndex = getNextIndex(charKey);\n\n if (nextIndex > -1 && nextIndex !== currentIndex) {\n setCurrentIndex(nextIndex);\n }\n return;\n }\n\n switch (event.key) {\n case ' ': {\n event.preventDefault();\n setInputValueByIndex(index);\n break;\n }\n\n case 'Enter': {\n setInputValueByIndex(index);\n break;\n }\n\n default:\n }\n setCurrentIndex(index === undefined ? 0 : index);\n\n if (onKeyDown) {\n event.persist();\n onKeyDown(event);\n }\n };\n\n const handleListboxFocus = (event: React.FocusEvent<HTMLElement>): void => {\n if (onFocus) {\n event.persist();\n onFocus(event);\n }\n };\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n event.persist();\n\n setSelectedIndexes(getSelectedIndexesFromValue(data, event.target.value));\n\n if (onChange) {\n const detail: ScrollableListItem[] = [];\n const valuesArray = event.target.value.split(',');\n\n valuesArray.forEach(val => {\n const item = findByValue(data, val);\n if (item) {\n detail.push(item);\n }\n });\n (event as any).detail = detail;\n\n onChange(event);\n }\n };\n\n const list = {\n ...otherProps,\n 'aria-activedescendant': getActiveDescendant(data, currentIndex, id),\n 'aria-multiselectable': true,\n data,\n disabled,\n id,\n onChange: () => null,\n onClick: handleListboxClick,\n onFocus: handleListboxFocus,\n onKeyDown: handleListboxKeyDown,\n scrollOnFocus: false,\n tabIndex: disabled ? -1 : otherProps.tabIndex ? otherProps.tabIndex : 0,\n value: currentIndex,\n selectedIndexes,\n multiselect: true,\n allOptionsSelected,\n };\n\n const input = {\n name,\n onChange: handleInputChange,\n ref: inputRef,\n tabIndex: -1,\n value: value ?? '',\n };\n\n return { list, input };\n};\n"],"names":["useLocalization","useId","useMergedRef","React","getIndexFromValue","useTypeahead","getSelectedIndexesFromValue","setInputValueByRef","option","index","findByValue","getActiveDescendant"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { nanoid } from "nanoid";
|
|
2
1
|
import * as React from "react";
|
|
2
|
+
import { useId } from "../../hooks/useId.js";
|
|
3
3
|
import { useMergedRef } from "../../hooks/useMergedRef.js";
|
|
4
4
|
import { useTypeahead } from "./useTypeahead.js";
|
|
5
5
|
import { getIndexFromValue, getSelectedIndexesFromValue, setInputValueByRef, getActiveDescendant, findByValue } from "./util.js";
|
|
@@ -19,7 +19,7 @@ const useMultiListbox = ({
|
|
|
19
19
|
...otherProps
|
|
20
20
|
}, ref) => {
|
|
21
21
|
const { texts } = useLocalization();
|
|
22
|
-
const id =
|
|
22
|
+
const id = useId(nativeId);
|
|
23
23
|
const inputRef = useMergedRef(ref);
|
|
24
24
|
const data = React.useMemo(() => {
|
|
25
25
|
return [{ text: texts.listbox.allOption, value: "#ALL-OPTIONS#" }, ...externalData];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMultiListbox.js","sources":["../../../src/components/Listbox/useMultiListbox.tsx"],"sourcesContent":["import { nanoid } from 'nanoid';\nimport * as React from 'react';\n\nimport { useLocalization } from '@/components';\nimport { useMergedRef } from '@/hooks/useMergedRef';\n\nimport type { ListboxProps } from './Listbox';\nimport type { ScrollableListProps, ScrollableListItem, ScrollableListItemValue } from './ScrollableList';\nimport { useTypeahead } from './useTypeahead';\nimport { setInputValueByRef, getIndexFromValue, getSelectedIndexesFromValue, findByValue, getActiveDescendant } from './util';\n\ntype useListbox = {\n list: ScrollableListProps;\n input: Omit<React.HTMLAttributes<HTMLInputElement>, 'defaultValue'>;\n};\n\nexport const useMultiListbox = (\n {\n data: externalData = [],\n defaultValue,\n disabled,\n emptyValue: _,\n id: nativeId,\n name,\n onClick,\n onChange,\n onFocus,\n onKeyDown,\n value,\n ...otherProps\n }: ListboxProps,\n ref: React.Ref<HTMLInputElement>\n): useListbox => {\n const { texts } = useLocalization();\n const id = React.useMemo(() => nativeId || nanoid(), [nativeId]);\n const inputRef = useMergedRef<HTMLInputElement>(ref);\n const data = React.useMemo(() => {\n return [{ text: texts.listbox.allOption, value: '#ALL-OPTIONS#' }, ...externalData];\n }, [externalData]);\n const [currentIndex, setCurrentIndex] = React.useState(getIndexFromValue(data, value) || 0);\n const [allOptionsSelected, setAllOptionsSelected] = React.useState(false);\n const { getNextIndex } = useTypeahead({ data, currentIndex });\n const [selectedIndexes, setSelectedIndexes] = React.useState(getSelectedIndexesFromValue(data, value));\n\n React.useEffect(() => {\n if (defaultValue && !value) {\n setInputValueByRef(inputRef.current, defaultValue.toString());\n }\n }, []);\n\n React.useEffect(() => {\n const selectableItemsCount = data.filter(item => !item.disabled).length - 1;\n\n if (selectableItemsCount === selectedIndexes.length) {\n setAllOptionsSelected(true);\n } else {\n setAllOptionsSelected(false);\n }\n }, [selectedIndexes, data]);\n\n const setInputValueByIndex = (index: number | undefined): void => {\n if (index !== undefined) {\n const option = data[index];\n\n if (option && !option.disabled) {\n let newInputValue: ScrollableListItemValue = '';\n const currentInputValue = inputRef.current?.value;\n const currentValuesArray = currentInputValue?.split(',') || [];\n const optionAlreadySelected = currentValuesArray.some(val => val === String(option.value));\n\n // Handling the select_all option\n if (option.value === '#ALL-OPTIONS#') {\n if (allOptionsSelected) {\n newInputValue = '';\n } else {\n newInputValue = data\n .filter((option, index) => index !== 0 && !option.disabled)\n .map(option => option.value)\n .join(',');\n }\n } else if (optionAlreadySelected) {\n newInputValue = currentValuesArray.filter(val => val !== String(option.value)).join(',');\n } else {\n newInputValue = currentInputValue ? `${currentInputValue},${option.value}` : option.value;\n }\n\n setInputValueByRef(inputRef.current, newInputValue);\n }\n }\n };\n\n const handleListboxClick = (event: React.MouseEvent<HTMLLIElement>, index: number): void => {\n event.preventDefault();\n\n if (data[index] && !data[index].disabled) {\n setCurrentIndex(index);\n setInputValueByIndex(index);\n }\n\n if (onClick) {\n onClick(event);\n }\n };\n\n const handleListboxKeyDown = (event: React.KeyboardEvent<HTMLElement>, index?: number): void => {\n const charKey = event.key;\n\n // event.key always returns the whole string (String.fromCharCode(event.keyCode) does not)\n // We need to filter out only alphabetical or numeric letters, that's why that condition has been changed\n if (charKey.length === 1 && /(\\w)/gu.test(charKey)) {\n const nextIndex = getNextIndex(charKey);\n\n if (nextIndex > -1 && nextIndex !== currentIndex) {\n setCurrentIndex(nextIndex);\n }\n return;\n }\n\n switch (event.key) {\n case ' ': {\n event.preventDefault();\n setInputValueByIndex(index);\n break;\n }\n\n case 'Enter': {\n setInputValueByIndex(index);\n break;\n }\n\n default:\n }\n setCurrentIndex(index === undefined ? 0 : index);\n\n if (onKeyDown) {\n event.persist();\n onKeyDown(event);\n }\n };\n\n const handleListboxFocus = (event: React.FocusEvent<HTMLElement>): void => {\n if (onFocus) {\n event.persist();\n onFocus(event);\n }\n };\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n event.persist();\n\n setSelectedIndexes(getSelectedIndexesFromValue(data, event.target.value));\n\n if (onChange) {\n const detail: ScrollableListItem[] = [];\n const valuesArray = event.target.value.split(',');\n\n valuesArray.forEach(val => {\n const item = findByValue(data, val);\n if (item) {\n detail.push(item);\n }\n });\n (event as any).detail = detail;\n\n onChange(event);\n }\n };\n\n const list = {\n ...otherProps,\n 'aria-activedescendant': getActiveDescendant(data, currentIndex, id),\n 'aria-multiselectable': true,\n data,\n disabled,\n id,\n onChange: () => null,\n onClick: handleListboxClick,\n onFocus: handleListboxFocus,\n onKeyDown: handleListboxKeyDown,\n scrollOnFocus: false,\n tabIndex: disabled ? -1 : otherProps.tabIndex ? otherProps.tabIndex : 0,\n value: currentIndex,\n selectedIndexes,\n multiselect: true,\n allOptionsSelected,\n };\n\n const input = {\n name,\n onChange: handleInputChange,\n ref: inputRef,\n tabIndex: -1,\n value: value ?? '',\n };\n\n return { list, input };\n};\n"],"names":["option","index"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useMultiListbox.js","sources":["../../../src/components/Listbox/useMultiListbox.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { useLocalization } from '@/components';\nimport { useId } from '@/hooks/useId';\nimport { useMergedRef } from '@/hooks/useMergedRef';\n\nimport type { ListboxProps } from './Listbox';\nimport type { ScrollableListProps, ScrollableListItem, ScrollableListItemValue } from './ScrollableList';\nimport { useTypeahead } from './useTypeahead';\nimport { setInputValueByRef, getIndexFromValue, getSelectedIndexesFromValue, findByValue, getActiveDescendant } from './util';\n\ntype useListbox = {\n list: ScrollableListProps;\n input: Omit<React.HTMLAttributes<HTMLInputElement>, 'defaultValue'>;\n};\n\nexport const useMultiListbox = (\n {\n data: externalData = [],\n defaultValue,\n disabled,\n emptyValue: _,\n id: nativeId,\n name,\n onClick,\n onChange,\n onFocus,\n onKeyDown,\n value,\n ...otherProps\n }: ListboxProps,\n ref: React.Ref<HTMLInputElement>\n): useListbox => {\n const { texts } = useLocalization();\n const id = useId(nativeId);\n const inputRef = useMergedRef<HTMLInputElement>(ref);\n const data = React.useMemo(() => {\n return [{ text: texts.listbox.allOption, value: '#ALL-OPTIONS#' }, ...externalData];\n }, [externalData]);\n const [currentIndex, setCurrentIndex] = React.useState(getIndexFromValue(data, value) || 0);\n const [allOptionsSelected, setAllOptionsSelected] = React.useState(false);\n const { getNextIndex } = useTypeahead({ data, currentIndex });\n const [selectedIndexes, setSelectedIndexes] = React.useState(getSelectedIndexesFromValue(data, value));\n\n React.useEffect(() => {\n if (defaultValue && !value) {\n setInputValueByRef(inputRef.current, defaultValue.toString());\n }\n }, []);\n\n React.useEffect(() => {\n const selectableItemsCount = data.filter(item => !item.disabled).length - 1;\n\n if (selectableItemsCount === selectedIndexes.length) {\n setAllOptionsSelected(true);\n } else {\n setAllOptionsSelected(false);\n }\n }, [selectedIndexes, data]);\n\n const setInputValueByIndex = (index: number | undefined): void => {\n if (index !== undefined) {\n const option = data[index];\n\n if (option && !option.disabled) {\n let newInputValue: ScrollableListItemValue = '';\n const currentInputValue = inputRef.current?.value;\n const currentValuesArray = currentInputValue?.split(',') || [];\n const optionAlreadySelected = currentValuesArray.some(val => val === String(option.value));\n\n // Handling the select_all option\n if (option.value === '#ALL-OPTIONS#') {\n if (allOptionsSelected) {\n newInputValue = '';\n } else {\n newInputValue = data\n .filter((option, index) => index !== 0 && !option.disabled)\n .map(option => option.value)\n .join(',');\n }\n } else if (optionAlreadySelected) {\n newInputValue = currentValuesArray.filter(val => val !== String(option.value)).join(',');\n } else {\n newInputValue = currentInputValue ? `${currentInputValue},${option.value}` : option.value;\n }\n\n setInputValueByRef(inputRef.current, newInputValue);\n }\n }\n };\n\n const handleListboxClick = (event: React.MouseEvent<HTMLLIElement>, index: number): void => {\n event.preventDefault();\n\n if (data[index] && !data[index].disabled) {\n setCurrentIndex(index);\n setInputValueByIndex(index);\n }\n\n if (onClick) {\n onClick(event);\n }\n };\n\n const handleListboxKeyDown = (event: React.KeyboardEvent<HTMLElement>, index?: number): void => {\n const charKey = event.key;\n\n // event.key always returns the whole string (String.fromCharCode(event.keyCode) does not)\n // We need to filter out only alphabetical or numeric letters, that's why that condition has been changed\n if (charKey.length === 1 && /(\\w)/gu.test(charKey)) {\n const nextIndex = getNextIndex(charKey);\n\n if (nextIndex > -1 && nextIndex !== currentIndex) {\n setCurrentIndex(nextIndex);\n }\n return;\n }\n\n switch (event.key) {\n case ' ': {\n event.preventDefault();\n setInputValueByIndex(index);\n break;\n }\n\n case 'Enter': {\n setInputValueByIndex(index);\n break;\n }\n\n default:\n }\n setCurrentIndex(index === undefined ? 0 : index);\n\n if (onKeyDown) {\n event.persist();\n onKeyDown(event);\n }\n };\n\n const handleListboxFocus = (event: React.FocusEvent<HTMLElement>): void => {\n if (onFocus) {\n event.persist();\n onFocus(event);\n }\n };\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n event.persist();\n\n setSelectedIndexes(getSelectedIndexesFromValue(data, event.target.value));\n\n if (onChange) {\n const detail: ScrollableListItem[] = [];\n const valuesArray = event.target.value.split(',');\n\n valuesArray.forEach(val => {\n const item = findByValue(data, val);\n if (item) {\n detail.push(item);\n }\n });\n (event as any).detail = detail;\n\n onChange(event);\n }\n };\n\n const list = {\n ...otherProps,\n 'aria-activedescendant': getActiveDescendant(data, currentIndex, id),\n 'aria-multiselectable': true,\n data,\n disabled,\n id,\n onChange: () => null,\n onClick: handleListboxClick,\n onFocus: handleListboxFocus,\n onKeyDown: handleListboxKeyDown,\n scrollOnFocus: false,\n tabIndex: disabled ? -1 : otherProps.tabIndex ? otherProps.tabIndex : 0,\n value: currentIndex,\n selectedIndexes,\n multiselect: true,\n allOptionsSelected,\n };\n\n const input = {\n name,\n onChange: handleInputChange,\n ref: inputRef,\n tabIndex: -1,\n value: value ?? '',\n };\n\n return { list, input };\n};\n"],"names":["option","index"],"mappings":"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"}
|