1 | import * as React from "react";
|
2 | import { createComponent } from "reakit-system/createComponent";
|
3 | import { createHook } from "reakit-system/createHook";
|
4 | import { useLiveRef } from "reakit-utils/useLiveRef";
|
5 | import { BoxOptions, BoxHTMLProps, useBox } from "../Box/Box";
|
6 | import {
|
7 | CompositeItemOptions,
|
8 | CompositeItemHTMLProps,
|
9 | useCompositeItem,
|
10 | } from "../Composite/CompositeItem";
|
11 | import { unstable_ComboboxStateReturn } from "./ComboboxState";
|
12 | import { COMBOBOX_ITEM_KEYS } from "./__keys";
|
13 | import { getItemId } from "./__utils/getItemId";
|
14 | import { Item } from "./__utils/types";
|
15 |
|
16 | export const unstable_useComboboxItem = createHook<
|
17 | unstable_ComboboxItemOptions,
|
18 | unstable_ComboboxItemHTMLProps
|
19 | >({
|
20 | name: "ComboboxItem",
|
21 | compose: useBox,
|
22 | keys: COMBOBOX_ITEM_KEYS,
|
23 |
|
24 | propsAreEqual(prev, next) {
|
25 | if (prev.value !== next.value) return false;
|
26 | if (!prev.value || !next.value || !prev.baseId || !next.baseId) {
|
27 | return useCompositeItem.unstable_propsAreEqual(prev, next);
|
28 | }
|
29 | const {
|
30 | currentValue: prevCurrentValue,
|
31 | inputValue: prevInputValue,
|
32 |
|
33 | matches: prevMatches,
|
34 | ...prevProps
|
35 | } = prev;
|
36 | const {
|
37 | currentValue: nextCurrentValue,
|
38 | inputValue: nextInputValue,
|
39 |
|
40 | matches: nextMatches,
|
41 | ...nextProps
|
42 | } = next;
|
43 | if (prevCurrentValue !== nextCurrentValue) {
|
44 | if (next.value === prevCurrentValue || next.value === nextCurrentValue) {
|
45 | return false;
|
46 | }
|
47 | }
|
48 | const prevId = getItemId(prev.baseId, prev.value, prev.id);
|
49 | const nextId = getItemId(next.baseId, next.value, prev.id);
|
50 | return useCompositeItem.unstable_propsAreEqual(
|
51 | { ...prevProps, id: prevId },
|
52 | { ...nextProps, id: nextId }
|
53 | );
|
54 | },
|
55 |
|
56 | useOptions(options) {
|
57 | const trulyDisabled = options.disabled && !options.focusable;
|
58 | const value = trulyDisabled ? undefined : options.value;
|
59 |
|
60 | const registerItem = React.useCallback(
|
61 | (item: Item) => {
|
62 | if (options.visible) {
|
63 | options.registerItem?.({ ...item, value });
|
64 | }
|
65 | },
|
66 | [options.registerItem, options.visible, value]
|
67 | );
|
68 |
|
69 | if (options.id || !options.baseId || !options.value) {
|
70 | return { ...options, registerItem };
|
71 | }
|
72 |
|
73 | const id = getItemId(options.baseId, options.value, options.id);
|
74 | return { ...options, registerItem, id };
|
75 | },
|
76 |
|
77 | useProps(options, { onClick: htmlOnClick, ...htmlProps }) {
|
78 | const onClickRef = useLiveRef(htmlOnClick);
|
79 |
|
80 | const onClick = React.useCallback(
|
81 | (event: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
82 | onClickRef.current?.(event);
|
83 | if (event.defaultPrevented) return;
|
84 | if (!options.value) return;
|
85 | options.hide?.();
|
86 | options.setInputValue?.(options.value);
|
87 | },
|
88 | [options.hide, options.setInputValue, options.value]
|
89 | );
|
90 |
|
91 | return {
|
92 | children: options.value,
|
93 | onClick,
|
94 | tabIndex: -1,
|
95 | ...htmlProps,
|
96 | };
|
97 | },
|
98 | });
|
99 |
|
100 | export const unstable_ComboboxItem = createComponent({
|
101 | as: "span",
|
102 | memo: true,
|
103 | useHook: unstable_useComboboxItem,
|
104 | });
|
105 |
|
106 | export type unstable_ComboboxItemOptions = BoxOptions &
|
107 | CompositeItemOptions &
|
108 | Pick<
|
109 | Partial<unstable_ComboboxStateReturn>,
|
110 | "currentValue" | "inputValue" | "hide" | "visible"
|
111 | > &
|
112 | Pick<unstable_ComboboxStateReturn, "setInputValue" | "registerItem"> & {
|
113 | |
114 |
|
115 |
|
116 |
|
117 |
|
118 | value?: string;
|
119 | };
|
120 |
|
121 | export type unstable_ComboboxItemHTMLProps = BoxHTMLProps &
|
122 | CompositeItemHTMLProps;
|
123 |
|
124 | export type unstable_ComboboxItemProps = unstable_ComboboxItemOptions &
|
125 | unstable_ComboboxItemHTMLProps;
|