paris 0.21.1 → 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,21 @@
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
+
13
+ ## 0.21.2
14
+
15
+ ### Patch Changes
16
+
17
+ - 61edbc8: Fix portaled dropdowns (Select, Menu, Combobox) not appearing inside Drawers and Dialogs by moving the dropdown z-index layer (350) above the overlay layer (300).
18
+
3
19
  ## 0.21.1
4
20
 
5
21
  ### 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.1",
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)}
@@ -5,6 +5,7 @@ import { Callout } from '../callout';
5
5
  import { ChevronRight, Ellipsis } from '../icon';
6
6
  import { Menu, MenuButton, MenuItem, MenuItems } from '../menu';
7
7
  import { usePagination } from '../pagination';
8
+ import { Select } from '../select';
8
9
  import { Drawer } from './Drawer';
9
10
 
10
11
  const meta: Meta<typeof Drawer> = {
@@ -259,6 +260,42 @@ export const BottomPanelMultiSection: Story = {
259
260
  },
260
261
  };
261
262
 
263
+ export const WithSelectDropdown: Story = {
264
+ args: {
265
+ title: 'Settings',
266
+ size: 'default',
267
+ },
268
+ render: function Render(args) {
269
+ const [isOpen, setIsOpen] = useState(false);
270
+ const [selected, setSelected] = useState<string | null>(null);
271
+ return (
272
+ <>
273
+ <Button onClick={() => setIsOpen(true)}>Open drawer</Button>
274
+ <Drawer {...args} isOpen={isOpen} onClose={setIsOpen}>
275
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
276
+ <p>Try opening the Select dropdown below — it should appear above the Drawer.</p>
277
+ <Select
278
+ label="Country"
279
+ placeholder="Select a country"
280
+ value={selected}
281
+ onChange={setSelected}
282
+ options={[
283
+ { id: 'us', node: 'United States' },
284
+ { id: 'uk', node: 'United Kingdom' },
285
+ { id: 'ca', node: 'Canada' },
286
+ { id: 'au', node: 'Australia' },
287
+ { id: 'de', node: 'Germany' },
288
+ { id: 'fr', node: 'France' },
289
+ { id: 'jp', node: 'Japan' },
290
+ ]}
291
+ />
292
+ </div>
293
+ </Drawer>
294
+ </>
295
+ );
296
+ },
297
+ };
298
+
262
299
  export const Full: Story = {
263
300
  args: {
264
301
  title: 'Transaction details',
@@ -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;
@@ -435,8 +435,8 @@ export type Theme = {
435
435
  layers: {
436
436
  below: number;
437
437
  sticky: number;
438
- dropdown: number;
439
438
  overlay: number;
439
+ dropdown: number;
440
440
  popover: number;
441
441
  menu: number;
442
442
  };
@@ -1065,8 +1065,8 @@ export const LightTheme: Theme = {
1065
1065
  layers: {
1066
1066
  below: -1,
1067
1067
  sticky: 100,
1068
- dropdown: 200,
1069
1068
  overlay: 300,
1069
+ dropdown: 350,
1070
1070
  popover: 400,
1071
1071
  menu: 500,
1072
1072
  },