@lattice-ui/combobox 0.4.4 → 0.5.0-next.2

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.
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "combobox",
3
+ "tree": {
4
+ "$path": "out",
5
+ "out": {
6
+ "$path": "out"
7
+ }
8
+ }
9
+ }
@@ -1,3 +1,3 @@
1
1
  import { React } from "@lattice-ui/core";
2
2
  import type { ComboboxContentProps } from "./types";
3
- export declare function ComboboxContent(props: ComboboxContentProps): React.JSX.Element | undefined;
3
+ export declare function ComboboxContent(props: ComboboxContentProps): React.JSX.Element;
@@ -1,13 +1,23 @@
1
1
  -- Compiled with roblox-ts v3.0.0
2
2
  local TS = _G[script]
3
3
  local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
4
+ local composeRefs = _core.composeRefs
5
+ local getElementRef = _core.getElementRef
4
6
  local React = _core.React
5
- local Slot = _core.Slot
6
7
  local _layer = TS.import(script, TS.getModule(script, "@lattice-ui", "layer").out)
7
8
  local DismissableLayer = _layer.DismissableLayer
8
9
  local Presence = _layer.Presence
10
+ local _motion = TS.import(script, TS.getModule(script, "@lattice-ui", "motion").out)
11
+ local createCanvasGroupPopperEntranceRecipe = _motion.createCanvasGroupPopperEntranceRecipe
12
+ local usePresenceMotionController = _motion.usePresenceMotionController
9
13
  local usePopper = TS.import(script, TS.getModule(script, "@lattice-ui", "popper").out).usePopper
10
14
  local useComboboxContext = TS.import(script, script.Parent, "context").useComboboxContext
15
+ local CONTENT_OFFSET = 6
16
+ local HIDDEN_POSITION = UDim2.fromOffset(-9999, -9999)
17
+ local function toGuiPropBag(value)
18
+ local _value = value
19
+ return if type(_value) == "table" then value else {}
20
+ end
11
21
  local function toGuiObject(instance)
12
22
  if not instance or not instance:IsA("GuiObject") then
13
23
  return nil
@@ -16,83 +26,113 @@ local function toGuiObject(instance)
16
26
  end
17
27
  local function ComboboxContentImpl(props)
18
28
  local comboboxContext = useComboboxContext()
29
+ local open = comboboxContext.open
30
+ local shouldMeasure = open or props.motionPresent or props.onExitComplete ~= nil
19
31
  local popper = usePopper({
20
32
  anchorRef = comboboxContext.anchorRef,
21
33
  contentRef = comboboxContext.contentRef,
22
34
  placement = props.placement,
23
35
  offset = props.offset,
24
36
  padding = props.padding,
25
- enabled = props.enabled,
37
+ enabled = shouldMeasure,
38
+ })
39
+ local defaultTransition = React.useMemo(function()
40
+ return createCanvasGroupPopperEntranceRecipe(popper.placement, CONTENT_OFFSET)
41
+ end, { popper.placement })
42
+ local recipe = props.transition or defaultTransition
43
+ local motion = usePresenceMotionController({
44
+ present = props.motionPresent,
45
+ ready = popper.isPositioned,
46
+ forceMount = props.forceMount,
47
+ config = recipe,
48
+ onExitComplete = props.onExitComplete,
26
49
  })
27
50
  local setContentRef = React.useCallback(function(instance)
28
- comboboxContext.contentRef.current = toGuiObject(instance)
29
- end, { comboboxContext.contentRef })
51
+ local guiObject = toGuiObject(instance)
52
+ comboboxContext.contentRef.current = guiObject
53
+ motion.ref.current = guiObject
54
+ end, { comboboxContext.contentRef, motion.ref })
55
+ local handleDismiss = React.useCallback(function()
56
+ comboboxContext.setOpen(false)
57
+ end, { comboboxContext })
58
+ local shouldRender = motion.mounted
59
+ local contentVisible = shouldRender and (motion.present or motion.phase ~= "exited")
60
+ local popperPosition = if popper.isPositioned then popper.position else HIDDEN_POSITION
30
61
  local contentNode = if props.asChild then ((function()
31
62
  local child = props.children
32
63
  if not React.isValidElement(child) then
33
64
  error("[ComboboxContent] `asChild` requires a child element.")
34
65
  end
35
- return React.createElement(Slot, {
36
- AnchorPoint = popper.anchorPoint,
37
- Position = popper.position,
38
- Visible = props.visible,
66
+ local childProps = toGuiPropBag(child.props)
67
+ local childRef = getElementRef(child)
68
+ local _exp = child
69
+ local _object = table.clone(childProps)
70
+ setmetatable(_object, nil)
71
+ _object.Position = UDim2.fromOffset(0, 0)
72
+ _object.Visible = contentVisible
73
+ _object.ref = composeRefs(childRef)
74
+ return React.createElement("canvasgroup", {
75
+ AutomaticSize = Enum.AutomaticSize.XY,
76
+ BackgroundTransparency = 1,
77
+ BorderSizePixel = 0,
78
+ Size = UDim2.fromOffset(0, 0),
79
+ Visible = contentVisible,
39
80
  ref = setContentRef,
40
- }, child)
41
- end)()) else (React.createElement("frame", {
42
- AnchorPoint = popper.anchorPoint,
81
+ }, React.cloneElement(_exp, _object))
82
+ end)()) else (React.createElement("canvasgroup", {
83
+ AutomaticSize = Enum.AutomaticSize.XY,
43
84
  BackgroundTransparency = 1,
44
85
  BorderSizePixel = 0,
45
- Position = popper.position,
46
86
  Size = UDim2.fromOffset(0, 0),
47
- Visible = props.visible,
87
+ Visible = contentVisible,
48
88
  ref = setContentRef,
49
89
  }, props.children))
50
90
  return React.createElement(DismissableLayer, {
51
- enabled = props.enabled,
91
+ enabled = open,
52
92
  insideRefs = { comboboxContext.triggerRef, comboboxContext.inputRef },
53
93
  modal = false,
54
- onDismiss = props.onDismiss,
94
+ onDismiss = handleDismiss,
55
95
  onInteractOutside = props.onInteractOutside,
56
96
  onPointerDownOutside = props.onPointerDownOutside,
57
- }, contentNode)
97
+ }, React.createElement("frame", {
98
+ AnchorPoint = popper.anchorPoint,
99
+ BackgroundTransparency = 1,
100
+ BorderSizePixel = 0,
101
+ Position = popperPosition,
102
+ Size = UDim2.fromOffset(0, 0),
103
+ Visible = shouldRender,
104
+ }, contentNode))
58
105
  end
59
106
  local function ComboboxContent(props)
60
107
  local comboboxContext = useComboboxContext()
61
108
  local open = comboboxContext.open
62
- local forceMount = props.forceMount == true
63
- local handleDismiss = React.useCallback(function()
64
- comboboxContext.setOpen(false)
65
- end, { comboboxContext })
66
- if not open and not forceMount then
67
- return nil
68
- end
69
- if forceMount then
109
+ if props.forceMount then
70
110
  return React.createElement(ComboboxContentImpl, {
71
111
  asChild = props.asChild,
72
- enabled = open,
112
+ forceMount = props.forceMount,
113
+ motionPresent = open,
73
114
  offset = props.offset,
74
- onDismiss = handleDismiss,
75
115
  onInteractOutside = props.onInteractOutside,
76
116
  onPointerDownOutside = props.onPointerDownOutside,
77
117
  padding = props.padding,
78
118
  placement = props.placement,
79
- visible = open,
119
+ transition = props.transition,
80
120
  }, props.children)
81
121
  end
82
122
  return React.createElement(Presence, {
83
- exitFallbackMs = 0,
84
123
  present = open,
85
124
  render = function(state)
86
125
  return React.createElement(ComboboxContentImpl, {
87
126
  asChild = props.asChild,
88
- enabled = state.isPresent,
127
+ forceMount = props.forceMount,
128
+ motionPresent = state.isPresent,
89
129
  offset = props.offset,
90
- onDismiss = handleDismiss,
130
+ onExitComplete = state.onExitComplete,
91
131
  onInteractOutside = props.onInteractOutside,
92
132
  onPointerDownOutside = props.onPointerDownOutside,
93
133
  padding = props.padding,
94
134
  placement = props.placement,
95
- visible = state.isPresent,
135
+ transition = props.transition,
96
136
  }, props.children)
97
137
  end,
98
138
  })
@@ -26,13 +26,15 @@ local function ComboboxInput(props)
26
26
  comboboxContext.anchorRef.current = comboboxContext.triggerRef.current
27
27
  end
28
28
  end, { comboboxContext.anchorRef, comboboxContext.inputRef, comboboxContext.triggerRef })
29
+ local lastInputValueRef = React.useRef(comboboxContext.inputValue)
30
+ lastInputValueRef.current = comboboxContext.inputValue
29
31
  local handleTextChanged = React.useCallback(function(textBox)
30
- if textBox.Text == comboboxContext.inputValue then
32
+ if textBox.Text == lastInputValueRef.current then
31
33
  return nil
32
34
  end
33
35
  if disabled or readOnly then
34
- if textBox.Text ~= comboboxContext.inputValue then
35
- textBox.Text = comboboxContext.inputValue
36
+ if textBox.Text ~= lastInputValueRef.current then
37
+ textBox.Text = lastInputValueRef.current
36
38
  end
37
39
  return nil
38
40
  end
@@ -38,8 +38,9 @@ local function ComboboxItem(props)
38
38
  nextItemOrder += 1
39
39
  itemOrderRef.current = nextItemOrder
40
40
  end
41
+ local registerItem = comboboxContext.registerItem
41
42
  React.useEffect(function()
42
- return comboboxContext.registerItem({
43
+ return registerItem({
43
44
  id = itemIdRef.current,
44
45
  value = props.value,
45
46
  order = itemOrderRef.current,
@@ -50,7 +51,7 @@ local function ComboboxItem(props)
50
51
  return textValueRef.current
51
52
  end,
52
53
  })
53
- end, { comboboxContext, props.value })
54
+ end, { registerItem, props.value })
54
55
  local handleSelect = React.useCallback(function()
55
56
  if interactionDisabled then
56
57
  return nil
@@ -86,6 +86,21 @@ local function ComboboxRoot(props)
86
86
  local itemTextCacheRef = React.useRef({})
87
87
  local registryRevision, setRegistryRevision = React.useState(0)
88
88
  local registerItem = React.useCallback(function(item)
89
+ local _exp = itemEntriesRef.current
90
+ -- ▼ ReadonlyArray.filter ▼
91
+ local _newValue = {}
92
+ local _callback = function(entry)
93
+ return entry.id ~= item.id
94
+ end
95
+ local _length = 0
96
+ for _k, _v in _exp do
97
+ if _callback(_v, _k - 1, _exp) == true then
98
+ _length += 1
99
+ _newValue[_length] = _v
100
+ end
101
+ end
102
+ -- ▲ ReadonlyArray.filter ▲
103
+ itemEntriesRef.current = _newValue
89
104
  local _current = itemEntriesRef.current
90
105
  local _item = item
91
106
  table.insert(_current, _item)
@@ -94,14 +109,14 @@ local function ComboboxRoot(props)
94
109
  return revision + 1
95
110
  end)
96
111
  return function()
97
- local _exp = itemEntriesRef.current
112
+ local _exp_1 = itemEntriesRef.current
98
113
  -- ▼ ReadonlyArray.findIndex ▼
99
- local _callback = function(entry)
114
+ local _callback_1 = function(entry)
100
115
  return entry.id == item.id
101
116
  end
102
117
  local _result = -1
103
- for _i, _v in _exp do
104
- if _callback(_v, _i - 1, _exp) == true then
118
+ for _i, _v in _exp_1 do
119
+ if _callback_1(_v, _i - 1, _exp_1) == true then
105
120
  _result = _i - 1
106
121
  break
107
122
  end
@@ -110,6 +125,7 @@ local function ComboboxRoot(props)
110
125
  local index = _result
111
126
  if index >= 0 then
112
127
  table.remove(itemEntriesRef.current, index + 1)
128
+ itemTextCacheRef.current[item.value] = nil
113
129
  setRegistryRevision(function(revision)
114
130
  return revision + 1
115
131
  end)
@@ -23,11 +23,7 @@ local function resolveForcedComboboxValue(currentValue, options)
23
23
  return nil
24
24
  end
25
25
  if currentValue == nil then
26
- local _result = enabled[1]
27
- if _result ~= nil then
28
- _result = _result.value
29
- end
30
- return _result
26
+ return nil
31
27
  end
32
28
  -- ▼ ReadonlyArray.find ▼
33
29
  local _callback_1 = function(option)
@@ -1,4 +1,5 @@
1
1
  import type { LayerInteractEvent } from "@lattice-ui/layer";
2
+ import type { PresenceMotionConfig as MotionConfig } from "@lattice-ui/motion";
2
3
  import type { PopperPlacement } from "@lattice-ui/popper";
3
4
  import type React from "@rbxts/react";
4
5
  export type ComboboxFilterFn = (itemText: string, query: string) => boolean;
@@ -70,6 +71,7 @@ export type ComboboxPortalProps = {
70
71
  children?: React.ReactNode;
71
72
  };
72
73
  export type ComboboxContentProps = {
74
+ transition?: MotionConfig;
73
75
  asChild?: boolean;
74
76
  forceMount?: boolean;
75
77
  placement?: PopperPlacement;
package/package.json CHANGED
@@ -1,13 +1,25 @@
1
1
  {
2
2
  "name": "@lattice-ui/combobox",
3
- "version": "0.4.4",
3
+ "version": "0.5.0-next.2",
4
4
  "private": false,
5
5
  "main": "out/init.luau",
6
6
  "types": "out/index.d.ts",
7
+ "source": "src/index.ts",
8
+ "files": [
9
+ "default.project.json",
10
+ "out",
11
+ "src",
12
+ "README.md"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/astra-void/lattice-ui.git"
17
+ },
7
18
  "dependencies": {
8
- "@lattice-ui/core": "0.4.4",
9
- "@lattice-ui/popper": "0.4.4",
10
- "@lattice-ui/layer": "0.4.4"
19
+ "@lattice-ui/core": "0.5.0-next.2",
20
+ "@lattice-ui/motion": "0.5.0-next.2",
21
+ "@lattice-ui/popper": "0.5.0-next.2",
22
+ "@lattice-ui/layer": "0.5.0-next.2"
11
23
  },
12
24
  "devDependencies": {
13
25
  "@rbxts/react": "17.3.7-ts.1",
@@ -19,6 +31,8 @@
19
31
  },
20
32
  "scripts": {
21
33
  "build": "rbxtsc -p tsconfig.json",
34
+ "lint": "eslint .",
35
+ "lint:fix": "eslint . --fix",
22
36
  "typecheck": "tsc -p tsconfig.typecheck.json",
23
37
  "watch": "rbxtsc -p tsconfig.json -w"
24
38
  }
@@ -1,29 +1,44 @@
1
- import { React, Slot } from "@lattice-ui/core";
1
+ import { composeRefs, getElementRef, React } from "@lattice-ui/core";
2
+ import type { LayerInteractEvent } from "@lattice-ui/layer";
2
3
  import { DismissableLayer, Presence } from "@lattice-ui/layer";
4
+ import { createCanvasGroupPopperEntranceRecipe, usePresenceMotionController } from "@lattice-ui/motion";
5
+ import type { PopperPlacement } from "@lattice-ui/popper";
3
6
  import { usePopper } from "@lattice-ui/popper";
4
7
  import { useComboboxContext } from "./context";
5
8
  import type { ComboboxContentProps } from "./types";
6
9
 
7
- type ComboboxContentImplProps = {
8
- enabled: boolean;
9
- visible: boolean;
10
- onDismiss: () => void;
11
- asChild?: boolean;
12
- placement?: ComboboxContentProps["placement"];
13
- offset?: ComboboxContentProps["offset"];
14
- padding?: ComboboxContentProps["padding"];
15
- } & Pick<ComboboxContentProps, "children" | "onInteractOutside" | "onPointerDownOutside">;
10
+ const CONTENT_OFFSET = 6;
11
+ const HIDDEN_POSITION = UDim2.fromOffset(-9999, -9999);
12
+
13
+ type GuiPropBag = React.Attributes & Record<string, unknown>;
14
+
15
+ function toGuiPropBag(value: unknown): GuiPropBag {
16
+ return typeIs(value, "table") ? (value as GuiPropBag) : {};
17
+ }
16
18
 
17
19
  function toGuiObject(instance: Instance | undefined) {
18
20
  if (!instance || !instance.IsA("GuiObject")) {
19
21
  return undefined;
20
22
  }
21
-
22
23
  return instance;
23
24
  }
24
25
 
25
- function ComboboxContentImpl(props: ComboboxContentImplProps) {
26
+ function ComboboxContentImpl(props: {
27
+ motionPresent: boolean;
28
+ onExitComplete?: () => void;
29
+ placement?: PopperPlacement;
30
+ offset?: Vector2;
31
+ padding?: number;
32
+ forceMount?: boolean;
33
+ onPointerDownOutside?: (event: LayerInteractEvent) => void;
34
+ onInteractOutside?: (event: LayerInteractEvent) => void;
35
+ asChild?: boolean;
36
+ transition?: ComboboxContentProps["transition"];
37
+ children?: React.ReactNode;
38
+ }) {
26
39
  const comboboxContext = useComboboxContext();
40
+ const open = comboboxContext.open;
41
+ const shouldMeasure = open || props.motionPresent || props.onExitComplete !== undefined;
27
42
 
28
43
  const popper = usePopper({
29
44
  anchorRef: comboboxContext.anchorRef,
@@ -31,16 +46,40 @@ function ComboboxContentImpl(props: ComboboxContentImplProps) {
31
46
  placement: props.placement,
32
47
  offset: props.offset,
33
48
  padding: props.padding,
34
- enabled: props.enabled,
49
+ enabled: shouldMeasure,
50
+ });
51
+
52
+ const defaultTransition = React.useMemo(
53
+ () => createCanvasGroupPopperEntranceRecipe(popper.placement, CONTENT_OFFSET),
54
+ [popper.placement],
55
+ );
56
+ const recipe = props.transition ?? defaultTransition;
57
+
58
+ const motion = usePresenceMotionController<GuiObject>({
59
+ present: props.motionPresent,
60
+ ready: popper.isPositioned,
61
+ forceMount: props.forceMount,
62
+ config: recipe,
63
+ onExitComplete: props.onExitComplete,
35
64
  });
36
65
 
37
66
  const setContentRef = React.useCallback(
38
67
  (instance: Instance | undefined) => {
39
- comboboxContext.contentRef.current = toGuiObject(instance);
68
+ const guiObject = toGuiObject(instance);
69
+ comboboxContext.contentRef.current = guiObject;
70
+ motion.ref.current = guiObject;
40
71
  },
41
- [comboboxContext.contentRef],
72
+ [comboboxContext.contentRef, motion.ref],
42
73
  );
43
74
 
75
+ const handleDismiss = React.useCallback(() => {
76
+ comboboxContext.setOpen(false);
77
+ }, [comboboxContext]);
78
+
79
+ const shouldRender = motion.mounted;
80
+ const contentVisible = shouldRender && (motion.present || motion.phase !== "exited");
81
+ const popperPosition = popper.isPositioned ? popper.position : HIDDEN_POSITION;
82
+
44
83
  const contentNode = props.asChild ? (
45
84
  (() => {
46
85
  const child = props.children;
@@ -48,36 +87,59 @@ function ComboboxContentImpl(props: ComboboxContentImplProps) {
48
87
  error("[ComboboxContent] `asChild` requires a child element.");
49
88
  }
50
89
 
90
+ const childProps = toGuiPropBag((child as { props?: unknown }).props);
91
+ const childRef = getElementRef<Instance>(child);
92
+
51
93
  return (
52
- <Slot AnchorPoint={popper.anchorPoint} Position={popper.position} Visible={props.visible} ref={setContentRef}>
53
- {child}
54
- </Slot>
94
+ <canvasgroup
95
+ AutomaticSize={Enum.AutomaticSize.XY}
96
+ BackgroundTransparency={1}
97
+ BorderSizePixel={0}
98
+ Size={UDim2.fromOffset(0, 0)}
99
+ Visible={contentVisible}
100
+ ref={setContentRef as React.Ref<CanvasGroup>}
101
+ >
102
+ {React.cloneElement(child as React.ReactElement<GuiPropBag>, {
103
+ ...childProps,
104
+ Position: UDim2.fromOffset(0, 0),
105
+ Visible: contentVisible,
106
+ ref: composeRefs(childRef),
107
+ })}
108
+ </canvasgroup>
55
109
  );
56
110
  })()
57
111
  ) : (
58
- <frame
59
- AnchorPoint={popper.anchorPoint}
112
+ <canvasgroup
113
+ AutomaticSize={Enum.AutomaticSize.XY}
60
114
  BackgroundTransparency={1}
61
115
  BorderSizePixel={0}
62
- Position={popper.position}
63
116
  Size={UDim2.fromOffset(0, 0)}
64
- Visible={props.visible}
117
+ Visible={contentVisible}
65
118
  ref={setContentRef}
66
119
  >
67
120
  {props.children}
68
- </frame>
121
+ </canvasgroup>
69
122
  );
70
123
 
71
124
  return (
72
125
  <DismissableLayer
73
- enabled={props.enabled}
126
+ enabled={open}
74
127
  insideRefs={[comboboxContext.triggerRef, comboboxContext.inputRef]}
75
128
  modal={false}
76
- onDismiss={props.onDismiss}
129
+ onDismiss={handleDismiss}
77
130
  onInteractOutside={props.onInteractOutside}
78
131
  onPointerDownOutside={props.onPointerDownOutside}
79
132
  >
80
- {contentNode}
133
+ <frame
134
+ AnchorPoint={popper.anchorPoint}
135
+ BackgroundTransparency={1}
136
+ BorderSizePixel={0}
137
+ Position={popperPosition}
138
+ Size={UDim2.fromOffset(0, 0)}
139
+ Visible={shouldRender}
140
+ >
141
+ {contentNode}
142
+ </frame>
81
143
  </DismissableLayer>
82
144
  );
83
145
  }
@@ -85,28 +147,19 @@ function ComboboxContentImpl(props: ComboboxContentImplProps) {
85
147
  export function ComboboxContent(props: ComboboxContentProps) {
86
148
  const comboboxContext = useComboboxContext();
87
149
  const open = comboboxContext.open;
88
- const forceMount = props.forceMount === true;
89
-
90
- const handleDismiss = React.useCallback(() => {
91
- comboboxContext.setOpen(false);
92
- }, [comboboxContext]);
93
-
94
- if (!open && !forceMount) {
95
- return undefined;
96
- }
97
150
 
98
- if (forceMount) {
151
+ if (props.forceMount) {
99
152
  return (
100
153
  <ComboboxContentImpl
101
154
  asChild={props.asChild}
102
- enabled={open}
155
+ forceMount={props.forceMount}
156
+ motionPresent={open}
103
157
  offset={props.offset}
104
- onDismiss={handleDismiss}
105
158
  onInteractOutside={props.onInteractOutside}
106
159
  onPointerDownOutside={props.onPointerDownOutside}
107
160
  padding={props.padding}
108
161
  placement={props.placement}
109
- visible={open}
162
+ transition={props.transition}
110
163
  >
111
164
  {props.children}
112
165
  </ComboboxContentImpl>
@@ -115,19 +168,19 @@ export function ComboboxContent(props: ComboboxContentProps) {
115
168
 
116
169
  return (
117
170
  <Presence
118
- exitFallbackMs={0}
119
171
  present={open}
120
172
  render={(state) => (
121
173
  <ComboboxContentImpl
122
174
  asChild={props.asChild}
123
- enabled={state.isPresent}
175
+ forceMount={props.forceMount}
176
+ motionPresent={state.isPresent}
124
177
  offset={props.offset}
125
- onDismiss={handleDismiss}
178
+ onExitComplete={state.onExitComplete}
126
179
  onInteractOutside={props.onInteractOutside}
127
180
  onPointerDownOutside={props.onPointerDownOutside}
128
181
  padding={props.padding}
129
182
  placement={props.placement}
130
- visible={state.isPresent}
183
+ transition={props.transition}
131
184
  >
132
185
  {props.children}
133
186
  </ComboboxContentImpl>
@@ -34,15 +34,18 @@ export function ComboboxInput(props: ComboboxInputProps) {
34
34
  [comboboxContext.anchorRef, comboboxContext.inputRef, comboboxContext.triggerRef],
35
35
  );
36
36
 
37
+ const lastInputValueRef = React.useRef(comboboxContext.inputValue);
38
+ lastInputValueRef.current = comboboxContext.inputValue;
39
+
37
40
  const handleTextChanged = React.useCallback(
38
41
  (textBox: TextBox) => {
39
- if (textBox.Text === comboboxContext.inputValue) {
42
+ if (textBox.Text === lastInputValueRef.current) {
40
43
  return;
41
44
  }
42
45
 
43
46
  if (disabled || readOnly) {
44
- if (textBox.Text !== comboboxContext.inputValue) {
45
- textBox.Text = comboboxContext.inputValue;
47
+ if (textBox.Text !== lastInputValueRef.current) {
48
+ textBox.Text = lastInputValueRef.current;
46
49
  }
47
50
 
48
51
  return;
@@ -35,15 +35,17 @@ export function ComboboxItem(props: ComboboxItemProps) {
35
35
  itemOrderRef.current = nextItemOrder;
36
36
  }
37
37
 
38
+ const registerItem = comboboxContext.registerItem;
39
+
38
40
  React.useEffect(() => {
39
- return comboboxContext.registerItem({
41
+ return registerItem({
40
42
  id: itemIdRef.current,
41
43
  value: props.value,
42
44
  order: itemOrderRef.current,
43
45
  getDisabled: () => disabledRef.current,
44
46
  getTextValue: () => textValueRef.current,
45
47
  });
46
- }, [comboboxContext, props.value]);
48
+ }, [registerItem, props.value]);
47
49
 
48
50
  const handleSelect = React.useCallback(() => {
49
51
  if (interactionDisabled) {
@@ -55,6 +55,7 @@ export function ComboboxRoot(props: ComboboxProps) {
55
55
  const [registryRevision, setRegistryRevision] = React.useState(0);
56
56
 
57
57
  const registerItem = React.useCallback((item: ComboboxItemRegistration) => {
58
+ itemEntriesRef.current = itemEntriesRef.current.filter((entry) => entry.id !== item.id);
58
59
  itemEntriesRef.current.push(item);
59
60
  itemTextCacheRef.current[item.value] = item.getTextValue();
60
61
  setRegistryRevision((revision) => revision + 1);
@@ -63,6 +64,7 @@ export function ComboboxRoot(props: ComboboxProps) {
63
64
  const index = itemEntriesRef.current.findIndex((entry) => entry.id === item.id);
64
65
  if (index >= 0) {
65
66
  itemEntriesRef.current.remove(index);
67
+ delete itemTextCacheRef.current[item.value];
66
68
  setRegistryRevision((revision) => revision + 1);
67
69
  }
68
70
  };
@@ -19,7 +19,7 @@ export function resolveForcedComboboxValue(currentValue: string | undefined, opt
19
19
  }
20
20
 
21
21
  if (currentValue === undefined) {
22
- return enabled[0]?.value;
22
+ return undefined;
23
23
  }
24
24
 
25
25
  const selected = enabled.find((option) => option.value === currentValue);
@@ -1,4 +1,5 @@
1
1
  import type { LayerInteractEvent } from "@lattice-ui/layer";
2
+ import type { PresenceMotionConfig as MotionConfig } from "@lattice-ui/motion";
2
3
  import type { PopperPlacement } from "@lattice-ui/popper";
3
4
  import type React from "@rbxts/react";
4
5
 
@@ -80,6 +81,7 @@ export type ComboboxPortalProps = {
80
81
  };
81
82
 
82
83
  export type ComboboxContentProps = {
84
+ transition?: MotionConfig;
83
85
  asChild?: boolean;
84
86
  forceMount?: boolean;
85
87
  placement?: PopperPlacement;
@@ -1,4 +0,0 @@
1
-
2
- > @lattice-ui/combobox@0.4.4 build C:\Users\retur\OneDrive\Desktop\Workspace\rojo\lattice-ui\packages\combobox
3
- > rbxtsc -p tsconfig.json
4
-
@@ -1,4 +0,0 @@
1
-
2
- > @lattice-ui/combobox@0.4.4 typecheck C:\Users\retur\OneDrive\Desktop\Workspace\rojo\lattice-ui\packages\combobox
3
- > tsc -p tsconfig.typecheck.json
4
-
package/tsconfig.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "rootDir": "src",
5
- "outDir": "out",
6
- "declaration": true,
7
- "typeRoots": [
8
- "./node_modules/@rbxts",
9
- "../../node_modules/@rbxts",
10
- "./node_modules/@lattice-ui",
11
- "../../node_modules/@lattice-ui"
12
- ],
13
- "types": ["types", "compiler-types"]
14
- },
15
- "include": ["src"]
16
- }
@@ -1,35 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "noEmit": true,
5
- "baseUrl": "..",
6
- "rootDir": "..",
7
- "paths": {
8
- "@lattice-ui/accordion": ["accordion/src/index.ts"],
9
- "@lattice-ui/avatar": ["avatar/src/index.ts"],
10
- "@lattice-ui/checkbox": ["checkbox/src/index.ts"],
11
- "@lattice-ui/combobox": ["combobox/src/index.ts"],
12
- "@lattice-ui/core": ["core/src/index.ts"],
13
- "@lattice-ui/dialog": ["dialog/src/index.ts"],
14
- "@lattice-ui/focus": ["focus/src/index.ts"],
15
- "@lattice-ui/layer": ["layer/src/index.ts"],
16
- "@lattice-ui/menu": ["menu/src/index.ts"],
17
- "@lattice-ui/popover": ["popover/src/index.ts"],
18
- "@lattice-ui/popper": ["popper/src/index.ts"],
19
- "@lattice-ui/progress": ["progress/src/index.ts"],
20
- "@lattice-ui/radio-group": ["radio-group/src/index.ts"],
21
- "@lattice-ui/scroll-area": ["scroll-area/src/index.ts"],
22
- "@lattice-ui/select": ["select/src/index.ts"],
23
- "@lattice-ui/slider": ["slider/src/index.ts"],
24
- "@lattice-ui/style": ["style/src/index.ts"],
25
- "@lattice-ui/switch": ["switch/src/index.ts"],
26
- "@lattice-ui/system": ["system/src/index.ts"],
27
- "@lattice-ui/tabs": ["tabs/src/index.ts"],
28
- "@lattice-ui/text-field": ["text-field/src/index.ts"],
29
- "@lattice-ui/textarea": ["textarea/src/index.ts"],
30
- "@lattice-ui/toast": ["toast/src/index.ts"],
31
- "@lattice-ui/toggle-group": ["toggle-group/src/index.ts"],
32
- "@lattice-ui/tooltip": ["tooltip/src/index.ts"]
33
- }
34
- }
35
- }