@diabolic/hangover 0.1.1 → 0.1.2
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 +54 -0
- package/dist/index.cjs.js +74 -33
- package/dist/index.esm.js +74 -33
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -77,6 +77,7 @@ Root provider. All state lives here.
|
|
|
77
77
|
| `darkMode` | `boolean` | `false` | Enable dark mode. Applies `hangoverDropdown--dark` CSS class which overrides all color tokens. |
|
|
78
78
|
| `searchQuery` | `string` | — | Controlled search query. When provided, the internal search state is kept in sync with this value. Use together with `onEvent` (`type: "search"`) to handle changes. |
|
|
79
79
|
| `defaultSearchQuery` | `string` | `""` | Uncontrolled initial search query. Only applied on first render. |
|
|
80
|
+
| `useTranslationFunction` | `(text, payload?) => string` | — | Translation hook. When provided, **every** user-facing string — including built-in defaults — is routed through this function. See [Translation](#translation). |
|
|
80
81
|
| `onEvent` | `(event) => any` | — | Central event handler. See [Events](#events). |
|
|
81
82
|
| `ref` | `React.Ref` | — | Exposes imperative API. See [Imperative API](#imperative-api). |
|
|
82
83
|
| `...rest` | `any` | — | Any additional props (e.g. `data-*`, `className`, `style`) are forwarded to the root `<div>`. |
|
|
@@ -274,6 +275,7 @@ const config = {
|
|
|
274
275
|
defaultGroupExpanded: boolean | 'first',
|
|
275
276
|
hideOnSelection: boolean,
|
|
276
277
|
onEvent: ({ type, payload, prev }) => any,
|
|
278
|
+
useTranslationFunction: (text, payload) => string,
|
|
277
279
|
// ...any extra props are spread onto <Dropdown>
|
|
278
280
|
|
|
279
281
|
// Trigger — required
|
|
@@ -488,6 +490,58 @@ trigger.addEventListener('HO:select', (e) => {
|
|
|
488
490
|
|
|
489
491
|
---
|
|
490
492
|
|
|
493
|
+
## Translation
|
|
494
|
+
|
|
495
|
+
Pass a `useTranslationFunction` to `<Dropdown>` to localize the UI. When
|
|
496
|
+
provided, **every** user-facing string is routed through it — including
|
|
497
|
+
built-in defaults like `"Search"`, `"Select all"`, `"No results"` and
|
|
498
|
+
`"Nothing to show here"`, as well as your own labels (group names, item
|
|
499
|
+
labels, nav items, section/panel titles).
|
|
500
|
+
|
|
501
|
+
The function receives the original string and returns the translated one:
|
|
502
|
+
|
|
503
|
+
```jsx
|
|
504
|
+
const translations = {
|
|
505
|
+
'Search': 'Ara',
|
|
506
|
+
'Select all': 'Tümünü seç',
|
|
507
|
+
'No results': 'Sonuç yok',
|
|
508
|
+
'Nothing to show here': 'Gösterilecek bir şey yok',
|
|
509
|
+
'Fruits': 'Meyveler',
|
|
510
|
+
'Apple': 'Elma',
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
<Dropdown useTranslationFunction={(text) => translations[text] ?? text}>
|
|
514
|
+
{/* ... */}
|
|
515
|
+
</Dropdown>
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Dynamic values
|
|
519
|
+
|
|
520
|
+
When a string contains dynamic values, they are passed as a payload object
|
|
521
|
+
in the **second argument** instead of being concatenated into the string.
|
|
522
|
+
The default string uses `{placeholder}` tokens that map to payload keys:
|
|
523
|
+
|
|
524
|
+
```jsx
|
|
525
|
+
<Dropdown
|
|
526
|
+
useTranslationFunction={(text, payload) => {
|
|
527
|
+
// e.g. text = "{label} — {action}", payload = { label: "Fruits", action: "collapse" }
|
|
528
|
+
if (text === '{label} — {action}') {
|
|
529
|
+
return `${payload.label}: ${payload.action === 'collapse' ? 'kapat' : 'aç'}`
|
|
530
|
+
}
|
|
531
|
+
return translations[text] ?? text
|
|
532
|
+
}}
|
|
533
|
+
>
|
|
534
|
+
{/* ... */}
|
|
535
|
+
</Dropdown>
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
If no `useTranslationFunction` is provided, strings render as-is and any
|
|
539
|
+
`{placeholder}` tokens are interpolated from the payload automatically.
|
|
540
|
+
|
|
541
|
+
> Also available via [`fromConfig`](#fromconfig--config-driven-rendering) as `useTranslationFunction`.
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
491
545
|
## Imperative API
|
|
492
546
|
|
|
493
547
|
Attach a `ref` to `<Dropdown>` to control it programmatically from **outside** the tree:
|
package/dist/index.cjs.js
CHANGED
|
@@ -342,6 +342,7 @@ function useOutsideClick(refs, callback) {
|
|
|
342
342
|
function DropdownPanel({
|
|
343
343
|
placement = 'bottom-start',
|
|
344
344
|
offset = 8,
|
|
345
|
+
title,
|
|
345
346
|
anchor,
|
|
346
347
|
component: Comp,
|
|
347
348
|
children,
|
|
@@ -353,7 +354,8 @@ function DropdownPanel({
|
|
|
353
354
|
triggerRef,
|
|
354
355
|
fireEvent,
|
|
355
356
|
hasNav,
|
|
356
|
-
darkMode
|
|
357
|
+
darkMode,
|
|
358
|
+
t
|
|
357
359
|
} = useDropdownContext();
|
|
358
360
|
const panelRef = react.useRef(null);
|
|
359
361
|
const anchorRef = anchor ?? triggerRef;
|
|
@@ -393,7 +395,7 @@ function DropdownPanel({
|
|
|
393
395
|
className: classNames,
|
|
394
396
|
...rest,
|
|
395
397
|
children: children
|
|
396
|
-
}) : /*#__PURE__*/jsxRuntime.
|
|
398
|
+
}) : /*#__PURE__*/jsxRuntime.jsxs("div", {
|
|
397
399
|
ref: panelRef,
|
|
398
400
|
className: classNames,
|
|
399
401
|
style: style,
|
|
@@ -401,10 +403,13 @@ function DropdownPanel({
|
|
|
401
403
|
"aria-modal": "true",
|
|
402
404
|
"aria-label": "Dropdown",
|
|
403
405
|
...rest,
|
|
404
|
-
children: /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
406
|
+
children: [title && /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
407
|
+
className: "hangoverDropdown-panel-title",
|
|
408
|
+
children: t(title)
|
|
409
|
+
}), /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
405
410
|
className: "hangoverDropdown-panel-inner",
|
|
406
411
|
children: children
|
|
407
|
-
})
|
|
412
|
+
})]
|
|
408
413
|
});
|
|
409
414
|
return /*#__PURE__*/reactDom.createPortal(content, document.body);
|
|
410
415
|
}
|
|
@@ -437,7 +442,8 @@ function DropdownNavItem({
|
|
|
437
442
|
displayMode,
|
|
438
443
|
contentRef,
|
|
439
444
|
sectionRefs,
|
|
440
|
-
registerNavLabel
|
|
445
|
+
registerNavLabel,
|
|
446
|
+
t
|
|
441
447
|
} = useDropdownContext();
|
|
442
448
|
const isActive = activeNavId === id;
|
|
443
449
|
react.useEffect(() => {
|
|
@@ -493,7 +499,7 @@ function DropdownNavItem({
|
|
|
493
499
|
handleClick();
|
|
494
500
|
userOnClick?.();
|
|
495
501
|
},
|
|
496
|
-
title: typeof children === 'string' ? children : undefined,
|
|
502
|
+
title: typeof children === 'string' ? t(children) : undefined,
|
|
497
503
|
"data-ho-active": isActive,
|
|
498
504
|
...navItemRest,
|
|
499
505
|
children: [icon && /*#__PURE__*/jsxRuntime.jsx("span", {
|
|
@@ -502,7 +508,7 @@ function DropdownNavItem({
|
|
|
502
508
|
children: renderIcon(icon)
|
|
503
509
|
}), /*#__PURE__*/jsxRuntime.jsx("span", {
|
|
504
510
|
className: "hangoverDropdown-nav-item-label",
|
|
505
|
-
children: children
|
|
511
|
+
children: typeof children === 'string' ? t(children) : children
|
|
506
512
|
})]
|
|
507
513
|
});
|
|
508
514
|
}
|
|
@@ -615,12 +621,15 @@ function DefaultSearchIcon() {
|
|
|
615
621
|
*
|
|
616
622
|
* Props:
|
|
617
623
|
* searchPlaceholder string (default "Search")
|
|
624
|
+
* emptyText string (default "Nothing to show here") — shown when
|
|
625
|
+
* Content has no children; the search bar is hidden too
|
|
618
626
|
* title string — overrides active nav label as section title
|
|
619
627
|
* component custom wrapper component
|
|
620
628
|
* children DropdownSection / DropdownGroup / DropdownItem elements
|
|
621
629
|
*/
|
|
622
630
|
function DropdownContent({
|
|
623
631
|
searchPlaceholder = 'Search',
|
|
632
|
+
emptyText = 'Nothing to show here',
|
|
624
633
|
component: Comp,
|
|
625
634
|
children,
|
|
626
635
|
...rest
|
|
@@ -631,7 +640,8 @@ function DropdownContent({
|
|
|
631
640
|
contentRef,
|
|
632
641
|
displayMode,
|
|
633
642
|
activeNavId,
|
|
634
|
-
setScrollSpyActive
|
|
643
|
+
setScrollSpyActive,
|
|
644
|
+
t
|
|
635
645
|
} = useDropdownContext();
|
|
636
646
|
|
|
637
647
|
// Scroll spy: update active nav based on scroll position
|
|
@@ -686,8 +696,9 @@ function DropdownContent({
|
|
|
686
696
|
query: e.target.value
|
|
687
697
|
});
|
|
688
698
|
}
|
|
699
|
+
const isEmpty = react.Children.count(children) === 0;
|
|
689
700
|
const inner = /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
690
|
-
children: [/*#__PURE__*/jsxRuntime.jsxs("label", {
|
|
701
|
+
children: [!isEmpty && /*#__PURE__*/jsxRuntime.jsxs("label", {
|
|
691
702
|
className: "hangoverDropdown-search",
|
|
692
703
|
children: [/*#__PURE__*/jsxRuntime.jsx("span", {
|
|
693
704
|
className: "hangoverDropdown-search-icon",
|
|
@@ -695,8 +706,8 @@ function DropdownContent({
|
|
|
695
706
|
}), /*#__PURE__*/jsxRuntime.jsx("input", {
|
|
696
707
|
type: "text",
|
|
697
708
|
className: "hangoverDropdown-search-input",
|
|
698
|
-
placeholder: searchPlaceholder,
|
|
699
|
-
"aria-label": searchPlaceholder,
|
|
709
|
+
placeholder: t(searchPlaceholder),
|
|
710
|
+
"aria-label": t(searchPlaceholder),
|
|
700
711
|
value: searchQuery,
|
|
701
712
|
onChange: handleSearch
|
|
702
713
|
})]
|
|
@@ -704,7 +715,10 @@ function DropdownContent({
|
|
|
704
715
|
role: "listbox",
|
|
705
716
|
className: `hangoverDropdown-list${displayMode === 'tab' ? ' isTabMode' : ''}${displayMode === 'tab' && activeNavId === '__all__' ? ' isAllActive' : ''}`,
|
|
706
717
|
ref: contentRef,
|
|
707
|
-
children:
|
|
718
|
+
children: isEmpty ? /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
719
|
+
className: "hangoverDropdown-content-empty",
|
|
720
|
+
children: t(emptyText)
|
|
721
|
+
}) : children
|
|
708
722
|
})]
|
|
709
723
|
});
|
|
710
724
|
if (Comp) {
|
|
@@ -734,7 +748,8 @@ function DropdownSection({
|
|
|
734
748
|
activeNavId,
|
|
735
749
|
displayMode,
|
|
736
750
|
registerSectionRef,
|
|
737
|
-
hasNav
|
|
751
|
+
hasNav,
|
|
752
|
+
t
|
|
738
753
|
} = useDropdownContext();
|
|
739
754
|
const sectionRef = react.useRef(null);
|
|
740
755
|
const forId = forProp || forIdProp || '__all__';
|
|
@@ -799,7 +814,7 @@ function DropdownSection({
|
|
|
799
814
|
children: [title && hasNav && !(displayMode === 'tab' && activeNavId === '__all__') && /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
800
815
|
className: `hangoverDropdown-section-title${hasGroups ? ' isClickable' : ''}`,
|
|
801
816
|
onClick: hasGroups ? handleToggleAll : undefined,
|
|
802
|
-
"aria-label": hasGroups ? allExpanded ? 'Collapse all groups' : 'Expand all groups' : undefined,
|
|
817
|
+
"aria-label": hasGroups ? allExpanded ? t('Collapse all groups') : t('Expand all groups') : undefined,
|
|
803
818
|
role: hasGroups ? 'button' : undefined,
|
|
804
819
|
tabIndex: hasGroups ? 0 : undefined,
|
|
805
820
|
onKeyDown: hasGroups ? e => {
|
|
@@ -809,7 +824,7 @@ function DropdownSection({
|
|
|
809
824
|
}
|
|
810
825
|
} : undefined,
|
|
811
826
|
children: /*#__PURE__*/jsxRuntime.jsx("span", {
|
|
812
|
-
children: title
|
|
827
|
+
children: t(title)
|
|
813
828
|
})
|
|
814
829
|
}), children]
|
|
815
830
|
})
|
|
@@ -3025,7 +3040,8 @@ function DropdownItem({
|
|
|
3025
3040
|
selectedItem,
|
|
3026
3041
|
checkedItems,
|
|
3027
3042
|
searchQuery,
|
|
3028
|
-
fireEvent
|
|
3043
|
+
fireEvent,
|
|
3044
|
+
t
|
|
3029
3045
|
} = useDropdownContext();
|
|
3030
3046
|
const groupCtx = react.useContext(GroupContext);
|
|
3031
3047
|
const groupLabel = groupCtx?.groupLabel ?? '';
|
|
@@ -3063,6 +3079,7 @@ function DropdownItem({
|
|
|
3063
3079
|
fireEvent('select', {
|
|
3064
3080
|
id,
|
|
3065
3081
|
label,
|
|
3082
|
+
groupId,
|
|
3066
3083
|
groupLabel
|
|
3067
3084
|
});
|
|
3068
3085
|
}
|
|
@@ -3113,7 +3130,7 @@ function DropdownItem({
|
|
|
3113
3130
|
"aria-checked": type === 'checkbox' ? isChecked : undefined,
|
|
3114
3131
|
tabIndex: 0,
|
|
3115
3132
|
className: classNames,
|
|
3116
|
-
title: label
|
|
3133
|
+
title: label ? t(label) : undefined,
|
|
3117
3134
|
onClick: () => {
|
|
3118
3135
|
handleClick();
|
|
3119
3136
|
userOnClick?.();
|
|
@@ -3130,7 +3147,7 @@ function DropdownItem({
|
|
|
3130
3147
|
children: renderIcon(icon)
|
|
3131
3148
|
}), /*#__PURE__*/jsxRuntime.jsx("span", {
|
|
3132
3149
|
className: "hangoverDropdown-item-label",
|
|
3133
|
-
children: children
|
|
3150
|
+
children: typeof children === 'string' ? t(children) : children
|
|
3134
3151
|
}), actionsNode && /*#__PURE__*/jsxRuntime.jsx("span", {
|
|
3135
3152
|
className: "hangoverDropdown-item-actions",
|
|
3136
3153
|
onClick: e => e.stopPropagation(),
|
|
@@ -3229,7 +3246,8 @@ function DropdownGroup({
|
|
|
3229
3246
|
displayMode,
|
|
3230
3247
|
activeNavId,
|
|
3231
3248
|
registerGroupItems,
|
|
3232
|
-
searchQuery
|
|
3249
|
+
searchQuery,
|
|
3250
|
+
t
|
|
3233
3251
|
} = useDropdownContext();
|
|
3234
3252
|
|
|
3235
3253
|
// Determine initial expanded state
|
|
@@ -3318,7 +3336,7 @@ function DropdownGroup({
|
|
|
3318
3336
|
role: "checkbox",
|
|
3319
3337
|
"aria-checked": selectAllChecked,
|
|
3320
3338
|
tabIndex: 0,
|
|
3321
|
-
title:
|
|
3339
|
+
title: t('Select all'),
|
|
3322
3340
|
className: `hangoverDropdown-item isCheckboxType${selectAllChecked ? ' isChecked' : ''}`,
|
|
3323
3341
|
onClick: handleSelectAll,
|
|
3324
3342
|
onKeyDown: e => {
|
|
@@ -3329,7 +3347,7 @@ function DropdownGroup({
|
|
|
3329
3347
|
},
|
|
3330
3348
|
children: [/*#__PURE__*/jsxRuntime.jsx("span", {
|
|
3331
3349
|
className: "hangoverDropdown-item-label",
|
|
3332
|
-
children:
|
|
3350
|
+
children: t('Select all')
|
|
3333
3351
|
}), /*#__PURE__*/jsxRuntime.jsx("span", {
|
|
3334
3352
|
className: `hangoverDropdown-item-check-icon${selectAllChecked ? ' isVisible' : ''}`,
|
|
3335
3353
|
children: selectAllChecked && /*#__PURE__*/jsxRuntime.jsx("svg", {
|
|
@@ -3371,8 +3389,11 @@ function DropdownGroup({
|
|
|
3371
3389
|
}
|
|
3372
3390
|
},
|
|
3373
3391
|
"aria-expanded": isExpanded,
|
|
3374
|
-
"aria-label":
|
|
3375
|
-
|
|
3392
|
+
"aria-label": t('{label} — {action}', {
|
|
3393
|
+
label,
|
|
3394
|
+
action: t(isExpanded ? 'collapse' : 'expand')
|
|
3395
|
+
}),
|
|
3396
|
+
title: t(label),
|
|
3376
3397
|
children: [/*#__PURE__*/jsxRuntime.jsx("div", {
|
|
3377
3398
|
className: "hangoverDropdown-group-header-accent"
|
|
3378
3399
|
}), /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
@@ -3384,7 +3405,7 @@ function DropdownGroup({
|
|
|
3384
3405
|
children: renderIcon(icon)
|
|
3385
3406
|
}), /*#__PURE__*/jsxRuntime.jsx("span", {
|
|
3386
3407
|
className: "hangoverDropdown-group-header-label",
|
|
3387
|
-
children: label
|
|
3408
|
+
children: t(label)
|
|
3388
3409
|
}), /*#__PURE__*/jsxRuntime.jsx("span", {
|
|
3389
3410
|
className: "hangoverDropdown-group-header-chevron",
|
|
3390
3411
|
children: /*#__PURE__*/jsxRuntime.jsx(Chevron, {})
|
|
@@ -3398,14 +3419,14 @@ function DropdownGroup({
|
|
|
3398
3419
|
className: `hangoverDropdown-group-items-wrap${isExpanded ? ' isExpanded' : ''}`,
|
|
3399
3420
|
children: /*#__PURE__*/jsxRuntime.jsxs("div", {
|
|
3400
3421
|
role: "group",
|
|
3401
|
-
"aria-label": label,
|
|
3422
|
+
"aria-label": t(label),
|
|
3402
3423
|
className: "hangoverDropdown-group-items",
|
|
3403
3424
|
children: [showSelectAll && selectAllPosition === 'top' && selectAllItem, hasChildren ? hasVisibleItems ? children : /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
3404
3425
|
className: "hangoverDropdown-group-empty",
|
|
3405
|
-
children: noResultsText
|
|
3426
|
+
children: t(noResultsText)
|
|
3406
3427
|
}) : /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
3407
3428
|
className: "hangoverDropdown-group-empty",
|
|
3408
|
-
children: emptyText
|
|
3429
|
+
children: t(emptyText)
|
|
3409
3430
|
}), showSelectAll && selectAllPosition === 'bottom' && selectAllItem]
|
|
3410
3431
|
})
|
|
3411
3432
|
});
|
|
@@ -3717,9 +3738,10 @@ const Dropdown$1 = /*#__PURE__*/react.forwardRef(function Dropdown({
|
|
|
3717
3738
|
hideOnSelection: hideOnSelectionProp = true,
|
|
3718
3739
|
onEvent: onEventProp,
|
|
3719
3740
|
fromConfig,
|
|
3720
|
-
darkMode = false,
|
|
3741
|
+
darkMode: darkModeProp = false,
|
|
3721
3742
|
searchQuery: searchQueryProp,
|
|
3722
|
-
defaultSearchQuery = '',
|
|
3743
|
+
defaultSearchQuery: defaultSearchQueryProp = '',
|
|
3744
|
+
useTranslationFunction: useTranslationFunctionProp,
|
|
3723
3745
|
children,
|
|
3724
3746
|
...rest
|
|
3725
3747
|
}, ref) {
|
|
@@ -3728,6 +3750,23 @@ const Dropdown$1 = /*#__PURE__*/react.forwardRef(function Dropdown({
|
|
|
3728
3750
|
const defaultGroupExpanded = fromConfig?.defaultGroupExpanded ?? defaultGroupExpandedProp;
|
|
3729
3751
|
const hideOnSelection = fromConfig?.hideOnSelection ?? hideOnSelectionProp;
|
|
3730
3752
|
const onEvent = fromConfig?.onEvent ?? onEventProp;
|
|
3753
|
+
const darkMode = fromConfig?.darkMode ?? darkModeProp;
|
|
3754
|
+
const defaultSearchQuery = fromConfig?.defaultSearchQuery ?? defaultSearchQueryProp;
|
|
3755
|
+
const controlledSearchQuery = fromConfig?.searchQuery ?? searchQueryProp;
|
|
3756
|
+
const translationFn = fromConfig?.useTranslationFunction ?? useTranslationFunctionProp;
|
|
3757
|
+
|
|
3758
|
+
// Translation helper. Every user-facing string is routed through this.
|
|
3759
|
+
// - With a translation function: returns translationFn(str, payload).
|
|
3760
|
+
// - Without one: returns the string, interpolating any {placeholder}
|
|
3761
|
+
// tokens from the optional payload object.
|
|
3762
|
+
const t = react.useCallback((str, payload) => {
|
|
3763
|
+
if (typeof str !== 'string') return str;
|
|
3764
|
+
if (typeof translationFn === 'function') return translationFn(str, payload);
|
|
3765
|
+
if (payload) {
|
|
3766
|
+
return str.replace(/\{(\w+)\}/g, (match, key) => key in payload ? payload[key] : match);
|
|
3767
|
+
}
|
|
3768
|
+
return str;
|
|
3769
|
+
}, [translationFn]);
|
|
3731
3770
|
const [isOpen, setIsOpen] = react.useState(defaultOpen);
|
|
3732
3771
|
const [selectedItem, setSelectedItem] = react.useState(null);
|
|
3733
3772
|
const [checkedItems, setCheckedItems] = react.useState(() => new Map());
|
|
@@ -3737,10 +3776,10 @@ const Dropdown$1 = /*#__PURE__*/react.forwardRef(function Dropdown({
|
|
|
3737
3776
|
const [hasNav, setHasNav] = react.useState(false);
|
|
3738
3777
|
|
|
3739
3778
|
// Controlled searchQuery — sync internal state whenever the prop changes
|
|
3740
|
-
const isControlledSearch =
|
|
3779
|
+
const isControlledSearch = controlledSearchQuery !== undefined;
|
|
3741
3780
|
react.useEffect(() => {
|
|
3742
|
-
if (isControlledSearch) setSearchQuery(
|
|
3743
|
-
}, [isControlledSearch,
|
|
3781
|
+
if (isControlledSearch) setSearchQuery(controlledSearchQuery);
|
|
3782
|
+
}, [isControlledSearch, controlledSearchQuery]);
|
|
3744
3783
|
const triggerRef = react.useRef(null);
|
|
3745
3784
|
const contentRef = react.useRef(null); // scroll container inside DropdownContent
|
|
3746
3785
|
const firstGroupClaimedRef = react.useRef(false);
|
|
@@ -4005,6 +4044,8 @@ const Dropdown$1 = /*#__PURE__*/react.forwardRef(function Dropdown({
|
|
|
4005
4044
|
hasNav,
|
|
4006
4045
|
darkMode,
|
|
4007
4046
|
setHasNav,
|
|
4047
|
+
// i18n
|
|
4048
|
+
t,
|
|
4008
4049
|
// Refs
|
|
4009
4050
|
triggerRef,
|
|
4010
4051
|
contentRef,
|
|
@@ -4024,7 +4065,7 @@ const Dropdown$1 = /*#__PURE__*/react.forwardRef(function Dropdown({
|
|
|
4024
4065
|
registerSectionRef
|
|
4025
4066
|
}), [isOpen, selectedItem, checkedItems, activeNavId, activeNavLabel, searchQuery, hasNav, displayMode, defaultGroupExpanded, darkMode,
|
|
4026
4067
|
// all others are stable references
|
|
4027
|
-
fireEvent, registerGroupItems, setScrollSpyActive, registerNavLabel, registerSectionRef]);
|
|
4068
|
+
fireEvent, registerGroupItems, setScrollSpyActive, registerNavLabel, registerSectionRef, t]);
|
|
4028
4069
|
const resolvedChildren = (() => {
|
|
4029
4070
|
if (fromConfig && children) {
|
|
4030
4071
|
console.warn('[Dropdown] `fromConfig` and `children` cannot be used together. ' + '`fromConfig` takes precedence — `children` will be ignored.');
|
package/dist/index.esm.js
CHANGED
|
@@ -338,6 +338,7 @@ function useOutsideClick(refs, callback) {
|
|
|
338
338
|
function DropdownPanel({
|
|
339
339
|
placement = 'bottom-start',
|
|
340
340
|
offset = 8,
|
|
341
|
+
title,
|
|
341
342
|
anchor,
|
|
342
343
|
component: Comp,
|
|
343
344
|
children,
|
|
@@ -349,7 +350,8 @@ function DropdownPanel({
|
|
|
349
350
|
triggerRef,
|
|
350
351
|
fireEvent,
|
|
351
352
|
hasNav,
|
|
352
|
-
darkMode
|
|
353
|
+
darkMode,
|
|
354
|
+
t
|
|
353
355
|
} = useDropdownContext();
|
|
354
356
|
const panelRef = useRef(null);
|
|
355
357
|
const anchorRef = anchor ?? triggerRef;
|
|
@@ -389,7 +391,7 @@ function DropdownPanel({
|
|
|
389
391
|
className: classNames,
|
|
390
392
|
...rest,
|
|
391
393
|
children: children
|
|
392
|
-
}) : /*#__PURE__*/
|
|
394
|
+
}) : /*#__PURE__*/jsxs("div", {
|
|
393
395
|
ref: panelRef,
|
|
394
396
|
className: classNames,
|
|
395
397
|
style: style,
|
|
@@ -397,10 +399,13 @@ function DropdownPanel({
|
|
|
397
399
|
"aria-modal": "true",
|
|
398
400
|
"aria-label": "Dropdown",
|
|
399
401
|
...rest,
|
|
400
|
-
children: /*#__PURE__*/jsx("div", {
|
|
402
|
+
children: [title && /*#__PURE__*/jsx("div", {
|
|
403
|
+
className: "hangoverDropdown-panel-title",
|
|
404
|
+
children: t(title)
|
|
405
|
+
}), /*#__PURE__*/jsx("div", {
|
|
401
406
|
className: "hangoverDropdown-panel-inner",
|
|
402
407
|
children: children
|
|
403
|
-
})
|
|
408
|
+
})]
|
|
404
409
|
});
|
|
405
410
|
return /*#__PURE__*/createPortal(content, document.body);
|
|
406
411
|
}
|
|
@@ -433,7 +438,8 @@ function DropdownNavItem({
|
|
|
433
438
|
displayMode,
|
|
434
439
|
contentRef,
|
|
435
440
|
sectionRefs,
|
|
436
|
-
registerNavLabel
|
|
441
|
+
registerNavLabel,
|
|
442
|
+
t
|
|
437
443
|
} = useDropdownContext();
|
|
438
444
|
const isActive = activeNavId === id;
|
|
439
445
|
useEffect(() => {
|
|
@@ -489,7 +495,7 @@ function DropdownNavItem({
|
|
|
489
495
|
handleClick();
|
|
490
496
|
userOnClick?.();
|
|
491
497
|
},
|
|
492
|
-
title: typeof children === 'string' ? children : undefined,
|
|
498
|
+
title: typeof children === 'string' ? t(children) : undefined,
|
|
493
499
|
"data-ho-active": isActive,
|
|
494
500
|
...navItemRest,
|
|
495
501
|
children: [icon && /*#__PURE__*/jsx("span", {
|
|
@@ -498,7 +504,7 @@ function DropdownNavItem({
|
|
|
498
504
|
children: renderIcon(icon)
|
|
499
505
|
}), /*#__PURE__*/jsx("span", {
|
|
500
506
|
className: "hangoverDropdown-nav-item-label",
|
|
501
|
-
children: children
|
|
507
|
+
children: typeof children === 'string' ? t(children) : children
|
|
502
508
|
})]
|
|
503
509
|
});
|
|
504
510
|
}
|
|
@@ -611,12 +617,15 @@ function DefaultSearchIcon() {
|
|
|
611
617
|
*
|
|
612
618
|
* Props:
|
|
613
619
|
* searchPlaceholder string (default "Search")
|
|
620
|
+
* emptyText string (default "Nothing to show here") — shown when
|
|
621
|
+
* Content has no children; the search bar is hidden too
|
|
614
622
|
* title string — overrides active nav label as section title
|
|
615
623
|
* component custom wrapper component
|
|
616
624
|
* children DropdownSection / DropdownGroup / DropdownItem elements
|
|
617
625
|
*/
|
|
618
626
|
function DropdownContent({
|
|
619
627
|
searchPlaceholder = 'Search',
|
|
628
|
+
emptyText = 'Nothing to show here',
|
|
620
629
|
component: Comp,
|
|
621
630
|
children,
|
|
622
631
|
...rest
|
|
@@ -627,7 +636,8 @@ function DropdownContent({
|
|
|
627
636
|
contentRef,
|
|
628
637
|
displayMode,
|
|
629
638
|
activeNavId,
|
|
630
|
-
setScrollSpyActive
|
|
639
|
+
setScrollSpyActive,
|
|
640
|
+
t
|
|
631
641
|
} = useDropdownContext();
|
|
632
642
|
|
|
633
643
|
// Scroll spy: update active nav based on scroll position
|
|
@@ -682,8 +692,9 @@ function DropdownContent({
|
|
|
682
692
|
query: e.target.value
|
|
683
693
|
});
|
|
684
694
|
}
|
|
695
|
+
const isEmpty = Children.count(children) === 0;
|
|
685
696
|
const inner = /*#__PURE__*/jsxs(Fragment, {
|
|
686
|
-
children: [/*#__PURE__*/jsxs("label", {
|
|
697
|
+
children: [!isEmpty && /*#__PURE__*/jsxs("label", {
|
|
687
698
|
className: "hangoverDropdown-search",
|
|
688
699
|
children: [/*#__PURE__*/jsx("span", {
|
|
689
700
|
className: "hangoverDropdown-search-icon",
|
|
@@ -691,8 +702,8 @@ function DropdownContent({
|
|
|
691
702
|
}), /*#__PURE__*/jsx("input", {
|
|
692
703
|
type: "text",
|
|
693
704
|
className: "hangoverDropdown-search-input",
|
|
694
|
-
placeholder: searchPlaceholder,
|
|
695
|
-
"aria-label": searchPlaceholder,
|
|
705
|
+
placeholder: t(searchPlaceholder),
|
|
706
|
+
"aria-label": t(searchPlaceholder),
|
|
696
707
|
value: searchQuery,
|
|
697
708
|
onChange: handleSearch
|
|
698
709
|
})]
|
|
@@ -700,7 +711,10 @@ function DropdownContent({
|
|
|
700
711
|
role: "listbox",
|
|
701
712
|
className: `hangoverDropdown-list${displayMode === 'tab' ? ' isTabMode' : ''}${displayMode === 'tab' && activeNavId === '__all__' ? ' isAllActive' : ''}`,
|
|
702
713
|
ref: contentRef,
|
|
703
|
-
children:
|
|
714
|
+
children: isEmpty ? /*#__PURE__*/jsx("div", {
|
|
715
|
+
className: "hangoverDropdown-content-empty",
|
|
716
|
+
children: t(emptyText)
|
|
717
|
+
}) : children
|
|
704
718
|
})]
|
|
705
719
|
});
|
|
706
720
|
if (Comp) {
|
|
@@ -730,7 +744,8 @@ function DropdownSection({
|
|
|
730
744
|
activeNavId,
|
|
731
745
|
displayMode,
|
|
732
746
|
registerSectionRef,
|
|
733
|
-
hasNav
|
|
747
|
+
hasNav,
|
|
748
|
+
t
|
|
734
749
|
} = useDropdownContext();
|
|
735
750
|
const sectionRef = useRef(null);
|
|
736
751
|
const forId = forProp || forIdProp || '__all__';
|
|
@@ -795,7 +810,7 @@ function DropdownSection({
|
|
|
795
810
|
children: [title && hasNav && !(displayMode === 'tab' && activeNavId === '__all__') && /*#__PURE__*/jsx("div", {
|
|
796
811
|
className: `hangoverDropdown-section-title${hasGroups ? ' isClickable' : ''}`,
|
|
797
812
|
onClick: hasGroups ? handleToggleAll : undefined,
|
|
798
|
-
"aria-label": hasGroups ? allExpanded ? 'Collapse all groups' : 'Expand all groups' : undefined,
|
|
813
|
+
"aria-label": hasGroups ? allExpanded ? t('Collapse all groups') : t('Expand all groups') : undefined,
|
|
799
814
|
role: hasGroups ? 'button' : undefined,
|
|
800
815
|
tabIndex: hasGroups ? 0 : undefined,
|
|
801
816
|
onKeyDown: hasGroups ? e => {
|
|
@@ -805,7 +820,7 @@ function DropdownSection({
|
|
|
805
820
|
}
|
|
806
821
|
} : undefined,
|
|
807
822
|
children: /*#__PURE__*/jsx("span", {
|
|
808
|
-
children: title
|
|
823
|
+
children: t(title)
|
|
809
824
|
})
|
|
810
825
|
}), children]
|
|
811
826
|
})
|
|
@@ -3021,7 +3036,8 @@ function DropdownItem({
|
|
|
3021
3036
|
selectedItem,
|
|
3022
3037
|
checkedItems,
|
|
3023
3038
|
searchQuery,
|
|
3024
|
-
fireEvent
|
|
3039
|
+
fireEvent,
|
|
3040
|
+
t
|
|
3025
3041
|
} = useDropdownContext();
|
|
3026
3042
|
const groupCtx = useContext(GroupContext);
|
|
3027
3043
|
const groupLabel = groupCtx?.groupLabel ?? '';
|
|
@@ -3059,6 +3075,7 @@ function DropdownItem({
|
|
|
3059
3075
|
fireEvent('select', {
|
|
3060
3076
|
id,
|
|
3061
3077
|
label,
|
|
3078
|
+
groupId,
|
|
3062
3079
|
groupLabel
|
|
3063
3080
|
});
|
|
3064
3081
|
}
|
|
@@ -3109,7 +3126,7 @@ function DropdownItem({
|
|
|
3109
3126
|
"aria-checked": type === 'checkbox' ? isChecked : undefined,
|
|
3110
3127
|
tabIndex: 0,
|
|
3111
3128
|
className: classNames,
|
|
3112
|
-
title: label
|
|
3129
|
+
title: label ? t(label) : undefined,
|
|
3113
3130
|
onClick: () => {
|
|
3114
3131
|
handleClick();
|
|
3115
3132
|
userOnClick?.();
|
|
@@ -3126,7 +3143,7 @@ function DropdownItem({
|
|
|
3126
3143
|
children: renderIcon(icon)
|
|
3127
3144
|
}), /*#__PURE__*/jsx("span", {
|
|
3128
3145
|
className: "hangoverDropdown-item-label",
|
|
3129
|
-
children: children
|
|
3146
|
+
children: typeof children === 'string' ? t(children) : children
|
|
3130
3147
|
}), actionsNode && /*#__PURE__*/jsx("span", {
|
|
3131
3148
|
className: "hangoverDropdown-item-actions",
|
|
3132
3149
|
onClick: e => e.stopPropagation(),
|
|
@@ -3225,7 +3242,8 @@ function DropdownGroup({
|
|
|
3225
3242
|
displayMode,
|
|
3226
3243
|
activeNavId,
|
|
3227
3244
|
registerGroupItems,
|
|
3228
|
-
searchQuery
|
|
3245
|
+
searchQuery,
|
|
3246
|
+
t
|
|
3229
3247
|
} = useDropdownContext();
|
|
3230
3248
|
|
|
3231
3249
|
// Determine initial expanded state
|
|
@@ -3314,7 +3332,7 @@ function DropdownGroup({
|
|
|
3314
3332
|
role: "checkbox",
|
|
3315
3333
|
"aria-checked": selectAllChecked,
|
|
3316
3334
|
tabIndex: 0,
|
|
3317
|
-
title:
|
|
3335
|
+
title: t('Select all'),
|
|
3318
3336
|
className: `hangoverDropdown-item isCheckboxType${selectAllChecked ? ' isChecked' : ''}`,
|
|
3319
3337
|
onClick: handleSelectAll,
|
|
3320
3338
|
onKeyDown: e => {
|
|
@@ -3325,7 +3343,7 @@ function DropdownGroup({
|
|
|
3325
3343
|
},
|
|
3326
3344
|
children: [/*#__PURE__*/jsx("span", {
|
|
3327
3345
|
className: "hangoverDropdown-item-label",
|
|
3328
|
-
children:
|
|
3346
|
+
children: t('Select all')
|
|
3329
3347
|
}), /*#__PURE__*/jsx("span", {
|
|
3330
3348
|
className: `hangoverDropdown-item-check-icon${selectAllChecked ? ' isVisible' : ''}`,
|
|
3331
3349
|
children: selectAllChecked && /*#__PURE__*/jsx("svg", {
|
|
@@ -3367,8 +3385,11 @@ function DropdownGroup({
|
|
|
3367
3385
|
}
|
|
3368
3386
|
},
|
|
3369
3387
|
"aria-expanded": isExpanded,
|
|
3370
|
-
"aria-label":
|
|
3371
|
-
|
|
3388
|
+
"aria-label": t('{label} — {action}', {
|
|
3389
|
+
label,
|
|
3390
|
+
action: t(isExpanded ? 'collapse' : 'expand')
|
|
3391
|
+
}),
|
|
3392
|
+
title: t(label),
|
|
3372
3393
|
children: [/*#__PURE__*/jsx("div", {
|
|
3373
3394
|
className: "hangoverDropdown-group-header-accent"
|
|
3374
3395
|
}), /*#__PURE__*/jsx("div", {
|
|
@@ -3380,7 +3401,7 @@ function DropdownGroup({
|
|
|
3380
3401
|
children: renderIcon(icon)
|
|
3381
3402
|
}), /*#__PURE__*/jsx("span", {
|
|
3382
3403
|
className: "hangoverDropdown-group-header-label",
|
|
3383
|
-
children: label
|
|
3404
|
+
children: t(label)
|
|
3384
3405
|
}), /*#__PURE__*/jsx("span", {
|
|
3385
3406
|
className: "hangoverDropdown-group-header-chevron",
|
|
3386
3407
|
children: /*#__PURE__*/jsx(Chevron, {})
|
|
@@ -3394,14 +3415,14 @@ function DropdownGroup({
|
|
|
3394
3415
|
className: `hangoverDropdown-group-items-wrap${isExpanded ? ' isExpanded' : ''}`,
|
|
3395
3416
|
children: /*#__PURE__*/jsxs("div", {
|
|
3396
3417
|
role: "group",
|
|
3397
|
-
"aria-label": label,
|
|
3418
|
+
"aria-label": t(label),
|
|
3398
3419
|
className: "hangoverDropdown-group-items",
|
|
3399
3420
|
children: [showSelectAll && selectAllPosition === 'top' && selectAllItem, hasChildren ? hasVisibleItems ? children : /*#__PURE__*/jsx("div", {
|
|
3400
3421
|
className: "hangoverDropdown-group-empty",
|
|
3401
|
-
children: noResultsText
|
|
3422
|
+
children: t(noResultsText)
|
|
3402
3423
|
}) : /*#__PURE__*/jsx("div", {
|
|
3403
3424
|
className: "hangoverDropdown-group-empty",
|
|
3404
|
-
children: emptyText
|
|
3425
|
+
children: t(emptyText)
|
|
3405
3426
|
}), showSelectAll && selectAllPosition === 'bottom' && selectAllItem]
|
|
3406
3427
|
})
|
|
3407
3428
|
});
|
|
@@ -3713,9 +3734,10 @@ const Dropdown$1 = /*#__PURE__*/forwardRef(function Dropdown({
|
|
|
3713
3734
|
hideOnSelection: hideOnSelectionProp = true,
|
|
3714
3735
|
onEvent: onEventProp,
|
|
3715
3736
|
fromConfig,
|
|
3716
|
-
darkMode = false,
|
|
3737
|
+
darkMode: darkModeProp = false,
|
|
3717
3738
|
searchQuery: searchQueryProp,
|
|
3718
|
-
defaultSearchQuery = '',
|
|
3739
|
+
defaultSearchQuery: defaultSearchQueryProp = '',
|
|
3740
|
+
useTranslationFunction: useTranslationFunctionProp,
|
|
3719
3741
|
children,
|
|
3720
3742
|
...rest
|
|
3721
3743
|
}, ref) {
|
|
@@ -3724,6 +3746,23 @@ const Dropdown$1 = /*#__PURE__*/forwardRef(function Dropdown({
|
|
|
3724
3746
|
const defaultGroupExpanded = fromConfig?.defaultGroupExpanded ?? defaultGroupExpandedProp;
|
|
3725
3747
|
const hideOnSelection = fromConfig?.hideOnSelection ?? hideOnSelectionProp;
|
|
3726
3748
|
const onEvent = fromConfig?.onEvent ?? onEventProp;
|
|
3749
|
+
const darkMode = fromConfig?.darkMode ?? darkModeProp;
|
|
3750
|
+
const defaultSearchQuery = fromConfig?.defaultSearchQuery ?? defaultSearchQueryProp;
|
|
3751
|
+
const controlledSearchQuery = fromConfig?.searchQuery ?? searchQueryProp;
|
|
3752
|
+
const translationFn = fromConfig?.useTranslationFunction ?? useTranslationFunctionProp;
|
|
3753
|
+
|
|
3754
|
+
// Translation helper. Every user-facing string is routed through this.
|
|
3755
|
+
// - With a translation function: returns translationFn(str, payload).
|
|
3756
|
+
// - Without one: returns the string, interpolating any {placeholder}
|
|
3757
|
+
// tokens from the optional payload object.
|
|
3758
|
+
const t = useCallback((str, payload) => {
|
|
3759
|
+
if (typeof str !== 'string') return str;
|
|
3760
|
+
if (typeof translationFn === 'function') return translationFn(str, payload);
|
|
3761
|
+
if (payload) {
|
|
3762
|
+
return str.replace(/\{(\w+)\}/g, (match, key) => key in payload ? payload[key] : match);
|
|
3763
|
+
}
|
|
3764
|
+
return str;
|
|
3765
|
+
}, [translationFn]);
|
|
3727
3766
|
const [isOpen, setIsOpen] = useState(defaultOpen);
|
|
3728
3767
|
const [selectedItem, setSelectedItem] = useState(null);
|
|
3729
3768
|
const [checkedItems, setCheckedItems] = useState(() => new Map());
|
|
@@ -3733,10 +3772,10 @@ const Dropdown$1 = /*#__PURE__*/forwardRef(function Dropdown({
|
|
|
3733
3772
|
const [hasNav, setHasNav] = useState(false);
|
|
3734
3773
|
|
|
3735
3774
|
// Controlled searchQuery — sync internal state whenever the prop changes
|
|
3736
|
-
const isControlledSearch =
|
|
3775
|
+
const isControlledSearch = controlledSearchQuery !== undefined;
|
|
3737
3776
|
useEffect(() => {
|
|
3738
|
-
if (isControlledSearch) setSearchQuery(
|
|
3739
|
-
}, [isControlledSearch,
|
|
3777
|
+
if (isControlledSearch) setSearchQuery(controlledSearchQuery);
|
|
3778
|
+
}, [isControlledSearch, controlledSearchQuery]);
|
|
3740
3779
|
const triggerRef = useRef(null);
|
|
3741
3780
|
const contentRef = useRef(null); // scroll container inside DropdownContent
|
|
3742
3781
|
const firstGroupClaimedRef = useRef(false);
|
|
@@ -4001,6 +4040,8 @@ const Dropdown$1 = /*#__PURE__*/forwardRef(function Dropdown({
|
|
|
4001
4040
|
hasNav,
|
|
4002
4041
|
darkMode,
|
|
4003
4042
|
setHasNav,
|
|
4043
|
+
// i18n
|
|
4044
|
+
t,
|
|
4004
4045
|
// Refs
|
|
4005
4046
|
triggerRef,
|
|
4006
4047
|
contentRef,
|
|
@@ -4020,7 +4061,7 @@ const Dropdown$1 = /*#__PURE__*/forwardRef(function Dropdown({
|
|
|
4020
4061
|
registerSectionRef
|
|
4021
4062
|
}), [isOpen, selectedItem, checkedItems, activeNavId, activeNavLabel, searchQuery, hasNav, displayMode, defaultGroupExpanded, darkMode,
|
|
4022
4063
|
// all others are stable references
|
|
4023
|
-
fireEvent, registerGroupItems, setScrollSpyActive, registerNavLabel, registerSectionRef]);
|
|
4064
|
+
fireEvent, registerGroupItems, setScrollSpyActive, registerNavLabel, registerSectionRef, t]);
|
|
4024
4065
|
const resolvedChildren = (() => {
|
|
4025
4066
|
if (fromConfig && children) {
|
|
4026
4067
|
console.warn('[Dropdown] `fromConfig` and `children` cannot be used together. ' + '`fromConfig` takes precedence — `children` will be ignored.');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diabolic/hangover",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "A headless-style, compound React dropdown/field-picker component library",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "bugrakaan",
|
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
"import": "./dist/index.esm.js",
|
|
28
28
|
"require": "./dist/index.cjs.js"
|
|
29
29
|
},
|
|
30
|
-
"prepublishOnly": "npm run build",
|
|
31
30
|
"./styles": "./dist/hangover.css"
|
|
32
31
|
},
|
|
33
32
|
"files": [
|
|
@@ -49,6 +48,8 @@
|
|
|
49
48
|
"dev": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\" && rollup -c -w",
|
|
50
49
|
"lint": "eslint \"src/**/*.{js,jsx}\"",
|
|
51
50
|
"lint:fix": "eslint --fix \"src/**/*.{js,jsx}\"",
|
|
51
|
+
"export": "node scripts/export-component.mjs",
|
|
52
|
+
"prepublishOnly": "npm run build",
|
|
52
53
|
"storybook": "storybook dev -p 6006",
|
|
53
54
|
"build-storybook": "storybook build"
|
|
54
55
|
},
|
|
@@ -60,8 +61,8 @@
|
|
|
60
61
|
"@rollup/plugin-babel": "^6.0.4",
|
|
61
62
|
"@rollup/plugin-commonjs": "^25.0.7",
|
|
62
63
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
63
|
-
"@storybook/addon-docs": "^10.
|
|
64
|
-
"@storybook/react-vite": "^10.
|
|
64
|
+
"@storybook/addon-docs": "^10.4.6",
|
|
65
|
+
"@storybook/react-vite": "^10.4.6",
|
|
65
66
|
"eslint": "^9.39.4",
|
|
66
67
|
"eslint-plugin-react": "^7.37.5",
|
|
67
68
|
"react": "^18.2.0",
|
|
@@ -69,7 +70,8 @@
|
|
|
69
70
|
"rollup": "^4.13.0",
|
|
70
71
|
"rollup-plugin-scss": "^4.0.0",
|
|
71
72
|
"sass": "^1.99.0",
|
|
72
|
-
"storybook": "^10.
|
|
73
|
+
"storybook": "^10.4.6",
|
|
74
|
+
"eslint-plugin-storybook": "10.4.6"
|
|
73
75
|
},
|
|
74
76
|
"dependencies": {
|
|
75
77
|
"fuse.js": "^7.3.0"
|