@creopse/bridge 0.0.1 → 0.1.0
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/dist/react/components/AsyncImg/AsyncImg.d.ts +12 -0
- package/dist/react/components/AsyncImg/AsyncImg.js +20 -0
- package/dist/react/components/AsyncImg/index.d.ts +1 -0
- package/dist/react/components/AsyncImg/index.js +1 -0
- package/dist/react/components/CustomTransition/CustomTransition.d.ts +12 -0
- package/dist/react/components/CustomTransition/CustomTransition.js +48 -0
- package/dist/react/components/CustomTransition/index.d.ts +1 -0
- package/dist/react/components/CustomTransition/index.js +1 -0
- package/dist/react/components/Image/Image.d.ts +14 -0
- package/dist/react/components/Image/Image.js +20 -0
- package/dist/react/components/Image/index.d.ts +1 -0
- package/dist/react/components/Image/index.js +1 -0
- package/dist/react/components/MountedTeleport/MountedTeleport.d.ts +8 -0
- package/dist/react/components/MountedTeleport/MountedTeleport.js +43 -0
- package/dist/react/components/MountedTeleport/index.d.ts +1 -0
- package/dist/react/components/MountedTeleport/index.js +1 -0
- package/dist/react/components/PageLayout/PageLayout.d.ts +20 -0
- package/dist/react/components/PageLayout/PageLayout.js +30 -0
- package/dist/react/components/PageLayout/index.d.ts +1 -0
- package/dist/react/components/PageLayout/index.js +1 -0
- package/dist/react/components/ReadMore/ReadMore.d.ts +12 -0
- package/dist/react/components/ReadMore/ReadMore.js +33 -0
- package/dist/react/components/ReadMore/index.d.ts +1 -0
- package/dist/react/components/ReadMore/index.js +1 -0
- package/dist/react/components/StickyBottom/StickyBottom.d.ts +9 -0
- package/dist/react/components/StickyBottom/StickyBottom.js +57 -0
- package/dist/react/components/StickyBottom/index.d.ts +1 -0
- package/dist/react/components/StickyBottom/index.js +1 -0
- package/dist/react/components/StickyTop/StickyTop.d.ts +9 -0
- package/dist/react/components/StickyTop/StickyTop.js +56 -0
- package/dist/react/components/StickyTop/index.d.ts +1 -0
- package/dist/react/components/StickyTop/index.js +1 -0
- package/dist/react/components/index.d.ts +8 -0
- package/dist/react/components/index.js +8 -0
- package/dist/react/hooks/access.d.ts +25 -0
- package/dist/react/hooks/access.js +41 -0
- package/dist/react/hooks/core-bridge.d.ts +13 -0
- package/dist/react/hooks/core-bridge.js +19 -0
- package/dist/react/hooks/responsive.d.ts +15 -0
- package/dist/react/hooks/responsive.js +42 -0
- package/dist/react/hooks/user-preference.d.ts +4 -0
- package/dist/react/hooks/user-preference.js +17 -0
- package/dist/vite/react.js +1 -2
- package/dist/vite/vue.js +1 -2
- package/dist/vue/composables/access.d.ts +25 -0
- package/dist/vue/composables/access.js +58 -0
- package/dist/vue/composables/core-bridge.d.ts +12 -0
- package/dist/vue/composables/core-bridge.js +15 -0
- package/dist/vue/composables/responsive.d.ts +15 -0
- package/dist/vue/composables/responsive.js +35 -0
- package/dist/vue/composables/user-preference.d.ts +12 -0
- package/dist/vue/composables/user-preference.js +14 -0
- package/package.json +48 -3
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React, { type CSSProperties } from 'react';
|
|
2
|
+
export interface Props {
|
|
3
|
+
load: () => Promise<string>;
|
|
4
|
+
alt?: string;
|
|
5
|
+
width?: string | number;
|
|
6
|
+
height?: string | number;
|
|
7
|
+
title?: string;
|
|
8
|
+
loading?: 'lazy' | 'eager';
|
|
9
|
+
style?: CSSProperties;
|
|
10
|
+
}
|
|
11
|
+
declare const AsyncImg: React.FC<Props>;
|
|
12
|
+
export default AsyncImg;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
const AsyncImg = ({ load, alt, width, height, title, loading, style }) => {
|
|
4
|
+
const [url, setUrl] = useState('');
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
const init = async () => {
|
|
7
|
+
try {
|
|
8
|
+
const loadedUrl = await load();
|
|
9
|
+
setUrl(loadedUrl);
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
console.error('Failed to load image:', error);
|
|
13
|
+
// Optionally set a fallback image or error state
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
init();
|
|
17
|
+
}, [load]);
|
|
18
|
+
return (_jsx("img", { src: url, alt: alt, title: title, style: style, width: width, height: height, loading: loading }));
|
|
19
|
+
};
|
|
20
|
+
export default AsyncImg;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as AsyncImg, type Props as AsyncImgProps } from './AsyncImg';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as AsyncImg } from './AsyncImg';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
type Animation = 'fade' | 'slide-fade' | 'bounce';
|
|
3
|
+
type Mode = 'wait' | 'sync' | 'popLayout' | undefined;
|
|
4
|
+
export interface Props {
|
|
5
|
+
name?: Animation;
|
|
6
|
+
mode?: Mode;
|
|
7
|
+
appear?: boolean;
|
|
8
|
+
children?: React.ReactNode;
|
|
9
|
+
contentKey?: string | number;
|
|
10
|
+
}
|
|
11
|
+
declare const CustomTransition: React.FC<Props>;
|
|
12
|
+
export default CustomTransition;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { motion, AnimatePresence } from 'framer-motion';
|
|
3
|
+
const CustomTransition = ({ name = 'fade', mode = 'wait', appear = false, children, contentKey = 'default', }) => {
|
|
4
|
+
const animations = {
|
|
5
|
+
fade: {
|
|
6
|
+
initial: { opacity: 0 },
|
|
7
|
+
animate: { opacity: 1 },
|
|
8
|
+
exit: { opacity: 0 },
|
|
9
|
+
transition: { duration: 0.5, ease: 'easeInOut' },
|
|
10
|
+
},
|
|
11
|
+
'slide-fade': {
|
|
12
|
+
initial: { opacity: 0, x: 20 },
|
|
13
|
+
animate: { opacity: 1, x: 0 },
|
|
14
|
+
exit: { opacity: 0, x: 20 },
|
|
15
|
+
transition: {
|
|
16
|
+
duration: 0.3,
|
|
17
|
+
ease: 'easeOut'
|
|
18
|
+
},
|
|
19
|
+
// Separate exit transition
|
|
20
|
+
exitTransition: {
|
|
21
|
+
duration: 0.8,
|
|
22
|
+
ease: [0.36, 0.66, 0.04, 1]
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
bounce: {
|
|
26
|
+
initial: { scale: 0 },
|
|
27
|
+
animate: { scale: 1 },
|
|
28
|
+
exit: { scale: 0 },
|
|
29
|
+
transition: {
|
|
30
|
+
duration: 0.5,
|
|
31
|
+
type: 'spring',
|
|
32
|
+
bounce: 0.4,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
const animation = animations[name];
|
|
37
|
+
// For AnimatePresence modes
|
|
38
|
+
if (mode) {
|
|
39
|
+
return (_jsx(AnimatePresence, { mode: mode, initial: appear, children: _jsx(motion.div, { initial: animation.initial, animate: animation.animate, exit: animation.exit, transition: animation.transition, ...(name === 'slide-fade' && {
|
|
40
|
+
exit: animation.exit,
|
|
41
|
+
// @ts-expect-error - exitTransition is a custom property we added
|
|
42
|
+
transition: { ...animation.transition, exit: animation.exitTransition }
|
|
43
|
+
}), children: children }, contentKey) }));
|
|
44
|
+
}
|
|
45
|
+
// For simple animations without AnimatePresence
|
|
46
|
+
return (_jsx(motion.div, { initial: appear ? animation.initial : false, animate: animation.animate, transition: animation.transition, children: children }));
|
|
47
|
+
};
|
|
48
|
+
export default CustomTransition;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as CustomTransition, type Props as CustomTransitionProps, } from './CustomTransition';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as CustomTransition, } from './CustomTransition';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface Props {
|
|
3
|
+
src: string;
|
|
4
|
+
alt?: string;
|
|
5
|
+
width?: string | number;
|
|
6
|
+
height?: string | number;
|
|
7
|
+
title?: string;
|
|
8
|
+
loading?: 'lazy' | 'eager';
|
|
9
|
+
style?: React.CSSProperties;
|
|
10
|
+
size?: 'small' | 'medium' | 'large' | 'original';
|
|
11
|
+
sync?: boolean;
|
|
12
|
+
}
|
|
13
|
+
declare const Image: React.FC<Props>;
|
|
14
|
+
export default Image;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { AsyncImg } from '../AsyncImg';
|
|
3
|
+
import { useCoreBridge } from '../../hooks/core-bridge';
|
|
4
|
+
const Image = (props) => {
|
|
5
|
+
const { src, alt = '', width, height, title, loading, style, size = 'original', sync = false } = props;
|
|
6
|
+
const { helpers: { getImage }, } = useCoreBridge();
|
|
7
|
+
const filteredProps = {
|
|
8
|
+
alt,
|
|
9
|
+
width,
|
|
10
|
+
height,
|
|
11
|
+
title,
|
|
12
|
+
loading,
|
|
13
|
+
style,
|
|
14
|
+
};
|
|
15
|
+
if (sync) {
|
|
16
|
+
return _jsx("img", { src: src, ...filteredProps });
|
|
17
|
+
}
|
|
18
|
+
return _jsx(AsyncImg, { load: () => getImage(src, size), ...filteredProps });
|
|
19
|
+
};
|
|
20
|
+
export default Image;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Image, type Props as ImageProps } from './Image';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Image } from './Image';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { createPortal } from 'react-dom';
|
|
3
|
+
const MountedTeleport = ({ to, children, disabled = false, }) => {
|
|
4
|
+
const [isMounted, setIsMounted] = useState(false);
|
|
5
|
+
const [targetElement, setTargetElement] = useState(null);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
setIsMounted(true);
|
|
8
|
+
// Only cleanup on unmount, not on every effect run
|
|
9
|
+
return () => {
|
|
10
|
+
setIsMounted(false);
|
|
11
|
+
};
|
|
12
|
+
}, []);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!isMounted || disabled) {
|
|
15
|
+
setTargetElement(null);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
let target = null;
|
|
19
|
+
if (typeof to === 'string') {
|
|
20
|
+
target = document.querySelector(to);
|
|
21
|
+
if (!target) {
|
|
22
|
+
console.warn(`MountedTeleport: Target element not found for selector "${to}"`);
|
|
23
|
+
setTargetElement(null);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
else if (to instanceof HTMLElement) {
|
|
28
|
+
target = to;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
console.warn('MountedTeleport: Invalid target type. Expected string selector or HTMLElement.');
|
|
32
|
+
setTargetElement(null);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
setTargetElement(target);
|
|
36
|
+
}, [to, isMounted, disabled]);
|
|
37
|
+
// Don't render if not mounted, disabled, or no valid target
|
|
38
|
+
if (!isMounted || disabled || !targetElement) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return createPortal(children, targetElement);
|
|
42
|
+
};
|
|
43
|
+
export default MountedTeleport;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as MountedTeleport, type Props as MountedTeleportProps, } from './MountedTeleport';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as MountedTeleport, } from './MountedTeleport';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface Props {
|
|
3
|
+
title?: string | null;
|
|
4
|
+
displayTitle?: boolean;
|
|
5
|
+
onBack?: (() => void) | null;
|
|
6
|
+
header?: ReactNode;
|
|
7
|
+
headerExtra?: ReactNode;
|
|
8
|
+
titleSlot?: ReactNode;
|
|
9
|
+
children?: ReactNode;
|
|
10
|
+
}
|
|
11
|
+
export interface PageHeaderProps {
|
|
12
|
+
title?: ReactNode;
|
|
13
|
+
extra?: ReactNode;
|
|
14
|
+
header?: ReactNode;
|
|
15
|
+
onBack?: () => void;
|
|
16
|
+
children?: ReactNode;
|
|
17
|
+
}
|
|
18
|
+
export declare const PageHeader: ({ title, extra, header, onBack, children, }: PageHeaderProps) => import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export declare const PageLayout: ({ title, displayTitle, onBack, header, headerExtra, titleSlot, children, }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export default PageLayout;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Icon } from '@iconify/react';
|
|
3
|
+
import { useResponsive } from '../../hooks/responsive';
|
|
4
|
+
export const PageHeader = ({ title, extra, header, onBack, children, }) => {
|
|
5
|
+
return (_jsxs("div", { className: "n-page-header", children: [header && (_jsx("div", { className: "n-page-header__header", children: header })), _jsxs("div", { className: "n-page-header__content-header", children: [_jsx("div", { className: "n-page-header__back", onClick: onBack, role: "button", "aria-label": "Go back", children: _jsx("div", { className: "n-page-header-back", children: _jsx(Icon, { icon: "ic:baseline-arrow-back-ios" }) }) }), title && (_jsx("div", { className: "n-page-header__title", children: title })), extra && (_jsx("div", { className: "n-page-header__extra", children: extra }))] }), children && (_jsx("div", { className: "n-page-header__content", children: children }))] }));
|
|
6
|
+
};
|
|
7
|
+
// ——— PageLayout ——————————————————————————————————————————————————————————————
|
|
8
|
+
export const PageLayout = ({ title = null, displayTitle = true, onBack = null, header, headerExtra, titleSlot, children, }) => {
|
|
9
|
+
const { isMobile } = useResponsive();
|
|
10
|
+
const handleBack = () => {
|
|
11
|
+
if (onBack) {
|
|
12
|
+
onBack();
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
window.__creopse__.router.back();
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
return (_jsxs("div", { children: [_jsx(PageHeader, { header: header, title: displayTitle
|
|
19
|
+
? titleSlot ?? (_jsx("span", { style: {
|
|
20
|
+
display: '-webkit-box',
|
|
21
|
+
WebkitLineClamp: 2,
|
|
22
|
+
WebkitBoxOrient: 'vertical',
|
|
23
|
+
overflow: 'hidden',
|
|
24
|
+
wordBreak: 'break-word',
|
|
25
|
+
}, children: title || '' }))
|
|
26
|
+
: undefined, extra: headerExtra ? (_jsx("div", { className: isMobile
|
|
27
|
+
? 'flex flex-col items-end gap-2'
|
|
28
|
+
: 'flex items-center gap-2', children: headerExtra })) : undefined, onBack: handleBack }), children] }));
|
|
29
|
+
};
|
|
30
|
+
export default PageLayout;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as PageLayout, type Props as PageLayoutProps, PageHeader, type PageHeaderProps, } from './PageLayout';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as PageLayout, PageHeader, } from './PageLayout';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface Props {
|
|
3
|
+
moreStr?: string;
|
|
4
|
+
lessStr?: string;
|
|
5
|
+
text: string;
|
|
6
|
+
link?: string;
|
|
7
|
+
maxChars?: number;
|
|
8
|
+
className?: string;
|
|
9
|
+
linkClassName?: string;
|
|
10
|
+
}
|
|
11
|
+
declare const ReadMore: React.FC<Props>;
|
|
12
|
+
export default ReadMore;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
const ReadMore = ({ moreStr = 'Read more', lessStr = 'Read less', link = '#', maxChars = 100, text, className = '', linkClassName = '', }) => {
|
|
4
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
5
|
+
const formattedText = () => {
|
|
6
|
+
if (!isExpanded && text.length > maxChars) {
|
|
7
|
+
return text.substring(0, maxChars).trim() + '...';
|
|
8
|
+
}
|
|
9
|
+
return text;
|
|
10
|
+
};
|
|
11
|
+
const handleToggle = (event) => {
|
|
12
|
+
// Always prevent default for toggle functionality
|
|
13
|
+
event.preventDefault();
|
|
14
|
+
setIsExpanded(!isExpanded);
|
|
15
|
+
};
|
|
16
|
+
// Don't render anything if text is empty
|
|
17
|
+
if (!text) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const shouldShowToggle = text.length > maxChars;
|
|
21
|
+
return (_jsxs("div", { className: className, children: [_jsx("span", { children: formattedText() }), shouldShowToggle && (_jsxs(_Fragment, { children: [' ', !isExpanded ? (_jsx("a", { href: link, onClick: handleToggle, className: linkClassName, role: "button", tabIndex: 0, onKeyDown: (e) => {
|
|
22
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
23
|
+
e.preventDefault();
|
|
24
|
+
setIsExpanded(true);
|
|
25
|
+
}
|
|
26
|
+
}, children: moreStr })) : (lessStr && (_jsx("a", { href: link, onClick: handleToggle, className: linkClassName, role: "button", tabIndex: 0, onKeyDown: (e) => {
|
|
27
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
28
|
+
e.preventDefault();
|
|
29
|
+
setIsExpanded(false);
|
|
30
|
+
}
|
|
31
|
+
}, children: lessStr })))] }))] }));
|
|
32
|
+
};
|
|
33
|
+
export default ReadMore;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as ReadMore, type Props as ReadMoreProps } from './ReadMore';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as ReadMore } from './ReadMore';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useRef } from 'react';
|
|
3
|
+
const StickyBottom = ({ bottom = 0, zIndex = 1, className = '', children = _jsx("div", { children: "Sticky Bottom" }), }) => {
|
|
4
|
+
const elRef = useRef(null);
|
|
5
|
+
const [isSticky, setIsSticky] = useState(false);
|
|
6
|
+
const [width, setWidth] = useState('auto');
|
|
7
|
+
const [height, setHeight] = useState('auto');
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const handleScroll = () => {
|
|
10
|
+
if (!elRef.current)
|
|
11
|
+
return;
|
|
12
|
+
const rect = elRef.current.getBoundingClientRect();
|
|
13
|
+
const viewportHeight = window.innerHeight;
|
|
14
|
+
// Element should be sticky when it would go below the specified bottom position
|
|
15
|
+
const shouldStick = rect.bottom <= viewportHeight - bottom;
|
|
16
|
+
if (shouldStick && !isSticky) {
|
|
17
|
+
setWidth(rect.width + 'px');
|
|
18
|
+
setHeight(rect.height + 'px');
|
|
19
|
+
setIsSticky(true);
|
|
20
|
+
}
|
|
21
|
+
else if (!shouldStick && isSticky) {
|
|
22
|
+
setIsSticky(false);
|
|
23
|
+
setWidth('auto');
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const handleResize = () => {
|
|
27
|
+
if (!isSticky && elRef.current) {
|
|
28
|
+
// Update dimensions when not sticky
|
|
29
|
+
const rect = elRef.current.getBoundingClientRect();
|
|
30
|
+
setHeight(rect.height + 'px');
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
// Initial setup
|
|
34
|
+
if (elRef.current) {
|
|
35
|
+
const rect = elRef.current.getBoundingClientRect();
|
|
36
|
+
setHeight(rect.height + 'px');
|
|
37
|
+
}
|
|
38
|
+
window.addEventListener('scroll', handleScroll);
|
|
39
|
+
window.addEventListener('resize', handleResize);
|
|
40
|
+
// Check initial state
|
|
41
|
+
handleScroll();
|
|
42
|
+
return () => {
|
|
43
|
+
window.removeEventListener('scroll', handleScroll);
|
|
44
|
+
window.removeEventListener('resize', handleResize);
|
|
45
|
+
};
|
|
46
|
+
}, [bottom, isSticky]);
|
|
47
|
+
return (_jsx("div", { ref: elRef, style: {
|
|
48
|
+
height: isSticky ? height : 'auto',
|
|
49
|
+
zIndex
|
|
50
|
+
}, children: _jsx("div", { className: className, style: {
|
|
51
|
+
position: isSticky ? 'fixed' : 'static',
|
|
52
|
+
bottom: isSticky ? `${bottom}px` : 'auto',
|
|
53
|
+
width: isSticky ? width : 'auto',
|
|
54
|
+
zIndex,
|
|
55
|
+
}, children: children }) }));
|
|
56
|
+
};
|
|
57
|
+
export default StickyBottom;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as StickyBottom, type Props as StickyBottomProps, } from './StickyBottom';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as StickyBottom, } from './StickyBottom';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useRef } from 'react';
|
|
3
|
+
const StickyTop = ({ top = 0, zIndex = 1, className = '', children = _jsx("div", { children: "Sticky Top" }), }) => {
|
|
4
|
+
const elRef = useRef(null);
|
|
5
|
+
const [isSticky, setIsSticky] = useState(false);
|
|
6
|
+
const [width, setWidth] = useState('auto');
|
|
7
|
+
const [height, setHeight] = useState('auto');
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const handleScroll = () => {
|
|
10
|
+
if (!elRef.current)
|
|
11
|
+
return;
|
|
12
|
+
const rect = elRef.current.getBoundingClientRect();
|
|
13
|
+
// Element should be sticky when it would go above the specified top position
|
|
14
|
+
const shouldStick = rect.top <= top;
|
|
15
|
+
if (shouldStick && !isSticky) {
|
|
16
|
+
setWidth(rect.width + 'px');
|
|
17
|
+
setHeight(rect.height + 'px');
|
|
18
|
+
setIsSticky(true);
|
|
19
|
+
}
|
|
20
|
+
else if (!shouldStick && isSticky) {
|
|
21
|
+
setIsSticky(false);
|
|
22
|
+
setWidth('auto');
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const handleResize = () => {
|
|
26
|
+
if (!isSticky && elRef.current) {
|
|
27
|
+
// Update dimensions when not sticky
|
|
28
|
+
const rect = elRef.current.getBoundingClientRect();
|
|
29
|
+
setHeight(rect.height + 'px');
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
// Initial setup
|
|
33
|
+
if (elRef.current) {
|
|
34
|
+
const rect = elRef.current.getBoundingClientRect();
|
|
35
|
+
setHeight(rect.height + 'px');
|
|
36
|
+
}
|
|
37
|
+
window.addEventListener('scroll', handleScroll);
|
|
38
|
+
window.addEventListener('resize', handleResize);
|
|
39
|
+
// Check initial state
|
|
40
|
+
handleScroll();
|
|
41
|
+
return () => {
|
|
42
|
+
window.removeEventListener('scroll', handleScroll);
|
|
43
|
+
window.removeEventListener('resize', handleResize);
|
|
44
|
+
};
|
|
45
|
+
}, [top, isSticky]);
|
|
46
|
+
return (_jsx("div", { ref: elRef, style: {
|
|
47
|
+
height: isSticky ? height : 'auto',
|
|
48
|
+
zIndex
|
|
49
|
+
}, children: _jsx("div", { className: className, style: {
|
|
50
|
+
position: isSticky ? 'fixed' : 'static',
|
|
51
|
+
top: isSticky ? `${top}px` : 'auto',
|
|
52
|
+
width: isSticky ? width : 'auto',
|
|
53
|
+
zIndex,
|
|
54
|
+
}, children: children }) }));
|
|
55
|
+
};
|
|
56
|
+
export default StickyTop;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as StickyTop, type Props as StickyTopProps } from './StickyTop';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as StickyTop } from './StickyTop';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AccessGuard } from '@creopse/utils/enums';
|
|
2
|
+
import type { NTagType } from '@creopse/utils/types';
|
|
3
|
+
export declare const useAccess: () => {
|
|
4
|
+
accessGuards: {
|
|
5
|
+
label: string;
|
|
6
|
+
value: AccessGuard;
|
|
7
|
+
tagType: string;
|
|
8
|
+
}[];
|
|
9
|
+
getAccessGuardLabel: (value: AccessGuard) => string | undefined;
|
|
10
|
+
getAccessGuardTagType: (value: AccessGuard) => NTagType;
|
|
11
|
+
getRoleName: (roles: {
|
|
12
|
+
id: number;
|
|
13
|
+
name: string;
|
|
14
|
+
}[], id: number) => string;
|
|
15
|
+
isPermissionNative: (name: string) => boolean;
|
|
16
|
+
userIsSuperAdmin: (roles: {
|
|
17
|
+
name: string;
|
|
18
|
+
}[]) => boolean;
|
|
19
|
+
userCan: (permissions: {
|
|
20
|
+
name: string;
|
|
21
|
+
}[], permissionName: string) => boolean;
|
|
22
|
+
userIs: (roles: {
|
|
23
|
+
name: string;
|
|
24
|
+
}[], roleName: string) => boolean;
|
|
25
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { AccessGuard, Permission, UserRole } from '@creopse/utils/enums';
|
|
2
|
+
export const useAccess = () => {
|
|
3
|
+
// ── Permissions ───────────────────────────────────────────────────────────
|
|
4
|
+
const userCan = (permissions, permissionName) => {
|
|
5
|
+
return ((permissions?.findIndex((permission) => permission.name === permissionName) ?? -1) > -1);
|
|
6
|
+
};
|
|
7
|
+
const userIs = (roles, roleName) => {
|
|
8
|
+
return (roles?.findIndex((role) => role.name === roleName) ?? -1) > -1;
|
|
9
|
+
};
|
|
10
|
+
const userIsSuperAdmin = (roles) => {
|
|
11
|
+
return userIs(roles, UserRole.SUPER_ADMIN);
|
|
12
|
+
};
|
|
13
|
+
const isPermissionNative = (name) => {
|
|
14
|
+
return Object.values(Permission).includes(name);
|
|
15
|
+
};
|
|
16
|
+
// ── Access Guards ─────────────────────────────────────────────────────────
|
|
17
|
+
const accessGuards = [
|
|
18
|
+
{ label: 'API', value: AccessGuard.API, tagType: 'warning' },
|
|
19
|
+
{ label: 'Web', value: AccessGuard.WEB, tagType: 'info' },
|
|
20
|
+
{ label: 'Admin', value: AccessGuard.ADMIN, tagType: 'success' },
|
|
21
|
+
{ label: 'Mobile', value: AccessGuard.MOBILE, tagType: 'error' },
|
|
22
|
+
];
|
|
23
|
+
const getAccessGuardLabel = (value) => accessGuards.find((guard) => guard.value === value)?.label;
|
|
24
|
+
const getAccessGuardTagType = (value) => (accessGuards.find((guard) => guard.value === value)?.tagType ||
|
|
25
|
+
'default');
|
|
26
|
+
// ── Roles ─────────────────────────────────────────────────────────────────
|
|
27
|
+
const getRoleName = (roles, id) => {
|
|
28
|
+
return (roles?.find((r) => r.id === id)?.name ||
|
|
29
|
+
window.__creopse__.i18n.t('undefined'));
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
accessGuards,
|
|
33
|
+
getAccessGuardLabel,
|
|
34
|
+
getAccessGuardTagType,
|
|
35
|
+
getRoleName,
|
|
36
|
+
isPermissionNative,
|
|
37
|
+
userIsSuperAdmin,
|
|
38
|
+
userCan,
|
|
39
|
+
userIs,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BridgeAuthState, BridgeServerState, UserInterfaceState } from '@creopse/utils';
|
|
2
|
+
export declare const useCoreBridge: () => {
|
|
3
|
+
locale: string | undefined;
|
|
4
|
+
env: Readonly<import("@creopse/utils").EnvData> | undefined;
|
|
5
|
+
appConfig: Readonly<import("@creopse/utils").AppConfig> | undefined;
|
|
6
|
+
api: import("@creopse/utils").Api;
|
|
7
|
+
i18n: import("@creopse/utils").BridgeI18n;
|
|
8
|
+
router: import("@creopse/utils").BridgeRouter;
|
|
9
|
+
helpers: import("@creopse/utils").BridgeHelpers;
|
|
10
|
+
auth: BridgeAuthState;
|
|
11
|
+
server: BridgeServerState;
|
|
12
|
+
ui: UserInterfaceState;
|
|
13
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useStore } from 'zustand';
|
|
2
|
+
export const useCoreBridge = () => {
|
|
3
|
+
const { sharedData, api, i18n, router, helpers, stores } = window.__creopse__;
|
|
4
|
+
const authState = useStore(stores.auth);
|
|
5
|
+
const serverState = useStore(stores.server);
|
|
6
|
+
const uiState = useStore(stores.ui);
|
|
7
|
+
return {
|
|
8
|
+
locale: sharedData?.locale,
|
|
9
|
+
env: sharedData?.env,
|
|
10
|
+
appConfig: sharedData?.appConfig,
|
|
11
|
+
api,
|
|
12
|
+
i18n,
|
|
13
|
+
router,
|
|
14
|
+
helpers,
|
|
15
|
+
auth: authState,
|
|
16
|
+
server: serverState,
|
|
17
|
+
ui: uiState,
|
|
18
|
+
};
|
|
19
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const useResponsive: () => {
|
|
2
|
+
is2XlScreen: boolean;
|
|
3
|
+
isXlScreen: boolean;
|
|
4
|
+
isLgScreen: boolean;
|
|
5
|
+
isMdScreen: boolean;
|
|
6
|
+
isSmScreen: boolean;
|
|
7
|
+
fromSm: boolean;
|
|
8
|
+
fromMd: boolean;
|
|
9
|
+
fromLg: boolean;
|
|
10
|
+
fromXl: boolean;
|
|
11
|
+
from2Xl: boolean;
|
|
12
|
+
isMobile: boolean;
|
|
13
|
+
isTablet: boolean;
|
|
14
|
+
isDesktop: boolean;
|
|
15
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
const query = (q) => window.matchMedia(q);
|
|
3
|
+
const useMediaQuery = (q) => {
|
|
4
|
+
const [matches, setMatches] = useState(() => query(q).matches);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
const mq = query(q);
|
|
7
|
+
const handler = (e) => setMatches(e.matches);
|
|
8
|
+
mq.addEventListener('change', handler);
|
|
9
|
+
return () => mq.removeEventListener('change', handler);
|
|
10
|
+
}, [q]);
|
|
11
|
+
return matches;
|
|
12
|
+
};
|
|
13
|
+
export const useResponsive = () => {
|
|
14
|
+
const is2XlScreen = useMediaQuery('(min-width: 1536px)');
|
|
15
|
+
const isXlScreen = useMediaQuery('(min-width: 1280px) and (max-width: 1535px)');
|
|
16
|
+
const isLgScreen = useMediaQuery('(min-width: 1024px) and (max-width: 1279px)');
|
|
17
|
+
const isMdScreen = useMediaQuery('(min-width: 768px) and (max-width: 1023px)');
|
|
18
|
+
const isSmScreen = useMediaQuery('(max-width: 767px)');
|
|
19
|
+
const fromSm = useMediaQuery('(min-width: 640px)');
|
|
20
|
+
const fromMd = useMediaQuery('(min-width: 768px)');
|
|
21
|
+
const fromLg = useMediaQuery('(min-width: 1024px)');
|
|
22
|
+
const fromXl = useMediaQuery('(min-width: 1280px)');
|
|
23
|
+
const from2Xl = useMediaQuery('(min-width: 1536px)');
|
|
24
|
+
const isMobile = !fromMd;
|
|
25
|
+
const isTablet = fromMd && !fromLg;
|
|
26
|
+
const isDesktop = fromLg;
|
|
27
|
+
return {
|
|
28
|
+
is2XlScreen,
|
|
29
|
+
isXlScreen,
|
|
30
|
+
isLgScreen,
|
|
31
|
+
isMdScreen,
|
|
32
|
+
isSmScreen,
|
|
33
|
+
fromSm,
|
|
34
|
+
fromMd,
|
|
35
|
+
fromLg,
|
|
36
|
+
fromXl,
|
|
37
|
+
from2Xl,
|
|
38
|
+
isMobile,
|
|
39
|
+
isTablet,
|
|
40
|
+
isDesktop,
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Bool } from '@creopse/utils/enums';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
export const useUserPreference = () => {
|
|
4
|
+
const [defaultPrefs, setDefaultPrefs] = useState({
|
|
5
|
+
inAppNotifEnabled: Bool.TRUE,
|
|
6
|
+
emailNotifEnabled: Bool.TRUE,
|
|
7
|
+
locale: window.__creopse__.i18n.getLocale(),
|
|
8
|
+
});
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
// Resync if the locale changes on the host
|
|
11
|
+
setDefaultPrefs((prev) => ({
|
|
12
|
+
...prev,
|
|
13
|
+
locale: window.__creopse__.i18n.getLocale(),
|
|
14
|
+
}));
|
|
15
|
+
}, []);
|
|
16
|
+
return { defaultPrefs };
|
|
17
|
+
};
|
package/dist/vite/react.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DASHBOARD_MODULE_NAME, sanitizeId, SETTINGS_MODULE_NAME, } from '@creopse/utils/helpers';
|
|
1
2
|
import federation from '@originjs/vite-plugin-federation';
|
|
2
3
|
import basicSsl from '@vitejs/plugin-basic-ssl';
|
|
3
4
|
import react from '@vitejs/plugin-react';
|
|
@@ -10,8 +11,6 @@ import AutoImportPlugin from 'vite-plugin-auto-import-lite';
|
|
|
10
11
|
import i18nextLoader from 'vite-plugin-i18next-loader';
|
|
11
12
|
import json5Plugin from 'vite-plugin-json5';
|
|
12
13
|
import topLevelAwait from 'vite-plugin-top-level-await';
|
|
13
|
-
import { DASHBOARD_MODULE_NAME, SETTINGS_MODULE_NAME } from '../utils/constants';
|
|
14
|
-
import { sanitizeId } from '../utils/functions';
|
|
15
14
|
// ——— Plugin ——————————————————————————————————————————————————————————————————
|
|
16
15
|
export function CreopseReactPlugin(options = {}) {
|
|
17
16
|
const { manifestPath = './manifest.jsonc', srcDir = './src', outDir = 'frontend', i18nLocalesPath, hooksDirs = ['src/hooks'], shared = [], unocss = false, eslintAutoImport = false, } = options;
|
package/dist/vite/vue.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DASHBOARD_MODULE_NAME, sanitizeId, SETTINGS_MODULE_NAME, } from '@creopse/utils/helpers';
|
|
1
2
|
import federation from '@originjs/vite-plugin-federation';
|
|
2
3
|
import basicSsl from '@vitejs/plugin-basic-ssl';
|
|
3
4
|
import vue from '@vitejs/plugin-vue';
|
|
@@ -10,8 +11,6 @@ import AutoImportPlugin from 'vite-plugin-auto-import-lite';
|
|
|
10
11
|
import i18nextLoader from 'vite-plugin-i18next-loader';
|
|
11
12
|
import json5Plugin from 'vite-plugin-json5';
|
|
12
13
|
import topLevelAwait from 'vite-plugin-top-level-await';
|
|
13
|
-
import { DASHBOARD_MODULE_NAME, SETTINGS_MODULE_NAME } from '../utils/constants';
|
|
14
|
-
import { sanitizeId } from '../utils/functions';
|
|
15
14
|
// ——— Plugin ——————————————————————————————————————————————————————————————————
|
|
16
15
|
export function CreopseVuePlugin(options = {}) {
|
|
17
16
|
const { manifestPath = './manifest.jsonc', srcDir = './src', outDir = 'frontend', i18nLocalesPath, composablesDirs = ['src/composables'], shared = [], unocss = false, } = options;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AccessGuard } from '@creopse/utils/enums';
|
|
2
|
+
import type { NTagType } from '@creopse/utils/types';
|
|
3
|
+
export declare const useAccess: () => {
|
|
4
|
+
accessGuards: {
|
|
5
|
+
label: string;
|
|
6
|
+
value: AccessGuard;
|
|
7
|
+
tagType: string;
|
|
8
|
+
}[];
|
|
9
|
+
getAccessGuardLabel: (value: AccessGuard) => string | undefined;
|
|
10
|
+
getAccessGuardTagType: (value: AccessGuard) => NTagType;
|
|
11
|
+
getRoleName: (roles: {
|
|
12
|
+
id: number;
|
|
13
|
+
name: string;
|
|
14
|
+
}[], id: number) => string;
|
|
15
|
+
isPermissionNative: (name: string) => boolean;
|
|
16
|
+
userIsSuperAdmin: (roles: {
|
|
17
|
+
name: string;
|
|
18
|
+
}[]) => boolean;
|
|
19
|
+
userCan: (permissions: {
|
|
20
|
+
name: string;
|
|
21
|
+
}[], permissionName: string) => boolean;
|
|
22
|
+
userIs: (roles: {
|
|
23
|
+
name: string;
|
|
24
|
+
}[], roleName: string) => boolean;
|
|
25
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { AccessGuard, Permission, UserRole } from '@creopse/utils/enums';
|
|
2
|
+
import { useI18n } from 'vue-i18n';
|
|
3
|
+
export const useAccess = () => {
|
|
4
|
+
const { t } = useI18n();
|
|
5
|
+
// ── Permissions ───────────────────────────────────────────────────────────
|
|
6
|
+
const userCan = (permissions, permissionName) => {
|
|
7
|
+
return ((permissions?.findIndex((permission) => permission.name === permissionName) ?? -1) > -1);
|
|
8
|
+
};
|
|
9
|
+
const userIs = (roles, roleName) => {
|
|
10
|
+
return (roles?.findIndex((role) => role.name === roleName) ?? -1) > -1;
|
|
11
|
+
};
|
|
12
|
+
const userIsSuperAdmin = (roles) => {
|
|
13
|
+
return userIs(roles, UserRole.SUPER_ADMIN);
|
|
14
|
+
};
|
|
15
|
+
const isPermissionNative = (name) => {
|
|
16
|
+
return Object.values(Permission).includes(name);
|
|
17
|
+
};
|
|
18
|
+
// ── Access Guards ─────────────────────────────────────────────────────────
|
|
19
|
+
const accessGuards = [
|
|
20
|
+
{
|
|
21
|
+
label: 'API',
|
|
22
|
+
value: AccessGuard.API,
|
|
23
|
+
tagType: 'warning',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
label: 'Web',
|
|
27
|
+
value: AccessGuard.WEB,
|
|
28
|
+
tagType: 'info',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
label: 'Admin',
|
|
32
|
+
value: AccessGuard.ADMIN,
|
|
33
|
+
tagType: 'success',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
label: 'Mobile',
|
|
37
|
+
value: AccessGuard.MOBILE,
|
|
38
|
+
tagType: 'error',
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
const getAccessGuardLabel = (value) => accessGuards.find((guard) => guard.value === value)?.label;
|
|
42
|
+
const getAccessGuardTagType = (value) => (accessGuards.find((guard) => guard.value === value)?.tagType ||
|
|
43
|
+
'default');
|
|
44
|
+
// ── Roles ─────────────────────────────────────────────────────────────────
|
|
45
|
+
const getRoleName = (roles, id) => {
|
|
46
|
+
return roles?.find((r) => r.id === id)?.name || t('undefined');
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
accessGuards,
|
|
50
|
+
getAccessGuardLabel,
|
|
51
|
+
getAccessGuardTagType,
|
|
52
|
+
getRoleName,
|
|
53
|
+
isPermissionNative,
|
|
54
|
+
userIsSuperAdmin,
|
|
55
|
+
userCan,
|
|
56
|
+
userIs,
|
|
57
|
+
};
|
|
58
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const useCoreBridge: () => {
|
|
2
|
+
locale: string | undefined;
|
|
3
|
+
env: Readonly<import("@creopse/utils").EnvData> | undefined;
|
|
4
|
+
appConfig: Readonly<import("@creopse/utils").AppConfig> | undefined;
|
|
5
|
+
api: import("@creopse/utils").Api;
|
|
6
|
+
i18n: import("@creopse/utils").BridgeI18n;
|
|
7
|
+
router: import("@creopse/utils").BridgeRouter;
|
|
8
|
+
helpers: import("@creopse/utils").BridgeHelpers;
|
|
9
|
+
auth: import("@creopse/utils").BridgeStore<import("@creopse/utils").BridgeAuthState>;
|
|
10
|
+
server: import("@creopse/utils").BridgeStore<import("@creopse/utils").BridgeServerState>;
|
|
11
|
+
ui: import("@creopse/utils").BridgeStore<import("@creopse/utils").UserInterfaceState>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const useCoreBridge = () => {
|
|
2
|
+
const { sharedData, api, i18n, router, helpers, stores } = window.__creopse__;
|
|
3
|
+
return {
|
|
4
|
+
locale: sharedData?.locale,
|
|
5
|
+
env: sharedData?.env,
|
|
6
|
+
appConfig: sharedData?.appConfig,
|
|
7
|
+
api,
|
|
8
|
+
i18n,
|
|
9
|
+
router,
|
|
10
|
+
helpers,
|
|
11
|
+
auth: stores.auth,
|
|
12
|
+
server: stores.server,
|
|
13
|
+
ui: stores.ui,
|
|
14
|
+
};
|
|
15
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const useResponsive: () => {
|
|
2
|
+
is2XlScreen: import("vue").Ref<boolean, boolean>;
|
|
3
|
+
isXlScreen: import("vue").Ref<boolean, boolean>;
|
|
4
|
+
isLgScreen: import("vue").Ref<boolean, boolean>;
|
|
5
|
+
isMdScreen: import("vue").Ref<boolean, boolean>;
|
|
6
|
+
isSmScreen: import("vue").Ref<boolean, boolean>;
|
|
7
|
+
fromSm: import("vue").Ref<boolean, boolean>;
|
|
8
|
+
fromMd: import("vue").Ref<boolean, boolean>;
|
|
9
|
+
fromLg: import("vue").Ref<boolean, boolean>;
|
|
10
|
+
fromXl: import("vue").Ref<boolean, boolean>;
|
|
11
|
+
from2Xl: import("vue").Ref<boolean, boolean>;
|
|
12
|
+
isMobile: import("vue").ComputedRef<boolean>;
|
|
13
|
+
isTablet: import("vue").ComputedRef<boolean>;
|
|
14
|
+
isDesktop: import("vue").ComputedRef<boolean>;
|
|
15
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useMediaQuery } from '@vueuse/core';
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
export const useResponsive = () => {
|
|
4
|
+
// ── Approach (closed ranks) ─────────────────────────────
|
|
5
|
+
const is2XlScreen = useMediaQuery('(min-width: 1536px)');
|
|
6
|
+
const isXlScreen = useMediaQuery('(min-width: 1280px) and (max-width: 1535px)');
|
|
7
|
+
const isLgScreen = useMediaQuery('(min-width: 1024px) and (max-width: 1279px)');
|
|
8
|
+
const isMdScreen = useMediaQuery('(min-width: 768px) and (max-width: 1023px)');
|
|
9
|
+
const isSmScreen = useMediaQuery('(max-width: 767px)');
|
|
10
|
+
// ── Approach (min-width, mobile-first) ────────────────────
|
|
11
|
+
const fromSm = useMediaQuery('(min-width: 640px)');
|
|
12
|
+
const fromMd = useMediaQuery('(min-width: 768px)');
|
|
13
|
+
const fromLg = useMediaQuery('(min-width: 1024px)');
|
|
14
|
+
const fromXl = useMediaQuery('(min-width: 1280px)');
|
|
15
|
+
const from2Xl = useMediaQuery('(min-width: 1536px)');
|
|
16
|
+
// ── Semantic aliases ────────
|
|
17
|
+
const isMobile = computed(() => !fromMd.value);
|
|
18
|
+
const isTablet = computed(() => fromMd.value && !fromLg.value);
|
|
19
|
+
const isDesktop = computed(() => fromLg.value);
|
|
20
|
+
return {
|
|
21
|
+
is2XlScreen,
|
|
22
|
+
isXlScreen,
|
|
23
|
+
isLgScreen,
|
|
24
|
+
isMdScreen,
|
|
25
|
+
isSmScreen,
|
|
26
|
+
fromSm,
|
|
27
|
+
fromMd,
|
|
28
|
+
fromLg,
|
|
29
|
+
fromXl,
|
|
30
|
+
from2Xl,
|
|
31
|
+
isMobile,
|
|
32
|
+
isTablet,
|
|
33
|
+
isDesktop,
|
|
34
|
+
};
|
|
35
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Bool, UserPrefs } from '@creopse/utils';
|
|
2
|
+
export declare const useUserPreference: () => {
|
|
3
|
+
defaultPrefs: import("vue").Ref<{
|
|
4
|
+
inAppNotifEnabled?: Bool | undefined;
|
|
5
|
+
emailNotifEnabled?: Bool | undefined;
|
|
6
|
+
locale?: string | undefined;
|
|
7
|
+
}, UserPrefs | {
|
|
8
|
+
inAppNotifEnabled?: Bool | undefined;
|
|
9
|
+
emailNotifEnabled?: Bool | undefined;
|
|
10
|
+
locale?: string | undefined;
|
|
11
|
+
}>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Bool } from '@creopse/utils';
|
|
2
|
+
import { ref } from 'vue';
|
|
3
|
+
import { useI18n } from 'vue-i18n';
|
|
4
|
+
export const useUserPreference = () => {
|
|
5
|
+
const { locale } = useI18n();
|
|
6
|
+
const defaultPrefs = ref({
|
|
7
|
+
inAppNotifEnabled: Bool.TRUE,
|
|
8
|
+
emailNotifEnabled: Bool.TRUE,
|
|
9
|
+
locale: locale.value,
|
|
10
|
+
});
|
|
11
|
+
return {
|
|
12
|
+
defaultPrefs,
|
|
13
|
+
};
|
|
14
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@creopse/bridge",
|
|
3
3
|
"description": "Creopse Bridge Toolkit",
|
|
4
|
-
"version": "0.0
|
|
4
|
+
"version": "0.1.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Noé Gnanih <noegnanih@gmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -20,8 +20,54 @@
|
|
|
20
20
|
"dist",
|
|
21
21
|
"README.md"
|
|
22
22
|
],
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@iconify/react": "^6.0.2",
|
|
25
|
+
"@iconify/vue": "^5.0.0",
|
|
26
|
+
"@vueuse/core": "^10.11.1",
|
|
27
|
+
"naive-ui": "^2.44.1",
|
|
28
|
+
"react": "^19.2.4",
|
|
29
|
+
"react-dom": "^19.2.4",
|
|
30
|
+
"vue": "^3.5.27",
|
|
31
|
+
"vue-i18n": "^11.3.0",
|
|
32
|
+
"zustand": "^5.0.12"
|
|
33
|
+
},
|
|
34
|
+
"peerDependenciesMeta": {
|
|
35
|
+
"naive-ui": {
|
|
36
|
+
"optional": true
|
|
37
|
+
},
|
|
38
|
+
"@vueuse/core": {
|
|
39
|
+
"optional": true
|
|
40
|
+
},
|
|
41
|
+
"@iconify/vue": {
|
|
42
|
+
"optional": true
|
|
43
|
+
},
|
|
44
|
+
"@iconify/react": {
|
|
45
|
+
"optional": true
|
|
46
|
+
},
|
|
47
|
+
"vue": {
|
|
48
|
+
"optional": true
|
|
49
|
+
},
|
|
50
|
+
"vue-i18n": {
|
|
51
|
+
"optional": true
|
|
52
|
+
},
|
|
53
|
+
"react": {
|
|
54
|
+
"optional": true
|
|
55
|
+
},
|
|
56
|
+
"react-dom": {
|
|
57
|
+
"optional": true
|
|
58
|
+
},
|
|
59
|
+
"zustand": {
|
|
60
|
+
"optional": true
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"framer-motion": "^12.23.9",
|
|
65
|
+
"@creopse/utils": "0.1.0"
|
|
66
|
+
},
|
|
23
67
|
"devDependencies": {
|
|
24
68
|
"@originjs/vite-plugin-federation": "^1.4.1",
|
|
69
|
+
"@types/react": "^19.2.4",
|
|
70
|
+
"@types/react-dom": "^19.1.6",
|
|
25
71
|
"@vitejs/plugin-basic-ssl": "^2.3.0",
|
|
26
72
|
"@vitejs/plugin-react": "^4.6.0",
|
|
27
73
|
"@vitejs/plugin-vue": "^6.0.0",
|
|
@@ -34,8 +80,7 @@
|
|
|
34
80
|
"vite-plugin-auto-import-lite": "^0.0.2",
|
|
35
81
|
"vite-plugin-i18next-loader": "^3.1.3",
|
|
36
82
|
"vite-plugin-json5": "^1.2.0",
|
|
37
|
-
"vite-plugin-top-level-await": "^1.6.0"
|
|
38
|
-
"@creopse/utils": "0.0.16"
|
|
83
|
+
"vite-plugin-top-level-await": "^1.6.0"
|
|
39
84
|
},
|
|
40
85
|
"repository": {
|
|
41
86
|
"type": "git",
|