@enderfall/ui 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/components/AccessGate.d.ts +16 -0
- package/dist/components/AccessGate.d.ts.map +1 -0
- package/dist/components/AccessGate.js +9 -0
- package/dist/components/Button.d.ts +7 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Button.js +2 -0
- package/dist/components/Dropdown.d.ts +88 -0
- package/dist/components/Dropdown.d.ts.map +1 -0
- package/dist/components/Dropdown.js +179 -0
- package/dist/components/FloatingFooter.d.ts +10 -0
- package/dist/components/FloatingFooter.d.ts.map +1 -0
- package/dist/components/FloatingFooter.js +6 -0
- package/dist/components/FormField.d.ts +12 -0
- package/dist/components/FormField.d.ts.map +1 -0
- package/dist/components/FormField.js +2 -0
- package/dist/components/Input.d.ts +12 -0
- package/dist/components/Input.d.ts.map +1 -0
- package/dist/components/Input.js +5 -0
- package/dist/components/MainHeader.d.ts +15 -0
- package/dist/components/MainHeader.d.ts.map +1 -0
- package/dist/components/MainHeader.js +6 -0
- package/dist/components/Modal.d.ts +16 -0
- package/dist/components/Modal.d.ts.map +1 -0
- package/dist/components/Modal.js +82 -0
- package/dist/components/Panel.d.ts +9 -0
- package/dist/components/Panel.d.ts.map +1 -0
- package/dist/components/Panel.js +15 -0
- package/dist/components/PreferencesModal.d.ts +19 -0
- package/dist/components/PreferencesModal.d.ts.map +1 -0
- package/dist/components/PreferencesModal.js +17 -0
- package/dist/components/SideMenu.d.ts +28 -0
- package/dist/components/SideMenu.d.ts.map +1 -0
- package/dist/components/SideMenu.js +144 -0
- package/dist/components/Slider.d.ts +5 -0
- package/dist/components/Slider.d.ts.map +1 -0
- package/dist/components/Slider.js +20 -0
- package/dist/components/StackedCard.d.ts +29 -0
- package/dist/components/StackedCard.d.ts.map +1 -0
- package/dist/components/StackedCard.js +31 -0
- package/dist/components/StatDots.d.ts +12 -0
- package/dist/components/StatDots.d.ts.map +1 -0
- package/dist/components/StatDots.js +18 -0
- package/dist/components/Toggle.d.ts +9 -0
- package/dist/components/Toggle.d.ts.map +1 -0
- package/dist/components/Toggle.js +4 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/package.json +30 -0
- package/styles.css +17 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type AccessGateProps = {
|
|
2
|
+
status: "checking" | "locked" | "allowed";
|
|
3
|
+
titleChecking?: string;
|
|
4
|
+
titleLocked?: string;
|
|
5
|
+
messageChecking?: string;
|
|
6
|
+
messageLocked?: string;
|
|
7
|
+
primaryLabel: string;
|
|
8
|
+
secondaryLabel?: string;
|
|
9
|
+
onPrimary: () => void;
|
|
10
|
+
onSecondary?: () => void;
|
|
11
|
+
primaryClassName?: string;
|
|
12
|
+
secondaryClassName?: string;
|
|
13
|
+
};
|
|
14
|
+
export declare const AccessGate: ({ status, titleChecking, titleLocked, messageChecking, messageLocked, primaryLabel, secondaryLabel, onPrimary, onSecondary, primaryClassName, secondaryClassName, }: AccessGateProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=AccessGate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AccessGate.d.ts","sourceRoot":"","sources":["../../src/components/AccessGate.tsx"],"names":[],"mappings":"AAGA,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,qKAYxB,eAAe,mDA+BjB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from "./Button";
|
|
3
|
+
import { Panel } from "./Panel";
|
|
4
|
+
export const AccessGate = ({ status, titleChecking = "Checking access...", titleLocked = "App locked", messageChecking = "Verifying your access with Enderfall Hub.", messageLocked = "Open Enderfall Hub to verify premium or admin access.", primaryLabel, secondaryLabel, onPrimary, onSecondary, primaryClassName = "primary", secondaryClassName = "ghost", }) => {
|
|
5
|
+
if (status === "allowed")
|
|
6
|
+
return null;
|
|
7
|
+
const isChecking = status === "checking";
|
|
8
|
+
return (_jsx("div", { className: "ef-access-overlay", children: _jsxs(Panel, { variant: "card", borderWidth: 2, className: "ef-access-card", children: [_jsx("div", { className: "ef-access-title", children: isChecking ? titleChecking : titleLocked }), _jsx("p", { children: isChecking ? messageChecking : messageLocked }), _jsxs("div", { className: "ef-access-actions", children: [_jsx(Button, { type: "button", className: primaryClassName, onClick: onPrimary, disabled: isChecking, children: primaryLabel }), secondaryLabel && onSecondary ? (_jsx(Button, { type: "button", className: secondaryClassName, onClick: onSecondary, disabled: isChecking, children: secondaryLabel })) : null] })] }) }));
|
|
9
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ButtonHTMLAttributes } from "react";
|
|
2
|
+
type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
3
|
+
variant?: "primary" | "ghost" | "locked" | "danger" | "delete" | "warning" | "info" | "success";
|
|
4
|
+
};
|
|
5
|
+
export declare const Button: ({ variant, className, ...props }: ButtonProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=Button.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../src/components/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAElD,KAAK,WAAW,GAAG,oBAAoB,CAAC,iBAAiB,CAAC,GAAG;IAC3D,OAAO,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;CACjG,CAAC;AAEF,eAAO,MAAM,MAAM,GAAI,kCAA8C,WAAW,4CAK/E,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export type HeaderMenuItem = {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
content: ReactNode;
|
|
6
|
+
};
|
|
7
|
+
export type DropdownUserItem = {
|
|
8
|
+
id?: string;
|
|
9
|
+
label: string;
|
|
10
|
+
onClick: () => void;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
className?: string;
|
|
13
|
+
title?: string;
|
|
14
|
+
variant?: "default" | "theme-preview";
|
|
15
|
+
};
|
|
16
|
+
export type DropdownUserListItem = {
|
|
17
|
+
id?: string;
|
|
18
|
+
label: string;
|
|
19
|
+
onClick: () => void;
|
|
20
|
+
onEdit?: () => void;
|
|
21
|
+
onDelete?: () => void;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
className?: string;
|
|
24
|
+
title?: string;
|
|
25
|
+
avatarUrl?: string | null;
|
|
26
|
+
avatarFallback?: string;
|
|
27
|
+
subtitle?: string;
|
|
28
|
+
};
|
|
29
|
+
export type DropdownBookmarkOption = {
|
|
30
|
+
value: string;
|
|
31
|
+
label: string;
|
|
32
|
+
meta?: unknown;
|
|
33
|
+
className?: string;
|
|
34
|
+
};
|
|
35
|
+
export type DropdownBookmarkSection = {
|
|
36
|
+
label?: string;
|
|
37
|
+
options: DropdownBookmarkOption[];
|
|
38
|
+
};
|
|
39
|
+
type HeaderVariantProps = {
|
|
40
|
+
variant: "header";
|
|
41
|
+
menus: HeaderMenuItem[];
|
|
42
|
+
menuOpen: string | null;
|
|
43
|
+
onOpenMenu: (id: string) => void;
|
|
44
|
+
onCloseMenu: () => void;
|
|
45
|
+
};
|
|
46
|
+
type UserVariantProps = {
|
|
47
|
+
variant: "user";
|
|
48
|
+
name: string;
|
|
49
|
+
avatarUrl?: string | null;
|
|
50
|
+
avatarUrlFallback?: string | null;
|
|
51
|
+
avatarAlt?: string;
|
|
52
|
+
avatarFallback?: string;
|
|
53
|
+
items: DropdownUserItem[];
|
|
54
|
+
open?: boolean;
|
|
55
|
+
onOpenChange?: (open: boolean) => void;
|
|
56
|
+
};
|
|
57
|
+
type UserListVariantProps = {
|
|
58
|
+
variant: "user-list";
|
|
59
|
+
name: string;
|
|
60
|
+
avatarUrl?: string | null;
|
|
61
|
+
avatarUrlFallback?: string | null;
|
|
62
|
+
avatarAlt?: string;
|
|
63
|
+
avatarFallback?: string;
|
|
64
|
+
items: DropdownUserListItem[];
|
|
65
|
+
open?: boolean;
|
|
66
|
+
onOpenChange?: (open: boolean) => void;
|
|
67
|
+
emptyLabel?: string;
|
|
68
|
+
emptyClassName?: string;
|
|
69
|
+
};
|
|
70
|
+
type BookmarkVariantProps = {
|
|
71
|
+
variant: "bookmark";
|
|
72
|
+
label?: string;
|
|
73
|
+
layout?: "row" | "field";
|
|
74
|
+
value: string;
|
|
75
|
+
triggerLabel?: string;
|
|
76
|
+
placeholder?: string;
|
|
77
|
+
sections: DropdownBookmarkSection[];
|
|
78
|
+
onChange: (value: string, option?: DropdownBookmarkOption) => void;
|
|
79
|
+
renderTriggerIcon?: ReactNode;
|
|
80
|
+
renderItemIcon?: (option: DropdownBookmarkOption) => ReactNode;
|
|
81
|
+
caret?: ReactNode;
|
|
82
|
+
emptyLabel?: string;
|
|
83
|
+
emptyClassName?: string;
|
|
84
|
+
};
|
|
85
|
+
type DropdownProps = HeaderVariantProps | UserVariantProps | UserListVariantProps | BookmarkVariantProps;
|
|
86
|
+
export declare const Dropdown: (props: DropdownProps) => import("react/jsx-runtime").JSX.Element;
|
|
87
|
+
export {};
|
|
88
|
+
//# sourceMappingURL=Dropdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Dropdown.d.ts","sourceRoot":"","sources":["../../src/components/Dropdown.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAwC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAG7E,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,SAAS,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,SAAS,GAAG,eAAe,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,sBAAsB,EAAE,CAAC;CACnC,CAAC;AAEF,KAAK,kBAAkB,GAAG;IACxB,OAAO,EAAE,QAAQ,CAAC;IAClB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,OAAO,EAAE,WAAW,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,OAAO,EAAE,UAAU,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,uBAAuB,EAAE,CAAC;IACpC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACnE,iBAAiB,CAAC,EAAE,SAAS,CAAC;IAC9B,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,SAAS,CAAC;IAC/D,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,KAAK,aAAa,GACd,kBAAkB,GAClB,gBAAgB,GAChB,oBAAoB,GACpB,oBAAoB,CAAC;AAgFzB,eAAO,MAAM,QAAQ,GAAI,OAAO,aAAa,4CAiY5C,CAAC"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { Button } from "./Button";
|
|
4
|
+
const DefaultChevron = ({ open }) => (_jsx("span", { className: `chevron ${open ? "open" : ""}`, "aria-hidden": "true", children: _jsx("svg", { viewBox: "0 0 24 24", children: _jsx("path", { d: "M6 9l6 6 6-6", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }) }) }));
|
|
5
|
+
const DefaultCaret = () => (_jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { d: "M6 9l6 6 6-6", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
6
|
+
const IconEdit = () => (_jsxs("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", children: [_jsx("path", { d: "M4 20l4.5-1 9.4-9.4a1.8 1.8 0 0 0 0-2.6l-1-1a1.8 1.8 0 0 0-2.6 0L4.9 15.4 4 20z", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M13.5 6.5l4 4", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" })] }));
|
|
7
|
+
const IconTrash = () => (_jsxs("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", children: [_jsx("path", { d: "M4 7h16", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M7 7l1 12a1 1 0 0 0 1 .9h6a1 1 0 0 0 1-.9l1-12", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" })] }));
|
|
8
|
+
export const Dropdown = (props) => {
|
|
9
|
+
const closeTimerRef = useRef(null);
|
|
10
|
+
const closeDelayMs = 160;
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
return () => {
|
|
13
|
+
if (closeTimerRef.current !== null) {
|
|
14
|
+
window.clearTimeout(closeTimerRef.current);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}, []);
|
|
18
|
+
const cancelScheduledClose = () => {
|
|
19
|
+
if (closeTimerRef.current !== null) {
|
|
20
|
+
window.clearTimeout(closeTimerRef.current);
|
|
21
|
+
closeTimerRef.current = null;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
if (props.variant === "header") {
|
|
25
|
+
const scheduleClose = () => {
|
|
26
|
+
cancelScheduledClose();
|
|
27
|
+
closeTimerRef.current = window.setTimeout(() => {
|
|
28
|
+
props.onCloseMenu();
|
|
29
|
+
}, closeDelayMs);
|
|
30
|
+
};
|
|
31
|
+
return (_jsx("div", { className: "ef-menu-bar", children: props.menus.map((menu) => (_jsxs("div", { className: "ef-menu-group", onMouseEnter: () => {
|
|
32
|
+
cancelScheduledClose();
|
|
33
|
+
props.onOpenMenu(menu.id);
|
|
34
|
+
}, onMouseLeave: scheduleClose, children: [_jsx("button", { className: "ef-menu-button", type: "button", children: menu.label }), props.menuOpen === menu.id ? (_jsx("div", { className: "ef-menu-popover", "data-open": "true", onMouseEnter: () => {
|
|
35
|
+
cancelScheduledClose();
|
|
36
|
+
props.onOpenMenu(menu.id);
|
|
37
|
+
}, onMouseLeave: scheduleClose, children: menu.content })) : null] }, menu.id))) }));
|
|
38
|
+
}
|
|
39
|
+
if (props.variant === "user" || props.variant === "user-list") {
|
|
40
|
+
const [internalOpen, setInternalOpen] = useState(false);
|
|
41
|
+
const [avatarState, setAvatarState] = useState("primary");
|
|
42
|
+
const ref = useRef(null);
|
|
43
|
+
const isOpen = props.open ?? internalOpen;
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
setAvatarState("primary");
|
|
46
|
+
}, [props.avatarUrl, props.avatarUrlFallback]);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (!isOpen)
|
|
49
|
+
return;
|
|
50
|
+
const handlePointer = (event) => {
|
|
51
|
+
if (!ref.current)
|
|
52
|
+
return;
|
|
53
|
+
if (ref.current.contains(event.target))
|
|
54
|
+
return;
|
|
55
|
+
if (props.open === undefined) {
|
|
56
|
+
setInternalOpen(false);
|
|
57
|
+
}
|
|
58
|
+
props.onOpenChange?.(false);
|
|
59
|
+
};
|
|
60
|
+
window.addEventListener("pointerdown", handlePointer);
|
|
61
|
+
return () => window.removeEventListener("pointerdown", handlePointer);
|
|
62
|
+
}, [isOpen, props.open, props.onOpenChange]);
|
|
63
|
+
const setOpen = (next) => {
|
|
64
|
+
if (props.open === undefined) {
|
|
65
|
+
setInternalOpen(next);
|
|
66
|
+
}
|
|
67
|
+
props.onOpenChange?.(next);
|
|
68
|
+
};
|
|
69
|
+
const fallback = props.avatarFallback ?? props.name.slice(0, 1).toUpperCase();
|
|
70
|
+
const primaryAvatar = props.avatarUrl ?? null;
|
|
71
|
+
const fallbackAvatar = props.avatarUrlFallback ?? null;
|
|
72
|
+
const currentAvatar = avatarState === "primary"
|
|
73
|
+
? primaryAvatar
|
|
74
|
+
: avatarState === "fallback"
|
|
75
|
+
? fallbackAvatar
|
|
76
|
+
: null;
|
|
77
|
+
const renderUserListItems = () => {
|
|
78
|
+
if (props.variant !== "user-list") {
|
|
79
|
+
return props.items.map((item, index) => {
|
|
80
|
+
const key = item.id ?? `${item.label}-${index}`;
|
|
81
|
+
if (item.variant === "theme-preview") {
|
|
82
|
+
return (_jsx(Button, { type: "button", variant: "primary", className: ["theme-preview", item.className].filter(Boolean).join(" "), onClick: () => {
|
|
83
|
+
item.onClick();
|
|
84
|
+
setOpen(false);
|
|
85
|
+
}, disabled: item.disabled, title: item.title, children: item.label }, key));
|
|
86
|
+
}
|
|
87
|
+
return (_jsx("button", { className: ["dropdown-item", item.className].filter(Boolean).join(" "), type: "button", onClick: () => {
|
|
88
|
+
item.onClick();
|
|
89
|
+
setOpen(false);
|
|
90
|
+
}, disabled: item.disabled, title: item.title, children: item.label }, key));
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (!props.items.length) {
|
|
94
|
+
return (_jsx("div", { className: ["dropdown-empty", props.emptyClassName].filter(Boolean).join(" "), children: props.emptyLabel ?? "No items." }));
|
|
95
|
+
}
|
|
96
|
+
return props.items.map((item, index) => {
|
|
97
|
+
const fallback = item.avatarFallback ?? item.label.slice(0, 1).toUpperCase();
|
|
98
|
+
const isDisabled = !!item.disabled;
|
|
99
|
+
return (_jsxs("div", { className: [
|
|
100
|
+
"dropdown-item",
|
|
101
|
+
"dropdown-item-rich",
|
|
102
|
+
item.className,
|
|
103
|
+
isDisabled ? "is-disabled" : "",
|
|
104
|
+
]
|
|
105
|
+
.filter(Boolean)
|
|
106
|
+
.join(" "), role: "button", tabIndex: isDisabled ? -1 : 0, "aria-disabled": isDisabled ? "true" : undefined, onClick: () => {
|
|
107
|
+
if (isDisabled)
|
|
108
|
+
return;
|
|
109
|
+
item.onClick();
|
|
110
|
+
setOpen(false);
|
|
111
|
+
}, onKeyDown: (event) => {
|
|
112
|
+
if (isDisabled)
|
|
113
|
+
return;
|
|
114
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
115
|
+
event.preventDefault();
|
|
116
|
+
item.onClick();
|
|
117
|
+
setOpen(false);
|
|
118
|
+
}
|
|
119
|
+
}, title: item.title, children: [_jsx("span", { className: "dropdown-avatar", children: item.avatarUrl ? (_jsx("img", { src: item.avatarUrl, alt: item.label, loading: "lazy", decoding: "async", referrerPolicy: "no-referrer", crossOrigin: "anonymous" })) : (_jsx("span", { className: "dropdown-avatar-fallback", children: fallback })) }), _jsxs("span", { className: "dropdown-item-text", children: [_jsx("span", { className: "dropdown-item-label", children: item.label }), item.subtitle ? (_jsx("span", { className: "dropdown-item-subtitle", children: item.subtitle })) : null] }), item.onEdit || item.onDelete ? (_jsxs("span", { className: "dropdown-item-actions", children: [item.onEdit ? (_jsx(Button, { type: "button", variant: "ghost", className: "dropdown-action", "aria-label": `Edit ${item.label}`, onClick: (event) => {
|
|
120
|
+
event.stopPropagation();
|
|
121
|
+
item.onEdit?.();
|
|
122
|
+
setOpen(false);
|
|
123
|
+
}, children: _jsx(IconEdit, {}) })) : null, item.onDelete ? (_jsx(Button, { type: "button", variant: "delete", className: "dropdown-action is-delete", "aria-label": `Delete ${item.label}`, onClick: (event) => {
|
|
124
|
+
event.stopPropagation();
|
|
125
|
+
item.onDelete?.();
|
|
126
|
+
}, children: _jsx(IconTrash, {}) })) : null] })) : null] }, item.id ?? `${item.label}-${index}`));
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
return (_jsxs("div", { className: "user-section", ref: ref, "data-open": isOpen ? "true" : "false", children: [_jsxs("button", { className: "user-button", onClick: () => setOpen(!isOpen), type: "button", children: [_jsx("span", { className: "avatar", children: currentAvatar ? (_jsx("img", { src: currentAvatar, alt: props.avatarAlt ?? props.name, loading: "eager", decoding: "async", referrerPolicy: "no-referrer", crossOrigin: "anonymous", onError: () => {
|
|
130
|
+
if (avatarState === "primary" && fallbackAvatar) {
|
|
131
|
+
setAvatarState("fallback");
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
setAvatarState("none");
|
|
135
|
+
}
|
|
136
|
+
} })) : (_jsx("span", { className: "avatar-fallback", children: fallback })) }), _jsx("span", { className: "user-name", children: props.name }), _jsx(DefaultChevron, { open: isOpen })] }), _jsx("div", { className: "dropdown", "data-open": isOpen ? "true" : "false", children: renderUserListItems() })] }));
|
|
137
|
+
}
|
|
138
|
+
const { label, layout = "row", value, placeholder = "Select a saved connection", sections, onChange, renderTriggerIcon, renderItemIcon, triggerLabel, caret, emptyLabel = "No saved connections.", emptyClassName, } = props;
|
|
139
|
+
const [open, setOpen] = useState(false);
|
|
140
|
+
const ref = useRef(null);
|
|
141
|
+
const options = useMemo(() => sections.flatMap((section) => section.options), [sections]);
|
|
142
|
+
const active = options.find((item) => item.value === value) ?? null;
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
if (!open)
|
|
145
|
+
return;
|
|
146
|
+
const handlePointer = (event) => {
|
|
147
|
+
if (!ref.current)
|
|
148
|
+
return;
|
|
149
|
+
if (ref.current.contains(event.target))
|
|
150
|
+
return;
|
|
151
|
+
setOpen(false);
|
|
152
|
+
};
|
|
153
|
+
window.addEventListener("pointerdown", handlePointer);
|
|
154
|
+
return () => window.removeEventListener("pointerdown", handlePointer);
|
|
155
|
+
}, [open]);
|
|
156
|
+
const handleEllipsisTooltip = (event) => {
|
|
157
|
+
const target = event.currentTarget;
|
|
158
|
+
if (target.scrollWidth > target.clientWidth) {
|
|
159
|
+
target.setAttribute("title", target.textContent ?? "");
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
target.removeAttribute("title");
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
const clearEllipsisTooltip = (event) => {
|
|
166
|
+
event.currentTarget.removeAttribute("title");
|
|
167
|
+
};
|
|
168
|
+
const dropdownBody = (_jsxs("div", { className: `bookmark-dropdown ${open ? "open" : ""}`, children: [_jsxs("button", { className: "bookmark-trigger", onClick: () => setOpen((prev) => !prev), type: "button", children: [renderTriggerIcon ? _jsx("span", { className: "bookmark-icon", children: renderTriggerIcon }) : null, _jsx("span", { className: "bookmark-text", children: triggerLabel ?? (active ? active.label : placeholder) }), _jsx("span", { className: "bookmark-caret", children: caret ?? _jsx(DefaultCaret, {}) })] }), open ? (_jsx("div", { className: "bookmark-menu", children: options.length === 0 ? (_jsx("div", { className: ["bookmark-empty", emptyClassName].filter(Boolean).join(" "), children: emptyLabel })) : (sections.map((section) => (_jsxs("div", { children: [section.label ? _jsx("div", { className: "bookmark-group", children: section.label }) : null, section.options.map((item) => (_jsxs("button", { className: [
|
|
169
|
+
"bookmark-item",
|
|
170
|
+
item.value === value ? "active" : "",
|
|
171
|
+
item.className,
|
|
172
|
+
]
|
|
173
|
+
.filter(Boolean)
|
|
174
|
+
.join(" "), type: "button", onClick: () => {
|
|
175
|
+
onChange(item.value, item);
|
|
176
|
+
setOpen(false);
|
|
177
|
+
}, children: [renderItemIcon ? (_jsx("span", { className: "bookmark-icon", children: renderItemIcon(item) })) : null, _jsx("span", { className: "bookmark-text", onMouseEnter: handleEllipsisTooltip, onMouseLeave: clearEllipsisTooltip, children: item.label })] }, item.value)))] }, section.label ?? "options")))) })) : null] }));
|
|
178
|
+
return (_jsxs("div", { className: layout === "row" ? "bookmark-row" : "bookmark-field", ref: ref, children: [layout === "row" && label ? _jsx("div", { className: "bookmark-label", children: label }) : null, dropdownBody] }));
|
|
179
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
type FloatingFooterProps = {
|
|
3
|
+
title?: string;
|
|
4
|
+
subtitle?: string;
|
|
5
|
+
actions?: ReactNode;
|
|
6
|
+
className?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare const FloatingFooter: ({ title, subtitle, actions, className, }: FloatingFooterProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=FloatingFooter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FloatingFooter.d.ts","sourceRoot":"","sources":["../../src/components/FloatingFooter.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,KAAK,mBAAmB,GAAG;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,0CAK5B,mBAAmB,4CAWrB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Panel } from "./Panel";
|
|
3
|
+
export const FloatingFooter = ({ title = "Quick Actions", subtitle, actions, className, }) => {
|
|
4
|
+
const classes = ["ef-floating-footer", className].filter(Boolean).join(" ");
|
|
5
|
+
return (_jsxs(Panel, { variant: "full", borderWidth: 2, className: classes, children: [_jsxs("div", { className: "ef-footer-copy", children: [_jsx("div", { className: "ef-footer-title", children: title }), subtitle ? _jsx("div", { className: "ef-footer-subtitle", children: subtitle }) : null] }), _jsx("div", { className: "ef-footer-actions", children: actions })] }));
|
|
6
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
type FormFieldProps = {
|
|
3
|
+
label: string;
|
|
4
|
+
htmlFor?: string;
|
|
5
|
+
helper?: string;
|
|
6
|
+
error?: string;
|
|
7
|
+
required?: boolean;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
};
|
|
10
|
+
export declare const FormField: ({ label, htmlFor, helper, error, required, children, }: FormFieldProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=FormField.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormField.d.ts","sourceRoot":"","sources":["../../src/components/FormField.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,wDAOvB,cAAc,4CAUhB,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export const FormField = ({ label, htmlFor, helper, error, required, children, }) => (_jsxs("div", { className: ["ef-field", error ? "has-error" : ""].filter(Boolean).join(" "), children: [_jsxs("label", { className: "ef-field-label", htmlFor: htmlFor, children: [_jsx("span", { children: label }), required ? _jsx("span", { className: "ef-field-required", children: "*" }) : null] }), children, helper ? _jsx("div", { className: "ef-field-helper", children: helper }) : null, error ? _jsx("div", { className: "ef-field-error", children: error }) : null] }));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { InputHTMLAttributes, TextareaHTMLAttributes, SelectHTMLAttributes } from "react";
|
|
2
|
+
type BaseProps = {
|
|
3
|
+
className?: string;
|
|
4
|
+
};
|
|
5
|
+
type InputProps = InputHTMLAttributes<HTMLInputElement> & BaseProps;
|
|
6
|
+
type TextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement> & BaseProps;
|
|
7
|
+
type SelectProps = SelectHTMLAttributes<HTMLSelectElement> & BaseProps;
|
|
8
|
+
export declare const Input: ({ className, ...props }: InputProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare const Textarea: ({ className, ...props }: TextareaProps) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare const Select: ({ className, ...props }: SelectProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=Input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Input.d.ts","sourceRoot":"","sources":["../../src/components/Input.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE/F,KAAK,SAAS,GAAG;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,KAAK,UAAU,GAAG,mBAAmB,CAAC,gBAAgB,CAAC,GAAG,SAAS,CAAC;AACpE,KAAK,aAAa,GAAG,sBAAsB,CAAC,mBAAmB,CAAC,GAAG,SAAS,CAAC;AAC7E,KAAK,WAAW,GAAG,oBAAoB,CAAC,iBAAiB,CAAC,GAAG,SAAS,CAAC;AAIvE,eAAO,MAAM,KAAK,GAAI,yBAAyB,UAAU,4CAExD,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,yBAAyB,aAAa,4CAE9D,CAAC;AAEF,eAAO,MAAM,MAAM,GAAI,yBAAyB,WAAW,4CAE1D,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
const cx = (...classes) => classes.filter(Boolean).join(" ");
|
|
3
|
+
export const Input = ({ className, ...props }) => (_jsx("input", { ...props, className: cx("ef-input", className) }));
|
|
4
|
+
export const Textarea = ({ className, ...props }) => (_jsx("textarea", { ...props, className: cx("ef-textarea", className) }));
|
|
5
|
+
export const Select = ({ className, ...props }) => (_jsx("select", { ...props, className: cx("ef-select", className) }));
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { type HeaderMenuItem } from "./Dropdown";
|
|
3
|
+
type MainHeaderProps = {
|
|
4
|
+
title?: string;
|
|
5
|
+
subtitle?: string;
|
|
6
|
+
logoSrc?: string;
|
|
7
|
+
menus: HeaderMenuItem[];
|
|
8
|
+
menuOpen: string | null;
|
|
9
|
+
onOpenMenu: (id: string) => void;
|
|
10
|
+
onCloseMenu: () => void;
|
|
11
|
+
actions?: ReactNode;
|
|
12
|
+
};
|
|
13
|
+
export declare const MainHeader: ({ title, subtitle, logoSrc, menus, menuOpen, onOpenMenu, onCloseMenu, actions, }: MainHeaderProps) => import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=MainHeader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MainHeader.d.ts","sourceRoot":"","sources":["../../src/components/MainHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAG3D,KAAK,eAAe,GAAG;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,kFASxB,eAAe,4CA0BjB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Dropdown } from "./Dropdown";
|
|
3
|
+
import { Panel } from "./Panel";
|
|
4
|
+
export const MainHeader = ({ title = "Enderfall", subtitle = "Galaxy tools for creators", logoSrc, menus, menuOpen, onOpenMenu, onCloseMenu, actions, }) => {
|
|
5
|
+
return (_jsxs(Panel, { variant: "header", borderWidth: 2, className: "ef-main-header", children: [_jsxs("div", { className: "ef-header-left", children: [_jsxs("div", { className: "ef-brand", children: [logoSrc ? (_jsx("img", { src: logoSrc, alt: title, className: "ef-logo" })) : (_jsx("div", { className: "ef-logo-fallback", children: "E" })), _jsxs("div", { children: [_jsx("div", { className: "ef-brand-name", children: title }), subtitle ? _jsx("div", { className: "ef-tagline", children: subtitle }) : null] })] }), _jsx(Dropdown, { variant: "header", menus: menus, menuOpen: menuOpen, onOpenMenu: onOpenMenu, onCloseMenu: onCloseMenu })] }), _jsx("div", { className: "ef-header-actions", children: actions })] }));
|
|
6
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
type ModalSize = "default" | "wide";
|
|
3
|
+
type ModalProps = {
|
|
4
|
+
isOpen: boolean;
|
|
5
|
+
title: string;
|
|
6
|
+
subtitle?: string;
|
|
7
|
+
size?: ModalSize;
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
actions?: ReactNode;
|
|
10
|
+
headerActions?: ReactNode;
|
|
11
|
+
className?: string;
|
|
12
|
+
children?: ReactNode;
|
|
13
|
+
};
|
|
14
|
+
export declare const Modal: ({ isOpen, title, subtitle, size, onClose, actions, headerActions, className, children, }: ModalProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=Modal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../src/components/Modal.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAmD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGxF,KAAK,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;AAIpC,KAAK,UAAU,GAAG;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,KAAK,GAAI,0FAUnB,UAAU,mDAgHZ,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { Panel } from "./Panel";
|
|
4
|
+
const MODAL_TRANSITION_MS = 240;
|
|
5
|
+
export const Modal = ({ isOpen, title, subtitle, size = "default", onClose, actions, headerActions, className, children, }) => {
|
|
6
|
+
const reduceMotion = typeof document !== "undefined" && document.documentElement.getAttribute("data-reduce-motion") === "true";
|
|
7
|
+
const transitionMs = reduceMotion ? 0 : MODAL_TRANSITION_MS;
|
|
8
|
+
const [isRendered, setIsRendered] = useState(isOpen);
|
|
9
|
+
const [isEntering, setIsEntering] = useState(false);
|
|
10
|
+
const [isExiting, setIsExiting] = useState(false);
|
|
11
|
+
const [enterOffset, setEnterOffset] = useState({ x: 0, y: 0 });
|
|
12
|
+
const exitTimerRef = useRef(null);
|
|
13
|
+
const enterFrameRef = useRef(null);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
return () => {
|
|
16
|
+
if (exitTimerRef.current !== null)
|
|
17
|
+
window.clearTimeout(exitTimerRef.current);
|
|
18
|
+
if (enterFrameRef.current !== null)
|
|
19
|
+
window.cancelAnimationFrame(enterFrameRef.current);
|
|
20
|
+
};
|
|
21
|
+
}, []);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (isOpen) {
|
|
24
|
+
if (exitTimerRef.current !== null) {
|
|
25
|
+
window.clearTimeout(exitTimerRef.current);
|
|
26
|
+
exitTimerRef.current = null;
|
|
27
|
+
}
|
|
28
|
+
const activeElement = document.activeElement;
|
|
29
|
+
const activeRect = activeElement?.getBoundingClientRect();
|
|
30
|
+
const sourceX = activeRect ? activeRect.left + activeRect.width / 2 : window.innerWidth / 2;
|
|
31
|
+
const sourceY = activeRect ? activeRect.top + activeRect.height / 2 : window.innerHeight - 56;
|
|
32
|
+
const centerX = window.innerWidth / 2;
|
|
33
|
+
const centerY = window.innerHeight / 2;
|
|
34
|
+
setEnterOffset({
|
|
35
|
+
x: (sourceX - centerX) * 0.18,
|
|
36
|
+
y: (sourceY - centerY) * 0.22,
|
|
37
|
+
});
|
|
38
|
+
setIsRendered(true);
|
|
39
|
+
setIsExiting(false);
|
|
40
|
+
setIsEntering(!reduceMotion);
|
|
41
|
+
if (reduceMotion)
|
|
42
|
+
return;
|
|
43
|
+
if (enterFrameRef.current !== null)
|
|
44
|
+
window.cancelAnimationFrame(enterFrameRef.current);
|
|
45
|
+
enterFrameRef.current = window.requestAnimationFrame(() => {
|
|
46
|
+
enterFrameRef.current = window.requestAnimationFrame(() => {
|
|
47
|
+
setIsEntering(false);
|
|
48
|
+
enterFrameRef.current = null;
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!isRendered)
|
|
54
|
+
return;
|
|
55
|
+
if (reduceMotion) {
|
|
56
|
+
setIsEntering(false);
|
|
57
|
+
setIsExiting(false);
|
|
58
|
+
setIsRendered(false);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
setIsEntering(false);
|
|
62
|
+
setIsExiting(true);
|
|
63
|
+
if (exitTimerRef.current !== null)
|
|
64
|
+
window.clearTimeout(exitTimerRef.current);
|
|
65
|
+
exitTimerRef.current = window.setTimeout(() => {
|
|
66
|
+
setIsRendered(false);
|
|
67
|
+
setIsExiting(false);
|
|
68
|
+
exitTimerRef.current = null;
|
|
69
|
+
}, transitionMs);
|
|
70
|
+
}, [isOpen, isRendered, reduceMotion, transitionMs]);
|
|
71
|
+
if (!isRendered)
|
|
72
|
+
return null;
|
|
73
|
+
const sizeClass = size === "wide" ? "ef-modal--wide" : "";
|
|
74
|
+
const modalStateClass = isExiting ? "is-exiting" : isEntering ? "is-entering" : "is-open";
|
|
75
|
+
const backdropClasses = ["ef-modal-backdrop", modalStateClass].filter(Boolean).join(" ");
|
|
76
|
+
const classes = ["ef-modal", sizeClass, modalStateClass, className].filter(Boolean).join(" ");
|
|
77
|
+
const modalStyle = {
|
|
78
|
+
"--ef-modal-enter-x": `${enterOffset.x}px`,
|
|
79
|
+
"--ef-modal-enter-y": `${enterOffset.y}px`,
|
|
80
|
+
};
|
|
81
|
+
return (_jsx("div", { className: backdropClasses, onClick: onClose, children: _jsxs(Panel, { variant: "card", borderWidth: 2, className: classes, style: modalStyle, onClick: (event) => event.stopPropagation(), children: [_jsxs("div", { className: "ef-modal-header", children: [_jsx("div", { className: "ef-modal-title", children: title }), _jsxs("div", { className: "ef-modal-header-actions", children: [headerActions, _jsx("button", { className: "icon-action small ef-modal-close", onClick: onClose, type: "button", "aria-label": "Close", children: "\u00D7" })] })] }), subtitle ? _jsx("div", { className: "ef-modal-subtitle", children: subtitle }) : null, children ? _jsx("div", { className: "ef-modal-body", children: children }) : null, actions ? _jsx("div", { className: "ef-modal-actions", children: actions }) : null] }) }));
|
|
82
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type HTMLAttributes } from "react";
|
|
2
|
+
type PanelVariant = "card" | "highlight" | "full" | "header";
|
|
3
|
+
type BorderWidth = 1 | 2;
|
|
4
|
+
export declare const Panel: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
5
|
+
variant?: PanelVariant;
|
|
6
|
+
borderWidth?: BorderWidth;
|
|
7
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=Panel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Panel.d.ts","sourceRoot":"","sources":["../../src/components/Panel.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,OAAO,CAAC;AAExD,KAAK,YAAY,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,QAAQ,CAAC;AAC7D,KAAK,WAAW,GAAG,CAAC,GAAG,CAAC,CAAC;AAOzB,eAAO,MAAM,KAAK;cAJN,YAAY;kBACR,WAAW;kDAqB1B,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
export const Panel = forwardRef(({ variant = "card", borderWidth = 1, className, children, ...rest }, ref) => {
|
|
4
|
+
const variantClass = variant === "highlight"
|
|
5
|
+
? "ef-panel--highlight"
|
|
6
|
+
: variant === "header"
|
|
7
|
+
? "ef-panel--header"
|
|
8
|
+
: variant === "full"
|
|
9
|
+
? "ef-panel--full"
|
|
10
|
+
: "ef-panel--card";
|
|
11
|
+
const borderClass = borderWidth === 2 ? "ef-panel--border-2" : "ef-panel--border-1";
|
|
12
|
+
const classes = ["ef-panel", variantClass, borderClass, className].filter(Boolean).join(" ");
|
|
13
|
+
return (_jsx("div", { ref: ref, className: classes, ...rest, children: children }));
|
|
14
|
+
});
|
|
15
|
+
Panel.displayName = "Panel";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
type ThemeOption = {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
};
|
|
6
|
+
type PreferencesModalProps = {
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
themeMode: string;
|
|
10
|
+
onThemeChange: (value: string) => void;
|
|
11
|
+
themeOptions: ThemeOption[];
|
|
12
|
+
animationsEnabled: boolean;
|
|
13
|
+
onAnimationsChange: (enabled: boolean) => void;
|
|
14
|
+
note?: string;
|
|
15
|
+
children?: ReactNode;
|
|
16
|
+
};
|
|
17
|
+
export declare const PreferencesModal: ({ isOpen, onClose, themeMode, onThemeChange, themeOptions, animationsEnabled, onAnimationsChange, note, children, }: PreferencesModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=PreferencesModal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PreferencesModal.d.ts","sourceRoot":"","sources":["../../src/components/PreferencesModal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,KAAK,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpD,KAAK,qBAAqB,GAAG;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,qHAU9B,qBAAqB,4CAoCvB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from "./Button";
|
|
3
|
+
import { Dropdown } from "./Dropdown";
|
|
4
|
+
import { Toggle } from "./Toggle";
|
|
5
|
+
import { Modal } from "./Modal";
|
|
6
|
+
export const PreferencesModal = ({ isOpen, onClose, themeMode, onThemeChange, themeOptions, animationsEnabled, onAnimationsChange, note = "Syncs across all Enderfall apps.", children, }) => {
|
|
7
|
+
const selectedTheme = themeOptions.find((option) => option.value === themeMode) ?? themeOptions[0];
|
|
8
|
+
const dropdownLabel = selectedTheme?.label ?? "Select theme";
|
|
9
|
+
const dropdownItems = themeOptions.map((option) => ({
|
|
10
|
+
id: option.value,
|
|
11
|
+
label: option.label,
|
|
12
|
+
onClick: () => onThemeChange(option.value),
|
|
13
|
+
className: `theme-preview theme-preview--${option.value}`,
|
|
14
|
+
variant: "theme-preview",
|
|
15
|
+
}));
|
|
16
|
+
return (_jsxs(Modal, { isOpen: isOpen, onClose: onClose, title: "Preferences", subtitle: "Applies across Enderfall apps.", children: [_jsxs("div", { className: "ef-modal-form", children: [_jsxs("label", { children: ["Theme", _jsx("div", { className: "ef-modal-user-dropdown", children: _jsx(Dropdown, { variant: "user", name: dropdownLabel, items: dropdownItems }) })] }), _jsx(Toggle, { checked: animationsEnabled, onChange: (event) => onAnimationsChange(event.target.checked), label: "Enable animations" }), children, note ? _jsx("div", { className: "ef-modal-note", children: note }) : null] }), _jsx("div", { className: "ef-modal-actions", children: _jsx(Button, { variant: "primary", type: "button", onClick: onClose, children: "Close" }) })] }));
|
|
17
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type ReactNode, type MouseEvent } from "react";
|
|
2
|
+
type SideMenuProps = {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
resetKey?: string | number | boolean | null;
|
|
5
|
+
hoverDelayMs?: number;
|
|
6
|
+
initialDelayMs?: number;
|
|
7
|
+
onOpenChange?: (openId: string | null) => void;
|
|
8
|
+
};
|
|
9
|
+
export declare const SideMenu: ({ children, resetKey, hoverDelayMs, initialDelayMs, onOpenChange, }: SideMenuProps) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
type SideMenuTriggerProps = {
|
|
11
|
+
onClick: (event: MouseEvent) => void;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
"aria-expanded": boolean;
|
|
14
|
+
};
|
|
15
|
+
type SideMenuSubmenuProps = {
|
|
16
|
+
id: string;
|
|
17
|
+
trigger: (props: SideMenuTriggerProps) => ReactNode;
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
className?: string;
|
|
20
|
+
panelClassName?: string;
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
enableViewportFlip?: boolean;
|
|
23
|
+
onOpenChange?: (open: boolean) => void;
|
|
24
|
+
variant?: "default" | "header";
|
|
25
|
+
};
|
|
26
|
+
export declare const SideMenuSubmenu: ({ id, trigger, children, className, panelClassName, disabled, enableViewportFlip, onOpenChange, variant, }: SideMenuSubmenuProps) => import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=SideMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SideMenu.d.ts","sourceRoot":"","sources":["../../src/components/SideMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,SAAS,EACd,KAAK,UAAU,EAChB,MAAM,OAAO,CAAC;AAcf,KAAK,aAAa,GAAG;IACnB,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;CAChD,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,qEAMtB,aAAa,4CA8Df,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,SAAS,CAAC;IACpD,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;CAChC,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,4GAU7B,oBAAoB,4CA4GtB,CAAC"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
3
|
+
const SideMenuContext = createContext(null);
|
|
4
|
+
export const SideMenu = ({ children, resetKey, hoverDelayMs = 220, initialDelayMs = 180, onOpenChange, }) => {
|
|
5
|
+
const [openId, setOpenId] = useState(null);
|
|
6
|
+
const hoverTimerRef = useRef(null);
|
|
7
|
+
const openedAtRef = useRef(Date.now());
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
if (hoverTimerRef.current !== null) {
|
|
10
|
+
window.clearTimeout(hoverTimerRef.current);
|
|
11
|
+
hoverTimerRef.current = null;
|
|
12
|
+
}
|
|
13
|
+
setOpenId(null);
|
|
14
|
+
openedAtRef.current = Date.now();
|
|
15
|
+
}, [resetKey]);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
onOpenChange?.(openId);
|
|
18
|
+
}, [openId, onOpenChange]);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
return () => {
|
|
21
|
+
if (hoverTimerRef.current !== null) {
|
|
22
|
+
window.clearTimeout(hoverTimerRef.current);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}, []);
|
|
26
|
+
const scheduleOpen = (id) => {
|
|
27
|
+
if (hoverTimerRef.current !== null) {
|
|
28
|
+
window.clearTimeout(hoverTimerRef.current);
|
|
29
|
+
}
|
|
30
|
+
const elapsed = Date.now() - openedAtRef.current;
|
|
31
|
+
const settleDelay = elapsed < initialDelayMs ? initialDelayMs - elapsed : 0;
|
|
32
|
+
hoverTimerRef.current = window.setTimeout(() => {
|
|
33
|
+
setOpenId(id);
|
|
34
|
+
}, hoverDelayMs + settleDelay);
|
|
35
|
+
};
|
|
36
|
+
const clearHover = () => {
|
|
37
|
+
if (hoverTimerRef.current !== null) {
|
|
38
|
+
window.clearTimeout(hoverTimerRef.current);
|
|
39
|
+
hoverTimerRef.current = null;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const toggleOpen = (id) => {
|
|
43
|
+
setOpenId((prev) => (prev === id ? null : id));
|
|
44
|
+
};
|
|
45
|
+
const value = useMemo(() => ({
|
|
46
|
+
openId,
|
|
47
|
+
setOpenId,
|
|
48
|
+
scheduleOpen,
|
|
49
|
+
clearHover,
|
|
50
|
+
toggleOpen,
|
|
51
|
+
openedAt: openedAtRef.current,
|
|
52
|
+
initialDelayMs,
|
|
53
|
+
}), [openId, initialDelayMs]);
|
|
54
|
+
return _jsx(SideMenuContext.Provider, { value: value, children: children });
|
|
55
|
+
};
|
|
56
|
+
export const SideMenuSubmenu = ({ id, trigger, children, className, panelClassName, disabled, enableViewportFlip = false, onOpenChange, variant = "default", }) => {
|
|
57
|
+
const context = useContext(SideMenuContext);
|
|
58
|
+
if (!context) {
|
|
59
|
+
throw new Error("SideMenuSubmenu must be used within a SideMenu provider.");
|
|
60
|
+
}
|
|
61
|
+
const { openId, setOpenId, scheduleOpen, clearHover, toggleOpen, openedAt, initialDelayMs } = context;
|
|
62
|
+
const isOpen = openId === id;
|
|
63
|
+
const panelRef = useRef(null);
|
|
64
|
+
const containerRef = useRef(null);
|
|
65
|
+
const [panelStyle, setPanelStyle] = useState({});
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
onOpenChange?.(isOpen);
|
|
68
|
+
}, [isOpen, onOpenChange]);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (!isOpen || !enableViewportFlip) {
|
|
71
|
+
setPanelStyle({});
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const updatePosition = () => {
|
|
75
|
+
const panel = panelRef.current;
|
|
76
|
+
const container = containerRef.current;
|
|
77
|
+
if (!panel || !container)
|
|
78
|
+
return;
|
|
79
|
+
const panelRect = panel.getBoundingClientRect();
|
|
80
|
+
const containerRect = container.getBoundingClientRect();
|
|
81
|
+
const margin = 12;
|
|
82
|
+
const defaultLeft = containerRect.right + 6;
|
|
83
|
+
const flippedLeft = containerRect.left - panelRect.width - 6;
|
|
84
|
+
let left = defaultLeft;
|
|
85
|
+
if (defaultLeft + panelRect.width > window.innerWidth - margin && flippedLeft >= margin) {
|
|
86
|
+
left = flippedLeft;
|
|
87
|
+
}
|
|
88
|
+
left = Math.min(Math.max(left, margin), window.innerWidth - margin - panelRect.width);
|
|
89
|
+
let top = containerRect.top;
|
|
90
|
+
top = Math.min(Math.max(top, margin), window.innerHeight - margin - panelRect.height);
|
|
91
|
+
setPanelStyle({
|
|
92
|
+
left: left - containerRect.left,
|
|
93
|
+
top: top - containerRect.top,
|
|
94
|
+
right: "auto",
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
const raf = requestAnimationFrame(updatePosition);
|
|
98
|
+
window.addEventListener("resize", updatePosition);
|
|
99
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
100
|
+
return () => {
|
|
101
|
+
cancelAnimationFrame(raf);
|
|
102
|
+
window.removeEventListener("resize", updatePosition);
|
|
103
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
104
|
+
};
|
|
105
|
+
}, [isOpen, enableViewportFlip]);
|
|
106
|
+
const handlePointerEnter = () => {
|
|
107
|
+
if (disabled || isOpen)
|
|
108
|
+
return;
|
|
109
|
+
if (Date.now() - openedAt < initialDelayMs)
|
|
110
|
+
return;
|
|
111
|
+
setOpenId(null);
|
|
112
|
+
scheduleOpen(id);
|
|
113
|
+
};
|
|
114
|
+
const handlePointerLeave = () => {
|
|
115
|
+
clearHover();
|
|
116
|
+
};
|
|
117
|
+
const handlePointerMove = () => {
|
|
118
|
+
if (disabled || isOpen)
|
|
119
|
+
return;
|
|
120
|
+
if (Date.now() - openedAt < initialDelayMs)
|
|
121
|
+
return;
|
|
122
|
+
scheduleOpen(id);
|
|
123
|
+
};
|
|
124
|
+
const handleClick = (event) => {
|
|
125
|
+
if (disabled)
|
|
126
|
+
return;
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
if (isOpen)
|
|
129
|
+
return;
|
|
130
|
+
setOpenId(id);
|
|
131
|
+
};
|
|
132
|
+
return (_jsxs("div", { className: [
|
|
133
|
+
className,
|
|
134
|
+
isOpen ? "is-open" : "",
|
|
135
|
+
variant === "header" ? "side-menu--header" : "",
|
|
136
|
+
]
|
|
137
|
+
.filter(Boolean)
|
|
138
|
+
.join(" "), "data-submenu-id": id, ref: containerRef, onPointerEnter: handlePointerEnter, onPointerLeave: handlePointerLeave, onPointerMove: handlePointerMove, children: [trigger({ onClick: handleClick, disabled, "aria-expanded": isOpen }), _jsx("div", { ref: panelRef, className: [
|
|
139
|
+
panelClassName,
|
|
140
|
+
isOpen ? "is-open" : "",
|
|
141
|
+
]
|
|
142
|
+
.filter(Boolean)
|
|
143
|
+
.join(" "), style: panelStyle, children: children })] }));
|
|
144
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type InputHTMLAttributes } from "react";
|
|
2
|
+
type SliderProps = Omit<InputHTMLAttributes<HTMLInputElement>, "type">;
|
|
3
|
+
export declare const Slider: ({ className, ...props }: SliderProps) => import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
export {};
|
|
5
|
+
//# sourceMappingURL=Slider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Slider.d.ts","sourceRoot":"","sources":["../../src/components/Slider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAEtE,KAAK,WAAW,GAAG,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC,CAAC;AAKvE,eAAO,MAAM,MAAM,GAAI,yBAAyB,WAAW,4CAyB1D,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
const getTheme = () => document.documentElement.dataset.theme || document.body.dataset.theme || "";
|
|
4
|
+
export const Slider = ({ className, ...props }) => {
|
|
5
|
+
const [theme, setTheme] = useState("");
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
setTheme(getTheme());
|
|
8
|
+
const observer = new MutationObserver(() => setTheme(getTheme()));
|
|
9
|
+
observer.observe(document.documentElement, {
|
|
10
|
+
attributes: true,
|
|
11
|
+
attributeFilter: ["data-theme"],
|
|
12
|
+
});
|
|
13
|
+
observer.observe(document.body, {
|
|
14
|
+
attributes: true,
|
|
15
|
+
attributeFilter: ["data-theme"],
|
|
16
|
+
});
|
|
17
|
+
return () => observer.disconnect();
|
|
18
|
+
}, []);
|
|
19
|
+
return (_jsx("input", { type: "range", className: ["ef-slider", className].filter(Boolean).join(" "), "data-ef-theme": theme || undefined, ...props }));
|
|
20
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from "react";
|
|
2
|
+
type Variant = "games" | "mods" | "servers" | "apps";
|
|
3
|
+
type Align = "center" | "left";
|
|
4
|
+
type Tone = "default" | "plain";
|
|
5
|
+
type Action = {
|
|
6
|
+
label: string;
|
|
7
|
+
href?: string;
|
|
8
|
+
onClick?: () => void;
|
|
9
|
+
};
|
|
10
|
+
type StackedCardProps = {
|
|
11
|
+
title?: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
tags?: string[];
|
|
14
|
+
imageUrl?: string | null;
|
|
15
|
+
variant?: Variant;
|
|
16
|
+
action?: Action;
|
|
17
|
+
align?: Align;
|
|
18
|
+
tone?: Tone;
|
|
19
|
+
showImage?: boolean;
|
|
20
|
+
children?: ReactNode;
|
|
21
|
+
frameClassName?: string;
|
|
22
|
+
cardClassName?: string;
|
|
23
|
+
bodyClassName?: string;
|
|
24
|
+
id?: string;
|
|
25
|
+
style?: CSSProperties;
|
|
26
|
+
};
|
|
27
|
+
export declare const StackedCard: ({ title, description, tags, imageUrl, variant, action, align, tone, showImage, children, frameClassName, cardClassName, bodyClassName, id, style, }: StackedCardProps) => import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=StackedCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StackedCard.d.ts","sourceRoot":"","sources":["../../src/components/StackedCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD,KAAK,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AACrD,KAAK,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAC/B,KAAK,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC;AAEhC,KAAK,MAAM,GAAG;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,CAAC;AAoBF,eAAO,MAAM,WAAW,GAAI,qJAgBzB,gBAAgB,4CA4DlB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
const variantClass = (variant) => {
|
|
3
|
+
switch (variant) {
|
|
4
|
+
case "games":
|
|
5
|
+
return "ef-stacked-variant-games";
|
|
6
|
+
case "mods":
|
|
7
|
+
return "ef-stacked-variant-mods";
|
|
8
|
+
case "servers":
|
|
9
|
+
return "ef-stacked-variant-servers";
|
|
10
|
+
case "apps":
|
|
11
|
+
return "ef-stacked-variant-apps";
|
|
12
|
+
default:
|
|
13
|
+
return "";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const alignClass = (align) => (align === "left" ? "ef-stacked-body-left" : "");
|
|
17
|
+
const toneClass = (tone) => (tone === "plain" ? "ef-stacked-body-plain" : "ef-stacked-body");
|
|
18
|
+
export const StackedCard = ({ title = "", description = "", tags, imageUrl, variant, action, align = "center", tone = "default", showImage = true, children, frameClassName, cardClassName, bodyClassName, id, style, }) => {
|
|
19
|
+
const bodyClasses = [toneClass(tone), alignClass(align), bodyClassName]
|
|
20
|
+
.filter(Boolean)
|
|
21
|
+
.join(" ");
|
|
22
|
+
return (_jsx("article", { className: ["ef-stacked-frame", variantClass(variant), frameClassName].filter(Boolean).join(" "), id: id, style: style, children: _jsxs("div", { className: ["ef-stacked-card", cardClassName].filter(Boolean).join(" "), children: [showImage ? (_jsx("div", { className: ["ef-stacked-image", imageUrl ? "ef-stacked-image-photo" : ""]
|
|
23
|
+
.filter(Boolean)
|
|
24
|
+
.join(" "), style: imageUrl
|
|
25
|
+
? {
|
|
26
|
+
backgroundImage: `linear-gradient(180deg, rgba(10, 12, 22, 0.1), rgba(10, 12, 22, 0.85)), url(${imageUrl})`,
|
|
27
|
+
backgroundSize: "cover",
|
|
28
|
+
backgroundPosition: "center",
|
|
29
|
+
}
|
|
30
|
+
: undefined })) : null, _jsxs("div", { className: bodyClasses, children: [children ? (children) : (_jsxs(_Fragment, { children: [_jsx("h3", { children: title }), _jsx("p", { children: description }), tags?.length ? (_jsx("div", { className: `ef-stacked-tags ${align === "left" ? "ef-stacked-tags-left" : ""}`, children: tags.map((tag) => (_jsx("span", { className: "ef-stacked-tag", children: tag }, tag))) })) : null] })), action ? (action.href ? (_jsx("a", { className: "ef-stacked-action", href: action.href, children: action.label })) : (_jsx("button", { className: "ef-stacked-action", type: "button", onClick: action.onClick, children: action.label }))) : null] })] }) }));
|
|
31
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type HTMLAttributes } from "react";
|
|
2
|
+
type StatDotsProps = HTMLAttributes<HTMLDivElement> & {
|
|
3
|
+
label: string;
|
|
4
|
+
count?: number;
|
|
5
|
+
value?: number;
|
|
6
|
+
defaultValue?: number;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
onChange?: (value: number) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare const StatDots: ({ label, count, value, defaultValue, disabled, onChange, className, ...rest }: StatDotsProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=StatDots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatDots.d.ts","sourceRoot":"","sources":["../../src/components/StatDots.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,OAAO,CAAC;AAGtD,KAAK,aAAa,GAAG,cAAc,CAAC,cAAc,CAAC,GAAG;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,+EAStB,aAAa,4CA+Bf,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Panel } from "./Panel";
|
|
4
|
+
export const StatDots = ({ label, count = 4, value, defaultValue = 0, disabled = false, onChange, className, ...rest }) => {
|
|
5
|
+
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
6
|
+
const currentValue = Math.max(0, Math.min(count, value ?? internalValue));
|
|
7
|
+
const handleSelect = (nextValue) => {
|
|
8
|
+
if (disabled)
|
|
9
|
+
return;
|
|
10
|
+
const resolvedValue = nextValue === currentValue ? 0 : nextValue;
|
|
11
|
+
if (value === undefined) {
|
|
12
|
+
setInternalValue(resolvedValue);
|
|
13
|
+
}
|
|
14
|
+
onChange?.(resolvedValue);
|
|
15
|
+
};
|
|
16
|
+
const classes = ["ef-stat-dots", className].filter(Boolean).join(" ");
|
|
17
|
+
return (_jsxs("div", { className: classes, ...rest, children: [_jsx(Panel, { variant: "highlight", borderWidth: 1, className: "ef-stat-dots__label", children: label }), _jsx("div", { className: "ef-stat-dots__dots", role: "group", "aria-label": label, children: Array.from({ length: count }, (_, index) => (_jsx("input", { className: "ef-stat-dots__input", type: "checkbox", checked: index < currentValue, disabled: disabled, onChange: () => handleSelect(index + 1) }, `${label}-${index}`))) })] }));
|
|
18
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { InputHTMLAttributes } from "react";
|
|
2
|
+
type ToggleProps = Omit<InputHTMLAttributes<HTMLInputElement>, "type"> & {
|
|
3
|
+
label?: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
variant?: "switch" | "checkbox";
|
|
6
|
+
};
|
|
7
|
+
export declare const Toggle: ({ label, description, className, variant, ...props }: ToggleProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=Toggle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Toggle.d.ts","sourceRoot":"","sources":["../../src/components/Toggle.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAEjD,KAAK,WAAW,GAAG,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC,GAAG;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;CACjC,CAAC;AAEF,eAAO,MAAM,MAAM,GAAI,sDAMpB,WAAW,4CAuBb,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export const Toggle = ({ label, description, className, variant = "switch", ...props }) => (_jsxs("label", { className: ["ef-toggle", `ef-toggle--${variant}`, className]
|
|
3
|
+
.filter(Boolean)
|
|
4
|
+
.join(" "), children: [_jsx("input", { type: "checkbox", className: "ef-toggle-input", ...props }), variant === "switch" ? (_jsx("span", { className: "ef-toggle-track", "aria-hidden": "true", children: _jsx("span", { className: "ef-toggle-thumb" }) })) : (_jsx("span", { className: "ef-toggle-box", "aria-hidden": "true", children: _jsx("span", { className: "ef-toggle-check" }) })), label ? (_jsxs("span", { className: "ef-toggle-label", children: [_jsx("span", { className: "ef-toggle-text", children: label }), description ? _jsx("span", { className: "ef-toggle-description", children: description }) : null] })) : null] }));
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type ThemeMode = string;
|
|
2
|
+
export { AccessGate } from "./components/AccessGate";
|
|
3
|
+
export { Button } from "./components/Button";
|
|
4
|
+
export { FormField } from "./components/FormField";
|
|
5
|
+
export { Input, Textarea, Select } from "./components/Input";
|
|
6
|
+
export { Panel } from "./components/Panel";
|
|
7
|
+
export { StackedCard } from "./components/StackedCard";
|
|
8
|
+
export { Dropdown } from "./components/Dropdown";
|
|
9
|
+
export { MainHeader } from "./components/MainHeader";
|
|
10
|
+
export { FloatingFooter } from "./components/FloatingFooter";
|
|
11
|
+
export { Modal } from "./components/Modal";
|
|
12
|
+
export { PreferencesModal } from "./components/PreferencesModal";
|
|
13
|
+
export { Toggle } from "./components/Toggle";
|
|
14
|
+
export { Slider } from "./components/Slider";
|
|
15
|
+
export { StatDots } from "./components/StatDots";
|
|
16
|
+
export { SideMenu, SideMenuSubmenu } from "./components/SideMenu";
|
|
17
|
+
type ThemeOptions<T extends ThemeMode> = {
|
|
18
|
+
storageKey: string;
|
|
19
|
+
defaultTheme: T;
|
|
20
|
+
allowed: readonly T[];
|
|
21
|
+
dataAttribute?: string;
|
|
22
|
+
bodyClass?: string | null;
|
|
23
|
+
};
|
|
24
|
+
export declare const getStoredTheme: <T extends ThemeMode>(options: ThemeOptions<T>) => T;
|
|
25
|
+
export declare const applyTheme: <T extends ThemeMode>(theme: T, options: ThemeOptions<T>) => void;
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAElE,KAAK,YAAY,CAAC,CAAC,SAAS,SAAS,IAAI;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,CAAC,CAAC;IAChB,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,SAAS,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,MAS3E,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,SAAS,EAAE,OAAO,CAAC,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,SAOjF,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export { AccessGate } from "./components/AccessGate";
|
|
2
|
+
export { Button } from "./components/Button";
|
|
3
|
+
export { FormField } from "./components/FormField";
|
|
4
|
+
export { Input, Textarea, Select } from "./components/Input";
|
|
5
|
+
export { Panel } from "./components/Panel";
|
|
6
|
+
export { StackedCard } from "./components/StackedCard";
|
|
7
|
+
export { Dropdown } from "./components/Dropdown";
|
|
8
|
+
export { MainHeader } from "./components/MainHeader";
|
|
9
|
+
export { FloatingFooter } from "./components/FloatingFooter";
|
|
10
|
+
export { Modal } from "./components/Modal";
|
|
11
|
+
export { PreferencesModal } from "./components/PreferencesModal";
|
|
12
|
+
export { Toggle } from "./components/Toggle";
|
|
13
|
+
export { Slider } from "./components/Slider";
|
|
14
|
+
export { StatDots } from "./components/StatDots";
|
|
15
|
+
export { SideMenu, SideMenuSubmenu } from "./components/SideMenu";
|
|
16
|
+
export const getStoredTheme = (options) => {
|
|
17
|
+
const stored = localStorage.getItem(options.storageKey);
|
|
18
|
+
if (stored && options.allowed.includes(stored)) {
|
|
19
|
+
return stored;
|
|
20
|
+
}
|
|
21
|
+
if (stored === "dark" && options.allowed.includes("galaxy")) {
|
|
22
|
+
return "galaxy";
|
|
23
|
+
}
|
|
24
|
+
return options.defaultTheme;
|
|
25
|
+
};
|
|
26
|
+
export const applyTheme = (theme, options) => {
|
|
27
|
+
const dataAttribute = options.dataAttribute ?? "data-theme";
|
|
28
|
+
if (options.bodyClass) {
|
|
29
|
+
document.body.classList.toggle(options.bodyClass, theme === options.defaultTheme);
|
|
30
|
+
}
|
|
31
|
+
document.documentElement.setAttribute(dataAttribute, theme);
|
|
32
|
+
localStorage.setItem(options.storageKey, theme);
|
|
33
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@enderfall/ui",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"styles.css"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc -p tsconfig.build.json",
|
|
14
|
+
"dev": "tsc -p tsconfig.build.json --watch"
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./styles.css": "./styles.css"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"react": "^18.2.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/react": "^18.2.43",
|
|
28
|
+
"typescript": "^5.3.3"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/styles.css
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap");
|
|
2
|
+
@import "./src/base.css";
|
|
3
|
+
@import "./src/theme.css";
|
|
4
|
+
@import "./src/components/BookmarkDropdown.css";
|
|
5
|
+
@import "./src/components/AccessGate.css";
|
|
6
|
+
@import "./src/components/Button.css";
|
|
7
|
+
@import "./src/components/Input.css";
|
|
8
|
+
@import "./src/components/Toggle.css";
|
|
9
|
+
@import "./src/components/Slider.css";
|
|
10
|
+
@import "./src/components/StackedCard.css";
|
|
11
|
+
@import "./src/components/Panel.css";
|
|
12
|
+
@import "./src/components/Modal.css";
|
|
13
|
+
@import "./src/components/HeaderMenu.css";
|
|
14
|
+
@import "./src/components/MainHeader.css";
|
|
15
|
+
@import "./src/components/FloatingFooter.css";
|
|
16
|
+
@import "./src/components/UserMenu.css";
|
|
17
|
+
@import "./src/components/StatDots.css";
|