@moontra/moonui 3.2.0 → 3.3.0
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/dist/chunk-VXDFZPDA.global.js +33 -0
- package/dist/{chunk-X5ULQZNC.global.js.map → chunk-VXDFZPDA.global.js.map} +1 -1
- package/dist/index.d.mts +39 -1
- package/dist/index.d.ts +39 -1
- package/dist/index.global.js +365 -30
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +118 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +110 -1
- package/dist/index.mjs.map +1 -1
- package/dist/lib/theme.global.js +30 -6
- package/dist/lib/theme.global.js.map +1 -1
- package/package.json +2 -1
- package/src/components/ui/__tests__/button-group.test.tsx +183 -0
- package/src/components/ui/__tests__/toggle-group.test.tsx +293 -0
- package/src/components/ui/button-group.tsx +125 -0
- package/src/components/ui/index.ts +19 -0
- package/src/components/ui/toggle-group.tsx +93 -0
- package/dist/chunk-X5ULQZNC.global.js +0 -33
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react'
|
|
3
|
+
import '@testing-library/jest-dom'
|
|
4
|
+
|
|
5
|
+
import { ToggleGroup, ToggleGroupItem } from '../toggle-group'
|
|
6
|
+
|
|
7
|
+
describe('ToggleGroup Components', () => {
|
|
8
|
+
describe('Rendering', () => {
|
|
9
|
+
it('renders group with items', () => {
|
|
10
|
+
render(
|
|
11
|
+
<ToggleGroup type="single" data-testid="group">
|
|
12
|
+
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
|
13
|
+
<ToggleGroupItem value="b">B</ToggleGroupItem>
|
|
14
|
+
</ToggleGroup>
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
const group = screen.getByTestId('group')
|
|
18
|
+
expect(group).toBeInTheDocument()
|
|
19
|
+
expect(screen.getByText('A')).toBeInTheDocument()
|
|
20
|
+
expect(screen.getByText('B')).toBeInTheDocument()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('applies base + custom className on the root', () => {
|
|
24
|
+
render(
|
|
25
|
+
<ToggleGroup type="single" className="custom-group" data-testid="group">
|
|
26
|
+
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
|
27
|
+
</ToggleGroup>
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
const group = screen.getByTestId('group')
|
|
31
|
+
// moonui-theme + FREE token/layout base sınıfları korunur
|
|
32
|
+
expect(group).toHaveClass('moonui-theme', 'inline-flex', 'items-center', 'gap-1')
|
|
33
|
+
expect(group).toHaveClass('custom-group')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('applies custom className on an item alongside toggleVariants', () => {
|
|
37
|
+
render(
|
|
38
|
+
<ToggleGroup type="single">
|
|
39
|
+
<ToggleGroupItem value="a" className="custom-item">
|
|
40
|
+
A
|
|
41
|
+
</ToggleGroupItem>
|
|
42
|
+
</ToggleGroup>
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
const item = screen.getByText('A')
|
|
46
|
+
expect(item).toHaveClass('custom-item')
|
|
47
|
+
// toggleVariants'tan gelen aktif-durum token sınıfı
|
|
48
|
+
expect(item).toHaveClass('data-[state=on]:bg-accent')
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('forwards ref on the group', () => {
|
|
52
|
+
const ref = React.createRef<HTMLDivElement>()
|
|
53
|
+
render(
|
|
54
|
+
<ToggleGroup type="single" ref={ref}>
|
|
55
|
+
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
|
56
|
+
</ToggleGroup>
|
|
57
|
+
)
|
|
58
|
+
expect(ref.current).toBeInstanceOf(HTMLDivElement)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('forwards ref on the item', () => {
|
|
62
|
+
const ref = React.createRef<HTMLButtonElement>()
|
|
63
|
+
render(
|
|
64
|
+
<ToggleGroup type="single">
|
|
65
|
+
<ToggleGroupItem value="a" ref={ref}>
|
|
66
|
+
A
|
|
67
|
+
</ToggleGroupItem>
|
|
68
|
+
</ToggleGroup>
|
|
69
|
+
)
|
|
70
|
+
expect(ref.current).toBeInstanceOf(HTMLButtonElement)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('maintains displayNames', () => {
|
|
74
|
+
expect(ToggleGroup.displayName).toBe('ToggleGroup')
|
|
75
|
+
expect(ToggleGroupItem.displayName).toBe('ToggleGroupItem')
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
describe('Single selection (type="single")', () => {
|
|
80
|
+
it('marks exactly one item active via defaultValue', () => {
|
|
81
|
+
render(
|
|
82
|
+
<ToggleGroup type="single" defaultValue="b">
|
|
83
|
+
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
|
84
|
+
<ToggleGroupItem value="b">B</ToggleGroupItem>
|
|
85
|
+
</ToggleGroup>
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
expect(screen.getByText('A')).toHaveAttribute('data-state', 'off')
|
|
89
|
+
expect(screen.getByText('B')).toHaveAttribute('data-state', 'on')
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('respects controlled value', () => {
|
|
93
|
+
render(
|
|
94
|
+
<ToggleGroup type="single" value="a">
|
|
95
|
+
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
|
96
|
+
<ToggleGroupItem value="b">B</ToggleGroupItem>
|
|
97
|
+
</ToggleGroup>
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
expect(screen.getByText('A')).toHaveAttribute('data-state', 'on')
|
|
101
|
+
expect(screen.getByText('B')).toHaveAttribute('data-state', 'off')
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('fires onValueChange with a string on click', () => {
|
|
105
|
+
const onValueChange = jest.fn()
|
|
106
|
+
render(
|
|
107
|
+
<ToggleGroup type="single" onValueChange={onValueChange}>
|
|
108
|
+
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
|
109
|
+
<ToggleGroupItem value="b">B</ToggleGroupItem>
|
|
110
|
+
</ToggleGroup>
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
fireEvent.click(screen.getByText('B'))
|
|
114
|
+
expect(onValueChange).toHaveBeenCalledWith('b')
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
describe('Multiple selection (type="multiple")', () => {
|
|
119
|
+
it('marks multiple items active via defaultValue', () => {
|
|
120
|
+
render(
|
|
121
|
+
<ToggleGroup type="multiple" defaultValue={['a', 'b']}>
|
|
122
|
+
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
|
123
|
+
<ToggleGroupItem value="b">B</ToggleGroupItem>
|
|
124
|
+
<ToggleGroupItem value="c">C</ToggleGroupItem>
|
|
125
|
+
</ToggleGroup>
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
expect(screen.getByText('A')).toHaveAttribute('data-state', 'on')
|
|
129
|
+
expect(screen.getByText('B')).toHaveAttribute('data-state', 'on')
|
|
130
|
+
expect(screen.getByText('C')).toHaveAttribute('data-state', 'off')
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('fires onValueChange with an array on click', () => {
|
|
134
|
+
const onValueChange = jest.fn()
|
|
135
|
+
render(
|
|
136
|
+
<ToggleGroup type="multiple" defaultValue={['a']} onValueChange={onValueChange}>
|
|
137
|
+
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
|
138
|
+
<ToggleGroupItem value="b">B</ToggleGroupItem>
|
|
139
|
+
</ToggleGroup>
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
fireEvent.click(screen.getByText('B'))
|
|
143
|
+
expect(onValueChange).toHaveBeenCalledWith(['a', 'b'])
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
describe('Variant / size context propagation', () => {
|
|
148
|
+
it('propagates group variant + size to items', () => {
|
|
149
|
+
render(
|
|
150
|
+
<ToggleGroup type="single" variant="outline" size="lg">
|
|
151
|
+
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
|
152
|
+
</ToggleGroup>
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
const item = screen.getByText('A')
|
|
156
|
+
// outline variant + lg size (toggleVariants ile birebir)
|
|
157
|
+
expect(item).toHaveClass('border', 'border-input')
|
|
158
|
+
expect(item).toHaveClass('h-11', 'px-5')
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('uses item prop when the group provides no variant/size', () => {
|
|
162
|
+
render(
|
|
163
|
+
<ToggleGroup type="single">
|
|
164
|
+
<ToggleGroupItem value="a" variant="outline" size="sm">
|
|
165
|
+
A
|
|
166
|
+
</ToggleGroupItem>
|
|
167
|
+
</ToggleGroup>
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
const item = screen.getByText('A')
|
|
171
|
+
// context tanımsız → item prop devreye girer
|
|
172
|
+
expect(item).toHaveClass('border', 'border-input')
|
|
173
|
+
expect(item).toHaveClass('h-9', 'px-2.5')
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('group variant overrides item variant (context wins)', () => {
|
|
177
|
+
render(
|
|
178
|
+
<ToggleGroup type="single" variant="outline">
|
|
179
|
+
<ToggleGroupItem value="a" variant="default">
|
|
180
|
+
A
|
|
181
|
+
</ToggleGroupItem>
|
|
182
|
+
</ToggleGroup>
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
const item = screen.getByText('A')
|
|
186
|
+
// context.variant ?? props.variant → outline kazanır
|
|
187
|
+
expect(item).toHaveClass('border', 'border-input')
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('falls back to default variant/size when nothing is set', () => {
|
|
191
|
+
render(
|
|
192
|
+
<ToggleGroup type="single">
|
|
193
|
+
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
|
194
|
+
</ToggleGroup>
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
const item = screen.getByText('A')
|
|
198
|
+
// default variant: bg-transparent, default size: h-10 px-3
|
|
199
|
+
expect(item).toHaveClass('bg-transparent', 'h-10', 'px-3')
|
|
200
|
+
})
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
describe('Disabled state', () => {
|
|
204
|
+
it('disables all items when the group is disabled', () => {
|
|
205
|
+
render(
|
|
206
|
+
<ToggleGroup type="single" disabled>
|
|
207
|
+
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
|
208
|
+
<ToggleGroupItem value="b">B</ToggleGroupItem>
|
|
209
|
+
</ToggleGroup>
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
expect(screen.getByText('A')).toBeDisabled()
|
|
213
|
+
expect(screen.getByText('B')).toBeDisabled()
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('disables an individual item', () => {
|
|
217
|
+
const onValueChange = jest.fn()
|
|
218
|
+
render(
|
|
219
|
+
<ToggleGroup type="single" onValueChange={onValueChange}>
|
|
220
|
+
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
|
221
|
+
<ToggleGroupItem value="b" disabled>
|
|
222
|
+
B
|
|
223
|
+
</ToggleGroupItem>
|
|
224
|
+
</ToggleGroup>
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
const disabledItem = screen.getByText('B')
|
|
228
|
+
expect(disabledItem).toBeDisabled()
|
|
229
|
+
fireEvent.click(disabledItem)
|
|
230
|
+
expect(onValueChange).not.toHaveBeenCalled()
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
describe('Accessibility (Radix-provided)', () => {
|
|
235
|
+
it('single group exposes radiogroup + radio roles', () => {
|
|
236
|
+
render(
|
|
237
|
+
<ToggleGroup type="single" data-testid="group">
|
|
238
|
+
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
|
239
|
+
<ToggleGroupItem value="b">B</ToggleGroupItem>
|
|
240
|
+
</ToggleGroup>
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
expect(screen.getByTestId('group')).toHaveAttribute('role', 'radiogroup')
|
|
244
|
+
expect(screen.getAllByRole('radio')).toHaveLength(2)
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
it('single active item reflects aria-checked', () => {
|
|
248
|
+
render(
|
|
249
|
+
<ToggleGroup type="single" defaultValue="a">
|
|
250
|
+
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
|
251
|
+
<ToggleGroupItem value="b">B</ToggleGroupItem>
|
|
252
|
+
</ToggleGroup>
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
expect(screen.getByText('A')).toHaveAttribute('aria-checked', 'true')
|
|
256
|
+
expect(screen.getByText('B')).toHaveAttribute('aria-checked', 'false')
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
it('multiple group exposes toolbar role + aria-pressed items', () => {
|
|
260
|
+
render(
|
|
261
|
+
<ToggleGroup type="multiple" defaultValue={['a']} data-testid="group">
|
|
262
|
+
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
|
263
|
+
<ToggleGroupItem value="b">B</ToggleGroupItem>
|
|
264
|
+
</ToggleGroup>
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
expect(screen.getByTestId('group')).toHaveAttribute('role', 'toolbar')
|
|
268
|
+
expect(screen.getByText('A')).toHaveAttribute('aria-pressed', 'true')
|
|
269
|
+
expect(screen.getByText('B')).toHaveAttribute('aria-pressed', 'false')
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
it('renders items as focusable buttons with Radix roving tabindex', () => {
|
|
273
|
+
render(
|
|
274
|
+
<ToggleGroup type="single" defaultValue="a">
|
|
275
|
+
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
|
276
|
+
<ToggleGroupItem value="b">B</ToggleGroupItem>
|
|
277
|
+
</ToggleGroup>
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
const first = screen.getByText('A')
|
|
281
|
+
const second = screen.getByText('B')
|
|
282
|
+
|
|
283
|
+
// Radix RovingFocusGroup her item'a bir tabindex atar (roving nav altyapısı).
|
|
284
|
+
// Ok-tuşuyla odak taşıma gerçek tarayıcıda Radix tarafından sağlanır;
|
|
285
|
+
// jsdom odak olaylarını tam simüle etmediği için burada odaklanabilirlik doğrulanır.
|
|
286
|
+
expect(first).toHaveAttribute('tabindex')
|
|
287
|
+
expect(second).toHaveAttribute('tabindex')
|
|
288
|
+
|
|
289
|
+
first.focus()
|
|
290
|
+
expect(first).toHaveFocus()
|
|
291
|
+
})
|
|
292
|
+
})
|
|
293
|
+
})
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
import { cn } from "../../lib/utils";
|
|
6
|
+
import { Button, type ButtonProps } from "./button";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Premium ButtonGroup Component
|
|
10
|
+
*
|
|
11
|
+
* Bitişik (segmentli) veya boşluklu buton grupları için yüksek kaliteli,
|
|
12
|
+
* erişilebilir bir kapsayıcı bileşen. Yatay/dikey yerleşim, bitişik kenar
|
|
13
|
+
* radius birleştirme ve çocuk `<Button>`'lara size/variant yayılımı sunar.
|
|
14
|
+
*
|
|
15
|
+
* Tamamen token-tabanlıdır: kenarlık ve köşe yuvarlama değerleri mevcut Button
|
|
16
|
+
* token'larından gelir; hardcoded renk YOKtur.
|
|
17
|
+
*/
|
|
18
|
+
const buttonGroupVariants = cva(
|
|
19
|
+
// Temel kapsayıcı: inline-flex; çocuklar akış içinde hizalanır
|
|
20
|
+
"moonui-theme inline-flex",
|
|
21
|
+
{
|
|
22
|
+
variants: {
|
|
23
|
+
orientation: {
|
|
24
|
+
// Yatay: butonlar dikeyde ortalanır (içerik-genişliği korunur)
|
|
25
|
+
horizontal: "flex-row items-center",
|
|
26
|
+
// Dikey: butonlar aynı genişliğe uzar (items-stretch) → kenarlar hizalanır
|
|
27
|
+
vertical: "flex-col items-stretch",
|
|
28
|
+
},
|
|
29
|
+
attached: {
|
|
30
|
+
// Bitişik: boşluk yok; radius birleştirme compoundVariants ile uygulanır
|
|
31
|
+
true: "",
|
|
32
|
+
// Boşluklu: normal aralık, radius korunur
|
|
33
|
+
false: "gap-2",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
compoundVariants: [
|
|
37
|
+
{
|
|
38
|
+
// Yatay + bitişik: bitişik yatay kenarlar düzleşir, -ml-px ile kenarlık örtüşür
|
|
39
|
+
orientation: "horizontal",
|
|
40
|
+
attached: true,
|
|
41
|
+
class: [
|
|
42
|
+
"[&>*:not(:first-child)]:rounded-l-none",
|
|
43
|
+
"[&>*:not(:last-child)]:rounded-r-none",
|
|
44
|
+
"[&>*:not(:first-child)]:-ml-px",
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
// Dikey + bitişik: bitişik dikey kenarlar düzleşir, -mt-px ile kenarlık örtüşür
|
|
49
|
+
orientation: "vertical",
|
|
50
|
+
attached: true,
|
|
51
|
+
class: [
|
|
52
|
+
"[&>*:not(:first-child)]:rounded-t-none",
|
|
53
|
+
"[&>*:not(:last-child)]:rounded-b-none",
|
|
54
|
+
"[&>*:not(:first-child)]:-mt-px",
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
defaultVariants: {
|
|
59
|
+
orientation: "horizontal",
|
|
60
|
+
attached: true,
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// ButtonGroup component props
|
|
66
|
+
export interface ButtonGroupProps
|
|
67
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
68
|
+
VariantProps<typeof buttonGroupVariants> {
|
|
69
|
+
/** Çocuk Button'lara yayılacak boyut (yalnızca çocuk kendi size'ını vermemişse) */
|
|
70
|
+
size?: ButtonProps["size"];
|
|
71
|
+
/** Çocuk Button'lara yayılacak varyant (yalnızca çocuk kendi variant'ını vermemişse) */
|
|
72
|
+
variant?: ButtonProps["variant"];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Premium ButtonGroup Component
|
|
77
|
+
*
|
|
78
|
+
* @param props - ButtonGroup bileşeni özellikleri
|
|
79
|
+
* @param props.orientation - Yerleşim yönü ("horizontal" | "vertical"), varsayılan "horizontal"
|
|
80
|
+
* @param props.attached - Bitişik (segmentli) mi yoksa boşluklu mu, varsayılan true
|
|
81
|
+
* @param props.size - Çocuk Button'lara yayılacak boyut (opsiyonel)
|
|
82
|
+
* @param props.variant - Çocuk Button'lara yayılacak varyant (opsiyonel)
|
|
83
|
+
*/
|
|
84
|
+
const ButtonGroup = React.forwardRef<HTMLDivElement, ButtonGroupProps>(
|
|
85
|
+
(
|
|
86
|
+
{ className, orientation, attached, size, variant, children, ...props },
|
|
87
|
+
ref
|
|
88
|
+
) => {
|
|
89
|
+
// size/variant yalnızca en az biri tanımlıysa çocuklara enjekte edilir
|
|
90
|
+
const shouldPropagate = size !== undefined || variant !== undefined;
|
|
91
|
+
|
|
92
|
+
const enhancedChildren = shouldPropagate
|
|
93
|
+
? React.Children.map(children, (child) => {
|
|
94
|
+
// Geçersiz element (string/number/null) veya Button olmayan çocuklar
|
|
95
|
+
// GRACEFUL geçilir — geçersiz prop enjekte edilmez
|
|
96
|
+
if (!React.isValidElement(child) || child.type !== Button) {
|
|
97
|
+
return child;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const childProps = child.props as ButtonProps;
|
|
101
|
+
|
|
102
|
+
// Çocuğun kendi prop'u önceliklidir (child.props.size ?? size)
|
|
103
|
+
return React.cloneElement(child as React.ReactElement<ButtonProps>, {
|
|
104
|
+
size: childProps.size ?? size,
|
|
105
|
+
variant: childProps.variant ?? variant,
|
|
106
|
+
});
|
|
107
|
+
})
|
|
108
|
+
: children;
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<div
|
|
112
|
+
ref={ref}
|
|
113
|
+
role="group"
|
|
114
|
+
className={cn(buttonGroupVariants({ orientation, attached }), className)}
|
|
115
|
+
{...props}
|
|
116
|
+
>
|
|
117
|
+
{enhancedChildren}
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
ButtonGroup.displayName = "ButtonGroup";
|
|
124
|
+
|
|
125
|
+
export { ButtonGroup, buttonGroupVariants };
|
|
@@ -445,6 +445,23 @@ export {
|
|
|
445
445
|
} from "./spinner";
|
|
446
446
|
export type { SpinnerProps as MoonUISpinnerProps } from "./spinner";
|
|
447
447
|
|
|
448
|
+
// Button Group (issue #257) — bitişik/segmentli buton grupları
|
|
449
|
+
export {
|
|
450
|
+
ButtonGroup as MoonUIButtonGroup,
|
|
451
|
+
buttonGroupVariants as moonUIButtonGroupVariants,
|
|
452
|
+
} from "./button-group";
|
|
453
|
+
export type { ButtonGroupProps as MoonUIButtonGroupProps } from "./button-group";
|
|
454
|
+
|
|
455
|
+
// Toggle Group (issue #257) — tek/çoklu seçim segmented toggle seti
|
|
456
|
+
export {
|
|
457
|
+
ToggleGroup as MoonUIToggleGroup,
|
|
458
|
+
ToggleGroupItem as MoonUIToggleGroupItem,
|
|
459
|
+
} from "./toggle-group";
|
|
460
|
+
export type {
|
|
461
|
+
ToggleGroupProps as MoonUIToggleGroupProps,
|
|
462
|
+
ToggleGroupItemProps as MoonUIToggleGroupItemProps,
|
|
463
|
+
} from "./toggle-group";
|
|
464
|
+
|
|
448
465
|
// Also export without MoonUI prefix for backward compatibility
|
|
449
466
|
export * from "./accordion";
|
|
450
467
|
export * from "./alert";
|
|
@@ -497,3 +514,5 @@ export * from "./tooltip";
|
|
|
497
514
|
export * from "./kbd";
|
|
498
515
|
export * from "./rating";
|
|
499
516
|
export * from "./spinner";
|
|
517
|
+
export * from "./button-group";
|
|
518
|
+
export * from "./toggle-group";
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
|
|
5
|
+
import { type VariantProps } from "class-variance-authority"
|
|
6
|
+
|
|
7
|
+
import { cn } from "../../lib/utils"
|
|
8
|
+
import { toggleVariants } from "./toggle"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* ToggleGroup Component
|
|
12
|
+
*
|
|
13
|
+
* Tek (single) veya çoklu (multiple) seçim destekleyen segmented toggle seti.
|
|
14
|
+
* Radix UI `ToggleGroup` primitive'i üzerine kuruludur; roving-tabindex klavye
|
|
15
|
+
* navigasyonu, tek/çoklu seçim mantığı ve erişilebilirlik rolleri (single için
|
|
16
|
+
* `role="radiogroup"` + item `role="radio"`, multiple için `role="toolbar"` +
|
|
17
|
+
* item `aria-pressed`) Radix tarafından yönetilir — bu davranış yeniden yazılmaz.
|
|
18
|
+
*
|
|
19
|
+
* `variant` ve `size` değerleri bir React context (`ToggleGroupContext`) ile
|
|
20
|
+
* item'lara yayılır (shadcn deseni). Her `ToggleGroupItem`, `Toggle` bileşeniyle
|
|
21
|
+
* görsel parite için `toggleVariants`'i yeniden kullanır — aktif durum
|
|
22
|
+
* `data-[state=on]:bg-accent data-[state=on]:text-accent-foreground` ile stillenir.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
// Grup düzeyinde belirlenen variant/size'i item'lara taşıyan context (shadcn deseni).
|
|
26
|
+
// Item, önce context değerini; context tanımsızsa kendi prop'unu kullanır.
|
|
27
|
+
const ToggleGroupContext = React.createContext<
|
|
28
|
+
VariantProps<typeof toggleVariants>
|
|
29
|
+
>({
|
|
30
|
+
size: "default",
|
|
31
|
+
variant: "default",
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// NOT: Radix `ToggleGroup.Root` prop tipi ayrımlı bir union'dır
|
|
35
|
+
// (type="single" → value: string, type="multiple" → value: string[]). Bir `interface`
|
|
36
|
+
// union'ı `extend` edemediğinden burada intersection'lı `type` alias kullanılır — bu,
|
|
37
|
+
// tek/çoklu ayrımını (discriminated union) korur.
|
|
38
|
+
export type ToggleGroupProps = React.ComponentPropsWithoutRef<
|
|
39
|
+
typeof ToggleGroupPrimitive.Root
|
|
40
|
+
> &
|
|
41
|
+
VariantProps<typeof toggleVariants>
|
|
42
|
+
|
|
43
|
+
const ToggleGroup = React.forwardRef<
|
|
44
|
+
React.ElementRef<typeof ToggleGroupPrimitive.Root>,
|
|
45
|
+
ToggleGroupProps
|
|
46
|
+
>(({ className, variant, size, children, ...props }, ref) => (
|
|
47
|
+
<ToggleGroupPrimitive.Root
|
|
48
|
+
ref={ref}
|
|
49
|
+
className={cn(
|
|
50
|
+
"moonui-theme inline-flex items-center justify-center gap-1",
|
|
51
|
+
className
|
|
52
|
+
)}
|
|
53
|
+
{...props}
|
|
54
|
+
>
|
|
55
|
+
<ToggleGroupContext.Provider value={{ variant, size }}>
|
|
56
|
+
{children}
|
|
57
|
+
</ToggleGroupContext.Provider>
|
|
58
|
+
</ToggleGroupPrimitive.Root>
|
|
59
|
+
))
|
|
60
|
+
|
|
61
|
+
ToggleGroup.displayName = "ToggleGroup"
|
|
62
|
+
|
|
63
|
+
export interface ToggleGroupItemProps
|
|
64
|
+
extends React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item>,
|
|
65
|
+
VariantProps<typeof toggleVariants> {}
|
|
66
|
+
|
|
67
|
+
const ToggleGroupItem = React.forwardRef<
|
|
68
|
+
React.ElementRef<typeof ToggleGroupPrimitive.Item>,
|
|
69
|
+
ToggleGroupItemProps
|
|
70
|
+
>(({ className, children, variant, size, ...props }, ref) => {
|
|
71
|
+
// Grup context'i varsa onun değeri, yoksa item'ın kendi prop'u geçerli olur.
|
|
72
|
+
const context = React.useContext(ToggleGroupContext)
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<ToggleGroupPrimitive.Item
|
|
76
|
+
ref={ref}
|
|
77
|
+
className={cn(
|
|
78
|
+
toggleVariants({
|
|
79
|
+
variant: context.variant ?? variant,
|
|
80
|
+
size: context.size ?? size,
|
|
81
|
+
}),
|
|
82
|
+
className
|
|
83
|
+
)}
|
|
84
|
+
{...props}
|
|
85
|
+
>
|
|
86
|
+
{children}
|
|
87
|
+
</ToggleGroupPrimitive.Item>
|
|
88
|
+
)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
ToggleGroupItem.displayName = "ToggleGroupItem"
|
|
92
|
+
|
|
93
|
+
export { ToggleGroup, ToggleGroupItem }
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
/** @license MoonUI v1.0.0 - MIT License - https://moonui.dev */
|
|
4
|
-
var M=Object.create;var $=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var z=Object.getOwnPropertyNames;var H=Object.getPrototypeOf,W=Object.prototype.hasOwnProperty;var ve=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,r)=>(typeof require<"u"?require:t)[r]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var d=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Y=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of z(t))!W.call(e,o)&&o!==r&&$(e,o,{get:()=>t[o],enumerable:!(n=B(t,o))||n.enumerable});return e};var me=(e,t,r)=>(r=e!=null?M(H(e)):{},Y(t||!e||!e.__esModule?$(r,"default",{value:e,enumerable:!0}):r,e));var T=d(u=>{"use strict";var y=Symbol.for("react.element"),G=Symbol.for("react.portal"),J=Symbol.for("react.fragment"),K=Symbol.for("react.strict_mode"),Q=Symbol.for("react.profiler"),X=Symbol.for("react.provider"),Z=Symbol.for("react.context"),ee=Symbol.for("react.forward_ref"),te=Symbol.for("react.suspense"),re=Symbol.for("react.memo"),ne=Symbol.for("react.lazy"),b=Symbol.iterator;function oe(e){return e===null||typeof e!="object"?null:(e=b&&e[b]||e["@@iterator"],typeof e=="function"?e:null)}var C={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},x=Object.assign,P={};function p(e,t,r){this.props=e,this.context=t,this.refs=P,this.updater=r||C}p.prototype.isReactComponent={};p.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};p.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function g(){}g.prototype=p.prototype;function E(e,t,r){this.props=e,this.context=t,this.refs=P,this.updater=r||C}var k=E.prototype=new g;k.constructor=E;x(k,p.prototype);k.isPureReactComponent=!0;var j=Array.isArray,I=Object.prototype.hasOwnProperty,R={current:null},q={key:!0,ref:!0,__self:!0,__source:!0};function N(e,t,r){var n,o={},i=null,s=null;if(t!=null)for(n in t.ref!==void 0&&(s=t.ref),t.key!==void 0&&(i=""+t.key),t)I.call(t,n)&&!q.hasOwnProperty(n)&&(o[n]=t[n]);var f=arguments.length-2;if(f===1)o.children=r;else if(1<f){for(var c=Array(f),a=0;a<f;a++)c[a]=arguments[a+2];o.children=c}if(e&&e.defaultProps)for(n in f=e.defaultProps,f)o[n]===void 0&&(o[n]=f[n]);return{$$typeof:y,type:e,key:i,ref:s,props:o,_owner:R.current}}function ue(e,t){return{$$typeof:y,type:e.type,key:t,ref:e.ref,props:e.props,_owner:e._owner}}function w(e){return typeof e=="object"&&e!==null&&e.$$typeof===y}function ie(e){var t={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,function(r){return t[r]})}var O=/\/+/g;function S(e,t){return typeof e=="object"&&e!==null&&e.key!=null?ie(""+e.key):t.toString(36)}function v(e,t,r,n,o){var i=typeof e;(i==="undefined"||i==="boolean")&&(e=null);var s=!1;if(e===null)s=!0;else switch(i){case"string":case"number":s=!0;break;case"object":switch(e.$$typeof){case y:case G:s=!0}}if(s)return s=e,o=o(s),e=n===""?"."+S(s,0):n,j(o)?(r="",e!=null&&(r=e.replace(O,"$&/")+"/"),v(o,t,r,"",function(a){return a})):o!=null&&(w(o)&&(o=ue(o,r+(!o.key||s&&s.key===o.key?"":(""+o.key).replace(O,"$&/")+"/")+e)),t.push(o)),1;if(s=0,n=n===""?".":n+":",j(e))for(var f=0;f<e.length;f++){i=e[f];var c=n+S(i,f);s+=v(i,t,r,c,o)}else if(c=oe(e),typeof c=="function")for(e=c.call(e),f=0;!(i=e.next()).done;)i=i.value,c=n+S(i,f++),s+=v(i,t,r,c,o);else if(i==="object")throw t=String(e),Error("Objects are not valid as a React child (found: "+(t==="[object Object]"?"object with keys {"+Object.keys(e).join(", ")+"}":t)+"). If you meant to render a collection of children, use an array instead.");return s}function _(e,t,r){if(e==null)return e;var n=[],o=0;return v(e,n,"","",function(i){return t.call(r,i,o++)}),n}function se(e){if(e._status===-1){var t=e._result;t=t(),t.then(function(r){(e._status===0||e._status===-1)&&(e._status=1,e._result=r)},function(r){(e._status===0||e._status===-1)&&(e._status=2,e._result=r)}),e._status===-1&&(e._status=0,e._result=t)}if(e._status===1)return e._result.default;throw e._result}var l={current:null},m={transition:null},ce={ReactCurrentDispatcher:l,ReactCurrentBatchConfig:m,ReactCurrentOwner:R};function D(){throw Error("act(...) is not supported in production builds of React.")}u.Children={map:_,forEach:function(e,t,r){_(e,function(){t.apply(this,arguments)},r)},count:function(e){var t=0;return _(e,function(){t++}),t},toArray:function(e){return _(e,function(t){return t})||[]},only:function(e){if(!w(e))throw Error("React.Children.only expected to receive a single React element child.");return e}};u.Component=p;u.Fragment=J;u.Profiler=Q;u.PureComponent=E;u.StrictMode=K;u.Suspense=te;u.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=ce;u.act=D;u.cloneElement=function(e,t,r){if(e==null)throw Error("React.cloneElement(...): The argument must be a React element, but you passed "+e+".");var n=x({},e.props),o=e.key,i=e.ref,s=e._owner;if(t!=null){if(t.ref!==void 0&&(i=t.ref,s=R.current),t.key!==void 0&&(o=""+t.key),e.type&&e.type.defaultProps)var f=e.type.defaultProps;for(c in t)I.call(t,c)&&!q.hasOwnProperty(c)&&(n[c]=t[c]===void 0&&f!==void 0?f[c]:t[c])}var c=arguments.length-2;if(c===1)n.children=r;else if(1<c){f=Array(c);for(var a=0;a<c;a++)f[a]=arguments[a+2];n.children=f}return{$$typeof:y,type:e.type,key:o,ref:i,props:n,_owner:s}};u.createContext=function(e){return e={$$typeof:Z,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null,_defaultValue:null,_globalName:null},e.Provider={$$typeof:X,_context:e},e.Consumer=e};u.createElement=N;u.createFactory=function(e){var t=N.bind(null,e);return t.type=e,t};u.createRef=function(){return{current:null}};u.forwardRef=function(e){return{$$typeof:ee,render:e}};u.isValidElement=w;u.lazy=function(e){return{$$typeof:ne,_payload:{_status:-1,_result:e},_init:se}};u.memo=function(e,t){return{$$typeof:re,type:e,compare:t===void 0?null:t}};u.startTransition=function(e){var t=m.transition;m.transition={};try{e()}finally{m.transition=t}};u.unstable_act=D;u.useCallback=function(e,t){return l.current.useCallback(e,t)};u.useContext=function(e){return l.current.useContext(e)};u.useDebugValue=function(){};u.useDeferredValue=function(e){return l.current.useDeferredValue(e)};u.useEffect=function(e,t){return l.current.useEffect(e,t)};u.useId=function(){return l.current.useId()};u.useImperativeHandle=function(e,t,r){return l.current.useImperativeHandle(e,t,r)};u.useInsertionEffect=function(e,t){return l.current.useInsertionEffect(e,t)};u.useLayoutEffect=function(e,t){return l.current.useLayoutEffect(e,t)};u.useMemo=function(e,t){return l.current.useMemo(e,t)};u.useReducer=function(e,t,r){return l.current.useReducer(e,t,r)};u.useRef=function(e){return l.current.useRef(e)};u.useState=function(e){return l.current.useState(e)};u.useSyncExternalStore=function(e,t,r){return l.current.useSyncExternalStore(e,t,r)};u.useTransition=function(){return l.current.useTransition()};u.version="18.3.1"});var L=d((Ee,V)=>{"use strict";V.exports=T()});var A=d(h=>{"use strict";var fe=L(),le=Symbol.for("react.element"),ae=Symbol.for("react.fragment"),pe=Object.prototype.hasOwnProperty,ye=fe.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,de={key:!0,ref:!0,__self:!0,__source:!0};function U(e,t,r){var n,o={},i=null,s=null;r!==void 0&&(i=""+r),t.key!==void 0&&(i=""+t.key),t.ref!==void 0&&(s=t.ref);for(n in t)pe.call(t,n)&&!de.hasOwnProperty(n)&&(o[n]=t[n]);if(e&&e.defaultProps)for(n in t=e.defaultProps,t)o[n]===void 0&&(o[n]=t[n]);return{$$typeof:le,type:e,key:i,ref:s,props:o,_owner:ye.current}}h.Fragment=ae;h.jsx=U;h.jsxs=U});var _e=d((Re,F)=>{"use strict";F.exports=A()});export{ve as a,d as b,me as c,L as d,_e as e};
|
|
5
|
-
/*! Bundled license information:
|
|
6
|
-
|
|
7
|
-
react/cjs/react.production.min.js:
|
|
8
|
-
(**
|
|
9
|
-
* @license React
|
|
10
|
-
* react.production.min.js
|
|
11
|
-
*
|
|
12
|
-
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
13
|
-
*
|
|
14
|
-
* This source code is licensed under the MIT license found in the
|
|
15
|
-
* LICENSE file in the root directory of this source tree.
|
|
16
|
-
*)
|
|
17
|
-
|
|
18
|
-
react/cjs/react-jsx-runtime.production.min.js:
|
|
19
|
-
(**
|
|
20
|
-
* @license React
|
|
21
|
-
* react-jsx-runtime.production.min.js
|
|
22
|
-
*
|
|
23
|
-
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
24
|
-
*
|
|
25
|
-
* This source code is licensed under the MIT license found in the
|
|
26
|
-
* LICENSE file in the root directory of this source tree.
|
|
27
|
-
*)
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
if (typeof window !== 'undefined' && !window.React) {
|
|
31
|
-
console.warn('MoonUI: React not found. Please include React and ReactDOM before MoonUI.');
|
|
32
|
-
}
|
|
33
|
-
//# sourceMappingURL=chunk-X5ULQZNC.global.js.map
|