oihana-next-ui 0.2.3 → 0.2.4
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/package.json +16 -16
- package/src/@configs/navigation.js +4 -0
- package/src/@locale/navigation.js +4 -0
- package/src/app/lab/@tabs/effects/page.js +31 -0
- package/src/app/lab/@tabs/megamenu/page.js +29 -0
- package/src/components/Aura.jsx +110 -0
- package/src/components/menus/Megamenu.jsx +165 -0
- package/src/demo/effects/AuraDemo.jsx +125 -0
- package/src/demo/menus/MegamenuDemo.jsx +125 -0
- package/src/themes/effects/aura.js +162 -0
- package/src/themes/navigation/megamenu.js +128 -0
- package/src/version.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oihana-next-ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Oihana Next.js UI component library — reusable components, hooks and utilities built with React 19, Next.js, Tailwind CSS and DaisyUI",
|
|
6
6
|
"author": {
|
|
@@ -57,48 +57,48 @@
|
|
|
57
57
|
"daisyui": "^5.0.0"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@maskito/core": "^5.
|
|
61
|
-
"@maskito/kit": "^5.
|
|
62
|
-
"@maskito/react": "^5.
|
|
60
|
+
"@maskito/core": "^5.3.1",
|
|
61
|
+
"@maskito/kit": "^5.3.1",
|
|
62
|
+
"@maskito/react": "^5.3.1",
|
|
63
63
|
"@use-gesture/react": "^10.3.1",
|
|
64
64
|
"chroma-js": "^3.2.0",
|
|
65
65
|
"clsx": "^2.1.1",
|
|
66
|
-
"dayjs": "^1.11.
|
|
66
|
+
"dayjs": "^1.11.21",
|
|
67
67
|
"flag-icons": "^7.5.0",
|
|
68
68
|
"html-react-parser": "^5.2.17",
|
|
69
69
|
"ky": "^1.14.3",
|
|
70
|
-
"motion": "^12.
|
|
70
|
+
"motion": "^12.42.0",
|
|
71
71
|
"next": "16.2.3",
|
|
72
72
|
"react": "19.2.3",
|
|
73
73
|
"react-dom": "19.2.3",
|
|
74
74
|
"react-icons": "^5.6.0",
|
|
75
|
-
"react-is": "^19.2.
|
|
75
|
+
"react-is": "^19.2.7",
|
|
76
76
|
"react-markdown": "^10.1.0",
|
|
77
77
|
"react-syntax-highlighter": "^16.1.1",
|
|
78
|
-
"react-use": "^17.6.
|
|
78
|
+
"react-use": "^17.6.1",
|
|
79
79
|
"rehype-external-links": "^3.0.0",
|
|
80
80
|
"rehype-raw": "^7.0.0",
|
|
81
81
|
"remark-breaks": "^4.0.0",
|
|
82
82
|
"remark-gfm": "^4.0.1",
|
|
83
|
-
"sanitize-html": "^2.17.
|
|
84
|
-
"tailwind-merge": "^3.
|
|
83
|
+
"sanitize-html": "^2.17.5",
|
|
84
|
+
"tailwind-merge": "^3.6.0",
|
|
85
85
|
"validator": "^13.15.35",
|
|
86
86
|
"vegas-js-core": "^1.0.47"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
89
|
"@biomejs/biome": "2.2.0",
|
|
90
|
-
"@tailwindcss/postcss": "^4.
|
|
91
|
-
"@tailwindcss/typography": "^0.5.
|
|
92
|
-
"@types/node": "^25.
|
|
90
|
+
"@tailwindcss/postcss": "^4.3.1",
|
|
91
|
+
"@tailwindcss/typography": "^0.5.20",
|
|
92
|
+
"@types/node": "^25.9.4",
|
|
93
93
|
"@types/react-syntax-highlighter": "^15.5.13",
|
|
94
94
|
"@types/sanitize-html": "^2.16.1",
|
|
95
95
|
"babel-plugin-react-compiler": "1.0.0",
|
|
96
|
-
"daisyui": "^5.
|
|
96
|
+
"daisyui": "^5.6.2",
|
|
97
97
|
"raw-loader": "^4.0.2",
|
|
98
98
|
"sharp": "^0.34.5",
|
|
99
99
|
"tailwind-scrollbar": "^4.0.2",
|
|
100
|
-
"tailwindcss": "^4.
|
|
101
|
-
"tailwindcss-animated": "^2.
|
|
100
|
+
"tailwindcss": "^4.3.1",
|
|
101
|
+
"tailwindcss-animated": "^2.1.0",
|
|
102
102
|
"tailwindcss-opentype": "^1.2.0"
|
|
103
103
|
},
|
|
104
104
|
"resolutions": {
|
|
@@ -18,6 +18,7 @@ import { RxAvatar as AvatarIcon } from "react-icons/rx";
|
|
|
18
18
|
import { LuBadgeAlert as BadgeIcon } from "react-icons/lu";
|
|
19
19
|
import { RiCheckboxFill as CheckBoxIcon } from "react-icons/ri";
|
|
20
20
|
import { LuListCollapse as CollapseIcon } from "react-icons/lu";
|
|
21
|
+
import { LuSparkles as EffectIcon } from "react-icons/lu";
|
|
21
22
|
import { CiGrid31 as FlexIcon } from "react-icons/ci";
|
|
22
23
|
import { IoMdGrid as GridIcon } from "react-icons/io";
|
|
23
24
|
import { FaPager as HeaderIcon } from "react-icons/fa6";
|
|
@@ -31,6 +32,7 @@ import { BsFillMarkdownFill as MarkdownIcon } from 'react-icons/bs' ;
|
|
|
31
32
|
import { GiDominoMask as MaskIcon } from "react-icons/gi";
|
|
32
33
|
import { RiLayoutMasonryFill as MasonryIcon } from "react-icons/ri";
|
|
33
34
|
import { TfiLayoutMenuSeparated as MenuIcon } from "react-icons/tfi" ;
|
|
35
|
+
import { LuLayoutPanelTop as MegamenuIcon } from "react-icons/lu";
|
|
34
36
|
import { SiDialogflow as ModalIcon } from "react-icons/si";
|
|
35
37
|
import { MdAnimation as MotionIcon } from "react-icons/md";
|
|
36
38
|
import { BiFoodMenu as NavigationIcon } from "react-icons/bi";
|
|
@@ -91,6 +93,7 @@ const navigation =
|
|
|
91
93
|
[
|
|
92
94
|
{ id : 'avatars' , type : LINK , Icon : AvatarIcon , path : '/lab/avatars' } ,
|
|
93
95
|
{ id : 'badges' , type : LINK , Icon : BadgeIcon , path : '/lab/badges' } ,
|
|
96
|
+
{ id : 'effects' , type : LINK , Icon : EffectIcon , path : '/lab/effects' } ,
|
|
94
97
|
{ id : 'images' , type : LINK , Icon : ImageIcon , path : '/lab/images' } ,
|
|
95
98
|
{ id : 'lists' , type : LINK , Icon : ListIcon , path : '/lab/lists' } ,
|
|
96
99
|
{ id : 'masks' , type : LINK , Icon : MaskIcon , path : '/lab/masks' } ,
|
|
@@ -110,6 +113,7 @@ const navigation =
|
|
|
110
113
|
[
|
|
111
114
|
{ id : 'pagination' , type : LINK , Icon : PaginationIcon , path : '/lab/pagination' } ,
|
|
112
115
|
{ id : 'menus' , type : LINK , Icon : MenuIcon , path : '/lab/menus' } ,
|
|
116
|
+
{ id : 'megamenu' , type : LINK , Icon : MegamenuIcon , path : '/lab/megamenu' } ,
|
|
113
117
|
]
|
|
114
118
|
} ,
|
|
115
119
|
{
|
|
@@ -10,6 +10,7 @@ const navigation =
|
|
|
10
10
|
display : 'Display' ,
|
|
11
11
|
avatars : 'Avatars' ,
|
|
12
12
|
badges : 'Badges' ,
|
|
13
|
+
effects : 'Effets' ,
|
|
13
14
|
headers : 'Entêtes' ,
|
|
14
15
|
images : 'Images' ,
|
|
15
16
|
markdown : 'Markdown' ,
|
|
@@ -45,6 +46,7 @@ const navigation =
|
|
|
45
46
|
navigation : 'Navigation' ,
|
|
46
47
|
lists : 'Listes' ,
|
|
47
48
|
menus : 'Menus' ,
|
|
49
|
+
megamenu : 'Megamenu' ,
|
|
48
50
|
pagination : 'Pagination' ,
|
|
49
51
|
tests : 'Tests' ,
|
|
50
52
|
|
|
@@ -59,6 +61,7 @@ const navigation =
|
|
|
59
61
|
display : 'Display' ,
|
|
60
62
|
avatars : 'Avatars' ,
|
|
61
63
|
badges : 'Badges' ,
|
|
64
|
+
effects : 'Effects' ,
|
|
62
65
|
headers : 'Headers' ,
|
|
63
66
|
images : 'Images' ,
|
|
64
67
|
markdown : 'Markdown' ,
|
|
@@ -94,6 +97,7 @@ const navigation =
|
|
|
94
97
|
navigation : 'Navigation' ,
|
|
95
98
|
lists : 'Lists' ,
|
|
96
99
|
menus : 'Menus' ,
|
|
100
|
+
megamenu : 'Megamenu' ,
|
|
97
101
|
pagination : 'Pagination' ,
|
|
98
102
|
tests : 'Tests' ,
|
|
99
103
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client' ;
|
|
2
|
+
|
|
3
|
+
import AuraDemo from '@/demo/effects/AuraDemo' ;
|
|
4
|
+
import Container from '@/display/Container' ;
|
|
5
|
+
import Page from '@/display/Page' ;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Effects showcase page.
|
|
9
|
+
*
|
|
10
|
+
* Displays the visual effect components (Aura, …).
|
|
11
|
+
*
|
|
12
|
+
* @param {Object} props
|
|
13
|
+
*/
|
|
14
|
+
const EffectsShowcase = () =>
|
|
15
|
+
{
|
|
16
|
+
return (
|
|
17
|
+
<Page full className='gap-8'>
|
|
18
|
+
|
|
19
|
+
<Container className="text-center" maxWidth="max-w-4xl">
|
|
20
|
+
<h1 className="text-4xl font-bold bg-linear-to-r from-secondary to-primary inline-block text-transparent bg-clip-text">
|
|
21
|
+
Effect Components
|
|
22
|
+
</h1>
|
|
23
|
+
</Container>
|
|
24
|
+
|
|
25
|
+
<AuraDemo />
|
|
26
|
+
|
|
27
|
+
</Page>
|
|
28
|
+
) ;
|
|
29
|
+
} ;
|
|
30
|
+
|
|
31
|
+
export default EffectsShowcase ;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use client' ;
|
|
2
|
+
|
|
3
|
+
import MegamenuDemo from '@/demo/menus/MegamenuDemo' ;
|
|
4
|
+
import Container from '@/display/Container' ;
|
|
5
|
+
import Page from '@/display/Page' ;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Megamenu showcase page.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} props
|
|
11
|
+
*/
|
|
12
|
+
const MegamenuShowcase = () =>
|
|
13
|
+
{
|
|
14
|
+
return (
|
|
15
|
+
<Page full className='gap-8'>
|
|
16
|
+
|
|
17
|
+
<Container className="text-center" maxWidth="max-w-4xl">
|
|
18
|
+
<h1 className="text-4xl font-bold bg-linear-to-r from-secondary to-primary inline-block text-transparent bg-clip-text">
|
|
19
|
+
Megamenu Component
|
|
20
|
+
</h1>
|
|
21
|
+
</Container>
|
|
22
|
+
|
|
23
|
+
<MegamenuDemo />
|
|
24
|
+
|
|
25
|
+
</Page>
|
|
26
|
+
) ;
|
|
27
|
+
} ;
|
|
28
|
+
|
|
29
|
+
export default MegamenuShowcase ;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use client' ;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Aura component for DaisyUI 5.6.
|
|
5
|
+
*
|
|
6
|
+
* Wraps any content with a border light effect. By default the aura is always
|
|
7
|
+
* animating ; set `trigger="hover"` to only reveal and animate it on hover.
|
|
8
|
+
*
|
|
9
|
+
* The light effect is built on `currentColor`, so in `hover` mode the wrapper
|
|
10
|
+
* colour is forced transparent at rest. Make sure the wrapped content sets its
|
|
11
|
+
* own text colour (e.g. a `card` with `text-base-content`) so it stays visible.
|
|
12
|
+
*
|
|
13
|
+
* @module components/Aura
|
|
14
|
+
* @see https://daisyui.com/components/aura
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```jsx
|
|
18
|
+
* // Always-on rainbow aura around a card
|
|
19
|
+
* <Aura variant="rainbow">
|
|
20
|
+
* <div className="card bg-base-100"><div className="card-body">Hi</div></div>
|
|
21
|
+
* </Aura>
|
|
22
|
+
*
|
|
23
|
+
* // Reveal on hover only, custom colour and duration
|
|
24
|
+
* <Aura trigger="hover" color="primary" duration={2000}>
|
|
25
|
+
* <button className="btn">Hover me</button>
|
|
26
|
+
* </Aura>
|
|
27
|
+
*
|
|
28
|
+
* // Custom colour + background tint (daisyUI custom-colour example)
|
|
29
|
+
* <Aura color="warning" background="warning">
|
|
30
|
+
* <div className="card bg-base-100 text-base-content"><div className="card-body">Hi</div></div>
|
|
31
|
+
* </Aura>
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import cn from '../themes/helpers/cn' ;
|
|
36
|
+
|
|
37
|
+
import getAuraClasses, { ALWAYS, HOVER } from '../themes/effects/aura' ;
|
|
38
|
+
|
|
39
|
+
import getTextColor from '../themes/colors/textColor' ;
|
|
40
|
+
import getBackgroundColor from '../themes/colors/backgroundColor' ;
|
|
41
|
+
import { TRANSPARENT } from '../themes/colors/tailwindcss' ;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param {Object} props
|
|
45
|
+
* @param {React.ElementType} [props.as='div'] - Root element type.
|
|
46
|
+
* @param {string} [props.background] - Background tint colour constant (→ `bg-*`).
|
|
47
|
+
* @param {React.ReactNode} [props.children] - Wrapped content.
|
|
48
|
+
* @param {string} [props.className] - Additional class name.
|
|
49
|
+
* @param {string} [props.color] - Light colour constant (→ `text-*`).
|
|
50
|
+
* @param {number} [props.duration] - Animation duration in milliseconds.
|
|
51
|
+
* @param {React.Ref} [props.ref] - Forwarded ref.
|
|
52
|
+
* @param {import('../themes/effects/aura').AuraSize} [props.size='md'] - Aura size.
|
|
53
|
+
* @param {import('../themes/effects/aura').AuraTrigger} [props.trigger='always'] - 'always' or 'hover'.
|
|
54
|
+
* @param {import('../themes/effects/aura').AuraVariant} [props.variant] - Aura style variant.
|
|
55
|
+
*/
|
|
56
|
+
const Aura =
|
|
57
|
+
({
|
|
58
|
+
as ,
|
|
59
|
+
background ,
|
|
60
|
+
children ,
|
|
61
|
+
className ,
|
|
62
|
+
color ,
|
|
63
|
+
duration ,
|
|
64
|
+
ref ,
|
|
65
|
+
size ,
|
|
66
|
+
style ,
|
|
67
|
+
trigger = ALWAYS ,
|
|
68
|
+
variant ,
|
|
69
|
+
...rest
|
|
70
|
+
}) =>
|
|
71
|
+
{
|
|
72
|
+
const Component = as || 'div' ;
|
|
73
|
+
|
|
74
|
+
// Resolve the light colour class (e.g. 'text-primary') from the constant.
|
|
75
|
+
const colorClass = color ? Object.keys( getTextColor( color ) )[ 0 ] : null ;
|
|
76
|
+
|
|
77
|
+
// In `hover` mode the wrapper colour is transparent at rest and restored on
|
|
78
|
+
// hover, so the aura (built on currentColor) only shows while hovered.
|
|
79
|
+
const colorClasses = trigger === HOVER
|
|
80
|
+
? cn(
|
|
81
|
+
getTextColor( TRANSPARENT ) ,
|
|
82
|
+
colorClass ? `hover:${ colorClass }` : 'hover:text-inherit' ,
|
|
83
|
+
)
|
|
84
|
+
: colorClass ;
|
|
85
|
+
|
|
86
|
+
const classNames = cn
|
|
87
|
+
(
|
|
88
|
+
getAuraClasses({ size , trigger , variant }) ,
|
|
89
|
+
colorClasses ,
|
|
90
|
+
background && getBackgroundColor( background ) ,
|
|
91
|
+
className ,
|
|
92
|
+
) ;
|
|
93
|
+
|
|
94
|
+
// Duration drives the DaisyUI aura animation through the `--tw-duration` CSS
|
|
95
|
+
// variable. Setting it inline is JIT-proof (a `duration-[Nms]` class built at
|
|
96
|
+
// runtime would never be emitted by Tailwind).
|
|
97
|
+
const mergedStyle = duration != null
|
|
98
|
+
? { '--tw-duration' : `${ duration }ms` , ...style }
|
|
99
|
+
: style ;
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<Component className={ classNames } ref={ ref } style={ mergedStyle } { ...rest }>
|
|
103
|
+
{ children }
|
|
104
|
+
</Component>
|
|
105
|
+
) ;
|
|
106
|
+
} ;
|
|
107
|
+
|
|
108
|
+
Aura.displayName = 'Aura' ;
|
|
109
|
+
|
|
110
|
+
export default Aura ;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
'use client' ;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Megamenu component for DaisyUI 5.6.
|
|
5
|
+
*
|
|
6
|
+
* A data-driven megamenu : each item renders a trigger button + a native popover
|
|
7
|
+
* holding its content. Unique HTML ids (`popovertarget` / `popover`) are derived
|
|
8
|
+
* from `useId()`, so no manual id wiring is needed. Anchoring is handled by
|
|
9
|
+
* DaisyUI's CSS (native popover API + CSS anchor positioning).
|
|
10
|
+
*
|
|
11
|
+
* Several megamenus can coexist on a page : basic ones anchor each popover to its
|
|
12
|
+
* own preceding trigger (CSS anchor resolution is DOM-order based), and `wide` /
|
|
13
|
+
* `full` get a unique per-instance anchor name (see below) instead of daisyUI's
|
|
14
|
+
* shared `--megamenu`, so they never collide.
|
|
15
|
+
*
|
|
16
|
+
* ⚠️ At most **10** popovers per megamenu (DaisyUI limit). Requires a browser with
|
|
17
|
+
* popover + anchor positioning support (progressive enhancement — no polyfill).
|
|
18
|
+
*
|
|
19
|
+
* @module components/menus/Megamenu
|
|
20
|
+
* @see https://daisyui.com/components/megamenu
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```jsx
|
|
24
|
+
* <Megamenu
|
|
25
|
+
* responsive
|
|
26
|
+
* width="wide"
|
|
27
|
+
* items={[
|
|
28
|
+
* { label: 'Services', links: [ { label: 'Enterprise', href: '#' }, { label: 'Security', href: '#' } ] },
|
|
29
|
+
* { label: 'AI', content: <div className="p-4">Custom content</div> },
|
|
30
|
+
* ]}
|
|
31
|
+
* />
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { Fragment, useId } from 'react' ;
|
|
36
|
+
|
|
37
|
+
import cn from '../../themes/helpers/cn' ;
|
|
38
|
+
|
|
39
|
+
import getMegamenuClasses, { FULL, MEGAMENU_ACTIVE, WIDE } from '../../themes/navigation/megamenu' ;
|
|
40
|
+
import getMenuClasses from '../../themes/navigation/menu' ;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* DaisyUI supports at most 10 popovers inside a megamenu.
|
|
44
|
+
* @type {number}
|
|
45
|
+
*/
|
|
46
|
+
export const MAX_POPOVERS = 10 ;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @typedef {Object} MegamenuItem
|
|
50
|
+
* @property {string} label - Trigger button label.
|
|
51
|
+
* @property {React.ReactNode} [content] - Popover content (takes precedence over `links`).
|
|
52
|
+
* @property {Array<{ label: string, href?: string }>} [links] - Convenience : renders a `menu` of links.
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {Object} props
|
|
57
|
+
* @param {string} [props.className] - Extra classes for the megamenu container.
|
|
58
|
+
* @param {MegamenuItem[]} [props.items=[]] - Menu items (max 10).
|
|
59
|
+
* @param {string} [props.popoverClassName] - Extra classes for each popover panel.
|
|
60
|
+
* @param {React.Ref} [props.ref] - Forwarded ref on the container.
|
|
61
|
+
* @param {boolean} [props.responsive=false] - Add a mobile trigger button + vertical layout on small screens.
|
|
62
|
+
* @param {import('../../themes/navigation/megamenu').MegamenuSize} [props.size] - Megamenu size.
|
|
63
|
+
* @param {string} [props.triggerClassName] - Extra classes for the mobile trigger button.
|
|
64
|
+
* @param {string} [props.triggerLabel='Menu'] - Mobile trigger button label.
|
|
65
|
+
* @param {boolean} [props.vertical=false] - Always vertical layout.
|
|
66
|
+
* @param {import('../../themes/navigation/megamenu').MegamenuWidth} [props.width] - Width modifier ('wide' | 'full').
|
|
67
|
+
*/
|
|
68
|
+
const Megamenu =
|
|
69
|
+
({
|
|
70
|
+
className ,
|
|
71
|
+
items = [] ,
|
|
72
|
+
popoverClassName ,
|
|
73
|
+
ref ,
|
|
74
|
+
responsive = false ,
|
|
75
|
+
size ,
|
|
76
|
+
style ,
|
|
77
|
+
triggerClassName ,
|
|
78
|
+
triggerLabel = 'Menu' ,
|
|
79
|
+
vertical = false ,
|
|
80
|
+
width ,
|
|
81
|
+
...rest
|
|
82
|
+
}) =>
|
|
83
|
+
{
|
|
84
|
+
const uid = useId() ;
|
|
85
|
+
|
|
86
|
+
// useId() can contain ':' — strip to a clean base usable as an HTML id.
|
|
87
|
+
const base = `megamenu-${ uid.replace( /[^a-z0-9]/gi , '' ) }` ;
|
|
88
|
+
const containerId = base ;
|
|
89
|
+
|
|
90
|
+
// wide / full anchor every popover to the container via a shared CSS anchor.
|
|
91
|
+
// DaisyUI hard-codes `--megamenu`, which collides when several coexist ; we
|
|
92
|
+
// give each instance a unique anchor name (inline style, so JIT-proof) — this
|
|
93
|
+
// mirrors daisyUI's manual `[anchor-name:--megamenu-x]` pattern automatically.
|
|
94
|
+
const anchored = width === WIDE || width === FULL ;
|
|
95
|
+
const anchorName = `--${ base }` ;
|
|
96
|
+
|
|
97
|
+
const list = Array.isArray( items ) ? items.slice( 0 , MAX_POPOVERS ) : [] ;
|
|
98
|
+
|
|
99
|
+
if ( process.env.NODE_ENV !== 'production' && Array.isArray( items ) && items.length > MAX_POPOVERS )
|
|
100
|
+
{
|
|
101
|
+
console.warn( `[Megamenu] DaisyUI supports at most ${ MAX_POPOVERS } popovers ; ${ items.length } provided, extra items are ignored.` ) ;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const classes = getMegamenuClasses({ className , responsive , size , vertical , width }) ;
|
|
105
|
+
|
|
106
|
+
// The container is only a popover when responsive (the mobile trigger toggles it).
|
|
107
|
+
const containerPopover = responsive ? { id : containerId , popover : 'auto' } : {} ;
|
|
108
|
+
|
|
109
|
+
const containerStyle = anchored ? { anchorName , ...style } : style ;
|
|
110
|
+
const popoverStyle = anchored ? { positionAnchor : anchorName } : undefined ;
|
|
111
|
+
|
|
112
|
+
const renderContent = ( item ) =>
|
|
113
|
+
{
|
|
114
|
+
if ( item?.content )
|
|
115
|
+
{
|
|
116
|
+
return item.content ;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if ( Array.isArray( item?.links ) )
|
|
120
|
+
{
|
|
121
|
+
return (
|
|
122
|
+
<ul className={ getMenuClasses() }>
|
|
123
|
+
{ item.links.map( ( link , i ) => (
|
|
124
|
+
<li key={ i }>
|
|
125
|
+
<a href={ link?.href }>{ link?.label }</a>
|
|
126
|
+
</li>
|
|
127
|
+
) ) }
|
|
128
|
+
</ul>
|
|
129
|
+
) ;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return null ;
|
|
133
|
+
} ;
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<>
|
|
137
|
+
{ responsive && (
|
|
138
|
+
<button className={ cn( 'btn sm:hidden' , triggerClassName ) } popoverTarget={ containerId }>
|
|
139
|
+
{ triggerLabel }
|
|
140
|
+
</button>
|
|
141
|
+
)}
|
|
142
|
+
|
|
143
|
+
<div className={ classes } ref={ ref } style={ containerStyle } { ...containerPopover } { ...rest }>
|
|
144
|
+
<span className={ MEGAMENU_ACTIVE } />
|
|
145
|
+
|
|
146
|
+
{ list.map( ( item , index ) =>
|
|
147
|
+
{
|
|
148
|
+
const popoverId = `${ base }-p${ index }` ;
|
|
149
|
+
return (
|
|
150
|
+
<Fragment key={ popoverId }>
|
|
151
|
+
<button popoverTarget={ popoverId }>{ item?.label }</button>
|
|
152
|
+
<div id={ popoverId } popover="auto" className={ popoverClassName } style={ popoverStyle }>
|
|
153
|
+
{ renderContent( item ) }
|
|
154
|
+
</div>
|
|
155
|
+
</Fragment>
|
|
156
|
+
) ;
|
|
157
|
+
} )}
|
|
158
|
+
</div>
|
|
159
|
+
</>
|
|
160
|
+
) ;
|
|
161
|
+
} ;
|
|
162
|
+
|
|
163
|
+
Megamenu.displayName = 'Megamenu' ;
|
|
164
|
+
|
|
165
|
+
export default Megamenu ;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
'use client' ;
|
|
2
|
+
|
|
3
|
+
import Container from '@/display/Container' ;
|
|
4
|
+
import Divider from '@/components/Divider' ;
|
|
5
|
+
import Aura from '@/components/Aura' ;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Aura showcase — variants, sizes, custom colours, hover trigger and duration.
|
|
9
|
+
*/
|
|
10
|
+
const AuraDemo = () =>
|
|
11
|
+
{
|
|
12
|
+
const variants = [ 'dual' , 'rainbow' , 'holo' , 'gold' , 'silver' , 'glow' ] ;
|
|
13
|
+
const auraSizes = [ 'xs' , 'sm' , 'md' , 'lg' , 'xl' ] ;
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Container className="flex flex-col gap-8 bg-base-200/60 p-8 rounded-box" maxWidth="max-w-7xl">
|
|
17
|
+
|
|
18
|
+
<h2 className="text-3xl font-bold">Aura Examples</h2>
|
|
19
|
+
|
|
20
|
+
{/* Default */}
|
|
21
|
+
<div className="flex flex-col gap-4">
|
|
22
|
+
<h3 className="text-xl font-semibold">Default</h3>
|
|
23
|
+
<div className="flex flex-wrap items-center gap-8">
|
|
24
|
+
<Aura>
|
|
25
|
+
<div className="card bg-base-100">
|
|
26
|
+
<div className="card-body"><p>This card has aura</p></div>
|
|
27
|
+
</div>
|
|
28
|
+
</Aura>
|
|
29
|
+
<Aura>
|
|
30
|
+
<button className="btn">button with aura</button>
|
|
31
|
+
</Aura>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<Divider />
|
|
36
|
+
|
|
37
|
+
{/* Variants */}
|
|
38
|
+
<div className="flex flex-col gap-4">
|
|
39
|
+
<h3 className="text-xl font-semibold">Variants</h3>
|
|
40
|
+
<div className="flex flex-wrap items-center gap-8">
|
|
41
|
+
{ variants.map( ( variant ) => (
|
|
42
|
+
<Aura key={ variant } variant={ variant }>
|
|
43
|
+
<div className="card bg-base-100">
|
|
44
|
+
<div className="card-body"><p>{ variant }</p></div>
|
|
45
|
+
</div>
|
|
46
|
+
</Aura>
|
|
47
|
+
) ) }
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<Divider />
|
|
52
|
+
|
|
53
|
+
{/* Custom colour */}
|
|
54
|
+
<div className="flex flex-col gap-4">
|
|
55
|
+
<h3 className="text-xl font-semibold">Custom colour & background</h3>
|
|
56
|
+
<div className="flex flex-wrap items-center gap-8">
|
|
57
|
+
<Aura color="warning">
|
|
58
|
+
<div className="card bg-base-100 text-base-content">
|
|
59
|
+
<div className="card-body"><p>Custom colour</p></div>
|
|
60
|
+
</div>
|
|
61
|
+
</Aura>
|
|
62
|
+
<Aura color="primary" background="primary">
|
|
63
|
+
<div className="card bg-base-100 text-base-content">
|
|
64
|
+
<div className="card-body"><p>Colour + background</p></div>
|
|
65
|
+
</div>
|
|
66
|
+
</Aura>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<Divider />
|
|
71
|
+
|
|
72
|
+
{/* Hover trigger */}
|
|
73
|
+
<div className="flex flex-col gap-4">
|
|
74
|
+
<h3 className="text-xl font-semibold">Trigger: hover</h3>
|
|
75
|
+
<p className="text-sm text-base-content/70">The aura only lights up while hovering.</p>
|
|
76
|
+
<div className="flex flex-wrap items-center gap-8">
|
|
77
|
+
<Aura trigger="hover" variant="rainbow">
|
|
78
|
+
<div className="card bg-base-100 text-base-content">
|
|
79
|
+
<div className="card-body"><p>Hover me (rainbow)</p></div>
|
|
80
|
+
</div>
|
|
81
|
+
</Aura>
|
|
82
|
+
<Aura trigger="hover" color="primary">
|
|
83
|
+
<button className="btn">Hover me</button>
|
|
84
|
+
</Aura>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<Divider />
|
|
89
|
+
|
|
90
|
+
{/* Sizes */}
|
|
91
|
+
<div className="flex flex-col gap-4">
|
|
92
|
+
<h3 className="text-xl font-semibold">Sizes</h3>
|
|
93
|
+
<div className="flex flex-wrap items-center gap-6">
|
|
94
|
+
{ auraSizes.map( ( size ) => (
|
|
95
|
+
<Aura key={ size } size={ size }>
|
|
96
|
+
<button className="btn">{ size.toUpperCase() }</button>
|
|
97
|
+
</Aura>
|
|
98
|
+
) ) }
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<Divider />
|
|
103
|
+
|
|
104
|
+
{/* Custom duration */}
|
|
105
|
+
<div className="flex flex-col gap-4">
|
|
106
|
+
<h3 className="text-xl font-semibold">Custom duration</h3>
|
|
107
|
+
<div className="flex flex-wrap items-center gap-8">
|
|
108
|
+
<Aura variant="rainbow" duration={ 2000 }>
|
|
109
|
+
<div className="card bg-base-100">
|
|
110
|
+
<div className="card-body"><p>2000ms duration</p></div>
|
|
111
|
+
</div>
|
|
112
|
+
</Aura>
|
|
113
|
+
<Aura variant="rainbow" duration={ 10000 }>
|
|
114
|
+
<div className="card bg-base-100">
|
|
115
|
+
<div className="card-body"><p>10000ms duration</p></div>
|
|
116
|
+
</div>
|
|
117
|
+
</Aura>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
</Container>
|
|
122
|
+
) ;
|
|
123
|
+
} ;
|
|
124
|
+
|
|
125
|
+
export default AuraDemo ;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
'use client' ;
|
|
2
|
+
|
|
3
|
+
import Container from '@/display/Container' ;
|
|
4
|
+
import Divider from '@/components/Divider' ;
|
|
5
|
+
import Megamenu from '@/components/menus/Megamenu' ;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Megamenu showcase.
|
|
9
|
+
*
|
|
10
|
+
* Several megamenus coexist on this page : basic ones anchor each popover to its
|
|
11
|
+
* own trigger (DOM-order anchor resolution), and wide/full get a unique anchor
|
|
12
|
+
* name per instance so they never collide.
|
|
13
|
+
*/
|
|
14
|
+
const MegamenuDemo = () =>
|
|
15
|
+
{
|
|
16
|
+
const items =
|
|
17
|
+
[
|
|
18
|
+
{
|
|
19
|
+
label : 'Services' ,
|
|
20
|
+
links :
|
|
21
|
+
[
|
|
22
|
+
{ label : 'Enterprise' , href : '#' } ,
|
|
23
|
+
{ label : 'CRM software' , href : '#' } ,
|
|
24
|
+
{ label : 'Security' , href : '#' } ,
|
|
25
|
+
{ label : 'Consulting' , href : '#' } ,
|
|
26
|
+
] ,
|
|
27
|
+
} ,
|
|
28
|
+
{
|
|
29
|
+
label : 'AI' ,
|
|
30
|
+
links :
|
|
31
|
+
[
|
|
32
|
+
{ label : 'AI infrastructure' , href : '#' } ,
|
|
33
|
+
{ label : 'Image generation' , href : '#' } ,
|
|
34
|
+
{ label : 'MCP servers' , href : '#' } ,
|
|
35
|
+
] ,
|
|
36
|
+
} ,
|
|
37
|
+
{
|
|
38
|
+
label : 'Cloud Solutions' ,
|
|
39
|
+
links :
|
|
40
|
+
[
|
|
41
|
+
{ label : 'Cloud computing' , href : '#' } ,
|
|
42
|
+
{ label : 'Storage solutions' , href : '#' } ,
|
|
43
|
+
{ label : 'Database services' , href : '#' } ,
|
|
44
|
+
{ label : 'CDN performance' , href : '#' } ,
|
|
45
|
+
] ,
|
|
46
|
+
} ,
|
|
47
|
+
] ;
|
|
48
|
+
|
|
49
|
+
const plainItems =
|
|
50
|
+
[
|
|
51
|
+
{ label : 'One' , content : <div className="p-4">Content for the first item</div> } ,
|
|
52
|
+
{ label : 'Two' , content : <div className="p-4">Content for the second item</div> } ,
|
|
53
|
+
{ label : 'Three' , content : <div className="p-4">Content for the third item</div> } ,
|
|
54
|
+
] ;
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<Container className="flex flex-col gap-8 bg-base-200/60 p-8 rounded-box" maxWidth="max-w-7xl">
|
|
58
|
+
|
|
59
|
+
<h2 className="text-3xl font-bold">Megamenu</h2>
|
|
60
|
+
<p className="text-sm text-base-content/70">
|
|
61
|
+
Built on the native popover API + CSS anchor positioning (recent browsers).
|
|
62
|
+
Multiple megamenus coexist on this page.
|
|
63
|
+
</p>
|
|
64
|
+
|
|
65
|
+
{/* Responsive small menus */}
|
|
66
|
+
<div className="flex flex-col gap-4">
|
|
67
|
+
<h3 className="text-xl font-semibold">Responsive small menus</h3>
|
|
68
|
+
<Megamenu
|
|
69
|
+
responsive
|
|
70
|
+
items = { items }
|
|
71
|
+
className = "w-full p-2 border border-base-300"
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<Divider />
|
|
76
|
+
|
|
77
|
+
{/* Wide */}
|
|
78
|
+
<div className="flex flex-col gap-4">
|
|
79
|
+
<h3 className="text-xl font-semibold">Wide popovers</h3>
|
|
80
|
+
<Megamenu
|
|
81
|
+
responsive
|
|
82
|
+
width = "wide"
|
|
83
|
+
items = { items }
|
|
84
|
+
className = "w-full p-2 border border-base-300"
|
|
85
|
+
/>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<Divider />
|
|
89
|
+
|
|
90
|
+
{/* Full width inside a navbar */}
|
|
91
|
+
<div className="flex flex-col gap-4">
|
|
92
|
+
<h3 className="text-xl font-semibold">Full width inside a navbar</h3>
|
|
93
|
+
<div className="navbar bg-base-100 shadow-sm">
|
|
94
|
+
<div className="navbar-start">
|
|
95
|
+
<a className="btn btn-ghost text-xl">daisyUI</a>
|
|
96
|
+
</div>
|
|
97
|
+
<div className="navbar-center">
|
|
98
|
+
<Megamenu responsive width="full" items={ items } />
|
|
99
|
+
</div>
|
|
100
|
+
<div className="navbar-end">
|
|
101
|
+
<button className="btn">Login</button>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<Divider />
|
|
107
|
+
|
|
108
|
+
{/* Sizes — five basic megamenus coexisting */}
|
|
109
|
+
<div className="flex flex-col gap-4">
|
|
110
|
+
<h3 className="text-xl font-semibold">Sizes</h3>
|
|
111
|
+
{ [ 'xs' , 'sm' , 'md' , 'lg' , 'xl' ].map( ( size ) => (
|
|
112
|
+
<Megamenu
|
|
113
|
+
key={ size }
|
|
114
|
+
size={ size }
|
|
115
|
+
items={ plainItems }
|
|
116
|
+
className="w-full p-2 border border-base-300"
|
|
117
|
+
/>
|
|
118
|
+
) ) }
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
</Container>
|
|
122
|
+
) ;
|
|
123
|
+
} ;
|
|
124
|
+
|
|
125
|
+
export default MegamenuDemo ;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aura class name generator for DaisyUI 5.6.
|
|
3
|
+
*
|
|
4
|
+
* Aura is a border light effect that wraps around any component. The effect is
|
|
5
|
+
* always animating by default ; pass `trigger: 'hover'` to only light it up on
|
|
6
|
+
* hover (the gate is built on top of the DaisyUI classes, see the component).
|
|
7
|
+
*
|
|
8
|
+
* @module themes/effects/aura
|
|
9
|
+
* @see https://daisyui.com/components/aura
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import cn from '../helpers/cn' ;
|
|
13
|
+
|
|
14
|
+
import { LG, MD, SM, XL, XS } from '../sizing/sizes' ;
|
|
15
|
+
|
|
16
|
+
// Variants (styles)
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {'dual' | 'rainbow' | 'holo' | 'gold' | 'silver' | 'glow'} AuraVariant
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export const DUAL = 'dual' ;
|
|
23
|
+
export const RAINBOW = 'rainbow' ;
|
|
24
|
+
export const HOLO = 'holo' ;
|
|
25
|
+
export const GOLD = 'gold' ;
|
|
26
|
+
export const SILVER = 'silver' ;
|
|
27
|
+
export const GLOW = 'glow' ;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Valid aura variants.
|
|
31
|
+
* @type {AuraVariant[]}
|
|
32
|
+
*/
|
|
33
|
+
export const variants = [ DUAL, RAINBOW, HOLO, GOLD, SILVER, GLOW ] ;
|
|
34
|
+
|
|
35
|
+
const variantMap =
|
|
36
|
+
{
|
|
37
|
+
[ DUAL ] : 'aura-dual',
|
|
38
|
+
[ RAINBOW ] : 'aura-rainbow',
|
|
39
|
+
[ HOLO ] : 'aura-holo',
|
|
40
|
+
[ GOLD ] : 'aura-gold',
|
|
41
|
+
[ SILVER ] : 'aura-silver',
|
|
42
|
+
[ GLOW ] : 'aura-glow',
|
|
43
|
+
} ;
|
|
44
|
+
|
|
45
|
+
// Sizes
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @typedef {'xs' | 'sm' | 'md' | 'lg' | 'xl'} AuraSize
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Valid aura sizes.
|
|
53
|
+
* @type {AuraSize[]}
|
|
54
|
+
*/
|
|
55
|
+
export const sizes = [ XS, SM, MD, LG, XL ] ;
|
|
56
|
+
|
|
57
|
+
const sizeMap =
|
|
58
|
+
{
|
|
59
|
+
[ XS ] : 'aura-xs',
|
|
60
|
+
[ SM ] : 'aura-sm',
|
|
61
|
+
[ MD ] : 'aura-md',
|
|
62
|
+
[ LG ] : 'aura-lg',
|
|
63
|
+
[ XL ] : 'aura-xl',
|
|
64
|
+
} ;
|
|
65
|
+
|
|
66
|
+
// Triggers
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @typedef {'always' | 'hover'} AuraTrigger
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
export const ALWAYS = 'always' ;
|
|
73
|
+
export const HOVER = 'hover' ;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Valid aura triggers.
|
|
77
|
+
* @type {AuraTrigger[]}
|
|
78
|
+
*/
|
|
79
|
+
export const triggers = [ ALWAYS, HOVER ] ;
|
|
80
|
+
|
|
81
|
+
export const AURA = 'aura' ;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Pauses the rotation animation (rest state of the `hover` trigger).
|
|
85
|
+
* @type {string}
|
|
86
|
+
*/
|
|
87
|
+
export const AURA_PAUSED = '[animation-play-state:paused]' ;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Resumes the rotation animation on hover (`hover` trigger).
|
|
91
|
+
* @type {string}
|
|
92
|
+
*/
|
|
93
|
+
export const AURA_RUNNING = 'hover:[animation-play-state:running]' ;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Generates aura class names for DaisyUI 5.6.
|
|
97
|
+
*
|
|
98
|
+
* Colour (`text-*`) and background (`bg-*`) tints are NOT handled here — they
|
|
99
|
+
* are resolved from the colour constants by the `<Aura>` component, because the
|
|
100
|
+
* `hover` trigger needs to gate the colour on `currentColor`.
|
|
101
|
+
*
|
|
102
|
+
* The animation duration is NOT a class : it is driven by the `--tw-duration`
|
|
103
|
+
* CSS variable, applied as an inline style by the `<Aura>` component (a dynamic
|
|
104
|
+
* `duration-[Nms]` class would never be generated by Tailwind's JIT scanner).
|
|
105
|
+
*
|
|
106
|
+
* @param {Object} [props]
|
|
107
|
+
* @param {Object} [props.after] - Class overrides applied after.
|
|
108
|
+
* @param {Object} [props.before] - Class definitions applied before.
|
|
109
|
+
* @param {string} [props.beforeClassName] - CSS string prepended.
|
|
110
|
+
* @param {string} [props.className] - CSS string appended.
|
|
111
|
+
* @param {AuraSize} [props.size='md'] - Aura size (xs, sm, md, lg, xl).
|
|
112
|
+
* @param {AuraTrigger} [props.trigger='always'] - 'always' (default) or 'hover'.
|
|
113
|
+
* @param {AuraVariant} [props.variant] - Aura style variant.
|
|
114
|
+
*
|
|
115
|
+
* @returns {string} Combined class names.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```js
|
|
119
|
+
* getAuraClasses({ variant: 'rainbow', size: 'lg' }) ;
|
|
120
|
+
* // → 'aura aura-rainbow aura-lg'
|
|
121
|
+
*
|
|
122
|
+
* getAuraClasses({ trigger: 'hover' }) ;
|
|
123
|
+
* // → 'aura aura-md [animation-play-state:paused] hover:[animation-play-state:running]'
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export const getAuraClasses =
|
|
127
|
+
({
|
|
128
|
+
after,
|
|
129
|
+
before,
|
|
130
|
+
beforeClassName,
|
|
131
|
+
className,
|
|
132
|
+
size = MD,
|
|
133
|
+
trigger = ALWAYS,
|
|
134
|
+
variant,
|
|
135
|
+
}
|
|
136
|
+
= {} ) => cn
|
|
137
|
+
(
|
|
138
|
+
beforeClassName,
|
|
139
|
+
{
|
|
140
|
+
...before,
|
|
141
|
+
|
|
142
|
+
[ AURA ] : true,
|
|
143
|
+
|
|
144
|
+
...!!variantMap[variant] && { [ variantMap[variant] ] : true },
|
|
145
|
+
...!!sizeMap[size] && { [ sizeMap[size] ] : true },
|
|
146
|
+
...trigger === HOVER && { [ AURA_PAUSED ] : true, [ AURA_RUNNING ] : true },
|
|
147
|
+
|
|
148
|
+
...after,
|
|
149
|
+
},
|
|
150
|
+
className,
|
|
151
|
+
) ;
|
|
152
|
+
|
|
153
|
+
export default getAuraClasses ;
|
|
154
|
+
|
|
155
|
+
/* Tailwind CSS safe list
|
|
156
|
+
`trigger="hover"` restores the light colour on hover via `hover:text-*`, which
|
|
157
|
+
is built at runtime — list the supported colours here so the JIT emits them.
|
|
158
|
+
| hover:text-primary | hover:text-secondary | hover:text-accent |
|
|
159
|
+
| hover:text-neutral | hover:text-info | hover:text-success |
|
|
160
|
+
| hover:text-warning | hover:text-error | hover:text-inherit |
|
|
161
|
+
| hover:text-base-content |
|
|
162
|
+
*/
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Megamenu class name generator for DaisyUI 5.6.
|
|
3
|
+
*
|
|
4
|
+
* Megamenu is a large horizontal menu where each item opens a popover. It is
|
|
5
|
+
* built on the native HTML popover API and CSS anchor positioning, so the
|
|
6
|
+
* anchoring is handled entirely by DaisyUI's CSS — this generator only composes
|
|
7
|
+
* the class modifiers (width, size, vertical).
|
|
8
|
+
*
|
|
9
|
+
* @module themes/navigation/megamenu
|
|
10
|
+
* @see https://daisyui.com/components/megamenu
|
|
11
|
+
*
|
|
12
|
+
* @safelist
|
|
13
|
+
* ## Sizes
|
|
14
|
+
* - megamenu-xs | megamenu-sm | megamenu-md | megamenu-lg | megamenu-xl
|
|
15
|
+
* ## Width
|
|
16
|
+
* - megamenu-wide | megamenu-full
|
|
17
|
+
* ## Direction
|
|
18
|
+
* - megamenu-vertical | max-sm:megamenu-vertical
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import cn from '../helpers/cn' ;
|
|
22
|
+
|
|
23
|
+
import { LG, MD, SM, XL, XS } from '../sizing/sizes' ;
|
|
24
|
+
|
|
25
|
+
export const MEGAMENU = 'megamenu' ;
|
|
26
|
+
export const MEGAMENU_ACTIVE = 'megamenu-active' ;
|
|
27
|
+
|
|
28
|
+
// Width
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @typedef {'wide' | 'full'} MegamenuWidth
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
export const WIDE = 'wide' ;
|
|
35
|
+
export const FULL = 'full' ;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Valid megamenu width modifiers.
|
|
39
|
+
* @type {MegamenuWidth[]}
|
|
40
|
+
*/
|
|
41
|
+
export const widths = [ WIDE, FULL ] ;
|
|
42
|
+
|
|
43
|
+
const widthMap =
|
|
44
|
+
{
|
|
45
|
+
[ WIDE ] : 'megamenu-wide',
|
|
46
|
+
[ FULL ] : 'megamenu-full',
|
|
47
|
+
} ;
|
|
48
|
+
|
|
49
|
+
// Sizes
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @typedef {'xs' | 'sm' | 'md' | 'lg' | 'xl'} MegamenuSize
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Valid megamenu sizes.
|
|
57
|
+
* @type {MegamenuSize[]}
|
|
58
|
+
*/
|
|
59
|
+
export const sizes = [ XS, SM, MD, LG, XL ] ;
|
|
60
|
+
|
|
61
|
+
const sizeMap =
|
|
62
|
+
{
|
|
63
|
+
[ XS ] : 'megamenu-xs',
|
|
64
|
+
[ SM ] : 'megamenu-sm',
|
|
65
|
+
[ MD ] : 'megamenu-md',
|
|
66
|
+
[ LG ] : 'megamenu-lg',
|
|
67
|
+
[ XL ] : 'megamenu-xl',
|
|
68
|
+
} ;
|
|
69
|
+
|
|
70
|
+
// Direction
|
|
71
|
+
|
|
72
|
+
export const MEGAMENU_VERTICAL = 'megamenu-vertical' ;
|
|
73
|
+
export const MEGAMENU_VERTICAL_MAX_SM = 'max-sm:megamenu-vertical' ;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Generates megamenu container class names for DaisyUI 5.6.
|
|
77
|
+
*
|
|
78
|
+
* @param {Object} [props]
|
|
79
|
+
* @param {Object} [props.after] - Class overrides applied after.
|
|
80
|
+
* @param {Object} [props.before] - Class definitions applied before.
|
|
81
|
+
* @param {string} [props.beforeClassName] - CSS string prepended.
|
|
82
|
+
* @param {string} [props.className] - CSS string appended.
|
|
83
|
+
* @param {boolean} [props.responsive=false] - Show vertically on small screens (`max-sm:megamenu-vertical`).
|
|
84
|
+
* @param {MegamenuSize} [props.size='md'] - Megamenu size (xs, sm, md, lg, xl).
|
|
85
|
+
* @param {boolean} [props.vertical=false] - Always vertical (`megamenu-vertical`).
|
|
86
|
+
* @param {MegamenuWidth} [props.width] - Width modifier ('wide' | 'full').
|
|
87
|
+
*
|
|
88
|
+
* @returns {string} Combined class names.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```js
|
|
92
|
+
* getMegamenuClasses({ width: 'wide', size: 'lg' }) ;
|
|
93
|
+
* // → 'megamenu megamenu-wide megamenu-lg'
|
|
94
|
+
*
|
|
95
|
+
* getMegamenuClasses({ responsive: true }) ;
|
|
96
|
+
* // → 'megamenu megamenu-md max-sm:megamenu-vertical'
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export const getMegamenuClasses =
|
|
100
|
+
({
|
|
101
|
+
after,
|
|
102
|
+
before,
|
|
103
|
+
beforeClassName,
|
|
104
|
+
className,
|
|
105
|
+
responsive = false,
|
|
106
|
+
size = MD,
|
|
107
|
+
vertical = false,
|
|
108
|
+
width,
|
|
109
|
+
}
|
|
110
|
+
= {} ) => cn
|
|
111
|
+
(
|
|
112
|
+
beforeClassName,
|
|
113
|
+
MEGAMENU,
|
|
114
|
+
{
|
|
115
|
+
...before,
|
|
116
|
+
|
|
117
|
+
...!!widthMap[width] && { [ widthMap[width] ] : true },
|
|
118
|
+
...!!sizeMap[size] && { [ sizeMap[size] ] : true },
|
|
119
|
+
|
|
120
|
+
[ MEGAMENU_VERTICAL ] : vertical === true,
|
|
121
|
+
[ MEGAMENU_VERTICAL_MAX_SM ] : responsive === true,
|
|
122
|
+
|
|
123
|
+
...after,
|
|
124
|
+
},
|
|
125
|
+
className,
|
|
126
|
+
) ;
|
|
127
|
+
|
|
128
|
+
export default getMegamenuClasses ;
|
package/src/version.js
CHANGED