funuicss 2.7.4 → 2.7.6
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/css/fun.css +63 -9
- package/index.d.ts +2 -0
- package/index.js +5 -1
- package/index.tsx +2 -0
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/ui/accordion/Accordion.d.ts +16 -0
- package/ui/accordion/Accordion.js +80 -0
- package/ui/accordion/Accordion.tsx +117 -0
- package/ui/flex/Flex.d.ts +29 -0
- package/ui/flex/Flex.js +47 -0
- package/ui/flex/Flex.tsx +115 -0
- package/ui/video/Video.js +53 -8
- package/ui/video/Video.tsx +85 -25
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface AccordionProps {
|
|
3
|
+
items: {
|
|
4
|
+
title: string;
|
|
5
|
+
content: React.ReactNode;
|
|
6
|
+
}[];
|
|
7
|
+
allowMultiple?: boolean;
|
|
8
|
+
defaultOpenIndexes?: number[];
|
|
9
|
+
itemClass?: string;
|
|
10
|
+
titleClass?: string;
|
|
11
|
+
iconClass?: string;
|
|
12
|
+
contentClass?: string;
|
|
13
|
+
activeClass?: string;
|
|
14
|
+
}
|
|
15
|
+
declare const Accordion: React.FC<AccordionProps>;
|
|
16
|
+
export default Accordion;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
37
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
38
|
+
if (ar || !(i in from)) {
|
|
39
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
40
|
+
ar[i] = from[i];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
44
|
+
};
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
var react_1 = __importStar(require("react"));
|
|
47
|
+
var pi_1 = require("react-icons/pi");
|
|
48
|
+
var AccordionItem = function (_a) {
|
|
49
|
+
var title = _a.title, content = _a.content, isOpen = _a.isOpen, onToggle = _a.onToggle, _b = _a.itemClass, itemClass = _b === void 0 ? '' : _b, _c = _a.titleClass, titleClass = _c === void 0 ? '' : _c, _d = _a.iconClass, iconClass = _d === void 0 ? '' : _d, _e = _a.contentClass, contentClass = _e === void 0 ? '' : _e, _f = _a.activeClass, activeClass = _f === void 0 ? '' : _f;
|
|
50
|
+
return (react_1.default.createElement("div", { className: "accordion-item ".concat(itemClass, " ").concat(isOpen ? activeClass : '') },
|
|
51
|
+
react_1.default.createElement("button", { className: "accordion-header ".concat(titleClass), onClick: onToggle },
|
|
52
|
+
react_1.default.createElement("span", null, title),
|
|
53
|
+
react_1.default.createElement("span", { className: iconClass }, isOpen ? (react_1.default.createElement("span", { className: "animated slide-up" },
|
|
54
|
+
react_1.default.createElement(pi_1.PiCaretUp, null))) : (react_1.default.createElement("span", { className: "animated slide-down" },
|
|
55
|
+
react_1.default.createElement(pi_1.PiCaretDown, null))))),
|
|
56
|
+
react_1.default.createElement("div", { className: "accordion-content ".concat(contentClass, " ").concat(isOpen ? 'open' : '') },
|
|
57
|
+
react_1.default.createElement("div", { className: "accordion-inner" }, content))));
|
|
58
|
+
};
|
|
59
|
+
var Accordion = function (_a) {
|
|
60
|
+
var _b;
|
|
61
|
+
var items = _a.items, _c = _a.allowMultiple, allowMultiple = _c === void 0 ? false : _c, // ❗ default is only one open
|
|
62
|
+
_d = _a.defaultOpenIndexes, // ❗ default is only one open
|
|
63
|
+
defaultOpenIndexes = _d === void 0 ? [] : _d, itemClass = _a.itemClass, titleClass = _a.titleClass, iconClass = _a.iconClass, contentClass = _a.contentClass, activeClass = _a.activeClass;
|
|
64
|
+
var _e = (0, react_1.useState)(allowMultiple ? defaultOpenIndexes : [(_b = defaultOpenIndexes[0]) !== null && _b !== void 0 ? _b : -1]), openIndexes = _e[0], setOpenIndexes = _e[1];
|
|
65
|
+
var toggleIndex = function (index) {
|
|
66
|
+
if (allowMultiple) {
|
|
67
|
+
if (openIndexes.includes(index)) {
|
|
68
|
+
setOpenIndexes(openIndexes.filter(function (i) { return i !== index; }));
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
setOpenIndexes(__spreadArray(__spreadArray([], openIndexes, true), [index], false));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
setOpenIndexes(openIndexes.includes(index) ? [] : [index]);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
return (react_1.default.createElement("div", { className: "accordion" }, items.map(function (item, index) { return (react_1.default.createElement(AccordionItem, { key: index, index: index, title: item.title, content: item.content, isOpen: openIndexes.includes(index), onToggle: function () { return toggleIndex(index); }, itemClass: itemClass, titleClass: titleClass, iconClass: iconClass, contentClass: contentClass, activeClass: activeClass })); })));
|
|
79
|
+
};
|
|
80
|
+
exports.default = Accordion;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
import { PiCaretDown, PiCaretUp } from 'react-icons/pi';
|
|
4
|
+
|
|
5
|
+
interface AccordionItemProps {
|
|
6
|
+
title: string;
|
|
7
|
+
content: React.ReactNode;
|
|
8
|
+
isOpen?: boolean;
|
|
9
|
+
onToggle?: () => void;
|
|
10
|
+
index?: number;
|
|
11
|
+
|
|
12
|
+
// Customization
|
|
13
|
+
itemClass?: string;
|
|
14
|
+
titleClass?: string;
|
|
15
|
+
iconClass?: string;
|
|
16
|
+
contentClass?: string;
|
|
17
|
+
activeClass?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const AccordionItem: React.FC<AccordionItemProps> = ({
|
|
21
|
+
title,
|
|
22
|
+
content,
|
|
23
|
+
isOpen,
|
|
24
|
+
onToggle,
|
|
25
|
+
itemClass = '',
|
|
26
|
+
titleClass = '',
|
|
27
|
+
iconClass = '',
|
|
28
|
+
contentClass = '',
|
|
29
|
+
activeClass = '',
|
|
30
|
+
}) => {
|
|
31
|
+
return (
|
|
32
|
+
<div className={`accordion-item ${itemClass} ${isOpen ? activeClass : ''}`}>
|
|
33
|
+
<button className={`accordion-header ${titleClass}`} onClick={onToggle}>
|
|
34
|
+
<span>{title}</span>
|
|
35
|
+
<span className={iconClass}>
|
|
36
|
+
{isOpen ? (
|
|
37
|
+
<span className="animated slide-up">
|
|
38
|
+
<PiCaretUp />
|
|
39
|
+
</span>
|
|
40
|
+
) : (
|
|
41
|
+
<span className="animated slide-down">
|
|
42
|
+
<PiCaretDown />
|
|
43
|
+
</span>
|
|
44
|
+
)}
|
|
45
|
+
</span>
|
|
46
|
+
</button>
|
|
47
|
+
<div className={`accordion-content ${contentClass} ${isOpen ? 'open' : ''}`}>
|
|
48
|
+
<div className="accordion-inner">{content}</div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
interface AccordionProps {
|
|
55
|
+
items: {
|
|
56
|
+
title: string;
|
|
57
|
+
content: React.ReactNode;
|
|
58
|
+
}[];
|
|
59
|
+
allowMultiple?: boolean; // ✅ NEW
|
|
60
|
+
defaultOpenIndexes?: number[]; // optional
|
|
61
|
+
|
|
62
|
+
// Custom styles
|
|
63
|
+
itemClass?: string;
|
|
64
|
+
titleClass?: string;
|
|
65
|
+
iconClass?: string;
|
|
66
|
+
contentClass?: string;
|
|
67
|
+
activeClass?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const Accordion: React.FC<AccordionProps> = ({
|
|
71
|
+
items,
|
|
72
|
+
allowMultiple = false, // ❗ default is only one open
|
|
73
|
+
defaultOpenIndexes = [],
|
|
74
|
+
itemClass,
|
|
75
|
+
titleClass,
|
|
76
|
+
iconClass,
|
|
77
|
+
contentClass,
|
|
78
|
+
activeClass,
|
|
79
|
+
}) => {
|
|
80
|
+
const [openIndexes, setOpenIndexes] = useState<number[]>(
|
|
81
|
+
allowMultiple ? defaultOpenIndexes : [defaultOpenIndexes[0] ?? -1]
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const toggleIndex = (index: number) => {
|
|
85
|
+
if (allowMultiple) {
|
|
86
|
+
if (openIndexes.includes(index)) {
|
|
87
|
+
setOpenIndexes(openIndexes.filter((i) => i !== index));
|
|
88
|
+
} else {
|
|
89
|
+
setOpenIndexes([...openIndexes, index]);
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
setOpenIndexes(openIndexes.includes(index) ? [] : [index]);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div className="accordion">
|
|
98
|
+
{items.map((item, index) => (
|
|
99
|
+
<AccordionItem
|
|
100
|
+
key={index}
|
|
101
|
+
index={index}
|
|
102
|
+
title={item.title}
|
|
103
|
+
content={item.content}
|
|
104
|
+
isOpen={openIndexes.includes(index)}
|
|
105
|
+
onToggle={() => toggleIndex(index)}
|
|
106
|
+
itemClass={itemClass}
|
|
107
|
+
titleClass={titleClass}
|
|
108
|
+
iconClass={iconClass}
|
|
109
|
+
contentClass={contentClass}
|
|
110
|
+
activeClass={activeClass}
|
|
111
|
+
/>
|
|
112
|
+
))}
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export default Accordion;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface FlexProps {
|
|
3
|
+
as?: keyof JSX.IntrinsicElements;
|
|
4
|
+
className?: string;
|
|
5
|
+
funcss?: string;
|
|
6
|
+
id?: string;
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
style?: React.CSSProperties;
|
|
9
|
+
direction?: 'row' | 'column' | 'row-reverse' | 'column-reverse';
|
|
10
|
+
wrap?: 'wrap' | 'nowrap' | 'wrap-reverse';
|
|
11
|
+
justify?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly';
|
|
12
|
+
alignItems?: 'stretch' | 'flex-start' | 'flex-end' | 'center' | 'baseline';
|
|
13
|
+
alignContent?: 'stretch' | 'center' | 'flex-start' | 'flex-end' | 'space-between' | 'space-around';
|
|
14
|
+
gap?: number;
|
|
15
|
+
gapX?: number;
|
|
16
|
+
gapY?: number;
|
|
17
|
+
gapUnit?: 'rem' | 'px' | 'em';
|
|
18
|
+
grow?: number;
|
|
19
|
+
shrink?: number;
|
|
20
|
+
basis?: string;
|
|
21
|
+
flex?: string;
|
|
22
|
+
responsiveSmall?: boolean;
|
|
23
|
+
responsiveMedium?: boolean;
|
|
24
|
+
responsiveLarge?: boolean;
|
|
25
|
+
fullWidth?: boolean;
|
|
26
|
+
fullHeight?: boolean;
|
|
27
|
+
}
|
|
28
|
+
export default function Flex({ as: Component, className, funcss, id, children, style, direction, wrap, justify, alignItems, alignContent, gap, gapX, gapY, gapUnit, grow, shrink, basis, flex, responsiveSmall, responsiveMedium, responsiveLarge, fullWidth, fullHeight, ...rest }: FlexProps): React.JSX.Element;
|
|
29
|
+
export {};
|
package/ui/flex/Flex.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
var __assign = (this && this.__assign) || function () {
|
|
4
|
+
__assign = Object.assign || function(t) {
|
|
5
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
6
|
+
s = arguments[i];
|
|
7
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
8
|
+
t[p] = s[p];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
return __assign.apply(this, arguments);
|
|
13
|
+
};
|
|
14
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
15
|
+
var t = {};
|
|
16
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
17
|
+
t[p] = s[p];
|
|
18
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
19
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
20
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
21
|
+
t[p[i]] = s[p[i]];
|
|
22
|
+
}
|
|
23
|
+
return t;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.default = Flex;
|
|
30
|
+
var react_1 = __importDefault(require("react"));
|
|
31
|
+
function Flex(_a) {
|
|
32
|
+
var _b = _a.as, Component = _b === void 0 ? 'div' : _b, className = _a.className, funcss = _a.funcss, id = _a.id, children = _a.children, _c = _a.style, style = _c === void 0 ? {} : _c,
|
|
33
|
+
// Flex container
|
|
34
|
+
_d = _a.direction,
|
|
35
|
+
// Flex container
|
|
36
|
+
direction = _d === void 0 ? 'row' : _d, _e = _a.wrap, wrap = _e === void 0 ? 'wrap' : _e, justify = _a.justify, alignItems = _a.alignItems, alignContent = _a.alignContent,
|
|
37
|
+
// Gap
|
|
38
|
+
gap = _a.gap, gapX = _a.gapX, gapY = _a.gapY, _f = _a.gapUnit, gapUnit = _f === void 0 ? 'rem' : _f,
|
|
39
|
+
// Flex item
|
|
40
|
+
grow = _a.grow, shrink = _a.shrink, basis = _a.basis, flex = _a.flex,
|
|
41
|
+
// Responsive
|
|
42
|
+
responsiveSmall = _a.responsiveSmall, responsiveMedium = _a.responsiveMedium, responsiveLarge = _a.responsiveLarge,
|
|
43
|
+
// Size
|
|
44
|
+
fullWidth = _a.fullWidth, fullHeight = _a.fullHeight, rest = __rest(_a, ["as", "className", "funcss", "id", "children", "style", "direction", "wrap", "justify", "alignItems", "alignContent", "gap", "gapX", "gapY", "gapUnit", "grow", "shrink", "basis", "flex", "responsiveSmall", "responsiveMedium", "responsiveLarge", "fullWidth", "fullHeight"]);
|
|
45
|
+
var responsiveClasses = "\n ".concat(responsiveSmall ? 'responsiveSmall' : '', "\n ").concat(responsiveMedium ? 'responsiveMedium' : '', "\n ").concat(responsiveLarge ? 'responsiveLarge' : '', "\n ").trim();
|
|
46
|
+
return (react_1.default.createElement(Component, __assign({ id: id, className: "\n ".concat(className || '', "\n ").concat(funcss || '', "\n ").concat(responsiveClasses, "\n ").trim(), style: __assign({ display: 'flex', flexDirection: direction, flexWrap: wrap, justifyContent: justify, alignItems: alignItems, alignContent: alignContent, gap: gap ? "".concat(gap).concat(gapUnit) : undefined, columnGap: gapX ? "".concat(gapX).concat(gapUnit) : undefined, rowGap: gapY ? "".concat(gapY).concat(gapUnit) : undefined, flexGrow: grow, flexShrink: shrink, flexBasis: basis, flex: flex, width: fullWidth ? '100%' : undefined, height: fullHeight ? '100%' : undefined }, style) }, rest), children));
|
|
47
|
+
}
|
package/ui/flex/Flex.tsx
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
interface FlexProps {
|
|
5
|
+
as?: keyof JSX.IntrinsicElements; // Render as any tag (e.g. section, ul)
|
|
6
|
+
className?: string;
|
|
7
|
+
funcss?: string;
|
|
8
|
+
id?: string;
|
|
9
|
+
children?: React.ReactNode;
|
|
10
|
+
style?: React.CSSProperties;
|
|
11
|
+
|
|
12
|
+
// Flex container properties
|
|
13
|
+
direction?: 'row' | 'column' | 'row-reverse' | 'column-reverse';
|
|
14
|
+
wrap?: 'wrap' | 'nowrap' | 'wrap-reverse';
|
|
15
|
+
justify?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly';
|
|
16
|
+
alignItems?: 'stretch' | 'flex-start' | 'flex-end' | 'center' | 'baseline';
|
|
17
|
+
alignContent?: 'stretch' | 'center' | 'flex-start' | 'flex-end' | 'space-between' | 'space-around';
|
|
18
|
+
|
|
19
|
+
// Gap utilities
|
|
20
|
+
gap?: number;
|
|
21
|
+
gapX?: number;
|
|
22
|
+
gapY?: number;
|
|
23
|
+
gapUnit?: 'rem' | 'px' | 'em';
|
|
24
|
+
|
|
25
|
+
// Flex item behavior
|
|
26
|
+
grow?: number;
|
|
27
|
+
shrink?: number;
|
|
28
|
+
basis?: string;
|
|
29
|
+
flex?: string;
|
|
30
|
+
|
|
31
|
+
// Responsive helpers
|
|
32
|
+
responsiveSmall?: boolean;
|
|
33
|
+
responsiveMedium?: boolean;
|
|
34
|
+
responsiveLarge?: boolean;
|
|
35
|
+
|
|
36
|
+
// Stretch helpers
|
|
37
|
+
fullWidth?: boolean;
|
|
38
|
+
fullHeight?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default function Flex({
|
|
42
|
+
as: Component = 'div',
|
|
43
|
+
className,
|
|
44
|
+
funcss,
|
|
45
|
+
id,
|
|
46
|
+
children,
|
|
47
|
+
style = {},
|
|
48
|
+
|
|
49
|
+
// Flex container
|
|
50
|
+
direction = 'row',
|
|
51
|
+
wrap = 'wrap',
|
|
52
|
+
justify,
|
|
53
|
+
alignItems,
|
|
54
|
+
alignContent,
|
|
55
|
+
|
|
56
|
+
// Gap
|
|
57
|
+
gap,
|
|
58
|
+
gapX,
|
|
59
|
+
gapY,
|
|
60
|
+
gapUnit = 'rem',
|
|
61
|
+
|
|
62
|
+
// Flex item
|
|
63
|
+
grow,
|
|
64
|
+
shrink,
|
|
65
|
+
basis,
|
|
66
|
+
flex,
|
|
67
|
+
|
|
68
|
+
// Responsive
|
|
69
|
+
responsiveSmall,
|
|
70
|
+
responsiveMedium,
|
|
71
|
+
responsiveLarge,
|
|
72
|
+
|
|
73
|
+
// Size
|
|
74
|
+
fullWidth,
|
|
75
|
+
fullHeight,
|
|
76
|
+
...rest
|
|
77
|
+
}: FlexProps) {
|
|
78
|
+
const responsiveClasses = `
|
|
79
|
+
${responsiveSmall ? 'responsiveSmall' : ''}
|
|
80
|
+
${responsiveMedium ? 'responsiveMedium' : ''}
|
|
81
|
+
${responsiveLarge ? 'responsiveLarge' : ''}
|
|
82
|
+
`.trim();
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<Component
|
|
86
|
+
id={id}
|
|
87
|
+
className={`
|
|
88
|
+
${className || ''}
|
|
89
|
+
${funcss || ''}
|
|
90
|
+
${responsiveClasses}
|
|
91
|
+
`.trim()}
|
|
92
|
+
style={{
|
|
93
|
+
display: 'flex',
|
|
94
|
+
flexDirection: direction,
|
|
95
|
+
flexWrap: wrap,
|
|
96
|
+
justifyContent: justify,
|
|
97
|
+
alignItems: alignItems,
|
|
98
|
+
alignContent: alignContent,
|
|
99
|
+
gap: gap ? `${gap}${gapUnit}` : undefined,
|
|
100
|
+
columnGap: gapX ? `${gapX}${gapUnit}` : undefined,
|
|
101
|
+
rowGap: gapY ? `${gapY}${gapUnit}` : undefined,
|
|
102
|
+
flexGrow: grow,
|
|
103
|
+
flexShrink: shrink,
|
|
104
|
+
flexBasis: basis,
|
|
105
|
+
flex: flex,
|
|
106
|
+
width: fullWidth ? '100%' : undefined,
|
|
107
|
+
height: fullHeight ? '100%' : undefined,
|
|
108
|
+
...style,
|
|
109
|
+
}}
|
|
110
|
+
{...rest}
|
|
111
|
+
>
|
|
112
|
+
{children}
|
|
113
|
+
</Component>
|
|
114
|
+
);
|
|
115
|
+
}
|
package/ui/video/Video.js
CHANGED
|
@@ -83,12 +83,30 @@ function Video(_a) {
|
|
|
83
83
|
var _g = (0, react_1.useState)(false), showVolume = _g[0], setShowVolume = _g[1];
|
|
84
84
|
var _h = (0, react_1.useState)(true), isMouseMoving = _h[0], setIsMouseMoving = _h[1];
|
|
85
85
|
var _j = (0, react_1.useState)(false), hasStarted = _j[0], setHasStarted = _j[1];
|
|
86
|
+
var handleVideoEnd = function () {
|
|
87
|
+
setIsPlaying(false);
|
|
88
|
+
setCurrentTime(duration); // optional
|
|
89
|
+
};
|
|
90
|
+
(0, react_1.useEffect)(function () {
|
|
91
|
+
var video = videoRef.current;
|
|
92
|
+
if (!video)
|
|
93
|
+
return;
|
|
94
|
+
video.addEventListener('ended', handleVideoEnd);
|
|
95
|
+
return function () {
|
|
96
|
+
video.removeEventListener('ended', handleVideoEnd);
|
|
97
|
+
};
|
|
98
|
+
}, [duration]);
|
|
86
99
|
var playVideo = function () {
|
|
87
100
|
var video = videoRef.current;
|
|
88
|
-
if (video
|
|
89
|
-
video
|
|
90
|
-
|
|
91
|
-
|
|
101
|
+
if (video) {
|
|
102
|
+
// ✅ if video ended, reset it to start
|
|
103
|
+
if (video.currentTime === video.duration) {
|
|
104
|
+
video.currentTime = 0;
|
|
105
|
+
}
|
|
106
|
+
video.play().then(function () {
|
|
107
|
+
setIsPlaying(true);
|
|
108
|
+
setHasStarted(true);
|
|
109
|
+
}).catch(function () { });
|
|
92
110
|
}
|
|
93
111
|
};
|
|
94
112
|
var pauseVideo = function () {
|
|
@@ -141,10 +159,24 @@ function Video(_a) {
|
|
|
141
159
|
setDuration(video.duration || 0);
|
|
142
160
|
onDuration === null || onDuration === void 0 ? void 0 : onDuration(video.duration);
|
|
143
161
|
if (autoPlay) {
|
|
144
|
-
|
|
162
|
+
video.play().then(function () {
|
|
163
|
+
setIsPlaying(true); // ✅ update UI state
|
|
164
|
+
setHasStarted(true);
|
|
165
|
+
}).catch(function () { });
|
|
145
166
|
}
|
|
146
167
|
}
|
|
147
168
|
};
|
|
169
|
+
(0, react_1.useEffect)(function () {
|
|
170
|
+
if (autoPlay && videoRef.current) {
|
|
171
|
+
videoRef.current.muted = true; // ✅ important for autoplay to work
|
|
172
|
+
videoRef.current.play().then(function () {
|
|
173
|
+
setIsPlaying(true);
|
|
174
|
+
setHasStarted(true);
|
|
175
|
+
}).catch(function (err) {
|
|
176
|
+
console.warn('Autoplay failed', err);
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}, [autoPlay]);
|
|
148
180
|
var handleProgressBarChange = function (e) {
|
|
149
181
|
var newTime = parseFloat(e.target.value);
|
|
150
182
|
if (videoRef.current) {
|
|
@@ -153,6 +185,9 @@ function Video(_a) {
|
|
|
153
185
|
setCurrentTime(newTime);
|
|
154
186
|
};
|
|
155
187
|
var handleVolumeChange = function (e) {
|
|
188
|
+
if (videoRef.current) {
|
|
189
|
+
videoRef.current.muted = false;
|
|
190
|
+
}
|
|
156
191
|
var newVolume = parseFloat(e.target.value);
|
|
157
192
|
setVolume(newVolume);
|
|
158
193
|
if (videoRef.current)
|
|
@@ -217,18 +252,28 @@ function Video(_a) {
|
|
|
217
252
|
pauseVideo();
|
|
218
253
|
};
|
|
219
254
|
}, []);
|
|
255
|
+
(0, react_1.useEffect)(function () {
|
|
256
|
+
var video = videoRef.current;
|
|
257
|
+
if (!video)
|
|
258
|
+
return;
|
|
259
|
+
var onEnd = function () {
|
|
260
|
+
setIsPlaying(false);
|
|
261
|
+
};
|
|
262
|
+
video.addEventListener('ended', onEnd);
|
|
263
|
+
return function () { return video.removeEventListener('ended', onEnd); };
|
|
264
|
+
}, []);
|
|
220
265
|
return (react_1.default.createElement("div", { ref: containerRef, className: "video_container fit ".concat(className || ''), id: "fun_video_container" },
|
|
221
266
|
poster && !hasStarted && !isPlaying && (react_1.default.createElement("div", { style: { backgroundImage: "url(".concat(poster, ")") }, className: "video_poster" })),
|
|
222
267
|
react_1.default.createElement("video", __assign({ ref: videoRef, preload: "auto", src: src, className: "video_player fit min-w-200", onClick: handlePlayPauseToggle, onLoadedMetadata: handleLoadedMetadata, playsInline: true, controls: false }, rest)),
|
|
223
268
|
react_1.default.createElement("div", { className: "video_controls ".concat(isMouseMoving ? 'show_controls' : 'hide_controls') },
|
|
224
269
|
react_1.default.createElement("div", { className: "w-80-p center animated fade-in" },
|
|
225
|
-
react_1.default.createElement(RowFlex_1.default, { gap: 0.3, funcss:
|
|
270
|
+
react_1.default.createElement(RowFlex_1.default, { gap: 0.3, funcss: 'mb-2', alignItems: "center" },
|
|
226
271
|
react_1.default.createElement("div", { className: 'video_time' },
|
|
227
272
|
react_1.default.createElement(Text_1.default, { text: (0, videoFunctions_1.formatTime)(currentTime), funcss: 'm-0', size: "sm" })),
|
|
228
273
|
react_1.default.createElement("div", { className: "col width-100-p" },
|
|
229
|
-
react_1.default.createElement("input", { type: "range", min: 0, max: duration, value: currentTime, onChange: handleProgressBarChange, className: "width-100-p styled-slider m-0", "aria-label": "Progress bar", style: { '--progress': "".concat((currentTime / duration) * 100) } })),
|
|
274
|
+
react_1.default.createElement("input", { type: "range", min: 0, max: duration, value: currentTime, onChange: handleProgressBarChange, className: "width-100-p videoSlider styled-slider m-0", "aria-label": "Progress bar", style: { '--progress': "".concat((currentTime / duration) * 100) } })),
|
|
230
275
|
react_1.default.createElement("div", { className: "video_time" },
|
|
231
|
-
react_1.default.createElement(Text_1.default, { text: "
|
|
276
|
+
react_1.default.createElement(Text_1.default, { text: "".concat((0, videoFunctions_1.formatTime)(duration - currentTime)), funcss: 'm-0', size: "sm" })))),
|
|
232
277
|
react_1.default.createElement("div", { className: "center-play-icon animated fade-in", onClick: handlePlayPauseToggle },
|
|
233
278
|
react_1.default.createElement("div", { className: 'play-button' }, isPlaying ? react_1.default.createElement(pi_1.PiPause, { size: 30 }) : react_1.default.createElement(pi_1.PiPlay, { size: 30 }))),
|
|
234
279
|
react_1.default.createElement(RowFlex_1.default, { funcss: 'animated slide-up', gap: 1, justify: "center" },
|
package/ui/video/Video.tsx
CHANGED
|
@@ -43,22 +43,48 @@ export default function Video({ src, poster, onDuration, isPause, className, aut
|
|
|
43
43
|
const [isMouseMoving, setIsMouseMoving] = useState(true);
|
|
44
44
|
const [hasStarted, setHasStarted] = useState(false);
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
const handleVideoEnd = () => {
|
|
49
|
+
setIsPlaying(false);
|
|
50
|
+
setCurrentTime(duration); // optional
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
const video = videoRef.current;
|
|
55
|
+
if (!video) return;
|
|
56
|
+
|
|
57
|
+
video.addEventListener('ended', handleVideoEnd);
|
|
58
|
+
return () => {
|
|
59
|
+
video.removeEventListener('ended', handleVideoEnd);
|
|
53
60
|
};
|
|
61
|
+
}, [duration]);
|
|
54
62
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
|
|
64
|
+
const playVideo = () => {
|
|
65
|
+
const video = videoRef.current;
|
|
66
|
+
if (video) {
|
|
67
|
+
// ✅ if video ended, reset it to start
|
|
68
|
+
if (video.currentTime === video.duration) {
|
|
69
|
+
video.currentTime = 0;
|
|
60
70
|
}
|
|
61
|
-
|
|
71
|
+
|
|
72
|
+
video.play().then(() => {
|
|
73
|
+
setIsPlaying(true);
|
|
74
|
+
setHasStarted(true);
|
|
75
|
+
}).catch(() => {});
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const pauseVideo = () => {
|
|
80
|
+
const video = videoRef.current;
|
|
81
|
+
if (video && !video.paused) {
|
|
82
|
+
video.pause();
|
|
83
|
+
setIsPlaying(false);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
|
|
62
88
|
|
|
63
89
|
useEffect(() => {
|
|
64
90
|
const handleKey = (e: KeyboardEvent) => handleKeyDown(e, isPlaying, playVideo, pauseVideo);
|
|
@@ -98,16 +124,33 @@ export default function Video({ src, poster, onDuration, isPause, className, aut
|
|
|
98
124
|
}
|
|
99
125
|
}, []);
|
|
100
126
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
127
|
+
const handleLoadedMetadata = () => {
|
|
128
|
+
const video = videoRef.current;
|
|
129
|
+
if (video) {
|
|
130
|
+
setDuration(video.duration || 0);
|
|
131
|
+
onDuration?.(video.duration);
|
|
132
|
+
if (autoPlay) {
|
|
133
|
+
video.play().then(() => {
|
|
134
|
+
setIsPlaying(true); // ✅ update UI state
|
|
135
|
+
setHasStarted(true);
|
|
136
|
+
}).catch(() => {});
|
|
109
137
|
}
|
|
110
|
-
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (autoPlay && videoRef.current) {
|
|
144
|
+
videoRef.current.muted = true; // ✅ important for autoplay to work
|
|
145
|
+
videoRef.current.play().then(() => {
|
|
146
|
+
setIsPlaying(true);
|
|
147
|
+
setHasStarted(true);
|
|
148
|
+
}).catch((err) => {
|
|
149
|
+
console.warn('Autoplay failed', err);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}, [autoPlay]);
|
|
153
|
+
|
|
111
154
|
|
|
112
155
|
const handleProgressBarChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
113
156
|
const newTime = parseFloat(e.target.value);
|
|
@@ -118,6 +161,9 @@ export default function Video({ src, poster, onDuration, isPause, className, aut
|
|
|
118
161
|
};
|
|
119
162
|
|
|
120
163
|
const handleVolumeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
164
|
+
if(videoRef.current){
|
|
165
|
+
videoRef.current.muted = false;
|
|
166
|
+
}
|
|
121
167
|
const newVolume = parseFloat(e.target.value);
|
|
122
168
|
setVolume(newVolume);
|
|
123
169
|
if (videoRef.current) videoRef.current.volume = newVolume;
|
|
@@ -185,6 +231,20 @@ export default function Video({ src, poster, onDuration, isPause, className, aut
|
|
|
185
231
|
};
|
|
186
232
|
}, []);
|
|
187
233
|
|
|
234
|
+
|
|
235
|
+
useEffect(() => {
|
|
236
|
+
const video = videoRef.current;
|
|
237
|
+
if (!video) return;
|
|
238
|
+
|
|
239
|
+
const onEnd = () => {
|
|
240
|
+
setIsPlaying(false);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
video.addEventListener('ended', onEnd);
|
|
244
|
+
return () => video.removeEventListener('ended', onEnd);
|
|
245
|
+
}, []);
|
|
246
|
+
|
|
247
|
+
|
|
188
248
|
return (
|
|
189
249
|
<div ref={containerRef} className={`video_container fit ${className || ''}`} id="fun_video_container">
|
|
190
250
|
{poster && !hasStarted && !isPlaying && (
|
|
@@ -207,7 +267,7 @@ export default function Video({ src, poster, onDuration, isPause, className, aut
|
|
|
207
267
|
|
|
208
268
|
<div className={`video_controls ${isMouseMoving ? 'show_controls' : 'hide_controls'}`}>
|
|
209
269
|
<div className="w-80-p center animated fade-in">
|
|
210
|
-
<RowFlex gap={0.3} funcss=
|
|
270
|
+
<RowFlex gap={0.3} funcss='mb-2' alignItems="center">
|
|
211
271
|
<div className='video_time'>
|
|
212
272
|
<Text text={formatTime(currentTime)} funcss='m-0' size="sm" />
|
|
213
273
|
</div>
|
|
@@ -218,13 +278,13 @@ export default function Video({ src, poster, onDuration, isPause, className, aut
|
|
|
218
278
|
max={duration}
|
|
219
279
|
value={currentTime}
|
|
220
280
|
onChange={handleProgressBarChange}
|
|
221
|
-
className="width-100-p styled-slider m-0"
|
|
281
|
+
className="width-100-p videoSlider styled-slider m-0"
|
|
222
282
|
aria-label="Progress bar"
|
|
223
283
|
style={{ '--progress': `${(currentTime / duration) * 100}` } as React.CSSProperties}
|
|
224
284
|
/>
|
|
225
285
|
</div>
|
|
226
286
|
<div className="video_time">
|
|
227
|
-
<Text text={
|
|
287
|
+
<Text text={`${formatTime(duration - currentTime)}`} funcss='m-0' size="sm" />
|
|
228
288
|
</div>
|
|
229
289
|
</RowFlex>
|
|
230
290
|
</div>
|