@majoo-ui/react 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,7 +12,7 @@ import Flex from '../../layout/flex/Flex.esm.js';
12
12
  import { Grid, GridItem } from '../../layout/grid/Grid.esm.js';
13
13
  import Collapalse from './transition.esm.js';
14
14
  import { TableHeaderCellContainer, TableHeadCellContent } from '../table/Table.esm.js';
15
- import { useTranslation } from 'react-i18next';
15
+ import { defaultTableLocaleText } from '../table/locale.esm.js';
16
16
 
17
17
  const TableContainer = styled('div', {
18
18
  display: 'block',
@@ -101,7 +101,8 @@ function EditableTableMobile(props) {
101
101
  onExpand,
102
102
  hiddenColumns,
103
103
  customEmptyDataProps,
104
- virtualized
104
+ virtualized,
105
+ sortColumnTitle = defaultTableLocaleText.sortColumnTitle
105
106
  } = props;
106
107
  const {
107
108
  getTableProps,
@@ -113,9 +114,6 @@ function EditableTableMobile(props) {
113
114
  toggleAllRowsExpanded,
114
115
  setHiddenColumns
115
116
  } = instance;
116
- const {
117
- t
118
- } = useTranslation('MajooUI/table');
119
117
  useEffect(() => {
120
118
  if (hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.length) {
121
119
  setHiddenColumns(hiddenColumns);
@@ -275,7 +273,7 @@ function EditableTableMobile(props) {
275
273
  const isSelector = (column === null || column === void 0 ? void 0 : column.id) === 'selection';
276
274
  const headerRender = column.render('Header');
277
275
  return (isMobileHeader || isSelector) && (/*#__PURE__*/createElement(TableHeadCell, Object.assign({}, column.getHeaderProps(props => [props, column.sortable ? column.getSortByToggleProps({
278
- title: t('sortColumnTitle', 'Urutkan') || 'Urutkan'
276
+ title: sortColumnTitle
279
277
  }) : {
280
278
  onClick: column.onToggle
281
279
  }, {
@@ -15,9 +15,9 @@ import InputCheckbox from '../form/components/InputCheckbox/InputCheckbox.esm.js
15
15
  import Flex from '../../layout/flex/Flex.esm.js';
16
16
  import useMediaQuery from '../../utils/useMediaQuery.esm.js';
17
17
  import EditableTableMobile from './EditableMobile.esm.js';
18
- import { useTranslation } from 'react-i18next';
19
18
  import { pick, isEqual } from 'lodash';
20
19
  import { defaultEmptyDataProps, TableHeaderCellContainer, TableHeadCellContent } from '../table/Table.esm.js';
20
+ import { useTableLocaleText } from '../table/locale.esm.js';
21
21
  import { useVirtual } from 'react-virtual';
22
22
  import Tooltip from '../tooltip/Tooltip.esm.js';
23
23
 
@@ -309,11 +309,10 @@ function EditableTable(props) {
309
309
  hiddenColumns,
310
310
  customEmptyDataProps: emptyDataProps,
311
311
  virtualized = false,
312
- onSortByChange
312
+ onSortByChange,
313
+ localeText
313
314
  } = props;
314
- const {
315
- t
316
- } = useTranslation('MajooUI/table');
315
+ const tableLocaleText = useTableLocaleText(localeText);
317
316
  const [selectedIndex, setSelectedIndex] = useState(null);
318
317
  const isMobile = useMediaQuery(isMobileLayout === undefined ? '(max-width: 767px)' : isMobileLayout ? '(max-width: 8000px)' : '(max-width: 0px)');
319
318
  const IndeterminateCheckbox = props => {
@@ -403,7 +402,7 @@ function EditableTable(props) {
403
402
  });
404
403
  }
405
404
  }, [selectedRowIds, data, onSelectedChange, selectedIndex]);
406
- const customEmptyDataProps = emptyDataProps || defaultEmptyDataProps(t, isMobile);
405
+ const customEmptyDataProps = emptyDataProps || defaultEmptyDataProps(tableLocaleText, isMobile);
407
406
  const getCustomRows = (row, renderRow) => {
408
407
  var _a;
409
408
  let customStyle = {};
@@ -473,7 +472,7 @@ function EditableTable(props) {
473
472
  children: visibleHeaders.map(column => {
474
473
  const headerRender = column.render('Header');
475
474
  return !(column === null || column === void 0 ? void 0 : column.isMobileOnly) && jsx(TableHeadCell, Object.assign({}, column.getHeaderProps(props => [props, column.sortable ? column.getSortByToggleProps({
476
- title: t('sortColumnTitle', 'Urutkan') || 'Urutkan'
475
+ title: tableLocaleText.sortColumnTitle
477
476
  }) : {
478
477
  onClick: column.onToggle
479
478
  }, {
@@ -597,6 +596,7 @@ function EditableTable(props) {
597
596
  onExpand: onExpand,
598
597
  hiddenColumns: hiddenColumns,
599
598
  customEmptyDataProps: customEmptyDataProps,
599
+ sortColumnTitle: tableLocaleText.sortColumnTitle,
600
600
  virtualized: virtualized,
601
601
  rowVirtualizer: rowVirtualizer,
602
602
  tableContainerRef: tableContainerRef
@@ -219,23 +219,17 @@ const OptionWrapper = styled('div', {
219
219
  backgroundColor: '$bgWhite',
220
220
  boxShadow: '0px 2px 12px #00000014',
221
221
  width: '100%',
222
- maxHeight: '300px',
223
- position: 'absolute',
224
- float: 'left',
225
- top: '100%',
226
- left: '0',
222
+ // In-flow on purpose: Popover's flip/shift/size measure the floating element,
223
+ // which is only as tall as this wrapper. An absolute wrapper measures 0px and
224
+ // the dropdown never flips. The cap shrinks to the space Popover reports.
225
+ maxHeight: 'min(300px, var(--majoo-popover-available-height, 300px))',
226
+ position: 'relative',
227
227
  variants: {
228
+ // Kept for API compatibility only — Popover (floating-ui) owns placement now,
229
+ // treating it as a preference that flips when the preferred side lacks room.
228
230
  placement: {
229
- bottom: {
230
- top: '100%',
231
- left: '0',
232
- transform: 'none'
233
- },
234
- top: {
235
- top: '0',
236
- left: '0',
237
- transform: 'translate(0, -100%)'
238
- }
231
+ bottom: {},
232
+ top: {}
239
233
  },
240
234
  withFooter: {
241
235
  true: {
@@ -443,7 +437,10 @@ const InputSelect = /*#__PURE__*/React.forwardRef((_a, ref) => {
443
437
  return setItem(options);
444
438
  } else {
445
439
  setItem(options.filter(item => {
446
- const nameIsIncluded = item.name.toLowerCase().includes(value.toLocaleLowerCase());
440
+ var _a;
441
+ // textSelector can point at a non-string field (numeric id, null), so never
442
+ // assume item.name is a string here.
443
+ const nameIsIncluded = String((_a = item.name) !== null && _a !== void 0 ? _a : '').toLowerCase().includes(value.toLocaleLowerCase());
447
444
  if (item.render && isFunction(item.render)) {
448
445
  const extractedString = item.render() ? extractStringFromHTML(renderToString(item.render())).join(',') : '';
449
446
  return nameIsIncluded || extractedString.toLowerCase().includes(value.toLocaleLowerCase());
@@ -538,12 +535,23 @@ const InputSelect = /*#__PURE__*/React.forwardRef((_a, ref) => {
538
535
  return addedGroupItems;
539
536
  }, []));
540
537
  }, [item]);
541
- const stateReducer = (_, changes) => {
538
+ const stateReducer = (state, changes) => {
542
539
  switch (changes.type) {
543
540
  case Downshift.stateChangeTypes.blurButton:
544
541
  return {
545
542
  isOpen: true
546
543
  };
544
+ // Downshift wipes selectedItem on Escape whenever its own isOpen is already false,
545
+ // firing onChange(null) on consumers that never asked to clear the selection.
546
+ // Escape only closes the menu and restores the label of the current selection.
547
+ case Downshift.stateChangeTypes.keyDownEscape:
548
+ {
549
+ const nextChanges = Object.assign(Object.assign({}, changes), {
550
+ inputValue: state.selectedItem ? state.selectedItem.name : ''
551
+ });
552
+ delete nextChanges.selectedItem;
553
+ return nextChanges;
554
+ }
547
555
  default:
548
556
  return changes;
549
557
  }
@@ -683,13 +691,8 @@ const InputSelect = /*#__PURE__*/React.forwardRef((_a, ref) => {
683
691
  position: 'absolute',
684
692
  inset: 0,
685
693
  zIndex: '$tooltip',
686
- top: item.length > 0 ? '0px' : placement === 'top' ? '-20px' : '20px',
687
- // bottom: '80px',
688
- width: '100%',
689
- height: scrollerRef.current ? scrollerRef.current.clientHeight : '40px',
690
694
  backgroundColor: '$bgWhite',
691
- opacity: 0.8,
692
- transform: placement === 'top' ? 'translate(0, -100%)' : 'none'
695
+ opacity: 0.8
693
696
  },
694
697
  children: jsx(Text, {
695
698
  variant: "caption",
@@ -699,9 +702,10 @@ const InputSelect = /*#__PURE__*/React.forwardRef((_a, ref) => {
699
702
  condition: renderFooter && typeof renderFooter === 'function',
700
703
  wrapper: child => jsxs(Box, {
701
704
  css: {
702
- position: 'absolute',
703
- float: 'left',
704
- display: isOpen ? 'block' : 'none',
705
+ position: 'relative',
706
+ display: isOpen ? 'flex' : 'none',
707
+ flexDirection: 'column',
708
+ maxHeight: 'var(--majoo-popover-available-height, calc(100vh - 160px))',
705
709
  zIndex: '$popover',
706
710
  width: '100%'
707
711
  },
@@ -714,11 +718,16 @@ const InputSelect = /*#__PURE__*/React.forwardRef((_a, ref) => {
714
718
  }, {
715
719
  suppressRefError: true
716
720
  }), {
717
- css: Object.assign({
721
+ css: Object.assign(Object.assign({
718
722
  display: isOpen ? 'block' : 'none',
719
723
  zIndex: '$popover',
720
- pointerEvents: 'auto'
721
- }, optionCss),
724
+ pointerEvents: 'auto',
725
+ minHeight: isLoading ? '40px' : undefined
726
+ }, renderFooter && typeof renderFooter === 'function' ? {
727
+ flex: 1,
728
+ minHeight: 0,
729
+ maxHeight: 'none'
730
+ } : {}), optionCss),
722
731
  withFooter: renderFooter && typeof renderFooter === 'function',
723
732
  placement: placement,
724
733
  children: [!search && embedSearch && isOpen && jsx(SelectContainer, {
@@ -36,6 +36,9 @@ import { ConditionalWrapper } from '../../../../wrapper/ConditionalWrapper.esm.j
36
36
  import OptionGroupItem from './OptionGroupItem.esm.js';
37
37
  import { useMountedLayoutEffect } from 'react-table';
38
38
  import { useVirtual } from 'react-virtual';
39
+ import { useFloating, autoUpdate } from '@floating-ui/react';
40
+ import { Portal } from '@majoo-ui/helpers';
41
+ import { createPopoverMiddleware } from '../../../popover/middleware.esm.js';
39
42
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
40
43
 
41
44
  const startTransition = React__default.startTransition || (callback => {
@@ -223,34 +226,23 @@ const TagWrapper = styled(Box, {
223
226
  margin: '10px 0',
224
227
  gap: '5px'
225
228
  });
229
+
230
+ // Positioned by floating-ui (see `useFloating` below) and portalled, so it is
231
+ // neither clipped by an `overflow: hidden` ancestor nor pushed off the viewport.
232
+ // Its children stay in-flow: floating-ui measures this element to decide flips.
226
233
  const OptionWrapper = styled('div', {
227
234
  display: 'none',
228
235
  overflow: 'hidden',
229
- zIndex: 12,
236
+ zIndex: '$tooltip',
237
+ pointerEvents: 'auto',
230
238
  borderRadius: '$sm',
231
239
  backgroundColor: '$bgWhite',
232
240
  boxShadow: '0px 2px 12px #00000014',
233
- width: '100%',
234
- // height: '160px',
235
- position: 'absolute',
236
- float: 'left',
237
- // top: '40px',
238
- left: '0px',
241
+ width: 'var(--majoo-popover-reference-width, 100%)',
239
242
  color: '$textPrimary',
240
- variants: {
241
- placement: {
242
- bottom: {
243
- top: '100%',
244
- left: '0',
245
- transform: 'none'
246
- },
247
- top: {
248
- top: '0',
249
- left: '0',
250
- transform: 'translate(0, -100%)'
251
- }
252
- }
253
- }
243
+ fontFamily: 'inherit',
244
+ fontSize: '$short-content-regular',
245
+ textAlign: 'left'
254
246
  });
255
247
  const Option = styled('div', {
256
248
  flexDirection: 'column',
@@ -258,7 +250,7 @@ const Option = styled('div', {
258
250
  borderRadius: '$lg',
259
251
  overflowY: 'auto',
260
252
  padding: '8px 16px',
261
- maxHeight: '160px',
253
+ maxHeight: 'min(160px, var(--majoo-popover-available-height, 160px))',
262
254
  height: 'auto',
263
255
  '@sm': {
264
256
  borderRadius: 'unset'
@@ -815,6 +807,20 @@ const InputSelectCheck = /*#__PURE__*/forwardRef(({
815
807
  }
816
808
  }
817
809
  });
810
+
811
+ // downshift owns open/close and outside-click; floating-ui only positions the
812
+ // menu. The floating ref is attached only while the menu is shown, so a closed
813
+ // select does not keep autoUpdate's scroll/resize listeners alive.
814
+ // `placement` is a preference: the menu flips when that side lacks room.
815
+ const isMenuShown = isOpen && disabled === false;
816
+ const floating = useFloating({
817
+ open: isMenuShown,
818
+ placement,
819
+ whileElementsMounted: autoUpdate,
820
+ middleware: createPopoverMiddleware({
821
+ sideOffset: 4
822
+ })
823
+ });
818
824
  const memoOptions = useMemo(() => {
819
825
  // Skip expensive operations for very large datasets
820
826
  const isVeryLarge = option?.length > 10000;
@@ -1019,6 +1025,7 @@ const InputSelectCheck = /*#__PURE__*/forwardRef(({
1019
1025
  mobileOnly: mobileOnly,
1020
1026
  desktopOnly: desktopOnly,
1021
1027
  children: [/*#__PURE__*/jsxs(Select, {
1028
+ ref: floating.refs.setReference,
1022
1029
  size: size,
1023
1030
  disabled: disabled || readOnly,
1024
1031
  isInvalid: isInvalid,
@@ -1162,152 +1169,153 @@ const InputSelectCheck = /*#__PURE__*/forwardRef(({
1162
1169
  },
1163
1170
  color: "brand",
1164
1171
  children: `${totalOptions ? totalOptions : selectedItems?.filter(item => item?.id !== '!all')?.length} ${labelCounter || t('labelCounterText', 'Item terpilih')}`
1165
- }), /*#__PURE__*/jsxs(OptionWrapper, {
1166
- placement: placement,
1167
- css: {
1168
- display: isOpen && disabled === false ? 'block' : 'none',
1169
- top: size === 'lg' ? '45px' : '35px',
1170
- minHeight: isLoading ? 40 : 0
1171
- },
1172
- children: [isLoading && /*#__PURE__*/jsx(Flex, {
1173
- justify: "center",
1174
- align: "center",
1172
+ }), /*#__PURE__*/jsx(Portal, {
1173
+ children: /*#__PURE__*/jsxs(OptionWrapper, {
1174
+ ref: isMenuShown ? floating.refs.setFloating : null,
1175
+ style: {
1176
+ position: floating.strategy,
1177
+ top: floating.y ?? 0,
1178
+ left: floating.x ?? 0
1179
+ },
1175
1180
  css: {
1176
- position: 'absolute',
1177
- inset: 0,
1178
- zIndex: '$tooltip',
1179
- top: items.length > 0 ? '0px' : placement === 'top' ? '-20px' : '0px',
1180
- // bottom: '80px',
1181
- width: '100%',
1182
- minHeight: 40,
1183
- height: scrollerRef.current ? scrollerRef.current.clientHeight : '40px',
1184
- backgroundColor: '$bgWhite',
1185
- opacity: 0.8,
1186
- transform: placement === 'top' ? 'translate(0, -100%)' : 'none'
1181
+ display: isMenuShown ? 'block' : 'none',
1182
+ minHeight: isLoading ? 40 : 0
1187
1183
  },
1188
- children: /*#__PURE__*/jsx(Text, {
1189
- variant: "caption",
1190
- children: "Loading...."
1191
- })
1192
- }), items.length > 0 || isLoading ? /*#__PURE__*/jsx(Option, {
1193
- ...getMenuProps({
1194
- ref: scrollerRef
1195
- }, {
1196
- suppressRefError: true
1197
- }),
1198
- css: optionListCss,
1199
- children: /*#__PURE__*/jsx(ConditionalWrapper, {
1200
- condition: !!fetchData && items.length > 0,
1201
- wrapper: child => /*#__PURE__*/jsx(InfiniteScroll, {
1202
- loadMore: () => {
1203
- if (!isLoading) {
1204
- if (scrollerRef.current) {
1205
- scrollerRef.current.scrollBy(0, -5);
1184
+ children: [isLoading && /*#__PURE__*/jsx(Flex, {
1185
+ justify: "center",
1186
+ align: "center",
1187
+ css: {
1188
+ position: 'absolute',
1189
+ inset: 0,
1190
+ zIndex: '$tooltip',
1191
+ minHeight: 40,
1192
+ backgroundColor: '$bgWhite',
1193
+ opacity: 0.8
1194
+ },
1195
+ children: /*#__PURE__*/jsx(Text, {
1196
+ variant: "caption",
1197
+ children: "Loading...."
1198
+ })
1199
+ }), items.length > 0 || isLoading ? /*#__PURE__*/jsx(Option, {
1200
+ ...getMenuProps({
1201
+ ref: scrollerRef
1202
+ }, {
1203
+ suppressRefError: true
1204
+ }),
1205
+ css: optionListCss,
1206
+ children: /*#__PURE__*/jsx(ConditionalWrapper, {
1207
+ condition: !!fetchData && items.length > 0,
1208
+ wrapper: child => /*#__PURE__*/jsx(InfiniteScroll, {
1209
+ loadMore: () => {
1210
+ if (!isLoading) {
1211
+ if (scrollerRef.current) {
1212
+ scrollerRef.current.scrollBy(0, -5);
1213
+ }
1214
+ fetchData?.({
1215
+ query
1216
+ });
1206
1217
  }
1207
- fetchData?.({
1208
- query
1209
- });
1210
- }
1211
- },
1212
- hasMore: true,
1213
- threshold: 0,
1214
- useWindow: false,
1215
- children: child
1216
- }, isLoading),
1217
- children: isOpen && shouldVirtualize ?
1218
- /*#__PURE__*/
1219
- // Virtualized rendering for large lists
1220
- jsx(Fragment, {
1221
- children: /*#__PURE__*/jsx("div", {
1222
- style: {
1223
- height: `${rowVirtualizer.totalSize}px`,
1224
- position: 'relative',
1225
- width: '100%'
1226
1218
  },
1227
- children: rowVirtualizer.virtualItems.map(virtualRow => {
1228
- const virtualItem = getItemAtIndex(virtualRow.index);
1229
- if (!virtualItem) return null;
1230
- const {
1231
- type,
1232
- item
1233
- } = virtualItem;
1234
- return /*#__PURE__*/jsx("div", {
1235
- style: {
1236
- position: 'absolute',
1237
- top: 0,
1238
- left: 0,
1239
- width: '100%',
1240
- transform: `translateY(${virtualRow.start}px)`
1241
- },
1242
- children: type === 'group' ?
1243
- /*#__PURE__*/
1244
- // Render option group header (without children, as they're rendered separately)
1245
- jsxs(ItemWrapper, {
1246
- ...getItemProps({
1247
- item,
1248
- index: virtualRow.index,
1249
- disabled: item.isDisabled
1250
- }),
1251
- css: {
1252
- backgroundColor: highlightedIndex === virtualRow.index ? '$bgGray' : '$bgWhite',
1253
- cursor: item.isDisabled ? 'not-allowed' : 'pointer',
1254
- color: item.isDisabled ? '$textDisable' : 'textPrimary',
1255
- fontWeight: 600
1219
+ hasMore: true,
1220
+ threshold: 0,
1221
+ useWindow: false,
1222
+ children: child
1223
+ }, isLoading),
1224
+ children: isOpen && shouldVirtualize ?
1225
+ /*#__PURE__*/
1226
+ // Virtualized rendering for large lists
1227
+ jsx(Fragment, {
1228
+ children: /*#__PURE__*/jsx("div", {
1229
+ style: {
1230
+ height: `${rowVirtualizer.totalSize}px`,
1231
+ position: 'relative',
1232
+ width: '100%'
1233
+ },
1234
+ children: rowVirtualizer.virtualItems.map(virtualRow => {
1235
+ const virtualItem = getItemAtIndex(virtualRow.index);
1236
+ if (!virtualItem) return null;
1237
+ const {
1238
+ type,
1239
+ item
1240
+ } = virtualItem;
1241
+ return /*#__PURE__*/jsx("div", {
1242
+ style: {
1243
+ position: 'absolute',
1244
+ top: 0,
1245
+ left: 0,
1246
+ width: '100%',
1247
+ transform: `translateY(${virtualRow.start}px)`
1256
1248
  },
1257
- children: [/*#__PURE__*/jsx(IconWrapper, {
1249
+ children: type === 'group' ?
1250
+ /*#__PURE__*/
1251
+ // Render option group header (without children, as they're rendered separately)
1252
+ jsxs(ItemWrapper, {
1253
+ ...getItemProps({
1254
+ item,
1255
+ index: virtualRow.index,
1256
+ disabled: item.isDisabled
1257
+ }),
1258
1258
  css: {
1259
- width: '16px',
1260
- height: '16px',
1261
- border: '1px solid $bgBorder',
1259
+ backgroundColor: highlightedIndex === virtualRow.index ? '$bgGray' : '$bgWhite',
1262
1260
  cursor: item.isDisabled ? 'not-allowed' : 'pointer',
1263
- ...checkBoxCss
1261
+ color: item.isDisabled ? '$textDisable' : 'textPrimary',
1262
+ fontWeight: 600
1264
1263
  },
1265
- children: !item?.hideCheck && item.isDisabled ? /*#__PURE__*/jsx(CheckOutline, {
1266
- color: colors.iconDisable
1267
- }) : item?.children?.filter(e => selectedValueSet.has(e.id) && !e.isDisabled)?.length === item?.children?.filter(e => !e.isDisabled)?.length ? /*#__PURE__*/jsx(CheckOutline, {
1268
- color: colors.iconBrand
1269
- }) : item?.children?.filter(e => selectedValueSet.has(e.id) && !e.isDisabled)?.length > 0 ? /*#__PURE__*/jsx(IndeterminateFilled, {
1270
- children: "-"
1271
- }) : null
1272
- }), /*#__PURE__*/jsx(Item, {
1273
- css: {
1274
- backgroundColor: 'unset',
1275
- width: '100%'
1276
- },
1277
- children: item.render ? item.render?.(item.name) : item.name
1278
- })]
1279
- }) :
1280
- // Render regular item or child item
1281
- renderOptionItem(item, virtualRow.index, type === 'child')
1282
- }, `${type}-${item.id}-${virtualRow.index}`);
1264
+ children: [/*#__PURE__*/jsx(IconWrapper, {
1265
+ css: {
1266
+ width: '16px',
1267
+ height: '16px',
1268
+ border: '1px solid $bgBorder',
1269
+ cursor: item.isDisabled ? 'not-allowed' : 'pointer',
1270
+ ...checkBoxCss
1271
+ },
1272
+ children: !item?.hideCheck && item.isDisabled ? /*#__PURE__*/jsx(CheckOutline, {
1273
+ color: colors.iconDisable
1274
+ }) : item?.children?.filter(e => selectedValueSet.has(e.id) && !e.isDisabled)?.length === item?.children?.filter(e => !e.isDisabled)?.length ? /*#__PURE__*/jsx(CheckOutline, {
1275
+ color: colors.iconBrand
1276
+ }) : item?.children?.filter(e => selectedValueSet.has(e.id) && !e.isDisabled)?.length > 0 ? /*#__PURE__*/jsx(IndeterminateFilled, {
1277
+ children: "-"
1278
+ }) : null
1279
+ }), /*#__PURE__*/jsx(Item, {
1280
+ css: {
1281
+ backgroundColor: 'unset',
1282
+ width: '100%'
1283
+ },
1284
+ children: item.render ? item.render?.(item.name) : item.name
1285
+ })]
1286
+ }) :
1287
+ // Render regular item or child item
1288
+ renderOptionItem(item, virtualRow.index, type === 'child')
1289
+ }, `${type}-${item.id}-${virtualRow.index}`);
1290
+ })
1283
1291
  })
1284
- })
1285
- }) :
1286
- // Non-virtualized rendering for small lists
1287
- isOpen && items.map((item, index) => item?.children ? renderOptionGroupItem(item) : renderOptionItem(item, index))
1288
- })
1289
- }) : /*#__PURE__*/jsx(Option, {
1290
- css: {
1291
- height: 'auto'
1292
- },
1293
- ...getMenuProps({}, {
1294
- suppressRefError: true
1295
- }),
1296
- children: /*#__PURE__*/jsxs(StyledDiv, {
1297
- children: [/*#__PURE__*/jsx("div", {
1298
- style: {
1299
- marginRight: '6px'
1300
- },
1301
- children: /*#__PURE__*/jsx(CircleInfoOutline, {
1302
- size: "16px",
1303
- color: colors.iconDisable,
1292
+ }) :
1293
+ // Non-virtualized rendering for small lists
1294
+ isOpen && items.map((item, index) => item?.children ? renderOptionGroupItem(item) : renderOptionItem(item, index))
1295
+ })
1296
+ }) : /*#__PURE__*/jsx(Option, {
1297
+ css: {
1298
+ height: 'auto'
1299
+ },
1300
+ ...getMenuProps({}, {
1301
+ suppressRefError: true
1302
+ }),
1303
+ children: /*#__PURE__*/jsxs(StyledDiv, {
1304
+ children: [/*#__PURE__*/jsx("div", {
1304
1305
  style: {
1305
- verticalAlign: 'text-bottom'
1306
- }
1307
- })
1308
- }), textEmptyItem ?? t('emptyItemText', 'Item tidak ditemukan')]
1309
- })
1310
- })]
1306
+ marginRight: '6px'
1307
+ },
1308
+ children: /*#__PURE__*/jsx(CircleInfoOutline, {
1309
+ size: "16px",
1310
+ color: colors.iconDisable,
1311
+ style: {
1312
+ verticalAlign: 'text-bottom'
1313
+ }
1314
+ })
1315
+ }), textEmptyItem ?? t('emptyItemText', 'Item tidak ditemukan')]
1316
+ })
1317
+ })]
1318
+ })
1311
1319
  })]
1312
1320
  });
1313
1321
  });
@@ -34,7 +34,6 @@ function MapPicker(props) {
34
34
  const isDesktop = useMediaQuery('(min-width: 767px)');
35
35
  const {
36
36
  center,
37
- setCenter,
38
37
  places,
39
38
  searchQuery,
40
39
  searchResult,
@@ -49,12 +48,15 @@ function MapPicker(props) {
49
48
  showSelectButton,
50
49
  onChange
51
50
  });
51
+ // Keyed on the coordinates, not the object: a parent re-rendering with a new but equal
52
+ // `value` must not pull the map back from where the user dragged it. handleSearchLatLng is
53
+ // left out because it changes with every new `onChange`, which InputMapPicker passes inline.
52
54
  useEffect(() => {
53
55
  if (value && (value === null || value === void 0 ? void 0 : value.lat) && (value === null || value === void 0 ? void 0 : value.lng)) {
54
- setCenter(value);
55
56
  handleSearchLatLng(value);
56
57
  }
57
- }, [value, setCenter]);
58
+ // eslint-disable-next-line react-hooks/exhaustive-deps
59
+ }, [value === null || value === void 0 ? void 0 : value.lat, value === null || value === void 0 ? void 0 : value.lng]);
58
60
  function handleMarkerClick() {
59
61
  if (places.length > 0) {
60
62
  const mapObject = {