@ttoss/components 2.3.0 → 2.4.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/README.md CHANGED
@@ -1,366 +1,279 @@
1
1
  # @ttoss/components
2
2
 
3
- <strong>@ttoss/components</strong> is a set of React components that you can use to build your apps using ttoss ecosystem.
3
+ React components for the ttoss ecosystem. **ESM only** package.
4
4
 
5
- ## ESM Only
5
+ ## Quick Start
6
6
 
7
- This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) because [react-markdown](https://github.com/remarkjs/react-markdown).
7
+ ```shell
8
+ pnpm add @ttoss/components @ttoss/ui @emotion/react @ttoss/react-hooks
9
+ ```
8
10
 
9
- ## Getting Started
11
+ **📖 [View all components in Storybook](https://storybook.ttoss.dev/?path=/docs/components-accordion--docs)**
10
12
 
11
- ### Install @ttoss/components
13
+ ## Components Overview
12
14
 
13
- ```shell
14
- pnpm add @ttoss/components @ttoss/ui @emotion/react @ttoss/react-hooks
15
+ All components are theme-aware and integrate seamlessly with `@ttoss/ui`.
16
+
17
+ ### Accordion
18
+
19
+ Collapsible content sections. [📖 Docs](https://storybook.ttoss.dev/?path=/docs/components-accordion--docs)
20
+
21
+ ```tsx
22
+ import {
23
+ Accordion,
24
+ AccordionItem,
25
+ AccordionItemButton,
26
+ AccordionItemHeading,
27
+ AccordionItemPanel,
28
+ } from '@ttoss/components/Accordion';
29
+
30
+ <Accordion allowMultipleExpanded>
31
+ <AccordionItem>
32
+ <AccordionItemHeading>
33
+ <AccordionItemButton>Section Title</AccordionItemButton>
34
+ </AccordionItemHeading>
35
+ <AccordionItemPanel>Section content</AccordionItemPanel>
36
+ </AccordionItem>
37
+ </Accordion>;
15
38
  ```
16
39
 
17
- ## Components
40
+ ### Drawer
18
41
 
19
- You can check all the components of the library `@ttoss/ui` on the [Storybook](https://storybook.ttoss.dev/?path=/story/components).
42
+ Slide-out panels from screen edges. [📖 Docs](https://storybook.ttoss.dev/?path=/docs/components-drawer--docs)
20
43
 
21
- ### List
44
+ ```tsx
45
+ import { Drawer } from '@ttoss/components/Drawer';
46
+
47
+ <Drawer open={isOpen} direction="right" size="300px">
48
+ <div>Drawer content</div>
49
+ </Drawer>;
50
+ ```
51
+
52
+ ### FileUploader
53
+
54
+ Drag-and-drop file upload with progress tracking. [📖 Docs](https://storybook.ttoss.dev/?path=/docs/components-fileuploader--docs)
55
+
56
+ ```tsx
57
+ import { FileUploader } from '@ttoss/components/FileUploader';
58
+
59
+ <FileUploader
60
+ onUpload={async (file) => ({ url: 'file-url', id: 'file-id' })}
61
+ onUploadComplete={(file, result) => console.log('Uploaded:', result)}
62
+ />;
63
+ ```
64
+
65
+ ### InstallPwa
66
+
67
+ PWA installation prompt component. [📖 Docs](https://storybook.ttoss.dev/?path=/docs/components-installpwa--docs)
68
+
69
+ ```tsx
70
+ import { InstallPwa } from '@ttoss/components/InstallPwa';
22
71
 
23
- The `List` component is a React component that renders an unordered list `(<ul>)` and accepts
24
- `ListItem` as its children. Each ListItem can contain any React content, including other components.
72
+ <InstallPwa />;
73
+ ```
74
+
75
+ ### JsonEditor
76
+
77
+ JSON editor component. Re-exports from [json-edit-react](https://carlosdevpereira.github.io/json-edit-react/). [📖 Docs](https://storybook.ttoss.dev/?path=/docs/components-jsoneditor--docs)
25
78
 
26
79
  ```tsx
27
- import React from 'react';
28
- import { List, ListItem } from '@ttoss/components/list';
29
-
30
- const MyComponent = () => (
31
- <List>
32
- <ListItem>Item 1</ListItem>
33
- <ListItem>Item 2</ListItem>
34
- <ListItem>Item 3</ListItem>
35
- <ListItem>
36
- <CustomComponent />
37
- </ListItem>
38
- </List>
39
- );
80
+ import { JsonEditor } from '@ttoss/components/JsonEditor';
81
+
82
+ <JsonEditor data={jsonData} setData={setJsonData} />;
40
83
  ```
41
84
 
42
- In this example, `List` is used to render an `<ul>` list with four items. The last item contains a custom React component (CustomComponent), demonstrating that ListItem can receive any React content as its children.
85
+ ### JsonView
86
+
87
+ JSON viewer component. Re-exports from [react-json-view-lite](https://github.com/AnyRoad/react-json-view-lite). [📖 Docs](https://storybook.ttoss.dev/?path=/docs/components-jsonview--docs)
88
+
89
+ ```tsx
90
+ import { JsonView } from '@ttoss/components/JsonView';
91
+
92
+ <JsonView data={jsonData} />;
93
+ ```
94
+
95
+ ### List
96
+
97
+ Unordered lists with customizable items. [📖 Docs](https://storybook.ttoss.dev/?path=/docs/components-list--docs)
98
+
99
+ ```tsx
100
+ import { List, ListItem } from '@ttoss/components/List';
43
101
 
44
- This is a basic example of how to use the `List` component with `ListItem`. You can customize the content and styles as needed to fit your project requirements.
102
+ <List>
103
+ <ListItem>First item</ListItem>
104
+ <ListItem>Second item</ListItem>
105
+ </List>;
106
+ ```
45
107
 
46
108
  ### Markdown
47
109
 
48
- Markdown uses [react-markdown](https://remarkjs.github.io/react-markdown/) under the hood, so the props are the same. You can update the elements as you want. Ex:
110
+ Render markdown content with theme integration. [📖 Docs](https://storybook.ttoss.dev/?path=/docs/components-markdown--docs)
111
+
112
+ ```tsx
113
+ import { Markdown } from '@ttoss/components/Markdown';
114
+
115
+ <Markdown
116
+ components={{
117
+ a: ({ children, ...props }) => <Link {...props}>{children}</Link>,
118
+ }}
119
+ >
120
+ # Heading Some **bold** text
121
+ </Markdown>;
122
+ ```
123
+
124
+ ### Menu
125
+
126
+ Dropdown menus with customizable triggers. [📖 Docs](https://storybook.ttoss.dev/?path=/docs/components-menu--docs)
49
127
 
50
128
  ```tsx
51
- const MARKDOWN_CONTENT = `
52
- # Heading 1
53
- ## Heading 2
54
- ### Heading 3
55
- #### Heading 4
56
- ##### Heading 5
57
-
58
- Lorem ipsum dolor sit, amet consectetur adipisicing elit. Odit quasi dolorum aperiam fugiat earum expedita non eligendi similique id minus explicabo, eum facere nihil aspernatur libero! Sapiente aliquid tenetur dolor.
59
-
60
- - Item 1
61
- - Item 2
62
- - Item 3
63
-
64
- ![Alt Text](https://fastly.picsum.photos/id/436/200/300.jpg?hmac=OuJRsPTZRaNZhIyVFbzDkMYMyORVpV86q5M8igEfM3Y "Alt Text")
65
-
66
- [Google](https://google.com)
67
- `;
68
-
69
- const Component = () => {
70
- return (
71
- <Markdown
72
- components={{
73
- a: ({ children, ...props }) => (
74
- <Link {...props} quiet>
75
- {children}
76
- </Link>
77
- ),
78
- }}
79
- >
80
- {MARKDOWN_CONTENT}
81
- </Markdown>
82
- );
83
- };
129
+ import { Menu } from '@ttoss/components/Menu';
130
+
131
+ <Menu trigger={<Button>Open Menu</Button>}>
132
+ <Menu.Item onClick={() => {}}>Action 1</Menu.Item>
133
+ <Menu.Item onClick={() => {}}>Action 2</Menu.Item>
134
+ </Menu>;
84
135
  ```
85
136
 
86
137
  ### Modal
87
138
 
88
- Modal uses [react-modal](https://reactcommunity.org/react-modal/) under the hood, so the props are the same. The only difference is that the styles are theme-aware. You can [style the modal](https://reactcommunity.org/react-modal/styles/) using theme tokens, except that [array as value](https://theme-ui.com/sx-prop#responsive-values) don't work.
139
+ Theme-aware modals with accessibility features. [📖 Docs](https://storybook.ttoss.dev/?path=/docs/components-modal--docs)
89
140
 
90
141
  ```tsx
91
- import { Modal } from '@ttoss/components';
92
- import { Box, Button, Flex, Text } from '@ttoss/ui';
93
-
94
- /**
95
- * See https://reactcommunity.org/react-modal/accessibility/#app-element
96
- */
97
- // Modal.setAppElement('#root'); Prefer using this static method over setting it on the component.
98
-
99
- Modal.setAppElement('#modal-root');
100
-
101
- const Component = () => {
102
- const [isOpen, setIsOpen] = React.useState(false);
103
-
104
- return (
105
- <Box id="modal-root">
106
- <Modal
107
- isOpen={isOpen}
108
- onAfterOpen={action('onAfterOpen')}
109
- onAfterClose={action('onAfterClose')}
110
- onRequestClose={() => {
111
- action('onRequestClose')();
112
- setIsOpen(false);
113
- }}
114
- style={{
115
- overlay: {
116
- backgroundColor: 'primary',
117
- },
118
- content: {
119
- backgroundColor: 'secondary',
120
- padding: ['lg', 'xl'], // Array as value don't work.
121
- },
122
- }}
123
- >
124
- <Flex>
125
- <Text>This is a modal.</Text>
126
- <Text>Here is the content.</Text>
127
- <Button
128
- onClick={() => {
129
- setIsOpen(false);
130
- }}
131
- >
132
- Close Modal
133
- </Button>
134
- </Flex>
135
- </Modal>
136
- <Button
137
- onClick={() => {
138
- setIsOpen(true);
139
- }}
140
- >
141
- Open Modal
142
- </Button>
143
- </Box>
144
- );
145
- };
142
+ import { Modal } from '@ttoss/components/Modal';
143
+
144
+ <Modal
145
+ isOpen={isOpen}
146
+ onRequestClose={() => setIsOpen(false)}
147
+ style={{ content: { backgroundColor: 'secondary' } }}
148
+ >
149
+ Modal content
150
+ </Modal>;
146
151
  ```
147
152
 
148
- ### Search
153
+ ### NotificationCard
149
154
 
150
- `Search` is a component that integrates an input field with debouncing functionality, making it ideal for search bars where you want to limit the rate of search queries based on user input.
155
+ Display notification messages with actions. [📖 Docs](https://storybook.ttoss.dev/?path=/docs/components-notificationcard--docs)
151
156
 
152
- It uses the `useDebounce` hook from `@ttoss/react-hooks` to delay the search action until the user has stopped typing for a specified duration, which helps to prevent unnecessary or excessive queries.
157
+ ```tsx
158
+ import { NotificationCard } from '@ttoss/components/NotificationCard';
159
+
160
+ <NotificationCard
161
+ title="Notification Title"
162
+ message="Notification message"
163
+ onClose={() => {}}
164
+ />;
165
+ ```
166
+
167
+ ### NotificationsMenu
168
+
169
+ Menu component for displaying notifications. [📖 Docs](https://storybook.ttoss.dev/?path=/docs/components-notificationsmenu--docs)
153
170
 
154
171
  ```tsx
155
- import React, { useState } from 'react';
156
- import { Search } from '@ttoss/components';
157
- import { Box } from '@ttoss/ui';
158
-
159
- const SearchComponent = () => {
160
- const [searchText, setSearchText] = useState('');
161
-
162
- const handleSearchChange = (newValue) => {
163
- setSearchText(newValue);
164
- // Perform search or update logic here
165
- };
166
-
167
- return (
168
- <Box>
169
- <Search
170
- value={searchText}
171
- onChange={handleSearchChange}
172
- loading={/* loading state here */}
173
- debounce={500} // Adjust the debounce time as needed
174
- />
175
- </Box>
176
- );
177
- };
172
+ import { NotificationsMenu } from '@ttoss/components/NotificationsMenu';
173
+
174
+ <NotificationsMenu
175
+ notifications={[{ id: '1', title: 'New message', read: false }]}
176
+ onNotificationClick={(notification) => {}}
177
+ />;
178
178
  ```
179
179
 
180
- In this example, the `Search` component receives the current search text and a handler function to update this text. The `loading` prop can be used to display a loading indicator, and the `debounce` prop controls the debounce delay.
180
+ ### Search
181
181
 
182
- ### Table
182
+ Debounced search input with loading states. [📖 Docs](https://storybook.ttoss.dev/?path=/docs/components-search--docs)
183
+
184
+ ```tsx
185
+ import { Search } from '@ttoss/components/Search';
186
+
187
+ <Search
188
+ value={searchText}
189
+ onChange={setSearchText}
190
+ loading={isLoading}
191
+ debounce={300}
192
+ />;
193
+ ```
183
194
 
184
- The `Table` component is a flexible and customizable table that supports sorting, filtering, and pagination. It is designed to be easy to use and integrate with your data sources. It exports all [TanStack Table](https://tanstack.com/table/latest) hooks and methods.
195
+ ### Table
185
196
 
186
- #### Basic Usage
197
+ Flexible tables with sorting and pagination. Uses [TanStack Table](https://tanstack.com/table/latest). [📖 Docs](https://storybook.ttoss.dev/?path=/docs/components-table--docs)
187
198
 
188
199
  ```tsx
189
- import * as React from 'react';
190
200
  import {
191
201
  Table,
192
- createColumnHelper,
193
- flexRender,
194
- getCoreRowModel,
195
202
  useReactTable,
196
- } from '@ttoss/components/table';
197
-
198
- type Person = {
199
- firstName: string;
200
- lastName: string;
201
- age: number;
202
- visits: number;
203
- status: string;
204
- progress: number;
205
- };
206
-
207
- const defaultData: Person[] = [
208
- {
209
- firstName: 'tanner',
210
- lastName: 'linsley',
211
- age: 24,
212
- visits: 100,
213
- status: 'In Relationship',
214
- progress: 50,
215
- },
216
- {
217
- firstName: 'tandy',
218
- lastName: 'miller',
219
- age: 40,
220
- visits: 40,
221
- status: 'Single',
222
- progress: 80,
223
- },
224
- {
225
- firstName: 'joe',
226
- lastName: 'dirte',
227
- age: 45,
228
- visits: 20,
229
- status: 'Complicated',
230
- progress: 10,
231
- },
232
- ];
233
-
234
- const columnHelper = createColumnHelper<Person>();
203
+ createColumnHelper,
204
+ } from '@ttoss/components/Table';
235
205
 
236
206
  const columns = [
237
- columnHelper.accessor('firstName', {
238
- cell: (info) => {
239
- return info.getValue();
240
- },
241
- footer: (info) => {
242
- return info.column.id;
243
- },
244
- }),
245
- columnHelper.accessor(
246
- (row) => {
247
- return row.lastName;
248
- },
249
- {
250
- id: 'lastName',
251
- cell: (info) => {
252
- return <i>{info.getValue()}</i>;
253
- },
254
- header: () => {
255
- return <span>Last Name</span>;
256
- },
257
- footer: (info) => {
258
- return info.column.id;
259
- },
260
- }
261
- ),
262
- columnHelper.accessor('age', {
263
- header: () => {
264
- return 'Age';
265
- },
266
- cell: (info) => {
267
- return info.renderValue();
268
- },
269
- footer: (info) => {
270
- return info.column.id;
271
- },
272
- }),
273
- columnHelper.accessor('visits', {
274
- header: () => {
275
- return <span>Visits</span>;
276
- },
277
- footer: (info) => {
278
- return info.column.id;
279
- },
280
- }),
281
- columnHelper.accessor('status', {
282
- header: 'Status',
283
- footer: (info) => {
284
- return info.column.id;
285
- },
286
- }),
287
- columnHelper.accessor('progress', {
288
- header: 'Profile Progress',
289
- footer: (info) => {
290
- return info.column.id;
291
- },
292
- }),
207
+ columnHelper.accessor('name', { header: 'Name' }),
208
+ columnHelper.accessor('email', { header: 'Email' }),
293
209
  ];
294
210
 
295
- const RenderTable = () => {
296
- const [data] = React.useState(() => {
297
- return [...defaultData];
298
- });
299
-
300
- const table = useReactTable({
301
- data,
302
- columns,
303
- getCoreRowModel: getCoreRowModel(),
304
- });
305
-
306
- return (
307
- <Table>
308
- <Table.Head>
309
- {table.getHeaderGroups().map((headerGroup) => {
310
- return (
311
- <Table.Row key={headerGroup.id}>
312
- {headerGroup.headers.map((header) => {
313
- return (
314
- <Table.Header key={header.id}>
315
- {header.isPlaceholder
316
- ? null
317
- : flexRender(
318
- header.column.columnDef.header,
319
- header.getContext()
320
- )}
321
- </Table.Header>
322
- );
323
- })}
324
- </Table.Row>
325
- );
326
- })}
327
- </Table.Head>
328
- <Table.Body>
329
- {table.getRowModel().rows.map((row) => {
330
- return (
331
- <Table.Row key={row.id}>
332
- {row.getVisibleCells().map((cell) => {
333
- return (
334
- <Table.Cell key={cell.id}>
335
- {flexRender(cell.column.columnDef.cell, cell.getContext())}
336
- </Table.Cell>
337
- );
338
- })}
339
- </Table.Row>
340
- );
341
- })}
342
- </Table.Body>
343
- <Table.Footer>
344
- {table.getFooterGroups().map((footerGroup) => {
345
- return (
346
- <Table.Row key={footerGroup.id}>
347
- {footerGroup.headers.map((header) => {
348
- return (
349
- <Table.Header key={header.id}>
350
- {header.isPlaceholder
351
- ? null
352
- : flexRender(
353
- header.column.columnDef.footer,
354
- header.getContext()
355
- )}
356
- </Table.Header>
357
- );
358
- })}
359
- </Table.Row>
360
- );
361
- })}
362
- </Table.Footer>
363
- </Table>
364
- );
365
- };
211
+ const table = useReactTable({
212
+ data,
213
+ columns,
214
+ getCoreRowModel: getCoreRowModel(),
215
+ });
216
+
217
+ <Table>
218
+ <Table.Head>
219
+ {table.getHeaderGroups().map((headerGroup) => (
220
+ <Table.Row key={headerGroup.id}>
221
+ {headerGroup.headers.map((header) => (
222
+ <Table.Header key={header.id}>
223
+ {flexRender(header.column.columnDef.header, header.getContext())}
224
+ </Table.Header>
225
+ ))}
226
+ </Table.Row>
227
+ ))}
228
+ </Table.Head>
229
+ <Table.Body>
230
+ {table.getRowModel().rows.map((row) => (
231
+ <Table.Row key={row.id}>
232
+ {row.getVisibleCells().map((cell) => (
233
+ <Table.Cell key={cell.id}>
234
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
235
+ </Table.Cell>
236
+ ))}
237
+ </Table.Row>
238
+ ))}
239
+ </Table.Body>
240
+ </Table>;
241
+ ```
242
+
243
+ ### Tabs
244
+
245
+ Tab navigation with content panels. [📖 Docs](https://storybook.ttoss.dev/?path=/docs/components-tabs--docs)
246
+
247
+ ```tsx
248
+ import { Tabs } from '@ttoss/components/Tabs';
249
+
250
+ <Tabs>
251
+ <Tabs.TabList>
252
+ <Tabs.Tab>Tab 1</Tabs.Tab>
253
+ <Tabs.Tab>Tab 2</Tabs.Tab>
254
+ </Tabs.TabList>
255
+ <Tabs.TabContent>
256
+ <Tabs.TabPanel>Content 1</Tabs.TabPanel>
257
+ <Tabs.TabPanel>Content 2</Tabs.TabPanel>
258
+ </Tabs.TabContent>
259
+ </Tabs>;
260
+ ```
261
+
262
+ ### Toast
263
+
264
+ Toast notification system. [📖 Docs](https://storybook.ttoss.dev/?path=/docs/components-toast--docs)
265
+
266
+ ```tsx
267
+ import { Toast } from '@ttoss/components/Toast';
268
+
269
+ <Toast
270
+ message="Success message"
271
+ type="success"
272
+ isOpen={isOpen}
273
+ onClose={() => setIsOpen(false)}
274
+ />;
275
+ ```
276
+
277
+ ```
278
+
366
279
  ```
@@ -0,0 +1,48 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+
4
+ type UploadResult = {
5
+ url: string;
6
+ id: string;
7
+ };
8
+ type FileUploadState = {
9
+ file: File;
10
+ status: 'pending' | 'uploading' | 'completed' | 'error';
11
+ progress?: number;
12
+ result?: UploadResult;
13
+ error?: Error;
14
+ };
15
+ type OnUpload = (file: File, onProgress?: (progress: number) => void) => Promise<UploadResult>;
16
+ type OnUploadStart = (file: File) => void;
17
+ type OnUploadProgress = (file: File, progress: number) => void;
18
+ type OnUploadComplete = (file: File, result: UploadResult) => void;
19
+ type OnUploadError = (file: File, error: Error) => void;
20
+ type OnFilesChange = (files: FileUploadState[]) => void;
21
+ type OnRemoveFile = (file: FileUploadState, index: number) => void;
22
+ type FileUploaderProps = {
23
+ onUpload: OnUpload;
24
+ onUploadStart?: OnUploadStart;
25
+ onUploadProgress?: OnUploadProgress;
26
+ onUploadComplete?: OnUploadComplete;
27
+ onUploadError?: OnUploadError;
28
+ onFilesChange?: OnFilesChange;
29
+ onRemoveFile?: OnRemoveFile;
30
+ accept?: string;
31
+ multiple?: boolean;
32
+ maxSize?: number;
33
+ maxFiles?: number;
34
+ disabled?: boolean;
35
+ autoUpload?: boolean;
36
+ retryAttempts?: number;
37
+ placeholder?: string;
38
+ error?: string;
39
+ children?: React.ReactNode;
40
+ showFileList?: boolean;
41
+ FileListComponent?: (props: {
42
+ files: FileUploadState[];
43
+ onRemoveFile: (index: number) => void;
44
+ }) => React.ReactNode;
45
+ };
46
+ declare const FileUploader: ({ onUpload, onUploadStart, onUploadProgress, onUploadComplete, onUploadError, onFilesChange, onRemoveFile, accept, multiple, maxSize, maxFiles, disabled, autoUpload, retryAttempts, placeholder, error, children, showFileList, FileListComponent, }: FileUploaderProps) => react_jsx_runtime.JSX.Element;
47
+
48
+ export { type FileUploadState, FileUploader, type FileUploaderProps, type OnFilesChange, type OnRemoveFile, type OnUpload, type OnUploadComplete, type OnUploadError, type OnUploadProgress, type OnUploadStart, type UploadResult };
@@ -1 +1,2 @@
1
1
  export { JsonEditor } from 'json-edit-react';
2
+ import 'react-json-view-lite';