groovinads-ui 1.9.98 → 1.9.99

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
@@ -13,6 +13,9 @@
13
13
 
14
14
  The library includes the following components:
15
15
 
16
+ - [Context Providers](#context-providers):
17
+ - [ThemeProvider](#themeprovider): For managing application theme (light, dark, system).
18
+
16
19
  - [Buttons](#buttons):
17
20
  - [Button](#button): For user actions.
18
21
 
@@ -25,20 +28,24 @@ The library includes the following components:
25
28
 
26
29
  - [Inputs](#inputs):
27
30
  - [Checkbox](#checkbox): For multiple option selections.
31
+ - [Dropzone](#dropzone): For drag-and-drop file uploads with progress tracking.
28
32
  - [Input](#input): For user data entry.
29
33
  - [InputChip](#inputChip): For dynamically managing and displaying keywords.
30
- - [InputEmail](#inputEmail): For managing email lists, including adding, displaying, and deleting email addresses.
34
+ - [InputEmail](#inputEmail): For managing email lists.
31
35
  - [Radio](#radio): For exclusive selections.
36
+ - [Slider](#slider): For selecting values using a slider control.
32
37
  - [Switch](#switch): For toggle states.
33
38
  - [Textarea](#textarea): For multiline text input.
34
39
 
35
40
  - [Labels](#labels):
36
41
  - [Alert](#alert): For displaying alerts.
42
+ - [BlockIcon](#blockicon): For displaying icons with block containers.
37
43
  - [Card](#card): For containing and grouping related content.
38
44
  - [EditableContent](#editablecontent): For inline editing of text content.
39
45
  - [Icon](#icon): For displaying icons.
40
46
  - [LoginSource](#loginsource): For show icons of login sources.
41
47
  - [PillComponent](#pillcomponent): For displaying information.
48
+ - [ProgressBar](#progressbar): For displaying progress indicators.
42
49
  - [Spinner](#spinner): For loading animations.
43
50
  - [StatusIcon](#statusicon): For displaying status icons.
44
51
 
@@ -166,6 +173,150 @@ yarn add groovinads-ui
166
173
 
167
174
  Here are examples of how to use the components included in the Groovinads UI library:
168
175
 
176
+ ## Context Providers
177
+
178
+ ### ThemeProvider
179
+
180
+ The `ThemeProvider` manages the application's theme system, supporting three modes: `'light'`, `'dark'`, and `'default'` (system preference).
181
+
182
+ #### Features
183
+
184
+ - **Automatic theme persistence**: Theme preference is saved to browser cookies
185
+ - **Cross-subdomain sharing**: Optional `rootDomain` prop enables theme sharing across subdomains
186
+ - **System integration**: `'default'` mode respects the operating system theme
187
+ - **SSR-safe**: Works correctly with Server-Side Rendering
188
+ - **Auto-sync with DOM**: Automatically sets `data-theme` attribute on `document.documentElement`
189
+
190
+ #### Basic Usage
191
+
192
+ Wrap your application root with the `ThemeProvider`:
193
+
194
+ ```jsx
195
+ import React from 'react';
196
+ import ReactDOM from 'react-dom/client';
197
+ import { ThemeProvider } from 'groovinads-ui';
198
+ import App from './App';
199
+
200
+ ReactDOM.createRoot(document.getElementById('root')).render(
201
+ <React.StrictMode>
202
+ <ThemeProvider>
203
+ <App />
204
+ </ThemeProvider>
205
+ </React.StrictMode>
206
+ );
207
+ ```
208
+
209
+ #### Cross-Subdomain Theme Sharing
210
+
211
+ By default, theme preferences are automatically shared across all Groovinads subdomains (`.groovinads.com`):
212
+
213
+ ```jsx
214
+ import React from 'react';
215
+ import ReactDOM from 'react-dom/client';
216
+ import { ThemeProvider } from 'groovinads-ui';
217
+ import App from './App';
218
+
219
+ // Default behavior - shares theme across all *.groovinads.com subdomains
220
+ ReactDOM.createRoot(document.getElementById('root')).render(
221
+ <React.StrictMode>
222
+ <ThemeProvider>
223
+ <App />
224
+ </ThemeProvider>
225
+ </React.StrictMode>
226
+ );
227
+ ```
228
+
229
+ **Custom Domain or Disable Cross-Subdomain:**
230
+
231
+ ```jsx
232
+ // Custom domain
233
+ <ThemeProvider rootDomain=".example.com">
234
+ <App />
235
+ </ThemeProvider>
236
+
237
+ // Disable cross-subdomain (current domain only)
238
+ <ThemeProvider rootDomain={null}>
239
+ <App />
240
+ </ThemeProvider>
241
+ ```
242
+
243
+ **Important Notes:**
244
+ - **Default**: `.groovinads.com` - theme is shared across all subdomains
245
+ - The `rootDomain` should start with a dot (`.`) to work across all subdomains
246
+ - On localhost, the domain attribute is automatically omitted for local development
247
+ - Cookie is stored with a 1-year expiration and `SameSite=Lax` for security
248
+
249
+ #### Using the useTheme Hook
250
+
251
+ Access and control the theme from any component:
252
+
253
+ ```jsx
254
+ import React from 'react';
255
+ import { useTheme } from 'groovinads-ui';
256
+
257
+ function ThemeSelector() {
258
+ const { theme, setTheme, isLight, isDark, isDefault } = useTheme();
259
+
260
+ return (
261
+ <div>
262
+ <p>Current theme: {theme}</p>
263
+ <button onClick={() => setTheme('light')}>Light</button>
264
+ <button onClick={() => setTheme('dark')}>Dark</button>
265
+ <button onClick={() => setTheme('default')}>System</button>
266
+ </div>
267
+ );
268
+ }
269
+ ```
270
+
271
+ #### Props
272
+
273
+ | Prop | Type | Default | Description |
274
+ |------|------|---------|-------------|
275
+ | `children` | `ReactNode` | **required** | Application content to wrap with theme context |
276
+ | `initialTheme` | `'light' \| 'dark' \| 'default'` | `'default'` | Initial theme value (overrides cookie on mount) |
277
+ | `rootDomain` | `string` | `'.groovinads.com'` | Root domain for cross-subdomain cookies. Theme is automatically shared across all subdomains. Set to `null` to disable cross-subdomain sharing. |
278
+
279
+ #### useTheme Return Value
280
+
281
+ | Property | Type | Description |
282
+ |----------|------|-------------|
283
+ | `theme` | `'light' \| 'dark' \| 'default'` | Current active theme |
284
+ | `setTheme` | `(theme: ThemeMode) => void` | Function to change theme |
285
+ | `isLight` | `boolean` | `true` if theme is `'light'` |
286
+ | `isDark` | `boolean` | `true` if theme is `'dark'` |
287
+ | `isDefault` | `boolean` | `true` if theme is `'default'` |
288
+
289
+ #### Implementation Notes
290
+
291
+ - **Cookie Storage**: Theme preference is stored in a cookie named `theme_pref`
292
+ - When theme is `'default'`: The `data-theme` attribute is removed from the HTML element and the cookie is deleted
293
+ - When theme is `'light'` or `'dark'`: The `data-theme` attribute is set on `document.documentElement` and the preference is saved to a cookie with 1-year expiration
294
+ - **Cross-Subdomain**: When `rootDomain` is provided, the cookie is set with the `domain` attribute to share across subdomains
295
+ - **Localhost Handling**: On localhost, the domain attribute is automatically omitted to work in local development
296
+ - **SSR-Safe**: The component checks for `window` existence before accessing `document.cookie` or `document`
297
+ - **Security**: Cookies are set with `SameSite=Lax` and `path=/` for security and accessibility
298
+
299
+ #### Integration with Navbar
300
+
301
+ The `Navbar` component with `showUserMenu={true}` automatically includes a theme selector in the user dropdown menu when wrapped with `ThemeProvider`.
302
+
303
+ ```jsx
304
+ import React from 'react';
305
+ import { ThemeProvider, Navbar } from 'groovinads-ui';
306
+
307
+ function App() {
308
+ return (
309
+ <ThemeProvider>
310
+ <Navbar
311
+ showUserMenu={true}
312
+ userData={userData}
313
+ />
314
+ {/* Rest of your app */}
315
+ </ThemeProvider>
316
+ );
317
+ }
318
+ ```
319
+
169
320
  ## Buttons
170
321
 
171
322
  ### Button
@@ -829,6 +980,199 @@ export default ExampleInputChip;
829
980
  | `textError` | String | No | n/a | 'You must enter a valid email address' | Allows adding custom error text when entering an invalid email address. |
830
981
  | `titleList` | String | No | n/a | 'Added emails' | Allows adding a custom text that will be shown as the title of the email list. |
831
982
 
983
+ ### Dropzone
984
+
985
+ A generic drag-and-drop file upload component with progress tracking, validation, and full control via props. Perfect for image, video, and document uploads.
986
+
987
+ ```jsx
988
+ import React, { useState } from 'react';
989
+ import { Dropzone } from 'groovinads-ui';
990
+
991
+ const ExampleDropzone = () => {
992
+ const [files, setFiles] = useState([]);
993
+ const [showError, setShowError] = useState(false);
994
+ const [errorMessage, setErrorMessage] = useState('');
995
+
996
+ // Your upload function that returns a Promise
997
+ const handleUpload = async (file) => {
998
+ const formData = new FormData();
999
+ formData.append('file', file);
1000
+
1001
+ const response = await fetch('/api/upload', {
1002
+ method: 'POST',
1003
+ body: formData,
1004
+ });
1005
+
1006
+ const result = await response.json();
1007
+
1008
+ // Return file metadata
1009
+ return {
1010
+ id: result.id,
1011
+ url: result.url,
1012
+ thumbnail: result.thumbnail,
1013
+ name: file.name,
1014
+ size: file.size,
1015
+ type: file.type,
1016
+ width: result.width,
1017
+ height: result.height,
1018
+ };
1019
+ };
1020
+
1021
+ const handleSuccess = (result) => {
1022
+ // Add to files list
1023
+ setFiles((prev) => [...prev, result]);
1024
+ console.log('File uploaded:', result);
1025
+ };
1026
+
1027
+ const handleRemove = (file) => {
1028
+ // Remove from files list
1029
+ setFiles((prev) => prev.filter((f) => f.id !== file.id));
1030
+ };
1031
+
1032
+ const handleError = (error, file) => {
1033
+ console.error('Upload error:', error);
1034
+ setShowError(true);
1035
+ setErrorMessage('Upload failed. Please try again.');
1036
+ };
1037
+
1038
+ const handleValidate = () => {
1039
+ if (files.length === 0) {
1040
+ setShowError(true);
1041
+ setErrorMessage('You must upload at least one file');
1042
+ }
1043
+ };
1044
+
1045
+ return (
1046
+ <Dropzone
1047
+ // Data
1048
+ files={files}
1049
+
1050
+ // Events
1051
+ onUpload={handleUpload}
1052
+ onSuccess={handleSuccess}
1053
+ onRemove={handleRemove}
1054
+ onError={handleError}
1055
+
1056
+ // Configuration
1057
+ accept={{ 'image/*': ['.png', '.jpg', '.jpeg'] }}
1058
+ maxSize={20 * 1024 * 1024} // 20MB
1059
+ multiple={true}
1060
+
1061
+ // Validation
1062
+ showError={showError}
1063
+ requiredText={errorMessage}
1064
+
1065
+ // UI Texts
1066
+ title="Drag and drop your image here"
1067
+ subtitle="PNG, JPG up to 20MB"
1068
+ buttonText="Browse file…"
1069
+ />
1070
+ );
1071
+ };
1072
+
1073
+ export default ExampleDropzone;
1074
+ ```
1075
+
1076
+ #### Props
1077
+
1078
+ | Property | Type | Required | Default | Description |
1079
+ | --------------------- | -------- | -------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
1080
+ | **Data** | | | | |
1081
+ | `files` | Array | No | `[]` | Array of uploaded files. Each file should have `id`, `url`, `name`, `size`, etc. |
1082
+ | **Events** | | | | |
1083
+ | `onError` | Function | No | n/a | Called on upload or validation errors. Receives `(error, file)`. |
1084
+ | `onRemove` | Function | No | n/a | Called when user clicks remove button. Receives the file object. |
1085
+ | `onSuccess` | Function | No | n/a | Called when upload succeeds. Receives the result from `onUpload`. |
1086
+ | `onUpload` | Function | **Yes** | n/a | Async function that handles file upload. Must return a Promise with file metadata `{ id, url, name, size, ... }`. |
1087
+ | **Configuration** | | | | |
1088
+ | `accept` | Object | No | n/a | Accepted file types in react-dropzone format: `{ 'image/*': ['.png', '.jpg'] }`. |
1089
+ | `disabled` | Boolean | No | `false` | Disable the dropzone. |
1090
+ | `maxSize` | Number | No | `20971520` (20MB) | Maximum file size in bytes. |
1091
+ | `multiple` | Boolean | No | `true` | Allow multiple file uploads. |
1092
+ | `validator` | Function | No | n/a | Custom file validator. Return `{ code, message }` for errors, `null` for valid files. |
1093
+ | **Validation** | | | | |
1094
+ | `requiredText` | String | No | n/a | Error message displayed when `showError` is `true`. Used via the `data-error` attribute. |
1095
+ | `showError` | Boolean | No | `false` | If `true`, displays the dropzone in error state with `not-validated` class. Controlled externally for validation. |
1096
+ | **UI Texts** | | | | |
1097
+ | `buttonText` | String | No | `'Browse file…'` | Browse button text. |
1098
+ | `orLabel` | String | No | `'or'` | Text for the separator between dropzone and browse button. |
1099
+ | `subtitle` | String | No | n/a | Subtitle text (e.g., file size limits). |
1100
+ | `title` | String | No | `'Drag and drop your files here'` | Main dropzone title. |
1101
+ | `uploadingText` | String | No | `'Uploading…'` | Text shown during upload. |
1102
+ | **UI Customization** | | | | |
1103
+ | `className` | String | No | `''` | Additional CSS classes. |
1104
+ | `icon` | String | No | `'cloud-arrow-up'` | FontAwesome icon name. |
1105
+ | `iconScale` | Number | No | `2` | Icon size scale. Options: `0.7`, `1`, `2`, `3`, `4`. |
1106
+ | **Format Validation** | | | | |
1107
+ | `formatLabel` | Function | No | n/a | Function to generate format label for display. Receives file, returns string. |
1108
+ | `formatValidator` | Function | No | n/a | Function to validate file format. Receives file, returns boolean. |
1109
+
1110
+ #### File Object Structure
1111
+
1112
+ The component normalizes file objects to ensure consistency. Your `onUpload` function should return:
1113
+
1114
+ ```javascript
1115
+ {
1116
+ id: string | number, // Unique identifier
1117
+ url: string, // File URL (required)
1118
+ thumbnail?: string, // Thumbnail URL (optional)
1119
+ name: string, // File name
1120
+ size: number, // File size in bytes
1121
+ type?: string, // MIME type
1122
+ width?: number, // Image/video width
1123
+ height?: number, // Image/video height
1124
+ duration?: number, // Video duration
1125
+ }
1126
+ ```
1127
+
1128
+ The component also supports legacy formats with fields like `media`, `file_description`, `file_name`, `file_size`, etc.
1129
+
1130
+ #### Examples
1131
+
1132
+ **Image Upload**
1133
+ ```jsx
1134
+ <Dropzone
1135
+ files={images}
1136
+ onUpload={uploadImage}
1137
+ onSuccess={(result) => setImages([...images, result])}
1138
+ onRemove={(file) => setImages(images.filter(f => f.id !== file.id))}
1139
+ accept={{ 'image/*': ['.png', '.jpg', '.jpeg'] }}
1140
+ maxSize={20 * 1024 * 1024}
1141
+ title="Upload your creative"
1142
+ subtitle="PNG, JPG up to 20MB"
1143
+ />
1144
+ ```
1145
+
1146
+ **Video Upload (Single File)**
1147
+ ```jsx
1148
+ <Dropzone
1149
+ files={video ? [video] : []}
1150
+ onUpload={uploadVideo}
1151
+ onSuccess={setVideo}
1152
+ onRemove={() => setVideo(null)}
1153
+ accept={{ 'video/*': ['.mp4', '.mov'] }}
1154
+ maxSize={100 * 1024 * 1024}
1155
+ multiple={false}
1156
+ icon="video"
1157
+ title="Upload your video"
1158
+ subtitle="MP4, MOV up to 100MB"
1159
+ />
1160
+ ```
1161
+
1162
+ **Document Upload with Format Validation**
1163
+ ```jsx
1164
+ <Dropzone
1165
+ files={documents}
1166
+ onUpload={uploadDocument}
1167
+ onSuccess={(result) => setDocuments([...documents, result])}
1168
+ accept={{ 'application/pdf': ['.pdf'] }}
1169
+ formatValidator={(file) => file.size < 10 * 1024 * 1024}
1170
+ formatLabel={(file) => `${(file.size / 1024).toFixed(0)} KB`}
1171
+ title="Upload PDF document"
1172
+ subtitle="PDF only, max 10MB"
1173
+ />
1174
+ ```
1175
+
832
1176
  ### Radio
833
1177
 
834
1178
  ```jsx
@@ -1307,37 +1651,144 @@ import { ModalComponent } from 'groovinads-ui';
1307
1651
 
1308
1652
  ## Navigation
1309
1653
 
1310
- ### Accordion
1654
+ ### AccordionComponent
1655
+
1656
+ A flexible accordion component built on top of react-bootstrap Accordion. Headers render as `<div>` elements (not buttons) to allow nesting interactive elements like buttons without HTML validation conflicts.
1657
+
1311
1658
  ```jsx
1312
1659
  import React from 'react';
1313
- import { Accordion } from 'react-bootstrap';
1314
- import { AccordionComponent } from 'groovinads-ui';
1315
-
1316
- <AccordionComponent defaultActiveKey="0">
1317
- <Accordion.Item eventKey="0">
1318
- <Accordion.Header>Accordion Item #1</Accordion.Header>
1319
- <Accordion.Body>
1320
- This is the first item's accordion body.
1321
- </Accordion.Body>
1322
- </Accordion.Item>
1323
- <Accordion.Item eventKey="1">
1324
- <Accordion.Header>Accordion Item #2</Accordion.Header>
1325
- <Accordion.Body>
1326
- This is the second item's accordion body.
1327
- </Accordion.Body>
1328
- </Accordion.Item>
1660
+ import { AccordionComponent, AccordionItem } from 'groovinads-ui';
1661
+
1662
+ // Basic usage
1663
+ <AccordionComponent defaultActiveKey="0" iconPosition="start">
1664
+ <AccordionItem eventKey="0" header="Section 1">
1665
+ This is the first item's accordion body.
1666
+ </AccordionItem>
1667
+ <AccordionItem eventKey="1" header="Section 2">
1668
+ This is the second item's accordion body.
1669
+ </AccordionItem>
1670
+ <AccordionItem eventKey="2" header="Section 3">
1671
+ This is the third item's accordion body.
1672
+ </AccordionItem>
1329
1673
  </AccordionComponent>
1330
1674
  ```
1331
1675
 
1332
- | Property | Type | Required | Options | Default | Description |
1333
- | ----------------- | ----------------- | -------- | ----------------- | ------- | ---------------------------------------------------------------------------------------------------------- |
1334
- | `activeKey` | String / String[] | No | n/a | n/a | Currently active accordion item key(s). String for single, array for multiple when alwaysOpen is true. |
1335
- | `alwaysOpen` | Boolean | No | `true` `false` | `false` | Allow multiple accordion items to stay open at the same time. |
1336
- | `caretPosition` | String | No | `start` `end` | `start` | Position of the caret icon. `start` places it before the content, `end` places it after. |
1337
- | `children` | Node | Yes | n/a | n/a | Accordion.Item components from react-bootstrap. |
1338
- | `className` | String | No | n/a | `''` | Additional CSS classes. |
1339
- | `defaultActiveKey`| String / String[] | No | n/a | n/a | Default active key for uncontrolled mode. String for single, array for multiple when alwaysOpen is true. |
1340
- | `size` | String | No | `sm` `md` `lg` | `md` | Size variant for the accordion. |
1676
+ **Advanced usage with complex headers:**
1677
+
1678
+ ```jsx
1679
+ import React from 'react';
1680
+ import { AccordionComponent, AccordionItem } from 'groovinads-ui';
1681
+ import { Button } from 'groovinads-ui';
1682
+
1683
+ const AdvancedAccordion = () => {
1684
+ const handleEdit = (e, id) => {
1685
+ e.stopPropagation(); // Prevent accordion toggle
1686
+ console.log('Edit clicked for:', id);
1687
+ };
1688
+
1689
+ return (
1690
+ <AccordionComponent defaultActiveKey="0" iconPosition="start">
1691
+ <AccordionItem
1692
+ eventKey="0"
1693
+ header={
1694
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}>
1695
+ <div>
1696
+ <strong>Campaign Name</strong>
1697
+ <div style={{ fontSize: '12px', color: '#888' }}>
1698
+ Budget: $50,000 | Status: Active
1699
+ </div>
1700
+ </div>
1701
+ <Button size="sm" onClick={(e) => handleEdit(e, 1)}>
1702
+ Edit
1703
+ </Button>
1704
+ </div>
1705
+ }
1706
+ >
1707
+ <p>Campaign details go here...</p>
1708
+ </AccordionItem>
1709
+ <AccordionItem
1710
+ eventKey="1"
1711
+ header={
1712
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}>
1713
+ <div>
1714
+ <strong>Another Campaign</strong>
1715
+ <div style={{ fontSize: '12px', color: '#888' }}>
1716
+ Budget: $35,000 | Status: Scheduled
1717
+ </div>
1718
+ </div>
1719
+ <Button size="sm" onClick={(e) => handleEdit(e, 2)}>
1720
+ Edit
1721
+ </Button>
1722
+ </div>
1723
+ }
1724
+ >
1725
+ <p>More campaign details...</p>
1726
+ </AccordionItem>
1727
+ </AccordionComponent>
1728
+ );
1729
+ };
1730
+ ```
1731
+
1732
+ **Props for AccordionComponent:**
1733
+
1734
+ | Property | Type | Required | Options | Default | Description |
1735
+ | ----------------- | ----------------- | -------- | ------------- | ------- | ---------------------------------------------------------------------------------------------------------- |
1736
+ | `activeKey` | String / String[] | No | n/a | n/a | Currently active key(s) for controlled mode. Use with state to control which items are open. String for single item, array for multiple when alwaysOpen is true. |
1737
+ | `alwaysOpen` | Boolean | No | `true` `false`| `false` | If true, allows multiple accordion items to stay open at the same time. If false, opening one closes others. |
1738
+ | `children` | Node | Yes | n/a | n/a | AccordionItem components that define the accordion sections. |
1739
+ | `className` | String | No | n/a | `''` | Additional CSS classes to apply to the accordion container. |
1740
+ | `defaultActiveKey`| String / String[] | No | n/a | n/a | Default active key(s) for uncontrolled mode. Use string for single item, array for multiple when alwaysOpen is true. |
1741
+ | `iconPosition` | String | No | `start` `end` | `start` | Position of the caret icon. `start` places it before the content (left), `end` places it after (right). |
1742
+ | `onSelect` | Function | No | n/a | n/a | Callback fired when the active item changes. Receives `(eventKey: string \| string[] \| null) => void`. Use with `activeKey` for controlled mode. |
1743
+ | `size` | String | No | `sm` `md` `lg`| `md` | Size variant for the accordion. `sm` for compact, `md` for default, `lg` for larger spacing. |
1744
+
1745
+ **Props for AccordionItem:**
1746
+
1747
+ | Property | Type | Required | Description |
1748
+ | ---------- | ------ | -------- | ---------------------------------------------------------------------------- |
1749
+ | `eventKey` | String | Yes | Unique identifier for this accordion item. Used to control which item is open. |
1750
+ | `children` | Node | Yes | Content to display in the accordion body when the item is expanded. |
1751
+ | `header` | Node | Yes | Content to display in the accordion header. Can be text, JSX, or complex components. |
1752
+
1753
+ **Controlled Mode Example:**
1754
+
1755
+ ```jsx
1756
+ import React, { useState } from 'react';
1757
+ import { AccordionComponent, AccordionItem } from 'groovinads-ui';
1758
+
1759
+ const ControlledAccordion = () => {
1760
+ const [activeKey, setActiveKey] = useState('0');
1761
+
1762
+ const handleSelect = (eventKey) => {
1763
+ console.log('Selected:', eventKey);
1764
+ setActiveKey(eventKey);
1765
+ };
1766
+
1767
+ return (
1768
+ <AccordionComponent activeKey={activeKey} onSelect={handleSelect}>
1769
+ <AccordionItem eventKey="0" header="Section 1">
1770
+ Content 1
1771
+ </AccordionItem>
1772
+ <AccordionItem eventKey="1" header="Section 2">
1773
+ Content 2
1774
+ </AccordionItem>
1775
+ <AccordionItem eventKey="2" header="Section 3">
1776
+ Content 3
1777
+ </AccordionItem>
1778
+ </AccordionComponent>
1779
+ );
1780
+ };
1781
+ ```
1782
+
1783
+ **Key Features:**
1784
+
1785
+ - **Headers as divs**: Headers render as `<div>` elements instead of `<button>` elements, allowing you to nest interactive elements (buttons, links, inputs) inside headers without HTML validation errors or click event conflicts.
1786
+ - **Flexible headers**: Headers accept any React node, so you can create complex, multi-line headers with custom layouts, icons, badges, and buttons.
1787
+ - **Icon positioning**: Caret icon can be positioned at the start (left) or end (right) of the header.
1788
+ - **Multiple open items**: Use `alwaysOpen={true}` to allow multiple sections to be expanded simultaneously.
1789
+ - **Controlled/Uncontrolled**: Works in both controlled (with `activeKey` and `onSelect` props) and uncontrolled (`defaultActiveKey`) modes. In controlled mode, use `activeKey` with `onSelect` to manage state externally.
1790
+
1791
+ **Note:** When nesting buttons or other interactive elements in headers, make sure to call `e.stopPropagation()` in their click handlers to prevent the accordion from toggling when clicking the nested elements.
1341
1792
 
1342
1793
  ### Aside
1343
1794
  ```jsx
@@ -1368,15 +1819,39 @@ import { Navbar } from 'groovinads-ui';
1368
1819
  const NavbarComponent = () => {
1369
1820
  const [show, setShow] = useState(false);
1370
1821
 
1822
+ // Mock data - en una app real, esto vendría de tu AuthContext o similar
1823
+ const applications = [
1824
+ {
1825
+ id_application: 1,
1826
+ application_name: 'Dashboard',
1827
+ application_url: '/dashboard',
1828
+ application_icon: 'grid-2',
1829
+ favorite: true,
1830
+ },
1831
+ // ... más aplicaciones
1832
+ ];
1833
+
1834
+ const userData = {
1835
+ auth_data: {
1836
+ oauth_username: 'John Doe',
1837
+ oauth_picture: 'https://example.com/avatar.jpg',
1838
+ email: 'john.doe@example.com',
1839
+ oauth_name: 'Google',
1840
+ },
1841
+ };
1842
+
1371
1843
  return (
1372
1844
  <div>
1373
1845
  <Button onClick={() => setShow((prev) => !prev)}>Toggle Sidebar</Button>
1374
1846
  <Navbar
1375
- logoUrl='https://ui.groovinads.com/assets/groovinads-logo.svg' // custom url
1847
+ logoUrl='https://ui.groovinads.com/assets/groovinads-logo.svg'
1376
1848
  showDeckMenu={true}
1377
1849
  showUserMenu={true}
1378
1850
  show={show}
1379
1851
  setShow={setShow}
1852
+ // Nuevas props: pasar datos para evitar fetches internos
1853
+ applications={applications}
1854
+ userData={userData}
1380
1855
  >
1381
1856
  <div>Custom Content</div>
1382
1857
  </Navbar>
@@ -1387,15 +1862,72 @@ const NavbarComponent = () => {
1387
1862
  export default NavbarComponent;
1388
1863
  ```
1389
1864
 
1390
- | Property | Type | Required | Options | Default | Description |
1391
- | ----------------- | -------- | -------- | -------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------ |
1392
- | `children` | Node | No | n/a | n/a | Allows inserting custom content within the Navbar. |
1393
- | `logoUrl` | String | No | n/a | Groovinads logo | Accepts a URL to customize the logo image. If empty, show the Groovinads logo. |
1394
- | `logoUrlMobile` | String | No | n/a | Groovinads logo | Accepts a URL for the mobile version of the logo. If not provided, uses `logoUrl` on mobile devices. |
1395
- | `setShow` | Function | No | n/a | n/a | Function to toggle the visibility state between visible and hidden. |
1396
- | `show` | Boolean | No | `true` `false` | n/a | Controls the visibility of the sidebar. If `true`, the sidebar is displayed; if `false`, it is hidden. |
1397
- | `showDeckMenu` | Boolean | No | `true` `false` | `false` | Controls the visibility of the deck menu in the navbar. If `true`, it is displayed; if `false`, it is hidden. |
1398
- | `showUserMenu` | Boolean | No | `true` `false` | `false` | Controls the visibility of the user menu. If `true`, the user menu is shown; if `false`, it is hidden. |
1865
+ | Property | Type | Required | Options | Default | Description |
1866
+ | -------------------- | -------- | -------- | -------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1867
+ | `applications` | Array | No | n/a | n/a | Array of application objects for the deck menu. When provided, prevents internal API calls. Each object should have: `id_application`, `application_name`, `application_url`, `application_icon`, `favorite`. The component automatically maps `application_name` → `app_name` and `application_icon` → `app_icon_sm` internally. |
1868
+ | `applicationsLoading`| Boolean | No | `true` `false` | `false` | Loading state for applications data. Shows skeleton loader when `true`. |
1869
+ | `children` | Node | No | n/a | n/a | Allows inserting custom content within the Navbar. |
1870
+ | `favoritesTitle` | String | No | n/a | `'Favorites'` | Title for the favorites section in the deck menu. Allows customization of the favorites label. |
1871
+ | `logoUrl` | String | No | n/a | Groovinads logo | Accepts a URL to customize the logo image. If empty, show the Groovinads logo. |
1872
+ | `logoUrlMobile` | String | No | n/a | Groovinads logo | Accepts a URL for the mobile version of the logo. If not provided, uses `logoUrl` on mobile devices. |
1873
+ | `onApplicationClick` | Function | No | n/a | n/a | Callback fired when user clicks an application. Receives the application object. If not provided, opens application in new tab (default behavior). |
1874
+ | `onFavoriteToggle` | Function | No | n/a | n/a | Callback fired when user toggles favorite status. Receives `(applicationId, isFavorite)`. Must return a Promise. If not provided, makes internal API call (default behavior). |
1875
+ | `setShow` | Function | No | n/a | n/a | Function to toggle the visibility state of the sidebar. When provided (along with `show`), the sidebar toggle button is rendered. When omitted, the button is hidden. |
1876
+ | `show` | Boolean | No | `true` `false` | n/a | Controls the visibility of the sidebar. When provided (along with `setShow`), the sidebar toggle button is rendered. When omitted, the button is hidden. |
1877
+ | `showDeckMenu` | Boolean | No | `true` `false` | `false` | Controls the visibility of the deck menu in the navbar. If `true`, it is displayed; if `false`, it is hidden. Applications open in a new tab when clicked. |
1878
+ | `showUserMenu` | Boolean | No | `true` `false` | `false` | Controls the visibility of the user menu. If `true`, the user menu is shown; if `false`, it is hidden. |
1879
+ | `userData` | Object | No | n/a | n/a | User data object for the user menu. When provided, prevents internal API calls. Should contain `auth_data` with: `oauth_username`, `oauth_picture`, `email`, `oauth_name`. |
1880
+ | `userDataLoading` | Boolean | No | `true` `false` | `false` | Loading state for user data. Shows skeleton loader when `true`. |
1881
+
1882
+ **⚠️ Important Note on Data Props:**
1883
+ - When `showDeckMenu={true}` is used **without** providing `applications` prop, the component will make an internal API call to `/v2/applications`
1884
+ - When `showUserMenu={true}` is used **without** providing `userData` prop, the component will make an internal API call to `/v2/auth/status`
1885
+ - To avoid duplicate API calls in your app, always provide `applications` and `userData` props from your app's state management (e.g., AuthContext, Redux, etc.)
1886
+
1887
+ **💡 Event Handlers for Custom Navigation:**
1888
+
1889
+ If you need to integrate the Navbar with your application's routing (e.g., react-router) or handle favorites with your own state management, use the event handler props:
1890
+
1891
+ ```jsx
1892
+ import { useNavigate } from 'react-router-dom';
1893
+
1894
+ const NavbarComponent = () => {
1895
+ const navigate = useNavigate();
1896
+ const [applications, setApplications] = useState([...]);
1897
+
1898
+ // Custom navigation handler
1899
+ const handleApplicationClick = (app) => {
1900
+ // Navigate using react-router instead of opening new tab
1901
+ navigate(app.application_url);
1902
+ };
1903
+
1904
+ // Custom favorite toggle handler
1905
+ const handleFavoriteToggle = async (appId, isFavorite) => {
1906
+ // Update in your API
1907
+ await updateFavoriteAPI(appId, isFavorite);
1908
+
1909
+ // Update local state
1910
+ setApplications(prev =>
1911
+ prev.map(app =>
1912
+ app.id_application === appId
1913
+ ? { ...app, favorite: isFavorite }
1914
+ : app
1915
+ )
1916
+ );
1917
+ };
1918
+
1919
+ return (
1920
+ <Navbar
1921
+ showDeckMenu={true}
1922
+ applications={applications}
1923
+ onApplicationClick={handleApplicationClick}
1924
+ onFavoriteToggle={handleFavoriteToggle}
1925
+ />
1926
+ );
1927
+ };
1928
+ ```
1929
+
1930
+ **Note:** If you don't provide these handlers, the component uses default behavior (opens in new tab for clicks, makes internal API call for favorites).
1399
1931
 
1400
1932
  ### Pagination
1401
1933