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.
@@ -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 {};
@@ -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
+ }
@@ -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 && video.paused) {
89
- video.play().catch(function () { });
90
- setIsPlaying(true);
91
- setHasStarted(true);
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
- playVideo();
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, funcss: "padding-5", alignItems: "center" },
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: "-".concat((0, videoFunctions_1.formatTime)(duration - currentTime)), funcss: 'm-0', size: "sm" })))),
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" },
@@ -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
- const playVideo = () => {
47
- const video = videoRef.current;
48
- if (video && video.paused) {
49
- video.play().catch(() => {});
50
- setIsPlaying(true);
51
- setHasStarted(true);
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
- const pauseVideo = () => {
56
- const video = videoRef.current;
57
- if (video && !video.paused) {
58
- video.pause();
59
- setIsPlaying(false);
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
- const handleLoadedMetadata = () => {
102
- const video = videoRef.current;
103
- if (video) {
104
- setDuration(video.duration || 0);
105
- onDuration?.(video.duration);
106
- if (autoPlay) {
107
- playVideo();
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} funcss="padding-5" alignItems="center">
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={`-${formatTime(duration - currentTime)}`} funcss='m-0' size="sm" />
284
+ <Text text={`${formatTime(duration - currentTime)}`} funcss='m-0' size="sm" />
228
285
  </div>
229
286
  </RowFlex>
230
287
  </div>