diff --git a/README.md b/README.md index 6cb5023..88ebbed 100644 --- a/README.md +++ b/README.md @@ -68,8 +68,10 @@ This library now ships with dedicated documentation files for each component in - [List](docs/List.md) (covers List and ListItem) - [Modal](docs/Modal.md) - [Odometer](docs/Odometer.md) (component + `useOdometer` hook) +- [Popover](docs/Popover.md) - [ScrollArea](docs/ScrollArea.md) - [Select](docs/Select.md) +- [Shift](docs/Shift.md) - [SvgSprite](docs/SvgSprite.md) - [Switch](docs/Switch.md) - [Tabs](docs/Tabs.md) (includes `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`) @@ -77,6 +79,7 @@ This library now ships with dedicated documentation files for each component in - [TextArea](docs/TextArea.md) - [TextField](docs/TextField.md) - [Toast](docs/Toast.md) +- [ToggleGroup](docs/ToggleGroup.md) - [Tooltip](docs/Tooltip.md) - [Truncate](docs/Truncate.md) - [TruncateList](docs/TruncateList.md) diff --git a/docs/Popover.md b/docs/Popover.md new file mode 100644 index 0000000..a86cef6 --- /dev/null +++ b/docs/Popover.md @@ -0,0 +1,136 @@ +### Popover + +Documentation for the Popover component and its subcomponents: Popover, PopoverTrigger, PopoverContent, PopoverAnchor, PopoverClose. The implementation is built on top of `@radix-ui/react-popover` and adds visual features, support for global configuration, and additional props. + +See the official Radix documentation: https://www.radix-ui.com/primitives/docs/components/popover + +#### Import + +```ts +import {Popover, PopoverTrigger, PopoverContent, PopoverAnchor, PopoverClose} from "addon-ui"; +``` + +#### Basic usage + +```tsx + + Open Popover + +

Popover content goes here.

+ Close +
+
+``` + +--- + +#### Props: Popover (Root) + +Only the prop name, type, and default are listed below. + +| Prop | Type | Default | +| ---------------- | ----------------------------- | ------- | +| `defaultOpen` | `boolean` | — | +| `open` | `boolean` | — | +| `onOpenChange` | `(open: boolean) => void` | — | +| `modal` | `boolean` | — | +| Radix Root props | all `PopoverProps` from Radix | — | + +Supports contextual props via the UI provider: `useComponentProps("popover")`. + +#### Props: PopoverTrigger + +| Prop | Type | Default | +| ------------------- | ------------------------- | ------- | +| `center` | `boolean` | `false` | +| Radix Trigger props | all `PopoverTriggerProps` | — | + +Supports contextual props: `useComponentProps("popoverTrigger")`. + +#### Props: PopoverContent + +| Prop | Type | Default | +| ------------------- | ------------------------- | ------- | +| `maxWidth` | `number` | — | +| `minWidth` | `number` | — | +| `fullWidth` | `boolean` | — | +| `arrow` | `boolean` | — | +| `arrowWidth` | `number` | — | +| `arrowHeight` | `number` | — | +| `overlay` | `boolean` | — | +| `overlayClassname` | `string` | — | +| `container` | `HTMLElement` | — | +| Radix Content props | all `PopoverContentProps` | — | + +Supports contextual props: `useComponentProps("popoverContent")`. + +#### Props: PopoverAnchor + +| Prop | Type | Default | +| ------------------ | ------------------------ | ------- | +| Radix Anchor props | all `PopoverAnchorProps` | — | + +Supports contextual props: `useComponentProps("popoverAnchor")`. + +#### Props: PopoverClose + +| Prop | Type | Default | +| ----------------- | ----------------------- | ------- | +| Radix Close props | all `PopoverCloseProps` | — | + +Supports contextual props: `useComponentProps("popoverClose")`. + +--- + +### CSS variables for Popover + +Only variables actually referenced in `src/components/Popover/popover.module.scss` are listed, with their exact fallback chains. + +| Variable | Fallback chain | +| :-------------------------------------- | :---------------------------------------------------------------------------------------------------- | +| `--popover-overlay-bg` | `var(--popover-overlay-bg, var(--overlay-bg, rgba(0, 0, 0, 0.5)))` | +| `--popover-trigger-color` | none (define in theme) | +| `--popover-trigger-bg-color` | `var(--popover-trigger-bg-color, var(--bg-secondary-color))` | +| `--popover-trigger-shadow-offset` | none (define in theme) | +| `--popover-trigger-shadow-color` | none (define in theme) | +| `--popover-trigger-border-width` | `var(--popover-trigger-border-width, 1px)` | +| `--popover-trigger-border-style` | `var(--popover-trigger-border-style, solid)` | +| `--popover-trigger-border-color` | `var(--popover-trigger-border-color, var(--border-color))` | +| `--popover-trigger-border-radius` | `var(--popover-trigger-border-radius, 10px)` | +| `--popover-trigger-font-size` | `var(--popover-trigger-font-size, var(--popover-font-size, 14px))` | +| `--popover-trigger-font-weight` | `var(--popover-trigger-font-weight, 500)` | +| `--popover-trigger-padding` | `var(--popover-trigger-padding, 8px 12px)` | +| `--popover-trigger-height` | none (define in theme) | +| `--popover-trigger-gap` | `var(--popover-trigger-gap, 5px)` | +| `--popover-trigger-shadow-offset-hover` | none (define in theme) | +| `--popover-trigger-border-width-hover` | `var(--popover-trigger-border-width-hover, var(--popover-trigger-border-width, 1px))` | +| `--popover-trigger-border-color-hover` | `var(--popover-trigger-border-color-hover, var(--popover-trigger-border-color, var(--border-color)))` | +| `--popover-content-padding` | `var(--popover-content-padding, 16px)` | +| `--popover-content-bg-color` | `var(--popover-content-bg-color, var(--bg-primary-color))` | +| `--popover-content-border-radius` | `var(--popover-content-border-radius, 8px)` | +| `--popover-content-shadow-offset` | `var(--popover-content-shadow-offset, 0 0 5px 0)` | +| `--popover-content-shadow-color` | `var(--popover-content-shadow-color, rgba(0, 0, 0, 0.25))` | +| `--popover-content-height` | none (define in theme) | +| `--popover-content-max-height` | `var(--popover-content-max-height, var(--radix-popover-content-available-height))` | +| `--popover-content-full-width` | `var(--popover-content-full-width, var(--radix-popover-content-available-width))` | +| `--popover-arrow-bg-color` | `var(--popover-arrow-bg-color, var(--popover-content-bg-color, var(--bg-primary-color)))` | +| `--popover-speed-color` | `var(--popover-speed-color, var(--speed-color))` | +| `--popover-speed-background` | `var(--popover-speed-background, var(--speed-color))` | +| `--popover-speed-shadow` | `var(--popover-speed-shadow, var(--speed-color))` | +| `--popover-speed-border-color` | `var(--popover-speed-border-color, var(--speed-color))` | + +--- + +### Accessibility (A11y) + +Components inherit behavior and ARIA attributes from Radix Popover: keyboard navigation, correct roles, and states. It adheres to the [WAI-ARIA Popover Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/popover/). + +Useful: https://www.radix-ui.com/primitives/docs/components/popover + +--- + +### Usage notes + +- The component supports global configuration via `ui.config.ts`. +- `PopoverContent` can be customized with `arrow`, `fullWidth`, and `overlay` props. +- `PopoverTrigger` supports `center` alignment. diff --git a/docs/Select.md b/docs/Select.md index ded1536..ab07429 100644 --- a/docs/Select.md +++ b/docs/Select.md @@ -1,13 +1,22 @@ ### Select -Documentation for the Select component and its subcomponents: Select, SelectTrigger, SelectContent, SelectItem. The implementation is built on top of `@radix-ui/react-select` and adds visual features, support for localization (RTL), and global configuration. +Documentation for the Select component and its subcomponents: Select, SelectTrigger, SelectContent, SelectItem, SelectItemText, SelectItemIndicator, SelectIcon, SelectValue. The implementation is built on top of `@radix-ui/react-select` and adds visual features, support for localization (RTL), and global configuration. See the official Radix documentation: https://www.radix-ui.com/primitives/docs/components/select #### Import ```ts -import {Select, SelectTrigger, SelectContent, SelectItem} from "addon-ui"; +import { + Select, + SelectTrigger, + SelectContent, + SelectItem, + SelectItemText, + SelectItemIndicator, + SelectIcon, + SelectValue, +} from "addon-ui"; ``` #### Basic usage @@ -61,17 +70,18 @@ Supports contextual props: `useComponentProps("selectTrigger")`. #### Props: SelectContent -| Prop | Type | Default | -| ------------------- | ------------------------ | ------- | -| `arrow` | `boolean` | — | -| `arrowWidth` | `number` | — | -| `arrowHeight` | `number` | — | -| `fullWidth` | `boolean` | — | -| `viewportProps` | `SelectViewportProps` | — | -| `scrollUpButton` | `ReactNode` | — | -| `scrollDownButton` | `ReactNode` | — | -| `container` | `HTMLElement` | — | -| Radix Content props | all `SelectContentProps` | — | +| Prop | Type | Default | +| ------------------- | ---------------------------- | ---------- | +| `arrow` | `boolean` | — | +| `arrowWidth` | `number` | — | +| `arrowHeight` | `number` | — | +| `fullWidth` | `boolean` | `true` | +| `position` | `"popper" \| "item-aligned"` | `"popper"` | +| `viewportProps` | `SelectViewportProps` | — | +| `scrollUpButton` | `ReactNode` | — | +| `scrollDownButton` | `ReactNode` | — | +| `container` | `HTMLElement` | — | +| Radix Content props | all `SelectContentProps` | — | Supports contextual props: `useComponentProps("selectContent")`. @@ -87,6 +97,14 @@ Supports contextual props: `useComponentProps("selectContent")`. Supports contextual props: `useComponentProps("selectItem")`. +#### Props: SelectItemText, SelectItemIndicator, SelectIcon, SelectValue + +These components are wrappers around Radix UI primitives and do not have additional custom props. + +| Prop | Type | Default | +| ----------- | ----------------------------- | ------- | +| Radix props | all corresponding Radix props | — | + --- ### CSS variables for Select @@ -108,6 +126,7 @@ Only variables actually referenced in `src/components/Select/select.module.scss` | `--select-trigger-padding` | `var(--select-trigger-padding, 8px 12px)` | | `--select-trigger-height` | none (define in theme) | | `--select-trigger-gap` | `var(--select-trigger-gap, 5px)` | +| `--select-placeholder-color` | `var(--select-placeholder-color, var(--placeholder-color))` | | `--select-trigger-shadow-offset-hover` | none (define in theme) | | `--select-trigger-border-width-hover` | `var(--select-trigger-border-width-hover, var(--select-trigger-border-width, 1px))` | | `--select-trigger-border-color-hover` | `var(--select-trigger-border-color-hover, var(--select-trigger-border-color, var(--border-color)))` | @@ -138,6 +157,7 @@ Only variables actually referenced in `src/components/Select/select.module.scss` | `--select-scroll-btn-bg-color` | `var(--select-scroll-btn-bg-color, var(--select-content-bg-color, var(--bg-primary-color)))` | | `--select-scroll-btn-cursor` | `var(--select-scroll-btn-cursor, default)` | | `--select-scroll-btn-hover-bg-color` | `var(--select-scroll-btn-hover-bg-color, var(--bg-secondary-color))` | +| `--select-arrow-bg-color` | `var(--select-arrow-bg-color, var(--select-content-bg-color, var(--bg-primary-color)))` | --- diff --git a/docs/Shift.md b/docs/Shift.md new file mode 100644 index 0000000..b73e6ed --- /dev/null +++ b/docs/Shift.md @@ -0,0 +1,75 @@ +### Shift + +The `Shift` component is a container designed for smooth vertical transitions between multiple children. It is useful for building multi-step forms, tab-like switching, or replacing UI elements with a sliding effect. + +#### Import + +```ts +import {Shift} from "addon-ui"; +``` + +#### Basic usage + +```tsx +const [step, setStep] = useState(1); + + +
+

Step 1

+

First step content

+ +
+
+

Step 2

+

Second step content

+ +
+
; +``` + +--- + +#### Props: Shift + +Only the prop name, type, and default are listed below. + +| Prop | Type | Default | +| ---------------- | -------------------------- | ------- | +| `active` | `number` | `1` | +| `height` | `number` | — | +| `adaptiveHeight` | `boolean` | `true` | +| `itemClassName` | `string` | — | +| — | all `HTMLDivElement` props | — | + +#### Notes on props: + +- `active`: A 1-based index (1, 2, 3...) that determines which child is currently visible. +- `height`: Fixed height for the container. If provided, `adaptiveHeight` behavior is effectively overridden. +- `adaptiveHeight`: If `true`, the container automatically adjusts its height to match the current child's height with a smooth transition. + +--- + +### CSS variables for Shift + +The following variables are used in `src/components/Shift/shift.module.scss` to control the transition animations. + +| Variable | Fallback chain | +| :------------------------ | :---------------------------------------------- | +| `--shift-speed-height` | `var(--shift-speed-height, var(--speed-sm))` | +| `--shift-speed-transform` | `var(--shift-speed-transform, var(--speed-sm))` | +| `--shift-speed-opacity` | `var(--shift-speed-opacity, var(--speed-sm))` | + +--- + +### Accessibility (A11y) + +`Shift` is a layout-oriented component and does not provide specific ARIA roles or keyboard interactions by itself. Ensure that the children inside `Shift` are accessible. When switching the `active` index, consider using ARIA live regions or manual focus management if the context changes significantly for screen reader users. + +--- + +### Usage notes + +- Each child of the `Shift` component is wrapped in an absolute-positioned container to allow smooth transitions. +- If a child is not a valid React element (e.g., a string or number), it is automatically wrapped in a ``. +- `adaptiveHeight` is enabled by default, which is recommended when children have varying heights to ensure a smooth layout experience. +- The component uses `translate3d` and `opacity` for high-performance animations. diff --git a/docs/ToggleGroup.md b/docs/ToggleGroup.md new file mode 100644 index 0000000..57e988f --- /dev/null +++ b/docs/ToggleGroup.md @@ -0,0 +1,101 @@ +### ToggleGroup + +Documentation for the ToggleGroup component and its subcomponents: ToggleGroup, ToggleGroupItem, ToggleGroupIndicator. The implementation is built on top of `@radix-ui/react-toggle-group` and adds visual features, support for global configuration, and additional props. + +See the official Radix documentation: https://www.radix-ui.com/primitives/docs/components/toggle-group + +#### Import + +```ts +import {ToggleGroup, ToggleGroupItem, ToggleGroupIndicator} from "addon-ui"; +``` + +#### Basic usage + +```tsx + + + + + + + + +``` + +--- + +#### Props: ToggleGroup (Root) + +Only the prop name, type, and default are listed below. + +| Prop | Type | Default | +| ---------------- | ---------------------------------------------------------- | ------- | +| `type` | `"single" \| "multiple"` (required) | — | +| `value` | `string \| string[]` | — | +| `defaultValue` | `string \| string[]` | — | +| `onValueChange` | `(value: string \| string[]) => void` | — | +| `disabled` | `boolean` | — | +| `rovingFocus` | `boolean` | `true` | +| `orientation` | `"horizontal" \| "vertical" \| undefined` | — | +| `dir` | `"ltr" \| "rtl" \| undefined` | — | +| `loop` | `boolean` | `true` | +| Radix Root props | all `ToggleGroupSingleProps` or `ToggleGroupMultipleProps` | — | + +Supports contextual props via the UI provider: `useComponentProps("toggleGroup")`. + +#### Props: ToggleGroupItem + +| Prop | Type | Default | +| ---------------- | -------------------------- | ------- | +| `value` | `string` (required) | — | +| `disabled` | `boolean` | — | +| `asChild` | `boolean` | `true` | +| Radix Item props | all `ToggleGroupItemProps` | — | + +Supports contextual props: `useComponentProps("toggleGroupItem")`. + +#### Props: ToggleGroupIndicator + +This component is a wrapper that displays its children only when its parent `ToggleGroupItem` is in the "on" state. + +| Prop | Type | Default | +| :--------- | :------------------------- | :------ | +| `children` | `ReactNode` | — | +| — | all `HTMLDivElement` props | — | + +--- + +### CSS variables for ToggleGroup + +Only variables actually referenced in `src/components/ToggleGroup/toggleGroup.module.scss` are listed, with their exact fallback chains. + +| Variable | Fallback chain | +| :---------------------------------- | :---------------------------------------------------------- | +| `--toggle-group-gap` | `var(--toggle-group-gap, 10px)` | +| `--toggle-group-padding` | `var(--toggle-group-padding, 0)` | +| `--toggle-group-speed-bg` | `var(--toggle-group-speed-bg, var(--speed-color))` | +| `--toggle-group-item-speed-bg` | `var(--toggle-group-item-speed-bg, var(--speed-color))` | +| `--toggle-group-item-speed-color` | `var(--toggle-group-item-speed-color, var(--speed-color))` | +| `--toggle-group-item-checked-bg` | `var(--toggle-group-item-checked-bg, var(--primary-color))` | +| `--toggle-group-item-checked-color` | none (define in theme) | + +--- + +### Accessibility (A11y) + +Components inherit behavior and ARIA attributes from Radix Toggle Group: keyboard navigation (arrow keys), roving focus, and states. It adheres to the [WAI-ARIA Toggle Button Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/button/). + +Useful: https://www.radix-ui.com/primitives/docs/components/toggle-group + +--- + +### Usage notes + +- The component supports global configuration via `ui.config.ts`. +- `ToggleGroupItem` has `asChild` set to `true` by default, making it easy to use with custom button components. +- `ToggleGroupIndicator` is specifically styled to show/hide based on the item state. diff --git a/src/components/Button/button.module.scss b/src/components/Button/button.module.scss index 82575f9..a66e649 100644 --- a/src/components/Button/button.module.scss +++ b/src/components/Button/button.module.scss @@ -9,7 +9,6 @@ $root: button; height: var(--button-height, 34px); border-radius: var(--button-border-radius, 10px); padding: var(--button-padding, 0 16px); - will-change: background, color, transform; transition: color var(--button-speed-color, var(--speed-color)), background var(--button-speed-bg, var(--speed-color)), diff --git a/src/components/Popover/Popover.stories.tsx b/src/components/Popover/Popover.stories.tsx new file mode 100644 index 0000000..be2137a --- /dev/null +++ b/src/components/Popover/Popover.stories.tsx @@ -0,0 +1,128 @@ +import React from "react"; +import {Meta, StoryObj} from "@storybook/react"; + +import { + Popover as PopoverComponent, + PopoverAnchor, + PopoverContent, + PopoverContentProps, + PopoverProps, + PopoverTrigger, + PopoverTriggerProps, +} from "./index"; + +import {TextArea, TextField} from "../index"; + +type Props = PopoverProps & + PopoverContentProps & + PopoverTriggerProps & { + withAnchor: boolean; + triggerWidth: number; + }; + +const meta: Meta = { + title: "Components/Popover", + component: PopoverComponent, + tags: ["autodocs"], + argTypes: { + // Root + modal: {table: {category: "Root"}}, + + // Trigger + center: {table: {category: "Trigger"}}, + + // Content + side: { + options: ["top", "right", "bottom", "left"], + control: {type: "select"}, + table: {category: "Content"}, + }, + align: { + options: ["start", "center", "end"], + control: {type: "select"}, + table: {category: "Content"}, + }, + sticky: { + options: ["partial", "always"], + control: {type: "select"}, + table: {category: "Content"}, + }, + alignOffset: {table: {category: "Content"}}, + sideOffset: {table: {category: "Content"}}, + minWidth: {table: {category: "Content"}, control: {type: "number"}}, + maxWidth: {table: {category: "Content"}, control: {type: "number"}}, + fullWidth: {table: {category: "Content"}}, + overlay: {table: {category: "Content"}}, + avoidCollisions: {table: {category: "Content"}}, + collisionPadding: {table: {category: "Content"}}, + hideWhenDetached: {table: {category: "Content"}}, + arrow: {table: {category: "Content"}}, + arrowWidth: {table: {category: "Content"}}, + arrowHeight: {table: {category: "Content"}}, + + // Storybook view + withAnchor: {table: {category: "Storybook_view"}}, + triggerWidth: {table: {category: "Storybook_view"}, control: {type: "number"}}, + }, +}; + +export default meta; + +export const Popover: StoryObj = { + args: { + modal: false, + + center: false, + + side: "bottom", + sideOffset: 10, + align: "center", + alignOffset: 0, + minWidth: undefined, + maxWidth: undefined, + fullWidth: false, + overlay: true, + avoidCollisions: true, + collisionPadding: 0, + sticky: "partial", + hideWhenDetached: false, + arrow: true, + arrowWidth: 10, + arrowHeight: 5, + + withAnchor: false, + triggerWidth: undefined, + }, + render: args => { + const {modal, center, withAnchor, triggerWidth, ...other} = args; + + return ( + + + Open Popover + {withAnchor && } + + + +
+ {["Name", "Surname", "Age"].map(item => ( +
+ +
+ +
+
+ ))} +