@tomny-dev/uzi 0.8.0 → 0.9.0-pr.89.299.1.9555816

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/README.md CHANGED
@@ -2,210 +2,80 @@
2
2
 
3
3
  Rapid-fire React UI components. Ship faster, build more.
4
4
 
5
+ Uzi is a reusable React component library for app interfaces, with design tokens, accessible interaction primitives, application layouts, and AI chat components.
6
+
5
7
  ## Install
6
8
 
7
9
  ```bash
8
10
  pnpm add @tomny-dev/uzi
9
11
  ```
10
12
 
11
- ## Usage
13
+ Uzi supports React 18 and 19 and uses CSS modules internally; Tailwind is not required.
12
14
 
13
- ```tsx
14
- import { Button, Card, Modal, ThemeProvider, useToast } from "@tomny-dev/uzi";
15
- ```
15
+ ## Quick start
16
16
 
17
- If you want the packaged component styles, import the exported stylesheet once in your app:
17
+ Import the component stylesheet **once** in your application's global CSS:
18
18
 
19
19
  ```css
20
20
  @import "@tomny-dev/uzi/styles.css";
21
21
  ```
22
22
 
23
- ## Storybook
24
-
25
- Uzi includes Storybook as the interactive component catalog for exploring components, variants, states, and composition patterns.
26
-
27
- To run Storybook locally:
28
-
29
- ```bash
30
- pnpm install
31
- pnpm storybook
32
- ```
33
-
34
- Then open `http://localhost:6006`.
35
-
36
- To verify the production Storybook build:
37
-
38
- ```bash
39
- pnpm build-storybook
40
- ```
41
-
42
- The static output is written to `storybook-static/`. Story files live beside their components under `src/components/**` using the `*.stories.tsx` naming convention.
43
-
44
- The **Components / AIChat** stories demonstrate streaming, custom renderers, scoped requests, message limits, error recovery, and an externally controlled runtime with mock approvals; no external AI credentials are needed.
45
-
46
- CI builds Storybook on every pull request and push to `main`. After changes land on `main`, the Storybook deployment workflow publishes `storybook-static/` to the `uzi-storybook` Cloudflare Pages project. The deployment workflow requires these repository secrets:
47
-
48
- - `CLOUDFLARE_API_TOKEN` — API token with Pages Write permission.
49
- - `CLOUDFLARE_ACCOUNT_ID` — Cloudflare account identifier.
50
-
51
- The Cloudflare Pages project should use `main` as its production branch. A custom domain such as `uzi.tomny.dev` can be attached to that Pages project after the project exists.
52
-
53
- ## Using With Coding Agents
54
-
55
- Installing `@tomny-dev/uzi` in an app does not by itself cause Claude Code, Codex, or similar agents to prefer `uzi` components. You should add that policy to the consumer repo's agent guidance.
56
-
57
- Recommended snippet for the consumer repo's `CLAUDE.md`, `AGENTS.md`, or equivalent:
58
-
59
- ```md
60
- ## UI Components
61
-
62
- - Prefer `@tomny-dev/uzi` for shared UI primitives and layout components.
63
- - Check `uzi` before creating new local primitives such as buttons, inputs, labels, cards, modals, selects, dropdown menus, alerts, shells, navigation, and theme controls.
64
- - Only introduce a repo-local primitive when `uzi` lacks the required behavior or when the local file is intentionally app-specific composition.
65
- - Keep imports consistent with the surrounding area of the codebase. If the repo already uses thin wrappers around `uzi`, follow that local pattern.
66
- ```
67
-
68
- If you want agents to discover props and examples directly, also configure the `uzi` MCP server. See [`mcp/README.md`](./mcp/README.md).
69
-
70
- ## Themes
71
-
72
- `uzi` ships with built-in light/dark tokens and a small accent palette layer.
23
+ Then use components in your app:
73
24
 
74
25
  ```tsx
75
26
  "use client";
76
27
 
77
- import { Button, ThemeProvider, useTheme } from "@tomny-dev/uzi";
78
-
79
- function ThemeToggle() {
80
- const { resolvedTheme, toggleTheme, accent, setAccent } = useTheme();
81
-
82
- return (
83
- <div>
84
- <Button onClick={toggleTheme}>
85
- {resolvedTheme === "dark" ? "Switch to light" : "Switch to dark"}
86
- </Button>
87
- <Button variant="outline" onClick={() => setAccent(accent === "violet" ? "blue" : "violet")}>
88
- Toggle accent
89
- </Button>
90
- </div>
91
- );
92
- }
28
+ import { Button, Card, ThemeProvider } from "@tomny-dev/uzi";
93
29
 
94
30
  export function App() {
95
31
  return (
96
32
  <ThemeProvider defaultTheme="system" defaultAccent="blue">
97
- <ThemeToggle />
33
+ <Card padding="md">
34
+ <h2>Welcome to Uzi</h2>
35
+ <Button onClick={() => alert("Hello!")}>Get started</Button>
36
+ </Card>
98
37
  </ThemeProvider>
99
38
  );
100
39
  }
101
40
  ```
102
41
 
103
- Supported themes:
104
-
105
- - `light`
106
- - `dark`
107
- - `system`
108
-
109
- Supported accent palettes:
110
-
111
- - `blue`
112
- - `cyan`
113
- - `violet`
114
- - `emerald`
115
- - `amber`
116
- - `rose`
42
+ For installation variants and a longer walkthrough, see [Getting Started](docs/getting-started.md).
117
43
 
118
- ## Component Philosophy
44
+ ## What's included
119
45
 
120
- `uzi` is not meant to be a from-scratch accessibility framework.
121
-
122
- - Use native HTML for simple form controls when the platform already gives the right behavior.
123
- - Use Radix for interaction-heavy primitives such as menus, dialogs, popovers, toasts, and custom selects.
124
- - Use `uzi` for the public component API, styling, tokens, and higher-level templates like `TopBar`, `SidebarNav`, and `AppShell`.
125
-
126
- In practice, `uzi` should spend its complexity budget on reusable app scaffolding and cohesive design, not on rebuilding low-level widget behavior that Radix or the browser already solves.
127
-
128
- ## Components
129
-
130
- | Component | Description |
46
+ | Area | Examples |
131
47
  |---|---|
132
- | `Avatar` | Profile image with fallback states |
133
- | `AIChat` | Plug-and-play AI SDK streaming chat with scoped context and customizable renderers |
134
- | `AIChatView` | Controlled chat presentation for external runtimes, tool approvals, attachment adapters and persisted history |
135
- | `AIChatDock` | Shared floating chat launcher, responsive panel and expand/close controls |
136
- | `Button` | Primary, secondary, outline, ghost variants |
137
- | `Card` | Discrete object container with tone/padding control |
138
- | `Surface` | Semantic visual grouping with base/subtle/raised/selected hierarchy |
139
- | `Checkbox` | Checkbox form primitive |
140
- | `Input` | Text input primitive |
141
- | `Label` | Form label primitive |
142
- | `MultiSelect` | Custom multi-option picker with checkbox-style menu |
143
- | `Pill` | Inline badge/tag |
144
- | `Modal` | Radix-backed overlay dialog |
145
- | `Select` | Styled Radix-based single-select field |
146
- | `Dropdown` | Deprecated compatibility alias for Select |
147
- | `DropdownMenu` | Radix-based action menu primitives |
148
- | `AppShell` | Responsive layout with collapsible sidebar |
149
- | `SidebarNav` | Sidebar navigation list |
150
- | `TopBar` | Composable header shell for brand, nav triggers, and actions |
151
- | `PageContainer` | Centered responsive page wrapper with standard horizontal and vertical gutters |
152
- | `PageHeader` | Page title, eyebrow, description, and action composition |
153
- | `SectionHeader` | Reusable section title, description, and actions |
154
- | `EmptyState` | Empty-result/resource state with visual and action slots |
155
- | `Stack` / `Inline` | Small gap/alignment layout helpers for common composition |
156
- | `Stat` / `StatGroup` | Domain-agnostic summary value and responsive stat grouping |
157
- | `Toolbar` / `ToolbarGroup` | Responsive compact filter/action workspace |
158
- | `ThemeToggleButton` | Reusable light/dark toggle wired to `ThemeProvider` |
159
- | `ThemeProvider` / `useTheme` | Light/dark + accent palette theming |
160
- | `ToastProvider` / `useToast` | Radix-backed toast notification system |
161
-
162
- `TopBar` supports:
163
-
164
- - `brandingLocation="left" | "center"`
165
- - `isSticky={true | false}`
166
- - `showThemeToggle`
167
- - `themeToggleProps`
168
-
169
- ## Documentation
48
+ | UI primitives | `Button`, `Card`, `Surface`, `Input`, `Label`, `Checkbox`, `Select`, `MultiSelect`, `Modal`, `DropdownMenu` |
49
+ | App layout | `AppShell`, `SidebarNav`, `TopBar`, `PageContainer`, `PageHeader`, `SectionHeader`, `Stack`, `Inline` |
50
+ | Common patterns | `Toolbar`, `ToolbarGroup`, `EmptyState`, `Avatar`, `Pill`, `Stat`, `StatGroup` |
51
+ | Theming and feedback | `ThemeProvider`, `useTheme`, `ThemeToggleButton`, `ToastProvider`, `useToast` |
52
+ | AI experiences | `AIChat`, `AIChatView`, `AIChatDock`, `FloatingActionStack`, `FloatingActionButton` |
170
53
 
171
- - [Getting Started](docs/getting-started.md) — Installation, setup, and first component
172
- - [Theming](docs/theming.md) — ThemeProvider, useTheme, accent palettes, CSS overrides
173
- - [Layout Patterns](docs/guides/layout-patterns.md) — Dashboard, full-width header, center-branded
174
- - [Application Scaffolding](docs/guides/application-scaffolding.md) — Page containers, headers, empty states, stats, and layout composition
175
- - [Visual Hierarchy](docs/guides/visual-hierarchy.md) — Surface levels, typography, borders, shadows, and toolbar conventions
176
- - [Form Patterns](docs/guides/form-patterns.md) — Input, Select, MultiSelect, SegmentedToggle patterns
177
- - [AI Chat](docs/guides/ai-chat.md) — Streaming endpoint contract, authentication, scope authorization, renderers and local Storybook demos
54
+ These are representative exports, not an exhaustive API reference. Use the guides below for configuration and composition examples.
178
55
 
179
- ## SSR Notes
180
-
181
- This package bundles CSS inline into the JavaScript output via `vite-plugin-css-injected-by-js`.
182
- At runtime, a `<style>` tag is injected into the document head so consumers do **not** need
183
- to import `./styles.css` for client-side rendering.
56
+ ## Documentation
184
57
 
185
- If your app uses SSR (Next.js, Remix, etc.), the injected `<style>` tag won't run on the
186
- server, so there may be a brief flash of unstyled content on the first client render.
187
- Consumers who need SSR-safe styles should continue importing the separate stylesheet:
58
+ - [Getting Started](docs/getting-started.md) — Installation, styles, and first components
59
+ - [Theming](docs/theming.md) — Light/dark mode, accent palettes, theme APIs, and CSS overrides
60
+ - [Layout Patterns](docs/guides/layout-patterns.md) — Dashboard and header compositions
61
+ - [Application Scaffolding](docs/guides/application-scaffolding.md) — Page structure, empty states, and stats
62
+ - [Visual Hierarchy](docs/guides/visual-hierarchy.md) — Surface, typography, border, and toolbar conventions
63
+ - [Form Patterns](docs/guides/form-patterns.md) — Input, Select, MultiSelect, and SegmentedToggle
64
+ - [AI Chat](docs/guides/ai-chat.md) — Streaming API contracts, scope authorization, and renderers
65
+ - [SSR and Next.js](docs/ssr.md) — Server rendering, stylesheet imports, and server-safe exports
188
66
 
189
- ```css
190
- @import "@tomny-dev/uzi/styles.css";
191
- ```
67
+ ## Using Uzi with coding agents
192
68
 
193
- ### Next.js App Router
69
+ Installing the package does not automatically tell Claude Code, Codex, or other agents to prefer Uzi components. Add a rule to your consuming application's `AGENTS.md`, `CLAUDE.md`, or equivalent instructing agents to check Uzi before creating new shared UI primitives.
194
70
 
195
- The main `@tomny-dev/uzi` entry is a client entry because it exports interactive components
196
- and React context providers. Next.js Server Components can import and render these exports;
197
- Next.js will preserve the client boundary without requiring a local wrapper.
71
+ For component discovery, usage examples, and agent configuration, see the [Uzi MCP server guide](mcp/README.md). The MCP server is not bundled in the main npm package.
198
72
 
199
- The application-scaffolding components themselves do not use hooks or browser-only APIs, but they remain exposed through the main client entry in this release. A separate server-safe layout entry can be considered later if consumer measurements justify the added package surface.
73
+ ## SSR and Next.js
200
74
 
201
- Use `@tomny-dev/uzi/server` for the server-safe `getThemeScript` export.
202
- Use `@tomny-dev/uzi/utils` for environment-neutral utilities such as `cx`, including
203
- calls made directly from Server Components.
75
+ For SSR frameworks, keep the stylesheet import shown above to prevent a flash of unstyled components. The main package entry is a client entry; `@tomny-dev/uzi/server` exposes the server-safe `getThemeScript`, while `@tomny-dev/uzi/utils` exposes environment-neutral helpers such as `cx`.
204
76
 
205
- ## Notes
77
+ See [SSR and Next.js](docs/ssr.md) for details.
206
78
 
207
- - No Tailwind — components use CSS modules internally
208
- - `"use client"` is handled by the bundle — no need to wrap imports
209
- - `react` and `react-dom` are peer dependencies, provided by your app
79
+ ## Contributing
210
80
 
211
- - `FloatingActionStack` and `FloatingActionButton` provide a single, accessible floating-action rail with mobile safe-area spacing. An `AIChatDock` can opt out of its built-in FAB via `showLauncher={false}`.
81
+ Working on Uzi itself? See [CONTRIBUTING.md](CONTRIBUTING.md) for local development, Storybook, testing, and design conventions. Infrastructure maintainers can find the Cloudflare Pages setup in [Storybook Deployment](docs/deployment.md).
@@ -13,10 +13,16 @@ export type MultiSelectProps = {
13
13
  placeholder?: string;
14
14
  fullWidth?: boolean;
15
15
  maxVisibleValues?: number;
16
+ /** Minimum popup width, independent of the trigger (e.g. '24rem'). */
17
+ contentMinWidth?: string;
16
18
  /** Formats the closed trigger text from the selected options, including an empty selection. */
17
19
  formatValue?: (selected: MultiSelectOption[]) => string;
18
20
  /** Shows Select all and Clear all actions. Bulk actions modify enabled options only. */
19
21
  bulkActions?: boolean;
22
+ /** Stage changes until Apply. Cancel/dismiss discards the draft. */
23
+ draftMode?: boolean;
24
+ /** Minimum number of selected values required to apply; immediate mode blocks changes below this count. */
25
+ minSelected?: number;
20
26
  selectAllLabel?: string;
21
27
  /** Label for clearing all enabled selections. Disabled selected options remain selected. */
22
28
  clearAllLabel?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"MultiSelect.d.ts","sourceRoot":"","sources":["../../../src/components/multi-select/MultiSelect.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wFAAwF;IACxF,IAAI,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,+FAA+F;IAC/F,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAAiB,EAAE,KAAK,MAAM,CAAC;IACxD,wFAAwF;IACxF,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4FAA4F;IAC5F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,WAAW,4FAuNvB,CAAC"}
1
+ {"version":3,"file":"MultiSelect.d.ts","sourceRoot":"","sources":["../../../src/components/multi-select/MultiSelect.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wFAAwF;IACxF,IAAI,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sEAAsE;IACtE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+FAA+F;IAC/F,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAAiB,EAAE,KAAK,MAAM,CAAC;IACxD,wFAAwF;IACxF,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oEAAoE;IACpE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,2GAA2G;IAC3G,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4FAA4F;IAC5F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,WAAW,4FAgPvB,CAAC"}
package/dist/index26.js CHANGED
@@ -20,6 +20,9 @@ var indicator = "uzi-multi-select-module__indicator__NO-WE";
20
20
  var indicatorSelected = "uzi-multi-select-module__indicatorSelected__9sEAy";
21
21
  var indicatorDisabled = "uzi-multi-select-module__indicatorDisabled__vqdiH";
22
22
  var optionIcon = "uzi-multi-select-module__optionIcon__TQddK";
23
+ var draftActions = "uzi-multi-select-module__draftActions__-R2g3";
24
+ var draftButton = "uzi-multi-select-module__draftButton__tO1zu";
25
+ var optionsViewport = "uzi-multi-select-module__optionsViewport__Fus0q";
23
26
  var multi_select_module_default = {
24
27
  wrapper,
25
28
  wrapperFullWidth,
@@ -41,9 +44,12 @@ var multi_select_module_default = {
41
44
  indicator,
42
45
  indicatorSelected,
43
46
  indicatorDisabled,
44
- optionIcon
47
+ optionIcon,
48
+ draftActions,
49
+ draftButton,
50
+ optionsViewport
45
51
  };
46
52
  //#endregion
47
- export { bulkAction, chevron, chip, chipSummary, multi_select_module_default as default, indicator, indicatorDisabled, indicatorSelected, menu, menuFadeIn, option, optionDisabled, optionIcon, optionLabel, optionSelected, placeholder, separator, summary, trigger, value, wrapper, wrapperFullWidth };
53
+ export { bulkAction, chevron, chip, chipSummary, multi_select_module_default as default, draftActions, draftButton, indicator, indicatorDisabled, indicatorSelected, menu, menuFadeIn, option, optionDisabled, optionIcon, optionLabel, optionSelected, optionsViewport, placeholder, separator, summary, trigger, value, wrapper, wrapperFullWidth };
48
54
 
49
55
  //# sourceMappingURL=index26.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index26.js","names":[],"sources":["../src/components/multi-select/multi-select.module.css"],"sourcesContent":[".wrapper {\n position: relative;\n display: inline-flex;\n min-width: 0;\n max-width: 100%;\n}\n\n.wrapperFullWidth {\n width: 100%;\n}\n\n.trigger {\n width: 100%;\n min-width: 0;\n min-height: var(--uzi-control-min-height);\n display: inline-flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.625rem;\n border-radius: var(--uzi-control-radius);\n border: 1px solid var(--ts-dropdown-border, var(--border));\n background: var(--ts-dropdown-bg, var(--uzi-surface-2, var(--secondary)));\n color: var(--ts-dropdown-text, var(--foreground));\n padding:\n var(--uzi-control-padding-y)\n var(--uzi-control-padding-x-compact)\n var(--uzi-control-padding-y)\n var(--uzi-control-padding-x);\n font-size: var(--uzi-control-font-size);\n line-height: var(--uzi-control-line-height);\n font-family: inherit;\n box-shadow: var(--uzi-control-shadow);\n transition:\n border-color 0.15s ease,\n background 0.15s ease,\n color 0.15s ease,\n box-shadow 0.15s ease;\n cursor: pointer;\n text-align: left;\n}\n\n.trigger:hover:not(:disabled) {\n border-color: var(--ts-dropdown-accent, var(--accent));\n box-shadow: var(--uzi-control-shadow-hover);\n}\n\n.trigger:focus-visible,\n.trigger[data-state=\"open\"] {\n border-color: var(--ts-dropdown-accent, var(--primary));\n outline: var(--focus-ring);\n outline-offset: var(--focus-ring-offset);\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--primary) 30%, transparent);\n}\n\n.trigger:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n box-shadow: none;\n}\n\n.value {\n min-width: 0;\n display: flex;\n flex: 1;\n flex-wrap: wrap;\n gap: 0.375rem;\n align-items: center;\n}\n\n.placeholder {\n color: var(--muted-foreground);\n}\n\n.summary {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 500;\n}\n\n.chip {\n display: inline-flex;\n align-items: center;\n max-width: 100%;\n padding: 2px 8px;\n border-radius: 999px;\n border: 1px solid color-mix(in srgb, var(--primary) 32%, transparent);\n background: color-mix(in srgb, var(--primary) 10%, transparent);\n color: var(--ts-dropdown-text, var(--foreground));\n font-size: 0.75rem;\n font-weight: 600;\n line-height: 1.2;\n white-space: nowrap;\n}\n\n.chipSummary {\n color: var(--primary);\n}\n\n.chevron {\n width: 10px;\n height: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--muted-foreground);\n flex-shrink: 0;\n transition: transform 0.15s ease, color 0.15s ease;\n}\n\n.trigger:hover:not(:disabled) .chevron,\n.trigger[data-state=\"open\"] .chevron {\n color: var(--ts-dropdown-text, var(--foreground));\n}\n\n.trigger[data-state=\"open\"] .chevron {\n transform: rotate(180deg);\n}\n\n.menu {\n position: absolute;\n top: calc(100% + 6px);\n left: 0;\n z-index: var(--uzi-popover-z-index, 1100);\n min-width: 100%;\n max-height: 16rem;\n overflow-y: auto;\n border: 1px solid var(--ts-menu-border, var(--border));\n border-radius: var(--uzi-menu-radius);\n background: var(--ts-menu-bg, var(--popover));\n color: var(--ts-menu-fg, var(--popover-foreground));\n padding: var(--uzi-menu-padding);\n box-shadow: var(--uzi-menu-shadow);\n animation: menuFadeIn 140ms ease;\n}\n\n.bulkAction {\n width: 100%;\n display: flex;\n align-items: center;\n border: none;\n border-radius: var(--uzi-menu-item-radius);\n background: transparent;\n color: inherit;\n padding: var(--uzi-menu-item-padding-y) var(--uzi-menu-item-padding-x);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n outline: none;\n}\n\n.bulkAction[data-highlighted] {\n background: var(--ts-menu-hover-bg, var(--accent));\n color: var(--ts-menu-hover-fg, var(--accent-foreground));\n}\n\n.bulkAction[data-disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.separator {\n height: 1px;\n margin: var(--uzi-menu-padding) 0;\n background: var(--ts-menu-border, var(--border));\n}\n\n.option {\n width: 100%;\n display: flex;\n align-items: center;\n gap: var(--uzi-menu-item-gap);\n border: none;\n border-radius: var(--uzi-menu-item-radius);\n background: transparent;\n color: inherit;\n padding: var(--uzi-menu-item-padding-y) var(--uzi-menu-item-padding-x);\n font: inherit;\n cursor: pointer;\n text-align: left;\n transition: background 0.12s ease, color 0.12s ease;\n}\n\n.option:hover:not(:disabled),\n.option:focus-visible,\n.option[data-highlighted] {\n background: var(--ts-menu-hover-bg, var(--accent));\n color: var(--ts-menu-hover-fg, var(--accent-foreground));\n outline: none;\n}\n\n.optionDisabled,\n.option[data-disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.optionSelected .optionLabel {\n color: var(--primary);\n}\n\n.indicator {\n width: 1rem;\n height: 1rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n border-radius: 0.3125rem;\n border: 1px solid var(--border);\n color: transparent;\n background: transparent;\n transition: border-color 0.12s ease, background 0.12s ease, color 0.12s ease;\n}\n\n.indicatorSelected {\n border-color: color-mix(in srgb, var(--primary) 55%, transparent);\n background: color-mix(in srgb, var(--primary) 12%, transparent);\n color: var(--primary);\n}\n\n.indicatorDisabled {\n opacity: 0.65;\n}\n\n.optionLabel {\n min-width: 0;\n flex: 1;\n display: inline-flex;\n align-items: center;\n gap: var(--uzi-menu-item-gap);\n}\n\n.optionIcon {\n width: 1rem;\n height: 1rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.optionIcon > * {\n max-width: 100%;\n max-height: 100%;\n}\n\n@keyframes menuFadeIn {\n from {\n opacity: 0;\n transform: translateY(-4px) scale(0.98);\n }\n\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"index26.js","names":[],"sources":["../src/components/multi-select/multi-select.module.css"],"sourcesContent":[".wrapper {\n position: relative;\n display: inline-flex;\n min-width: 0;\n max-width: 100%;\n}\n\n.wrapperFullWidth {\n width: 100%;\n}\n\n.trigger {\n width: 100%;\n min-width: 0;\n min-height: var(--uzi-control-min-height);\n display: inline-flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.625rem;\n border-radius: var(--uzi-control-radius);\n border: 1px solid var(--ts-dropdown-border, var(--border));\n background: var(--ts-dropdown-bg, var(--uzi-surface-2, var(--secondary)));\n color: var(--ts-dropdown-text, var(--foreground));\n padding:\n var(--uzi-control-padding-y)\n var(--uzi-control-padding-x-compact)\n var(--uzi-control-padding-y)\n var(--uzi-control-padding-x);\n font-size: var(--uzi-control-font-size);\n line-height: var(--uzi-control-line-height);\n font-family: inherit;\n box-shadow: var(--uzi-control-shadow);\n transition:\n border-color 0.15s ease,\n background 0.15s ease,\n color 0.15s ease,\n box-shadow 0.15s ease;\n cursor: pointer;\n text-align: left;\n}\n\n.trigger:hover:not(:disabled) {\n border-color: var(--ts-dropdown-accent, var(--accent));\n box-shadow: var(--uzi-control-shadow-hover);\n}\n\n.trigger:focus-visible,\n.trigger[data-state=\"open\"] {\n border-color: var(--ts-dropdown-accent, var(--primary));\n outline: var(--focus-ring);\n outline-offset: var(--focus-ring-offset);\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--primary) 30%, transparent);\n}\n\n.trigger:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n box-shadow: none;\n}\n\n.value {\n min-width: 0;\n display: flex;\n flex: 1;\n flex-wrap: wrap;\n gap: 0.375rem;\n align-items: center;\n}\n\n.placeholder {\n color: var(--muted-foreground);\n}\n\n.summary {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 500;\n}\n\n.chip {\n display: inline-flex;\n align-items: center;\n max-width: 100%;\n padding: 2px 8px;\n border-radius: 999px;\n border: 1px solid color-mix(in srgb, var(--primary) 32%, transparent);\n background: color-mix(in srgb, var(--primary) 10%, transparent);\n color: var(--ts-dropdown-text, var(--foreground));\n font-size: 0.75rem;\n font-weight: 600;\n line-height: 1.2;\n white-space: nowrap;\n}\n\n.chipSummary {\n color: var(--primary);\n}\n\n.chevron {\n width: 10px;\n height: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--muted-foreground);\n flex-shrink: 0;\n transition: transform 0.15s ease, color 0.15s ease;\n}\n\n.trigger:hover:not(:disabled) .chevron,\n.trigger[data-state=\"open\"] .chevron {\n color: var(--ts-dropdown-text, var(--foreground));\n}\n\n.trigger[data-state=\"open\"] .chevron {\n transform: rotate(180deg);\n}\n\n.menu {\n position: absolute;\n top: calc(100% + 6px);\n left: 0;\n z-index: var(--uzi-popover-z-index, 1100);\n min-width: var(--radix-dropdown-menu-trigger-width);\n width: max(var(--radix-dropdown-menu-trigger-width), var(--uzi-multiselect-content-min-width, 0px));\n max-width: min(calc(100vw - 1rem), var(--radix-dropdown-menu-content-available-width));\n max-height: min(28rem, var(--radix-dropdown-menu-content-available-height, calc(100dvh - 1rem)));\n display: flex;\n flex-direction: column;\n overflow: hidden;\n border: 1px solid var(--ts-menu-border, var(--border));\n border-radius: var(--uzi-menu-radius);\n background: var(--ts-menu-bg, var(--popover));\n color: var(--ts-menu-fg, var(--popover-foreground));\n padding: var(--uzi-menu-padding);\n box-shadow: var(--uzi-menu-shadow);\n animation: menuFadeIn 140ms ease;\n}\n\n.bulkAction {\n width: 100%;\n display: flex;\n align-items: center;\n border: none;\n border-radius: var(--uzi-menu-item-radius);\n background: transparent;\n color: inherit;\n padding: var(--uzi-menu-item-padding-y) var(--uzi-menu-item-padding-x);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n outline: none;\n}\n\n.bulkAction[data-highlighted] {\n background: var(--ts-menu-hover-bg, var(--accent));\n color: var(--ts-menu-hover-fg, var(--accent-foreground));\n}\n\n.bulkAction[data-disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.separator {\n height: 1px;\n margin: var(--uzi-menu-padding) 0;\n background: var(--ts-menu-border, var(--border));\n}\n\n.option {\n width: 100%;\n display: flex;\n align-items: center;\n gap: var(--uzi-menu-item-gap);\n border: none;\n border-radius: var(--uzi-menu-item-radius);\n background: transparent;\n color: inherit;\n padding: var(--uzi-menu-item-padding-y) var(--uzi-menu-item-padding-x);\n font: inherit;\n cursor: pointer;\n text-align: left;\n transition: background 0.12s ease, color 0.12s ease;\n}\n\n.option:hover:not(:disabled),\n.option:focus-visible,\n.option[data-highlighted] {\n background: var(--ts-menu-hover-bg, var(--accent));\n color: var(--ts-menu-hover-fg, var(--accent-foreground));\n outline: none;\n}\n\n.optionDisabled,\n.option[data-disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.optionSelected .optionLabel {\n color: var(--primary);\n}\n\n.indicator {\n width: 1rem;\n height: 1rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n border-radius: 0.3125rem;\n border: 1px solid var(--border);\n color: transparent;\n background: transparent;\n transition: border-color 0.12s ease, background 0.12s ease, color 0.12s ease;\n}\n\n.indicatorSelected {\n border-color: color-mix(in srgb, var(--primary) 55%, transparent);\n background: color-mix(in srgb, var(--primary) 12%, transparent);\n color: var(--primary);\n}\n\n.indicatorDisabled {\n opacity: 0.65;\n}\n\n.optionLabel {\n min-width: 0;\n flex: 1;\n display: inline-flex;\n align-items: center;\n gap: var(--uzi-menu-item-gap);\n}\n\n.optionIcon {\n width: 1rem;\n height: 1rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.optionIcon > * {\n max-width: 100%;\n max-height: 100%;\n}\n\n@keyframes menuFadeIn {\n from {\n opacity: 0;\n transform: translateY(-4px) scale(0.98);\n }\n\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n.draftActions {\n display: flex;\n flex-shrink: 0;\n gap: 0.5rem;\n padding: 0.5rem 0.25rem 0.125rem;\n border-top: 1px solid var(--ts-menu-border, var(--border));\n}\n.draftButton {\n flex: 1;\n border: 1px solid var(--ts-menu-border, var(--border));\n border-radius: var(--uzi-menu-item-radius);\n background: transparent;\n color: inherit;\n padding: 0.4rem 0.75rem;\n font: inherit;\n cursor: pointer;\n}\n.draftButton:last-child {\n background: var(--primary);\n color: var(--primary-foreground);\n border-color: var(--primary);\n}\n.draftButton:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-ring-offset); }\n.draftButton:disabled { opacity: 0.5; cursor: not-allowed; }\n\n.optionsViewport {\n min-height: 0;\n overflow-y: auto;\n overscroll-behavior: contain;\n flex: 1 1 auto;\n scrollbar-gutter: stable;\n}\n"],"mappings":""}
package/dist/index27.cjs CHANGED
@@ -20,6 +20,9 @@ var indicator = "uzi-multi-select-module__indicator__NO-WE";
20
20
  var indicatorSelected = "uzi-multi-select-module__indicatorSelected__9sEAy";
21
21
  var indicatorDisabled = "uzi-multi-select-module__indicatorDisabled__vqdiH";
22
22
  var optionIcon = "uzi-multi-select-module__optionIcon__TQddK";
23
+ var draftActions = "uzi-multi-select-module__draftActions__-R2g3";
24
+ var draftButton = "uzi-multi-select-module__draftButton__tO1zu";
25
+ var optionsViewport = "uzi-multi-select-module__optionsViewport__Fus0q";
23
26
  var multi_select_module_default = {
24
27
  wrapper,
25
28
  wrapperFullWidth,
@@ -41,7 +44,10 @@ var multi_select_module_default = {
41
44
  indicator,
42
45
  indicatorSelected,
43
46
  indicatorDisabled,
44
- optionIcon
47
+ optionIcon,
48
+ draftActions,
49
+ draftButton,
50
+ optionsViewport
45
51
  };
46
52
  //#endregion
47
53
  exports.bulkAction = bulkAction;
@@ -49,6 +55,8 @@ exports.chevron = chevron;
49
55
  exports.chip = chip;
50
56
  exports.chipSummary = chipSummary;
51
57
  exports.default = multi_select_module_default;
58
+ exports.draftActions = draftActions;
59
+ exports.draftButton = draftButton;
52
60
  exports.indicator = indicator;
53
61
  exports.indicatorDisabled = indicatorDisabled;
54
62
  exports.indicatorSelected = indicatorSelected;
@@ -59,6 +67,7 @@ exports.optionDisabled = optionDisabled;
59
67
  exports.optionIcon = optionIcon;
60
68
  exports.optionLabel = optionLabel;
61
69
  exports.optionSelected = optionSelected;
70
+ exports.optionsViewport = optionsViewport;
62
71
  exports.placeholder = placeholder;
63
72
  exports.separator = separator;
64
73
  exports.summary = summary;
@@ -1 +1 @@
1
- {"version":3,"file":"index27.cjs","names":[],"sources":["../src/components/multi-select/multi-select.module.css"],"sourcesContent":[".wrapper {\n position: relative;\n display: inline-flex;\n min-width: 0;\n max-width: 100%;\n}\n\n.wrapperFullWidth {\n width: 100%;\n}\n\n.trigger {\n width: 100%;\n min-width: 0;\n min-height: var(--uzi-control-min-height);\n display: inline-flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.625rem;\n border-radius: var(--uzi-control-radius);\n border: 1px solid var(--ts-dropdown-border, var(--border));\n background: var(--ts-dropdown-bg, var(--uzi-surface-2, var(--secondary)));\n color: var(--ts-dropdown-text, var(--foreground));\n padding:\n var(--uzi-control-padding-y)\n var(--uzi-control-padding-x-compact)\n var(--uzi-control-padding-y)\n var(--uzi-control-padding-x);\n font-size: var(--uzi-control-font-size);\n line-height: var(--uzi-control-line-height);\n font-family: inherit;\n box-shadow: var(--uzi-control-shadow);\n transition:\n border-color 0.15s ease,\n background 0.15s ease,\n color 0.15s ease,\n box-shadow 0.15s ease;\n cursor: pointer;\n text-align: left;\n}\n\n.trigger:hover:not(:disabled) {\n border-color: var(--ts-dropdown-accent, var(--accent));\n box-shadow: var(--uzi-control-shadow-hover);\n}\n\n.trigger:focus-visible,\n.trigger[data-state=\"open\"] {\n border-color: var(--ts-dropdown-accent, var(--primary));\n outline: var(--focus-ring);\n outline-offset: var(--focus-ring-offset);\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--primary) 30%, transparent);\n}\n\n.trigger:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n box-shadow: none;\n}\n\n.value {\n min-width: 0;\n display: flex;\n flex: 1;\n flex-wrap: wrap;\n gap: 0.375rem;\n align-items: center;\n}\n\n.placeholder {\n color: var(--muted-foreground);\n}\n\n.summary {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 500;\n}\n\n.chip {\n display: inline-flex;\n align-items: center;\n max-width: 100%;\n padding: 2px 8px;\n border-radius: 999px;\n border: 1px solid color-mix(in srgb, var(--primary) 32%, transparent);\n background: color-mix(in srgb, var(--primary) 10%, transparent);\n color: var(--ts-dropdown-text, var(--foreground));\n font-size: 0.75rem;\n font-weight: 600;\n line-height: 1.2;\n white-space: nowrap;\n}\n\n.chipSummary {\n color: var(--primary);\n}\n\n.chevron {\n width: 10px;\n height: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--muted-foreground);\n flex-shrink: 0;\n transition: transform 0.15s ease, color 0.15s ease;\n}\n\n.trigger:hover:not(:disabled) .chevron,\n.trigger[data-state=\"open\"] .chevron {\n color: var(--ts-dropdown-text, var(--foreground));\n}\n\n.trigger[data-state=\"open\"] .chevron {\n transform: rotate(180deg);\n}\n\n.menu {\n position: absolute;\n top: calc(100% + 6px);\n left: 0;\n z-index: var(--uzi-popover-z-index, 1100);\n min-width: 100%;\n max-height: 16rem;\n overflow-y: auto;\n border: 1px solid var(--ts-menu-border, var(--border));\n border-radius: var(--uzi-menu-radius);\n background: var(--ts-menu-bg, var(--popover));\n color: var(--ts-menu-fg, var(--popover-foreground));\n padding: var(--uzi-menu-padding);\n box-shadow: var(--uzi-menu-shadow);\n animation: menuFadeIn 140ms ease;\n}\n\n.bulkAction {\n width: 100%;\n display: flex;\n align-items: center;\n border: none;\n border-radius: var(--uzi-menu-item-radius);\n background: transparent;\n color: inherit;\n padding: var(--uzi-menu-item-padding-y) var(--uzi-menu-item-padding-x);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n outline: none;\n}\n\n.bulkAction[data-highlighted] {\n background: var(--ts-menu-hover-bg, var(--accent));\n color: var(--ts-menu-hover-fg, var(--accent-foreground));\n}\n\n.bulkAction[data-disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.separator {\n height: 1px;\n margin: var(--uzi-menu-padding) 0;\n background: var(--ts-menu-border, var(--border));\n}\n\n.option {\n width: 100%;\n display: flex;\n align-items: center;\n gap: var(--uzi-menu-item-gap);\n border: none;\n border-radius: var(--uzi-menu-item-radius);\n background: transparent;\n color: inherit;\n padding: var(--uzi-menu-item-padding-y) var(--uzi-menu-item-padding-x);\n font: inherit;\n cursor: pointer;\n text-align: left;\n transition: background 0.12s ease, color 0.12s ease;\n}\n\n.option:hover:not(:disabled),\n.option:focus-visible,\n.option[data-highlighted] {\n background: var(--ts-menu-hover-bg, var(--accent));\n color: var(--ts-menu-hover-fg, var(--accent-foreground));\n outline: none;\n}\n\n.optionDisabled,\n.option[data-disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.optionSelected .optionLabel {\n color: var(--primary);\n}\n\n.indicator {\n width: 1rem;\n height: 1rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n border-radius: 0.3125rem;\n border: 1px solid var(--border);\n color: transparent;\n background: transparent;\n transition: border-color 0.12s ease, background 0.12s ease, color 0.12s ease;\n}\n\n.indicatorSelected {\n border-color: color-mix(in srgb, var(--primary) 55%, transparent);\n background: color-mix(in srgb, var(--primary) 12%, transparent);\n color: var(--primary);\n}\n\n.indicatorDisabled {\n opacity: 0.65;\n}\n\n.optionLabel {\n min-width: 0;\n flex: 1;\n display: inline-flex;\n align-items: center;\n gap: var(--uzi-menu-item-gap);\n}\n\n.optionIcon {\n width: 1rem;\n height: 1rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.optionIcon > * {\n max-width: 100%;\n max-height: 100%;\n}\n\n@keyframes menuFadeIn {\n from {\n opacity: 0;\n transform: translateY(-4px) scale(0.98);\n }\n\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"index27.cjs","names":[],"sources":["../src/components/multi-select/multi-select.module.css"],"sourcesContent":[".wrapper {\n position: relative;\n display: inline-flex;\n min-width: 0;\n max-width: 100%;\n}\n\n.wrapperFullWidth {\n width: 100%;\n}\n\n.trigger {\n width: 100%;\n min-width: 0;\n min-height: var(--uzi-control-min-height);\n display: inline-flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.625rem;\n border-radius: var(--uzi-control-radius);\n border: 1px solid var(--ts-dropdown-border, var(--border));\n background: var(--ts-dropdown-bg, var(--uzi-surface-2, var(--secondary)));\n color: var(--ts-dropdown-text, var(--foreground));\n padding:\n var(--uzi-control-padding-y)\n var(--uzi-control-padding-x-compact)\n var(--uzi-control-padding-y)\n var(--uzi-control-padding-x);\n font-size: var(--uzi-control-font-size);\n line-height: var(--uzi-control-line-height);\n font-family: inherit;\n box-shadow: var(--uzi-control-shadow);\n transition:\n border-color 0.15s ease,\n background 0.15s ease,\n color 0.15s ease,\n box-shadow 0.15s ease;\n cursor: pointer;\n text-align: left;\n}\n\n.trigger:hover:not(:disabled) {\n border-color: var(--ts-dropdown-accent, var(--accent));\n box-shadow: var(--uzi-control-shadow-hover);\n}\n\n.trigger:focus-visible,\n.trigger[data-state=\"open\"] {\n border-color: var(--ts-dropdown-accent, var(--primary));\n outline: var(--focus-ring);\n outline-offset: var(--focus-ring-offset);\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--primary) 30%, transparent);\n}\n\n.trigger:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n box-shadow: none;\n}\n\n.value {\n min-width: 0;\n display: flex;\n flex: 1;\n flex-wrap: wrap;\n gap: 0.375rem;\n align-items: center;\n}\n\n.placeholder {\n color: var(--muted-foreground);\n}\n\n.summary {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 500;\n}\n\n.chip {\n display: inline-flex;\n align-items: center;\n max-width: 100%;\n padding: 2px 8px;\n border-radius: 999px;\n border: 1px solid color-mix(in srgb, var(--primary) 32%, transparent);\n background: color-mix(in srgb, var(--primary) 10%, transparent);\n color: var(--ts-dropdown-text, var(--foreground));\n font-size: 0.75rem;\n font-weight: 600;\n line-height: 1.2;\n white-space: nowrap;\n}\n\n.chipSummary {\n color: var(--primary);\n}\n\n.chevron {\n width: 10px;\n height: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--muted-foreground);\n flex-shrink: 0;\n transition: transform 0.15s ease, color 0.15s ease;\n}\n\n.trigger:hover:not(:disabled) .chevron,\n.trigger[data-state=\"open\"] .chevron {\n color: var(--ts-dropdown-text, var(--foreground));\n}\n\n.trigger[data-state=\"open\"] .chevron {\n transform: rotate(180deg);\n}\n\n.menu {\n position: absolute;\n top: calc(100% + 6px);\n left: 0;\n z-index: var(--uzi-popover-z-index, 1100);\n min-width: var(--radix-dropdown-menu-trigger-width);\n width: max(var(--radix-dropdown-menu-trigger-width), var(--uzi-multiselect-content-min-width, 0px));\n max-width: min(calc(100vw - 1rem), var(--radix-dropdown-menu-content-available-width));\n max-height: min(28rem, var(--radix-dropdown-menu-content-available-height, calc(100dvh - 1rem)));\n display: flex;\n flex-direction: column;\n overflow: hidden;\n border: 1px solid var(--ts-menu-border, var(--border));\n border-radius: var(--uzi-menu-radius);\n background: var(--ts-menu-bg, var(--popover));\n color: var(--ts-menu-fg, var(--popover-foreground));\n padding: var(--uzi-menu-padding);\n box-shadow: var(--uzi-menu-shadow);\n animation: menuFadeIn 140ms ease;\n}\n\n.bulkAction {\n width: 100%;\n display: flex;\n align-items: center;\n border: none;\n border-radius: var(--uzi-menu-item-radius);\n background: transparent;\n color: inherit;\n padding: var(--uzi-menu-item-padding-y) var(--uzi-menu-item-padding-x);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n outline: none;\n}\n\n.bulkAction[data-highlighted] {\n background: var(--ts-menu-hover-bg, var(--accent));\n color: var(--ts-menu-hover-fg, var(--accent-foreground));\n}\n\n.bulkAction[data-disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.separator {\n height: 1px;\n margin: var(--uzi-menu-padding) 0;\n background: var(--ts-menu-border, var(--border));\n}\n\n.option {\n width: 100%;\n display: flex;\n align-items: center;\n gap: var(--uzi-menu-item-gap);\n border: none;\n border-radius: var(--uzi-menu-item-radius);\n background: transparent;\n color: inherit;\n padding: var(--uzi-menu-item-padding-y) var(--uzi-menu-item-padding-x);\n font: inherit;\n cursor: pointer;\n text-align: left;\n transition: background 0.12s ease, color 0.12s ease;\n}\n\n.option:hover:not(:disabled),\n.option:focus-visible,\n.option[data-highlighted] {\n background: var(--ts-menu-hover-bg, var(--accent));\n color: var(--ts-menu-hover-fg, var(--accent-foreground));\n outline: none;\n}\n\n.optionDisabled,\n.option[data-disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.optionSelected .optionLabel {\n color: var(--primary);\n}\n\n.indicator {\n width: 1rem;\n height: 1rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n border-radius: 0.3125rem;\n border: 1px solid var(--border);\n color: transparent;\n background: transparent;\n transition: border-color 0.12s ease, background 0.12s ease, color 0.12s ease;\n}\n\n.indicatorSelected {\n border-color: color-mix(in srgb, var(--primary) 55%, transparent);\n background: color-mix(in srgb, var(--primary) 12%, transparent);\n color: var(--primary);\n}\n\n.indicatorDisabled {\n opacity: 0.65;\n}\n\n.optionLabel {\n min-width: 0;\n flex: 1;\n display: inline-flex;\n align-items: center;\n gap: var(--uzi-menu-item-gap);\n}\n\n.optionIcon {\n width: 1rem;\n height: 1rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.optionIcon > * {\n max-width: 100%;\n max-height: 100%;\n}\n\n@keyframes menuFadeIn {\n from {\n opacity: 0;\n transform: translateY(-4px) scale(0.98);\n }\n\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n.draftActions {\n display: flex;\n flex-shrink: 0;\n gap: 0.5rem;\n padding: 0.5rem 0.25rem 0.125rem;\n border-top: 1px solid var(--ts-menu-border, var(--border));\n}\n.draftButton {\n flex: 1;\n border: 1px solid var(--ts-menu-border, var(--border));\n border-radius: var(--uzi-menu-item-radius);\n background: transparent;\n color: inherit;\n padding: 0.4rem 0.75rem;\n font: inherit;\n cursor: pointer;\n}\n.draftButton:last-child {\n background: var(--primary);\n color: var(--primary-foreground);\n border-color: var(--primary);\n}\n.draftButton:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-ring-offset); }\n.draftButton:disabled { opacity: 0.5; cursor: not-allowed; }\n\n.optionsViewport {\n min-height: 0;\n overflow-y: auto;\n overscroll-behavior: contain;\n flex: 1 1 auto;\n scrollbar-gutter: stable;\n}\n"],"mappings":""}
package/dist/index27.js CHANGED
@@ -5,9 +5,16 @@ import * as React from "react";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
7
7
  //#region src/components/multi-select/MultiSelect.tsx
8
- var MultiSelect = React.forwardRef(({ options, value, onChange, placeholder = "Select options", fullWidth = true, maxVisibleValues = 2, formatValue, bulkActions = false, selectAllLabel = "Select all", clearAllLabel, className, disabled = false, name, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy }, ref) => {
9
- const selectedSet = React.useMemo(() => new Set(value), [value]);
10
- const selectedOptions = React.useMemo(() => options.filter((opt) => selectedSet.has(opt.value)), [options, selectedSet]);
8
+ var MultiSelect = React.forwardRef(({ options, value, onChange, placeholder = "Select options", fullWidth = true, maxVisibleValues = 2, contentMinWidth, formatValue, bulkActions = false, draftMode = false, minSelected = 0, selectAllLabel = "Select all", clearAllLabel, className, disabled = false, name, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy }, ref) => {
9
+ const [open, setOpen] = React.useState(false);
10
+ const [draft, setDraft] = React.useState(value);
11
+ const editValue = draftMode && open ? draft : value;
12
+ const updateSelection = (next) => {
13
+ if (draftMode) setDraft(next);
14
+ else if (next.length >= minSelected) onChange(next);
15
+ };
16
+ const selectedSet = React.useMemo(() => new Set(editValue), [editValue]);
17
+ const selectedOptions = React.useMemo(() => options.filter((opt) => value.includes(opt.value)), [options, value]);
11
18
  const enabledValues = React.useMemo(() => options.filter((option) => !option.disabled).map((option) => option.value), [options]);
12
19
  const allEnabledSelected = enabledValues.length > 0 && enabledValues.every((entry) => selectedSet.has(entry));
13
20
  const hasDisabledOptions = options.some((option) => option.disabled);
@@ -15,20 +22,25 @@ var MultiSelect = React.forwardRef(({ options, value, onChange, placeholder = "S
15
22
  const formattedValue = React.useMemo(() => formatValue?.(selectedOptions), [formatValue, selectedOptions]);
16
23
  const toggleValue = React.useCallback((nextValue) => {
17
24
  if (selectedSet.has(nextValue)) {
18
- onChange(value.filter((entry) => entry !== nextValue));
25
+ updateSelection(editValue.filter((entry) => entry !== nextValue));
19
26
  return;
20
27
  }
21
- onChange([...value, nextValue]);
28
+ updateSelection([...editValue, nextValue]);
22
29
  }, [
23
- onChange,
30
+ updateSelection,
24
31
  selectedSet,
25
- value
32
+ editValue
26
33
  ]);
27
34
  const visibleCount = Math.max(1, maxVisibleValues);
28
35
  const visibleOptions = selectedOptions.slice(0, visibleCount);
29
36
  const overflowCount = Math.max(0, selectedOptions.length - visibleOptions.length);
30
37
  return /* @__PURE__ */ jsx(DropdownMenuPrimitive.Root, {
31
38
  modal: false,
39
+ open,
40
+ onOpenChange: (next) => {
41
+ setDraft([...value]);
42
+ setOpen(next);
43
+ },
32
44
  children: /* @__PURE__ */ jsxs("div", {
33
45
  className: cx(multi_select_module_default.wrapper, fullWidth && multi_select_module_default.wrapperFullWidth, className),
34
46
  children: [
@@ -87,6 +99,7 @@ var MultiSelect = React.forwardRef(({ options, value, onChange, placeholder = "S
87
99
  }, entry)) : null,
88
100
  /* @__PURE__ */ jsx(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsxs(DropdownMenuPrimitive.Content, {
89
101
  className: multi_select_module_default.menu,
102
+ style: contentMinWidth ? { "--uzi-multiselect-content-min-width": contentMinWidth } : void 0,
90
103
  sideOffset: 4,
91
104
  align: "start",
92
105
  children: [
@@ -95,7 +108,7 @@ var MultiSelect = React.forwardRef(({ options, value, onChange, placeholder = "S
95
108
  disabled: allEnabledSelected,
96
109
  onSelect: (event) => {
97
110
  event.preventDefault();
98
- onChange([...value, ...enabledValues.filter((entry) => !selectedSet.has(entry))]);
111
+ updateSelection([...editValue, ...enabledValues.filter((entry) => !selectedSet.has(entry))]);
99
112
  },
100
113
  children: selectAllLabel
101
114
  }), /* @__PURE__ */ jsx(DropdownMenuPrimitive.Item, {
@@ -104,49 +117,73 @@ var MultiSelect = React.forwardRef(({ options, value, onChange, placeholder = "S
104
117
  onSelect: (event) => {
105
118
  event.preventDefault();
106
119
  const enabledSet = new Set(enabledValues);
107
- onChange(value.filter((entry) => !enabledSet.has(entry)));
120
+ updateSelection(editValue.filter((entry) => !enabledSet.has(entry)));
108
121
  },
109
122
  children: resolvedClearAllLabel
110
123
  })] }) : null,
111
124
  bulkActions && enabledValues.length > 0 ? /* @__PURE__ */ jsx(DropdownMenuPrimitive.Separator, { className: multi_select_module_default.separator }) : null,
112
- options.map((option) => {
113
- const selected = selectedSet.has(option.value);
114
- return /* @__PURE__ */ jsxs(DropdownMenuPrimitive.CheckboxItem, {
115
- className: cx(multi_select_module_default.option, selected && multi_select_module_default.optionSelected, option.disabled && multi_select_module_default.optionDisabled),
116
- checked: selected,
117
- disabled: option.disabled,
118
- onCheckedChange: () => toggleValue(option.value),
119
- onSelect: (event) => event.preventDefault(),
120
- children: [/* @__PURE__ */ jsx("span", {
121
- className: cx(multi_select_module_default.indicator, selected && multi_select_module_default.indicatorSelected, option.disabled && multi_select_module_default.indicatorDisabled),
122
- "aria-hidden": "true",
123
- children: /* @__PURE__ */ jsx(DropdownMenuPrimitive.ItemIndicator, {
124
- forceMount: true,
125
- children: /* @__PURE__ */ jsx("svg", {
126
- viewBox: "0 0 16 16",
127
- width: "16",
128
- height: "16",
129
- fill: "none",
130
- xmlns: "http://www.w3.org/2000/svg",
131
- children: /* @__PURE__ */ jsx("path", {
132
- d: "M3.5 8.5 6.5 11.5 12.5 4.5",
133
- stroke: "currentColor",
134
- strokeWidth: "1.8",
135
- strokeLinecap: "round",
136
- strokeLinejoin: "round"
125
+ /* @__PURE__ */ jsx("div", {
126
+ className: multi_select_module_default.optionsViewport,
127
+ children: options.map((option) => {
128
+ const selected = selectedSet.has(option.value);
129
+ return /* @__PURE__ */ jsxs(DropdownMenuPrimitive.CheckboxItem, {
130
+ className: cx(multi_select_module_default.option, selected && multi_select_module_default.optionSelected, option.disabled && multi_select_module_default.optionDisabled),
131
+ checked: selected,
132
+ disabled: option.disabled,
133
+ onCheckedChange: () => toggleValue(option.value),
134
+ onSelect: (event) => event.preventDefault(),
135
+ children: [/* @__PURE__ */ jsx("span", {
136
+ className: cx(multi_select_module_default.indicator, selected && multi_select_module_default.indicatorSelected, option.disabled && multi_select_module_default.indicatorDisabled),
137
+ "aria-hidden": "true",
138
+ children: /* @__PURE__ */ jsx(DropdownMenuPrimitive.ItemIndicator, {
139
+ forceMount: true,
140
+ children: /* @__PURE__ */ jsx("svg", {
141
+ viewBox: "0 0 16 16",
142
+ width: "16",
143
+ height: "16",
144
+ fill: "none",
145
+ xmlns: "http://www.w3.org/2000/svg",
146
+ children: /* @__PURE__ */ jsx("path", {
147
+ d: "M3.5 8.5 6.5 11.5 12.5 4.5",
148
+ stroke: "currentColor",
149
+ strokeWidth: "1.8",
150
+ strokeLinecap: "round",
151
+ strokeLinejoin: "round"
152
+ })
137
153
  })
138
154
  })
139
- })
140
- }), /* @__PURE__ */ jsxs("span", {
141
- className: multi_select_module_default.optionLabel,
142
- children: [option.icon ? /* @__PURE__ */ jsx("span", {
143
- className: multi_select_module_default.optionIcon,
144
- "aria-hidden": "true",
145
- children: option.icon
146
- }) : null, option.label]
147
- })]
148
- }, option.value);
149
- })
155
+ }), /* @__PURE__ */ jsxs("span", {
156
+ className: multi_select_module_default.optionLabel,
157
+ children: [option.icon ? /* @__PURE__ */ jsx("span", {
158
+ className: multi_select_module_default.optionIcon,
159
+ "aria-hidden": "true",
160
+ children: option.icon
161
+ }) : null, option.label]
162
+ })]
163
+ }, option.value);
164
+ })
165
+ }),
166
+ draftMode ? /* @__PURE__ */ jsxs("div", {
167
+ className: multi_select_module_default.draftActions,
168
+ children: [/* @__PURE__ */ jsx("button", {
169
+ type: "button",
170
+ className: multi_select_module_default.draftButton,
171
+ onClick: () => {
172
+ setDraft([...value]);
173
+ setOpen(false);
174
+ },
175
+ children: "Cancel"
176
+ }), /* @__PURE__ */ jsx("button", {
177
+ type: "button",
178
+ className: multi_select_module_default.draftButton,
179
+ disabled: editValue.length < minSelected,
180
+ onClick: () => {
181
+ onChange(editValue);
182
+ setOpen(false);
183
+ },
184
+ children: "Apply"
185
+ })]
186
+ }) : null
150
187
  ]
151
188
  }) })
152
189
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"index27.js","names":[],"sources":["../src/components/multi-select/MultiSelect.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\";\nimport { cx } from \"../../utils/cx\";\nimport styles from \"./multi-select.module.css\";\n\nexport type MultiSelectOption = {\n label: string;\n value: string;\n disabled?: boolean;\n /** Optional decorative leading visual. The label remains the accessible option name. */\n icon?: React.ReactElement;\n};\n\nexport type MultiSelectProps = {\n options: MultiSelectOption[];\n value: string[];\n onChange: (value: string[]) => void;\n placeholder?: string;\n fullWidth?: boolean;\n maxVisibleValues?: number;\n /** Formats the closed trigger text from the selected options, including an empty selection. */\n formatValue?: (selected: MultiSelectOption[]) => string;\n /** Shows Select all and Clear all actions. Bulk actions modify enabled options only. */\n bulkActions?: boolean;\n selectAllLabel?: string;\n /** Label for clearing all enabled selections. Disabled selected options remain selected. */\n clearAllLabel?: string;\n className?: string;\n disabled?: boolean;\n name?: string;\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n};\n\nexport const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(\n (\n {\n options,\n value,\n onChange,\n placeholder = \"Select options\",\n fullWidth = true,\n maxVisibleValues = 2,\n formatValue,\n bulkActions = false,\n selectAllLabel = \"Select all\",\n clearAllLabel,\n className,\n disabled = false,\n name,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n },\n ref,\n ) => {\n const selectedSet = React.useMemo(() => new Set(value), [value]);\n const selectedOptions = React.useMemo(\n () => options.filter((opt) => selectedSet.has(opt.value)),\n [options, selectedSet],\n );\n\n const enabledValues = React.useMemo(\n () => options.filter((option) => !option.disabled).map((option) => option.value),\n [options],\n );\n const allEnabledSelected =\n enabledValues.length > 0 && enabledValues.every((entry) => selectedSet.has(entry));\n const hasDisabledOptions = options.some((option) => option.disabled);\n const resolvedClearAllLabel =\n clearAllLabel ?? (hasDisabledOptions ? \"Clear enabled\" : \"Clear all\");\n\n const formattedValue = React.useMemo(\n () => formatValue?.(selectedOptions),\n [formatValue, selectedOptions],\n );\n\n const toggleValue = React.useCallback(\n (nextValue: string) => {\n if (selectedSet.has(nextValue)) {\n onChange(value.filter((entry) => entry !== nextValue));\n return;\n }\n\n onChange([...value, nextValue]);\n },\n [onChange, selectedSet, value],\n );\n\n const visibleCount = Math.max(1, maxVisibleValues);\n const visibleOptions = selectedOptions.slice(0, visibleCount);\n const overflowCount = Math.max(\n 0,\n selectedOptions.length - visibleOptions.length,\n );\n\n return (\n <DropdownMenuPrimitive.Root modal={false}>\n <div\n className={cx(\n styles.wrapper,\n fullWidth && styles.wrapperFullWidth,\n className,\n )}\n >\n <DropdownMenuPrimitive.Trigger asChild>\n <button\n ref={ref}\n type=\"button\"\n className={styles.trigger}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n disabled={disabled}\n >\n <span className={styles.value}>\n {formatValue ? (\n <span className={styles.summary}>{formattedValue}</span>\n ) : selectedOptions.length === 0 ? (\n <span className={styles.placeholder}>{placeholder}</span>\n ) : (\n <>\n {visibleOptions.map((option) => (\n <span key={option.value} className={styles.chip}>\n {option.icon ? <span className={styles.optionIcon} aria-hidden=\"true\">{option.icon}</span> : null}\n {option.label}\n </span>\n ))}\n {overflowCount > 0 ? (\n <span className={cx(styles.chip, styles.chipSummary)}>\n +{overflowCount}\n </span>\n ) : null}\n </>\n )}\n </span>\n <span className={styles.chevron} aria-hidden=\"true\">\n <svg\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"10\"\n height=\"10\"\n >\n <path\n d=\"M2 3.5L5 6.5L8 3.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </span>\n </button>\n </DropdownMenuPrimitive.Trigger>\n\n {name\n ? value.map((entry) => (\n <input key={entry} type=\"hidden\" name={name} value={entry} />\n ))\n : null}\n\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n className={styles.menu}\n sideOffset={4}\n align=\"start\"\n >\n {bulkActions && enabledValues.length > 0 ? (\n <>\n <DropdownMenuPrimitive.Item\n className={styles.bulkAction}\n disabled={allEnabledSelected}\n onSelect={(event) => {\n event.preventDefault();\n onChange([...value, ...enabledValues.filter((entry) => !selectedSet.has(entry))]);\n }}\n >\n {selectAllLabel}\n </DropdownMenuPrimitive.Item>\n <DropdownMenuPrimitive.Item\n className={styles.bulkAction}\n disabled={!enabledValues.some((entry) => selectedSet.has(entry))}\n onSelect={(event) => {\n event.preventDefault();\n const enabledSet = new Set(enabledValues);\n onChange(value.filter((entry) => !enabledSet.has(entry)));\n }}\n >\n {resolvedClearAllLabel}\n </DropdownMenuPrimitive.Item>\n </>\n ) : null}\n {bulkActions && enabledValues.length > 0 ? (\n <DropdownMenuPrimitive.Separator className={styles.separator} />\n ) : null}\n {options.map((option) => {\n const selected = selectedSet.has(option.value);\n\n return (\n <DropdownMenuPrimitive.CheckboxItem\n key={option.value}\n className={cx(\n styles.option,\n selected && styles.optionSelected,\n option.disabled && styles.optionDisabled,\n )}\n checked={selected}\n disabled={option.disabled}\n onCheckedChange={() => toggleValue(option.value)}\n onSelect={(event) => event.preventDefault()}\n >\n <span\n className={cx(\n styles.indicator,\n selected && styles.indicatorSelected,\n option.disabled && styles.indicatorDisabled,\n )}\n aria-hidden=\"true\"\n >\n <DropdownMenuPrimitive.ItemIndicator forceMount>\n <svg\n viewBox=\"0 0 16 16\"\n width=\"16\"\n height=\"16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3.5 8.5 6.5 11.5 12.5 4.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n <span className={styles.optionLabel}>\n {option.icon ? <span className={styles.optionIcon} aria-hidden=\"true\">{option.icon}</span> : null}\n {option.label}\n </span>\n </DropdownMenuPrimitive.CheckboxItem>\n );\n })}\n </DropdownMenuPrimitive.Content>\n </DropdownMenuPrimitive.Portal>\n </div>\n </DropdownMenuPrimitive.Root>\n );\n },\n);\n\nMultiSelect.displayName = \"MultiSelect\";\n"],"mappings":";;;;;;;AAoCA,IAAa,cAAc,MAAM,YAE7B,EACE,SACA,OACA,UACA,cAAc,kBACd,YAAY,MACZ,mBAAmB,GACnB,aACA,cAAc,OACd,iBAAiB,cACjB,eACA,WACA,WAAW,OACX,MACA,cAAc,WACd,mBAAmB,kBAErB,QACG;CACH,MAAM,cAAc,MAAM,cAAc,IAAI,IAAI,KAAK,GAAG,CAAC,KAAK,CAAC;CAC/D,MAAM,kBAAkB,MAAM,cACtB,QAAQ,QAAQ,QAAQ,YAAY,IAAI,IAAI,KAAK,CAAC,GACxD,CAAC,SAAS,WAAW,CACvB;CAEA,MAAM,gBAAgB,MAAM,cACpB,QAAQ,QAAQ,WAAW,CAAC,OAAO,QAAQ,CAAC,CAAC,KAAK,WAAW,OAAO,KAAK,GAC/E,CAAC,OAAO,CACV;CACA,MAAM,qBACJ,cAAc,SAAS,KAAK,cAAc,OAAO,UAAU,YAAY,IAAI,KAAK,CAAC;CACnF,MAAM,qBAAqB,QAAQ,MAAM,WAAW,OAAO,QAAQ;CACnE,MAAM,wBACJ,kBAAkB,qBAAqB,kBAAkB;CAE3D,MAAM,iBAAiB,MAAM,cACrB,cAAc,eAAe,GACnC,CAAC,aAAa,eAAe,CAC/B;CAEA,MAAM,cAAc,MAAM,aACvB,cAAsB;EACrB,IAAI,YAAY,IAAI,SAAS,GAAG;GAC9B,SAAS,MAAM,QAAQ,UAAU,UAAU,SAAS,CAAC;GACrD;EACF;EAEA,SAAS,CAAC,GAAG,OAAO,SAAS,CAAC;CAChC,GACA;EAAC;EAAU;EAAa;CAAK,CAC/B;CAEA,MAAM,eAAe,KAAK,IAAI,GAAG,gBAAgB;CACjD,MAAM,iBAAiB,gBAAgB,MAAM,GAAG,YAAY;CAC5D,MAAM,gBAAgB,KAAK,IACzB,GACA,gBAAgB,SAAS,eAAe,MAC1C;CAEA,OACE,oBAAC,sBAAsB,MAAvB;EAA4B,OAAO;YACjC,qBAAC,OAAD;GACE,WAAW,GACT,4BAAO,SACP,aAAa,4BAAO,kBACpB,SACF;aALF;IAOE,oBAAC,sBAAsB,SAAvB;KAA+B,SAAA;eAC7B,qBAAC,UAAD;MACO;MACL,MAAK;MACL,WAAW,4BAAO;MAClB,cAAY;MACZ,mBAAiB;MACP;gBANZ,CAQE,oBAAC,QAAD;OAAM,WAAW,4BAAO;iBACrB,cACC,oBAAC,QAAD;QAAM,WAAW,4BAAO;kBAAU;OAAqB,CAAA,IACrD,gBAAgB,WAAW,IAC7B,oBAAC,QAAD;QAAM,WAAW,4BAAO;kBAAc;OAAkB,CAAA,IAExD,qBAAA,UAAA,EAAA,UAAA,CACG,eAAe,KAAK,WACnB,qBAAC,QAAD;QAAyB,WAAW,4BAAO;kBAA3C,CACG,OAAO,OAAO,oBAAC,QAAD;SAAM,WAAW,4BAAO;SAAY,eAAY;mBAAQ,OAAO;QAAW,CAAA,IAAI,MAC5F,OAAO,KACJ;UAHK,OAAO,KAGZ,CACP,GACA,gBAAgB,IACf,qBAAC,QAAD;QAAM,WAAW,GAAG,4BAAO,MAAM,4BAAO,WAAW;kBAAnD,CAAsD,KAClD,aACE;YACJ,IACJ,EAAA,CAAA;MAEA,CAAA,GACN,oBAAC,QAAD;OAAM,WAAW,4BAAO;OAAS,eAAY;iBAC3C,oBAAC,OAAD;QACE,SAAQ;QACR,MAAK;QACL,OAAM;QACN,OAAM;QACN,QAAO;kBAEP,oBAAC,QAAD;SACE,GAAE;SACF,QAAO;SACP,aAAY;SACZ,eAAc;SACd,gBAAe;QAChB,CAAA;OACE,CAAA;MACD,CAAA,CACA;;IACqB,CAAA;IAE9B,OACG,MAAM,KAAK,UACT,oBAAC,SAAD;KAAmB,MAAK;KAAe;KAAM,OAAO;IAAQ,GAAhD,KAAgD,CAC7D,IACD;IAEJ,oBAAC,sBAAsB,QAAvB,EAAA,UACE,qBAAC,sBAAsB,SAAvB;KACE,WAAW,4BAAO;KAClB,YAAY;KACZ,OAAM;eAHR;MAKG,eAAe,cAAc,SAAS,IACrC,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,sBAAsB,MAAvB;OACE,WAAW,4BAAO;OAClB,UAAU;OACV,WAAW,UAAU;QACnB,MAAM,eAAe;QACrB,SAAS,CAAC,GAAG,OAAO,GAAG,cAAc,QAAQ,UAAU,CAAC,YAAY,IAAI,KAAK,CAAC,CAAC,CAAC;OAClF;iBAEC;MACyB,CAAA,GAC5B,oBAAC,sBAAsB,MAAvB;OACE,WAAW,4BAAO;OAClB,UAAU,CAAC,cAAc,MAAM,UAAU,YAAY,IAAI,KAAK,CAAC;OAC/D,WAAW,UAAU;QACnB,MAAM,eAAe;QACrB,MAAM,aAAa,IAAI,IAAI,aAAa;QACxC,SAAS,MAAM,QAAQ,UAAU,CAAC,WAAW,IAAI,KAAK,CAAC,CAAC;OAC1D;iBAEC;MACyB,CAAA,CAC5B,EAAA,CAAA,IACA;MACH,eAAe,cAAc,SAAS,IACrC,oBAAC,sBAAsB,WAAvB,EAAiC,WAAW,4BAAO,UAAY,CAAA,IAC7D;MACH,QAAQ,KAAK,WAAW;OACvB,MAAM,WAAW,YAAY,IAAI,OAAO,KAAK;OAE7C,OACE,qBAAC,sBAAsB,cAAvB;QAEE,WAAW,GACT,4BAAO,QACP,YAAY,4BAAO,gBACnB,OAAO,YAAY,4BAAO,cAC5B;QACA,SAAS;QACT,UAAU,OAAO;QACjB,uBAAuB,YAAY,OAAO,KAAK;QAC/C,WAAW,UAAU,MAAM,eAAe;kBAV5C,CAYE,oBAAC,QAAD;SACE,WAAW,GACT,4BAAO,WACP,YAAY,4BAAO,mBACnB,OAAO,YAAY,4BAAO,iBAC5B;SACA,eAAY;mBAEZ,oBAAC,sBAAsB,eAAvB;UAAqC,YAAA;oBACnC,oBAAC,OAAD;WACE,SAAQ;WACR,OAAM;WACN,QAAO;WACP,MAAK;WACL,OAAM;qBAEN,oBAAC,QAAD;YACE,GAAE;YACF,QAAO;YACP,aAAY;YACZ,eAAc;YACd,gBAAe;WAChB,CAAA;UACE,CAAA;SAC8B,CAAA;QACjC,CAAA,GACN,qBAAC,QAAD;SAAM,WAAW,4BAAO;mBAAxB,CACO,OAAO,OAAO,oBAAC,QAAD;UAAM,WAAW,4BAAO;UAAY,eAAY;oBAAQ,OAAO;SAAW,CAAA,IAAI,MAC5F,OAAO,KACJ;UACwB;UAzC7B,OAAO,KAyCsB;MAExC,CAAC;KAC4B;OACH,CAAA;GAC3B;;CACqB,CAAA;AAEhC,CACF;AAEA,YAAY,cAAc"}
1
+ {"version":3,"file":"index27.js","names":[],"sources":["../src/components/multi-select/MultiSelect.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\";\nimport { cx } from \"../../utils/cx\";\nimport styles from \"./multi-select.module.css\";\n\nexport type MultiSelectOption = {\n label: string;\n value: string;\n disabled?: boolean;\n /** Optional decorative leading visual. The label remains the accessible option name. */\n icon?: React.ReactElement;\n};\n\nexport type MultiSelectProps = {\n options: MultiSelectOption[];\n value: string[];\n onChange: (value: string[]) => void;\n placeholder?: string;\n fullWidth?: boolean;\n maxVisibleValues?: number;\n /** Minimum popup width, independent of the trigger (e.g. '24rem'). */\n contentMinWidth?: string;\n /** Formats the closed trigger text from the selected options, including an empty selection. */\n formatValue?: (selected: MultiSelectOption[]) => string;\n /** Shows Select all and Clear all actions. Bulk actions modify enabled options only. */\n bulkActions?: boolean;\n /** Stage changes until Apply. Cancel/dismiss discards the draft. */\n draftMode?: boolean;\n /** Minimum number of selected values required to apply; immediate mode blocks changes below this count. */\n minSelected?: number;\n selectAllLabel?: string;\n /** Label for clearing all enabled selections. Disabled selected options remain selected. */\n clearAllLabel?: string;\n className?: string;\n disabled?: boolean;\n name?: string;\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n};\n\nexport const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(\n (\n {\n options,\n value,\n onChange,\n placeholder = \"Select options\",\n fullWidth = true,\n maxVisibleValues = 2,\n contentMinWidth,\n formatValue,\n bulkActions = false,\n draftMode = false,\n minSelected = 0,\n selectAllLabel = \"Select all\",\n clearAllLabel,\n className,\n disabled = false,\n name,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n },\n ref,\n ) => {\n const [open, setOpen] = React.useState(false);\n const [draft, setDraft] = React.useState<string[]>(value);\n const editValue = draftMode && open ? draft : value;\n const updateSelection = (next: string[]) => {\n if (draftMode) setDraft(next);\n else if (next.length >= minSelected) onChange(next);\n };\n const selectedSet = React.useMemo(() => new Set(editValue), [editValue]);\n const selectedOptions = React.useMemo(\n () => options.filter((opt) => value.includes(opt.value)),\n [options, value],\n );\n\n const enabledValues = React.useMemo(\n () => options.filter((option) => !option.disabled).map((option) => option.value),\n [options],\n );\n const allEnabledSelected =\n enabledValues.length > 0 && enabledValues.every((entry) => selectedSet.has(entry));\n const hasDisabledOptions = options.some((option) => option.disabled);\n const resolvedClearAllLabel =\n clearAllLabel ?? (hasDisabledOptions ? \"Clear enabled\" : \"Clear all\");\n\n const formattedValue = React.useMemo(\n () => formatValue?.(selectedOptions),\n [formatValue, selectedOptions],\n );\n\n const toggleValue = React.useCallback(\n (nextValue: string) => {\n if (selectedSet.has(nextValue)) {\n updateSelection(editValue.filter((entry) => entry !== nextValue));\n return;\n }\n\n updateSelection([...editValue, nextValue]);\n },\n [updateSelection, selectedSet, editValue],\n );\n\n const visibleCount = Math.max(1, maxVisibleValues);\n const visibleOptions = selectedOptions.slice(0, visibleCount);\n const overflowCount = Math.max(\n 0,\n selectedOptions.length - visibleOptions.length,\n );\n\n return (\n <DropdownMenuPrimitive.Root modal={false} open={open} onOpenChange={(next) => {\n setDraft([...value]);\n setOpen(next);\n }}>\n <div\n className={cx(\n styles.wrapper,\n fullWidth && styles.wrapperFullWidth,\n className,\n )}\n >\n <DropdownMenuPrimitive.Trigger asChild>\n <button\n ref={ref}\n type=\"button\"\n className={styles.trigger}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n disabled={disabled}\n >\n <span className={styles.value}>\n {formatValue ? (\n <span className={styles.summary}>{formattedValue}</span>\n ) : selectedOptions.length === 0 ? (\n <span className={styles.placeholder}>{placeholder}</span>\n ) : (\n <>\n {visibleOptions.map((option) => (\n <span key={option.value} className={styles.chip}>\n {option.icon ? <span className={styles.optionIcon} aria-hidden=\"true\">{option.icon}</span> : null}\n {option.label}\n </span>\n ))}\n {overflowCount > 0 ? (\n <span className={cx(styles.chip, styles.chipSummary)}>\n +{overflowCount}\n </span>\n ) : null}\n </>\n )}\n </span>\n <span className={styles.chevron} aria-hidden=\"true\">\n <svg\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"10\"\n height=\"10\"\n >\n <path\n d=\"M2 3.5L5 6.5L8 3.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </span>\n </button>\n </DropdownMenuPrimitive.Trigger>\n\n {name\n ? value.map((entry) => (\n <input key={entry} type=\"hidden\" name={name} value={entry} />\n ))\n : null}\n\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n className={styles.menu}\n style={contentMinWidth ? { '--uzi-multiselect-content-min-width': contentMinWidth } as React.CSSProperties : undefined}\n sideOffset={4}\n align=\"start\"\n >\n {bulkActions && enabledValues.length > 0 ? (\n <>\n <DropdownMenuPrimitive.Item\n className={styles.bulkAction}\n disabled={allEnabledSelected}\n onSelect={(event) => {\n event.preventDefault();\n updateSelection([...editValue, ...enabledValues.filter((entry) => !selectedSet.has(entry))]);\n }}\n >\n {selectAllLabel}\n </DropdownMenuPrimitive.Item>\n <DropdownMenuPrimitive.Item\n className={styles.bulkAction}\n disabled={!enabledValues.some((entry) => selectedSet.has(entry))}\n onSelect={(event) => {\n event.preventDefault();\n const enabledSet = new Set(enabledValues);\n updateSelection(editValue.filter((entry) => !enabledSet.has(entry)));\n }}\n >\n {resolvedClearAllLabel}\n </DropdownMenuPrimitive.Item>\n </>\n ) : null}\n {bulkActions && enabledValues.length > 0 ? (\n <DropdownMenuPrimitive.Separator className={styles.separator} />\n ) : null}\n <div className={styles.optionsViewport}>\n {options.map((option) => {\n const selected = selectedSet.has(option.value);\n\n return (\n <DropdownMenuPrimitive.CheckboxItem\n key={option.value}\n className={cx(\n styles.option,\n selected && styles.optionSelected,\n option.disabled && styles.optionDisabled,\n )}\n checked={selected}\n disabled={option.disabled}\n onCheckedChange={() => toggleValue(option.value)}\n onSelect={(event) => event.preventDefault()}\n >\n <span\n className={cx(\n styles.indicator,\n selected && styles.indicatorSelected,\n option.disabled && styles.indicatorDisabled,\n )}\n aria-hidden=\"true\"\n >\n <DropdownMenuPrimitive.ItemIndicator forceMount>\n <svg\n viewBox=\"0 0 16 16\"\n width=\"16\"\n height=\"16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3.5 8.5 6.5 11.5 12.5 4.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n <span className={styles.optionLabel}>\n {option.icon ? <span className={styles.optionIcon} aria-hidden=\"true\">{option.icon}</span> : null}\n {option.label}\n </span>\n </DropdownMenuPrimitive.CheckboxItem>\n );\n })}\n </div>\n {draftMode ? (\n <div className={styles.draftActions}>\n <button type=\"button\" className={styles.draftButton} onClick={() => { setDraft([...value]); setOpen(false); }}>Cancel</button>\n <button type=\"button\" className={styles.draftButton} disabled={editValue.length < minSelected} onClick={() => {\n onChange(editValue);\n setOpen(false);\n }}>Apply</button>\n </div>\n ) : null}\n </DropdownMenuPrimitive.Content>\n </DropdownMenuPrimitive.Portal>\n </div>\n </DropdownMenuPrimitive.Root>\n );\n },\n);\n\nMultiSelect.displayName = \"MultiSelect\";\n"],"mappings":"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"}
package/dist/index28.cjs CHANGED
@@ -8,9 +8,16 @@ let react_jsx_runtime = require("react/jsx-runtime");
8
8
  let _radix_ui_react_dropdown_menu = require("@radix-ui/react-dropdown-menu");
9
9
  _radix_ui_react_dropdown_menu = require_runtime.__toESM(_radix_ui_react_dropdown_menu, 1);
10
10
  //#region src/components/multi-select/MultiSelect.tsx
11
- var MultiSelect = react.forwardRef(({ options, value, onChange, placeholder = "Select options", fullWidth = true, maxVisibleValues = 2, formatValue, bulkActions = false, selectAllLabel = "Select all", clearAllLabel, className, disabled = false, name, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy }, ref) => {
12
- const selectedSet = react.useMemo(() => new Set(value), [value]);
13
- const selectedOptions = react.useMemo(() => options.filter((opt) => selectedSet.has(opt.value)), [options, selectedSet]);
11
+ var MultiSelect = react.forwardRef(({ options, value, onChange, placeholder = "Select options", fullWidth = true, maxVisibleValues = 2, contentMinWidth, formatValue, bulkActions = false, draftMode = false, minSelected = 0, selectAllLabel = "Select all", clearAllLabel, className, disabled = false, name, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy }, ref) => {
12
+ const [open, setOpen] = react.useState(false);
13
+ const [draft, setDraft] = react.useState(value);
14
+ const editValue = draftMode && open ? draft : value;
15
+ const updateSelection = (next) => {
16
+ if (draftMode) setDraft(next);
17
+ else if (next.length >= minSelected) onChange(next);
18
+ };
19
+ const selectedSet = react.useMemo(() => new Set(editValue), [editValue]);
20
+ const selectedOptions = react.useMemo(() => options.filter((opt) => value.includes(opt.value)), [options, value]);
14
21
  const enabledValues = react.useMemo(() => options.filter((option) => !option.disabled).map((option) => option.value), [options]);
15
22
  const allEnabledSelected = enabledValues.length > 0 && enabledValues.every((entry) => selectedSet.has(entry));
16
23
  const hasDisabledOptions = options.some((option) => option.disabled);
@@ -18,20 +25,25 @@ var MultiSelect = react.forwardRef(({ options, value, onChange, placeholder = "S
18
25
  const formattedValue = react.useMemo(() => formatValue?.(selectedOptions), [formatValue, selectedOptions]);
19
26
  const toggleValue = react.useCallback((nextValue) => {
20
27
  if (selectedSet.has(nextValue)) {
21
- onChange(value.filter((entry) => entry !== nextValue));
28
+ updateSelection(editValue.filter((entry) => entry !== nextValue));
22
29
  return;
23
30
  }
24
- onChange([...value, nextValue]);
31
+ updateSelection([...editValue, nextValue]);
25
32
  }, [
26
- onChange,
33
+ updateSelection,
27
34
  selectedSet,
28
- value
35
+ editValue
29
36
  ]);
30
37
  const visibleCount = Math.max(1, maxVisibleValues);
31
38
  const visibleOptions = selectedOptions.slice(0, visibleCount);
32
39
  const overflowCount = Math.max(0, selectedOptions.length - visibleOptions.length);
33
40
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dropdown_menu.Root, {
34
41
  modal: false,
42
+ open,
43
+ onOpenChange: (next) => {
44
+ setDraft([...value]);
45
+ setOpen(next);
46
+ },
35
47
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
36
48
  className: require_cx.cx(require_multi_select_module.default.wrapper, fullWidth && require_multi_select_module.default.wrapperFullWidth, className),
37
49
  children: [
@@ -90,6 +102,7 @@ var MultiSelect = react.forwardRef(({ options, value, onChange, placeholder = "S
90
102
  }, entry)) : null,
91
103
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dropdown_menu.Portal, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_radix_ui_react_dropdown_menu.Content, {
92
104
  className: require_multi_select_module.default.menu,
105
+ style: contentMinWidth ? { "--uzi-multiselect-content-min-width": contentMinWidth } : void 0,
93
106
  sideOffset: 4,
94
107
  align: "start",
95
108
  children: [
@@ -98,7 +111,7 @@ var MultiSelect = react.forwardRef(({ options, value, onChange, placeholder = "S
98
111
  disabled: allEnabledSelected,
99
112
  onSelect: (event) => {
100
113
  event.preventDefault();
101
- onChange([...value, ...enabledValues.filter((entry) => !selectedSet.has(entry))]);
114
+ updateSelection([...editValue, ...enabledValues.filter((entry) => !selectedSet.has(entry))]);
102
115
  },
103
116
  children: selectAllLabel
104
117
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dropdown_menu.Item, {
@@ -107,49 +120,73 @@ var MultiSelect = react.forwardRef(({ options, value, onChange, placeholder = "S
107
120
  onSelect: (event) => {
108
121
  event.preventDefault();
109
122
  const enabledSet = new Set(enabledValues);
110
- onChange(value.filter((entry) => !enabledSet.has(entry)));
123
+ updateSelection(editValue.filter((entry) => !enabledSet.has(entry)));
111
124
  },
112
125
  children: resolvedClearAllLabel
113
126
  })] }) : null,
114
127
  bulkActions && enabledValues.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dropdown_menu.Separator, { className: require_multi_select_module.default.separator }) : null,
115
- options.map((option) => {
116
- const selected = selectedSet.has(option.value);
117
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_radix_ui_react_dropdown_menu.CheckboxItem, {
118
- className: require_cx.cx(require_multi_select_module.default.option, selected && require_multi_select_module.default.optionSelected, option.disabled && require_multi_select_module.default.optionDisabled),
119
- checked: selected,
120
- disabled: option.disabled,
121
- onCheckedChange: () => toggleValue(option.value),
122
- onSelect: (event) => event.preventDefault(),
123
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
124
- className: require_cx.cx(require_multi_select_module.default.indicator, selected && require_multi_select_module.default.indicatorSelected, option.disabled && require_multi_select_module.default.indicatorDisabled),
125
- "aria-hidden": "true",
126
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dropdown_menu.ItemIndicator, {
127
- forceMount: true,
128
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
129
- viewBox: "0 0 16 16",
130
- width: "16",
131
- height: "16",
132
- fill: "none",
133
- xmlns: "http://www.w3.org/2000/svg",
134
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
135
- d: "M3.5 8.5 6.5 11.5 12.5 4.5",
136
- stroke: "currentColor",
137
- strokeWidth: "1.8",
138
- strokeLinecap: "round",
139
- strokeLinejoin: "round"
128
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
129
+ className: require_multi_select_module.default.optionsViewport,
130
+ children: options.map((option) => {
131
+ const selected = selectedSet.has(option.value);
132
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_radix_ui_react_dropdown_menu.CheckboxItem, {
133
+ className: require_cx.cx(require_multi_select_module.default.option, selected && require_multi_select_module.default.optionSelected, option.disabled && require_multi_select_module.default.optionDisabled),
134
+ checked: selected,
135
+ disabled: option.disabled,
136
+ onCheckedChange: () => toggleValue(option.value),
137
+ onSelect: (event) => event.preventDefault(),
138
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
139
+ className: require_cx.cx(require_multi_select_module.default.indicator, selected && require_multi_select_module.default.indicatorSelected, option.disabled && require_multi_select_module.default.indicatorDisabled),
140
+ "aria-hidden": "true",
141
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dropdown_menu.ItemIndicator, {
142
+ forceMount: true,
143
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
144
+ viewBox: "0 0 16 16",
145
+ width: "16",
146
+ height: "16",
147
+ fill: "none",
148
+ xmlns: "http://www.w3.org/2000/svg",
149
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
150
+ d: "M3.5 8.5 6.5 11.5 12.5 4.5",
151
+ stroke: "currentColor",
152
+ strokeWidth: "1.8",
153
+ strokeLinecap: "round",
154
+ strokeLinejoin: "round"
155
+ })
140
156
  })
141
157
  })
142
- })
143
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
144
- className: require_multi_select_module.default.optionLabel,
145
- children: [option.icon ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
146
- className: require_multi_select_module.default.optionIcon,
147
- "aria-hidden": "true",
148
- children: option.icon
149
- }) : null, option.label]
150
- })]
151
- }, option.value);
152
- })
158
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
159
+ className: require_multi_select_module.default.optionLabel,
160
+ children: [option.icon ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
161
+ className: require_multi_select_module.default.optionIcon,
162
+ "aria-hidden": "true",
163
+ children: option.icon
164
+ }) : null, option.label]
165
+ })]
166
+ }, option.value);
167
+ })
168
+ }),
169
+ draftMode ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
170
+ className: require_multi_select_module.default.draftActions,
171
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
172
+ type: "button",
173
+ className: require_multi_select_module.default.draftButton,
174
+ onClick: () => {
175
+ setDraft([...value]);
176
+ setOpen(false);
177
+ },
178
+ children: "Cancel"
179
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
180
+ type: "button",
181
+ className: require_multi_select_module.default.draftButton,
182
+ disabled: editValue.length < minSelected,
183
+ onClick: () => {
184
+ onChange(editValue);
185
+ setOpen(false);
186
+ },
187
+ children: "Apply"
188
+ })]
189
+ }) : null
153
190
  ]
154
191
  }) })
155
192
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"index28.cjs","names":[],"sources":["../src/components/multi-select/MultiSelect.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\";\nimport { cx } from \"../../utils/cx\";\nimport styles from \"./multi-select.module.css\";\n\nexport type MultiSelectOption = {\n label: string;\n value: string;\n disabled?: boolean;\n /** Optional decorative leading visual. The label remains the accessible option name. */\n icon?: React.ReactElement;\n};\n\nexport type MultiSelectProps = {\n options: MultiSelectOption[];\n value: string[];\n onChange: (value: string[]) => void;\n placeholder?: string;\n fullWidth?: boolean;\n maxVisibleValues?: number;\n /** Formats the closed trigger text from the selected options, including an empty selection. */\n formatValue?: (selected: MultiSelectOption[]) => string;\n /** Shows Select all and Clear all actions. Bulk actions modify enabled options only. */\n bulkActions?: boolean;\n selectAllLabel?: string;\n /** Label for clearing all enabled selections. Disabled selected options remain selected. */\n clearAllLabel?: string;\n className?: string;\n disabled?: boolean;\n name?: string;\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n};\n\nexport const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(\n (\n {\n options,\n value,\n onChange,\n placeholder = \"Select options\",\n fullWidth = true,\n maxVisibleValues = 2,\n formatValue,\n bulkActions = false,\n selectAllLabel = \"Select all\",\n clearAllLabel,\n className,\n disabled = false,\n name,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n },\n ref,\n ) => {\n const selectedSet = React.useMemo(() => new Set(value), [value]);\n const selectedOptions = React.useMemo(\n () => options.filter((opt) => selectedSet.has(opt.value)),\n [options, selectedSet],\n );\n\n const enabledValues = React.useMemo(\n () => options.filter((option) => !option.disabled).map((option) => option.value),\n [options],\n );\n const allEnabledSelected =\n enabledValues.length > 0 && enabledValues.every((entry) => selectedSet.has(entry));\n const hasDisabledOptions = options.some((option) => option.disabled);\n const resolvedClearAllLabel =\n clearAllLabel ?? (hasDisabledOptions ? \"Clear enabled\" : \"Clear all\");\n\n const formattedValue = React.useMemo(\n () => formatValue?.(selectedOptions),\n [formatValue, selectedOptions],\n );\n\n const toggleValue = React.useCallback(\n (nextValue: string) => {\n if (selectedSet.has(nextValue)) {\n onChange(value.filter((entry) => entry !== nextValue));\n return;\n }\n\n onChange([...value, nextValue]);\n },\n [onChange, selectedSet, value],\n );\n\n const visibleCount = Math.max(1, maxVisibleValues);\n const visibleOptions = selectedOptions.slice(0, visibleCount);\n const overflowCount = Math.max(\n 0,\n selectedOptions.length - visibleOptions.length,\n );\n\n return (\n <DropdownMenuPrimitive.Root modal={false}>\n <div\n className={cx(\n styles.wrapper,\n fullWidth && styles.wrapperFullWidth,\n className,\n )}\n >\n <DropdownMenuPrimitive.Trigger asChild>\n <button\n ref={ref}\n type=\"button\"\n className={styles.trigger}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n disabled={disabled}\n >\n <span className={styles.value}>\n {formatValue ? (\n <span className={styles.summary}>{formattedValue}</span>\n ) : selectedOptions.length === 0 ? (\n <span className={styles.placeholder}>{placeholder}</span>\n ) : (\n <>\n {visibleOptions.map((option) => (\n <span key={option.value} className={styles.chip}>\n {option.icon ? <span className={styles.optionIcon} aria-hidden=\"true\">{option.icon}</span> : null}\n {option.label}\n </span>\n ))}\n {overflowCount > 0 ? (\n <span className={cx(styles.chip, styles.chipSummary)}>\n +{overflowCount}\n </span>\n ) : null}\n </>\n )}\n </span>\n <span className={styles.chevron} aria-hidden=\"true\">\n <svg\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"10\"\n height=\"10\"\n >\n <path\n d=\"M2 3.5L5 6.5L8 3.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </span>\n </button>\n </DropdownMenuPrimitive.Trigger>\n\n {name\n ? value.map((entry) => (\n <input key={entry} type=\"hidden\" name={name} value={entry} />\n ))\n : null}\n\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n className={styles.menu}\n sideOffset={4}\n align=\"start\"\n >\n {bulkActions && enabledValues.length > 0 ? (\n <>\n <DropdownMenuPrimitive.Item\n className={styles.bulkAction}\n disabled={allEnabledSelected}\n onSelect={(event) => {\n event.preventDefault();\n onChange([...value, ...enabledValues.filter((entry) => !selectedSet.has(entry))]);\n }}\n >\n {selectAllLabel}\n </DropdownMenuPrimitive.Item>\n <DropdownMenuPrimitive.Item\n className={styles.bulkAction}\n disabled={!enabledValues.some((entry) => selectedSet.has(entry))}\n onSelect={(event) => {\n event.preventDefault();\n const enabledSet = new Set(enabledValues);\n onChange(value.filter((entry) => !enabledSet.has(entry)));\n }}\n >\n {resolvedClearAllLabel}\n </DropdownMenuPrimitive.Item>\n </>\n ) : null}\n {bulkActions && enabledValues.length > 0 ? (\n <DropdownMenuPrimitive.Separator className={styles.separator} />\n ) : null}\n {options.map((option) => {\n const selected = selectedSet.has(option.value);\n\n return (\n <DropdownMenuPrimitive.CheckboxItem\n key={option.value}\n className={cx(\n styles.option,\n selected && styles.optionSelected,\n option.disabled && styles.optionDisabled,\n )}\n checked={selected}\n disabled={option.disabled}\n onCheckedChange={() => toggleValue(option.value)}\n onSelect={(event) => event.preventDefault()}\n >\n <span\n className={cx(\n styles.indicator,\n selected && styles.indicatorSelected,\n option.disabled && styles.indicatorDisabled,\n )}\n aria-hidden=\"true\"\n >\n <DropdownMenuPrimitive.ItemIndicator forceMount>\n <svg\n viewBox=\"0 0 16 16\"\n width=\"16\"\n height=\"16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3.5 8.5 6.5 11.5 12.5 4.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n <span className={styles.optionLabel}>\n {option.icon ? <span className={styles.optionIcon} aria-hidden=\"true\">{option.icon}</span> : null}\n {option.label}\n </span>\n </DropdownMenuPrimitive.CheckboxItem>\n );\n })}\n </DropdownMenuPrimitive.Content>\n </DropdownMenuPrimitive.Portal>\n </div>\n </DropdownMenuPrimitive.Root>\n );\n },\n);\n\nMultiSelect.displayName = \"MultiSelect\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"index28.cjs","names":[],"sources":["../src/components/multi-select/MultiSelect.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\";\nimport { cx } from \"../../utils/cx\";\nimport styles from \"./multi-select.module.css\";\n\nexport type MultiSelectOption = {\n label: string;\n value: string;\n disabled?: boolean;\n /** Optional decorative leading visual. The label remains the accessible option name. */\n icon?: React.ReactElement;\n};\n\nexport type MultiSelectProps = {\n options: MultiSelectOption[];\n value: string[];\n onChange: (value: string[]) => void;\n placeholder?: string;\n fullWidth?: boolean;\n maxVisibleValues?: number;\n /** Minimum popup width, independent of the trigger (e.g. '24rem'). */\n contentMinWidth?: string;\n /** Formats the closed trigger text from the selected options, including an empty selection. */\n formatValue?: (selected: MultiSelectOption[]) => string;\n /** Shows Select all and Clear all actions. Bulk actions modify enabled options only. */\n bulkActions?: boolean;\n /** Stage changes until Apply. Cancel/dismiss discards the draft. */\n draftMode?: boolean;\n /** Minimum number of selected values required to apply; immediate mode blocks changes below this count. */\n minSelected?: number;\n selectAllLabel?: string;\n /** Label for clearing all enabled selections. Disabled selected options remain selected. */\n clearAllLabel?: string;\n className?: string;\n disabled?: boolean;\n name?: string;\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n};\n\nexport const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(\n (\n {\n options,\n value,\n onChange,\n placeholder = \"Select options\",\n fullWidth = true,\n maxVisibleValues = 2,\n contentMinWidth,\n formatValue,\n bulkActions = false,\n draftMode = false,\n minSelected = 0,\n selectAllLabel = \"Select all\",\n clearAllLabel,\n className,\n disabled = false,\n name,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n },\n ref,\n ) => {\n const [open, setOpen] = React.useState(false);\n const [draft, setDraft] = React.useState<string[]>(value);\n const editValue = draftMode && open ? draft : value;\n const updateSelection = (next: string[]) => {\n if (draftMode) setDraft(next);\n else if (next.length >= minSelected) onChange(next);\n };\n const selectedSet = React.useMemo(() => new Set(editValue), [editValue]);\n const selectedOptions = React.useMemo(\n () => options.filter((opt) => value.includes(opt.value)),\n [options, value],\n );\n\n const enabledValues = React.useMemo(\n () => options.filter((option) => !option.disabled).map((option) => option.value),\n [options],\n );\n const allEnabledSelected =\n enabledValues.length > 0 && enabledValues.every((entry) => selectedSet.has(entry));\n const hasDisabledOptions = options.some((option) => option.disabled);\n const resolvedClearAllLabel =\n clearAllLabel ?? (hasDisabledOptions ? \"Clear enabled\" : \"Clear all\");\n\n const formattedValue = React.useMemo(\n () => formatValue?.(selectedOptions),\n [formatValue, selectedOptions],\n );\n\n const toggleValue = React.useCallback(\n (nextValue: string) => {\n if (selectedSet.has(nextValue)) {\n updateSelection(editValue.filter((entry) => entry !== nextValue));\n return;\n }\n\n updateSelection([...editValue, nextValue]);\n },\n [updateSelection, selectedSet, editValue],\n );\n\n const visibleCount = Math.max(1, maxVisibleValues);\n const visibleOptions = selectedOptions.slice(0, visibleCount);\n const overflowCount = Math.max(\n 0,\n selectedOptions.length - visibleOptions.length,\n );\n\n return (\n <DropdownMenuPrimitive.Root modal={false} open={open} onOpenChange={(next) => {\n setDraft([...value]);\n setOpen(next);\n }}>\n <div\n className={cx(\n styles.wrapper,\n fullWidth && styles.wrapperFullWidth,\n className,\n )}\n >\n <DropdownMenuPrimitive.Trigger asChild>\n <button\n ref={ref}\n type=\"button\"\n className={styles.trigger}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n disabled={disabled}\n >\n <span className={styles.value}>\n {formatValue ? (\n <span className={styles.summary}>{formattedValue}</span>\n ) : selectedOptions.length === 0 ? (\n <span className={styles.placeholder}>{placeholder}</span>\n ) : (\n <>\n {visibleOptions.map((option) => (\n <span key={option.value} className={styles.chip}>\n {option.icon ? <span className={styles.optionIcon} aria-hidden=\"true\">{option.icon}</span> : null}\n {option.label}\n </span>\n ))}\n {overflowCount > 0 ? (\n <span className={cx(styles.chip, styles.chipSummary)}>\n +{overflowCount}\n </span>\n ) : null}\n </>\n )}\n </span>\n <span className={styles.chevron} aria-hidden=\"true\">\n <svg\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"10\"\n height=\"10\"\n >\n <path\n d=\"M2 3.5L5 6.5L8 3.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </span>\n </button>\n </DropdownMenuPrimitive.Trigger>\n\n {name\n ? value.map((entry) => (\n <input key={entry} type=\"hidden\" name={name} value={entry} />\n ))\n : null}\n\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n className={styles.menu}\n style={contentMinWidth ? { '--uzi-multiselect-content-min-width': contentMinWidth } as React.CSSProperties : undefined}\n sideOffset={4}\n align=\"start\"\n >\n {bulkActions && enabledValues.length > 0 ? (\n <>\n <DropdownMenuPrimitive.Item\n className={styles.bulkAction}\n disabled={allEnabledSelected}\n onSelect={(event) => {\n event.preventDefault();\n updateSelection([...editValue, ...enabledValues.filter((entry) => !selectedSet.has(entry))]);\n }}\n >\n {selectAllLabel}\n </DropdownMenuPrimitive.Item>\n <DropdownMenuPrimitive.Item\n className={styles.bulkAction}\n disabled={!enabledValues.some((entry) => selectedSet.has(entry))}\n onSelect={(event) => {\n event.preventDefault();\n const enabledSet = new Set(enabledValues);\n updateSelection(editValue.filter((entry) => !enabledSet.has(entry)));\n }}\n >\n {resolvedClearAllLabel}\n </DropdownMenuPrimitive.Item>\n </>\n ) : null}\n {bulkActions && enabledValues.length > 0 ? (\n <DropdownMenuPrimitive.Separator className={styles.separator} />\n ) : null}\n <div className={styles.optionsViewport}>\n {options.map((option) => {\n const selected = selectedSet.has(option.value);\n\n return (\n <DropdownMenuPrimitive.CheckboxItem\n key={option.value}\n className={cx(\n styles.option,\n selected && styles.optionSelected,\n option.disabled && styles.optionDisabled,\n )}\n checked={selected}\n disabled={option.disabled}\n onCheckedChange={() => toggleValue(option.value)}\n onSelect={(event) => event.preventDefault()}\n >\n <span\n className={cx(\n styles.indicator,\n selected && styles.indicatorSelected,\n option.disabled && styles.indicatorDisabled,\n )}\n aria-hidden=\"true\"\n >\n <DropdownMenuPrimitive.ItemIndicator forceMount>\n <svg\n viewBox=\"0 0 16 16\"\n width=\"16\"\n height=\"16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3.5 8.5 6.5 11.5 12.5 4.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n <span className={styles.optionLabel}>\n {option.icon ? <span className={styles.optionIcon} aria-hidden=\"true\">{option.icon}</span> : null}\n {option.label}\n </span>\n </DropdownMenuPrimitive.CheckboxItem>\n );\n })}\n </div>\n {draftMode ? (\n <div className={styles.draftActions}>\n <button type=\"button\" className={styles.draftButton} onClick={() => { setDraft([...value]); setOpen(false); }}>Cancel</button>\n <button type=\"button\" className={styles.draftButton} disabled={editValue.length < minSelected} onClick={() => {\n onChange(editValue);\n setOpen(false);\n }}>Apply</button>\n </div>\n ) : null}\n </DropdownMenuPrimitive.Content>\n </DropdownMenuPrimitive.Portal>\n </div>\n </DropdownMenuPrimitive.Root>\n );\n },\n);\n\nMultiSelect.displayName = \"MultiSelect\";\n"],"mappings":"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"}
package/dist/style.css CHANGED
@@ -1141,9 +1141,13 @@ body {
1141
1141
  top: calc(100% + 6px);
1142
1142
  left: 0;
1143
1143
  z-index: var(--uzi-popover-z-index, 1100);
1144
- min-width: 100%;
1145
- max-height: 16rem;
1146
- overflow-y: auto;
1144
+ min-width: var(--radix-dropdown-menu-trigger-width);
1145
+ width: max(var(--radix-dropdown-menu-trigger-width), var(--uzi-multiselect-content-min-width, 0px));
1146
+ max-width: min(calc(100vw - 1rem), var(--radix-dropdown-menu-content-available-width));
1147
+ max-height: min(28rem, var(--radix-dropdown-menu-content-available-height, calc(100dvh - 1rem)));
1148
+ display: flex;
1149
+ flex-direction: column;
1150
+ overflow: hidden;
1147
1151
  border: 1px solid var(--ts-menu-border, var(--border));
1148
1152
  border-radius: var(--uzi-menu-radius);
1149
1153
  background: var(--ts-menu-bg, var(--popover));
@@ -1275,6 +1279,39 @@ body {
1275
1279
  transform: translateY(0) scale(1);
1276
1280
  }
1277
1281
  }
1282
+
1283
+ .uzi-multi-select-module__draftActions__-R2g3 {
1284
+ display: flex;
1285
+ flex-shrink: 0;
1286
+ gap: 0.5rem;
1287
+ padding: 0.5rem 0.25rem 0.125rem;
1288
+ border-top: 1px solid var(--ts-menu-border, var(--border));
1289
+ }
1290
+ .uzi-multi-select-module__draftButton__tO1zu {
1291
+ flex: 1;
1292
+ border: 1px solid var(--ts-menu-border, var(--border));
1293
+ border-radius: var(--uzi-menu-item-radius);
1294
+ background: transparent;
1295
+ color: inherit;
1296
+ padding: 0.4rem 0.75rem;
1297
+ font: inherit;
1298
+ cursor: pointer;
1299
+ }
1300
+ .uzi-multi-select-module__draftButton__tO1zu:last-child {
1301
+ background: var(--primary);
1302
+ color: var(--primary-foreground);
1303
+ border-color: var(--primary);
1304
+ }
1305
+ .uzi-multi-select-module__draftButton__tO1zu:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-ring-offset); }
1306
+ .uzi-multi-select-module__draftButton__tO1zu:disabled { opacity: 0.5; cursor: not-allowed; }
1307
+
1308
+ .uzi-multi-select-module__optionsViewport__Fus0q {
1309
+ min-height: 0;
1310
+ overflow-y: auto;
1311
+ overscroll-behavior: contain;
1312
+ flex: 1 1 auto;
1313
+ scrollbar-gutter: stable;
1314
+ }
1278
1315
  .uzi-select-module__wrapper__7szq0 {
1279
1316
  position: relative;
1280
1317
  display: inline-flex;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomny-dev/uzi",
3
- "version": "0.8.0",
3
+ "version": "0.9.0-pr.89.299.1.9555816",
4
4
  "description": "Rapid-fire React UI components. Ship faster, build more.",
5
5
  "type": "module",
6
6
  "repository": {