@dynamic-labs-sdk/droplet 0.27.1 → 0.27.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,38 +1,30 @@
1
1
  # `@dynamic-labs-sdk/droplet`
2
2
 
3
- Dynamic Labs' internal React component library. Built on Radix UI primitives, Tailwind CSS v4, and an OKLCH-based design token system. Ships with 35+ components, animated icons, dark mode, and a live Storybook.
3
+ Dynamic Labs' React component library. Built on Radix UI primitives, Tailwind CSS v4, and an OKLCH-based design token system. Ships with 35+ components, animated icons, dark mode, and a live Storybook.
4
4
 
5
5
  ---
6
6
 
7
7
  ## Installation
8
8
 
9
- This package is internal to the `dynamic-js-sdk` monorepo. Add it as a workspace dependency:
10
-
11
- ```json
12
- {
13
- "dependencies": {
14
- "@dynamic-labs-sdk/droplet": "workspace:*"
15
- }
16
- }
9
+ ```bash
10
+ npm install @dynamic-labs-sdk/droplet
11
+ # or
12
+ pnpm add @dynamic-labs-sdk/droplet
13
+ # or
14
+ yarn add @dynamic-labs-sdk/droplet
17
15
  ```
18
16
 
17
+ **Peer dependencies:** `react >= 18`, `react-dom >= 18`, `tailwindcss >= 4`. Next.js (`>= 14`) is optional.
18
+
19
19
  ---
20
20
 
21
21
  ## Setup
22
22
 
23
- Import the design tokens stylesheet **once at your app root**. This provides the full color, shadow, radius, and typography token system and is required for all components to render correctly.
23
+ Droplet ships Tailwind CSS v4 utility classes baked into its compiled output. You must (1) have Tailwind v4 configured in your app, (2) import the design tokens stylesheet, and (3) tell Tailwind to scan droplet's `dist/` so its utility classes are emitted into your stylesheet.
24
24
 
25
- ```ts
26
- import '@dynamic-labs-sdk/droplet/styles/globals.css';
27
- ```
25
+ ### 1. Install and configure Tailwind v4
28
26
 
29
- If you're consuming from a source workspace (not the built `dist/`), use the relative path instead:
30
-
31
- ```ts
32
- import '../../packages/droplet/src/styles/globals.css';
33
- ```
34
-
35
- Your bundler must support Tailwind CSS v4 via the `@tailwindcss/vite` plugin:
27
+ If you don't already have Tailwind v4, follow [the Tailwind v4 install guide](https://tailwindcss.com/docs/installation). The `@tailwindcss/vite` plugin is the simplest path for Vite/Next:
36
28
 
37
29
  ```ts
38
30
  // vite.config.ts
@@ -44,6 +36,26 @@ export default defineConfig({
44
36
  });
45
37
  ```
46
38
 
39
+ ### 2. Import the design tokens stylesheet
40
+
41
+ Import once at your app root. This provides the full color, shadow, radius, and typography token system and is required for all components to render correctly.
42
+
43
+ ```ts
44
+ import '@dynamic-labs-sdk/droplet/styles/globals.css';
45
+ ```
46
+
47
+ ### 3. Add droplet to Tailwind's `@source` paths
48
+
49
+ Tailwind v4 only scans the consuming project by default, so it will miss the utility classes inside droplet's compiled JS. Add an `@source` directive to your app's main stylesheet:
50
+
51
+ ```css
52
+ /* app.css */
53
+ @import 'tailwindcss';
54
+ @source "../node_modules/@dynamic-labs-sdk/droplet/dist";
55
+ ```
56
+
57
+ Adjust the relative path to match where your stylesheet lives. Without this step, components will render unstyled.
58
+
47
59
  ---
48
60
 
49
61
  ## Usage
@@ -194,6 +206,90 @@ import {
194
206
 
195
207
  ---
196
208
 
209
+ ## Theming
210
+
211
+ Droplet's theme is a layer of CSS custom properties. Tailwind utility classes resolve through them (`bg-brand-default` → `var(--brand-default)`), and every component reads from the same vars — so you can fully rebrand by **redefining the vars in your own stylesheet**. No Tailwind config changes, no rebuild, no fork.
212
+
213
+ ### Quick start: change the brand color
214
+
215
+ ```css
216
+ /* your-theme.css — imported AFTER @dynamic-labs-sdk/droplet/styles/globals.css */
217
+
218
+ :root {
219
+ --brand-default: oklch(0.67 0.21 30); /* your light-mode brand */
220
+ --brand-hover: oklch(0.72 0.19 30);
221
+ --brand-accented: oklch(0.58 0.22 30);
222
+ --brand-light: oklch(0.97 0.03 30);
223
+ --ring: oklch(0.67 0.21 30);
224
+ --action: oklch(0.67 0.21 30);
225
+ }
226
+
227
+ .dark {
228
+ --brand-default: oklch(0.6 0.22 30); /* your dark-mode brand */
229
+ --brand-hover: oklch(0.68 0.19 30);
230
+ --brand-accented: oklch(0.52 0.23 30);
231
+ --brand-light: oklch(0.27 0.07 30);
232
+ --ring: oklch(0.6 0.22 30);
233
+ --action: oklch(0.6 0.22 30);
234
+ }
235
+ ```
236
+
237
+ ```ts
238
+ // in your app entry, AFTER the droplet import
239
+ import '@dynamic-labs-sdk/droplet/styles/globals.css';
240
+ import './your-theme.css';
241
+ ```
242
+
243
+ That's it. Buttons, links, focus rings, text-selection highlights, sidebars — all switch to the new brand.
244
+
245
+ ### Cascade order matters
246
+
247
+ Your theme stylesheet **must load after** `@dynamic-labs-sdk/droplet/styles/globals.css`, or the same selectors (`:root`, `.dark`) in droplet's stylesheet will win and your overrides will silently do nothing. If you `@import` in a single CSS file, put droplet's import **first**:
248
+
249
+ ```css
250
+ @import '@dynamic-labs-sdk/droplet/styles/globals.css';
251
+ @import './your-theme.css';
252
+ ```
253
+
254
+ ### What's themeable
255
+
256
+ Anything in the [Design tokens](#design-tokens) table below — colors, shadows, radii. The most common things to override:
257
+
258
+ | What you want to change | Tokens |
259
+ | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
260
+ | Brand color (buttons, focus rings, links, selection) | `--brand-default`, `--brand-hover`, `--brand-accented`, `--brand-light`, `--ring`, `--action` |
261
+ | Surfaces (page bg, cards, sidebar) | `--background`, `--card`, `--bg-bottom`, `--bg-default`, `--sidebar` |
262
+ | Text colors | `--foreground`, `--muted-foreground`, `--text-link` |
263
+ | Radius scale | `--radius-sm` through `--radius-4xl` |
264
+ | Selection highlight | `--selection-bg` |
265
+ | Solid button top-highlight | `--button-highlight` |
266
+ | Checkbox / radio hover fills | `--checkbox-hover-fill`, `--radio-hover-fill` |
267
+
268
+ For semantic colors (success/danger/warning/process), override the matching `--<state>-default`, `--<state>-light`, `--text-<state>` triplet.
269
+
270
+ ### Scoping a theme to part of your app
271
+
272
+ If you only want droplet themed inside a subtree (e.g., a marketing site reusing one component), put the overrides on a wrapper class instead of `:root`:
273
+
274
+ ```css
275
+ .my-theme {
276
+ --brand-default: oklch(0.67 0.21 30);
277
+ /* ... */
278
+ }
279
+ ```
280
+
281
+ ```tsx
282
+ <div className="my-theme">
283
+ <Button>Themed</Button>
284
+ </div>
285
+ ```
286
+
287
+ ### Color space
288
+
289
+ Droplet uses [OKLCH](https://oklch.com/) throughout. If your design system is in hex/rgb, tools like [oklch.com](https://oklch.com) or [oklch.fyi](https://oklch.fyi) will convert. You don't *have* to use OKLCH in your overrides — `oklch()`, `hex`, `rgb()`, and `hsl()` all interoperate fine — but staying in OKLCH gives you perceptually uniform lightness ramps when you tune hover/accented variants.
290
+
291
+ ---
292
+
197
293
  ## Design tokens
198
294
 
199
295
  All tokens are CSS custom properties defined in `globals.css`. Tailwind utility classes map directly to them — `bg-brand-default` resolves to `var(--brand-default)`.
@@ -0,0 +1,2 @@
1
+ export { ToggleCard, type ToggleCardProps, type ToggleCardInputProps, type ToggleCardTag, type ToggleCardActionLink, type ToggleCardCustomActionIcon, } from './toggle-card';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/toggle-card/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,GAChC,MAAM,eAAe,CAAC"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * ToggleCard — droplet's rich toggle composite.
3
+ *
4
+ * Rebuilt from Northstar's SingleToggleCard to support the redcoast dashboard
5
+ * migration. ToggleRow (sibling primitive) remains the minimal/canonical
6
+ * single-line toggle; ToggleCard is the multi-feature card variant with:
7
+ * - Accordion expand/collapse for nested children
8
+ * - Pluggable input type: switch (default), radio, or checkbox
9
+ * - Optional left icon
10
+ * - Tags row above the title and a RightSideTags slot on the right
11
+ * - Optional notification dot in the trigger
12
+ * - Optional `actionLink` rendered below content
13
+ * - Optional `customActionIcon` rendered alongside the trigger affordance
14
+ * - Whole-card click handler
15
+ *
16
+ * The API mirrors Northstar SingleToggleCard so a redcoast migration can be a
17
+ * mechanical rename. Where the Northstar API surfaced layout-tuning props
18
+ * (`disableHover`, `dontSeparateContentFromHeader`, `descriptionModificators`,
19
+ * `titleModificators`) the equivalent expression here is via Tailwind classes
20
+ * on `className` or `triggerClassName`.
21
+ *
22
+ * Authored in droplet so a single canonical card is available across teams.
23
+ * Backed by Card + Switch/Checkbox/RadioGroup + Radix Accordion.
24
+ */
25
+ import { type ChangeEvent, type FC, type ReactElement, type ReactNode } from 'react';
26
+ type ToggleCardInputType = 'toggle' | 'radio' | 'checkbox';
27
+ export type ToggleCardInputProps = {
28
+ checked?: boolean;
29
+ disabled?: boolean;
30
+ /** Hide the input control entirely (useful when the row is informational). */
31
+ hidden?: boolean;
32
+ id?: string;
33
+ /** When true, renders a lock badge instead of a switch — signals the value
34
+ * is fixed and cannot be toggled via this UI. Implies disabled. */
35
+ isLocked?: boolean;
36
+ /** Indicates the saved/unsaved state via styling — informational, optional. */
37
+ isSaved?: boolean;
38
+ /** When true, paints the input with the warning accent (informational). */
39
+ isWarning?: boolean;
40
+ name?: string;
41
+ /** Native onChange — receives a synthetic event for parity with HTML inputs. */
42
+ onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
43
+ /** Controls which control element renders next to the title. */
44
+ type?: ToggleCardInputType;
45
+ };
46
+ export type ToggleCardTag = {
47
+ Tag: ReactElement;
48
+ id: string;
49
+ };
50
+ export type ToggleCardActionLink = {
51
+ onClick: () => void;
52
+ text: string;
53
+ };
54
+ export type ToggleCardCustomActionIcon = {
55
+ Icon: ReactElement;
56
+ className?: string;
57
+ };
58
+ export type ToggleCardProps = {
59
+ /** Optional icon rendered before the title. */
60
+ Icon?: ReactElement;
61
+ /** Tags shown on the RIGHT side of the trigger (e.g. status badges). */
62
+ RightSideTags?: ToggleCardTag[];
63
+ /** Tags shown ABOVE the title (e.g. status pills). */
64
+ Tags?: ToggleCardTag[];
65
+ /** Required key for Radix Accordion item identity. */
66
+ accordionKey: string;
67
+ /** Action button rendered at the bottom of expanded content. */
68
+ actionLink?: ToggleCardActionLink;
69
+ /** Whether the accordion CAN expand. Defaults to true. */
70
+ allowExpand?: boolean;
71
+ /** Children — rendered inside the AccordionContent when expanded. */
72
+ children?: ReactNode;
73
+ /** className applied to the outer Accordion root. */
74
+ className?: string;
75
+ /** Whether the accordion can be collapsed (Radix `collapsible`). */
76
+ collapsible?: boolean;
77
+ /** Replaces the default chevron with a custom icon. */
78
+ customActionIcon?: ToggleCardCustomActionIcon;
79
+ /** Whether the accordion is expanded by default. */
80
+ defaultExpanded?: boolean;
81
+ /** Optional secondary line below the title. */
82
+ description?: string;
83
+ /** Disables the entire card (input + trigger). */
84
+ disabled?: boolean;
85
+ /** Inputs (toggle/radio/checkbox) rendered alongside the title. */
86
+ inputProps?: ToggleCardInputProps;
87
+ /** When true, draws a notification dot on the trigger. */
88
+ notification?: boolean;
89
+ /**
90
+ * Click handler for the whole card surface.
91
+ *
92
+ * @deprecated Currently a no-op: attaching onClick to a wrapper that nests
93
+ * interactive children (AccordionTrigger, Switch / Checkbox / RadioGroup)
94
+ * is an a11y anti-pattern. Kept in the type signature for Northstar
95
+ * mechanical-rename parity. A future iteration may re-introduce this with
96
+ * a dedicated click target (e.g. an overlay button) outside the nested
97
+ * interactives.
98
+ */
99
+ onCardClick?: () => void;
100
+ /** Accordion value-change handler. */
101
+ onValueChange?: (value: string | undefined) => void;
102
+ /** Card title — rendered as the primary label inside the trigger. */
103
+ title?: string;
104
+ /** className applied to the trigger row. */
105
+ triggerClassName?: string;
106
+ /** Test id applied to the accordion trigger element. */
107
+ triggerTestId?: string;
108
+ /** Controlled accordion value. */
109
+ value?: string;
110
+ };
111
+ export declare const ToggleCard: FC<ToggleCardProps>;
112
+ export {};
113
+ //# sourceMappingURL=toggle-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggle-card.d.ts","sourceRoot":"","sources":["../../../src/components/toggle-card/toggle-card.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,EAAE,EACP,KAAK,YAAY,EACjB,KAAK,SAAS,EAEf,MAAM,OAAO,CAAC;AAcf,KAAK,mBAAmB,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AAE3D,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;uEACmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAC1D,gEAAgE;IAChE,IAAI,CAAC,EAAE,mBAAmB,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,YAAY,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,+CAA+C;IAC/C,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,wEAAwE;IACxE,aAAa,CAAC,EAAE,aAAa,EAAE,CAAC;IAChC,sDAAsD;IACtD,IAAI,CAAC,EAAE,aAAa,EAAE,CAAC;IACvB,sDAAsD;IACtD,YAAY,EAAE,MAAM,CAAC;IACrB,gEAAgE;IAChE,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,0DAA0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,0BAA0B,CAAC;IAC9C,oDAAoD;IACpD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mEAAmE;IACnE,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,0DAA0D;IAC1D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,sCAAsC;IACtC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACpD,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAuFF,eAAO,MAAM,UAAU,EAAE,EAAE,CAAC,eAAe,CA6L1C,CAAC"}
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ import type { StoryObj } from '@storybook/react';
3
+ declare const meta: {
4
+ args: {
5
+ accordionKey: string;
6
+ description: string;
7
+ inputProps: {
8
+ checked: true;
9
+ type: "toggle";
10
+ };
11
+ title: string;
12
+ };
13
+ argTypes: {
14
+ allowExpand: {
15
+ control: "boolean";
16
+ };
17
+ disabled: {
18
+ control: "boolean";
19
+ };
20
+ notification: {
21
+ control: "boolean";
22
+ };
23
+ };
24
+ component: React.FC<import("./toggle-card").ToggleCardProps>;
25
+ parameters: {
26
+ layout: string;
27
+ };
28
+ tags: string[];
29
+ title: string;
30
+ };
31
+ export default meta;
32
+ type Story = StoryObj<typeof meta>;
33
+ export declare const Default: Story;
34
+ export declare const WithDescription: Story;
35
+ export declare const WithTags: Story;
36
+ export declare const WithRightSideTags: Story;
37
+ export declare const Expandable: Story;
38
+ export declare const WithActionLink: Story;
39
+ export declare const IsLocked: Story;
40
+ export declare const Notification: Story;
41
+ export declare const RadioInput: Story;
42
+ export declare const CheckboxInput: Story;
43
+ export declare const Disabled: Story;
44
+ export declare const WholeCardClickable: Story;
45
+ //# sourceMappingURL=toggle-card.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggle-card.stories.d.ts","sourceRoot":"","sources":["../../../src/components/toggle-card/toggle-card.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAKvD,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgByB,CAAC;AAEpC,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAiBrB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAkB7B,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAuBtB,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,KAqB/B,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,KAwBxB,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KA6B5B,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAetB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAuB1B,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,KAmBxB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAmB3B,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAmBtB,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,KAqBhC,CAAC"}
package/dist/index.cjs CHANGED
@@ -2192,6 +2192,189 @@ const StatusBadge = ({ status, label, className }) => {
2192
2192
  });
2193
2193
  };
2194
2194
 
2195
+ //#endregion
2196
+ //#region src/components/toggle-card/toggle-card.tsx
2197
+ /**
2198
+ * ToggleCard — droplet's rich toggle composite.
2199
+ *
2200
+ * Rebuilt from Northstar's SingleToggleCard to support the redcoast dashboard
2201
+ * migration. ToggleRow (sibling primitive) remains the minimal/canonical
2202
+ * single-line toggle; ToggleCard is the multi-feature card variant with:
2203
+ * - Accordion expand/collapse for nested children
2204
+ * - Pluggable input type: switch (default), radio, or checkbox
2205
+ * - Optional left icon
2206
+ * - Tags row above the title and a RightSideTags slot on the right
2207
+ * - Optional notification dot in the trigger
2208
+ * - Optional `actionLink` rendered below content
2209
+ * - Optional `customActionIcon` rendered alongside the trigger affordance
2210
+ * - Whole-card click handler
2211
+ *
2212
+ * The API mirrors Northstar SingleToggleCard so a redcoast migration can be a
2213
+ * mechanical rename. Where the Northstar API surfaced layout-tuning props
2214
+ * (`disableHover`, `dontSeparateContentFromHeader`, `descriptionModificators`,
2215
+ * `titleModificators`) the equivalent expression here is via Tailwind classes
2216
+ * on `className` or `triggerClassName`.
2217
+ *
2218
+ * Authored in droplet so a single canonical card is available across teams.
2219
+ * Backed by Card + Switch/Checkbox/RadioGroup + Radix Accordion.
2220
+ */
2221
+ const inputCommon = "shrink-0 touch-manipulation cursor-pointer disabled:cursor-not-allowed disabled:opacity-50";
2222
+ const renderInput = (input, id) => {
2223
+ if (!input || input.hidden) return null;
2224
+ if (input.isLocked) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2225
+ "aria-label": "Locked",
2226
+ title: "Locked",
2227
+ className: "shrink-0 inline-flex items-center justify-center size-5 rounded-full bg-muted text-muted-foreground mt-0.5",
2228
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
2229
+ className: "size-3",
2230
+ viewBox: "0 0 24 24",
2231
+ fill: "none",
2232
+ stroke: "currentColor",
2233
+ strokeWidth: "2",
2234
+ strokeLinecap: "round",
2235
+ strokeLinejoin: "round",
2236
+ "aria-hidden": true,
2237
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
2238
+ x: "3",
2239
+ y: "11",
2240
+ width: "18",
2241
+ height: "11",
2242
+ rx: "2",
2243
+ ry: "2"
2244
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M7 11V7a5 5 0 0110 0v4" })]
2245
+ })
2246
+ });
2247
+ const type = input.type ?? "toggle";
2248
+ const handleChange = (next) => {
2249
+ if (!input.onChange) return;
2250
+ input.onChange({ target: {
2251
+ checked: next,
2252
+ name: input.name ?? ""
2253
+ } });
2254
+ };
2255
+ if (type === "toggle") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_toggle_row.Switch, {
2256
+ id: input.id ?? id,
2257
+ checked: Boolean(input.checked),
2258
+ disabled: input.disabled,
2259
+ onCheckedChange: handleChange,
2260
+ className: require_toggle_row.cn(inputCommon, "mt-0.5")
2261
+ });
2262
+ if (type === "checkbox") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Checkbox, {
2263
+ id: input.id ?? id,
2264
+ checked: Boolean(input.checked),
2265
+ disabled: input.disabled,
2266
+ onCheckedChange: (state) => handleChange(state === true),
2267
+ className: require_toggle_row.cn(inputCommon, "mt-1")
2268
+ });
2269
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RadioGroup, {
2270
+ value: input.checked ? input.id ?? id : "",
2271
+ onValueChange: (v) => handleChange(v === (input.id ?? id)),
2272
+ disabled: input.disabled,
2273
+ className: "shrink-0 mt-0.5",
2274
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RadioGroupItem, {
2275
+ id: input.id ?? id,
2276
+ value: input.id ?? id,
2277
+ className: inputCommon
2278
+ })
2279
+ });
2280
+ };
2281
+ const ToggleCard = ({ accordionKey, title, description, inputProps, allowExpand = true, defaultExpanded, value, onValueChange, collapsible = true, Tags, RightSideTags, notification, actionLink, customActionIcon, onCardClick: _onCardClick, children, disabled, triggerTestId, className, triggerClassName }) => {
2282
+ const generatedId = (0, react.useId)();
2283
+ const inputId = inputProps?.id ?? generatedId;
2284
+ const isParentMode = !allowExpand && Boolean(children);
2285
+ const renderContent = () => {
2286
+ const actionLinkButton = actionLink ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2287
+ type: "button",
2288
+ onClick: actionLink.onClick,
2289
+ className: "mt-3 text-sm font-medium text-(--action) hover:underline underline-offset-4 cursor-pointer",
2290
+ children: actionLink.text
2291
+ }) : null;
2292
+ if (allowExpand) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_radix_ui_react_accordion.Content, {
2293
+ className: "border-t border-border-divider px-5 py-4",
2294
+ children: [children, actionLinkButton]
2295
+ });
2296
+ if (!children && !actionLink) return null;
2297
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2298
+ className: "border-t border-border-divider bg-card px-5 py-3 space-y-2",
2299
+ children: [children, actionLinkButton]
2300
+ });
2301
+ };
2302
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_accordion.Root, {
2303
+ type: "single",
2304
+ collapsible,
2305
+ defaultValue: defaultExpanded ? accordionKey : void 0,
2306
+ value,
2307
+ onValueChange,
2308
+ "data-slot": "toggle-card",
2309
+ className: require_toggle_row.cn("rounded-xl bg-card shadow-card overflow-hidden", disabled && "opacity-60", className),
2310
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_radix_ui_react_accordion.Item, {
2311
+ value: accordionKey,
2312
+ className: "border-0",
2313
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_accordion.Header, {
2314
+ className: "m-0",
2315
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2316
+ className: require_toggle_row.cn("group flex items-start gap-3 px-5 py-4", isParentMode && "bg-bg-bottom", triggerClassName),
2317
+ children: [
2318
+ renderInput(inputProps, inputId),
2319
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2320
+ className: "flex-1 min-w-0",
2321
+ children: [
2322
+ title && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2323
+ className: "flex items-baseline gap-1.5 flex-wrap leading-5",
2324
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2325
+ htmlFor: inputId,
2326
+ className: require_toggle_row.cn("text-sm font-medium select-none leading-5", disabled && "text-muted-foreground"),
2327
+ children: [title, notification && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2328
+ "aria-hidden": true,
2329
+ className: "inline-block ml-1.5 size-1.5 rounded-full bg-destructive align-middle"
2330
+ })]
2331
+ }), Tags && Tags.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: Tags.map((t) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2332
+ className: "leading-5",
2333
+ children: t.Tag
2334
+ }, t.id)) })]
2335
+ }),
2336
+ !title && Tags && Tags.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2337
+ className: "flex items-baseline gap-1.5 mb-1 leading-5",
2338
+ children: Tags.map((t) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2339
+ className: "leading-5",
2340
+ children: t.Tag
2341
+ }, t.id))
2342
+ }),
2343
+ description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2344
+ className: "text-sm text-muted-foreground mt-0.5 text-pretty",
2345
+ children: description
2346
+ })
2347
+ ]
2348
+ }),
2349
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2350
+ className: "flex items-center gap-2 shrink-0 self-start",
2351
+ children: [RightSideTags && RightSideTags.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2352
+ className: "flex items-center gap-1.5",
2353
+ children: RightSideTags.map((t) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t.Tag }, t.id))
2354
+ }), allowExpand && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_accordion.Trigger, {
2355
+ "data-testid": triggerTestId,
2356
+ className: require_toggle_row.cn("shrink-0 inline-flex size-7 items-center justify-center rounded-md text-muted-foreground transition-[transform,background-color] duration-150 hover:bg-overlay-hover [&[data-state=open]>svg]:rotate-180", customActionIcon?.className),
2357
+ disabled: !allowExpand,
2358
+ children: customActionIcon ? customActionIcon.Icon : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
2359
+ className: "size-4",
2360
+ viewBox: "0 0 24 24",
2361
+ fill: "none",
2362
+ stroke: "currentColor",
2363
+ strokeWidth: "2",
2364
+ strokeLinecap: "round",
2365
+ strokeLinejoin: "round",
2366
+ "aria-hidden": true,
2367
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polyline", { points: "6 9 12 15 18 9" })
2368
+ })
2369
+ })]
2370
+ })
2371
+ ]
2372
+ })
2373
+ }), renderContent()]
2374
+ })
2375
+ });
2376
+ };
2377
+
2195
2378
  //#endregion
2196
2379
  //#region src/components/icons/arrow-up-right/arrow-up-right.tsx
2197
2380
  const ArrowUpRightIcon = (0, react.forwardRef)((props, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnimatedIcon, {
@@ -3858,6 +4041,7 @@ exports.TabsTrigger = TabsTrigger;
3858
4041
  exports.TerminalIcon = TerminalIcon;
3859
4042
  exports.Textarea = Textarea;
3860
4043
  exports.Toaster = Toaster;
4044
+ exports.ToggleCard = ToggleCard;
3861
4045
  exports.ToggleRow = require_toggle_row.ToggleRow;
3862
4046
  exports.Tooltip = Tooltip;
3863
4047
  exports.TooltipContent = TooltipContent;