funuicss 2.7.4 → 2.7.5
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 +50 -8
- package/ui/video/Video.tsx +82 -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) {
|
|
@@ -217,18 +249,28 @@ function Video(_a) {
|
|
|
217
249
|
pauseVideo();
|
|
218
250
|
};
|
|
219
251
|
}, []);
|
|
252
|
+
(0, react_1.useEffect)(function () {
|
|
253
|
+
var video = videoRef.current;
|
|
254
|
+
if (!video)
|
|
255
|
+
return;
|
|
256
|
+
var onEnd = function () {
|
|
257
|
+
setIsPlaying(false);
|
|
258
|
+
};
|
|
259
|
+
video.addEventListener('ended', onEnd);
|
|
260
|
+
return function () { return video.removeEventListener('ended', onEnd); };
|
|
261
|
+
}, []);
|
|
220
262
|
return (react_1.default.createElement("div", { ref: containerRef, className: "video_container fit ".concat(className || ''), id: "fun_video_container" },
|
|
221
263
|
poster && !hasStarted && !isPlaying && (react_1.default.createElement("div", { style: { backgroundImage: "url(".concat(poster, ")") }, className: "video_poster" })),
|
|
222
264
|
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
265
|
react_1.default.createElement("div", { className: "video_controls ".concat(isMouseMoving ? 'show_controls' : 'hide_controls') },
|
|
224
266
|
react_1.default.createElement("div", { className: "w-80-p center animated fade-in" },
|
|
225
|
-
react_1.default.createElement(RowFlex_1.default, { gap: 0.3,
|
|
267
|
+
react_1.default.createElement(RowFlex_1.default, { gap: 0.3, alignItems: "center" },
|
|
226
268
|
react_1.default.createElement("div", { className: 'video_time' },
|
|
227
269
|
react_1.default.createElement(Text_1.default, { text: (0, videoFunctions_1.formatTime)(currentTime), funcss: 'm-0', size: "sm" })),
|
|
228
270
|
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) } })),
|
|
271
|
+
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
272
|
react_1.default.createElement("div", { className: "video_time" },
|
|
231
|
-
react_1.default.createElement(Text_1.default, { text: "
|
|
273
|
+
react_1.default.createElement(Text_1.default, { text: "".concat((0, videoFunctions_1.formatTime)(duration - currentTime)), funcss: 'm-0', size: "sm" })))),
|
|
232
274
|
react_1.default.createElement("div", { className: "center-play-icon animated fade-in", onClick: handlePlayPauseToggle },
|
|
233
275
|
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
276
|
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);
|
|
@@ -185,6 +228,20 @@ export default function Video({ src, poster, onDuration, isPause, className, aut
|
|
|
185
228
|
};
|
|
186
229
|
}, []);
|
|
187
230
|
|
|
231
|
+
|
|
232
|
+
useEffect(() => {
|
|
233
|
+
const video = videoRef.current;
|
|
234
|
+
if (!video) return;
|
|
235
|
+
|
|
236
|
+
const onEnd = () => {
|
|
237
|
+
setIsPlaying(false);
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
video.addEventListener('ended', onEnd);
|
|
241
|
+
return () => video.removeEventListener('ended', onEnd);
|
|
242
|
+
}, []);
|
|
243
|
+
|
|
244
|
+
|
|
188
245
|
return (
|
|
189
246
|
<div ref={containerRef} className={`video_container fit ${className || ''}`} id="fun_video_container">
|
|
190
247
|
{poster && !hasStarted && !isPlaying && (
|
|
@@ -207,7 +264,7 @@ export default function Video({ src, poster, onDuration, isPause, className, aut
|
|
|
207
264
|
|
|
208
265
|
<div className={`video_controls ${isMouseMoving ? 'show_controls' : 'hide_controls'}`}>
|
|
209
266
|
<div className="w-80-p center animated fade-in">
|
|
210
|
-
<RowFlex gap={0.3}
|
|
267
|
+
<RowFlex gap={0.3} alignItems="center">
|
|
211
268
|
<div className='video_time'>
|
|
212
269
|
<Text text={formatTime(currentTime)} funcss='m-0' size="sm" />
|
|
213
270
|
</div>
|
|
@@ -218,13 +275,13 @@ export default function Video({ src, poster, onDuration, isPause, className, aut
|
|
|
218
275
|
max={duration}
|
|
219
276
|
value={currentTime}
|
|
220
277
|
onChange={handleProgressBarChange}
|
|
221
|
-
className="width-100-p styled-slider m-0"
|
|
278
|
+
className="width-100-p videoSlider styled-slider m-0"
|
|
222
279
|
aria-label="Progress bar"
|
|
223
280
|
style={{ '--progress': `${(currentTime / duration) * 100}` } as React.CSSProperties}
|
|
224
281
|
/>
|
|
225
282
|
</div>
|
|
226
283
|
<div className="video_time">
|
|
227
|
-
<Text text={
|
|
284
|
+
<Text text={`${formatTime(duration - currentTime)}`} funcss='m-0' size="sm" />
|
|
228
285
|
</div>
|
|
229
286
|
</RowFlex>
|
|
230
287
|
</div>
|