@telegraph/data-list 0.0.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # @telegraph/data-list
2
+
3
+ ## 0.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#593](https://github.com/knocklabs/telegraph/pull/593) [`322bf1e`](https://github.com/knocklabs/telegraph/commit/322bf1e463b0a2a5b83899843d8ea54004b89b9b) Thanks [@kylemcd](https://github.com/kylemcd)! - feat: add data-list package, add alignSelf prop to Box component.
8
+
9
+ - Updated dependencies [[`322bf1e`](https://github.com/knocklabs/telegraph/commit/322bf1e463b0a2a5b83899843d8ea54004b89b9b)]:
10
+ - @telegraph/layout@0.2.3
11
+ - @telegraph/typography@0.1.25
12
+ - @telegraph/icon@0.2.7
package/README.md ADDED
@@ -0,0 +1,680 @@
1
+ # 📋 DataList
2
+
3
+ > Flexible data list component for displaying label-value pairs in a structured, composable format.
4
+
5
+ ![Telegraph by Knock](https://github.com/knocklabs/telegraph/assets/29106675/9b5022e3-b02c-4582-ba57-3d6171e45e44)
6
+
7
+ [![npm version](https://img.shields.io/npm/v/@telegraph/data-list.svg)](https://www.npmjs.com/package/@telegraph/data-list)
8
+ [![minzipped size](https://img.shields.io/bundlephobia/minzip/@telegraph/data-list)](https://bundlephobia.com/result?p=@telegraph/data-list)
9
+ [![license](https://img.shields.io/npm/l/@telegraph/data-list)](https://github.com/knocklabs/telegraph/blob/main/LICENSE)
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @telegraph/data-list
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```tsx
20
+ import { DataList } from "@telegraph/data-list";
21
+ import { Text } from "@telegraph/typography";
22
+
23
+ export const UserProfile = () => (
24
+ <DataList.List>
25
+ <DataList.Item label="Name">
26
+ <Text as="span">John Doe</Text>
27
+ </DataList.Item>
28
+ <DataList.Item label="Email">
29
+ <Text as="span">john@example.com</Text>
30
+ </DataList.Item>
31
+ <DataList.Item label="Role">
32
+ <Text as="span">Administrator</Text>
33
+ </DataList.Item>
34
+ </DataList.List>
35
+ );
36
+ ```
37
+
38
+ ## API Reference
39
+
40
+ ### `<DataList.List>`
41
+
42
+ Container component that wraps data list items.
43
+
44
+ | Prop | Type | Default | Description |
45
+ | ------------- | ---------------------- | ---------- | ------------------------------------- |
46
+ | `direction` | `"column" \| "row"` | `"column"` | Layout direction of list items |
47
+ | `gap` | `keyof tokens.spacing` | `"4"` | Gap between list items |
48
+ | All Box props | - | - | Inherits all Box component properties |
49
+
50
+ ### `<DataList.Item>`
51
+
52
+ Simplified API for label-value pairs. Combines Label and Value in one component.
53
+
54
+ | Prop | Type | Default | Description |
55
+ | ----------- | --------------------------- | ------- | ------------------------------------- |
56
+ | `label` | `React.ReactNode \| string` | - | Label text or content |
57
+ | `icon` | `IconProps` | - | Optional icon to display before label |
58
+ | `direction` | `"row" \| "column"` | `"row"` | Layout direction of item |
59
+ | `gap` | `keyof tokens.spacing` | `"1"` | Gap between label and value |
60
+
61
+ ### `<DataList.ListItem>`
62
+
63
+ Row container for composing custom label-value layouts.
64
+
65
+ | Prop | Type | Default | Description |
66
+ | ----------- | ---------------------- | ------------ | -------------------------------- |
67
+ | `direction` | `"row" \| "column"` | `"row"` | Layout direction |
68
+ | `gap` | `keyof tokens.spacing` | `"1"` | Gap between children |
69
+ | `align` | Flexbox align values | `"baseline"` | Cross-axis alignment of children |
70
+
71
+ ### `<DataList.Label>`
72
+
73
+ Label component for displaying field names.
74
+
75
+ | Prop | Type | Default | Description |
76
+ | ----------- | ---------------------- | -------- | ------------------------------- |
77
+ | `maxW` | `keyof tokens.spacing` | `"36"` | Maximum width of label |
78
+ | `maxH` | `keyof tokens.spacing` | `"6"` | Maximum height of label |
79
+ | `w` | `keyof tokens.spacing` | `"full"` | Width of label |
80
+ | `icon` | `IconProps` | - | Optional icon to display |
81
+ | `textProps` | `TextProps` | - | Props to pass to Text component |
82
+
83
+ ### `<DataList.Value>`
84
+
85
+ Value component for displaying field values.
86
+
87
+ | Prop | Type | Default | Description |
88
+ | --------------- | ---- | ------- | ---------------------------------- |
89
+ | All Stack props | - | - | Inherits all Stack component props |
90
+
91
+ ### Icon Props Structure
92
+
93
+ ```tsx
94
+ type IconProps = TgphComponentProps<typeof Icon>;
95
+
96
+ // Example usage
97
+ icon={{ icon: Mail, color: "blue", size: "1" }}
98
+ ```
99
+
100
+ ## Usage Patterns
101
+
102
+ ### Basic Data List
103
+
104
+ ```tsx
105
+ import { DataList } from "@telegraph/data-list";
106
+ import { Text } from "@telegraph/typography";
107
+
108
+ export const BasicInfo = () => (
109
+ <DataList.List>
110
+ <DataList.Item label="First Name">
111
+ <Text as="span">Jane</Text>
112
+ </DataList.Item>
113
+ <DataList.Item label="Last Name">
114
+ <Text as="span">Smith</Text>
115
+ </DataList.Item>
116
+ <DataList.Item label="Status">
117
+ <Text as="span">Active</Text>
118
+ </DataList.Item>
119
+ </DataList.List>
120
+ );
121
+ ```
122
+
123
+ ### With Icons
124
+
125
+ ```tsx
126
+ import { DataList } from "@telegraph/data-list";
127
+ import { Text } from "@telegraph/typography";
128
+ import { Mail, MapPin, Phone } from "lucide-react";
129
+
130
+ export const ContactInfo = () => (
131
+ <DataList.List>
132
+ <DataList.Item label="Email" icon={{ icon: Mail }}>
133
+ <Text as="span">jane@example.com</Text>
134
+ </DataList.Item>
135
+ <DataList.Item label="Phone" icon={{ icon: Phone }}>
136
+ <Text as="span">+1 (555) 123-4567</Text>
137
+ </DataList.Item>
138
+ <DataList.Item label="Location" icon={{ icon: MapPin }}>
139
+ <Text as="span">San Francisco, CA</Text>
140
+ </DataList.Item>
141
+ </DataList.List>
142
+ );
143
+ ```
144
+
145
+ ### With Long Text
146
+
147
+ ```tsx
148
+ import { DataList } from "@telegraph/data-list";
149
+ import { Text } from "@telegraph/typography";
150
+
151
+ export const Description = () => (
152
+ <DataList.List>
153
+ <DataList.Item label="Summary">
154
+ <Text as="span">
155
+ This is a very long description that will wrap to multiple lines while
156
+ keeping the label aligned with the first line of text.
157
+ </Text>
158
+ </DataList.Item>
159
+ </DataList.List>
160
+ );
161
+ ```
162
+
163
+ ### Column Layout
164
+
165
+ ```tsx
166
+ import { DataList } from "@telegraph/data-list";
167
+ import { Text } from "@telegraph/typography";
168
+
169
+ export const ColumnLayout = () => (
170
+ <DataList.List>
171
+ <DataList.Item label="Description" direction="column">
172
+ <Text as="span">
173
+ When you need more vertical space, use column direction to stack the
174
+ label above the value.
175
+ </Text>
176
+ </DataList.Item>
177
+ </DataList.List>
178
+ );
179
+ ```
180
+
181
+ ### With Form Inputs
182
+
183
+ ```tsx
184
+ import { DataList } from "@telegraph/data-list";
185
+ import { Input } from "@telegraph/input";
186
+ import { TextArea } from "@telegraph/textarea";
187
+
188
+ export const EditableProfile = () => (
189
+ <DataList.List>
190
+ <DataList.Item label="Name">
191
+ <Input placeholder="Enter name" />
192
+ </DataList.Item>
193
+ <DataList.Item label="Email">
194
+ <Input type="email" placeholder="Enter email" />
195
+ </DataList.Item>
196
+ <DataList.Item label="Bio" direction="column">
197
+ <TextArea placeholder="Tell us about yourself" rows={4} />
198
+ </DataList.Item>
199
+ </DataList.List>
200
+ );
201
+ ```
202
+
203
+ ## Advanced Usage
204
+
205
+ ### Composition Pattern
206
+
207
+ For maximum control, use the composition API:
208
+
209
+ ```tsx
210
+ import { DataList } from "@telegraph/data-list";
211
+ import { Icon } from "@telegraph/icon";
212
+ import { Tag } from "@telegraph/tag";
213
+ import { Text } from "@telegraph/typography";
214
+ import { CheckCircle, User } from "lucide-react";
215
+
216
+ export const CustomDataList = () => (
217
+ <DataList.List gap="3" maxW="120">
218
+ <DataList.ListItem>
219
+ <DataList.Label icon={{ icon: User }}>User</DataList.Label>
220
+ <DataList.Value>
221
+ <Stack direction="row" align="center" gap="2">
222
+ <Text as="span">John Doe</Text>
223
+ <Icon icon={CheckCircle} color="green" size="1" alt="Verified" />
224
+ </Stack>
225
+ </DataList.Value>
226
+ </DataList.ListItem>
227
+
228
+ <DataList.ListItem>
229
+ <DataList.Label textProps={{ weight: "bold" }}>Status</DataList.Label>
230
+ <DataList.Value>
231
+ <Tag color="green">Active</Tag>
232
+ </DataList.Value>
233
+ </DataList.ListItem>
234
+ </DataList.List>
235
+ );
236
+ ```
237
+
238
+ ### Custom Label Styling
239
+
240
+ ```tsx
241
+ import { DataList } from "@telegraph/data-list";
242
+ import { Text } from "@telegraph/typography";
243
+
244
+ export const StyledLabels = () => (
245
+ <DataList.List>
246
+ <DataList.ListItem>
247
+ <DataList.Label
248
+ maxW="48"
249
+ textProps={{ color: "blue", weight: "bold", size: "3" }}
250
+ >
251
+ Important Field
252
+ </DataList.Label>
253
+ <DataList.Value>
254
+ <Text as="span">Custom styled label</Text>
255
+ </DataList.Value>
256
+ </DataList.ListItem>
257
+ </DataList.List>
258
+ );
259
+ ```
260
+
261
+ ### Nested Data Lists
262
+
263
+ ```tsx
264
+ import { DataList } from "@telegraph/data-list";
265
+ import { Stack } from "@telegraph/layout";
266
+ import { Text } from "@telegraph/typography";
267
+
268
+ export const NestedList = () => (
269
+ <DataList.List>
270
+ <DataList.Item label="User Details">
271
+ <Stack gap="2">
272
+ <DataList.List gap="2">
273
+ <DataList.Item label="Name">
274
+ <Text as="span">John Doe</Text>
275
+ </DataList.Item>
276
+ <DataList.Item label="Email">
277
+ <Text as="span">john@example.com</Text>
278
+ </DataList.Item>
279
+ </DataList.List>
280
+ </Stack>
281
+ </DataList.Item>
282
+ </DataList.List>
283
+ );
284
+ ```
285
+
286
+ ### With Interactive Elements
287
+
288
+ ```tsx
289
+ import { Button } from "@telegraph/button";
290
+ import { DataList } from "@telegraph/data-list";
291
+ import { Stack } from "@telegraph/layout";
292
+ import { Text } from "@telegraph/typography";
293
+ import { Copy, ExternalLink } from "lucide-react";
294
+
295
+ export const InteractiveList = () => {
296
+ const copyToClipboard = (text: string) => {
297
+ navigator.clipboard.writeText(text);
298
+ };
299
+
300
+ return (
301
+ <DataList.List>
302
+ <DataList.ListItem>
303
+ <DataList.Label>API Key</DataList.Label>
304
+ <DataList.Value>
305
+ <Stack direction="row" align="center" gap="2">
306
+ <Text as="span" color="gray">
307
+ sk_test_****
308
+ </Text>
309
+ <Button
310
+ variant="ghost"
311
+ size="1"
312
+ icon={{ icon: Copy, alt: "Copy API key" }}
313
+ onClick={() => copyToClipboard("sk_test_1234")}
314
+ />
315
+ </Stack>
316
+ </DataList.Value>
317
+ </DataList.ListItem>
318
+
319
+ <DataList.ListItem>
320
+ <DataList.Label>Documentation</DataList.Label>
321
+ <DataList.Value>
322
+ <Button
323
+ as="a"
324
+ href="https://docs.example.com"
325
+ variant="ghost"
326
+ size="1"
327
+ trailingIcon={{ icon: ExternalLink, alt: "" }}
328
+ >
329
+ View Docs
330
+ </Button>
331
+ </DataList.Value>
332
+ </DataList.ListItem>
333
+ </DataList.List>
334
+ );
335
+ };
336
+ ```
337
+
338
+ ### Responsive Layout
339
+
340
+ ```tsx
341
+ import { DataList } from "@telegraph/data-list";
342
+ import { Text } from "@telegraph/typography";
343
+ import { useMediaQuery } from "your-hooks";
344
+
345
+ export const ResponsiveList = () => {
346
+ const isMobile = useMediaQuery("(max-width: 768px)");
347
+
348
+ return (
349
+ <DataList.List>
350
+ <DataList.Item
351
+ label="Description"
352
+ direction={isMobile ? "column" : "row"}
353
+ >
354
+ <Text as="span">This switches to column layout on mobile devices</Text>
355
+ </DataList.Item>
356
+ </DataList.List>
357
+ );
358
+ };
359
+ ```
360
+
361
+ ### User Settings Example
362
+
363
+ ```tsx
364
+ import { DataList } from "@telegraph/data-list";
365
+ import { Input } from "@telegraph/input";
366
+ import { Stack } from "@telegraph/layout";
367
+ import { Select } from "@telegraph/select";
368
+ import { Text } from "@telegraph/typography";
369
+ import { Bell, Globe, Lock, User } from "lucide-react";
370
+
371
+ export const UserSettings = () => (
372
+ <Stack gap="6">
373
+ <Stack gap="2">
374
+ <Text as="h2" size="5" weight="semibold">
375
+ Profile Settings
376
+ </Text>
377
+ <DataList.List>
378
+ <DataList.Item label="Username" icon={{ icon: User }}>
379
+ <Input defaultValue="johndoe" />
380
+ </DataList.Item>
381
+ <DataList.Item label="Email" icon={{ icon: User }}>
382
+ <Input type="email" defaultValue="john@example.com" />
383
+ </DataList.Item>
384
+ </DataList.List>
385
+ </Stack>
386
+
387
+ <Stack gap="2">
388
+ <Text as="h2" size="5" weight="semibold">
389
+ Preferences
390
+ </Text>
391
+ <DataList.List>
392
+ <DataList.Item label="Language" icon={{ icon: Globe }}>
393
+ <Select defaultValue="en">
394
+ <option value="en">English</option>
395
+ <option value="es">Spanish</option>
396
+ <option value="fr">French</option>
397
+ </Select>
398
+ </DataList.Item>
399
+ <DataList.Item label="Notifications" icon={{ icon: Bell }}>
400
+ <Select defaultValue="all">
401
+ <option value="all">All notifications</option>
402
+ <option value="important">Important only</option>
403
+ <option value="none">None</option>
404
+ </Select>
405
+ </DataList.Item>
406
+ </DataList.List>
407
+ </Stack>
408
+
409
+ <Stack gap="2">
410
+ <Text as="h2" size="5" weight="semibold">
411
+ Security
412
+ </Text>
413
+ <DataList.List>
414
+ <DataList.Item label="Password" icon={{ icon: Lock }}>
415
+ <Input type="password" placeholder="Enter new password" />
416
+ </DataList.Item>
417
+ </DataList.List>
418
+ </Stack>
419
+ </Stack>
420
+ );
421
+ ```
422
+
423
+ ## Design Considerations
424
+
425
+ ### Label Alignment
426
+
427
+ The DataList component automatically aligns labels with the first line of their values, even when values span multiple lines. This is achieved through baseline alignment on the ListItem component.
428
+
429
+ ```tsx
430
+ // Labels stay aligned with first line of multi-line content
431
+ <DataList.Item label="Description">
432
+ <Text as="span">
433
+ This is a very long description that wraps to multiple lines. The label
434
+ "Description" will align with the first line of this text.
435
+ </Text>
436
+ </DataList.Item>
437
+ ```
438
+
439
+ ### Icon Alignment
440
+
441
+ When using icons with labels, the icon is automatically vertically centered with the label text while maintaining baseline alignment with the value.
442
+
443
+ ```tsx
444
+ // Icon is centered with label text
445
+ <DataList.Item label="Email" icon={{ icon: Mail }}>
446
+ <Text as="span">john@example.com</Text>
447
+ </DataList.Item>
448
+ ```
449
+
450
+ ### Width Control
451
+
452
+ Control label widths to create consistent alignment across items:
453
+
454
+ ```tsx
455
+ <DataList.List>
456
+ <DataList.ListItem>
457
+ <DataList.Label maxW="48">Short Label</DataList.Label>
458
+ <DataList.Value>Value</DataList.Value>
459
+ </DataList.ListItem>
460
+ <DataList.ListItem>
461
+ <DataList.Label maxW="48">Much Longer Label Name</DataList.Label>
462
+ <DataList.Value>Value</DataList.Value>
463
+ </DataList.ListItem>
464
+ </DataList.List>
465
+ ```
466
+
467
+ ## Accessibility
468
+
469
+ - ✅ **Semantic HTML**: Uses proper label and content semantics
470
+ - ✅ **Keyboard Navigation**: All interactive elements are keyboard accessible
471
+ - ✅ **Screen Reader Support**: Proper label associations and content structure
472
+ - ✅ **Focus Management**: Clear focus indicators for interactive elements
473
+ - ✅ **ARIA Attributes**: Proper use of labels and descriptions
474
+
475
+ ### Best Practices
476
+
477
+ 1. **Label Association**: Use meaningful labels that describe the content
478
+ 2. **Interactive Elements**: Ensure all buttons and inputs are keyboard accessible
479
+ 3. **Icon Accessibility**: Provide alt text for icons or use `aria-hidden` for decorative icons
480
+ 4. **Form Integration**: Associate labels with form inputs using proper HTML attributes
481
+ 5. **Content Structure**: Keep label-value pairs logically grouped
482
+
483
+ ```tsx
484
+ // ✅ Good accessibility practices
485
+ <DataList.Item label="Email" icon={{ icon: Mail, "aria-hidden": true }}>
486
+ <Input
487
+ type="email"
488
+ aria-label="Email address"
489
+ defaultValue="john@example.com"
490
+ />
491
+ </DataList.Item>
492
+
493
+ // ❌ Poor accessibility
494
+ <DataList.Item label="📧"> {/* Don't use emojis as labels */}
495
+ <Input /> {/* No accessible label */}
496
+ </DataList.Item>
497
+ ```
498
+
499
+ ## Examples
500
+
501
+ ### Product Details
502
+
503
+ ```tsx
504
+ import { DataList } from "@telegraph/data-list";
505
+ import { Stack } from "@telegraph/layout";
506
+ import { Tag } from "@telegraph/tag";
507
+ import { Text } from "@telegraph/typography";
508
+ import { Calendar, DollarSign, Package, Tag as TagIcon } from "lucide-react";
509
+
510
+ export const ProductDetails = () => (
511
+ <DataList.List maxW="120">
512
+ <DataList.Item label="Product Name" icon={{ icon: Package }}>
513
+ <Text as="span" weight="semibold">
514
+ Premium Wireless Headphones
515
+ </Text>
516
+ </DataList.Item>
517
+
518
+ <DataList.Item label="Price" icon={{ icon: DollarSign }}>
519
+ <Text as="span" size="4" weight="bold">
520
+ $299.99
521
+ </Text>
522
+ </DataList.Item>
523
+
524
+ <DataList.Item label="Release Date" icon={{ icon: Calendar }}>
525
+ <Text as="span">March 15, 2024</Text>
526
+ </DataList.Item>
527
+
528
+ <DataList.Item label="Tags" icon={{ icon: TagIcon }}>
529
+ <Stack direction="row" gap="2" wrap="wrap">
530
+ <Tag color="blue">Electronics</Tag>
531
+ <Tag color="green">Featured</Tag>
532
+ <Tag color="purple">New</Tag>
533
+ </Stack>
534
+ </DataList.Item>
535
+
536
+ <DataList.Item label="Description" direction="column">
537
+ <Text as="p" color="gray">
538
+ Experience premium sound quality with active noise cancellation, 40-hour
539
+ battery life, and comfortable over-ear design perfect for long listening
540
+ sessions.
541
+ </Text>
542
+ </DataList.Item>
543
+ </DataList.List>
544
+ );
545
+ ```
546
+
547
+ ### API Response Viewer
548
+
549
+ ```tsx
550
+ import { Button } from "@telegraph/button";
551
+ import { DataList } from "@telegraph/data-list";
552
+ import { Stack } from "@telegraph/layout";
553
+ import { Tag } from "@telegraph/tag";
554
+ import { Text } from "@telegraph/typography";
555
+ import { CheckCircle, Clock, Copy, XCircle } from "lucide-react";
556
+
557
+ export const APIResponseViewer = ({ response }) => {
558
+ const statusColor = response.status === 200 ? "green" : "red";
559
+ const statusIcon = response.status === 200 ? CheckCircle : XCircle;
560
+
561
+ return (
562
+ <DataList.List>
563
+ <DataList.ListItem>
564
+ <DataList.Label>Status</DataList.Label>
565
+ <DataList.Value>
566
+ <Stack direction="row" align="center" gap="2">
567
+ <Icon icon={statusIcon} color={statusColor} size="1" alt="" />
568
+ <Tag color={statusColor}>{response.status}</Tag>
569
+ </Stack>
570
+ </DataList.Value>
571
+ </DataList.ListItem>
572
+
573
+ <DataList.Item label="Endpoint">
574
+ <Stack direction="row" align="center" gap="2">
575
+ <Text as="code">{response.endpoint}</Text>
576
+ <Button
577
+ variant="ghost"
578
+ size="1"
579
+ icon={{ icon: Copy, alt: "Copy endpoint" }}
580
+ onClick={() => navigator.clipboard.writeText(response.endpoint)}
581
+ />
582
+ </Stack>
583
+ </DataList.Item>
584
+
585
+ <DataList.Item label="Response Time" icon={{ icon: Clock }}>
586
+ <Text as="span">{response.responseTime}ms</Text>
587
+ </DataList.Item>
588
+
589
+ <DataList.Item label="Response Body" direction="column">
590
+ <Text
591
+ as="pre"
592
+ style={{
593
+ background: "var(--tgph-gray-2)",
594
+ padding: "var(--tgph-spacing-3)",
595
+ borderRadius: "var(--tgph-rounded-2)",
596
+ overflow: "auto",
597
+ }}
598
+ >
599
+ {JSON.stringify(response.body, null, 2)}
600
+ </Text>
601
+ </DataList.Item>
602
+ </DataList.List>
603
+ );
604
+ };
605
+ ```
606
+
607
+ ### Invoice Details
608
+
609
+ ```tsx
610
+ import { DataList } from "@telegraph/data-list";
611
+ import { Stack } from "@telegraph/layout";
612
+ import { Tag } from "@telegraph/tag";
613
+ import { Text } from "@telegraph/typography";
614
+ import { Calendar, DollarSign, FileText, User } from "lucide-react";
615
+
616
+ export const InvoiceDetails = () => (
617
+ <Stack gap="4">
618
+ <DataList.List>
619
+ <DataList.Item label="Invoice Number" icon={{ icon: FileText }}>
620
+ <Text as="span" weight="semibold">
621
+ INV-2024-001
622
+ </Text>
623
+ </DataList.Item>
624
+
625
+ <DataList.Item label="Customer" icon={{ icon: User }}>
626
+ <Text as="span">Acme Corporation</Text>
627
+ </DataList.Item>
628
+
629
+ <DataList.Item label="Issue Date" icon={{ icon: Calendar }}>
630
+ <Text as="span">March 1, 2024</Text>
631
+ </DataList.Item>
632
+
633
+ <DataList.Item label="Due Date" icon={{ icon: Calendar }}>
634
+ <Text as="span">March 31, 2024</Text>
635
+ </DataList.Item>
636
+
637
+ <DataList.Item label="Status">
638
+ <Tag color="green">Paid</Tag>
639
+ </DataList.Item>
640
+ </DataList.List>
641
+
642
+ <Stack gap="2">
643
+ <Text as="h3" size="3" weight="semibold">
644
+ Line Items
645
+ </Text>
646
+ <DataList.List gap="2">
647
+ <DataList.Item label="Consulting Services">
648
+ <Text as="span">$5,000.00</Text>
649
+ </DataList.Item>
650
+ <DataList.Item label="Development Hours">
651
+ <Text as="span">$3,500.00</Text>
652
+ </DataList.Item>
653
+ <DataList.Item label="Tax (10%)">
654
+ <Text as="span">$850.00</Text>
655
+ </DataList.Item>
656
+ </DataList.List>
657
+ </Stack>
658
+
659
+ <DataList.List>
660
+ <DataList.Item label="Total Amount" icon={{ icon: DollarSign }}>
661
+ <Text as="span" size="5" weight="bold">
662
+ $9,350.00
663
+ </Text>
664
+ </DataList.Item>
665
+ </DataList.List>
666
+ </Stack>
667
+ );
668
+ ```
669
+
670
+ ## References
671
+
672
+ - [Storybook Demo](https://storybook.telegraph.dev/?path=/docs/datalist)
673
+
674
+ ## Contributing
675
+
676
+ See our [Contributing Guide](../../CONTRIBUTING.md) for more details.
677
+
678
+ ## License
679
+
680
+ MIT License - see [LICENSE](../../LICENSE) for details.
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),S=require("@telegraph/icon"),c=require("@telegraph/layout"),m=require("@telegraph/typography"),y=({direction:t="column",gap:r="4",...s})=>e.jsx(c.Stack,{direction:t,gap:r,...s}),i=({direction:t="row",gap:r="1",align:s="baseline",...n})=>e.jsx(c.Stack,{direction:t,gap:r,align:s,...n}),a=({maxW:t="36",maxH:r="6",w:s="full",icon:n,children:o,textProps:u,...x})=>{const{color:j="gray",weight:g="medium",...d}=u||{};return e.jsxs(c.Stack,{direction:"row",align:"baseline",gap:"2",maxW:t,w:s,maxH:r,...x,children:[n&&e.jsx(c.Stack,{alignSelf:"center",children:e.jsx(S.Icon,{size:"1",color:"gray",...n})}),e.jsx(m.Text,{as:"label",...d,color:j,weight:g,children:o})]})},l=({...t})=>e.jsx(c.Stack,{...t}),b=({label:t,direction:r,icon:s,children:n,...o})=>e.jsxs(i,{direction:r,...o,children:[e.jsx(a,{icon:s,children:t}),e.jsx(l,{children:n})]}),h={List:y,ListItem:i,Label:a,Value:l,Item:b};exports.DataList=h;
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../src/DataList/DataList.tsx"],"sourcesContent":["import { TgphComponentProps } from \"@telegraph/helpers\";\nimport { Icon } from \"@telegraph/icon\";\nimport { Stack } from \"@telegraph/layout\";\nimport { Text } from \"@telegraph/typography\";\nimport React from \"react\";\n\ntype ListProps = TgphComponentProps<typeof Stack>;\n\nconst List = ({ direction = \"column\", gap = \"4\", ...props }: ListProps) => {\n return <Stack direction={direction} gap={gap} {...props} />;\n};\n\ntype ListItemProps = TgphComponentProps<typeof Stack>;\n\nconst ListItem = ({\n direction = \"row\",\n gap = \"1\",\n align = \"baseline\",\n ...props\n}: ListItemProps) => {\n return <Stack direction={direction} gap={gap} align={align} {...props} />;\n};\n\ntype LabelProps = {\n textProps?: TgphComponentProps<typeof Text>;\n icon?: TgphComponentProps<typeof Icon>;\n} & TgphComponentProps<typeof Stack>;\n\nconst Label = ({\n maxW = \"36\",\n maxH = \"6\",\n w = \"full\",\n icon,\n children,\n textProps,\n ...props\n}: LabelProps) => {\n const {\n color = \"gray\",\n weight = \"medium\",\n ...restTextProps\n } = textProps || {};\n return (\n <Stack\n direction=\"row\"\n align=\"baseline\"\n gap=\"2\"\n maxW={maxW}\n w={w}\n maxH={maxH}\n {...props}\n >\n {icon && (\n <Stack alignSelf=\"center\">\n <Icon size=\"1\" color=\"gray\" {...icon} />\n </Stack>\n )}\n <Text as=\"label\" {...restTextProps} color={color} weight={weight}>\n {children}\n </Text>\n </Stack>\n );\n};\n\ntype ValueProps = TgphComponentProps<typeof Stack>;\n\nconst Value = ({ ...props }: ValueProps) => {\n return <Stack {...props} />;\n};\n\ntype ItemProps = ListItemProps & {\n label: React.ReactNode | string;\n icon?: TgphComponentProps<typeof Icon>;\n};\n\nconst Item = ({ label, direction, icon, children, ...props }: ItemProps) => {\n return (\n <ListItem direction={direction} {...props}>\n <Label icon={icon}>{label}</Label>\n <Value>{children}</Value>\n </ListItem>\n );\n};\n\nconst DataList = {\n List,\n ListItem,\n Label,\n Value,\n Item,\n};\n\nexport { DataList };\n"],"names":["List","direction","gap","props","jsx","Stack","ListItem","align","Label","maxW","maxH","w","icon","children","textProps","color","weight","restTextProps","jsxs","Icon","Text","Value","Item","label","DataList"],"mappings":"oNAQMA,EAAO,CAAC,CAAE,UAAAC,EAAY,SAAU,IAAAC,EAAM,IAAK,GAAGC,KAC3CC,EAAAA,IAACC,EAAAA,MAAA,CAAM,UAAAJ,EAAsB,IAAAC,EAAW,GAAGC,EAAO,EAKrDG,EAAW,CAAC,CAChB,UAAAL,EAAY,MACZ,IAAAC,EAAM,IACN,MAAAK,EAAQ,WACR,GAAGJ,CACL,UACUE,EAAAA,MAAA,CAAM,UAAAJ,EAAsB,IAAAC,EAAU,MAAAK,EAAe,GAAGJ,EAAO,EAQnEK,EAAQ,CAAC,CACb,KAAAC,EAAO,KACP,KAAAC,EAAO,IACP,EAAAC,EAAI,OAAA,KACJC,EACA,SAAAC,EACA,UAAAC,EACA,GAAGX,CACL,IAAkB,CAChB,KAAM,CACJ,MAAAY,EAAQ,OACR,OAAAC,EAAS,SACT,GAAGC,CAAA,EACDH,GAAa,CAAA,EACjB,OACEI,EAAAA,KAACb,EAAAA,MAAA,CACC,UAAU,MACV,MAAM,WACN,IAAI,IACJ,KAAAI,EACA,EAAAE,EACA,KAAAD,EACC,GAAGP,EAEH,SAAA,CAAAS,GACCR,EAAAA,IAACC,EAAAA,MAAA,CAAM,UAAU,SACf,SAAAD,EAAAA,IAACe,EAAAA,KAAA,CAAK,KAAK,IAAI,MAAM,OAAQ,GAAGP,CAAA,CAAM,EACxC,EAEFR,MAACgB,EAAAA,MAAK,GAAG,QAAS,GAAGH,EAAe,MAAAF,EAAc,OAAAC,EAC/C,SAAAH,CAAA,CACH,CAAA,CAAA,CAAA,CAGN,EAIMQ,EAAQ,CAAC,CAAE,GAAGlB,KACXC,MAACC,EAAAA,MAAA,CAAO,GAAGF,CAAA,CAAO,EAQrBmB,EAAO,CAAC,CAAE,MAAAC,EAAO,UAAAtB,EAAW,KAAAW,EAAM,SAAAC,EAAU,GAAGV,KAEjDe,EAAAA,KAACZ,EAAA,CAAS,UAAAL,EAAuB,GAAGE,EAClC,SAAA,CAAAC,EAAAA,IAACI,EAAA,CAAM,KAAAI,EAAa,SAAAW,CAAA,CAAM,EAC1BnB,MAACiB,GAAO,SAAAR,CAAA,CAAS,CAAA,EACnB,EAIEW,EAAW,CACf,KAAAxB,EACA,SAAAM,EACA,MAAAE,EACA,MAAAa,EACA,KAAAC,CACF"}
@@ -0,0 +1,53 @@
1
+ import { jsx as t, jsxs as l } from "react/jsx-runtime";
2
+ import { Icon as h } from "@telegraph/icon";
3
+ import { Stack as i } from "@telegraph/layout";
4
+ import { Text as x } from "@telegraph/typography";
5
+ const b = ({ direction: r = "column", gap: e = "4", ...o }) => /* @__PURE__ */ t(i, { direction: r, gap: e, ...o }), c = ({
6
+ direction: r = "row",
7
+ gap: e = "1",
8
+ align: o = "baseline",
9
+ ...n
10
+ }) => /* @__PURE__ */ t(i, { direction: r, gap: e, align: o, ...n }), a = ({
11
+ maxW: r = "36",
12
+ maxH: e = "6",
13
+ w: o = "full",
14
+ icon: n,
15
+ children: s,
16
+ textProps: u,
17
+ ...p
18
+ }) => {
19
+ const {
20
+ color: d = "gray",
21
+ weight: f = "medium",
22
+ ...g
23
+ } = u || {};
24
+ return /* @__PURE__ */ l(
25
+ i,
26
+ {
27
+ direction: "row",
28
+ align: "baseline",
29
+ gap: "2",
30
+ maxW: r,
31
+ w: o,
32
+ maxH: e,
33
+ ...p,
34
+ children: [
35
+ n && /* @__PURE__ */ t(i, { alignSelf: "center", children: /* @__PURE__ */ t(h, { size: "1", color: "gray", ...n }) }),
36
+ /* @__PURE__ */ t(x, { as: "label", ...g, color: d, weight: f, children: s })
37
+ ]
38
+ }
39
+ );
40
+ }, m = ({ ...r }) => /* @__PURE__ */ t(i, { ...r }), L = ({ label: r, direction: e, icon: o, children: n, ...s }) => /* @__PURE__ */ l(c, { direction: e, ...s, children: [
41
+ /* @__PURE__ */ t(a, { icon: o, children: r }),
42
+ /* @__PURE__ */ t(m, { children: n })
43
+ ] }), S = {
44
+ List: b,
45
+ ListItem: c,
46
+ Label: a,
47
+ Value: m,
48
+ Item: L
49
+ };
50
+ export {
51
+ S as DataList
52
+ };
53
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../../src/DataList/DataList.tsx"],"sourcesContent":["import { TgphComponentProps } from \"@telegraph/helpers\";\nimport { Icon } from \"@telegraph/icon\";\nimport { Stack } from \"@telegraph/layout\";\nimport { Text } from \"@telegraph/typography\";\nimport React from \"react\";\n\ntype ListProps = TgphComponentProps<typeof Stack>;\n\nconst List = ({ direction = \"column\", gap = \"4\", ...props }: ListProps) => {\n return <Stack direction={direction} gap={gap} {...props} />;\n};\n\ntype ListItemProps = TgphComponentProps<typeof Stack>;\n\nconst ListItem = ({\n direction = \"row\",\n gap = \"1\",\n align = \"baseline\",\n ...props\n}: ListItemProps) => {\n return <Stack direction={direction} gap={gap} align={align} {...props} />;\n};\n\ntype LabelProps = {\n textProps?: TgphComponentProps<typeof Text>;\n icon?: TgphComponentProps<typeof Icon>;\n} & TgphComponentProps<typeof Stack>;\n\nconst Label = ({\n maxW = \"36\",\n maxH = \"6\",\n w = \"full\",\n icon,\n children,\n textProps,\n ...props\n}: LabelProps) => {\n const {\n color = \"gray\",\n weight = \"medium\",\n ...restTextProps\n } = textProps || {};\n return (\n <Stack\n direction=\"row\"\n align=\"baseline\"\n gap=\"2\"\n maxW={maxW}\n w={w}\n maxH={maxH}\n {...props}\n >\n {icon && (\n <Stack alignSelf=\"center\">\n <Icon size=\"1\" color=\"gray\" {...icon} />\n </Stack>\n )}\n <Text as=\"label\" {...restTextProps} color={color} weight={weight}>\n {children}\n </Text>\n </Stack>\n );\n};\n\ntype ValueProps = TgphComponentProps<typeof Stack>;\n\nconst Value = ({ ...props }: ValueProps) => {\n return <Stack {...props} />;\n};\n\ntype ItemProps = ListItemProps & {\n label: React.ReactNode | string;\n icon?: TgphComponentProps<typeof Icon>;\n};\n\nconst Item = ({ label, direction, icon, children, ...props }: ItemProps) => {\n return (\n <ListItem direction={direction} {...props}>\n <Label icon={icon}>{label}</Label>\n <Value>{children}</Value>\n </ListItem>\n );\n};\n\nconst DataList = {\n List,\n ListItem,\n Label,\n Value,\n Item,\n};\n\nexport { DataList };\n"],"names":["List","direction","gap","props","jsx","Stack","ListItem","align","Label","maxW","maxH","w","icon","children","textProps","color","weight","restTextProps","jsxs","Icon","Text","Value","Item","label","DataList"],"mappings":";;;;AAQA,MAAMA,IAAO,CAAC,EAAE,WAAAC,IAAY,UAAU,KAAAC,IAAM,KAAK,GAAGC,QAC3C,gBAAAC,EAACC,GAAA,EAAM,WAAAJ,GAAsB,KAAAC,GAAW,GAAGC,GAAO,GAKrDG,IAAW,CAAC;AAAA,EAChB,WAAAL,IAAY;AAAA,EACZ,KAAAC,IAAM;AAAA,EACN,OAAAK,IAAQ;AAAA,EACR,GAAGJ;AACL,wBACUE,GAAA,EAAM,WAAAJ,GAAsB,KAAAC,GAAU,OAAAK,GAAe,GAAGJ,GAAO,GAQnEK,IAAQ,CAAC;AAAA,EACb,MAAAC,IAAO;AAAA,EACP,MAAAC,IAAO;AAAA,EACP,GAAAC,IAAI;AAAA,EACJ,MAAAC;AAAA,EACA,UAAAC;AAAA,EACA,WAAAC;AAAA,EACA,GAAGX;AACL,MAAkB;AAChB,QAAM;AAAA,IACJ,OAAAY,IAAQ;AAAA,IACR,QAAAC,IAAS;AAAA,IACT,GAAGC;AAAA,EAAA,IACDH,KAAa,CAAA;AACjB,SACE,gBAAAI;AAAA,IAACb;AAAA,IAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAM;AAAA,MACN,KAAI;AAAA,MACJ,MAAAI;AAAA,MACA,GAAAE;AAAA,MACA,MAAAD;AAAA,MACC,GAAGP;AAAA,MAEH,UAAA;AAAA,QAAAS,KACC,gBAAAR,EAACC,GAAA,EAAM,WAAU,UACf,UAAA,gBAAAD,EAACe,GAAA,EAAK,MAAK,KAAI,OAAM,QAAQ,GAAGP,EAAA,CAAM,GACxC;AAAA,QAEF,gBAAAR,EAACgB,KAAK,IAAG,SAAS,GAAGH,GAAe,OAAAF,GAAc,QAAAC,GAC/C,UAAAH,EAAA,CACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN,GAIMQ,IAAQ,CAAC,EAAE,GAAGlB,QACX,gBAAAC,EAACC,GAAA,EAAO,GAAGF,EAAA,CAAO,GAQrBmB,IAAO,CAAC,EAAE,OAAAC,GAAO,WAAAtB,GAAW,MAAAW,GAAM,UAAAC,GAAU,GAAGV,QAEjD,gBAAAe,EAACZ,GAAA,EAAS,WAAAL,GAAuB,GAAGE,GAClC,UAAA;AAAA,EAAA,gBAAAC,EAACI,GAAA,EAAM,MAAAI,GAAa,UAAAW,EAAA,CAAM;AAAA,EAC1B,gBAAAnB,EAACiB,KAAO,UAAAR,EAAA,CAAS;AAAA,GACnB,GAIEW,IAAW;AAAA,EACf,MAAAxB;AAAA,EACA,UAAAM;AAAA,EACA,OAAAE;AAAA,EACA,OAAAa;AAAA,EACA,MAAAC;AACF;"}
@@ -0,0 +1,25 @@
1
+ import { TgphComponentProps } from '@telegraph/helpers';
2
+ import { Icon } from '@telegraph/icon';
3
+ import { Stack } from '@telegraph/layout';
4
+ import { Text } from '@telegraph/typography';
5
+ import { default as React } from 'react';
6
+ type ListProps = TgphComponentProps<typeof Stack>;
7
+ type ListItemProps = TgphComponentProps<typeof Stack>;
8
+ type LabelProps = {
9
+ textProps?: TgphComponentProps<typeof Text>;
10
+ icon?: TgphComponentProps<typeof Icon>;
11
+ } & TgphComponentProps<typeof Stack>;
12
+ type ValueProps = TgphComponentProps<typeof Stack>;
13
+ type ItemProps = ListItemProps & {
14
+ label: React.ReactNode | string;
15
+ icon?: TgphComponentProps<typeof Icon>;
16
+ };
17
+ declare const DataList: {
18
+ List: ({ direction, gap, ...props }: ListProps) => import("react/jsx-runtime").JSX.Element;
19
+ ListItem: ({ direction, gap, align, ...props }: ListItemProps) => import("react/jsx-runtime").JSX.Element;
20
+ Label: ({ maxW, maxH, w, icon, children, textProps, ...props }: LabelProps) => import("react/jsx-runtime").JSX.Element;
21
+ Value: ({ ...props }: ValueProps) => import("react/jsx-runtime").JSX.Element;
22
+ Item: ({ label, direction, icon, children, ...props }: ItemProps) => import("react/jsx-runtime").JSX.Element;
23
+ };
24
+ export { DataList };
25
+ //# sourceMappingURL=DataList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DataList.d.ts","sourceRoot":"","sources":["../../../src/DataList/DataList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,KAAK,SAAS,GAAG,kBAAkB,CAAC,OAAO,KAAK,CAAC,CAAC;AAMlD,KAAK,aAAa,GAAG,kBAAkB,CAAC,OAAO,KAAK,CAAC,CAAC;AAWtD,KAAK,UAAU,GAAG;IAChB,SAAS,CAAC,EAAE,kBAAkB,CAAC,OAAO,IAAI,CAAC,CAAC;IAC5C,IAAI,CAAC,EAAE,kBAAkB,CAAC,OAAO,IAAI,CAAC,CAAC;CACxC,GAAG,kBAAkB,CAAC,OAAO,KAAK,CAAC,CAAC;AAsCrC,KAAK,UAAU,GAAG,kBAAkB,CAAC,OAAO,KAAK,CAAC,CAAC;AAMnD,KAAK,SAAS,GAAG,aAAa,GAAG;IAC/B,KAAK,EAAE,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC;IAChC,IAAI,CAAC,EAAE,kBAAkB,CAAC,OAAO,IAAI,CAAC,CAAC;CACxC,CAAC;AAWF,QAAA,MAAM,QAAQ;yCA5E+C,SAAS;oDAWnE,aAAa;oEAiBb,UAAU;0BA8BgB,UAAU;2DASuB,SAAS;CAetE,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { DataList } from './DataList';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/DataList/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { DataList } from './DataList';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@telegraph/data-list",
3
+ "version": "0.0.1",
4
+ "description": "Flexible data list component for displaying label-value pairs in a structured, composable format.",
5
+ "repository": "https://github.com/knocklabs/telegraph/tree/main/packages/data-list",
6
+ "author": "@knocklabs",
7
+ "license": "MIT",
8
+ "main": "./dist/cjs/index.js",
9
+ "module": "./dist/esm/index.mjs",
10
+ "types": "./dist/types/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/esm/index.mjs",
14
+ "require": "./dist/cjs/index.js",
15
+ "types": "./dist/types/index.d.ts"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md"
21
+ ],
22
+ "prettier": "@telegraph/prettier-config",
23
+ "scripts": {
24
+ "clean": "rm -rf dist",
25
+ "dev": "vite build --watch --emptyOutDir false",
26
+ "build": "yarn clean && vite build",
27
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
28
+ "format": "prettier \"src/**/*.{js,ts,tsx}\" --write",
29
+ "format:check": "prettier \"src/**/*.{js,ts,tsx}\" --check",
30
+ "preview": "vite preview"
31
+ },
32
+ "dependencies": {
33
+ "@telegraph/helpers": "^0.0.13",
34
+ "@telegraph/icon": "^0.2.7",
35
+ "@telegraph/layout": "^0.2.3",
36
+ "@telegraph/typography": "^0.1.25"
37
+ },
38
+ "devDependencies": {
39
+ "@knocklabs/eslint-config": "^0.0.5",
40
+ "@knocklabs/typescript-config": "^0.0.2",
41
+ "@telegraph/postcss-config": "^0.0.30",
42
+ "@telegraph/prettier-config": "^0.0.7",
43
+ "@telegraph/vite-config": "^0.0.15",
44
+ "@types/react": "^19.1.11",
45
+ "eslint": "^8.56.0",
46
+ "react": "^19.1.1",
47
+ "react-dom": "^19.1.1",
48
+ "typescript": "^5.9.3",
49
+ "vite": "^6.0.11"
50
+ },
51
+ "peerDependencies": {
52
+ "react": "^18.0.0 || ^19.0.0",
53
+ "react-dom": "^18.0.0 || ^19.0.0"
54
+ }
55
+ }