paris 0.21.2 → 0.21.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # paris
2
2
 
3
+ ## 0.21.3
4
+
5
+ ### Patch Changes
6
+
7
+ - d4b6239: fix(select,combobox): fix portaled dropdown width and alignment
8
+
9
+ - Use correct CSS variables (`--button-width`, `--input-width`) for portaled dropdown width in Select and Combobox since Headless UI v2.2.x no longer sets `--anchor-width`
10
+ - Measure actual pixel offset between container and input to align Combobox dropdown with the input container, not the input element
11
+ - Prevent `ComboboxButton` wrapper from intercepting keyboard events (e.g. space key) meant for the input
12
+
3
13
  ## 0.21.2
4
14
 
5
15
  ### Patch Changes
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "paris",
3
3
  "author": "Sanil Chawla <sanil@slingshot.fm> (https://sanil.co)",
4
4
  "description": "Paris is Slingshot's React design system. It's a collection of reusable components, design tokens, and guidelines that help us build consistent, accessible, and performant user interfaces.",
5
- "version": "0.21.2",
5
+ "version": "0.21.3",
6
6
  "homepage": "https://paris.slingshot.fm",
7
7
  "license": "MIT",
8
8
  "repository": {
@@ -2,10 +2,16 @@
2
2
 
3
3
  import { faClose } from '@fortawesome/free-solid-svg-icons';
4
4
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
5
- import { ComboboxInput, ComboboxOption, ComboboxOptions, Combobox as HCombobox } from '@headlessui/react';
5
+ import {
6
+ ComboboxButton,
7
+ ComboboxInput,
8
+ ComboboxOption,
9
+ ComboboxOptions,
10
+ Combobox as HCombobox,
11
+ } from '@headlessui/react';
6
12
  import { clsx } from 'clsx';
7
- import type { ComponentPropsWithoutRef, CSSProperties, ReactNode } from 'react';
8
- import { useId, useMemo, useState } from 'react';
13
+ import type { ComponentPropsWithoutRef, CSSProperties, MouseEvent, ReactNode } from 'react';
14
+ import { useCallback, useId, useLayoutEffect, useMemo, useRef, useState } from 'react';
9
15
  import { MemoizedEnhancer } from '../../helpers/renderEnhancer';
10
16
  import type { ButtonProps } from '../button';
11
17
  import { Button } from '../button';
@@ -162,6 +168,25 @@ export function Combobox<T extends Record<string, any> = Record<string, any>>({
162
168
  const inputID = useId();
163
169
  const [selectedID, setSelectedID] = useState<string | null>(value?.id || null);
164
170
  const [query, setQuery] = useState('');
171
+ const containerElRef = useRef<HTMLElement | null>(null);
172
+ const inputElRef = useRef<HTMLElement | null>(null);
173
+ const [anchorOffset, setAnchorOffset] = useState(0);
174
+
175
+ const containerRef = useCallback((node: HTMLButtonElement | null) => {
176
+ containerElRef.current = node;
177
+ }, []);
178
+
179
+ const inputRef = useCallback((node: HTMLInputElement | null) => {
180
+ inputElRef.current = node;
181
+ }, []);
182
+
183
+ useLayoutEffect(() => {
184
+ if (containerElRef.current && inputElRef.current) {
185
+ const containerLeft = containerElRef.current.getBoundingClientRect().left;
186
+ const inputLeft = inputElRef.current.getBoundingClientRect().left;
187
+ setAnchorOffset(containerLeft - inputLeft);
188
+ }
189
+ }, [startEnhancer, value]);
165
190
 
166
191
  const optionsWithCustomValue = useMemo(
167
192
  () => [...(allowCustomValue && customValueToOption ? [customValueToOption(query)] : []), ...options],
@@ -203,7 +228,10 @@ export function Combobox<T extends Record<string, any> = Record<string, any>>({
203
228
  }
204
229
  }}
205
230
  >
206
- <div
231
+ <ComboboxButton
232
+ as="div"
233
+ ref={containerRef}
234
+ tabIndex={-1}
207
235
  data-status={disabled ? 'disabled' : status || 'default'}
208
236
  {...overrides?.inputContainer}
209
237
  className={clsx(overrides?.inputContainer?.className, inputStyles.inputContainer)}
@@ -231,11 +259,20 @@ export function Combobox<T extends Record<string, any> = Record<string, any>>({
231
259
  value.node
232
260
  ) : (
233
261
  <ComboboxInput
262
+ ref={inputRef}
234
263
  id={inputID}
235
264
  {...overrides?.input}
236
265
  placeholder={placeholder}
237
266
  // value={query}
238
267
  displayValue={() => value?.node as string}
268
+ onClick={(e: MouseEvent<HTMLInputElement>) => {
269
+ e.stopPropagation();
270
+ overrides?.input?.onClick?.(e);
271
+ }}
272
+ onKeyDown={(e) => {
273
+ e.stopPropagation();
274
+ overrides?.input?.onKeyDown?.(e);
275
+ }}
239
276
  onChange={(e) => {
240
277
  setQuery(e.target.value);
241
278
  if (onInputChange) onInputChange(e.target.value);
@@ -261,7 +298,8 @@ export function Combobox<T extends Record<string, any> = Record<string, any>>({
261
298
  size="xs"
262
299
  shape="circle"
263
300
  startEnhancer={<FontAwesomeIcon icon={faClose} fontSize="10px" />}
264
- onClick={() => {
301
+ onClick={(e) => {
302
+ e.stopPropagation();
265
303
  if (onChange) {
266
304
  onChange(null);
267
305
  }
@@ -290,10 +328,14 @@ export function Combobox<T extends Record<string, any> = Record<string, any>>({
290
328
  )}
291
329
  </div>
292
330
  )}
293
- </div>
331
+ </ComboboxButton>
294
332
  <ComboboxOptions
295
333
  as="ul"
296
- anchor="bottom start"
334
+ anchor={{
335
+ to: 'bottom start',
336
+ gap: 9,
337
+ offset: anchorOffset,
338
+ }}
297
339
  transition
298
340
  {...overrides?.optionsContainer}
299
341
  className={clsx(overrides?.optionsContainer?.className, styles.options)}
@@ -25,7 +25,7 @@
25
25
  }
26
26
 
27
27
  .options {
28
- width: var(--anchor-width);
28
+ width: max(var(--button-width, 0px), var(--input-width, 0px));
29
29
  max-height: var(--options-maxHeight, auto);
30
30
  overflow-y: auto;
31
31
  overflow-x: hidden;