@mks2508/mks-ui 0.5.1 → 0.5.4

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.
Files changed (72) hide show
  1. package/dist/react-ui/index.js +8 -1
  2. package/dist/react-ui/primitives/index.js +5 -0
  3. package/dist/react-ui/primitives/waapi/Gooey/Gooey.types.d.ts +103 -0
  4. package/dist/react-ui/primitives/waapi/Gooey/Gooey.types.d.ts.map +1 -0
  5. package/dist/react-ui/primitives/waapi/Gooey/GooeyCanvas.d.ts +10 -0
  6. package/dist/react-ui/primitives/waapi/Gooey/GooeyCanvas.d.ts.map +1 -0
  7. package/dist/react-ui/primitives/waapi/Gooey/GooeyCanvas.js +59 -0
  8. package/dist/react-ui/primitives/waapi/Gooey/GooeyFilter.d.ts +7 -0
  9. package/dist/react-ui/primitives/waapi/Gooey/GooeyFilter.d.ts.map +1 -0
  10. package/dist/react-ui/primitives/waapi/Gooey/GooeyFilter.js +78 -0
  11. package/dist/react-ui/primitives/waapi/Gooey/MorphPath.d.ts +7 -0
  12. package/dist/react-ui/primitives/waapi/Gooey/MorphPath.d.ts.map +1 -0
  13. package/dist/react-ui/primitives/waapi/Gooey/MorphPath.js +51 -0
  14. package/dist/react-ui/primitives/waapi/Gooey/gooey-utils.d.ts +87 -0
  15. package/dist/react-ui/primitives/waapi/Gooey/gooey-utils.d.ts.map +1 -0
  16. package/dist/react-ui/primitives/waapi/Gooey/gooey-utils.js +177 -0
  17. package/dist/react-ui/primitives/waapi/Gooey/index.d.ts +28 -0
  18. package/dist/react-ui/primitives/waapi/Gooey/index.d.ts.map +1 -0
  19. package/dist/react-ui/primitives/waapi/Gooey/index.js +5 -0
  20. package/dist/react-ui/primitives/waapi/Gooey/useMorphPath.d.ts +7 -0
  21. package/dist/react-ui/primitives/waapi/Gooey/useMorphPath.d.ts.map +1 -0
  22. package/dist/react-ui/primitives/waapi/Gooey/useMorphPath.js +47 -0
  23. package/dist/react-ui/primitives/waapi/index.d.ts +2 -0
  24. package/dist/react-ui/primitives/waapi/index.d.ts.map +1 -1
  25. package/dist/react-ui/primitives/waapi/index.js +6 -0
  26. package/dist/react-ui/ui/DataCard/DataCard.styles.d.ts +26 -16
  27. package/dist/react-ui/ui/DataCard/DataCard.styles.d.ts.map +1 -1
  28. package/dist/react-ui/ui/DataCard/DataCard.styles.js +36 -74
  29. package/dist/react-ui/ui/DataCard/DataCard.types.d.ts +50 -70
  30. package/dist/react-ui/ui/DataCard/DataCard.types.d.ts.map +1 -1
  31. package/dist/react-ui/ui/DataCard/index.d.ts +24 -93
  32. package/dist/react-ui/ui/DataCard/index.d.ts.map +1 -1
  33. package/dist/react-ui/ui/DataCard/index.js +76 -118
  34. package/dist/react-ui/ui/DynamicToggle/DynamicToggle-Cm6-VceQ.css +304 -0
  35. package/dist/react-ui/ui/DynamicToggle/DynamicToggle.css +303 -0
  36. package/dist/react-ui/ui/DynamicToggle/DynamicToggle.js +0 -0
  37. package/dist/react-ui/ui/DynamicToggle/DynamicToggle.styles.d.ts +35 -0
  38. package/dist/react-ui/ui/DynamicToggle/DynamicToggle.styles.d.ts.map +1 -0
  39. package/dist/react-ui/ui/DynamicToggle/DynamicToggle.styles.js +67 -0
  40. package/dist/react-ui/ui/DynamicToggle/DynamicToggle.types.d.ts +138 -0
  41. package/dist/react-ui/ui/DynamicToggle/DynamicToggle.types.d.ts.map +1 -0
  42. package/dist/react-ui/ui/DynamicToggle/index.d.ts +31 -0
  43. package/dist/react-ui/ui/DynamicToggle/index.d.ts.map +1 -0
  44. package/dist/react-ui/ui/DynamicToggle/index.js +188 -0
  45. package/dist/react-ui/ui/Switch/index.js +1 -1
  46. package/dist/react-ui/ui/index.d.ts +1 -0
  47. package/dist/react-ui/ui/index.d.ts.map +1 -1
  48. package/dist/react-ui/ui/index.js +2 -0
  49. package/package.json +2 -2
  50. package/src/css.d.ts +1 -0
  51. package/src/react-ui/primitives/waapi/Gooey/Gooey.types.ts +123 -0
  52. package/src/react-ui/primitives/waapi/Gooey/GooeyCanvas.tsx +80 -0
  53. package/src/react-ui/primitives/waapi/Gooey/GooeyFilter.tsx +77 -0
  54. package/src/react-ui/primitives/waapi/Gooey/MorphPath.tsx +58 -0
  55. package/src/react-ui/primitives/waapi/Gooey/gooey-utils.ts +244 -0
  56. package/src/react-ui/primitives/waapi/Gooey/index.ts +50 -0
  57. package/src/react-ui/primitives/waapi/Gooey/useMorphPath.ts +48 -0
  58. package/src/react-ui/primitives/waapi/index.ts +23 -0
  59. package/src/react-ui/ui/DataCard/DataCard.styles.ts +45 -101
  60. package/src/react-ui/ui/DataCard/DataCard.types.ts +52 -73
  61. package/src/react-ui/ui/DataCard/index.tsx +118 -184
  62. package/src/react-ui/ui/DynamicToggle/DynamicToggle.css +303 -0
  63. package/src/react-ui/ui/DynamicToggle/DynamicToggle.styles.ts +85 -0
  64. package/src/react-ui/ui/DynamicToggle/DynamicToggle.types.ts +174 -0
  65. package/src/react-ui/ui/DynamicToggle/index.tsx +294 -0
  66. package/src/react-ui/ui/DynamicToggle/prototype-v7.html +615 -0
  67. package/src/react-ui/ui/DynamicToggle/prototype.html +419 -0
  68. package/src/react-ui/ui/Switch/index.tsx +1 -1
  69. package/src/react-ui/ui/index.ts +3 -0
  70. /package/dist/react-ui/blocks/Terminal/panel/{terminal-filter-dropdown.module-CNVWCefU.css → terminal-filter-dropdown.module-C6oDcFBS.css} +0 -0
  71. /package/dist/react-ui/blocks/Terminal/panel/{terminal-session-tabs.module-cmyJ11jP.css → terminal-session-tabs.module-D_-sgyza.css} +0 -0
  72. /package/dist/react-ui/components/MorphingPopover/{morphing-popover.module-BycNI8nU.css → morphing-popover.module-B1ftlaYj.css} +0 -0
@@ -0,0 +1,303 @@
1
+ /**
2
+ * DynamicToggle — CSS state transitions.
3
+ *
4
+ * Rules requiring :has(), container queries, clip-path, or pseudo-elements.
5
+ * Layout, colors, sizing in Tailwind (DynamicToggle.styles.ts).
6
+ *
7
+ * @import '@mks2508/mks-ui/dist/react-ui/ui/DynamicToggle/DynamicToggle.css';
8
+ */
9
+
10
+ /* ── Variables ── */
11
+ [data-slot="dt-root"] {
12
+ --dt-dur: 0.22s;
13
+ --dt-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
14
+ --dt-fade: 0.45;
15
+ }
16
+
17
+ /* ── Track: explicit row prevents h-full items from overflowing container ── */
18
+ [data-slot="dt-root"] [data-slot="dt-track"] {
19
+ grid-template-rows: minmax(0, 1fr);
20
+ }
21
+
22
+ /* ── Top-level option spans 2 grid cols ── */
23
+ [data-slot="dt-root"] [data-slot="dt-track"] > label {
24
+ grid-column: span 2;
25
+ }
26
+
27
+ /* ── Main indicator slide ── */
28
+ [data-slot="dt-root"] [data-slot="dt-indicator"] {
29
+ transition: translate var(--dt-dur) var(--dt-ease);
30
+ translate: 100% 0;
31
+ }
32
+ [data-slot="dt-root"] [data-slot="dt-track"]:has(> input:checked) [data-slot="dt-indicator"] {
33
+ translate: 0 0;
34
+ }
35
+
36
+ /* ── Primary option text ── */
37
+ [data-slot="dt-root"] [data-slot="dt-track"]:has(> input:checked) > label {
38
+ color: var(--card);
39
+ z-index: 2;
40
+ }
41
+ [data-slot="dt-root"] [data-slot="dt-track"]:not(:has(> input:checked)) > label {
42
+ color: var(--foreground);
43
+ opacity: var(--dt-fade);
44
+ }
45
+
46
+ /* ── Group: container queries ── */
47
+ [data-slot="dt-root"] [data-slot="dt-group"] {
48
+ container-type: size;
49
+ overflow: hidden;
50
+ }
51
+
52
+ /* ── Group indicator: clip-path reveal ── */
53
+ [data-slot="dt-root"] [data-slot="dt-group-indicator"] {
54
+ pointer-events: none;
55
+ transition:
56
+ translate var(--dt-dur) var(--dt-ease),
57
+ clip-path var(--dt-dur) var(--dt-ease),
58
+ background var(--dt-dur) var(--dt-ease);
59
+ clip-path: inset(
60
+ 73cqh calc(50% + 1px) calc(27cqh - 2px) calc(50% - 3px)
61
+ round var(--dt-radius, 9999px)
62
+ );
63
+ translate: -50% 0;
64
+ }
65
+ [data-slot="dt-root"] [data-slot="dt-track"]:has(> input:checked) [data-slot="dt-group-indicator"] {
66
+ background: transparent;
67
+ }
68
+ [data-slot="dt-root"] [data-slot="dt-group"]:has(input:checked) [data-slot="dt-group-indicator"] {
69
+ background: var(--card);
70
+ clip-path: inset(0 0 0 0 round var(--dt-radius, 9999px));
71
+ }
72
+ [data-slot="dt-root"] [data-slot="dt-group"]:has(input:nth-of-type(1):checked) [data-slot="dt-group-indicator"] {
73
+ translate: -100% 0;
74
+ }
75
+ [data-slot="dt-root"] [data-slot="dt-group"]:has(input:nth-of-type(2):checked) [data-slot="dt-group-indicator"] {
76
+ translate: 0 0;
77
+ }
78
+
79
+ /* ══════════════════════════════════════════════════════════
80
+ * GROUP COLLAPSED STATE
81
+ *
82
+ * ::before = title text (via data-label attr)
83
+ * ::after = combined opts text (via data-opts attr)
84
+ * <label>s = controlled by data-collapsed mode
85
+ *
86
+ * 3 modes: title | opts | title-opts (default)
87
+ * ══════════════════════════════════════════════════════════ */
88
+
89
+ /* ── ::before — group title ── */
90
+ [data-slot="dt-group"]::before {
91
+ content: attr(data-label);
92
+ position: absolute;
93
+ left: 50%;
94
+ top: 50%;
95
+ translate: -50% -80%;
96
+ color: var(--foreground);
97
+ font-size: inherit;
98
+ font-weight: 500;
99
+ z-index: 2;
100
+ white-space: nowrap;
101
+ pointer-events: none;
102
+ transition:
103
+ scale var(--dt-dur) var(--dt-ease),
104
+ translate var(--dt-dur) var(--dt-ease),
105
+ opacity var(--dt-dur) var(--dt-ease);
106
+ }
107
+
108
+ /* ── ::after — combined opts text ── */
109
+ [data-slot="dt-group"]::after {
110
+ content: attr(data-opts);
111
+ position: absolute;
112
+ left: 50%;
113
+ top: 50%;
114
+ translate: -50% 20%;
115
+ color: var(--muted-foreground);
116
+ font-size: 0.85em;
117
+ opacity: 0.6;
118
+ z-index: 2;
119
+ white-space: nowrap;
120
+ pointer-events: none;
121
+ transition: opacity var(--dt-dur) var(--dt-ease);
122
+ }
123
+ [data-slot="dt-group"]:not([data-opts])::after {
124
+ content: none;
125
+ }
126
+
127
+ /* ── Group labels — transition props ── */
128
+ [data-slot="dt-root"] [data-slot="dt-group"] label {
129
+ color: var(--muted-foreground);
130
+ cursor: pointer;
131
+ z-index: 2;
132
+ transition:
133
+ color var(--dt-dur) var(--dt-ease),
134
+ opacity var(--dt-dur) var(--dt-ease),
135
+ translate var(--dt-dur) var(--dt-ease);
136
+ }
137
+ [data-slot="dt-root"] [data-slot="dt-group"] label span {
138
+ display: grid;
139
+ place-items: center;
140
+ height: 100%;
141
+ width: 100%;
142
+ border-radius: var(--dt-radius, 9999px);
143
+ transition: scale var(--dt-dur) var(--dt-ease);
144
+ }
145
+
146
+ /* ── Collapsed mode: "title" — only ::before, labels slide+scale out ── */
147
+ [data-slot="dt-group"][data-collapsed="title"]::before {
148
+ translate: -50% -50%;
149
+ }
150
+ [data-slot="dt-group"][data-collapsed="title"]::after {
151
+ display: none;
152
+ }
153
+ [data-slot="dt-group"][data-collapsed="title"]:not(:has(input:checked)) label {
154
+ opacity: 0;
155
+ translate: 0 30%;
156
+ }
157
+ [data-slot="dt-group"][data-collapsed="title"]:not(:has(input:checked)) label span {
158
+ scale: 0.5;
159
+ }
160
+
161
+ /* ── Collapsed mode: "opts" — only ::after, labels slide+scale out ── */
162
+ [data-slot="dt-group"][data-collapsed="opts"]::before {
163
+ display: none;
164
+ }
165
+ [data-slot="dt-group"][data-collapsed="opts"]::after {
166
+ translate: -50% -50%;
167
+ font-size: inherit;
168
+ opacity: 0.7;
169
+ }
170
+ [data-slot="dt-group"][data-collapsed="opts"]:not(:has(input:checked)) label {
171
+ opacity: 0;
172
+ translate: 0 30%;
173
+ }
174
+ [data-slot="dt-group"][data-collapsed="opts"]:not(:has(input:checked)) label span {
175
+ scale: 0.5;
176
+ }
177
+
178
+ /* ── Collapsed mode: "title-opts" — WIP: disabled, falls back to "title" behavior ── */
179
+ /* TODO: title-opts needs a redesign — title (::before) and scaled labels overlap
180
+ at all container sizes. The codepen original morph relied on specific dimensions
181
+ that don't translate to the component's size variants. Needs a different approach
182
+ (e.g., crossfade, flex layout, or JS-measured positions). */
183
+ [data-slot="dt-group"][data-collapsed="title-opts"]::after {
184
+ content: none;
185
+ }
186
+ [data-slot="dt-group"][data-collapsed="title-opts"]::before {
187
+ translate: -50% -50%;
188
+ }
189
+ [data-slot="dt-group"][data-collapsed="title-opts"]:not(:has(input:checked)) label {
190
+ opacity: 0;
191
+ translate: 0 30%;
192
+ }
193
+
194
+ /* ── When group expanded ── */
195
+ [data-slot="dt-group"]:has(input:checked)::before {
196
+ translate: -50% -250%;
197
+ scale: 0.85;
198
+ }
199
+ [data-slot="dt-group"]:has(input:checked)::after {
200
+ opacity: 0;
201
+ }
202
+ [data-slot="dt-group"]:has(input:checked) label {
203
+ opacity: 0.75;
204
+ color: var(--muted-foreground);
205
+ translate: 0 0;
206
+ }
207
+ [data-slot="dt-group"]:has(input:checked) label span {
208
+ scale: 1;
209
+ }
210
+ [data-slot="dt-group"]:has(input:nth-of-type(1):checked) label:nth-of-type(1),
211
+ [data-slot="dt-group"]:has(input:nth-of-type(2):checked) label:nth-of-type(2) {
212
+ color: var(--foreground);
213
+ opacity: 1;
214
+ }
215
+
216
+ /* ══════════════════════════════════════════════════════════
217
+ * GROUP LABEL (above/below the pill)
218
+ *
219
+ * Replaces the old "bubble" element. Positioned via CSS grid.
220
+ * In filter/path morph modes, rendered inside GooeyCanvas.
221
+ * In none mode, simple CSS-driven show/hide.
222
+ * ══════════════════════════════════════════════════════════ */
223
+
224
+ [data-slot="dt-group-label"] {
225
+ display: grid;
226
+ grid-template-rows: 0fr;
227
+ left: 20%;
228
+ right: 20%;
229
+ transition:
230
+ grid-template-rows calc(var(--dt-dur) * 1.5) var(--dt-ease),
231
+ opacity var(--dt-dur) var(--dt-ease);
232
+ opacity: 0;
233
+ background: var(--card);
234
+ border: 1px solid var(--border);
235
+ z-index: 3;
236
+ }
237
+ [data-slot="dt-group-label"] > span {
238
+ overflow: hidden;
239
+ min-height: 0;
240
+ display: flex;
241
+ align-items: center;
242
+ justify-content: center;
243
+ padding: 0 0.75em;
244
+ height: calc(var(--dt-h, 38px) * 0.4);
245
+ box-sizing: border-box;
246
+ }
247
+
248
+ /* Top position */
249
+ [data-slot="dt-group-label"][data-position="top"] {
250
+ bottom: 100%;
251
+ border-radius: calc(var(--dt-h, 38px) * 0.2) calc(var(--dt-h, 38px) * 0.2) 0 0;
252
+ border-bottom: none;
253
+ margin-bottom: -1px;
254
+ }
255
+
256
+ /* Bottom position */
257
+ [data-slot="dt-group-label"][data-position="bottom"] {
258
+ top: 100%;
259
+ border-radius: 0 0 calc(var(--dt-h, 38px) * 0.2) calc(var(--dt-h, 38px) * 0.2);
260
+ border-top: none;
261
+ margin-top: -1px;
262
+ }
263
+
264
+ /* When group active → group label grows */
265
+ [data-slot="dt-root"]:not(:has([data-slot="dt-track"] > input:checked)) [data-slot="dt-group-label"] {
266
+ grid-template-rows: 1fr;
267
+ opacity: 1;
268
+ }
269
+ [data-slot="dt-root"]:not(:has([data-slot="dt-track"] > input:checked)) [data-slot="dt-group-label"] > span {
270
+ padding: 0.35em 0.75em;
271
+ }
272
+
273
+ /* ── Filter morph mode ── */
274
+ [data-slot="dt-root"][data-morph="filter"] {
275
+ background: transparent;
276
+ border-color: transparent;
277
+ box-shadow: none;
278
+ overflow: visible;
279
+ }
280
+ [data-slot="dt-root"][data-morph="filter"] [data-slot="dt-group-label"] {
281
+ border: none;
282
+ }
283
+ [data-slot="dt-root"][data-morph="filter"] [data-slot="dt-track"] {
284
+ position: relative;
285
+ z-index: 1;
286
+ }
287
+
288
+ /* ── Filter morph: ::before hides on expand, gooey canvas handles junction ── */
289
+ [data-slot="dt-root"][data-morph="filter"] [data-slot="dt-group"]:has(input:checked)::before {
290
+ opacity: 0;
291
+ translate: -50% -80%;
292
+ scale: 1;
293
+ }
294
+
295
+ /* ── Path morph mode ── */
296
+ [data-slot="dt-root"][data-morph="path"] {
297
+ background: transparent;
298
+ border-color: transparent;
299
+ }
300
+ [data-slot="dt-root"][data-morph="path"] [data-slot="dt-track"] {
301
+ position: relative;
302
+ z-index: 1;
303
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * DynamicToggle style slots + CVA variants.
3
+ *
4
+ * Layout via Tailwind. State animations via CSS file (`:has()`, `clip-path`).
5
+ * Shape propagated to indicators via `--dt-radius` CSS variable.
6
+ *
7
+ * @module @mks2508/mks-ui/react/ui/DynamicToggle
8
+ */
9
+
10
+ import { cva, type VariantProps } from 'class-variance-authority';
11
+ import type { StyleSlots } from '@/core/types';
12
+
13
+ /** Slot names for DynamicToggle */
14
+ export type DynamicToggleSlot =
15
+ | 'root'
16
+ | 'track'
17
+ | 'option'
18
+ | 'indicator'
19
+ | 'group'
20
+ | 'groupIndicator'
21
+ | 'groupLabel';
22
+
23
+ /**
24
+ * Default styles for each DynamicToggle slot.
25
+ *
26
+ * Width is set by size variants — required because indicator uses `width: 50%`
27
+ * and clip-path uses container query units. Override: `slots={{ root: 'w-80' }}`.
28
+ */
29
+ export const dynamicToggleStyles: StyleSlots<DynamicToggleSlot> = {
30
+ root: 'relative border p-[2px] select-none',
31
+ track: 'relative grid grid-cols-[repeat(4,1fr)] place-items-center w-full h-full',
32
+ option: 'inline-grid place-items-center cursor-pointer font-medium z-[2] h-full w-full whitespace-nowrap',
33
+ indicator: 'absolute w-1/2 left-0 top-0 bottom-0 bg-foreground rounded-[var(--dt-radius,9999px)] pointer-events-none z-0',
34
+ group: 'col-span-2 relative w-full h-full grid grid-cols-2',
35
+ groupIndicator: 'absolute left-1/2 top-0 bottom-0 w-1/2 bg-foreground rounded-[var(--dt-radius,9999px)] pointer-events-none z-0',
36
+ groupLabel: [
37
+ 'absolute',
38
+ 'flex items-center justify-center',
39
+ 'text-muted-foreground font-medium whitespace-nowrap',
40
+ 'pointer-events-none',
41
+ ].join(' '),
42
+ };
43
+
44
+ /**
45
+ * CVA variants for DynamicToggle root.
46
+ *
47
+ * @example
48
+ * ```tsx
49
+ * <DynamicToggle size="sm" variant="outline" shape="rounded">
50
+ * ```
51
+ */
52
+ export const dynamicToggleVariants = cva(dynamicToggleStyles.root, {
53
+ variants: {
54
+ /** Visual variant — background and border */
55
+ variant: {
56
+ default: 'bg-card border-border shadow-sm',
57
+ ghost: 'bg-transparent border-transparent',
58
+ muted: 'bg-muted border-muted',
59
+ outline: 'bg-transparent border-border',
60
+ },
61
+ /**
62
+ * Size — height, width, and font size.
63
+ * Width is required for the 50% indicator and cqh clip-path.
64
+ */
65
+ size: {
66
+ sm: 'h-[30px] w-[210px] text-[10px] [--dt-h:30px]',
67
+ default: 'h-[38px] w-[260px] text-xs [--dt-h:38px]',
68
+ lg: 'h-[44px] w-80 text-sm [--dt-h:44px]',
69
+ },
70
+ /** Shape — border radius propagated to indicators via --dt-radius */
71
+ shape: {
72
+ pill: 'rounded-full [--dt-radius:9999px]',
73
+ rounded: 'rounded-xl [--dt-radius:0.75rem]',
74
+ square: 'rounded-md [--dt-radius:0.375rem]',
75
+ },
76
+ },
77
+ defaultVariants: {
78
+ variant: 'default',
79
+ size: 'default',
80
+ shape: 'pill',
81
+ },
82
+ });
83
+
84
+ /** Variant props extracted from CVA */
85
+ export type DynamicToggleVariantProps = VariantProps<typeof dynamicToggleVariants>;
@@ -0,0 +1,174 @@
1
+ /**
2
+ * DynamicToggle type definitions.
3
+ *
4
+ * CSS-animated toggle where one option expands into sub-options.
5
+ * Uses hidden radios + `:has(:checked)` for zero-JS animation.
6
+ * Optional gooey morph via `GooeyCanvas` or `MorphPath` primitives.
7
+ *
8
+ * @module @mks2508/mks-ui/react/ui/DynamicToggle
9
+ */
10
+
11
+ import type { SlotOverrides, IBaseConfig } from '@/core/types';
12
+ import type { DynamicToggleSlot, DynamicToggleVariantProps } from './DynamicToggle.styles';
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Shared types
16
+ // ---------------------------------------------------------------------------
17
+
18
+ /** How the group appears when collapsed (standalone option active). */
19
+ export type DynamicToggleCollapsedMode = 'title' | 'opts' | 'title-opts';
20
+
21
+ /** Morph technique for the pill↔groupLabel junction. */
22
+ export type DynamicToggleMorphMode = 'none' | 'filter' | 'path';
23
+
24
+ // ---------------------------------------------------------------------------
25
+ // Config
26
+ // ---------------------------------------------------------------------------
27
+
28
+ /**
29
+ * Configuration for DynamicToggle animation behavior.
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * <DynamicToggle config={{ morphMode: 'filter', duration: 0.3 }}>
34
+ * ...
35
+ * </DynamicToggle>
36
+ * ```
37
+ */
38
+ export interface IDynamicToggleConfig extends IBaseConfig {
39
+ /** CSS transition duration in seconds (default: 0.22) */
40
+ duration?: number;
41
+ /** Label animation style (default: 'morph') */
42
+ labelAnimation?: 'morph' | 'float' | 'none';
43
+ /** Gooey morph mode for the pill↔groupLabel junction (default: 'none') */
44
+ morphMode?: DynamicToggleMorphMode;
45
+ }
46
+
47
+ // ---------------------------------------------------------------------------
48
+ // Context
49
+ // ---------------------------------------------------------------------------
50
+
51
+ /**
52
+ * Context shared between DynamicToggle root and its children.
53
+ */
54
+ export type DynamicToggleContextType = {
55
+ /** Current selected value */
56
+ value: string;
57
+ /** Update selected value */
58
+ setValue: (value: string) => void;
59
+ /** Auto-generated radio group name */
60
+ groupName: string;
61
+ /** Whether a group child is active */
62
+ groupActive: boolean;
63
+ /** Whether the toggle is disabled */
64
+ disabled: boolean;
65
+ /** Register group info (called by DynamicToggleGroup on mount) */
66
+ registerGroup: (
67
+ label: string,
68
+ values: string[],
69
+ position: 'top' | 'bottom' | 'hidden',
70
+ collapsedMode: DynamicToggleCollapsedMode,
71
+ ) => void;
72
+ };
73
+
74
+ // ---------------------------------------------------------------------------
75
+ // Root
76
+ // ---------------------------------------------------------------------------
77
+
78
+ /**
79
+ * Props for the DynamicToggle root container.
80
+ *
81
+ * Supports exactly one `DynamicToggleOption` + one `DynamicToggleGroup`.
82
+ * The group expands into sub-options when one of its children is active.
83
+ *
84
+ * @example
85
+ * ```tsx
86
+ * <DynamicToggle
87
+ * value="tree" onValueChange={setVal}
88
+ * size="sm" config={{ morphMode: 'filter' }}
89
+ * >
90
+ * <DynamicToggleOption value="tree">Tree</DynamicToggleOption>
91
+ * <DynamicToggleGroup label="Changes" collapsedMode="title">
92
+ * <DynamicToggleOption value="flat">Flat</DynamicToggleOption>
93
+ * <DynamicToggleOption value="grouped">Grouped</DynamicToggleOption>
94
+ * </DynamicToggleGroup>
95
+ * </DynamicToggle>
96
+ * ```
97
+ */
98
+ export interface IDynamicToggleProps extends DynamicToggleVariantProps {
99
+ /** Controlled value */
100
+ value?: string;
101
+ /** Uncontrolled default value */
102
+ defaultValue?: string;
103
+ /** Change callback */
104
+ onValueChange?: (value: string) => void;
105
+ /** Disable the entire toggle */
106
+ disabled?: boolean;
107
+ /** Slot class overrides */
108
+ slots?: SlotOverrides<DynamicToggleSlot>;
109
+ /** Animation/behavior configuration */
110
+ config?: IDynamicToggleConfig;
111
+ /** Accessible label for the radio group */
112
+ 'aria-label'?: string;
113
+ /** Additional class for the root */
114
+ className?: string;
115
+ /** DynamicToggleOption and DynamicToggleGroup children */
116
+ children: React.ReactNode;
117
+ }
118
+
119
+ // ---------------------------------------------------------------------------
120
+ // Option
121
+ // ---------------------------------------------------------------------------
122
+
123
+ /**
124
+ * Props for a single toggle option (top-level or inside a group).
125
+ *
126
+ * @example
127
+ * ```tsx
128
+ * <DynamicToggleOption value="tree">Tree</DynamicToggleOption>
129
+ * ```
130
+ */
131
+ export interface IDynamicToggleOptionProps {
132
+ /** Value this option represents */
133
+ value: string;
134
+ /** Label content */
135
+ children: React.ReactNode;
136
+ /** Additional class */
137
+ className?: string;
138
+ }
139
+
140
+ // ---------------------------------------------------------------------------
141
+ // Group
142
+ // ---------------------------------------------------------------------------
143
+
144
+ /**
145
+ * Props for an expandable group of options.
146
+ *
147
+ * When none of the group's options are active, shows collapsed content
148
+ * based on `collapsedMode`:
149
+ * - `'title'` — only the group label
150
+ * - `'opts'` — only the combined sub-option text ("Solo · Team")
151
+ * - `'title-opts'` — WIP: currently falls back to 'title' behavior
152
+ *
153
+ * When one is active, expands with a clip-path reveal animation.
154
+ *
155
+ * @example
156
+ * ```tsx
157
+ * <DynamicToggleGroup label="Premium" collapsedMode="title">
158
+ * <DynamicToggleOption value="solo">Solo</DynamicToggleOption>
159
+ * <DynamicToggleOption value="team">Team</DynamicToggleOption>
160
+ * </DynamicToggleGroup>
161
+ * ```
162
+ */
163
+ export interface IDynamicToggleGroupProps {
164
+ /** Label shown as group title / group label text */
165
+ label: string;
166
+ /** Group label position relative to the pill (default: 'top') */
167
+ labelPosition?: 'top' | 'bottom' | 'hidden';
168
+ /** How the group appears when collapsed (default: 'title') */
169
+ collapsedMode?: DynamicToggleCollapsedMode;
170
+ /** Exactly 2 DynamicToggleOption children */
171
+ children: React.ReactNode;
172
+ /** Additional class */
173
+ className?: string;
174
+ }