funuicss 3.9.13 → 3.9.14
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 +1 -1
- package/ui/appbar/AppBar.d.ts +1 -0
- package/ui/appbar/AppBar.js +572 -22
- package/ui/richtext/RichText.js +336 -17
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "3.9.
|
|
2
|
+
"version": "3.9.14",
|
|
3
3
|
"name": "funuicss",
|
|
4
4
|
"description": "React and Next.js component UI Library for creating Easy and good looking websites with fewer lines of code. Elevate your web development experience with our cutting-edge React/Next.js component UI Library. Craft stunning websites effortlessly, boasting both seamless functionality and aesthetic appeal—all achieved with minimal lines of code. Unleash the power of simplicity and style in your projects!",
|
|
5
5
|
"main": "index.js",
|
package/ui/appbar/AppBar.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ interface NavbarProps {
|
|
|
31
31
|
rightLinks?: NavLink[] | string;
|
|
32
32
|
renderLink?: (link: NavLink, index: number) => React.ReactNode;
|
|
33
33
|
mobileMenuBreakpoint?: number;
|
|
34
|
+
visibleLinks?: boolean;
|
|
34
35
|
logoType?: 'text' | 'image' | 'both' | 'none';
|
|
35
36
|
logoText?: string;
|
|
36
37
|
logoTextSize?: string;
|
package/ui/appbar/AppBar.js
CHANGED
|
@@ -85,9 +85,11 @@ var DropdownArrow = function (_a) {
|
|
|
85
85
|
};
|
|
86
86
|
// Link Item Component with Dropdown Support
|
|
87
87
|
var LinkItem = function (_a) {
|
|
88
|
-
var link = _a.link, renderLink = _a.renderLink, _b = _a.linkPadding, linkPadding = _b === void 0 ? '' : _b, _c = _a.activeLinkColor, activeLinkColor = _c === void 0 ? 'primary' : _c, _d = _a.dropdownArrow, dropdownArrow = _d === void 0 ? true : _d, _e = _a.isMobile, isMobile = _e === void 0 ? false : _e
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
var link = _a.link, renderLink = _a.renderLink, _b = _a.linkPadding, linkPadding = _b === void 0 ? '' : _b, _c = _a.activeLinkColor, activeLinkColor = _c === void 0 ? 'primary' : _c, _d = _a.dropdownArrow, dropdownArrow = _d === void 0 ? true : _d, _e = _a.isMobile, isMobile = _e === void 0 ? false : _e, _f = _a.visibleLinks // Add visibleLinks prop here
|
|
89
|
+
, visibleLinks = _f === void 0 ? false : _f // Add visibleLinks prop here
|
|
90
|
+
;
|
|
91
|
+
var _g = (0, react_1.useState)(false), isOpen = _g[0], setIsOpen = _g[1];
|
|
92
|
+
var _h = (0, react_1.useState)(null), iconNode = _h[0], setIconNode = _h[1];
|
|
91
93
|
var timeoutRef = (0, react_1.useRef)();
|
|
92
94
|
var dropdownRef = (0, react_1.useRef)(null);
|
|
93
95
|
// Handle dynamic icon loading
|
|
@@ -117,17 +119,17 @@ var LinkItem = function (_a) {
|
|
|
117
119
|
var handleMouseEnter = function () {
|
|
118
120
|
if (timeoutRef.current)
|
|
119
121
|
clearTimeout(timeoutRef.current);
|
|
120
|
-
if (!isMobile && hasChildren) {
|
|
122
|
+
if ((!isMobile || visibleLinks) && hasChildren) { // Updated condition
|
|
121
123
|
setIsOpen(true);
|
|
122
124
|
}
|
|
123
125
|
};
|
|
124
126
|
var handleMouseLeave = function () {
|
|
125
|
-
if (!isMobile) {
|
|
127
|
+
if (!isMobile || visibleLinks) { // Updated condition
|
|
126
128
|
timeoutRef.current = setTimeout(function () { return setIsOpen(false); }, 150);
|
|
127
129
|
}
|
|
128
130
|
};
|
|
129
131
|
var handleClick = function (e) {
|
|
130
|
-
if (hasChildren && isMobile) {
|
|
132
|
+
if (hasChildren && (isMobile && !visibleLinks)) { // Only toggle on mobile when visibleLinks is false
|
|
131
133
|
e.preventDefault();
|
|
132
134
|
setIsOpen(!isOpen);
|
|
133
135
|
}
|
|
@@ -156,11 +158,15 @@ var LinkItem = function (_a) {
|
|
|
156
158
|
display: 'flex',
|
|
157
159
|
alignItems: 'center'
|
|
158
160
|
} }, linkContent),
|
|
159
|
-
hasChildren && isOpen && (React.createElement("div", { className: "nav_dropdown-menu ".concat(isMobile ? 'nav_dropdown-mobile' : '') },
|
|
161
|
+
hasChildren && isOpen && (React.createElement("div", { className: "nav_dropdown-menu ".concat((isMobile && !visibleLinks) ? 'nav_dropdown-mobile' : '') },
|
|
162
|
+
" ",
|
|
163
|
+
link.children.map(function (child, index) { return (React.createElement(LinkItem, { key: index, link: child, renderLink: renderLink, linkPadding: linkPadding, activeLinkColor: activeLinkColor, dropdownArrow: dropdownArrow, isMobile: isMobile, visibleLinks: visibleLinks })); })))));
|
|
160
164
|
};
|
|
161
165
|
// Links component to render navigation links
|
|
162
166
|
var NavLinks = function (_a) {
|
|
163
|
-
var links = _a.links, renderLink = _a.renderLink, _b = _a.linkGap, linkGap = _b === void 0 ? '1rem' : _b, _c = _a.linkPadding, linkPadding = _c === void 0 ? '0.5rem 1rem' : _c, _d = _a.activeLinkColor, activeLinkColor = _d === void 0 ? 'primary' : _d, _e = _a.dropdownArrow, dropdownArrow = _e === void 0 ? true : _e, _f = _a.isMobile, isMobile = _f === void 0 ? false : _f
|
|
167
|
+
var links = _a.links, renderLink = _a.renderLink, _b = _a.linkGap, linkGap = _b === void 0 ? '1rem' : _b, _c = _a.linkPadding, linkPadding = _c === void 0 ? '0.5rem 1rem' : _c, _d = _a.activeLinkColor, activeLinkColor = _d === void 0 ? 'primary' : _d, _e = _a.dropdownArrow, dropdownArrow = _e === void 0 ? true : _e, _f = _a.isMobile, isMobile = _f === void 0 ? false : _f, _g = _a.visibleLinks // Add visibleLinks prop here
|
|
168
|
+
, visibleLinks = _g === void 0 ? false : _g // Add visibleLinks prop here
|
|
169
|
+
;
|
|
164
170
|
if (!links || !Array.isArray(links) || links.length === 0) {
|
|
165
171
|
return null;
|
|
166
172
|
}
|
|
@@ -168,8 +174,8 @@ var NavLinks = function (_a) {
|
|
|
168
174
|
display: 'flex',
|
|
169
175
|
alignItems: 'center',
|
|
170
176
|
gap: linkGap,
|
|
171
|
-
flexDirection: isMobile ? 'column' : 'row'
|
|
172
|
-
} }, links.map(function (link, index) { return (React.createElement(LinkItem, { key: index, link: link, renderLink: renderLink, linkPadding: linkPadding, activeLinkColor: activeLinkColor, dropdownArrow: dropdownArrow, isMobile: isMobile })); })));
|
|
177
|
+
flexDirection: (isMobile && !visibleLinks) ? 'column' : 'row' // Only column layout when mobile menu is open
|
|
178
|
+
} }, links.map(function (link, index) { return (React.createElement(LinkItem, { key: index, link: link, renderLink: renderLink, linkPadding: linkPadding, activeLinkColor: activeLinkColor, dropdownArrow: dropdownArrow, isMobile: isMobile, visibleLinks: visibleLinks })); })));
|
|
173
179
|
};
|
|
174
180
|
// Logo component with multiple display options
|
|
175
181
|
var Logo = function (_a) {
|
|
@@ -244,46 +250,590 @@ function AppBar(localProps) {
|
|
|
244
250
|
var hasLeftLinks = parsedLeftLinks.length > 0;
|
|
245
251
|
return (React.createElement("div", { className: "left-section", style: { display: 'flex', alignItems: 'center', gap: '2rem' } },
|
|
246
252
|
shouldRenderLogo && (React.createElement(Logo, { type: final.logoType, text: final.logoText, textSize: final.logoTextSize, textColor: final.logoTextColor, textWeight: final.logoTextWeight, url: final.logoUrl, alt: final.logoAlt, width: final.logoWidth, height: final.logoHeight, href: final.logoHref, onClick: final.onLogoClick })),
|
|
247
|
-
hasLeftLinks && !isMobileScreen
|
|
253
|
+
hasLeftLinks && (!isMobileScreen || final.visibleLinks) && ( // Updated condition
|
|
254
|
+
React.createElement(NavLinks, { links: parsedLeftLinks, renderLink: final.renderLink, linkGap: final.linkGap, linkPadding: final.linkPadding, activeLinkColor: final.activeLinkColor, dropdownArrow: final.dropdownArrow, isMobile: isMobileScreen, visibleLinks: final.visibleLinks }))));
|
|
248
255
|
};
|
|
249
256
|
var renderCenterSection = function () {
|
|
250
257
|
if (final.center)
|
|
251
258
|
return final.center;
|
|
252
|
-
if (parsedCenterLinks.length > 0 && !isMobileScreen) {
|
|
253
|
-
return (React.createElement(NavLinks, { links: parsedCenterLinks, renderLink: final.renderLink, linkGap: final.linkGap, linkPadding: final.linkPadding, activeLinkColor: final.activeLinkColor, dropdownArrow: final.dropdownArrow }));
|
|
259
|
+
if (parsedCenterLinks.length > 0 && (!isMobileScreen || final.visibleLinks)) { // Updated condition
|
|
260
|
+
return (React.createElement(NavLinks, { links: parsedCenterLinks, renderLink: final.renderLink, linkGap: final.linkGap, linkPadding: final.linkPadding, activeLinkColor: final.activeLinkColor, dropdownArrow: final.dropdownArrow, isMobile: isMobileScreen, visibleLinks: final.visibleLinks }));
|
|
254
261
|
}
|
|
255
262
|
return null;
|
|
256
263
|
};
|
|
257
264
|
var renderRightSection = function () {
|
|
258
265
|
if (final.right)
|
|
259
266
|
return final.right;
|
|
260
|
-
if (parsedRightLinks.length > 0 && !isMobileScreen) {
|
|
261
|
-
return (React.createElement(NavLinks, { links: parsedRightLinks, renderLink: final.renderLink, linkGap: final.linkGap, linkPadding: final.linkPadding, activeLinkColor: final.activeLinkColor, dropdownArrow: final.dropdownArrow }));
|
|
267
|
+
if (parsedRightLinks.length > 0 && (!isMobileScreen || final.visibleLinks)) { // Updated condition
|
|
268
|
+
return (React.createElement(NavLinks, { links: parsedRightLinks, renderLink: final.renderLink, linkGap: final.linkGap, linkPadding: final.linkPadding, activeLinkColor: final.activeLinkColor, dropdownArrow: final.dropdownArrow, isMobile: isMobileScreen, visibleLinks: final.visibleLinks }));
|
|
262
269
|
}
|
|
263
270
|
return null;
|
|
264
271
|
};
|
|
265
|
-
// Mobile menu content
|
|
272
|
+
// Mobile menu content - only show when visibleLinks is false
|
|
266
273
|
var renderMobileMenu = function () {
|
|
267
|
-
if (!isMobileScreen || !isMobileMenuOpen)
|
|
268
|
-
return null;
|
|
274
|
+
if (!isMobileScreen || !isMobileMenuOpen || final.visibleLinks)
|
|
275
|
+
return null; // Don't show mobile menu when visibleLinks is true
|
|
269
276
|
var allLinks = __spreadArray(__spreadArray(__spreadArray([], parsedLeftLinks, true), parsedCenterLinks, true), parsedRightLinks, true);
|
|
270
277
|
return (React.createElement("div", { className: "nav_mobile-menu" },
|
|
271
|
-
React.createElement(NavLinks, { links: allLinks, renderLink: final.renderLink, linkGap: "0.5rem", linkPadding: "1rem", activeLinkColor: final.activeLinkColor, dropdownArrow: final.dropdownArrow, isMobile: true })));
|
|
278
|
+
React.createElement(NavLinks, { links: allLinks, renderLink: final.renderLink, linkGap: "0.5rem", linkPadding: "1rem", activeLinkColor: final.activeLinkColor, dropdownArrow: final.dropdownArrow, isMobile: true, visibleLinks: final.visibleLinks })));
|
|
272
279
|
};
|
|
273
280
|
return (React.createElement(React.Fragment, null,
|
|
274
|
-
React.createElement("nav", { id: 'appBar', className: "navigation-bar\n ".concat(isMobileMenuOpen ? 'navbar-mobile-open' : '', "\n ").concat(final.funcss || '', "\n ").concat(final.testing ? "" : final.fixedTop ? 'fixed_top_navbar' : '', "\n ").concat(final.sideBar ? 'there_is_sidebar' : '', "\n ").concat(final.transparent ? 'transparent' : '', "\n ").concat(final.fixedBottom ? 'fixedBottom' : '', "\n "), style: {
|
|
281
|
+
React.createElement("nav", { id: 'appBar', className: "navigation-bar\n ".concat(isMobileMenuOpen ? 'navbar-mobile-open' : '', "\n ").concat(final.funcss || '', "\n ").concat(final.testing ? "" : final.fixedTop ? 'fixed_top_navbar' : '', "\n ").concat(final.sideBar ? 'there_is_sidebar' : '', "\n ").concat(final.transparent ? 'transparent' : '', "\n ").concat(final.fixedBottom ? 'fixedBottom' : '', "\n ").concat(final.visibleLinks ? 'visible-links-mode' : '', " // Add class for styling\n "), style: {
|
|
275
282
|
padding: "".concat(final.padding || ''),
|
|
276
283
|
justifyContent: "".concat(final.justify || ''),
|
|
277
284
|
} },
|
|
278
285
|
React.createElement("div", { className: "logoWrapper" },
|
|
279
286
|
renderLeftSection(),
|
|
280
|
-
isMobileScreen && isMobileMenuOpen &&
|
|
287
|
+
isMobileScreen && isMobileMenuOpen && !final.visibleLinks && ( // Only show close button when not in visibleLinks mode
|
|
288
|
+
React.createElement("div", { className: "hover-text-error pointer _closeNav", onClick: closeMenu },
|
|
281
289
|
React.createElement(Trigger, { isOpen: isMobileMenuOpen })))),
|
|
282
290
|
React.createElement("div", { className: "linkWrapper" }, renderCenterSection()),
|
|
283
291
|
React.createElement("div", { className: "linkWrapper" }, renderRightSection()),
|
|
284
|
-
isMobileScreen && !isMobileMenuOpen && (React.createElement(React.Fragment, null, final.hasSidebar ?
|
|
292
|
+
isMobileScreen && !isMobileMenuOpen && !final.visibleLinks && (React.createElement(React.Fragment, null, final.hasSidebar ?
|
|
285
293
|
React.createElement("span", { className: "sidebar-trigger pointer hover-text-primary", onClick: final.openSidebar }, final.sidebarTrigger || React.createElement(Trigger, { isOpen: final.sidebarOpen }))
|
|
286
294
|
:
|
|
287
295
|
React.createElement("span", { className: "sidebar-trigger pointer hover-text-primary", onClick: toggleMenu }, final.sidebarTrigger || React.createElement(Trigger, { isOpen: isMobileMenuOpen }))))),
|
|
288
296
|
renderMobileMenu()));
|
|
289
297
|
}
|
|
298
|
+
// 'use client';
|
|
299
|
+
// import * as React from 'react';
|
|
300
|
+
// import { useState, useEffect, useRef } from 'react';
|
|
301
|
+
// import { usePathname } from 'next/navigation';
|
|
302
|
+
// import Hamburger from './Hamburger';
|
|
303
|
+
// import { useComponentConfiguration } from '../../utils/componentUtils';
|
|
304
|
+
// import { getDynamicIcon } from '../../utils/getDynamicIcon';
|
|
305
|
+
// interface NavLink {
|
|
306
|
+
// label: string;
|
|
307
|
+
// href: string;
|
|
308
|
+
// icon?: string;
|
|
309
|
+
// iconPosition?: 'prefix' | 'suffix';
|
|
310
|
+
// children?: NavLink[];
|
|
311
|
+
// active?: boolean;
|
|
312
|
+
// className?: string;
|
|
313
|
+
// }
|
|
314
|
+
// interface NavbarProps {
|
|
315
|
+
// // Layout & Behavior
|
|
316
|
+
// fixedTop?: boolean;
|
|
317
|
+
// funcss?: string;
|
|
318
|
+
// padding?: string;
|
|
319
|
+
// fixedBottom?: boolean;
|
|
320
|
+
// justify?: string;
|
|
321
|
+
// transparent?: boolean;
|
|
322
|
+
// testing?: boolean;
|
|
323
|
+
// // Content Sections
|
|
324
|
+
// children?: React.ReactNode;
|
|
325
|
+
// left?: React.ReactNode;
|
|
326
|
+
// center?: React.ReactNode;
|
|
327
|
+
// right?: React.ReactNode;
|
|
328
|
+
// // Sidebar
|
|
329
|
+
// sidebarTrigger?: React.ReactNode;
|
|
330
|
+
// sideBar?: number;
|
|
331
|
+
// hasSidebar?: boolean;
|
|
332
|
+
// sidebarOpen?: boolean;
|
|
333
|
+
// openSidebar?: () => void;
|
|
334
|
+
// // Variant Support
|
|
335
|
+
// variant?: string;
|
|
336
|
+
// // Navigation Links
|
|
337
|
+
// leftLinks?: NavLink[] | string;
|
|
338
|
+
// centerLinks?: NavLink[] | string;
|
|
339
|
+
// rightLinks?: NavLink[] | string;
|
|
340
|
+
// renderLink?: (link: NavLink, index: number) => React.ReactNode;
|
|
341
|
+
// mobileMenuBreakpoint?: number;
|
|
342
|
+
// // Logo Customization
|
|
343
|
+
// logoType?: 'text' | 'image' | 'both' | 'none';
|
|
344
|
+
// logoText?: string;
|
|
345
|
+
// logoTextSize?: string;
|
|
346
|
+
// logoTextColor?: string;
|
|
347
|
+
// logoTextWeight?: string;
|
|
348
|
+
// logoUrl?: string;
|
|
349
|
+
// logoAlt?: string;
|
|
350
|
+
// logoWidth?: string;
|
|
351
|
+
// logoHeight?: string;
|
|
352
|
+
// logoHref?: string;
|
|
353
|
+
// onLogoClick?: () => void;
|
|
354
|
+
// // Link Styling
|
|
355
|
+
// linkGap?: string;
|
|
356
|
+
// linkPadding?: string;
|
|
357
|
+
// activeLinkColor?: string;
|
|
358
|
+
// dropdownArrow?: boolean;
|
|
359
|
+
// }
|
|
360
|
+
// // Parse string to object utility
|
|
361
|
+
// const parseIfString = <T,>(value: T | string, fallback: T): T => {
|
|
362
|
+
// if (typeof value === 'string') {
|
|
363
|
+
// try {
|
|
364
|
+
// const parsed = JSON.parse(value) as T;
|
|
365
|
+
// if (Array.isArray(fallback) && !Array.isArray(parsed)) {
|
|
366
|
+
// console.warn('Parsed value is not an array, using fallback');
|
|
367
|
+
// return fallback;
|
|
368
|
+
// }
|
|
369
|
+
// return parsed;
|
|
370
|
+
// } catch (error) {
|
|
371
|
+
// console.error('Failed to parse JSON string:', error);
|
|
372
|
+
// return fallback;
|
|
373
|
+
// }
|
|
374
|
+
// }
|
|
375
|
+
// if (value == null) {
|
|
376
|
+
// return fallback;
|
|
377
|
+
// }
|
|
378
|
+
// return value as T;
|
|
379
|
+
// };
|
|
380
|
+
// // Dropdown Arrow Icon Component
|
|
381
|
+
// const DropdownArrow = ({ isOpen }: { isOpen: boolean }) => (
|
|
382
|
+
// <svg
|
|
383
|
+
// width="16"
|
|
384
|
+
// height="16"
|
|
385
|
+
// viewBox="0 0 16 16"
|
|
386
|
+
// style={{
|
|
387
|
+
// transition: 'transform 0.3s ease',
|
|
388
|
+
// transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)'
|
|
389
|
+
// }}
|
|
390
|
+
// >
|
|
391
|
+
// <path
|
|
392
|
+
// d="M4 6 L8 10 L12 6"
|
|
393
|
+
// fill="none"
|
|
394
|
+
// stroke="currentColor"
|
|
395
|
+
// strokeWidth="2"
|
|
396
|
+
// strokeLinecap="round"
|
|
397
|
+
// />
|
|
398
|
+
// </svg>
|
|
399
|
+
// );
|
|
400
|
+
// // Link Item Component with Dropdown Support
|
|
401
|
+
// const LinkItem = ({
|
|
402
|
+
// link,
|
|
403
|
+
// renderLink,
|
|
404
|
+
// linkPadding = '',
|
|
405
|
+
// activeLinkColor = 'primary',
|
|
406
|
+
// dropdownArrow = true,
|
|
407
|
+
// isMobile = false
|
|
408
|
+
// }: {
|
|
409
|
+
// link: NavLink;
|
|
410
|
+
// renderLink?: (link: NavLink, index: number) => React.ReactNode;
|
|
411
|
+
// linkPadding?: string;
|
|
412
|
+
// activeLinkColor?: string;
|
|
413
|
+
// dropdownArrow?: boolean;
|
|
414
|
+
// isMobile?: boolean;
|
|
415
|
+
// }) => {
|
|
416
|
+
// const [isOpen, setIsOpen] = useState(false);
|
|
417
|
+
// const [iconNode, setIconNode] = useState<React.ReactNode>(null);
|
|
418
|
+
// const timeoutRef = useRef<NodeJS.Timeout>();
|
|
419
|
+
// const dropdownRef = useRef<HTMLDivElement>(null);
|
|
420
|
+
// // Handle dynamic icon loading
|
|
421
|
+
// useEffect(() => {
|
|
422
|
+
// if (link.icon) {
|
|
423
|
+
// getDynamicIcon(link.icon).then(setIconNode);
|
|
424
|
+
// } else {
|
|
425
|
+
// setIconNode(null);
|
|
426
|
+
// }
|
|
427
|
+
// }, [link.icon]);
|
|
428
|
+
// // Close dropdown when clicking outside
|
|
429
|
+
// useEffect(() => {
|
|
430
|
+
// const handleClickOutside = (event: MouseEvent) => {
|
|
431
|
+
// if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
|
432
|
+
// setIsOpen(false);
|
|
433
|
+
// }
|
|
434
|
+
// };
|
|
435
|
+
// if (isOpen) {
|
|
436
|
+
// document.addEventListener('mousedown', handleClickOutside);
|
|
437
|
+
// }
|
|
438
|
+
// return () => {
|
|
439
|
+
// document.removeEventListener('mousedown', handleClickOutside);
|
|
440
|
+
// };
|
|
441
|
+
// }, [isOpen]);
|
|
442
|
+
// const hasChildren = link.children && link.children.length > 0;
|
|
443
|
+
// const handleMouseEnter = () => {
|
|
444
|
+
// if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
445
|
+
// if (!isMobile && hasChildren) {
|
|
446
|
+
// setIsOpen(true);
|
|
447
|
+
// }
|
|
448
|
+
// };
|
|
449
|
+
// const handleMouseLeave = () => {
|
|
450
|
+
// if (!isMobile) {
|
|
451
|
+
// timeoutRef.current = setTimeout(() => setIsOpen(false), 150);
|
|
452
|
+
// }
|
|
453
|
+
// };
|
|
454
|
+
// const handleClick = (e: React.MouseEvent) => {
|
|
455
|
+
// if (hasChildren && isMobile) {
|
|
456
|
+
// e.preventDefault();
|
|
457
|
+
// setIsOpen(!isOpen);
|
|
458
|
+
// }
|
|
459
|
+
// };
|
|
460
|
+
// const linkContent = (
|
|
461
|
+
// <span
|
|
462
|
+
// className={`nav_link-content ${link.active ? 'active' : ''} ${link.className || ''}`}
|
|
463
|
+
// style={{
|
|
464
|
+
// display: 'flex',
|
|
465
|
+
// alignItems: 'center',
|
|
466
|
+
// gap: '0.5rem',
|
|
467
|
+
// padding: linkPadding,
|
|
468
|
+
// color: 'inherit',
|
|
469
|
+
// textDecoration: 'none'
|
|
470
|
+
// }}
|
|
471
|
+
// >
|
|
472
|
+
// {/* Prefix Icon */}
|
|
473
|
+
// {iconNode && link.iconPosition !== 'suffix' && (
|
|
474
|
+
// <span className="nav_link-icon prefix" style={{ display: 'flex', alignItems: 'center' }}>
|
|
475
|
+
// {iconNode}
|
|
476
|
+
// </span>
|
|
477
|
+
// )}
|
|
478
|
+
// <span className="nav_link-text">{link.label}</span>
|
|
479
|
+
// {/* Suffix Icon */}
|
|
480
|
+
// {iconNode && link.iconPosition === 'suffix' && (
|
|
481
|
+
// <span className="nav_link-icon suffix" style={{ display: 'flex', alignItems: 'center' }}>
|
|
482
|
+
// {iconNode}
|
|
483
|
+
// </span>
|
|
484
|
+
// )}
|
|
485
|
+
// {/* Dropdown Arrow */}
|
|
486
|
+
// {hasChildren && dropdownArrow && (
|
|
487
|
+
// <span className="nav_link-arrow" style={{ display: 'flex', alignItems: 'center' }}>
|
|
488
|
+
// <DropdownArrow isOpen={isOpen} />
|
|
489
|
+
// </span>
|
|
490
|
+
// )}
|
|
491
|
+
// </span>
|
|
492
|
+
// );
|
|
493
|
+
// // If custom renderer is provided, use it
|
|
494
|
+
// if (renderLink) {
|
|
495
|
+
// return renderLink(link, 0);
|
|
496
|
+
// }
|
|
497
|
+
// return (
|
|
498
|
+
// <div
|
|
499
|
+
// ref={dropdownRef}
|
|
500
|
+
// className={`nav_item ${hasChildren ? 'has-dropdown' : ''} ${isOpen ? 'dropdown-open' : ''}`}
|
|
501
|
+
// onMouseEnter={handleMouseEnter}
|
|
502
|
+
// onMouseLeave={handleMouseLeave}
|
|
503
|
+
// style={{ position: 'relative' }}
|
|
504
|
+
// >
|
|
505
|
+
// <a
|
|
506
|
+
// href={link.href}
|
|
507
|
+
// className={`nav_link ${link.active ? 'active' : ''}`}
|
|
508
|
+
// onClick={handleClick}
|
|
509
|
+
// style={{
|
|
510
|
+
// textDecoration: 'none',
|
|
511
|
+
// color: 'inherit',
|
|
512
|
+
// display: 'flex',
|
|
513
|
+
// alignItems: 'center'
|
|
514
|
+
// }}
|
|
515
|
+
// >
|
|
516
|
+
// {linkContent}
|
|
517
|
+
// </a>
|
|
518
|
+
// {/* Dropdown Menu */}
|
|
519
|
+
// {hasChildren && isOpen && (
|
|
520
|
+
// <div className={`nav_dropdown-menu ${isMobile ? 'nav_dropdown-mobile' : ''}`}>
|
|
521
|
+
// {link.children!.map((child, index) => (
|
|
522
|
+
// <LinkItem
|
|
523
|
+
// key={index}
|
|
524
|
+
// link={child}
|
|
525
|
+
// renderLink={renderLink}
|
|
526
|
+
// linkPadding={linkPadding}
|
|
527
|
+
// activeLinkColor={activeLinkColor}
|
|
528
|
+
// dropdownArrow={dropdownArrow}
|
|
529
|
+
// isMobile={isMobile}
|
|
530
|
+
// />
|
|
531
|
+
// ))}
|
|
532
|
+
// </div>
|
|
533
|
+
// )}
|
|
534
|
+
// </div>
|
|
535
|
+
// );
|
|
536
|
+
// };
|
|
537
|
+
// // Links component to render navigation links
|
|
538
|
+
// const NavLinks = ({
|
|
539
|
+
// links,
|
|
540
|
+
// renderLink,
|
|
541
|
+
// linkGap = '1rem',
|
|
542
|
+
// linkPadding = '0.5rem 1rem',
|
|
543
|
+
// activeLinkColor = 'primary',
|
|
544
|
+
// dropdownArrow = true,
|
|
545
|
+
// isMobile = false
|
|
546
|
+
// }: {
|
|
547
|
+
// links: NavLink[];
|
|
548
|
+
// renderLink?: (link: NavLink, index: number) => React.ReactNode;
|
|
549
|
+
// linkGap?: string;
|
|
550
|
+
// linkPadding?: string;
|
|
551
|
+
// activeLinkColor?: string;
|
|
552
|
+
// dropdownArrow?: boolean;
|
|
553
|
+
// isMobile?: boolean;
|
|
554
|
+
// }) => {
|
|
555
|
+
// if (!links || !Array.isArray(links) || links.length === 0) {
|
|
556
|
+
// return null;
|
|
557
|
+
// }
|
|
558
|
+
// return (
|
|
559
|
+
// <div
|
|
560
|
+
// className="nav_links"
|
|
561
|
+
// style={{
|
|
562
|
+
// display: 'flex',
|
|
563
|
+
// alignItems: 'center',
|
|
564
|
+
// gap: linkGap,
|
|
565
|
+
// flexDirection: isMobile ? 'column' : 'row'
|
|
566
|
+
// }}
|
|
567
|
+
// >
|
|
568
|
+
// {links.map((link, index) => (
|
|
569
|
+
// <LinkItem
|
|
570
|
+
// key={index}
|
|
571
|
+
// link={link}
|
|
572
|
+
// renderLink={renderLink}
|
|
573
|
+
// linkPadding={linkPadding}
|
|
574
|
+
// activeLinkColor={activeLinkColor}
|
|
575
|
+
// dropdownArrow={dropdownArrow}
|
|
576
|
+
// isMobile={isMobile}
|
|
577
|
+
// />
|
|
578
|
+
// ))}
|
|
579
|
+
// </div>
|
|
580
|
+
// );
|
|
581
|
+
// };
|
|
582
|
+
// // Logo component with multiple display options
|
|
583
|
+
// const Logo = ({
|
|
584
|
+
// type = 'text',
|
|
585
|
+
// text = 'MyApp',
|
|
586
|
+
// textSize = 'xl',
|
|
587
|
+
// textColor = 'primary',
|
|
588
|
+
// textWeight = 'bold',
|
|
589
|
+
// url = '',
|
|
590
|
+
// alt = 'Logo',
|
|
591
|
+
// width = '40px',
|
|
592
|
+
// height = 'auto',
|
|
593
|
+
// href = '/',
|
|
594
|
+
// onClick
|
|
595
|
+
// }: {
|
|
596
|
+
// type?: 'text' | 'image' | 'both' | 'none';
|
|
597
|
+
// text?: string;
|
|
598
|
+
// textSize?: string;
|
|
599
|
+
// textColor?: string;
|
|
600
|
+
// textWeight?: string;
|
|
601
|
+
// url?: string;
|
|
602
|
+
// alt?: string;
|
|
603
|
+
// width?: string;
|
|
604
|
+
// height?: string;
|
|
605
|
+
// href?: string;
|
|
606
|
+
// onClick?: () => void;
|
|
607
|
+
// }) => {
|
|
608
|
+
// if (type === 'none') return null;
|
|
609
|
+
// const logoContent = (
|
|
610
|
+
// <div className="logo-content" style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
|
611
|
+
// {/* Image Logo */}
|
|
612
|
+
// {(type === 'image' || type === 'both') && url && (
|
|
613
|
+
// <img
|
|
614
|
+
// src={url}
|
|
615
|
+
// alt={alt}
|
|
616
|
+
// style={{
|
|
617
|
+
// width,
|
|
618
|
+
// height,
|
|
619
|
+
// objectFit: 'contain',
|
|
620
|
+
// display: 'block'
|
|
621
|
+
// }}
|
|
622
|
+
// className="logo-image"
|
|
623
|
+
// />
|
|
624
|
+
// )}
|
|
625
|
+
// {/* Text Logo */}
|
|
626
|
+
// {(type === 'text' || type === 'both') && text && (
|
|
627
|
+
// <span
|
|
628
|
+
// className={`logo-text text-${textSize} text-${textColor} font-${textWeight}`}
|
|
629
|
+
// style={{
|
|
630
|
+
// lineHeight: 1,
|
|
631
|
+
// whiteSpace: 'nowrap'
|
|
632
|
+
// }}
|
|
633
|
+
// >
|
|
634
|
+
// {text}
|
|
635
|
+
// </span>
|
|
636
|
+
// )}
|
|
637
|
+
// </div>
|
|
638
|
+
// );
|
|
639
|
+
// // Wrap in link if href provided
|
|
640
|
+
// if (href) {
|
|
641
|
+
// return (
|
|
642
|
+
// <a
|
|
643
|
+
// href={href}
|
|
644
|
+
// onClick={onClick}
|
|
645
|
+
// style={{
|
|
646
|
+
// textDecoration: 'none',
|
|
647
|
+
// color: 'inherit',
|
|
648
|
+
// display: 'flex',
|
|
649
|
+
// alignItems: 'center'
|
|
650
|
+
// }}
|
|
651
|
+
// className="logo-link"
|
|
652
|
+
// >
|
|
653
|
+
// {logoContent}
|
|
654
|
+
// </a>
|
|
655
|
+
// );
|
|
656
|
+
// }
|
|
657
|
+
// return (
|
|
658
|
+
// <div
|
|
659
|
+
// onClick={onClick}
|
|
660
|
+
// style={{ cursor: onClick ? 'pointer' : 'default' }}
|
|
661
|
+
// className="logo-container"
|
|
662
|
+
// >
|
|
663
|
+
// {logoContent}
|
|
664
|
+
// </div>
|
|
665
|
+
// );
|
|
666
|
+
// };
|
|
667
|
+
// export default function AppBar(localProps: NavbarProps) {
|
|
668
|
+
// // Use component configuration with variant support
|
|
669
|
+
// const { mergeWithLocal } = useComponentConfiguration('AppBar', localProps.variant);
|
|
670
|
+
// // Merge with config - LOCAL PROPS OVERRIDE CONFIG
|
|
671
|
+
// const { props: mergedProps } = mergeWithLocal(localProps);
|
|
672
|
+
// // Parse link props if they're strings
|
|
673
|
+
// const parsedLeftLinks = parseIfString<NavLink[]>(mergedProps.leftLinks, []);
|
|
674
|
+
// const parsedCenterLinks = parseIfString<NavLink[]>(mergedProps.centerLinks, []);
|
|
675
|
+
// const parsedRightLinks = parseIfString<NavLink[]>(mergedProps.rightLinks, []);
|
|
676
|
+
// // Use mergedProps directly
|
|
677
|
+
// const final = mergedProps;
|
|
678
|
+
// const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
|
679
|
+
// const [isMobileScreen, setIsMobileScreen] = useState(false);
|
|
680
|
+
// const pathname = usePathname();
|
|
681
|
+
// const toggleMenu = () => setIsMobileMenuOpen((prev) => !prev);
|
|
682
|
+
// const closeMenu = () => setIsMobileMenuOpen(false);
|
|
683
|
+
// useEffect(() => {
|
|
684
|
+
// const handleResize = () => {
|
|
685
|
+
// const isMobile = window.innerWidth < (final.mobileMenuBreakpoint || 992);
|
|
686
|
+
// setIsMobileScreen(isMobile);
|
|
687
|
+
// if (!isMobile) {
|
|
688
|
+
// closeMenu(); // close on larger screens
|
|
689
|
+
// }
|
|
690
|
+
// };
|
|
691
|
+
// handleResize(); // initial check
|
|
692
|
+
// window.addEventListener('resize', handleResize);
|
|
693
|
+
// return () => window.removeEventListener('resize', handleResize);
|
|
694
|
+
// }, [final.mobileMenuBreakpoint]);
|
|
695
|
+
// // Automatically close menu on route (pathname) change
|
|
696
|
+
// useEffect(() => {
|
|
697
|
+
// closeMenu();
|
|
698
|
+
// }, [pathname]);
|
|
699
|
+
// const Trigger = ({ isOpen }: { isOpen: boolean }) => {
|
|
700
|
+
// return <Hamburger isOpen={isOpen} />;
|
|
701
|
+
// };
|
|
702
|
+
// // Enhanced left section with logo customization
|
|
703
|
+
// const renderLeftSection = () => {
|
|
704
|
+
// // If custom left content is provided, use it (overrides everything)
|
|
705
|
+
// if (final.left) return final.left;
|
|
706
|
+
// // Render logo based on configuration
|
|
707
|
+
// const shouldRenderLogo = final.logoType && final.logoType !== 'none';
|
|
708
|
+
// const hasLeftLinks = parsedLeftLinks.length > 0;
|
|
709
|
+
// return (
|
|
710
|
+
// <div className="left-section" style={{ display: 'flex', alignItems: 'center', gap: '2rem' }}>
|
|
711
|
+
// {/* Logo */}
|
|
712
|
+
// {shouldRenderLogo && (
|
|
713
|
+
// <Logo
|
|
714
|
+
// type={final.logoType}
|
|
715
|
+
// text={final.logoText}
|
|
716
|
+
// textSize={final.logoTextSize}
|
|
717
|
+
// textColor={final.logoTextColor}
|
|
718
|
+
// textWeight={final.logoTextWeight}
|
|
719
|
+
// url={final.logoUrl}
|
|
720
|
+
// alt={final.logoAlt}
|
|
721
|
+
// width={final.logoWidth}
|
|
722
|
+
// height={final.logoHeight}
|
|
723
|
+
// href={final.logoHref}
|
|
724
|
+
// onClick={final.onLogoClick}
|
|
725
|
+
// />
|
|
726
|
+
// )}
|
|
727
|
+
// {/* Left navigation links (appear next to logo) */}
|
|
728
|
+
// {hasLeftLinks && !isMobileScreen && (
|
|
729
|
+
// <NavLinks
|
|
730
|
+
// links={parsedLeftLinks}
|
|
731
|
+
// renderLink={final.renderLink}
|
|
732
|
+
// linkGap={final.linkGap}
|
|
733
|
+
// linkPadding={final.linkPadding}
|
|
734
|
+
// activeLinkColor={final.activeLinkColor}
|
|
735
|
+
// dropdownArrow={final.dropdownArrow}
|
|
736
|
+
// />
|
|
737
|
+
// )}
|
|
738
|
+
// </div>
|
|
739
|
+
// );
|
|
740
|
+
// };
|
|
741
|
+
// const renderCenterSection = () => {
|
|
742
|
+
// if (final.center) return final.center;
|
|
743
|
+
// if (parsedCenterLinks.length > 0 && !isMobileScreen) {
|
|
744
|
+
// return (
|
|
745
|
+
// <NavLinks
|
|
746
|
+
// links={parsedCenterLinks}
|
|
747
|
+
// renderLink={final.renderLink}
|
|
748
|
+
// linkGap={final.linkGap}
|
|
749
|
+
// linkPadding={final.linkPadding}
|
|
750
|
+
// activeLinkColor={final.activeLinkColor}
|
|
751
|
+
// dropdownArrow={final.dropdownArrow}
|
|
752
|
+
// />
|
|
753
|
+
// );
|
|
754
|
+
// }
|
|
755
|
+
// return null;
|
|
756
|
+
// };
|
|
757
|
+
// const renderRightSection = () => {
|
|
758
|
+
// if (final.right) return final.right;
|
|
759
|
+
// if (parsedRightLinks.length > 0 && !isMobileScreen) {
|
|
760
|
+
// return (
|
|
761
|
+
// <NavLinks
|
|
762
|
+
// links={parsedRightLinks}
|
|
763
|
+
// renderLink={final.renderLink}
|
|
764
|
+
// linkGap={final.linkGap}
|
|
765
|
+
// linkPadding={final.linkPadding}
|
|
766
|
+
// activeLinkColor={final.activeLinkColor}
|
|
767
|
+
// dropdownArrow={final.dropdownArrow}
|
|
768
|
+
// />
|
|
769
|
+
// );
|
|
770
|
+
// }
|
|
771
|
+
// return null;
|
|
772
|
+
// };
|
|
773
|
+
// // Mobile menu content
|
|
774
|
+
// const renderMobileMenu = () => {
|
|
775
|
+
// if (!isMobileScreen || !isMobileMenuOpen) return null;
|
|
776
|
+
// const allLinks = [...parsedLeftLinks, ...parsedCenterLinks, ...parsedRightLinks];
|
|
777
|
+
// return (
|
|
778
|
+
// <div className="nav_mobile-menu">
|
|
779
|
+
// <NavLinks
|
|
780
|
+
// links={allLinks}
|
|
781
|
+
// renderLink={final.renderLink}
|
|
782
|
+
// linkGap="0.5rem"
|
|
783
|
+
// linkPadding="1rem"
|
|
784
|
+
// activeLinkColor={final.activeLinkColor}
|
|
785
|
+
// dropdownArrow={final.dropdownArrow}
|
|
786
|
+
// isMobile={true}
|
|
787
|
+
// />
|
|
788
|
+
// </div>
|
|
789
|
+
// );
|
|
790
|
+
// };
|
|
791
|
+
// return (
|
|
792
|
+
// <>
|
|
793
|
+
// <nav
|
|
794
|
+
// id='appBar'
|
|
795
|
+
// className={`navigation-bar
|
|
796
|
+
// ${isMobileMenuOpen ? 'navbar-mobile-open' : ''}
|
|
797
|
+
// ${final.funcss || ''}
|
|
798
|
+
// ${ final.testing ? "" : final.fixedTop ? 'fixed_top_navbar' : ''}
|
|
799
|
+
// ${final.sideBar ? 'there_is_sidebar' : ''}
|
|
800
|
+
// ${final.transparent ? 'transparent' : ''}
|
|
801
|
+
// ${final.fixedBottom ? 'fixedBottom' : ''}
|
|
802
|
+
// `}
|
|
803
|
+
// style={{
|
|
804
|
+
// padding: `${final.padding || ''}`,
|
|
805
|
+
// justifyContent: `${final.justify || ''}`,
|
|
806
|
+
// }}
|
|
807
|
+
// >
|
|
808
|
+
// <div className="logoWrapper">
|
|
809
|
+
// {renderLeftSection()}
|
|
810
|
+
// {isMobileScreen && isMobileMenuOpen && (
|
|
811
|
+
// <div className="hover-text-error pointer _closeNav" onClick={closeMenu}>
|
|
812
|
+
// <Trigger isOpen={isMobileMenuOpen} />
|
|
813
|
+
// </div>
|
|
814
|
+
// )}
|
|
815
|
+
// </div>
|
|
816
|
+
// <div className="linkWrapper">
|
|
817
|
+
// {renderCenterSection()}
|
|
818
|
+
// </div>
|
|
819
|
+
// <div className="linkWrapper">
|
|
820
|
+
// {renderRightSection()}
|
|
821
|
+
// </div>
|
|
822
|
+
// {isMobileScreen && !isMobileMenuOpen && (
|
|
823
|
+
// <>
|
|
824
|
+
// {final.hasSidebar ?
|
|
825
|
+
// <span className="sidebar-trigger pointer hover-text-primary" onClick={final.openSidebar}>
|
|
826
|
+
// {final.sidebarTrigger || <Trigger isOpen={final.sidebarOpen} />}
|
|
827
|
+
// </span>
|
|
828
|
+
// :
|
|
829
|
+
// <span className="sidebar-trigger pointer hover-text-primary" onClick={toggleMenu}>
|
|
830
|
+
// {final.sidebarTrigger || <Trigger isOpen={isMobileMenuOpen} />}
|
|
831
|
+
// </span>
|
|
832
|
+
// }
|
|
833
|
+
// </>
|
|
834
|
+
// )}
|
|
835
|
+
// </nav>
|
|
836
|
+
// {renderMobileMenu()}
|
|
837
|
+
// </>
|
|
838
|
+
// );
|
|
839
|
+
// }
|
package/ui/richtext/RichText.js
CHANGED
|
@@ -56,8 +56,10 @@ var RichText = function (_a) {
|
|
|
56
56
|
var isInitialMount = (0, react_1.useRef)(true);
|
|
57
57
|
var isTypingRef = (0, react_1.useRef)(false);
|
|
58
58
|
var typingTimeoutRef = (0, react_1.useRef)(null);
|
|
59
|
+
var isUserChangeRef = (0, react_1.useRef)(false);
|
|
59
60
|
var _f = (0, react_1.useState)(false), isFocused = _f[0], setIsFocused = _f[1];
|
|
60
61
|
var lastKnownValueRef = (0, react_1.useRef)(value);
|
|
62
|
+
var quillInstanceRef = (0, react_1.useRef)(null);
|
|
61
63
|
// Update refs when props change
|
|
62
64
|
(0, react_1.useEffect)(function () {
|
|
63
65
|
onChangeRef.current = onChange;
|
|
@@ -71,6 +73,12 @@ var RichText = function (_a) {
|
|
|
71
73
|
placeholder: placeholder,
|
|
72
74
|
modules: modules || defaultModules,
|
|
73
75
|
}), quill = _g.quill, quillRef = _g.quillRef;
|
|
76
|
+
// Store quill instance in ref
|
|
77
|
+
(0, react_1.useEffect)(function () {
|
|
78
|
+
if (quill) {
|
|
79
|
+
quillInstanceRef.current = quill;
|
|
80
|
+
}
|
|
81
|
+
}, [quill]);
|
|
74
82
|
// Debounced onChange handler
|
|
75
83
|
var debouncedOnChange = (0, react_1.useCallback)(function (content) {
|
|
76
84
|
if (debounceTimeoutRef.current) {
|
|
@@ -81,10 +89,11 @@ var RichText = function (_a) {
|
|
|
81
89
|
}, 300); // 300ms debounce delay
|
|
82
90
|
}, []);
|
|
83
91
|
// Handle text change with debouncing
|
|
84
|
-
var handleTextChange = (0, react_1.useCallback)(function () {
|
|
85
|
-
var _a, _b
|
|
86
|
-
if (!quill)
|
|
92
|
+
var handleTextChange = (0, react_1.useCallback)(function (delta, oldDelta, source) {
|
|
93
|
+
var _a, _b;
|
|
94
|
+
if (!quill || source !== 'user')
|
|
87
95
|
return;
|
|
96
|
+
isUserChangeRef.current = true;
|
|
88
97
|
isTypingRef.current = true;
|
|
89
98
|
// Reset typing flag after 500ms of inactivity
|
|
90
99
|
if (typingTimeoutRef.current) {
|
|
@@ -94,17 +103,23 @@ var RichText = function (_a) {
|
|
|
94
103
|
isTypingRef.current = false;
|
|
95
104
|
}, 500);
|
|
96
105
|
var plainText = quill.getText().trim();
|
|
106
|
+
var currentHTML = quill.root.innerHTML;
|
|
97
107
|
// --- Enforce maxValue if needed ---
|
|
98
108
|
if (maxValueRef.current && plainText.length > maxValueRef.current) {
|
|
99
|
-
|
|
100
|
-
quill.
|
|
101
|
-
|
|
109
|
+
// Store current selection
|
|
110
|
+
var selection = quill.getSelection();
|
|
111
|
+
// Remove the extra content
|
|
112
|
+
quill.deleteText(maxValueRef.current, plainText.length - maxValueRef.current);
|
|
113
|
+
// Restore selection if it was at the end
|
|
114
|
+
if (selection && selection.index > maxValueRef.current) {
|
|
115
|
+
quill.setSelection(maxValueRef.current);
|
|
116
|
+
}
|
|
102
117
|
return; // Don't trigger onChange for truncated text
|
|
103
118
|
}
|
|
104
119
|
// --- Clean the HTML output ---
|
|
105
|
-
var cleanedHTML = (
|
|
106
|
-
) === null ||
|
|
107
|
-
) === null ||
|
|
120
|
+
var cleanedHTML = (_b = (_a = currentHTML === null || currentHTML === void 0 ? void 0 : currentHTML.replace(/<p><br><\/p>/g, '') // remove empty paragraphs
|
|
121
|
+
) === null || _a === void 0 ? void 0 : _a.replace(/\s+/g, ' ') // collapse multiple spaces
|
|
122
|
+
) === null || _b === void 0 ? void 0 : _b.trim(); // remove leading/trailing spaces
|
|
108
123
|
lastKnownValueRef.current = cleanedHTML || '';
|
|
109
124
|
debouncedOnChange(lastKnownValueRef.current);
|
|
110
125
|
}, [quill, debouncedOnChange]);
|
|
@@ -116,11 +131,13 @@ var RichText = function (_a) {
|
|
|
116
131
|
// Handle focus
|
|
117
132
|
var handleFocus = (0, react_1.useCallback)(function () {
|
|
118
133
|
setIsFocused(true);
|
|
134
|
+
isUserChangeRef.current = false;
|
|
119
135
|
}, []);
|
|
120
136
|
// Handle blur
|
|
121
137
|
var handleBlur = (0, react_1.useCallback)(function () {
|
|
122
138
|
setIsFocused(false);
|
|
123
139
|
isTypingRef.current = false;
|
|
140
|
+
isUserChangeRef.current = false;
|
|
124
141
|
}, []);
|
|
125
142
|
// Set up event listeners
|
|
126
143
|
(0, react_1.useEffect)(function () {
|
|
@@ -156,7 +173,8 @@ var RichText = function (_a) {
|
|
|
156
173
|
if (isInitialMount.current && value) {
|
|
157
174
|
// clean before setting editor value
|
|
158
175
|
var cleanedValue = (_b = (_a = value === null || value === void 0 ? void 0 : value.replace(/<p><br><\/p>/g, '')) === null || _a === void 0 ? void 0 : _a.replace(/\s+/g, ' ')) === null || _b === void 0 ? void 0 : _b.trim();
|
|
159
|
-
quill.
|
|
176
|
+
// Use quill.clipboard.dangerouslyPasteHTML to preserve formatting
|
|
177
|
+
quill.clipboard.dangerouslyPasteHTML(0, cleanedValue || '');
|
|
160
178
|
lastKnownValueRef.current = cleanedValue || '';
|
|
161
179
|
isInitialMount.current = false;
|
|
162
180
|
}
|
|
@@ -169,24 +187,50 @@ var RichText = function (_a) {
|
|
|
169
187
|
// Skip if value is the same as current editor content
|
|
170
188
|
if (value === lastKnownValueRef.current)
|
|
171
189
|
return;
|
|
172
|
-
// Don't update while user is typing or editor is focused
|
|
173
|
-
if (isTypingRef.current || isFocused) {
|
|
190
|
+
// Don't update while user is typing or editor is focused with recent user changes
|
|
191
|
+
if (isTypingRef.current || (isFocused && isUserChangeRef.current)) {
|
|
174
192
|
return;
|
|
175
193
|
}
|
|
176
194
|
// clean before setting editor value
|
|
177
195
|
var cleanedValue = (_b = (_a = value === null || value === void 0 ? void 0 : value.replace(/<p><br><\/p>/g, '')) === null || _a === void 0 ? void 0 : _a.replace(/\s+/g, ' ')) === null || _b === void 0 ? void 0 : _b.trim();
|
|
178
196
|
if (quill.root.innerHTML !== cleanedValue) {
|
|
179
|
-
|
|
180
|
-
|
|
197
|
+
// Store current selection
|
|
198
|
+
var selection_1 = quill.getSelection();
|
|
199
|
+
// Get current HTML content
|
|
200
|
+
var currentHTML = quill.root.innerHTML;
|
|
201
|
+
// Calculate the difference
|
|
202
|
+
var currentLength = quill.getLength();
|
|
203
|
+
// Only update if there's a meaningful difference
|
|
204
|
+
if (cleanedValue !== currentHTML) {
|
|
205
|
+
// Replace content using quill's API
|
|
206
|
+
quill.clipboard.dangerouslyPasteHTML(0, cleanedValue || '');
|
|
207
|
+
lastKnownValueRef.current = cleanedValue || '';
|
|
208
|
+
// Try to restore selection position if we had one
|
|
209
|
+
if (selection_1) {
|
|
210
|
+
// Set a small timeout to ensure the content is updated
|
|
211
|
+
setTimeout(function () {
|
|
212
|
+
var newLength = quill.getLength();
|
|
213
|
+
var newIndex = Math.min(selection_1.index, newLength);
|
|
214
|
+
quill.setSelection(newIndex);
|
|
215
|
+
}, 0);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
181
218
|
}
|
|
182
219
|
}, [quill, value, isFocused]);
|
|
183
220
|
var insertEmoji = (0, react_1.useCallback)(function (emoji) {
|
|
184
221
|
if (quill && savedRange.current) {
|
|
185
222
|
var plainText = quill.getText().trim();
|
|
186
223
|
if (!maxValueRef.current || plainText.length + emoji.length <= maxValueRef.current) {
|
|
187
|
-
|
|
188
|
-
quill.
|
|
189
|
-
|
|
224
|
+
// Use the current selection if available, otherwise use saved range
|
|
225
|
+
var currentSelection = quill.getSelection();
|
|
226
|
+
var insertIndex = currentSelection ? currentSelection.index : savedRange.current.index;
|
|
227
|
+
quill.insertText(insertIndex, emoji);
|
|
228
|
+
quill.setSelection(insertIndex + emoji.length);
|
|
229
|
+
// Update saved range
|
|
230
|
+
savedRange.current = {
|
|
231
|
+
index: insertIndex + emoji.length,
|
|
232
|
+
length: 0
|
|
233
|
+
};
|
|
190
234
|
}
|
|
191
235
|
}
|
|
192
236
|
}, [quill]);
|
|
@@ -224,6 +268,281 @@ var RichText = function (_a) {
|
|
|
224
268
|
};
|
|
225
269
|
exports.default = RichText;
|
|
226
270
|
// 'use client';
|
|
271
|
+
// import React, { useEffect, useRef, useCallback, useState } from 'react';
|
|
272
|
+
// import { useQuill } from 'react-quilljs';
|
|
273
|
+
// import { MdOutlineEmojiEmotions } from 'react-icons/md';
|
|
274
|
+
// import { AllEmojis } from '../../utils/Emojis';
|
|
275
|
+
// import Dropdown from '../drop/Dropdown';
|
|
276
|
+
// import RowFlex from '../specials/RowFlex';
|
|
277
|
+
// import ToolTip from '../tooltip/ToolTip';
|
|
278
|
+
// import Circle from '../specials/Circle';
|
|
279
|
+
// import Tip from '../tooltip/Tip';
|
|
280
|
+
// import Flex from '../flex/Flex';
|
|
281
|
+
// type RangeStatic = {
|
|
282
|
+
// index: number;
|
|
283
|
+
// length: number;
|
|
284
|
+
// };
|
|
285
|
+
// interface RichTextProps {
|
|
286
|
+
// value: string;
|
|
287
|
+
// onChange: (content: string) => void;
|
|
288
|
+
// showEmojis?: boolean;
|
|
289
|
+
// placeholder?: string;
|
|
290
|
+
// afterEmoji?: React.ReactNode;
|
|
291
|
+
// funcss?: string;
|
|
292
|
+
// modules?: any;
|
|
293
|
+
// theme?: 'bubble' | 'snow';
|
|
294
|
+
// fontFamily?: string;
|
|
295
|
+
// maxValue?: number;
|
|
296
|
+
// }
|
|
297
|
+
// const RichText: React.FC<RichTextProps> = ({
|
|
298
|
+
// value,
|
|
299
|
+
// onChange,
|
|
300
|
+
// showEmojis = false,
|
|
301
|
+
// placeholder = 'Write something...',
|
|
302
|
+
// afterEmoji,
|
|
303
|
+
// funcss = '',
|
|
304
|
+
// modules,
|
|
305
|
+
// theme = 'bubble',
|
|
306
|
+
// fontFamily,
|
|
307
|
+
// maxValue,
|
|
308
|
+
// }) => {
|
|
309
|
+
// const savedRange = useRef<RangeStatic | null>(null);
|
|
310
|
+
// const onChangeRef = useRef(onChange);
|
|
311
|
+
// const maxValueRef = useRef(maxValue);
|
|
312
|
+
// const debounceTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
313
|
+
// const isInitialMount = useRef(true);
|
|
314
|
+
// const isTypingRef = useRef(false);
|
|
315
|
+
// const typingTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
316
|
+
// const [isFocused, setIsFocused] = useState(false);
|
|
317
|
+
// const lastKnownValueRef = useRef(value);
|
|
318
|
+
// // Update refs when props change
|
|
319
|
+
// useEffect(() => {
|
|
320
|
+
// onChangeRef.current = onChange;
|
|
321
|
+
// maxValueRef.current = maxValue;
|
|
322
|
+
// }, [onChange, maxValue]);
|
|
323
|
+
// const defaultModules = {
|
|
324
|
+
// toolbar: [['bold', 'italic', 'underline'], [{ list: 'bullet' }]],
|
|
325
|
+
// };
|
|
326
|
+
// const { quill, quillRef } = useQuill({
|
|
327
|
+
// theme,
|
|
328
|
+
// placeholder,
|
|
329
|
+
// modules: modules || defaultModules,
|
|
330
|
+
// });
|
|
331
|
+
// // Debounced onChange handler
|
|
332
|
+
// const debouncedOnChange = useCallback((content: string) => {
|
|
333
|
+
// if (debounceTimeoutRef.current) {
|
|
334
|
+
// clearTimeout(debounceTimeoutRef.current);
|
|
335
|
+
// }
|
|
336
|
+
// debounceTimeoutRef.current = setTimeout(() => {
|
|
337
|
+
// onChangeRef.current(content);
|
|
338
|
+
// }, 300); // 300ms debounce delay
|
|
339
|
+
// }, []);
|
|
340
|
+
// // Handle text change with debouncing
|
|
341
|
+
// const handleTextChange = useCallback(() => {
|
|
342
|
+
// if (!quill) return;
|
|
343
|
+
// isTypingRef.current = true;
|
|
344
|
+
// // Reset typing flag after 500ms of inactivity
|
|
345
|
+
// if (typingTimeoutRef.current) {
|
|
346
|
+
// clearTimeout(typingTimeoutRef.current);
|
|
347
|
+
// }
|
|
348
|
+
// typingTimeoutRef.current = setTimeout(() => {
|
|
349
|
+
// isTypingRef.current = false;
|
|
350
|
+
// }, 500);
|
|
351
|
+
// const plainText = quill.getText().trim();
|
|
352
|
+
// // --- Enforce maxValue if needed ---
|
|
353
|
+
// if (maxValueRef.current && plainText.length > maxValueRef.current) {
|
|
354
|
+
// const truncated = plainText.slice(0, maxValueRef.current);
|
|
355
|
+
// quill.setText(truncated);
|
|
356
|
+
// quill.setSelection(truncated.length);
|
|
357
|
+
// return; // Don't trigger onChange for truncated text
|
|
358
|
+
// }
|
|
359
|
+
// // --- Clean the HTML output ---
|
|
360
|
+
// const cleanedHTML = quill.root.innerHTML
|
|
361
|
+
// ?.replace(/<p><br><\/p>/g, '') // remove empty paragraphs
|
|
362
|
+
// ?.replace(/\s+/g, ' ') // collapse multiple spaces
|
|
363
|
+
// ?.trim(); // remove leading/trailing spaces
|
|
364
|
+
// lastKnownValueRef.current = cleanedHTML || '';
|
|
365
|
+
// debouncedOnChange(lastKnownValueRef.current);
|
|
366
|
+
// }, [quill, debouncedOnChange]);
|
|
367
|
+
// // Handle selection change
|
|
368
|
+
// const handleSelectionChange = useCallback((range: RangeStatic | null) => {
|
|
369
|
+
// if (range) savedRange.current = range;
|
|
370
|
+
// }, []);
|
|
371
|
+
// // Handle focus
|
|
372
|
+
// const handleFocus = useCallback(() => {
|
|
373
|
+
// setIsFocused(true);
|
|
374
|
+
// }, []);
|
|
375
|
+
// // Handle blur
|
|
376
|
+
// const handleBlur = useCallback(() => {
|
|
377
|
+
// setIsFocused(false);
|
|
378
|
+
// isTypingRef.current = false;
|
|
379
|
+
// }, []);
|
|
380
|
+
// // Set up event listeners
|
|
381
|
+
// useEffect(() => {
|
|
382
|
+
// if (!quill) return;
|
|
383
|
+
// const editor = quill.root;
|
|
384
|
+
// quill.on('selection-change', handleSelectionChange);
|
|
385
|
+
// quill.on('text-change', handleTextChange);
|
|
386
|
+
// // Add focus/blur event listeners
|
|
387
|
+
// editor.addEventListener('focus', handleFocus);
|
|
388
|
+
// editor.addEventListener('blur', handleBlur);
|
|
389
|
+
// return () => {
|
|
390
|
+
// quill.off('selection-change', handleSelectionChange);
|
|
391
|
+
// quill.off('text-change', handleTextChange);
|
|
392
|
+
// // Remove focus/blur event listeners
|
|
393
|
+
// editor.removeEventListener('focus', handleFocus);
|
|
394
|
+
// editor.removeEventListener('blur', handleBlur);
|
|
395
|
+
// // Clean up timeouts
|
|
396
|
+
// if (debounceTimeoutRef.current) {
|
|
397
|
+
// clearTimeout(debounceTimeoutRef.current);
|
|
398
|
+
// }
|
|
399
|
+
// if (typingTimeoutRef.current) {
|
|
400
|
+
// clearTimeout(typingTimeoutRef.current);
|
|
401
|
+
// }
|
|
402
|
+
// };
|
|
403
|
+
// }, [quill, handleSelectionChange, handleTextChange, handleFocus, handleBlur]);
|
|
404
|
+
// // Initialize editor with initial value
|
|
405
|
+
// useEffect(() => {
|
|
406
|
+
// if (!quill) return;
|
|
407
|
+
// // Only set initial value on first mount
|
|
408
|
+
// if (isInitialMount.current && value) {
|
|
409
|
+
// // clean before setting editor value
|
|
410
|
+
// const cleanedValue = value
|
|
411
|
+
// ?.replace(/<p><br><\/p>/g, '')
|
|
412
|
+
// ?.replace(/\s+/g, ' ')
|
|
413
|
+
// ?.trim();
|
|
414
|
+
// quill.root.innerHTML = cleanedValue || '';
|
|
415
|
+
// lastKnownValueRef.current = cleanedValue || '';
|
|
416
|
+
// isInitialMount.current = false;
|
|
417
|
+
// }
|
|
418
|
+
// }, [quill, value]);
|
|
419
|
+
// // Update editor when value prop changes (for external updates)
|
|
420
|
+
// useEffect(() => {
|
|
421
|
+
// if (!quill || isInitialMount.current) return;
|
|
422
|
+
// // Skip if value is the same as current editor content
|
|
423
|
+
// if (value === lastKnownValueRef.current) return;
|
|
424
|
+
// // Don't update while user is typing or editor is focused
|
|
425
|
+
// if (isTypingRef.current || isFocused) {
|
|
426
|
+
// return;
|
|
427
|
+
// }
|
|
428
|
+
// // clean before setting editor value
|
|
429
|
+
// const cleanedValue = value
|
|
430
|
+
// ?.replace(/<p><br><\/p>/g, '')
|
|
431
|
+
// ?.replace(/\s+/g, ' ')
|
|
432
|
+
// ?.trim();
|
|
433
|
+
// if (quill.root.innerHTML !== cleanedValue) {
|
|
434
|
+
// quill.root.innerHTML = cleanedValue || '';
|
|
435
|
+
// lastKnownValueRef.current = cleanedValue || '';
|
|
436
|
+
// }
|
|
437
|
+
// }, [quill, value, isFocused]);
|
|
438
|
+
// const insertEmoji = useCallback((emoji: string) => {
|
|
439
|
+
// if (quill && savedRange.current) {
|
|
440
|
+
// const plainText = quill.getText().trim();
|
|
441
|
+
// if (!maxValueRef.current || plainText.length + emoji.length <= maxValueRef.current) {
|
|
442
|
+
// const selection = quill.getSelection();
|
|
443
|
+
// quill.insertText(savedRange.current.index, emoji);
|
|
444
|
+
// quill.setSelection(savedRange.current.index + emoji.length);
|
|
445
|
+
// }
|
|
446
|
+
// }
|
|
447
|
+
// }, [quill]);
|
|
448
|
+
// const renderEmojiSection = useCallback((title: string, emojis: string[]) => (
|
|
449
|
+
// <>
|
|
450
|
+
// <div className="mb-2 mt-2 text-sm">{title}</div>
|
|
451
|
+
// <RowFlex gap={0.3}>
|
|
452
|
+
// {emojis.map((emoji, i) => (
|
|
453
|
+
// <span
|
|
454
|
+
// key={i}
|
|
455
|
+
// className="h6 pointer"
|
|
456
|
+
// onClick={() => insertEmoji(emoji)}
|
|
457
|
+
// >
|
|
458
|
+
// {emoji}
|
|
459
|
+
// </span>
|
|
460
|
+
// ))}
|
|
461
|
+
// </RowFlex>
|
|
462
|
+
// </>
|
|
463
|
+
// ), [insertEmoji]);
|
|
464
|
+
// return (
|
|
465
|
+
// <div
|
|
466
|
+
// className={`fit round-edge ${funcss}`}
|
|
467
|
+
// style={{ position: 'relative', overflow: 'visible' }}
|
|
468
|
+
// >
|
|
469
|
+
// <div id="editor-container" className="bubble-editor-container p-0">
|
|
470
|
+
// <div
|
|
471
|
+
// ref={quillRef}
|
|
472
|
+
// className={theme === 'bubble' ? 'bubble-editor' : 'snow-editor'}
|
|
473
|
+
// style={{
|
|
474
|
+
// fontFamily: fontFamily || 'inherit',
|
|
475
|
+
// }}
|
|
476
|
+
// />
|
|
477
|
+
// </div>
|
|
478
|
+
// {(showEmojis || maxValue) && (
|
|
479
|
+
// <div
|
|
480
|
+
// className="p-1"
|
|
481
|
+
// style={{ height: 'fit-content', top: `calc(100%)`, width: '100%' }}
|
|
482
|
+
// >
|
|
483
|
+
// <Flex justify="space-between" gap={1} alignItems="center" width="100%">
|
|
484
|
+
// {(showEmojis || afterEmoji) ? (
|
|
485
|
+
// <div>
|
|
486
|
+
// <Flex width="100%" gap={0.5} alignItems="center">
|
|
487
|
+
// {showEmojis && (
|
|
488
|
+
// <Dropdown
|
|
489
|
+
// closableOnlyOutside
|
|
490
|
+
// button={
|
|
491
|
+
// <ToolTip>
|
|
492
|
+
// <Circle size={2} funcss="bg border">
|
|
493
|
+
// <MdOutlineEmojiEmotions />
|
|
494
|
+
// </Circle>
|
|
495
|
+
// <Tip
|
|
496
|
+
// tip="top"
|
|
497
|
+
// animation="ScaleUp"
|
|
498
|
+
// duration={0.5}
|
|
499
|
+
// content="Emojis"
|
|
500
|
+
// />
|
|
501
|
+
// </ToolTip>
|
|
502
|
+
// }
|
|
503
|
+
// items={[
|
|
504
|
+
// {
|
|
505
|
+
// label: (
|
|
506
|
+
// <div
|
|
507
|
+
// className="w-200 h-200"
|
|
508
|
+
// style={{ overflowY: 'auto' }}
|
|
509
|
+
// >
|
|
510
|
+
// {renderEmojiSection('❤️ Smileys & People', AllEmojis.Smiley)}
|
|
511
|
+
// {renderEmojiSection('👍 Gestures & Body Parts', AllEmojis.Gesture)}
|
|
512
|
+
// {renderEmojiSection('🔥 Symbols & Expressions', AllEmojis.Symbols)}
|
|
513
|
+
// {renderEmojiSection('🚀 Travel, Objects & Activities', AllEmojis.Travel)}
|
|
514
|
+
// {renderEmojiSection('👨👩👧👦 People & Professions', AllEmojis.People)}
|
|
515
|
+
// {renderEmojiSection('🐶 Animals & Nature', AllEmojis.Animals)}
|
|
516
|
+
// </div>
|
|
517
|
+
// ),
|
|
518
|
+
// },
|
|
519
|
+
// ]}
|
|
520
|
+
// />
|
|
521
|
+
// )}
|
|
522
|
+
// {afterEmoji}
|
|
523
|
+
// </Flex>
|
|
524
|
+
// </div>
|
|
525
|
+
// ) : (
|
|
526
|
+
// <div />
|
|
527
|
+
// )}
|
|
528
|
+
// {maxValue && quill ? (
|
|
529
|
+
// <div className="text-xs text-right">
|
|
530
|
+
// <span className="text-primary">
|
|
531
|
+
// {quill.getText().trim().length}
|
|
532
|
+
// </span>
|
|
533
|
+
// /{maxValue}
|
|
534
|
+
// </div>
|
|
535
|
+
// ) : (
|
|
536
|
+
// <div />
|
|
537
|
+
// )}
|
|
538
|
+
// </Flex>
|
|
539
|
+
// </div>
|
|
540
|
+
// )}
|
|
541
|
+
// </div>
|
|
542
|
+
// );
|
|
543
|
+
// };
|
|
544
|
+
// export default RichText;
|
|
545
|
+
// 'use client';
|
|
227
546
|
// import React, { useEffect, useRef } from 'react';
|
|
228
547
|
// import { useQuill } from 'react-quilljs';
|
|
229
548
|
// import { MdOutlineEmojiEmotions } from 'react-icons/md';
|