torch-glare 2.5.6 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/bin/index.js +9 -24
- package/dist/bin/index.js.map +1 -1
- package/dist/src/commands/add.d.ts +2 -2
- package/dist/src/commands/add.d.ts.map +1 -1
- package/dist/src/commands/add.js +4 -70
- package/dist/src/commands/add.js.map +1 -1
- package/dist/src/commands/hook.d.ts +5 -4
- package/dist/src/commands/hook.d.ts.map +1 -1
- package/dist/src/commands/hook.js +7 -75
- package/dist/src/commands/hook.js.map +1 -1
- package/dist/src/commands/init.js +1 -1
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/layout.d.ts +5 -4
- package/dist/src/commands/layout.d.ts.map +1 -1
- package/dist/src/commands/layout.js +7 -75
- package/dist/src/commands/layout.js.map +1 -1
- package/dist/src/commands/provider.d.ts +5 -4
- package/dist/src/commands/provider.d.ts.map +1 -1
- package/dist/src/commands/provider.js +7 -75
- package/dist/src/commands/provider.js.map +1 -1
- package/dist/src/commands/update.d.ts.map +1 -1
- package/dist/src/commands/update.js +78 -43
- package/dist/src/commands/update.js.map +1 -1
- package/dist/src/commands/utils.d.ts +5 -4
- package/dist/src/commands/utils.d.ts.map +1 -1
- package/dist/src/commands/utils.js +7 -80
- package/dist/src/commands/utils.js.map +1 -1
- package/dist/src/shared/addFromRegistry.d.ts +16 -0
- package/dist/src/shared/addFromRegistry.d.ts.map +1 -0
- package/dist/src/shared/addFromRegistry.js +88 -0
- package/dist/src/shared/addFromRegistry.js.map +1 -0
- package/dist/src/shared/getInstallCommand.d.ts.map +1 -1
- package/dist/src/shared/getInstallCommand.js +5 -0
- package/dist/src/shared/getInstallCommand.js.map +1 -1
- package/dist/src/shared/installDependencies.d.ts +0 -10
- package/dist/src/shared/installDependencies.d.ts.map +1 -1
- package/dist/src/shared/installDependencies.js +0 -17
- package/dist/src/shared/installDependencies.js.map +1 -1
- package/dist/src/shared/installFromPlan.d.ts +7 -5
- package/dist/src/shared/installFromPlan.d.ts.map +1 -1
- package/dist/src/shared/installFromPlan.js +103 -36
- package/dist/src/shared/installFromPlan.js.map +1 -1
- package/dist/src/shared/loadRegistry.d.ts +12 -3
- package/dist/src/shared/loadRegistry.d.ts.map +1 -1
- package/dist/src/shared/loadRegistry.js +19 -20
- package/dist/src/shared/loadRegistry.js.map +1 -1
- package/dist/src/shared/registryClient.d.ts +19 -0
- package/dist/src/shared/registryClient.d.ts.map +1 -0
- package/dist/src/shared/registryClient.js +128 -0
- package/dist/src/shared/registryClient.js.map +1 -0
- package/dist/src/shared/resolveEntry.d.ts +10 -7
- package/dist/src/shared/resolveEntry.d.ts.map +1 -1
- package/dist/src/shared/resolveEntry.js +18 -13
- package/dist/src/shared/resolveEntry.js.map +1 -1
- package/dist/src/shared/suggestOtherCommand.d.ts +7 -1
- package/dist/src/shared/suggestOtherCommand.d.ts.map +1 -1
- package/dist/src/shared/suggestOtherCommand.js +29 -23
- package/dist/src/shared/suggestOtherCommand.js.map +1 -1
- package/dist/src/types/main.d.ts +31 -2
- package/dist/src/types/main.d.ts.map +1 -1
- package/package.json +6 -11
- package/apps/lib/components/ActionButton.tsx +0 -93
- package/apps/lib/components/ActionsGroup.tsx +0 -27
- package/apps/lib/components/AlertDialog.tsx +0 -204
- package/apps/lib/components/Avatar.tsx +0 -46
- package/apps/lib/components/Badge.tsx +0 -250
- package/apps/lib/components/BadgeField.tsx +0 -403
- package/apps/lib/components/Breadcrumb.tsx +0 -280
- package/apps/lib/components/Button.tsx +0 -325
- package/apps/lib/components/ButtonGroup.tsx +0 -190
- package/apps/lib/components/Calendar.tsx +0 -115
- package/apps/lib/components/Card.tsx +0 -95
- package/apps/lib/components/Checkbox.tsx +0 -42
- package/apps/lib/components/ColorPicker.tsx +0 -441
- package/apps/lib/components/ConclusionHeader.tsx +0 -148
- package/apps/lib/components/ContextMenu.tsx +0 -571
- package/apps/lib/components/CountBadge.tsx +0 -52
- package/apps/lib/components/DataTable.tsx +0 -213
- package/apps/lib/components/DataViews/badge.ts +0 -49
- package/apps/lib/components/DataViews/cell.tsx +0 -324
- package/apps/lib/components/DataViews/context.ts +0 -144
- package/apps/lib/components/DataViews/data-views.tsx +0 -395
- package/apps/lib/components/DataViews/filters/children.tsx +0 -98
- package/apps/lib/components/DataViews/filters/custom.tsx +0 -34
- package/apps/lib/components/DataViews/filters/filters.tsx +0 -157
- package/apps/lib/components/DataViews/filters/index.ts +0 -4
- package/apps/lib/components/DataViews/filters/labelled.tsx +0 -20
- package/apps/lib/components/DataViews/filters/presets.tsx +0 -65
- package/apps/lib/components/DataViews/filters/sync.tsx +0 -35
- package/apps/lib/components/DataViews/filters/values.ts +0 -173
- package/apps/lib/components/DataViews/header.tsx +0 -220
- package/apps/lib/components/DataViews/hooks/index.ts +0 -5
- package/apps/lib/components/DataViews/hooks/useActiveRow.ts +0 -22
- package/apps/lib/components/DataViews/hooks/useControllable.ts +0 -52
- package/apps/lib/components/DataViews/index.ts +0 -88
- package/apps/lib/components/DataViews/panel/columns.tsx +0 -153
- package/apps/lib/components/DataViews/panel/controls.tsx +0 -106
- package/apps/lib/components/DataViews/panel/index.ts +0 -3
- package/apps/lib/components/DataViews/panel/panel.tsx +0 -164
- package/apps/lib/components/DataViews/panel/saved-views.tsx +0 -67
- package/apps/lib/components/DataViews/panel/section.tsx +0 -81
- package/apps/lib/components/DataViews/panel/sort.tsx +0 -42
- package/apps/lib/components/DataViews/panel/tab.tsx +0 -31
- package/apps/lib/components/DataViews/slots.ts +0 -72
- package/apps/lib/components/DataViews/states.tsx +0 -73
- package/apps/lib/components/DataViews/types.ts +0 -510
- package/apps/lib/components/DataViews/views/board-view.tsx +0 -381
- package/apps/lib/components/DataViews/views/card-rows.tsx +0 -36
- package/apps/lib/components/DataViews/views/inbox-view.tsx +0 -260
- package/apps/lib/components/DataViews/views/pane-views.tsx +0 -196
- package/apps/lib/components/DataViews/views/table-view.tsx +0 -493
- package/apps/lib/components/DataViews/views/tree-view.tsx +0 -367
- package/apps/lib/components/DatePicker.tsx +0 -232
- package/apps/lib/components/Dialog.tsx +0 -116
- package/apps/lib/components/Divider.tsx +0 -28
- package/apps/lib/components/Drawer.tsx +0 -451
- package/apps/lib/components/DropdownMenu.tsx +0 -554
- package/apps/lib/components/FieldHint.tsx +0 -69
- package/apps/lib/components/Form.tsx +0 -182
- package/apps/lib/components/FormBuilder/context.ts +0 -85
- package/apps/lib/components/FormBuilder/field-kind.ts +0 -28
- package/apps/lib/components/FormBuilder/fields/ChoiceFields.tsx +0 -60
- package/apps/lib/components/FormBuilder/fields/ColorField.tsx +0 -59
- package/apps/lib/components/FormBuilder/fields/CustomField.tsx +0 -11
- package/apps/lib/components/FormBuilder/fields/DateField.tsx +0 -32
- package/apps/lib/components/FormBuilder/fields/FieldArray.tsx +0 -74
- package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +0 -168
- package/apps/lib/components/FormBuilder/fields/FileField.tsx +0 -39
- package/apps/lib/components/FormBuilder/fields/OptionListFields.tsx +0 -132
- package/apps/lib/components/FormBuilder/fields/OtpField.tsx +0 -31
- package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +0 -109
- package/apps/lib/components/FormBuilder/fields/RichTextEditorField.tsx +0 -31
- package/apps/lib/components/FormBuilder/fields/SelectField.tsx +0 -114
- package/apps/lib/components/FormBuilder/fields/SignatureField.tsx +0 -157
- package/apps/lib/components/FormBuilder/fields/SliderField.tsx +0 -67
- package/apps/lib/components/FormBuilder/fields/SwitchBoxField.tsx +0 -41
- package/apps/lib/components/FormBuilder/fields/TableField.tsx +0 -336
- package/apps/lib/components/FormBuilder/fields/TextField.tsx +0 -213
- package/apps/lib/components/FormBuilder/fields/TreeSelectField.tsx +0 -43
- package/apps/lib/components/FormBuilder/fields/countries.ts +0 -303
- package/apps/lib/components/FormBuilder/fields/index.ts +0 -25
- package/apps/lib/components/FormBuilder/form-builder.tsx +0 -198
- package/apps/lib/components/FormBuilder/index.ts +0 -31
- package/apps/lib/components/FormBuilder/numberFormat.ts +0 -16
- package/apps/lib/components/FormBuilder/submit.tsx +0 -67
- package/apps/lib/components/FormBuilder/types.ts +0 -316
- package/apps/lib/components/FormRenderer/FormDrawer.tsx +0 -250
- package/apps/lib/components/FormRenderer/detail.tsx +0 -256
- package/apps/lib/components/FormRenderer/form-renderer.tsx +0 -358
- package/apps/lib/components/FormRenderer/header.tsx +0 -88
- package/apps/lib/components/FormRenderer/index.ts +0 -17
- package/apps/lib/components/FormRenderer/notch-action.tsx +0 -64
- package/apps/lib/components/FormRenderer/section.tsx +0 -39
- package/apps/lib/components/FormRenderer/stepper.tsx +0 -393
- package/apps/lib/components/FormRenderer/types.ts +0 -115
- package/apps/lib/components/FormSummary.tsx +0 -282
- package/apps/lib/components/HeaderBar.tsx +0 -117
- package/apps/lib/components/ImageAttachment.tsx +0 -209
- package/apps/lib/components/InnerLabelField.tsx +0 -154
- package/apps/lib/components/Input.tsx +0 -224
- package/apps/lib/components/InputField.tsx +0 -147
- package/apps/lib/components/InputOTP.tsx +0 -84
- package/apps/lib/components/Label.tsx +0 -129
- package/apps/lib/components/LabelField.tsx +0 -76
- package/apps/lib/components/LabeledCheckBox.tsx +0 -51
- package/apps/lib/components/LabeledRadio.tsx +0 -51
- package/apps/lib/components/LinkButton.tsx +0 -94
- package/apps/lib/components/LoginButton.tsx +0 -51
- package/apps/lib/components/PasswordLevel.tsx +0 -58
- package/apps/lib/components/Popover.tsx +0 -312
- package/apps/lib/components/ProfileMenu.tsx +0 -85
- package/apps/lib/components/Radio.tsx +0 -59
- package/apps/lib/components/RadioCard.tsx +0 -50
- package/apps/lib/components/ScrollArea.tsx +0 -54
- package/apps/lib/components/SearchField.tsx +0 -62
- package/apps/lib/components/SearchableSelect.tsx +0 -318
- package/apps/lib/components/SearchableTable.tsx +0 -344
- package/apps/lib/components/SearchableTree.tsx +0 -485
- package/apps/lib/components/SearchableTreeDialog.tsx +0 -502
- package/apps/lib/components/SectionBlock.tsx +0 -168
- package/apps/lib/components/Select.tsx +0 -347
- package/apps/lib/components/SimpleSelect.tsx +0 -225
- package/apps/lib/components/Skeleton.tsx +0 -12
- package/apps/lib/components/SlideDatePicker.tsx +0 -255
- package/apps/lib/components/SpinLoading.tsx +0 -174
- package/apps/lib/components/Stepper.tsx +0 -480
- package/apps/lib/components/Switch.tsx +0 -82
- package/apps/lib/components/TabFormItem.tsx +0 -153
- package/apps/lib/components/TabSwitch.tsx +0 -172
- package/apps/lib/components/Table.tsx +0 -597
- package/apps/lib/components/TextEditor/ChartBlockTool.ts +0 -676
- package/apps/lib/components/TextEditor/RichTextField.tsx +0 -46
- package/apps/lib/components/TextEditor/TableDnDWrapper.ts +0 -462
- package/apps/lib/components/TextEditor/TextEditor.tsx +0 -1164
- package/apps/lib/components/TextEditor/TextEditorToolbar.tsx +0 -429
- package/apps/lib/components/TextEditor/editor-tools/AlignmentTune.ts +0 -70
- package/apps/lib/components/TextEditor/editor-tools/ColorInlineTool.ts +0 -50
- package/apps/lib/components/TextEditor/editor-tools/StrikethroughInlineTool.ts +0 -48
- package/apps/lib/components/TextEditor/editor-tools/inlineFormat.ts +0 -98
- package/apps/lib/components/TextEditor/editorjs.d.ts +0 -60
- package/apps/lib/components/TextEditor/index.ts +0 -7
- package/apps/lib/components/TextEditor/markdownParser.ts +0 -346
- package/apps/lib/components/Textarea.tsx +0 -117
- package/apps/lib/components/Timeline.tsx +0 -263
- package/apps/lib/components/Toast.tsx +0 -87
- package/apps/lib/components/Toggle.tsx +0 -125
- package/apps/lib/components/ToggleButton.tsx +0 -140
- package/apps/lib/components/Tooltip.tsx +0 -116
- package/apps/lib/components/TransparentLabel.tsx +0 -71
- package/apps/lib/components/TreeDropDown.tsx +0 -115
- package/apps/lib/components/TreeFolder/TreeFolder.tsx +0 -380
- package/apps/lib/components/TreeFolder/TreeFolderBreadcrumb.tsx +0 -75
- package/apps/lib/components/TreeFolder/TreeFolderRow.tsx +0 -354
- package/apps/lib/components/TreeFolder/TreeFolderStyles.tsx +0 -60
- package/apps/lib/components/TreeFolder/icons.tsx +0 -60
- package/apps/lib/components/TreeFolder/index.ts +0 -24
- package/apps/lib/components/TreeFolder/treeFolderUtils.ts +0 -109
- package/apps/lib/components/TreeFolder/types.ts +0 -77
- package/apps/lib/components/TreeFolder/useTreeFolderDnD.ts +0 -118
- package/apps/lib/hooks/useActiveTreeItem.ts +0 -61
- package/apps/lib/hooks/useClickOutside.ts +0 -27
- package/apps/lib/hooks/useDragDrop.tsx +0 -365
- package/apps/lib/hooks/useHtmlDir.ts +0 -31
- package/apps/lib/hooks/useInfiniteScroll.ts +0 -108
- package/apps/lib/hooks/useIsMobile.ts +0 -21
- package/apps/lib/hooks/useResize.ts +0 -67
- package/apps/lib/hooks/useTagSelection.ts +0 -300
- package/apps/lib/layouts/CNLayout.tsx +0 -372
- package/apps/lib/layouts/DataViewCard.tsx +0 -62
- package/apps/lib/layouts/FieldSection.tsx +0 -91
- package/apps/lib/layouts/TreeSubLayout.tsx +0 -173
- package/apps/lib/providers/ThemeProvider.tsx +0 -84
- package/apps/lib/registry.json +0 -1217
- package/apps/lib/tsconfig.json +0 -9
- package/apps/lib/tsconfig.tsbuildinfo +0 -1
- package/apps/lib/utils/cn.ts +0 -6
- package/apps/lib/utils/color.ts +0 -175
- package/apps/lib/utils/dataViews/path.ts +0 -67
- package/apps/lib/utils/dataViews/query.ts +0 -73
- package/apps/lib/utils/dataViews/types.ts +0 -187
- package/apps/lib/utils/dateFormat.ts +0 -108
- package/apps/lib/utils/resize.ts +0 -31
- package/apps/lib/utils/scroller.ts +0 -26
- package/apps/lib/utils/types.ts +0 -16
- package/docs/BLOCKS.md +0 -129
- package/docs/CHANGELOG-1.1.16.md +0 -108
- package/docs/Cover.png +0 -0
- package/docs/README.md +0 -215
- package/docs/components/action-button.md +0 -638
- package/docs/components/actions-group.md +0 -708
- package/docs/components/alert-dialog.md +0 -920
- package/docs/components/avatar.md +0 -709
- package/docs/components/badge-field.md +0 -738
- package/docs/components/badge.md +0 -301
- package/docs/components/breadcrumb.md +0 -930
- package/docs/components/button-group.md +0 -650
- package/docs/components/button.md +0 -639
- package/docs/components/calendar.md +0 -555
- package/docs/components/card.md +0 -622
- package/docs/components/chart-block-tool.md +0 -129
- package/docs/components/checkbox.md +0 -699
- package/docs/components/cn-layout.md +0 -854
- package/docs/components/color-picker.md +0 -101
- package/docs/components/conclusion-header.md +0 -80
- package/docs/components/context-menu.md +0 -483
- package/docs/components/count-badge.md +0 -560
- package/docs/components/data-table.md +0 -854
- package/docs/components/data-views/backend-response.md +0 -324
- package/docs/components/data-views/examples/a11y-rtl.md +0 -250
- package/docs/components/data-views/examples/api-orders-route.md +0 -130
- package/docs/components/data-views/examples/fields.md +0 -362
- package/docs/components/data-views/examples/filters.md +0 -307
- package/docs/components/data-views/examples/inbox-routing.md +0 -218
- package/docs/components/data-views/examples/index.md +0 -29
- package/docs/components/data-views/examples/overview.md +0 -244
- package/docs/components/data-views/examples/panel.md +0 -212
- package/docs/components/data-views/examples/scale.md +0 -231
- package/docs/components/data-views/examples/server-side.md +0 -210
- package/docs/components/data-views/examples/state.md +0 -250
- package/docs/components/data-views/examples/tree-custom.md +0 -388
- package/docs/components/data-views/examples/view-registry.md +0 -313
- package/docs/components/data-views/examples/views.md +0 -534
- package/docs/components/data-views/guide.md +0 -405
- package/docs/components/data-views/index.md +0 -1521
- package/docs/components/data-views/migration.md +0 -81
- package/docs/components/date-picker.md +0 -857
- package/docs/components/dialog.md +0 -929
- package/docs/components/divider.md +0 -766
- package/docs/components/drawer.md +0 -654
- package/docs/components/dropdown-menu.md +0 -668
- package/docs/components/field-hint.md +0 -898
- package/docs/components/field-section.md +0 -781
- package/docs/components/form-builder.md +0 -284
- package/docs/components/form-renderer.md +0 -396
- package/docs/components/form-summary.md +0 -138
- package/docs/components/form.md +0 -785
- package/docs/components/header-bar.md +0 -188
- package/docs/components/image-attachment.md +0 -906
- package/docs/components/inner-label-field.md +0 -819
- package/docs/components/input-field.md +0 -623
- package/docs/components/input-otp.md +0 -756
- package/docs/components/input.md +0 -634
- package/docs/components/label-field.md +0 -770
- package/docs/components/label.md +0 -761
- package/docs/components/labeled-check-box.md +0 -642
- package/docs/components/labeled-radio.md +0 -796
- package/docs/components/link-button.md +0 -685
- package/docs/components/login-button.md +0 -872
- package/docs/components/password-level.md +0 -803
- package/docs/components/popover.md +0 -649
- package/docs/components/profile-menu.md +0 -656
- package/docs/components/radio-card.md +0 -801
- package/docs/components/radio.md +0 -735
- package/docs/components/scroll-area.md +0 -691
- package/docs/components/search-field.md +0 -702
- package/docs/components/searchable-select.md +0 -368
- package/docs/components/searchable-table.md +0 -438
- package/docs/components/searchable-tree-dialog.md +0 -190
- package/docs/components/searchable-tree.md +0 -200
- package/docs/components/section-block.md +0 -483
- package/docs/components/select.md +0 -709
- package/docs/components/simple-select.md +0 -741
- package/docs/components/skeleton.md +0 -706
- package/docs/components/slide-date-picker.md +0 -837
- package/docs/components/spin-loading.md +0 -696
- package/docs/components/stepper.md +0 -316
- package/docs/components/switch.md +0 -770
- package/docs/components/tab-form-item.md +0 -773
- package/docs/components/tab-switch.md +0 -170
- package/docs/components/table-dnd-wrapper.md +0 -107
- package/docs/components/table.md +0 -1088
- package/docs/components/text-editor.md +0 -735
- package/docs/components/textarea.md +0 -681
- package/docs/components/timeline.md +0 -254
- package/docs/components/toast.md +0 -773
- package/docs/components/toggle-button.md +0 -643
- package/docs/components/toggle.md +0 -882
- package/docs/components/tooltip.md +0 -513
- package/docs/components/transparent-label.md +0 -721
- package/docs/components/tree-drop-down.md +0 -740
- package/docs/components/tree-folder.md +0 -110
- package/docs/components/tree-sub-layout.md +0 -707
- package/docs/explanation/architecture.md +0 -54
- package/docs/explanation/design-system.md +0 -52
- package/docs/how-to/form-and-list-recipes.md +0 -386
- package/docs/how-to/forms-with-form-builder.md +0 -472
- package/docs/how-to/guides.md +0 -1027
- package/docs/migration/changelog.md +0 -60
- package/docs/migration/form-builder-2.5.2.md +0 -113
- package/docs/reference/cli.md +0 -112
- package/docs/reference/components.md +0 -572
- package/docs/reference/hooks.md +0 -1523
- package/docs/reference/providers.md +0 -866
- package/docs/reference/tailwind-plugins.md +0 -942
- package/docs/reference/theme.md +0 -85
- package/docs/reference/types.md +0 -1016
- package/docs/reference/utilities.md +0 -726
- package/docs/tutorials/building-first-form.md +0 -846
- package/docs/tutorials/component-composition.md +0 -882
- package/docs/tutorials/getting-started.md +0 -302
- package/docs/tutorials/theming-basics.md +0 -785
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useCallback, useEffect, useRef } from "react";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Ask for the next page when the user scrolls near the end.
|
|
7
|
-
*
|
|
8
|
-
* Lives here rather than in a view because three of them need it — the table, the board's columns
|
|
9
|
-
* and the inbox list — and "near the end" has to mean the same thing in all three.
|
|
10
|
-
*
|
|
11
|
-
* It is an `IntersectionObserver` on a sentinel element, not a scroll listener. A scroll handler
|
|
12
|
-
* has to be throttled, reads layout on every frame to work out how close the bottom is, and stops
|
|
13
|
-
* being correct the moment the container resizes. The observer is told once and answers all three
|
|
14
|
-
* for free.
|
|
15
|
-
*
|
|
16
|
-
* Like everything else here it only *asks*. Nothing loads until you hand back more rows.
|
|
17
|
-
*/
|
|
18
|
-
export interface UseInfiniteScrollOptions {
|
|
19
|
-
/** Fetch the next page and append it. */
|
|
20
|
-
onLoadMore?: () => void;
|
|
21
|
-
/** Whether there is a next page at all. */
|
|
22
|
-
hasMore: boolean;
|
|
23
|
-
/** True while a page is in flight. */
|
|
24
|
-
loading?: boolean;
|
|
25
|
-
/**
|
|
26
|
-
* How far before the end to fire, in px. The default aims to have the next page arriving before
|
|
27
|
-
* the user reaches the gap, rather than after they have already stopped at it.
|
|
28
|
-
*/
|
|
29
|
-
rootMargin?: number;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* The element the sentinel actually scrolls inside, or `null` for the viewport.
|
|
34
|
-
*
|
|
35
|
-
* Walks up looking at `overflow-y` rather than at class names, so it keeps working whatever a
|
|
36
|
-
* view calls its container — the table, the board's columns and the inbox list all differ.
|
|
37
|
-
*/
|
|
38
|
-
function scrollParentOf(el: HTMLElement): HTMLElement | null {
|
|
39
|
-
let node = el.parentElement;
|
|
40
|
-
while (node) {
|
|
41
|
-
const overflow = getComputedStyle(node).overflowY;
|
|
42
|
-
if (overflow === "auto" || overflow === "scroll") return node;
|
|
43
|
-
node = node.parentElement;
|
|
44
|
-
}
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function useInfiniteScroll({
|
|
49
|
-
onLoadMore,
|
|
50
|
-
hasMore,
|
|
51
|
-
loading = false,
|
|
52
|
-
rootMargin = 400,
|
|
53
|
-
}: UseInfiniteScrollOptions) {
|
|
54
|
-
const sentinelRef = useRef<HTMLElement | null>(null);
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* The guard against asking twice for the same page.
|
|
58
|
-
*
|
|
59
|
-
* The observer fires on intersection, but the rows that answer it do not arrive for a round
|
|
60
|
-
* trip — and until they do, the sentinel is still on screen. Reading `loading` alone is not
|
|
61
|
-
* enough either: a caller that sets it a tick late leaves a window where a second fire gets
|
|
62
|
-
* through. So the hook latches when it asks, and only unlatches once the sentinel has actually
|
|
63
|
-
* left the viewport again.
|
|
64
|
-
*/
|
|
65
|
-
const askedRef = useRef(false);
|
|
66
|
-
|
|
67
|
-
const ready = Boolean(onLoadMore) && hasMore && !loading;
|
|
68
|
-
|
|
69
|
-
const handle = useCallback(
|
|
70
|
-
(entries: IntersectionObserverEntry[]) => {
|
|
71
|
-
const visible = entries.some((e) => e.isIntersecting);
|
|
72
|
-
if (!visible) {
|
|
73
|
-
askedRef.current = false;
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
if (askedRef.current || !ready) return;
|
|
77
|
-
askedRef.current = true;
|
|
78
|
-
onLoadMore?.();
|
|
79
|
-
},
|
|
80
|
-
[ready, onLoadMore],
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
useEffect(() => {
|
|
84
|
-
const el = sentinelRef.current;
|
|
85
|
-
if (!el || typeof IntersectionObserver === "undefined") return;
|
|
86
|
-
|
|
87
|
-
const observer = new IntersectionObserver(handle, {
|
|
88
|
-
// The scroller has to be named. `root: null` means the *viewport*, not the nearest
|
|
89
|
-
// scrollable ancestor — so inside a scrolling panel the sentinel is clipped away and
|
|
90
|
-
// `rootMargin` expands the wrong box, which reads as "infinite scroll simply never fires".
|
|
91
|
-
// Finding it here rather than asking for it keeps the call sites free of plumbing.
|
|
92
|
-
root: scrollParentOf(el),
|
|
93
|
-
rootMargin: `0px 0px ${rootMargin}px 0px`,
|
|
94
|
-
});
|
|
95
|
-
observer.observe(el);
|
|
96
|
-
return () => observer.disconnect();
|
|
97
|
-
}, [handle, rootMargin]);
|
|
98
|
-
|
|
99
|
-
// The latch is released by the sentinel leaving, and by nothing else.
|
|
100
|
-
//
|
|
101
|
-
// Releasing it when `loading` went false instead — which is the obvious thing to write — burns
|
|
102
|
-
// several pages on one flick to the bottom: the flag drops before the new rows have painted, so
|
|
103
|
-
// the sentinel is still on screen and the next observer callback asks again, and again. Tying
|
|
104
|
-
// the release to visibility makes one arrival at the end cost exactly one page, because the
|
|
105
|
-
// rows that arrive are what push the sentinel out of view.
|
|
106
|
-
|
|
107
|
-
return { sentinelRef };
|
|
108
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
|
|
5
|
-
const MOBILE_BREAKPOINT = 768;
|
|
6
|
-
|
|
7
|
-
export function useIsMobile() {
|
|
8
|
-
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);
|
|
9
|
-
|
|
10
|
-
React.useEffect(() => {
|
|
11
|
-
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
12
|
-
const onChange = () => {
|
|
13
|
-
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
14
|
-
};
|
|
15
|
-
mql.addEventListener("change", onChange);
|
|
16
|
-
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
17
|
-
return () => mql.removeEventListener("change", onChange);
|
|
18
|
-
}, []);
|
|
19
|
-
|
|
20
|
-
return !!isMobile;
|
|
21
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { MutableRefObject, RefObject, useEffect, useState } from "react";
|
|
2
|
-
import { calculateNewWidthFromMouse, calculateNewWidthFromTouch } from "../utils/resize";
|
|
3
|
-
|
|
4
|
-
// Hook to handle resizing with RTL support
|
|
5
|
-
export const useResize = (resizableRef: MutableRefObject<HTMLElement> | RefObject<HTMLElement>) => {
|
|
6
|
-
const [width, setWidth] = useState<number>();
|
|
7
|
-
const [isResizing, setIsResizing] = useState<boolean>(false);
|
|
8
|
-
const [isRTL, setIsRTL] = useState<boolean>(false);
|
|
9
|
-
|
|
10
|
-
// Start the resize process
|
|
11
|
-
const handleStartResize = () => {
|
|
12
|
-
setIsResizing(true);
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
// Stop the resize process
|
|
16
|
-
const handleStopResize = () => {
|
|
17
|
-
setIsResizing(false);
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
// Handle mouse move events
|
|
21
|
-
const handleMouseMove = (event: MouseEvent) => {
|
|
22
|
-
if (!isResizing) return;
|
|
23
|
-
const newWidth = calculateNewWidthFromMouse(event, resizableRef, isRTL);
|
|
24
|
-
setWidth(newWidth);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
// Handle touch move events
|
|
28
|
-
const handleTouchMove = (event: TouchEvent) => {
|
|
29
|
-
if (!isResizing) return;
|
|
30
|
-
event.preventDefault(); // Prevent scrolling or other touch-related behaviors
|
|
31
|
-
const newWidth = calculateNewWidthFromTouch(event, resizableRef, isRTL);
|
|
32
|
-
setWidth(newWidth);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
let htmlDir = "";
|
|
37
|
-
if (document) {
|
|
38
|
-
htmlDir = document.documentElement.getAttribute("dir") || "ltr";
|
|
39
|
-
}
|
|
40
|
-
// Determine the direction of the document
|
|
41
|
-
setIsRTL(htmlDir === "rtl");
|
|
42
|
-
}, []);
|
|
43
|
-
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
if (!resizableRef.current) return;
|
|
46
|
-
|
|
47
|
-
// Add event listeners for mouse and touch events
|
|
48
|
-
document.addEventListener("mousemove", handleMouseMove);
|
|
49
|
-
document.addEventListener("mouseup", handleStopResize);
|
|
50
|
-
document.addEventListener("touchmove", handleTouchMove, { passive: false });
|
|
51
|
-
document.addEventListener("touchend", handleStopResize);
|
|
52
|
-
|
|
53
|
-
// Cleanup event listeners on unmount
|
|
54
|
-
return () => {
|
|
55
|
-
document.removeEventListener("mousemove", handleMouseMove);
|
|
56
|
-
document.removeEventListener("mouseup", handleStopResize);
|
|
57
|
-
document.removeEventListener("touchmove", handleTouchMove);
|
|
58
|
-
document.removeEventListener("touchend", handleStopResize);
|
|
59
|
-
};
|
|
60
|
-
}, [isResizing, isRTL]); // Depend on `isRtl` to handle direction changes dynamically
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
width,
|
|
64
|
-
isResizing,
|
|
65
|
-
handleStartResize,
|
|
66
|
-
};
|
|
67
|
-
};
|
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect, useRef } from "react";
|
|
2
|
-
|
|
3
|
-
export interface Tag {
|
|
4
|
-
id: string;
|
|
5
|
-
name: string;
|
|
6
|
-
variant?: string;
|
|
7
|
-
value?: string;
|
|
8
|
-
isSelected: boolean;
|
|
9
|
-
[key: string]: unknown;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/** Stable key for a selection, used to tell an external change from one we just made. */
|
|
13
|
-
const signatureOf = (tags: Tag[]) => tags.map((t) => t.id).join("\0");
|
|
14
|
-
|
|
15
|
-
export const useTagSelection = ({
|
|
16
|
-
Tags,
|
|
17
|
-
onTagsChange,
|
|
18
|
-
inputRef,
|
|
19
|
-
singleSelect = false,
|
|
20
|
-
creatable = false,
|
|
21
|
-
}: {
|
|
22
|
-
Tags: Tag[];
|
|
23
|
-
onTagsChange?: (selectedTags: Tag[]) => void;
|
|
24
|
-
inputRef?: React.RefObject<HTMLInputElement | null>;
|
|
25
|
-
singleSelect?: boolean;
|
|
26
|
-
/** Allow typed text to become a selected tag that was never in `Tags`. */
|
|
27
|
-
creatable?: boolean;
|
|
28
|
-
}) => {
|
|
29
|
-
// Split initial tags into selected and unselected
|
|
30
|
-
const initialSelectedTags = Tags.filter((tag) => tag.isSelected);
|
|
31
|
-
const initialUnselectedTags = Tags.filter((tag) => !tag.isSelected);
|
|
32
|
-
|
|
33
|
-
// Initialize with available tags (excluding any initially selected ones)
|
|
34
|
-
const [tags, setTags] = useState<Tag[]>(initialUnselectedTags);
|
|
35
|
-
|
|
36
|
-
// Initialize with any pre-selected tags
|
|
37
|
-
const [selectedTagsStack, setSelectedTagsStack] = useState<Tag[]>(
|
|
38
|
-
singleSelect && initialSelectedTags.length > 0 ? [initialSelectedTags[0]] : initialSelectedTags,
|
|
39
|
-
);
|
|
40
|
-
const [searchTags, filterTagsBySearch] = useState("");
|
|
41
|
-
const [focusedTagIndex, setFocusedTagIndex] = useState<number | null>(null);
|
|
42
|
-
const [focusedPopoverIndex, setFocusedPopoverIndex] = useState<number | null>(null);
|
|
43
|
-
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
|
44
|
-
|
|
45
|
-
// LOCAL PATCH (Contact Center): key both effects below off a SIGNATURE of `Tags`, not the array
|
|
46
|
-
// reference. A caller that builds its tag list inline — `MultiSelectField` does, from the form
|
|
47
|
-
// value — hands over a fresh array every render, so the upstream `[Tags]` dependency changed on
|
|
48
|
-
// every pass: effect → setTags → re-render → new array → effect, an infinite render loop.
|
|
49
|
-
const tagsSignature = Tags.map((tag) => `${tag.id}:${tag.isSelected ? 1 : 0}`).join(" ");
|
|
50
|
-
|
|
51
|
-
// Update internal state when Tags actually changes.
|
|
52
|
-
//
|
|
53
|
-
// Filter against the INCOMING selection as well as the one we hold: on an external sync (the
|
|
54
|
-
// hydration case below) `selectedTagsStack` is still the pre-sync value at this point, so
|
|
55
|
-
// filtering by it alone left the freshly selected values sitting in the dropdown as if they
|
|
56
|
-
// were still available to add.
|
|
57
|
-
useEffect(() => {
|
|
58
|
-
const selectedIds = new Set([
|
|
59
|
-
...selectedTagsStack.map((tag) => tag.id),
|
|
60
|
-
...Tags.filter((tag) => tag.isSelected).map((tag) => tag.id),
|
|
61
|
-
]);
|
|
62
|
-
setTags(Tags.filter((tag) => !selectedIds.has(tag.id)));
|
|
63
|
-
}, [tagsSignature]);
|
|
64
|
-
|
|
65
|
-
// LOCAL PATCH (Contact Center): follow the incoming selection.
|
|
66
|
-
//
|
|
67
|
-
// Upstream seeded `selectedTagsStack` from `Tags` ONCE, and the effect above refreshed only the
|
|
68
|
-
// AVAILABLE list — never the selection. That made the component effectively uncontrolled: on an
|
|
69
|
-
// edit form, react-hook-form's `reset()` hydration lands after mount, so a person's saved emails
|
|
70
|
-
// and tags rendered as an empty field. Re-sync whenever the incoming selected set differs from
|
|
71
|
-
// what we hold, and mark the change as external so it isn't echoed straight back to the parent.
|
|
72
|
-
const incomingSelected = Tags.filter((tag) => tag.isSelected);
|
|
73
|
-
const incomingSignature = signatureOf(incomingSelected);
|
|
74
|
-
const lastSyncedRef = useRef<string | null>(null);
|
|
75
|
-
// Starts true so the mount pass below is swallowed — see the notify effect.
|
|
76
|
-
const skipNotifyRef = useRef(true);
|
|
77
|
-
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
if (lastSyncedRef.current === incomingSignature) return;
|
|
80
|
-
lastSyncedRef.current = incomingSignature;
|
|
81
|
-
// Already matches (we made this change ourselves) — don't re-set state, so the notify flag
|
|
82
|
-
// stays untouched and the user's next real edit still reaches the parent.
|
|
83
|
-
if (signatureOf(selectedTagsStack) === incomingSignature) return;
|
|
84
|
-
skipNotifyRef.current = true;
|
|
85
|
-
setSelectedTagsStack(
|
|
86
|
-
singleSelect && incomingSelected.length > 0 ? [incomingSelected[0]] : incomingSelected,
|
|
87
|
-
);
|
|
88
|
-
}, [incomingSignature]);
|
|
89
|
-
|
|
90
|
-
// Notify parent component when tags change.
|
|
91
|
-
//
|
|
92
|
-
// LOCAL PATCH (Contact Center): upstream fired this on MOUNT too, so an untouched field wrote
|
|
93
|
-
// `[]` into the form — marking it dirty and adding an empty-array key to the request payload for
|
|
94
|
-
// a value nobody entered. It also fires for a sync from the parent, which would echo the value
|
|
95
|
-
// straight back. Both are skipped via the flag.
|
|
96
|
-
useEffect(() => {
|
|
97
|
-
if (skipNotifyRef.current) {
|
|
98
|
-
skipNotifyRef.current = false;
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
onTagsChange?.(selectedTagsStack);
|
|
102
|
-
}, [selectedTagsStack]);
|
|
103
|
-
|
|
104
|
-
// Filter tags based on search input
|
|
105
|
-
const filteredTags = tags.filter((tag) =>
|
|
106
|
-
tag.name.toLowerCase().includes(searchTags.toLowerCase()),
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
// Filter selected tags based on search input
|
|
110
|
-
const filteredSelectedTags =
|
|
111
|
-
searchTags.length > 0
|
|
112
|
-
? selectedTagsStack.filter((tag) => tag.name.toLowerCase().includes(searchTags.toLowerCase()))
|
|
113
|
-
: selectedTagsStack;
|
|
114
|
-
|
|
115
|
-
const handleSelectTag = (id: string) => {
|
|
116
|
-
const tagToSelect = tags.find((tag) => tag.id === id);
|
|
117
|
-
if (tagToSelect) {
|
|
118
|
-
// If in single select mode, replace the current selection
|
|
119
|
-
if (singleSelect) {
|
|
120
|
-
// Move any currently selected tag back to available tags
|
|
121
|
-
if (selectedTagsStack.length > 0) {
|
|
122
|
-
const currentSelected = selectedTagsStack[0];
|
|
123
|
-
setTags((prev) => [...prev, { ...currentSelected, isSelected: false }]);
|
|
124
|
-
}
|
|
125
|
-
setSelectedTagsStack([{ ...tagToSelect, isSelected: true }]);
|
|
126
|
-
setTags((prev) => prev.filter((tag) => tag.id !== id));
|
|
127
|
-
} else {
|
|
128
|
-
// Multi-select behavior (original)
|
|
129
|
-
setSelectedTagsStack((prev) => [...prev, { ...tagToSelect, isSelected: true }]);
|
|
130
|
-
setTags((prev) => prev.filter((tag) => tag.id !== id));
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
filterTagsBySearch("");
|
|
134
|
-
setFocusedPopoverIndex(null);
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const handleUnselectTag = (id: string) => {
|
|
138
|
-
const tagToUnselect = selectedTagsStack.find((tag) => tag.id === id);
|
|
139
|
-
if (tagToUnselect) {
|
|
140
|
-
setSelectedTagsStack((prev) => prev.filter((tag) => tag.id !== id));
|
|
141
|
-
setTags((prev) => [...prev, { ...tagToUnselect, isSelected: false }]);
|
|
142
|
-
}
|
|
143
|
-
setFocusedTagIndex(null);
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* LOCAL PATCH (Contact Center): turn typed text into a selected tag.
|
|
148
|
-
*
|
|
149
|
-
* Upstream had no way in but `handleSelectTag(id)` against the fixed `Tags` list, so a free-text
|
|
150
|
-
* list (a person's emails, an organization's aliases) could not be expressed as a badge field at
|
|
151
|
-
* all — which is why those lists were built as one-column tables instead. Matching an existing
|
|
152
|
-
* tag by name selects it rather than creating a duplicate; the id IS the text, so a value the
|
|
153
|
-
* caller round-trips keeps a stable identity.
|
|
154
|
-
*/
|
|
155
|
-
const handleCreateTag = (rawName: string) => {
|
|
156
|
-
const name = rawName.trim();
|
|
157
|
-
if (!name) return;
|
|
158
|
-
const sameName = (tag: Tag) => tag.name.toLowerCase() === name.toLowerCase();
|
|
159
|
-
|
|
160
|
-
// Already chosen — just clear the box so the user sees their text was accepted.
|
|
161
|
-
if (selectedTagsStack.some(sameName)) {
|
|
162
|
-
filterTagsBySearch("");
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
// Offered in the list — select it instead of creating a look-alike.
|
|
166
|
-
const existing = tags.find(sameName);
|
|
167
|
-
if (existing) {
|
|
168
|
-
handleSelectTag(existing.id);
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const created: Tag = { id: name, name, value: name, isSelected: true };
|
|
173
|
-
setSelectedTagsStack((prev) => (singleSelect ? [created] : [...prev, created]));
|
|
174
|
-
filterTagsBySearch("");
|
|
175
|
-
setFocusedPopoverIndex(null);
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
// Reset the hook state with new data
|
|
179
|
-
const reset = (newTags: Tag[] = [], newSelectedTags: Tag[] = []) => {
|
|
180
|
-
// In single select mode, ensure we only have at most one selected tag
|
|
181
|
-
const selectedTags =
|
|
182
|
-
singleSelect && newSelectedTags.length > 0 ? [newSelectedTags[0]] : newSelectedTags;
|
|
183
|
-
|
|
184
|
-
setTags(newTags);
|
|
185
|
-
setSelectedTagsStack(selectedTags);
|
|
186
|
-
filterTagsBySearch("");
|
|
187
|
-
setFocusedTagIndex(null);
|
|
188
|
-
setFocusedPopoverIndex(null);
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const handleNavigationWithinSelectedTags = (e: React.KeyboardEvent<HTMLElement>) => {
|
|
192
|
-
if (e.key === "ArrowLeft") {
|
|
193
|
-
e.preventDefault();
|
|
194
|
-
setFocusedTagIndex((prev: number | null) =>
|
|
195
|
-
prev === 0 ? selectedTagsStack.length - 1 : prev! - 1,
|
|
196
|
-
);
|
|
197
|
-
} else if (e.key === "ArrowRight") {
|
|
198
|
-
e.preventDefault();
|
|
199
|
-
setFocusedTagIndex((prev: number | null) =>
|
|
200
|
-
prev === selectedTagsStack.length - 1 ? 0 : prev! + 1,
|
|
201
|
-
);
|
|
202
|
-
} else if (e.key === "Delete" || e.key === "Backspace") {
|
|
203
|
-
e.preventDefault();
|
|
204
|
-
const tagToRemove = selectedTagsStack[focusedTagIndex!];
|
|
205
|
-
handleUnselectTag(tagToRemove.id);
|
|
206
|
-
if (selectedTagsStack.length > 1) {
|
|
207
|
-
setFocusedTagIndex(Math.min(focusedTagIndex!, selectedTagsStack.length - 2));
|
|
208
|
-
} else {
|
|
209
|
-
setFocusedTagIndex(null);
|
|
210
|
-
}
|
|
211
|
-
} else if (e.key === "Escape") {
|
|
212
|
-
e.preventDefault();
|
|
213
|
-
setFocusedTagIndex(null);
|
|
214
|
-
} else if (e.key === "ArrowDown") {
|
|
215
|
-
e.preventDefault();
|
|
216
|
-
setIsPopoverOpen(true);
|
|
217
|
-
setFocusedPopoverIndex(0);
|
|
218
|
-
setFocusedTagIndex(null);
|
|
219
|
-
} else {
|
|
220
|
-
inputRef?.current?.focus();
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
const handleNavigationWithinPopover = (e: React.KeyboardEvent<HTMLElement>) => {
|
|
225
|
-
if (e.key === "ArrowUp") {
|
|
226
|
-
e.preventDefault();
|
|
227
|
-
if (focusedPopoverIndex === 0 || focusedPopoverIndex === null) {
|
|
228
|
-
if (selectedTagsStack.length > 0) {
|
|
229
|
-
setIsPopoverOpen(false);
|
|
230
|
-
setFocusedTagIndex(0);
|
|
231
|
-
setFocusedPopoverIndex(null);
|
|
232
|
-
} else {
|
|
233
|
-
setFocusedPopoverIndex(filteredTags.length - 1);
|
|
234
|
-
}
|
|
235
|
-
} else {
|
|
236
|
-
setFocusedPopoverIndex((prev) => (prev === null ? 0 : prev - 1));
|
|
237
|
-
}
|
|
238
|
-
} else if (e.key === "ArrowDown") {
|
|
239
|
-
e.preventDefault();
|
|
240
|
-
setFocusedPopoverIndex((prev) =>
|
|
241
|
-
prev === null ? 0 : prev === filteredTags.length - 1 ? 0 : prev + 1,
|
|
242
|
-
);
|
|
243
|
-
} else if (e.key === "Enter" && focusedPopoverIndex !== null) {
|
|
244
|
-
e.preventDefault();
|
|
245
|
-
handleSelectTag(filteredTags[focusedPopoverIndex].id);
|
|
246
|
-
} else if (e.key === "Escape") {
|
|
247
|
-
e.preventDefault();
|
|
248
|
-
setIsPopoverOpen(false);
|
|
249
|
-
setFocusedPopoverIndex(null);
|
|
250
|
-
} else {
|
|
251
|
-
inputRef?.current?.focus();
|
|
252
|
-
}
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
const handleInitialFocus = (e: React.KeyboardEvent<HTMLElement>) => {
|
|
256
|
-
if ((e.key === "ArrowLeft" || e.key === "ArrowRight") && selectedTagsStack.length > 0) {
|
|
257
|
-
e.preventDefault();
|
|
258
|
-
setFocusedTagIndex(e.key === "ArrowLeft" ? selectedTagsStack.length - 1 : 0);
|
|
259
|
-
} else if (e.key === "ArrowDown" && !isPopoverOpen && filteredTags.length > 0) {
|
|
260
|
-
e.preventDefault();
|
|
261
|
-
setIsPopoverOpen(true);
|
|
262
|
-
setFocusedPopoverIndex(0);
|
|
263
|
-
} else {
|
|
264
|
-
inputRef?.current?.focus();
|
|
265
|
-
}
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
const handleKeyDown = (e: React.KeyboardEvent<HTMLElement>) => {
|
|
269
|
-
if (focusedTagIndex !== null && !isPopoverOpen) {
|
|
270
|
-
handleNavigationWithinSelectedTags(e);
|
|
271
|
-
} else if (isPopoverOpen && filteredTags.length > 0) {
|
|
272
|
-
handleNavigationWithinPopover(e);
|
|
273
|
-
} else {
|
|
274
|
-
handleInitialFocus(e);
|
|
275
|
-
}
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
return {
|
|
279
|
-
tags,
|
|
280
|
-
selectedTagsStack,
|
|
281
|
-
searchTags,
|
|
282
|
-
handleSelectTag,
|
|
283
|
-
handleUnselectTag,
|
|
284
|
-
// LOCAL PATCH (Contact Center) — see above.
|
|
285
|
-
handleCreateTag,
|
|
286
|
-
creatable,
|
|
287
|
-
handleKeyDown,
|
|
288
|
-
setFocusedTagIndex,
|
|
289
|
-
setFocusedPopoverIndex,
|
|
290
|
-
filterTagsBySearch,
|
|
291
|
-
filteredTags,
|
|
292
|
-
filteredSelectedTags,
|
|
293
|
-
focusedTagIndex,
|
|
294
|
-
focusedPopoverIndex,
|
|
295
|
-
isPopoverOpen,
|
|
296
|
-
setIsPopoverOpen,
|
|
297
|
-
reset,
|
|
298
|
-
singleSelect,
|
|
299
|
-
};
|
|
300
|
-
};
|