diff --git a/.changeset/afraid-adults-thank.md b/.changeset/afraid-adults-thank.md new file mode 100644 index 000000000..4357b3367 --- /dev/null +++ b/.changeset/afraid-adults-thank.md @@ -0,0 +1,8 @@ +--- +"@cube-dev/ui-kit": patch +--- + +Replace `noCard` prop with `type` prop in ListBox component. The new `type` prop accepts three values: +- `card` (default): Standard card styling with border and margin +- `plain`: No border, no margin, no radius - suitable for embedded use +- `popover`: No border, but keeps margin and radius - suitable for overlay use diff --git a/.changeset/cuddly-meals-compete.md b/.changeset/cuddly-meals-compete.md new file mode 100644 index 000000000..6cde52bdb --- /dev/null +++ b/.changeset/cuddly-meals-compete.md @@ -0,0 +1,5 @@ +--- +"@cube-dev/ui-kit": patch +--- + +Remove the hardcoded default width for NumberInput. diff --git a/.changeset/tame-bananas-teach.md b/.changeset/tame-bananas-teach.md new file mode 100644 index 000000000..941edc439 --- /dev/null +++ b/.changeset/tame-bananas-teach.md @@ -0,0 +1,5 @@ +--- +"@cube-dev/ui-kit": patch +--- + +Fix right padding in NumberInput. diff --git a/src/components/fields/ComboBox/ComboBox.tsx b/src/components/fields/ComboBox/ComboBox.tsx index bcd9cf4e2..81e676629 100644 --- a/src/components/fields/ComboBox/ComboBox.tsx +++ b/src/components/fields/ComboBox/ComboBox.tsx @@ -929,9 +929,7 @@ function ComboBoxOverlay({ headingStyles={headingStyles} stateRef={listStateRef} size="medium" - mods={{ - popover: true, - }} + type="popover" onSelectionChange={onSelectionChange} > {children as any} diff --git a/src/components/fields/FilterListBox/FilterListBox.tsx b/src/components/fields/FilterListBox/FilterListBox.tsx index cc5135a78..e1620daae 100644 --- a/src/components/fields/FilterListBox/FilterListBox.tsx +++ b/src/components/fields/FilterListBox/FilterListBox.tsx @@ -103,7 +103,7 @@ const StyledHeaderWithoutBorder = tasty(StyledHeader, { }); export interface CubeFilterListBoxProps - extends Omit, 'filter'>, + extends Omit, 'filter' | 'type'>, FieldBaseProps { /** Placeholder text for the search input */ searchPlaceholder?: string; diff --git a/src/components/fields/ListBox/ListBox.docs.mdx b/src/components/fields/ListBox/ListBox.docs.mdx index d9f3a739a..853da85c8 100644 --- a/src/components/fields/ListBox/ListBox.docs.mdx +++ b/src/components/fields/ListBox/ListBox.docs.mdx @@ -126,6 +126,16 @@ The `mods` property accepts the following modifiers you can override: - Array of keys for items that should be disabled - Disabled items cannot be selected and are visually distinguished +### Visual Styling + +**type** (`'card' | 'plain' | 'popover'`, default: `'card'`) +- Controls the visual styling of the ListBox container +- `card`: Standard card styling with border and margin (default) +- `plain`: No border, no margin, no radius - suitable for embedded use +- `popover`: No border, but keeps margin and radius - suitable for overlay use +- Use `plain` when embedding ListBox directly into another component +- Use `popover` when using ListBox inside overlays (Dialog, ComboBox, Picker) + ### Focus and Interaction **focusOnHover** (`boolean`, default: `true`) @@ -515,6 +525,30 @@ const [selectedKey, setSelectedKey] = useState('apple'); ``` +### Visual Type Variants + + + +```jsx +{/* Card type (default) - with border and margin */} + + Apple + Banana + + +{/* Plain type - no border, no margin, no radius */} + + Apple + Banana + + +{/* Popover type - no border, but keeps margin and radius */} + + Apple + Banana + +``` + ### Custom Escape Handling diff --git a/src/components/fields/ListBox/ListBox.stories.tsx b/src/components/fields/ListBox/ListBox.stories.tsx index 018470f7f..1b8a34857 100644 --- a/src/components/fields/ListBox/ListBox.stories.tsx +++ b/src/components/fields/ListBox/ListBox.stories.tsx @@ -82,6 +82,14 @@ const meta: any = { defaultValue: { summary: 'medium' }, }, }, + type: { + options: ['card', 'plain', 'popover'], + control: { type: 'radio' }, + description: 'Visual type of the ListBox', + table: { + defaultValue: { summary: 'card' }, + }, + }, header: { control: { type: 'text' }, description: 'Custom header content', @@ -1392,3 +1400,102 @@ export const AllValuePropsExample: Story = { }, }, }; + +export const TypeVariants: Story = { + render: () => ( + +
+ + Card Type (default) + + + Standard card styling with border and margin + + + + {fruits.slice(0, 4).map((fruit) => ( + {fruit.label} + ))} + +
+ +
+ + Plain Type + + + No border, no margin, no radius - suitable for embedded use + + + + {fruits.slice(0, 4).map((fruit) => ( + {fruit.label} + ))} + +
+ +
+ + Popover Type + + + No border, but keeps margin and radius - suitable for overlay use + + + + {fruits.slice(0, 4).map((fruit) => ( + {fruit.label} + ))} + +
+ +
+ + Plain Type with Sections + + + Section margins are preserved even with plain type + + + + + Apple + Banana + + + Carrot + Broccoli + + +
+
+ ), + parameters: { + docs: { + description: { + story: + 'The `type` prop controls the visual styling of the ListBox. Use `card` for standalone use, `plain` for embedded use without decoration, and `popover` for use inside overlays where borders are handled by the container.', + }, + }, + }, +}; diff --git a/src/components/fields/ListBox/ListBox.tsx b/src/components/fields/ListBox/ListBox.tsx index 0eb2eba66..3494f2705 100644 --- a/src/components/fields/ListBox/ListBox.tsx +++ b/src/components/fields/ListBox/ListBox.tsx @@ -65,7 +65,11 @@ const ListBoxWrapperElement = tasty({ flow: 'column', gap: 0, position: 'relative', - radius: '1cr', + radius: { + '': '1cr', + '[data-type="popover"]': '(1cr - 1bw)', + '[data-type="plain"]': '0', + }, color: '#dark-02', transition: 'theme', outline: { @@ -79,7 +83,7 @@ const ListBoxWrapperElement = tasty({ valid: '#success-text.50', invalid: '#danger-text.50', disabled: true, - 'popover | searchable': false, + '[data-type="plain"] | [data-type="popover"] | searchable': false, }, }, }); @@ -93,7 +97,7 @@ const ListElement = tasty({ boxSizing: 'border-box', margin: { '': '.5x .5x 0 .5x', - sections: '.5x .5x 0 .5x', + '[data-type="plain"]': '0', }, height: 'max-content', }, @@ -309,6 +313,15 @@ export interface CubeListBoxProps * Defaults to "No items". */ emptyLabel?: ReactNode; + + /** + * Visual type of the ListBox styling. + * - `card` (default): Standard card styling with border and margin + * - `plain`: No border, no margin, no radius - suitable for embedded use + * - `popover`: No border, but keeps margin and radius - suitable for overlay use + * Defaults to 'card'. + */ + type?: 'card' | 'plain' | 'popover'; } const PROP_STYLES = [...BASE_STYLES, ...OUTER_STYLES, ...COLOR_STYLES]; @@ -516,6 +529,7 @@ export const ListBox = forwardRef(function ListBox( allValueProps, filter, emptyLabel = 'No items', + type = 'card', form, ...otherProps } = props; @@ -861,6 +875,7 @@ export const ListBox = forwardRef(function ListBox( qa="ListBoxWrapper" mods={mods} styles={styles} + data-type={type} > {header ? ( @@ -902,6 +917,7 @@ export const ListBox = forwardRef(function ListBox( styles={listStyles} aria-disabled={isDisabled || undefined} mods={{ sections: hasSections }} + data-type={type} style={ shouldVirtualize ? { diff --git a/src/components/fields/NumberInput/NumberInput.tsx b/src/components/fields/NumberInput/NumberInput.tsx index a2b9a4e20..0fd93e564 100644 --- a/src/components/fields/NumberInput/NumberInput.tsx +++ b/src/components/fields/NumberInput/NumberInput.tsx @@ -24,9 +24,6 @@ const StyledTextInputBase = tasty(TextInputBase, { styles: { textAlign: 'right', }, - wrapperStyles: { - width: 'initial 13x 100%', - }, }); const StepperContainer = tasty({ @@ -36,6 +33,7 @@ const StepperContainer = tasty({ gridRows: '1sf 1sf', flow: 'column', placeSelf: 'stretch', + margin: '(.5x - 1bw) left', }, }); diff --git a/src/components/fields/Picker/Picker.tsx b/src/components/fields/Picker/Picker.tsx index 98c414a7e..4ff5d0fe5 100644 --- a/src/components/fields/Picker/Picker.tsx +++ b/src/components/fields/Picker/Picker.tsx @@ -44,7 +44,7 @@ import { CubeListBoxProps, ListBox } from '../ListBox/ListBox'; import type { FieldBaseProps } from '../../../shared'; export interface CubePickerProps - extends Omit, 'size' | 'tooltip'>, + extends Omit, 'size' | 'tooltip' | 'type'>, Omit, BasePropsWithoutChildren, BaseStyleProps, @@ -683,9 +683,7 @@ export const Picker = forwardRef(function Picker( isLoading={isLoading} stateRef={internalListStateRef} isCheckable={isCheckable} - mods={{ - popover: true, - }} + type="popover" size="medium" showSelectAll={showSelectAll} selectAllLabel={selectAllLabel}