@hopgoldy/cube-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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 HoPGoldy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # @hopgoldy/cube-ui
2
+
3
+ Shared responsive application shell and UI primitives for Cube applications.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add @hopgoldy/cube-ui
9
+ ```
10
+
11
+ Import styles once in your app entry:
12
+
13
+ ```ts
14
+ import "@hopgoldy/cube-ui/styles.css";
15
+ ```
16
+
17
+ ## Example
18
+
19
+ ```tsx
20
+ // the app's top-level layout (in place of a hand-rolled AppContainer + theme provider)
21
+ import { CubeApp } from "@hopgoldy/cube-ui";
22
+ import { Outlet } from "react-router-dom";
23
+
24
+ export const AppContainer = () => (
25
+ <CubeApp
26
+ sidebarTop={<Logo />}
27
+ sidebarList={<nav>Sidebar links</nav>}
28
+ headerLeft="Cube App"
29
+ headerSearchBtnLabel="搜索"
30
+ onHeaderSearchBtnClick={() => openSearch()}
31
+ accountMenuStats={[{ key: "pages", title: "页面", value: 3 }]}
32
+ accountMenuItems={[{ key: "settings", label: "设置", onClick: () => navigate("/settings") }]}
33
+ onLogoutBtnClick={() => logout()}
34
+ userName={currentUser.name}
35
+ about={{ name: "Cube App", version: "1.0.0", repository: "https://github.com/you/app" }}
36
+ >
37
+ <Outlet />
38
+ </CubeApp>
39
+ );
40
+ ```
41
+
42
+ ```tsx
43
+ // any page that needs a mobile settings entry point
44
+ import { MobileAccountSheet } from "@hopgoldy/cube-ui";
45
+
46
+ const [visible, setVisible] = useState(false);
47
+ <MobileAccountSheet visible={visible} onVisibleChange={setVisible} />;
48
+ ```
49
+
50
+ ```tsx
51
+ // any page rendered inside CubeApp's children
52
+ import { ActionButton, ActionIcon, CubePage } from "@hopgoldy/cube-ui";
53
+ import { SettingOutlined } from "@ant-design/icons";
54
+
55
+ const HomePage = () => (
56
+ <CubePage
57
+ desktopHeaderLeft="首页"
58
+ mobileActionBar={
59
+ <>
60
+ <ActionIcon icon={<SettingOutlined />} aria-label="打开设置" />
61
+ <ActionButton>操作</ActionButton>
62
+ </>
63
+ }
64
+ >
65
+ page content
66
+ </CubePage>
67
+ );
68
+ ```
69
+
70
+ `CubeApp` is the single component every app configures: it composes `CubeConfigProvider`, renders the desktop shell (collapsible sidebar, header, account popover), and exposes the account/about configuration through context so `MobileAccountSheet` — a mobile-only settings drawer mounted by whichever page needs it, reached through the router's `<Outlet/>` — renders from the same data without taking it as props. Mobile has no persistent header or sidebar, matching how Cube apps actually behave today — mobile pages are bare content plus whatever action bar the page itself renders.
71
+
72
+ `CubeConfigProvider` applies the fixed Cube antd configuration (`zh_CN` locale, `primaryColor` token, a larger base font size on mobile) without rendering any shell. Mount it on its own for pages that should follow the Cube theme but live outside `CubeApp` — no configuration needed:
73
+
74
+ ```tsx
75
+ // a themed page outside the CubeApp shell (login, share, embedded views, ...)
76
+ import { CubeConfigProvider } from "@hopgoldy/cube-ui";
77
+
78
+ export const LoginPage = () => (
79
+ <CubeConfigProvider>
80
+ <LoginForm />
81
+ </CubeConfigProvider>
82
+ );
83
+ ```
84
+
85
+ `CubePage` is the single page-level container every page renders: it lays out scrollable content and an optional mobile-only action bar, and lets the page override `CubeApp`'s header-left slot via `desktopHeaderLeft` for as long as it stays mounted. `ActionIcon` and `ActionButton` are the shared mobile action-bar primitives, matching the pattern already used in cube-xxx apps.
86
+
87
+ This package is intentionally UI-only. It does not depend on routers, HTTP clients, application stores, or authentication logic.
package/dist/index.css ADDED
@@ -0,0 +1,284 @@
1
+ /* src/styles.css */
2
+ :root {
3
+ --cube-header-height: 3rem;
4
+ --cube-sidebar-width: 15rem;
5
+ --cube-mobile-action-height: 40px;
6
+ --cube-border-color: #d1d5db;
7
+ --cube-dark-border-color: #374151;
8
+ --cube-surface-color: #ffffff;
9
+ --cube-dark-surface-color: #141414;
10
+ }
11
+
12
+ /* src/app-header/app-header.module.css */
13
+ .root {
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: space-between;
17
+ width: 100%;
18
+ height: var(--cube-header-height, 3rem);
19
+ padding: 0.5rem 1rem;
20
+ overflow: hidden;
21
+ box-sizing: border-box;
22
+ color: inherit;
23
+ background: var(--cube-surface-color, #fff);
24
+ border-bottom: 1px solid var(--cube-border-color, #d1d5db);
25
+ transition:
26
+ color 200ms ease,
27
+ background-color 200ms ease,
28
+ border-color 200ms ease;
29
+ }
30
+ :global(.dark) .root,
31
+ :global([data-theme=dark]) .root {
32
+ background: var(--cube-dark-surface-color, #141414);
33
+ border-bottom-color: var(--cube-dark-border-color, #374151);
34
+ }
35
+ .leading {
36
+ display: flex;
37
+ flex: 1 1 auto;
38
+ align-items: center;
39
+ min-width: 0;
40
+ overflow: hidden;
41
+ }
42
+ .toggle {
43
+ display: inline-flex;
44
+ flex: 0 0 auto;
45
+ align-items: center;
46
+ justify-content: center;
47
+ padding: 0;
48
+ margin: 0 1rem 0 0;
49
+ font: inherit;
50
+ font-size: 1.25rem;
51
+ color: inherit;
52
+ cursor: pointer;
53
+ background: transparent;
54
+ border: 0;
55
+ }
56
+ .toggle:focus-visible {
57
+ outline: 2px solid #1677ff;
58
+ outline-offset: 3px;
59
+ border-radius: 2px;
60
+ }
61
+ .title {
62
+ min-width: 0;
63
+ overflow: hidden;
64
+ text-overflow: ellipsis;
65
+ white-space: nowrap;
66
+ }
67
+ .actions {
68
+ display: flex;
69
+ flex: 0 0 auto;
70
+ align-items: center;
71
+ gap: 0.5rem;
72
+ margin-left: 0.5rem;
73
+ }
74
+
75
+ /* src/account-menu/account-menu.module.css */
76
+ .trigger {
77
+ display: inline-flex;
78
+ align-items: center;
79
+ justify-content: center;
80
+ padding: 0.25rem;
81
+ margin: 0;
82
+ font: inherit;
83
+ font-size: 1.25rem;
84
+ line-height: 1;
85
+ color: inherit;
86
+ cursor: pointer;
87
+ background: transparent;
88
+ border: 0;
89
+ }
90
+ .trigger:focus-visible {
91
+ outline: 2px solid #1677ff;
92
+ outline-offset: 3px;
93
+ border-radius: 50%;
94
+ }
95
+ .menu {
96
+ width: 16rem;
97
+ max-width: calc(100vw - 2rem);
98
+ }
99
+ .stats {
100
+ display: flex;
101
+ justify-content: space-around;
102
+ gap: 1rem;
103
+ margin-bottom: 1rem;
104
+ }
105
+ .items {
106
+ display: flex;
107
+ flex-direction: column;
108
+ gap: 0.5rem;
109
+ }
110
+ .footer {
111
+ margin-top: 0.5rem;
112
+ }
113
+
114
+ /* src/about-dialog/about-dialog.module.css */
115
+ .body {
116
+ margin-bottom: 1rem;
117
+ }
118
+ .description {
119
+ margin: 1rem 0 0.5rem;
120
+ font-size: 1rem;
121
+ line-height: 1.5;
122
+ }
123
+ .item {
124
+ padding: 0.5rem;
125
+ color: #6b7280;
126
+ background: #f3f4f6;
127
+ border-radius: 0.375rem;
128
+ }
129
+ :global(.dark) .item,
130
+ :global([data-theme=dark]) .item {
131
+ color: #e5e7eb;
132
+ background: #262626;
133
+ }
134
+ .itemLabel {
135
+ color: inherit;
136
+ white-space: nowrap;
137
+ }
138
+ .itemValue {
139
+ min-width: 0;
140
+ overflow: hidden;
141
+ text-align: right;
142
+ text-overflow: ellipsis;
143
+ white-space: nowrap;
144
+ }
145
+ .link {
146
+ color: inherit;
147
+ text-decoration: none;
148
+ }
149
+ .poweredBy {
150
+ color: #6b7280;
151
+ }
152
+ :global(.dark) .poweredBy,
153
+ :global([data-theme=dark]) .poweredBy {
154
+ color: #e5e7eb;
155
+ }
156
+
157
+ /* src/cube-app/cube-app.module.css */
158
+ .root {
159
+ display: flex;
160
+ width: 100%;
161
+ height: 100%;
162
+ overflow: hidden;
163
+ }
164
+ .sidebar {
165
+ display: flex;
166
+ flex-direction: column;
167
+ flex-shrink: 0;
168
+ overflow: hidden;
169
+ transition: width 200ms ease;
170
+ }
171
+ .sidebarTop,
172
+ .sidebarBottom {
173
+ flex-shrink: 0;
174
+ }
175
+ .sidebarList {
176
+ flex: 1 1 auto;
177
+ min-height: 0;
178
+ overflow-y: auto;
179
+ }
180
+ .desktopArea {
181
+ flex-grow: 1;
182
+ min-width: 0;
183
+ height: 100%;
184
+ overflow: hidden;
185
+ transition: width 200ms ease;
186
+ }
187
+ .content {
188
+ height: calc(100% - var(--cube-header-height, 3rem) - 1px);
189
+ overflow-y: auto;
190
+ }
191
+ .searchButton {
192
+ width: 15rem;
193
+ }
194
+ .mobileRoot {
195
+ width: 100vw;
196
+ height: 100%;
197
+ overflow: hidden;
198
+ }
199
+ .mobileContent {
200
+ width: 100%;
201
+ height: 100%;
202
+ overflow-y: auto;
203
+ }
204
+
205
+ /* src/mobile-account-sheet/mobile-account-sheet.module.css */
206
+ .card {
207
+ padding: 0.5rem 0.75rem;
208
+ background: var(--cube-surface-color, #fff);
209
+ border: 1px solid var(--cube-border-color, #d1d5db);
210
+ border-radius: 0.5rem;
211
+ }
212
+ :global(.dark) .card,
213
+ :global([data-theme=dark]) .card {
214
+ background: var(--cube-dark-surface-color, #141414);
215
+ border-color: var(--cube-dark-border-color, #374151);
216
+ }
217
+ .cell {
218
+ display: flex;
219
+ align-items: center;
220
+ justify-content: space-between;
221
+ width: 100%;
222
+ padding: 0.375rem 0;
223
+ font: inherit;
224
+ color: inherit;
225
+ text-align: left;
226
+ cursor: pointer;
227
+ background: transparent;
228
+ border: 0;
229
+ }
230
+ .splitLine {
231
+ height: 1px;
232
+ margin: 0.25rem 0;
233
+ background: var(--cube-border-color, #d1d5db);
234
+ }
235
+ :global(.dark) .splitLine,
236
+ :global([data-theme=dark]) .splitLine {
237
+ background: var(--cube-dark-border-color, #374151);
238
+ }
239
+
240
+ /* src/loading/loading.module.css */
241
+ .pageLoading {
242
+ width: 100%;
243
+ height: 100%;
244
+ }
245
+ .delayedLoading {
246
+ display: flex;
247
+ align-items: center;
248
+ justify-content: center;
249
+ width: 100%;
250
+ margin: 1.5rem 0;
251
+ }
252
+ :global(.dark) .delayedLoading,
253
+ :global([data-theme=dark]) .delayedLoading {
254
+ color: #9ca3af;
255
+ }
256
+
257
+ /* src/page/page.module.css */
258
+ .container {
259
+ width: 100%;
260
+ height: 100%;
261
+ overflow: hidden;
262
+ }
263
+ .content {
264
+ position: relative;
265
+ height: 100%;
266
+ overflow-y: auto;
267
+ }
268
+ @media (max-width: 767px) {
269
+ .content {
270
+ height: calc(100% - var(--cube-mobile-action-height, 40px) - 1rem);
271
+ }
272
+ }
273
+ .actionBar {
274
+ display: flex;
275
+ flex-direction: row;
276
+ align-items: center;
277
+ gap: 0.5rem;
278
+ height: var(--cube-mobile-action-height, 40px);
279
+ padding: 0.5rem;
280
+ }
281
+ .actionIcon {
282
+ flex-shrink: 0;
283
+ }
284
+ /*# sourceMappingURL=index.css.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/styles.css","../src/app-header/app-header.module.css","../src/account-menu/account-menu.module.css","../src/about-dialog/about-dialog.module.css","../src/cube-app/cube-app.module.css","../src/mobile-account-sheet/mobile-account-sheet.module.css","../src/loading/loading.module.css","../src/page/page.module.css"],"sourcesContent":[":root {\n --cube-header-height: 3rem;\n --cube-sidebar-width: 15rem;\n --cube-mobile-action-height: 40px;\n --cube-border-color: #d1d5db;\n --cube-dark-border-color: #374151;\n --cube-surface-color: #ffffff;\n --cube-dark-surface-color: #141414;\n}\n",".root {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n height: var(--cube-header-height, 3rem);\n padding: 0.5rem 1rem;\n overflow: hidden;\n box-sizing: border-box;\n color: inherit;\n background: var(--cube-surface-color, #fff);\n border-bottom: 1px solid var(--cube-border-color, #d1d5db);\n transition: color 200ms ease, background-color 200ms ease,\n border-color 200ms ease;\n}\n\n:global(.dark) .root,\n:global([data-theme=\"dark\"]) .root {\n background: var(--cube-dark-surface-color, #141414);\n border-bottom-color: var(--cube-dark-border-color, #374151);\n}\n\n.leading {\n display: flex;\n flex: 1 1 auto;\n align-items: center;\n min-width: 0;\n overflow: hidden;\n}\n\n.toggle {\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n justify-content: center;\n padding: 0;\n margin: 0 1rem 0 0;\n font: inherit;\n font-size: 1.25rem;\n color: inherit;\n cursor: pointer;\n background: transparent;\n border: 0;\n}\n\n.toggle:focus-visible {\n outline: 2px solid #1677ff;\n outline-offset: 3px;\n border-radius: 2px;\n}\n\n.title {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.actions {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n gap: 0.5rem;\n margin-left: 0.5rem;\n}\n",".trigger {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 0.25rem;\n margin: 0;\n font: inherit;\n font-size: 1.25rem;\n line-height: 1;\n color: inherit;\n cursor: pointer;\n background: transparent;\n border: 0;\n}\n\n.trigger:focus-visible {\n outline: 2px solid #1677ff;\n outline-offset: 3px;\n border-radius: 50%;\n}\n\n.menu {\n width: 16rem;\n max-width: calc(100vw - 2rem);\n}\n\n.stats {\n display: flex;\n justify-content: space-around;\n gap: 1rem;\n margin-bottom: 1rem;\n}\n\n.items {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.footer {\n margin-top: 0.5rem;\n}\n",".body {\n margin-bottom: 1rem;\n}\n\n.description {\n margin: 1rem 0 0.5rem;\n font-size: 1rem;\n line-height: 1.5;\n}\n\n.item {\n padding: 0.5rem;\n color: #6b7280;\n background: #f3f4f6;\n border-radius: 0.375rem;\n}\n\n:global(.dark) .item,\n:global([data-theme=\"dark\"]) .item {\n color: #e5e7eb;\n background: #262626;\n}\n\n.itemLabel {\n color: inherit;\n white-space: nowrap;\n}\n\n.itemValue {\n min-width: 0;\n overflow: hidden;\n text-align: right;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.link {\n color: inherit;\n text-decoration: none;\n}\n\n.poweredBy {\n color: #6b7280;\n}\n\n:global(.dark) .poweredBy,\n:global([data-theme=\"dark\"]) .poweredBy {\n color: #e5e7eb;\n}\n",".root {\n display: flex;\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n\n.sidebar {\n display: flex;\n flex-direction: column;\n flex-shrink: 0;\n overflow: hidden;\n transition: width 200ms ease;\n}\n\n.sidebarTop,\n.sidebarBottom {\n flex-shrink: 0;\n}\n\n.sidebarList {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n}\n\n.desktopArea {\n flex-grow: 1;\n min-width: 0;\n height: 100%;\n overflow: hidden;\n transition: width 200ms ease;\n}\n\n.content {\n height: calc(100% - var(--cube-header-height, 3rem) - 1px);\n overflow-y: auto;\n}\n\n.searchButton {\n width: 15rem;\n}\n\n.mobileRoot {\n width: 100vw;\n height: 100%;\n overflow: hidden;\n}\n\n.mobileContent {\n width: 100%;\n height: 100%;\n overflow-y: auto;\n}\n",".card {\n padding: 0.5rem 0.75rem;\n background: var(--cube-surface-color, #fff);\n border: 1px solid var(--cube-border-color, #d1d5db);\n border-radius: 0.5rem;\n}\n\n:global(.dark) .card,\n:global([data-theme=\"dark\"]) .card {\n background: var(--cube-dark-surface-color, #141414);\n border-color: var(--cube-dark-border-color, #374151);\n}\n\n.cell {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 0.375rem 0;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n background: transparent;\n border: 0;\n}\n\n.splitLine {\n height: 1px;\n margin: 0.25rem 0;\n background: var(--cube-border-color, #d1d5db);\n}\n\n:global(.dark) .splitLine,\n:global([data-theme=\"dark\"]) .splitLine {\n background: var(--cube-dark-border-color, #374151);\n}\n",".pageLoading {\n width: 100%;\n height: 100%;\n}\n\n.delayedLoading {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin: 1.5rem 0;\n}\n\n:global(.dark) .delayedLoading,\n:global([data-theme=\"dark\"]) .delayedLoading {\n color: #9ca3af;\n}\n",".container {\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n\n.content {\n position: relative;\n height: 100%;\n overflow-y: auto;\n}\n\n@media (max-width: 767px) {\n .content {\n height: calc(100% - var(--cube-mobile-action-height, 40px) - 1rem);\n }\n}\n\n.actionBar {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 0.5rem;\n height: var(--cube-mobile-action-height, 40px);\n padding: 0.5rem;\n}\n\n.actionIcon {\n flex-shrink: 0;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,128 @@
1
+ import * as react from 'react';
2
+ import { PropsWithChildren, ReactNode, Key, HTMLAttributes } from 'react';
3
+ import { ButtonProps } from 'antd';
4
+
5
+ declare const DEFAULT_MOBILE_BREAKPOINT = 767;
6
+ interface UseMediaQueryOptions {
7
+ defaultMatches?: boolean;
8
+ }
9
+ interface ResponsiveProps extends PropsWithChildren {
10
+ defaultMatches?: boolean;
11
+ }
12
+ declare const useMediaQuery: (query: string, options?: UseMediaQueryOptions) => boolean;
13
+ declare const useIsMobile: ({ defaultMatches }?: UseMediaQueryOptions) => boolean;
14
+ declare const MobileOnly: ({ children, defaultMatches }: ResponsiveProps) => ReactNode;
15
+ declare const DesktopOnly: ({ children, defaultMatches }: ResponsiveProps) => ReactNode;
16
+
17
+ interface CubeConfigProviderProps {
18
+ children: ReactNode;
19
+ primaryColor?: string;
20
+ }
21
+ /**
22
+ * Applies the fixed Cube antd configuration (`zh_CN` locale, `primaryColor` token, a larger
23
+ * base font size on mobile) without rendering any application shell. Mount it on its own for
24
+ * pages that should share the Cube theme but live outside `CubeApp` — `CubeApp` itself wraps
25
+ * this component internally.
26
+ */
27
+ declare const CubeConfigProvider: ({ children, primaryColor }: CubeConfigProviderProps) => react.JSX.Element;
28
+
29
+ interface AccountMenuStat {
30
+ key: Key;
31
+ title: ReactNode;
32
+ value: string | number;
33
+ icon?: ReactNode;
34
+ }
35
+ interface AccountMenuItem {
36
+ key: Key;
37
+ label: ReactNode;
38
+ icon?: ReactNode;
39
+ danger?: boolean;
40
+ disabled?: boolean;
41
+ onClick?(): void;
42
+ }
43
+
44
+ interface AboutAppInfo {
45
+ name: string;
46
+ version?: string;
47
+ repository?: string;
48
+ description?: ReactNode;
49
+ }
50
+ interface AboutAuthorInfo {
51
+ name: string;
52
+ url?: string;
53
+ }
54
+ interface AboutItem {
55
+ key: Key;
56
+ icon?: ReactNode;
57
+ label: ReactNode;
58
+ value: ReactNode;
59
+ href?: string;
60
+ }
61
+
62
+ interface CubeAppAboutInfo extends AboutAppInfo {
63
+ contactEmail?: string;
64
+ author?: AboutAuthorInfo;
65
+ items?: AboutItem[];
66
+ }
67
+ interface CubeAppContextValue {
68
+ accountMenuStats?: AccountMenuStat[];
69
+ accountMenuItems?: AccountMenuItem[];
70
+ onLogoutBtnClick?(): void;
71
+ userName?: ReactNode;
72
+ openAbout(): void;
73
+ setHeaderLeft(node: ReactNode): void;
74
+ }
75
+ /**
76
+ * Reads the account/about configuration passed to `CubeApp`. Only needed by components
77
+ * mounted elsewhere in the route tree (e.g. `MobileAccountSheet`) — `CubeApp` itself
78
+ * already has this data as props.
79
+ */
80
+ declare const useCubeAppContext: () => CubeAppContextValue;
81
+ /**
82
+ * Lets the currently mounted page override `CubeApp`'s `headerLeft` slot. Resets to the
83
+ * default `headerLeft` prop when the calling component unmounts.
84
+ */
85
+ declare const useCubeAppHeaderLeft: (node: ReactNode) => void;
86
+ interface CubeAppProps {
87
+ children: ReactNode;
88
+ primaryColor?: string;
89
+ accountMenuStats?: AccountMenuStat[];
90
+ accountMenuItems?: AccountMenuItem[];
91
+ onLogoutBtnClick?(): void;
92
+ userName?: ReactNode;
93
+ about: CubeAppAboutInfo;
94
+ sidebarTop?: ReactNode;
95
+ sidebarList?: ReactNode;
96
+ sidebarBottom?: ReactNode;
97
+ headerLeft?: ReactNode;
98
+ headerRight?: ReactNode;
99
+ headerSearchBtnLabel?: string;
100
+ onHeaderSearchBtnClick?(): void;
101
+ }
102
+ declare const CubeApp: ({ children, primaryColor, accountMenuStats, accountMenuItems, onLogoutBtnClick, userName, about, sidebarTop, sidebarList, sidebarBottom, headerLeft, headerRight, headerSearchBtnLabel, onHeaderSearchBtnClick, }: CubeAppProps) => react.JSX.Element;
103
+
104
+ interface MobileAccountSheetProps {
105
+ visible: boolean;
106
+ onVisibleChange(visible: boolean): void;
107
+ }
108
+ declare const MobileAccountSheet: ({ visible, onVisibleChange }: MobileAccountSheetProps) => react.JSX.Element;
109
+
110
+ interface PageLoadingProps {
111
+ tip?: string;
112
+ }
113
+ declare const PageLoading: ({ tip }: PageLoadingProps) => react.JSX.Element;
114
+ interface DelayedLoadingProps extends HTMLAttributes<HTMLDivElement> {
115
+ tip?: string;
116
+ delay?: number;
117
+ }
118
+ declare const DelayedLoading: ({ tip, delay, className, ...props }: DelayedLoadingProps) => react.JSX.Element | null;
119
+
120
+ declare const ActionIcon: ({ className, size, ...props }: ButtonProps) => react.JSX.Element;
121
+ declare const ActionButton: ({ children, type, block, size, ...props }: ButtonProps) => react.JSX.Element;
122
+ interface CubePageProps extends PropsWithChildren {
123
+ desktopHeaderLeft?: ReactNode;
124
+ mobileActionBar?: ReactNode;
125
+ }
126
+ declare const CubePage: ({ desktopHeaderLeft, mobileActionBar, children }: CubePageProps) => react.JSX.Element;
127
+
128
+ export { type AboutAppInfo, type AboutAuthorInfo, type AboutItem, type AccountMenuItem, type AccountMenuStat, ActionButton, ActionIcon, CubeApp, type CubeAppAboutInfo, type CubeAppContextValue, type CubeAppProps, CubeConfigProvider, type CubeConfigProviderProps, CubePage, type CubePageProps, DEFAULT_MOBILE_BREAKPOINT, DelayedLoading, type DelayedLoadingProps, DesktopOnly, MobileAccountSheet, type MobileAccountSheetProps, MobileOnly, PageLoading, type PageLoadingProps, type ResponsiveProps, type UseMediaQueryOptions, useCubeAppContext, useCubeAppHeaderLeft, useIsMobile, useMediaQuery };
package/dist/index.js ADDED
@@ -0,0 +1,567 @@
1
+ // src/responsive/responsive.tsx
2
+ import { useCallback, useMemo, useSyncExternalStore } from "react";
3
+ var DEFAULT_MOBILE_BREAKPOINT = 767;
4
+ var useMediaQuery = (query, options = {}) => {
5
+ const { defaultMatches = false } = options;
6
+ const mediaQueryList = useMemo(
7
+ () => typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia(query) : null,
8
+ [query]
9
+ );
10
+ const subscribe = useCallback(
11
+ (notify) => {
12
+ if (!mediaQueryList) return () => void 0;
13
+ mediaQueryList.addEventListener("change", notify);
14
+ return () => mediaQueryList.removeEventListener("change", notify);
15
+ },
16
+ [mediaQueryList]
17
+ );
18
+ const getSnapshot = useCallback(
19
+ () => mediaQueryList?.matches ?? defaultMatches,
20
+ [defaultMatches, mediaQueryList]
21
+ );
22
+ const getServerSnapshot = useCallback(() => defaultMatches, [defaultMatches]);
23
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
24
+ };
25
+ var useIsMobile = ({ defaultMatches } = {}) => useMediaQuery(`(max-width: ${DEFAULT_MOBILE_BREAKPOINT}px)`, { defaultMatches });
26
+ var MobileOnly = ({ children, defaultMatches }) => {
27
+ const isMobile = useIsMobile({ defaultMatches });
28
+ return isMobile ? children : null;
29
+ };
30
+ var DesktopOnly = ({ children, defaultMatches }) => {
31
+ const isMobile = useIsMobile({ defaultMatches });
32
+ return isMobile ? null : children;
33
+ };
34
+
35
+ // src/cube-config-provider/cube-config-provider.tsx
36
+ import { useMemo as useMemo2 } from "react";
37
+ import { ConfigProvider } from "antd";
38
+ import zhCN from "antd/locale/zh_CN";
39
+ import { jsx } from "react/jsx-runtime";
40
+ var DEFAULT_PRIMARY_COLOR = "#ec008c";
41
+ var CubeConfigProvider = ({ children, primaryColor }) => {
42
+ const isMobile = useIsMobile();
43
+ const theme = useMemo2(
44
+ () => ({
45
+ token: {
46
+ colorPrimary: primaryColor ?? DEFAULT_PRIMARY_COLOR,
47
+ lineWidth: 2,
48
+ controlOutlineWidth: 1,
49
+ ...isMobile ? { fontSize: 16 } : {}
50
+ },
51
+ components: {
52
+ Card: {
53
+ colorBorderSecondary: "var(--cube-border-color, #d1d5db)",
54
+ lineHeight: 1.6
55
+ }
56
+ }
57
+ }),
58
+ [primaryColor, isMobile]
59
+ );
60
+ return /* @__PURE__ */ jsx(ConfigProvider, { locale: zhCN, theme, children });
61
+ };
62
+
63
+ // src/cube-app/cube-app.tsx
64
+ import {
65
+ createContext,
66
+ useCallback as useCallback2,
67
+ useContext,
68
+ useEffect,
69
+ useMemo as useMemo3,
70
+ useState as useState2
71
+ } from "react";
72
+ import { Button as Button2 } from "antd";
73
+ import { SearchOutlined, SmileOutlined } from "@ant-design/icons";
74
+
75
+ // src/app-header/app-header.tsx
76
+ import { MenuFoldOutlined, MenuUnfoldOutlined } from "@ant-design/icons";
77
+
78
+ // src/app-header/app-header.module.css
79
+ var app_header_default = {};
80
+
81
+ // src/app-header/app-header.tsx
82
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
83
+ var AppHeader = ({
84
+ title,
85
+ actions,
86
+ collapsed = false,
87
+ onToggleSidebar,
88
+ showSidebarToggle = onToggleSidebar !== void 0,
89
+ className,
90
+ ...headerProps
91
+ }) => {
92
+ const label = collapsed ? "\u5C55\u5F00\u4FA7\u8FB9\u680F" : "\u6536\u8D77\u4FA7\u8FB9\u680F";
93
+ const ToggleIcon = collapsed ? MenuUnfoldOutlined : MenuFoldOutlined;
94
+ return /* @__PURE__ */ jsxs("header", { className: [app_header_default.root, className].filter(Boolean).join(" "), ...headerProps, children: [
95
+ /* @__PURE__ */ jsxs("div", { className: app_header_default.leading, children: [
96
+ showSidebarToggle ? /* @__PURE__ */ jsx2(
97
+ "button",
98
+ {
99
+ type: "button",
100
+ className: app_header_default.toggle,
101
+ "aria-label": label,
102
+ "aria-expanded": !collapsed,
103
+ onClick: onToggleSidebar,
104
+ children: /* @__PURE__ */ jsx2(ToggleIcon, { "aria-hidden": true })
105
+ }
106
+ ) : null,
107
+ /* @__PURE__ */ jsx2("div", { className: app_header_default.title, children: title })
108
+ ] }),
109
+ actions ? /* @__PURE__ */ jsx2("div", { className: app_header_default.actions, children: actions }) : null
110
+ ] });
111
+ };
112
+
113
+ // src/account-menu/account-menu-button.tsx
114
+ import { useState } from "react";
115
+ import { UserOutlined } from "@ant-design/icons";
116
+ import { Popover } from "antd";
117
+
118
+ // src/account-menu/account-menu.module.css
119
+ var account_menu_default = {};
120
+
121
+ // src/account-menu/account-menu-button.tsx
122
+ import { jsx as jsx3 } from "react/jsx-runtime";
123
+ var AccountMenuButton = ({ content }) => {
124
+ const [open, setOpen] = useState(false);
125
+ const close = () => setOpen(false);
126
+ const renderedContent = typeof content === "function" ? content({ close }) : content;
127
+ return /* @__PURE__ */ jsx3(
128
+ Popover,
129
+ {
130
+ placement: "bottomRight",
131
+ trigger: "click",
132
+ content: renderedContent,
133
+ open,
134
+ onOpenChange: setOpen,
135
+ arrow: true,
136
+ children: /* @__PURE__ */ jsx3(
137
+ "button",
138
+ {
139
+ type: "button",
140
+ className: account_menu_default.trigger,
141
+ "aria-label": "\u6253\u5F00\u7528\u6237\u83DC\u5355",
142
+ "aria-expanded": open,
143
+ "aria-haspopup": "dialog",
144
+ children: /* @__PURE__ */ jsx3(UserOutlined, { "aria-hidden": true })
145
+ }
146
+ )
147
+ }
148
+ );
149
+ };
150
+
151
+ // src/account-menu/account-menu.tsx
152
+ import { Button, Statistic } from "antd";
153
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
154
+ var AccountMenu = ({ stats = [], items = [], footer }) => /* @__PURE__ */ jsxs2("div", { className: account_menu_default.menu, children: [
155
+ stats.length > 0 ? /* @__PURE__ */ jsx4("div", { className: account_menu_default.stats, children: stats.map((stat) => /* @__PURE__ */ jsx4(Statistic, { title: stat.title, value: stat.value, prefix: stat.icon }, stat.key)) }) : null,
156
+ items.length > 0 ? /* @__PURE__ */ jsx4("div", { className: account_menu_default.items, children: items.map((item) => /* @__PURE__ */ jsx4(
157
+ Button,
158
+ {
159
+ block: true,
160
+ icon: item.icon,
161
+ danger: item.danger,
162
+ disabled: item.disabled,
163
+ "aria-label": typeof item.label === "string" ? item.label : void 0,
164
+ onClick: item.onClick,
165
+ children: item.label
166
+ },
167
+ item.key
168
+ )) }) : null,
169
+ footer ? /* @__PURE__ */ jsx4("div", { className: account_menu_default.footer, children: footer }) : null
170
+ ] });
171
+
172
+ // src/about-dialog/about-dialog.tsx
173
+ import { BarcodeOutlined, GithubOutlined, SendOutlined } from "@ant-design/icons";
174
+ import { Flex, Modal } from "antd";
175
+
176
+ // src/about-dialog/about-dialog.module.css
177
+ var about_dialog_default = {};
178
+
179
+ // src/about-dialog/about-dialog.tsx
180
+ import { Fragment, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
181
+ var renderItem = (item) => {
182
+ const content = /* @__PURE__ */ jsx5("div", { className: about_dialog_default.item, children: /* @__PURE__ */ jsxs3(Flex, { justify: "space-between", gap: 16, children: [
183
+ /* @__PURE__ */ jsxs3("div", { className: about_dialog_default.itemLabel, children: [
184
+ item.icon ? /* @__PURE__ */ jsxs3(Fragment, { children: [
185
+ item.icon,
186
+ " \xA0"
187
+ ] }) : null,
188
+ item.label
189
+ ] }),
190
+ /* @__PURE__ */ jsx5("div", { className: about_dialog_default.itemValue, children: item.value })
191
+ ] }) });
192
+ return item.href ? /* @__PURE__ */ jsx5("a", { href: item.href, className: about_dialog_default.link, children: content }, item.key) : /* @__PURE__ */ jsx5("div", { children: content }, item.key);
193
+ };
194
+ var AboutDialog = ({
195
+ open,
196
+ onClose,
197
+ app,
198
+ contactEmail,
199
+ author,
200
+ items = []
201
+ }) => {
202
+ const defaultItems = [];
203
+ if (contactEmail) {
204
+ defaultItems.push({
205
+ key: "contact",
206
+ icon: /* @__PURE__ */ jsx5(SendOutlined, {}),
207
+ label: "\u8054\u7CFB\u6211",
208
+ value: contactEmail,
209
+ href: `mailto:${contactEmail}?&subject=${encodeURIComponent(
210
+ `${app.name} \u76F8\u5173`
211
+ )}`
212
+ });
213
+ }
214
+ if (app.version) {
215
+ defaultItems.push({
216
+ key: "version",
217
+ icon: /* @__PURE__ */ jsx5(BarcodeOutlined, {}),
218
+ label: "\u5F53\u524D\u7248\u672C",
219
+ value: app.version
220
+ });
221
+ }
222
+ if (app.repository) {
223
+ defaultItems.push({
224
+ key: "repository",
225
+ icon: /* @__PURE__ */ jsx5(GithubOutlined, {}),
226
+ label: "\u5F00\u6E90\u5730\u5740",
227
+ value: "github",
228
+ href: app.repository
229
+ });
230
+ }
231
+ const renderedItems = [...defaultItems, ...items];
232
+ return /* @__PURE__ */ jsx5(
233
+ Modal,
234
+ {
235
+ open,
236
+ onCancel: onClose,
237
+ onOk: onClose,
238
+ title: `\u5173\u4E8E\u5E94\u7528 ${app.name}`,
239
+ footer: (_, { OkBtn }) => /* @__PURE__ */ jsxs3(
240
+ Flex,
241
+ {
242
+ align: "center",
243
+ justify: author ? "space-between" : "flex-end",
244
+ gap: 16,
245
+ children: [
246
+ author ? /* @__PURE__ */ jsxs3("div", { className: about_dialog_default.poweredBy, children: [
247
+ "Powered by \u{1F497}",
248
+ " ",
249
+ author.url ? /* @__PURE__ */ jsx5("a", { href: author.url, children: author.name }) : author.name
250
+ ] }) : null,
251
+ /* @__PURE__ */ jsx5(OkBtn, {})
252
+ ]
253
+ }
254
+ ),
255
+ children: /* @__PURE__ */ jsxs3(Flex, { gap: 16, vertical: true, className: about_dialog_default.body, children: [
256
+ app.description ? /* @__PURE__ */ jsx5("div", { className: about_dialog_default.description, children: app.description }) : null,
257
+ renderedItems.map(renderItem)
258
+ ] })
259
+ }
260
+ );
261
+ };
262
+
263
+ // src/cube-app/cube-app.module.css
264
+ var cube_app_default = {};
265
+
266
+ // src/cube-app/cube-app.tsx
267
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
268
+ var CubeAppContext = createContext({
269
+ openAbout: () => void 0,
270
+ setHeaderLeft: () => void 0
271
+ });
272
+ var useCubeAppContext = () => useContext(CubeAppContext);
273
+ var useCubeAppHeaderLeft = (node) => {
274
+ const { setHeaderLeft } = useCubeAppContext();
275
+ useEffect(() => {
276
+ setHeaderLeft(node);
277
+ return () => setHeaderLeft(void 0);
278
+ }, [node, setHeaderLeft]);
279
+ };
280
+ var SIDEBAR_WIDTH = "var(--cube-sidebar-width, 15rem)";
281
+ var CubeApp = ({
282
+ children,
283
+ primaryColor,
284
+ accountMenuStats,
285
+ accountMenuItems,
286
+ onLogoutBtnClick,
287
+ userName,
288
+ about,
289
+ sidebarTop,
290
+ sidebarList,
291
+ sidebarBottom,
292
+ headerLeft,
293
+ headerRight,
294
+ headerSearchBtnLabel,
295
+ onHeaderSearchBtnClick
296
+ }) => {
297
+ const isMobile = useIsMobile();
298
+ const [collapsed, setCollapsed] = useState2(false);
299
+ const toggleSidebar = useCallback2(() => setCollapsed((value) => !value), []);
300
+ const [aboutOpen, setAboutOpen] = useState2(false);
301
+ const [headerLeftOverride, setHeaderLeftOverride] = useState2(void 0);
302
+ const itemsWithAbout = useMemo3(
303
+ () => [
304
+ ...accountMenuItems ?? [],
305
+ {
306
+ key: "cube-app-about",
307
+ label: "\u5173\u4E8E",
308
+ icon: /* @__PURE__ */ jsx6(SmileOutlined, {}),
309
+ onClick: () => setAboutOpen(true)
310
+ }
311
+ ],
312
+ [accountMenuItems]
313
+ );
314
+ const contextValue = useMemo3(
315
+ () => ({
316
+ accountMenuStats,
317
+ accountMenuItems: itemsWithAbout,
318
+ onLogoutBtnClick,
319
+ userName,
320
+ openAbout: () => setAboutOpen(true),
321
+ setHeaderLeft: setHeaderLeftOverride
322
+ }),
323
+ [accountMenuStats, itemsWithAbout, onLogoutBtnClick, userName]
324
+ );
325
+ const renderAccountMenuButton = () => /* @__PURE__ */ jsx6(
326
+ AccountMenuButton,
327
+ {
328
+ content: ({ close }) => /* @__PURE__ */ jsx6(
329
+ AccountMenu,
330
+ {
331
+ stats: accountMenuStats,
332
+ items: itemsWithAbout.map((item) => ({
333
+ ...item,
334
+ onClick: () => {
335
+ item.onClick?.();
336
+ close();
337
+ }
338
+ })),
339
+ footer: onLogoutBtnClick ? /* @__PURE__ */ jsx6(
340
+ Button2,
341
+ {
342
+ block: true,
343
+ danger: true,
344
+ "aria-label": "\u767B\u51FA",
345
+ onClick: () => {
346
+ onLogoutBtnClick();
347
+ close();
348
+ },
349
+ children: "\u767B\u51FA"
350
+ }
351
+ ) : void 0
352
+ }
353
+ )
354
+ }
355
+ );
356
+ const renderShell = () => {
357
+ if (isMobile) {
358
+ return /* @__PURE__ */ jsx6("div", { className: cube_app_default.mobileRoot, "data-testid": "cube-app-shell-mobile", children: /* @__PURE__ */ jsx6("main", { className: cube_app_default.mobileContent, children }) });
359
+ }
360
+ const sidebarStyle = {
361
+ width: collapsed ? "0px" : SIDEBAR_WIDTH
362
+ };
363
+ const contentStyle = {
364
+ width: collapsed ? "100vw" : `calc(100vw - ${SIDEBAR_WIDTH})`
365
+ };
366
+ return /* @__PURE__ */ jsxs4("div", { className: cube_app_default.root, "data-testid": "cube-app-shell", children: [
367
+ /* @__PURE__ */ jsxs4(
368
+ "aside",
369
+ {
370
+ className: cube_app_default.sidebar,
371
+ style: sidebarStyle,
372
+ "data-testid": "cube-app-shell-sidebar",
373
+ "aria-hidden": collapsed,
374
+ children: [
375
+ sidebarTop ? /* @__PURE__ */ jsx6("div", { className: cube_app_default.sidebarTop, children: sidebarTop }) : null,
376
+ /* @__PURE__ */ jsx6("div", { className: cube_app_default.sidebarList, children: sidebarList }),
377
+ sidebarBottom ? /* @__PURE__ */ jsx6("div", { className: cube_app_default.sidebarBottom, children: sidebarBottom }) : null
378
+ ]
379
+ }
380
+ ),
381
+ /* @__PURE__ */ jsxs4("div", { className: cube_app_default.desktopArea, style: contentStyle, children: [
382
+ /* @__PURE__ */ jsx6(
383
+ AppHeader,
384
+ {
385
+ title: headerLeftOverride ?? headerLeft,
386
+ collapsed,
387
+ onToggleSidebar: toggleSidebar,
388
+ actions: /* @__PURE__ */ jsxs4(Fragment2, { children: [
389
+ headerRight,
390
+ onHeaderSearchBtnClick ? /* @__PURE__ */ jsx6(
391
+ Button2,
392
+ {
393
+ className: cube_app_default.searchButton,
394
+ icon: /* @__PURE__ */ jsx6(SearchOutlined, { "aria-hidden": true }),
395
+ "aria-label": headerSearchBtnLabel ?? "\u641C\u7D22",
396
+ onClick: onHeaderSearchBtnClick,
397
+ children: headerSearchBtnLabel ?? "\u641C\u7D22"
398
+ }
399
+ ) : null,
400
+ renderAccountMenuButton()
401
+ ] })
402
+ }
403
+ ),
404
+ /* @__PURE__ */ jsx6("main", { className: cube_app_default.content, children })
405
+ ] })
406
+ ] });
407
+ };
408
+ return /* @__PURE__ */ jsx6(CubeConfigProvider, { primaryColor, children: /* @__PURE__ */ jsxs4(CubeAppContext.Provider, { value: contextValue, children: [
409
+ renderShell(),
410
+ /* @__PURE__ */ jsx6(
411
+ AboutDialog,
412
+ {
413
+ open: aboutOpen,
414
+ onClose: () => setAboutOpen(false),
415
+ app: about,
416
+ contactEmail: about.contactEmail,
417
+ author: about.author,
418
+ items: about.items
419
+ }
420
+ )
421
+ ] }) });
422
+ };
423
+
424
+ // src/mobile-account-sheet/mobile-account-sheet.tsx
425
+ import { Fragment as Fragment3 } from "react";
426
+ import { Button as Button3, Drawer, Flex as Flex2, Statistic as Statistic2 } from "antd";
427
+
428
+ // src/mobile-account-sheet/mobile-account-sheet.module.css
429
+ var mobile_account_sheet_default = {};
430
+
431
+ // src/mobile-account-sheet/mobile-account-sheet.tsx
432
+ import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
433
+ var Cell = ({ title, extra, onClick }) => onClick ? /* @__PURE__ */ jsxs5("button", { type: "button", className: mobile_account_sheet_default.cell, onClick, children: [
434
+ /* @__PURE__ */ jsx7("span", { children: title }),
435
+ extra ? /* @__PURE__ */ jsx7("span", { children: extra }) : null
436
+ ] }) : /* @__PURE__ */ jsxs5("div", { className: mobile_account_sheet_default.cell, children: [
437
+ /* @__PURE__ */ jsx7("span", { children: title }),
438
+ extra ? /* @__PURE__ */ jsx7("span", { children: extra }) : null
439
+ ] });
440
+ var MobileAccountSheet = ({ visible, onVisibleChange }) => {
441
+ const { accountMenuStats, accountMenuItems, onLogoutBtnClick, userName } = useCubeAppContext();
442
+ const close = () => onVisibleChange(false);
443
+ return /* @__PURE__ */ jsx7(
444
+ Drawer,
445
+ {
446
+ open: visible,
447
+ onClose: close,
448
+ placement: "left",
449
+ width: "100%",
450
+ closable: false,
451
+ styles: {
452
+ header: { display: "none" },
453
+ body: { padding: 12 },
454
+ footer: { padding: 8, borderTop: "none" }
455
+ },
456
+ footer: /* @__PURE__ */ jsx7(Button3, { block: true, size: "large", type: "primary", "aria-label": "\u8FD4\u56DE", onClick: close, children: "\u8FD4\u56DE" }),
457
+ children: /* @__PURE__ */ jsxs5(Flex2, { vertical: true, gap: 12, children: [
458
+ accountMenuStats && accountMenuStats.length > 0 ? /* @__PURE__ */ jsx7("div", { className: mobile_account_sheet_default.card, children: /* @__PURE__ */ jsx7(Flex2, { justify: "space-around", children: accountMenuStats.map((stat) => /* @__PURE__ */ jsx7(Statistic2, { title: stat.title, value: stat.value, prefix: stat.icon }, stat.key)) }) }) : null,
459
+ userName ? /* @__PURE__ */ jsx7("div", { className: mobile_account_sheet_default.card, children: /* @__PURE__ */ jsx7(Cell, { title: "\u767B\u5F55\u7528\u6237", extra: userName }) }) : null,
460
+ accountMenuItems && accountMenuItems.length > 0 ? /* @__PURE__ */ jsx7("div", { className: mobile_account_sheet_default.card, children: accountMenuItems.map((item, index) => /* @__PURE__ */ jsxs5(Fragment3, { children: [
461
+ /* @__PURE__ */ jsx7(
462
+ Cell,
463
+ {
464
+ title: /* @__PURE__ */ jsxs5(Fragment4, { children: [
465
+ item.icon,
466
+ " \xA0",
467
+ item.label
468
+ ] }),
469
+ onClick: () => {
470
+ item.onClick?.();
471
+ close();
472
+ }
473
+ }
474
+ ),
475
+ index !== accountMenuItems.length - 1 ? /* @__PURE__ */ jsx7("div", { className: mobile_account_sheet_default.splitLine }) : null
476
+ ] }, item.key)) }) : null,
477
+ onLogoutBtnClick ? /* @__PURE__ */ jsx7("div", { className: mobile_account_sheet_default.card, children: /* @__PURE__ */ jsx7(
478
+ Cell,
479
+ {
480
+ title: "\u767B\u51FA",
481
+ onClick: () => {
482
+ onLogoutBtnClick();
483
+ close();
484
+ }
485
+ }
486
+ ) }) : null
487
+ ] })
488
+ }
489
+ );
490
+ };
491
+
492
+ // src/loading/loading.tsx
493
+ import { useEffect as useEffect2, useState as useState3 } from "react";
494
+ import { Flex as Flex3 } from "antd";
495
+
496
+ // src/loading/loading.module.css
497
+ var loading_default = {};
498
+
499
+ // src/loading/loading.tsx
500
+ import { jsx as jsx8 } from "react/jsx-runtime";
501
+ var PageLoading = ({ tip = "\u52A0\u8F7D\u4E2D..." }) => /* @__PURE__ */ jsx8(Flex3, { justify: "center", align: "center", className: loading_default.pageLoading, children: tip });
502
+ var DelayedLoading = ({
503
+ tip = "\u9875\u9762\u52A0\u8F7D\u4E2D...",
504
+ delay = 500,
505
+ className,
506
+ ...props
507
+ }) => {
508
+ const [visible, setVisible] = useState3(false);
509
+ useEffect2(() => {
510
+ const timer = window.setTimeout(() => setVisible(true), delay);
511
+ return () => window.clearTimeout(timer);
512
+ }, [delay]);
513
+ if (!visible) return null;
514
+ return /* @__PURE__ */ jsx8(
515
+ "div",
516
+ {
517
+ className: [loading_default.delayedLoading, className].filter(Boolean).join(" "),
518
+ ...props,
519
+ children: tip
520
+ }
521
+ );
522
+ };
523
+
524
+ // src/page/page.tsx
525
+ import { Button as Button4 } from "antd";
526
+
527
+ // src/page/page.module.css
528
+ var page_default = {};
529
+
530
+ // src/page/page.tsx
531
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
532
+ var PageContainer = ({ children }) => /* @__PURE__ */ jsx9("div", { className: page_default.container, children });
533
+ var PageContent = ({ children }) => /* @__PURE__ */ jsx9("div", { className: page_default.content, children });
534
+ var PageActionBar = ({ children }) => /* @__PURE__ */ jsx9(MobileOnly, { children: /* @__PURE__ */ jsx9("div", { className: page_default.actionBar, children }) });
535
+ var ActionIcon = ({ className, size = "large", ...props }) => /* @__PURE__ */ jsx9(Button4, { className: [page_default.actionIcon, className].filter(Boolean).join(" "), size, ...props });
536
+ var ActionButton = ({
537
+ children,
538
+ type = "primary",
539
+ block = true,
540
+ size = "large",
541
+ ...props
542
+ }) => /* @__PURE__ */ jsx9(Button4, { block, size, type, ...props, children });
543
+ var CubePage = ({ desktopHeaderLeft, mobileActionBar, children }) => {
544
+ useCubeAppHeaderLeft(desktopHeaderLeft);
545
+ return /* @__PURE__ */ jsxs6(PageContainer, { children: [
546
+ /* @__PURE__ */ jsx9(PageContent, { children }),
547
+ mobileActionBar ? /* @__PURE__ */ jsx9(PageActionBar, { children: mobileActionBar }) : null
548
+ ] });
549
+ };
550
+ export {
551
+ ActionButton,
552
+ ActionIcon,
553
+ CubeApp,
554
+ CubeConfigProvider,
555
+ CubePage,
556
+ DEFAULT_MOBILE_BREAKPOINT,
557
+ DelayedLoading,
558
+ DesktopOnly,
559
+ MobileAccountSheet,
560
+ MobileOnly,
561
+ PageLoading,
562
+ useCubeAppContext,
563
+ useCubeAppHeaderLeft,
564
+ useIsMobile,
565
+ useMediaQuery
566
+ };
567
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/responsive/responsive.tsx","../src/cube-config-provider/cube-config-provider.tsx","../src/cube-app/cube-app.tsx","../src/app-header/app-header.tsx","../src/app-header/app-header.module.css","../src/account-menu/account-menu-button.tsx","../src/account-menu/account-menu.module.css","../src/account-menu/account-menu.tsx","../src/about-dialog/about-dialog.tsx","../src/about-dialog/about-dialog.module.css","../src/cube-app/cube-app.module.css","../src/mobile-account-sheet/mobile-account-sheet.tsx","../src/mobile-account-sheet/mobile-account-sheet.module.css","../src/loading/loading.tsx","../src/loading/loading.module.css","../src/page/page.tsx","../src/page/page.module.css"],"sourcesContent":["import { type PropsWithChildren, type ReactNode, useCallback, useMemo, useSyncExternalStore } from \"react\";\n\nexport const DEFAULT_MOBILE_BREAKPOINT = 767;\n\nexport interface UseMediaQueryOptions {\n defaultMatches?: boolean;\n}\n\nexport interface ResponsiveProps extends PropsWithChildren {\n defaultMatches?: boolean;\n}\n\nexport const useMediaQuery = (query: string, options: UseMediaQueryOptions = {}): boolean => {\n const { defaultMatches = false } = options;\n const mediaQueryList = useMemo(\n () =>\n typeof window !== \"undefined\" && typeof window.matchMedia === \"function\"\n ? window.matchMedia(query)\n : null,\n [query],\n );\n\n const subscribe = useCallback(\n (notify: () => void) => {\n if (!mediaQueryList) return () => undefined;\n mediaQueryList.addEventListener(\"change\", notify);\n return () => mediaQueryList.removeEventListener(\"change\", notify);\n },\n [mediaQueryList],\n );\n\n const getSnapshot = useCallback(\n () => mediaQueryList?.matches ?? defaultMatches,\n [defaultMatches, mediaQueryList],\n );\n const getServerSnapshot = useCallback(() => defaultMatches, [defaultMatches]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n};\n\nexport const useIsMobile = ({ defaultMatches }: UseMediaQueryOptions = {}): boolean =>\n useMediaQuery(`(max-width: ${DEFAULT_MOBILE_BREAKPOINT}px)`, { defaultMatches });\n\nexport const MobileOnly = ({ children, defaultMatches }: ResponsiveProps): ReactNode => {\n const isMobile = useIsMobile({ defaultMatches });\n return isMobile ? children : null;\n};\n\nexport const DesktopOnly = ({ children, defaultMatches }: ResponsiveProps): ReactNode => {\n const isMobile = useIsMobile({ defaultMatches });\n return isMobile ? null : children;\n};\n","import { useMemo, type ReactNode } from \"react\";\nimport { ConfigProvider } from \"antd\";\nimport zhCN from \"antd/locale/zh_CN\";\nimport { useIsMobile } from \"../responsive/responsive\";\n\nexport interface CubeConfigProviderProps {\n children: ReactNode;\n\n primaryColor?: string;\n}\n\nconst DEFAULT_PRIMARY_COLOR = \"#ec008c\";\n\n/**\n * Applies the fixed Cube antd configuration (`zh_CN` locale, `primaryColor` token, a larger\n * base font size on mobile) without rendering any application shell. Mount it on its own for\n * pages that should share the Cube theme but live outside `CubeApp` — `CubeApp` itself wraps\n * this component internally.\n */\nexport const CubeConfigProvider = ({ children, primaryColor }: CubeConfigProviderProps) => {\n const isMobile = useIsMobile();\n\n const theme = useMemo(\n () => ({\n token: {\n colorPrimary: primaryColor ?? DEFAULT_PRIMARY_COLOR,\n lineWidth: 2,\n controlOutlineWidth: 1,\n ...(isMobile ? { fontSize: 16 } : {}),\n },\n components: {\n Card: {\n colorBorderSecondary: \"var(--cube-border-color, #d1d5db)\",\n lineHeight: 1.6,\n },\n },\n }),\n [primaryColor, isMobile],\n );\n\n return (\n <ConfigProvider locale={zhCN} theme={theme}>\n {children}\n </ConfigProvider>\n );\n};\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\nimport { Button } from \"antd\";\nimport { SearchOutlined, SmileOutlined } from \"@ant-design/icons\";\nimport { useIsMobile } from \"../responsive/responsive\";\nimport { CubeConfigProvider } from \"../cube-config-provider/cube-config-provider\";\nimport { AppHeader } from \"../app-header/app-header\";\nimport { AccountMenuButton } from \"../account-menu/account-menu-button\";\nimport { AccountMenu, type AccountMenuItem, type AccountMenuStat } from \"../account-menu/account-menu\";\nimport {\n AboutDialog,\n type AboutAppInfo,\n type AboutAuthorInfo,\n type AboutItem,\n} from \"../about-dialog/about-dialog\";\nimport styles from \"./cube-app.module.css\";\n\nexport interface CubeAppAboutInfo extends AboutAppInfo {\n contactEmail?: string;\n author?: AboutAuthorInfo;\n items?: AboutItem[];\n}\n\nexport interface CubeAppContextValue {\n accountMenuStats?: AccountMenuStat[];\n accountMenuItems?: AccountMenuItem[];\n onLogoutBtnClick?(): void;\n userName?: ReactNode;\n openAbout(): void;\n setHeaderLeft(node: ReactNode): void;\n}\n\nconst CubeAppContext = createContext<CubeAppContextValue>({\n openAbout: () => undefined,\n setHeaderLeft: () => undefined,\n});\n\n/**\n * Reads the account/about configuration passed to `CubeApp`. Only needed by components\n * mounted elsewhere in the route tree (e.g. `MobileAccountSheet`) — `CubeApp` itself\n * already has this data as props.\n */\nexport const useCubeAppContext = (): CubeAppContextValue => useContext(CubeAppContext);\n\n/**\n * Lets the currently mounted page override `CubeApp`'s `headerLeft` slot. Resets to the\n * default `headerLeft` prop when the calling component unmounts.\n */\nexport const useCubeAppHeaderLeft = (node: ReactNode): void => {\n const { setHeaderLeft } = useCubeAppContext();\n\n useEffect(() => {\n setHeaderLeft(node);\n return () => setHeaderLeft(undefined);\n }, [node, setHeaderLeft]);\n};\n\nexport interface CubeAppProps {\n children: ReactNode;\n\n primaryColor?: string;\n\n accountMenuStats?: AccountMenuStat[];\n accountMenuItems?: AccountMenuItem[];\n onLogoutBtnClick?(): void;\n userName?: ReactNode;\n about: CubeAppAboutInfo;\n\n sidebarTop?: ReactNode;\n sidebarList?: ReactNode;\n sidebarBottom?: ReactNode;\n\n headerLeft?: ReactNode;\n headerRight?: ReactNode;\n headerSearchBtnLabel?: string;\n onHeaderSearchBtnClick?(): void;\n}\n\nconst SIDEBAR_WIDTH = \"var(--cube-sidebar-width, 15rem)\";\n\nexport const CubeApp = ({\n children,\n primaryColor,\n accountMenuStats,\n accountMenuItems,\n onLogoutBtnClick,\n userName,\n about,\n sidebarTop,\n sidebarList,\n sidebarBottom,\n headerLeft,\n headerRight,\n headerSearchBtnLabel,\n onHeaderSearchBtnClick,\n}: CubeAppProps) => {\n const isMobile = useIsMobile();\n const [collapsed, setCollapsed] = useState(false);\n const toggleSidebar = useCallback(() => setCollapsed((value) => !value), []);\n const [aboutOpen, setAboutOpen] = useState(false);\n const [headerLeftOverride, setHeaderLeftOverride] = useState<ReactNode>(undefined);\n\n const itemsWithAbout = useMemo<AccountMenuItem[]>(\n () => [\n ...(accountMenuItems ?? []),\n {\n key: \"cube-app-about\",\n label: \"关于\",\n icon: <SmileOutlined />,\n onClick: () => setAboutOpen(true),\n },\n ],\n [accountMenuItems],\n );\n\n const contextValue = useMemo<CubeAppContextValue>(\n () => ({\n accountMenuStats,\n accountMenuItems: itemsWithAbout,\n onLogoutBtnClick,\n userName,\n openAbout: () => setAboutOpen(true),\n setHeaderLeft: setHeaderLeftOverride,\n }),\n [accountMenuStats, itemsWithAbout, onLogoutBtnClick, userName],\n );\n\n const renderAccountMenuButton = () => (\n <AccountMenuButton\n content={({ close }) => (\n <AccountMenu\n stats={accountMenuStats}\n items={itemsWithAbout.map((item) => ({\n ...item,\n onClick: () => {\n item.onClick?.();\n close();\n },\n }))}\n footer={\n onLogoutBtnClick ? (\n <Button\n block\n danger\n aria-label=\"登出\"\n onClick={() => {\n onLogoutBtnClick();\n close();\n }}\n >\n 登出\n </Button>\n ) : undefined\n }\n />\n )}\n />\n );\n\n const renderShell = () => {\n if (isMobile) {\n return (\n <div className={styles.mobileRoot} data-testid=\"cube-app-shell-mobile\">\n <main className={styles.mobileContent}>{children}</main>\n </div>\n );\n }\n\n const sidebarStyle: CSSProperties = {\n width: collapsed ? \"0px\" : SIDEBAR_WIDTH,\n };\n const contentStyle: CSSProperties = {\n width: collapsed ? \"100vw\" : `calc(100vw - ${SIDEBAR_WIDTH})`,\n };\n\n return (\n <div className={styles.root} data-testid=\"cube-app-shell\">\n <aside\n className={styles.sidebar}\n style={sidebarStyle}\n data-testid=\"cube-app-shell-sidebar\"\n aria-hidden={collapsed}\n >\n {sidebarTop ? <div className={styles.sidebarTop}>{sidebarTop}</div> : null}\n <div className={styles.sidebarList}>{sidebarList}</div>\n {sidebarBottom ? <div className={styles.sidebarBottom}>{sidebarBottom}</div> : null}\n </aside>\n <div className={styles.desktopArea} style={contentStyle}>\n <AppHeader\n title={headerLeftOverride ?? headerLeft}\n collapsed={collapsed}\n onToggleSidebar={toggleSidebar}\n actions={\n <>\n {headerRight}\n {onHeaderSearchBtnClick ? (\n <Button\n className={styles.searchButton}\n icon={<SearchOutlined aria-hidden />}\n aria-label={headerSearchBtnLabel ?? \"\\u641c\\u7d22\"}\n onClick={onHeaderSearchBtnClick}\n >\n {headerSearchBtnLabel ?? \"\\u641c\\u7d22\"}\n </Button>\n ) : null}\n {renderAccountMenuButton()}\n </>\n }\n />\n <main className={styles.content}>{children}</main>\n </div>\n </div>\n );\n };\n\n return (\n <CubeConfigProvider primaryColor={primaryColor}>\n <CubeAppContext.Provider value={contextValue}>\n {renderShell()}\n <AboutDialog\n open={aboutOpen}\n onClose={() => setAboutOpen(false)}\n app={about}\n contactEmail={about.contactEmail}\n author={about.author}\n items={about.items}\n />\n </CubeAppContext.Provider>\n </CubeConfigProvider>\n );\n};\n","import type { HTMLAttributes, ReactNode } from \"react\";\nimport { MenuFoldOutlined, MenuUnfoldOutlined } from \"@ant-design/icons\";\nimport styles from \"./app-header.module.css\";\n\nexport interface AppHeaderProps extends Omit<HTMLAttributes<HTMLElement>, \"title\"> {\n title?: ReactNode;\n actions?: ReactNode;\n collapsed?: boolean;\n showSidebarToggle?: boolean;\n onToggleSidebar?(): void;\n}\n\nexport const AppHeader = ({\n title,\n actions,\n collapsed = false,\n onToggleSidebar,\n showSidebarToggle = onToggleSidebar !== undefined,\n className,\n ...headerProps\n}: AppHeaderProps) => {\n const label = collapsed ? \"展开侧边栏\" : \"收起侧边栏\";\n const ToggleIcon = collapsed ? MenuUnfoldOutlined : MenuFoldOutlined;\n\n return (\n <header className={[styles.root, className].filter(Boolean).join(\" \")} {...headerProps}>\n <div className={styles.leading}>\n {showSidebarToggle ? (\n <button\n type=\"button\"\n className={styles.toggle}\n aria-label={label}\n aria-expanded={!collapsed}\n onClick={onToggleSidebar}\n >\n <ToggleIcon aria-hidden />\n </button>\n ) : null}\n <div className={styles.title}>{title}</div>\n </div>\n {actions ? <div className={styles.actions}>{actions}</div> : null}\n </header>\n );\n};\n",".root {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n height: var(--cube-header-height, 3rem);\n padding: 0.5rem 1rem;\n overflow: hidden;\n box-sizing: border-box;\n color: inherit;\n background: var(--cube-surface-color, #fff);\n border-bottom: 1px solid var(--cube-border-color, #d1d5db);\n transition: color 200ms ease, background-color 200ms ease,\n border-color 200ms ease;\n}\n\n:global(.dark) .root,\n:global([data-theme=\"dark\"]) .root {\n background: var(--cube-dark-surface-color, #141414);\n border-bottom-color: var(--cube-dark-border-color, #374151);\n}\n\n.leading {\n display: flex;\n flex: 1 1 auto;\n align-items: center;\n min-width: 0;\n overflow: hidden;\n}\n\n.toggle {\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n justify-content: center;\n padding: 0;\n margin: 0 1rem 0 0;\n font: inherit;\n font-size: 1.25rem;\n color: inherit;\n cursor: pointer;\n background: transparent;\n border: 0;\n}\n\n.toggle:focus-visible {\n outline: 2px solid #1677ff;\n outline-offset: 3px;\n border-radius: 2px;\n}\n\n.title {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.actions {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n gap: 0.5rem;\n margin-left: 0.5rem;\n}\n","import { type ReactNode, useState } from \"react\";\nimport { UserOutlined } from \"@ant-design/icons\";\nimport { Popover } from \"antd\";\nimport styles from \"./account-menu.module.css\";\n\nexport interface AccountMenuRenderContext {\n close(): void;\n}\n\nexport interface AccountMenuButtonProps {\n content: ReactNode | ((context: AccountMenuRenderContext) => ReactNode);\n}\n\nexport const AccountMenuButton = ({ content }: AccountMenuButtonProps) => {\n const [open, setOpen] = useState(false);\n const close = () => setOpen(false);\n const renderedContent = typeof content === \"function\" ? content({ close }) : content;\n\n return (\n <Popover\n placement=\"bottomRight\"\n trigger=\"click\"\n content={renderedContent}\n open={open}\n onOpenChange={setOpen}\n arrow\n >\n <button\n type=\"button\"\n className={styles.trigger}\n aria-label=\"打开用户菜单\"\n aria-expanded={open}\n aria-haspopup=\"dialog\"\n >\n <UserOutlined aria-hidden />\n </button>\n </Popover>\n );\n};\n",".trigger {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 0.25rem;\n margin: 0;\n font: inherit;\n font-size: 1.25rem;\n line-height: 1;\n color: inherit;\n cursor: pointer;\n background: transparent;\n border: 0;\n}\n\n.trigger:focus-visible {\n outline: 2px solid #1677ff;\n outline-offset: 3px;\n border-radius: 50%;\n}\n\n.menu {\n width: 16rem;\n max-width: calc(100vw - 2rem);\n}\n\n.stats {\n display: flex;\n justify-content: space-around;\n gap: 1rem;\n margin-bottom: 1rem;\n}\n\n.items {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.footer {\n margin-top: 0.5rem;\n}\n","import type { Key, ReactNode } from \"react\";\nimport { Button, Statistic } from \"antd\";\nimport styles from \"./account-menu.module.css\";\n\nexport interface AccountMenuStat {\n key: Key;\n title: ReactNode;\n value: string | number;\n icon?: ReactNode;\n}\n\nexport interface AccountMenuItem {\n key: Key;\n label: ReactNode;\n icon?: ReactNode;\n danger?: boolean;\n disabled?: boolean;\n onClick?(): void;\n}\n\nexport interface AccountMenuProps {\n stats?: AccountMenuStat[];\n items?: AccountMenuItem[];\n footer?: ReactNode;\n}\n\nexport const AccountMenu = ({ stats = [], items = [], footer }: AccountMenuProps) => (\n <div className={styles.menu}>\n {stats.length > 0 ? (\n <div className={styles.stats}>\n {stats.map((stat) => (\n <Statistic key={stat.key} title={stat.title} value={stat.value} prefix={stat.icon} />\n ))}\n </div>\n ) : null}\n {items.length > 0 ? (\n <div className={styles.items}>\n {items.map((item) => (\n <Button\n key={item.key}\n block\n icon={item.icon}\n danger={item.danger}\n disabled={item.disabled}\n aria-label={typeof item.label === \"string\" ? item.label : undefined}\n onClick={item.onClick}\n >\n {item.label}\n </Button>\n ))}\n </div>\n ) : null}\n {footer ? <div className={styles.footer}>{footer}</div> : null}\n </div>\n);\n","import type { Key, ReactNode } from \"react\";\nimport { BarcodeOutlined, GithubOutlined, SendOutlined } from \"@ant-design/icons\";\nimport { Flex, Modal } from \"antd\";\nimport styles from \"./about-dialog.module.css\";\n\nexport interface AboutAppInfo {\n name: string;\n version?: string;\n repository?: string;\n description?: ReactNode;\n}\n\nexport interface AboutAuthorInfo {\n name: string;\n url?: string;\n}\n\nexport interface AboutItem {\n key: Key;\n icon?: ReactNode;\n label: ReactNode;\n value: ReactNode;\n href?: string;\n}\n\nexport interface AboutDialogProps {\n open: boolean;\n onClose(): void;\n app: AboutAppInfo;\n contactEmail?: string;\n author?: AboutAuthorInfo;\n items?: AboutItem[];\n}\n\nconst renderItem = (item: AboutItem) => {\n const content = (\n <div className={styles.item}>\n <Flex justify=\"space-between\" gap={16}>\n <div className={styles.itemLabel}>\n {item.icon ? <>{item.icon} &nbsp;</> : null}\n {item.label}\n </div>\n <div className={styles.itemValue}>{item.value}</div>\n </Flex>\n </div>\n );\n\n return item.href ? (\n <a key={item.key} href={item.href} className={styles.link}>\n {content}\n </a>\n ) : (\n <div key={item.key}>{content}</div>\n );\n};\n\nexport const AboutDialog = ({\n open,\n onClose,\n app,\n contactEmail,\n author,\n items = [],\n}: AboutDialogProps) => {\n const defaultItems: AboutItem[] = [];\n\n if (contactEmail) {\n defaultItems.push({\n key: \"contact\",\n icon: <SendOutlined />,\n label: \"联系我\",\n value: contactEmail,\n href: `mailto:${contactEmail}?&subject=${encodeURIComponent(\n `${app.name} 相关`,\n )}`,\n });\n }\n\n if (app.version) {\n defaultItems.push({\n key: \"version\",\n icon: <BarcodeOutlined />,\n label: \"当前版本\",\n value: app.version,\n });\n }\n\n if (app.repository) {\n defaultItems.push({\n key: \"repository\",\n icon: <GithubOutlined />,\n label: \"开源地址\",\n value: \"github\",\n href: app.repository,\n });\n }\n\n const renderedItems = [...defaultItems, ...items];\n\n return (\n <Modal\n open={open}\n onCancel={onClose}\n onOk={onClose}\n title={`关于应用 ${app.name}`}\n footer={(_, { OkBtn }) => (\n <Flex\n align=\"center\"\n justify={author ? \"space-between\" : \"flex-end\"}\n gap={16}\n >\n {author ? (\n <div className={styles.poweredBy}>\n Powered by 💗{\" \"}\n {author.url ? (\n <a href={author.url}>{author.name}</a>\n ) : (\n author.name\n )}\n </div>\n ) : null}\n <OkBtn />\n </Flex>\n )}\n >\n <Flex gap={16} vertical className={styles.body}>\n {app.description ? (\n <div className={styles.description}>{app.description}</div>\n ) : null}\n {renderedItems.map(renderItem)}\n </Flex>\n </Modal>\n );\n};\n",".body {\n margin-bottom: 1rem;\n}\n\n.description {\n margin: 1rem 0 0.5rem;\n font-size: 1rem;\n line-height: 1.5;\n}\n\n.item {\n padding: 0.5rem;\n color: #6b7280;\n background: #f3f4f6;\n border-radius: 0.375rem;\n}\n\n:global(.dark) .item,\n:global([data-theme=\"dark\"]) .item {\n color: #e5e7eb;\n background: #262626;\n}\n\n.itemLabel {\n color: inherit;\n white-space: nowrap;\n}\n\n.itemValue {\n min-width: 0;\n overflow: hidden;\n text-align: right;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.link {\n color: inherit;\n text-decoration: none;\n}\n\n.poweredBy {\n color: #6b7280;\n}\n\n:global(.dark) .poweredBy,\n:global([data-theme=\"dark\"]) .poweredBy {\n color: #e5e7eb;\n}\n",".root {\n display: flex;\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n\n.sidebar {\n display: flex;\n flex-direction: column;\n flex-shrink: 0;\n overflow: hidden;\n transition: width 200ms ease;\n}\n\n.sidebarTop,\n.sidebarBottom {\n flex-shrink: 0;\n}\n\n.sidebarList {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n}\n\n.desktopArea {\n flex-grow: 1;\n min-width: 0;\n height: 100%;\n overflow: hidden;\n transition: width 200ms ease;\n}\n\n.content {\n height: calc(100% - var(--cube-header-height, 3rem) - 1px);\n overflow-y: auto;\n}\n\n.searchButton {\n width: 15rem;\n}\n\n.mobileRoot {\n width: 100vw;\n height: 100%;\n overflow: hidden;\n}\n\n.mobileContent {\n width: 100%;\n height: 100%;\n overflow-y: auto;\n}\n","import { Fragment, type ReactNode } from \"react\";\nimport { Button, Drawer, Flex, Statistic } from \"antd\";\nimport { useCubeAppContext } from \"../cube-app/cube-app\";\nimport styles from \"./mobile-account-sheet.module.css\";\n\nexport interface MobileAccountSheetProps {\n visible: boolean;\n onVisibleChange(visible: boolean): void;\n}\n\ninterface CellProps {\n title: ReactNode;\n extra?: ReactNode;\n onClick?(): void;\n}\n\nconst Cell = ({ title, extra, onClick }: CellProps) =>\n onClick ? (\n <button type=\"button\" className={styles.cell} onClick={onClick}>\n <span>{title}</span>\n {extra ? <span>{extra}</span> : null}\n </button>\n ) : (\n <div className={styles.cell}>\n <span>{title}</span>\n {extra ? <span>{extra}</span> : null}\n </div>\n );\n\nexport const MobileAccountSheet = ({ visible, onVisibleChange }: MobileAccountSheetProps) => {\n const { accountMenuStats, accountMenuItems, onLogoutBtnClick, userName } = useCubeAppContext();\n const close = () => onVisibleChange(false);\n\n return (\n <Drawer\n open={visible}\n onClose={close}\n placement=\"left\"\n width=\"100%\"\n closable={false}\n styles={{\n header: { display: \"none\" },\n body: { padding: 12 },\n footer: { padding: 8, borderTop: \"none\" },\n }}\n footer={\n <Button block size=\"large\" type=\"primary\" aria-label=\"返回\" onClick={close}>\n 返回\n </Button>\n }\n >\n <Flex vertical gap={12}>\n {accountMenuStats && accountMenuStats.length > 0 ? (\n <div className={styles.card}>\n <Flex justify=\"space-around\">\n {accountMenuStats.map((stat) => (\n <Statistic key={stat.key} title={stat.title} value={stat.value} prefix={stat.icon} />\n ))}\n </Flex>\n </div>\n ) : null}\n\n {userName ? (\n <div className={styles.card}>\n <Cell title=\"登录用户\" extra={userName} />\n </div>\n ) : null}\n\n {accountMenuItems && accountMenuItems.length > 0 ? (\n <div className={styles.card}>\n {accountMenuItems.map((item, index) => (\n <Fragment key={item.key}>\n <Cell\n title={\n <>\n {item.icon} &nbsp;{item.label}\n </>\n }\n onClick={() => {\n item.onClick?.();\n close();\n }}\n />\n {index !== accountMenuItems.length - 1 ? <div className={styles.splitLine} /> : null}\n </Fragment>\n ))}\n </div>\n ) : null}\n\n {onLogoutBtnClick ? (\n <div className={styles.card}>\n <Cell\n title=\"登出\"\n onClick={() => {\n onLogoutBtnClick();\n close();\n }}\n />\n </div>\n ) : null}\n </Flex>\n </Drawer>\n );\n};\n",".card {\n padding: 0.5rem 0.75rem;\n background: var(--cube-surface-color, #fff);\n border: 1px solid var(--cube-border-color, #d1d5db);\n border-radius: 0.5rem;\n}\n\n:global(.dark) .card,\n:global([data-theme=\"dark\"]) .card {\n background: var(--cube-dark-surface-color, #141414);\n border-color: var(--cube-dark-border-color, #374151);\n}\n\n.cell {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 0.375rem 0;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n background: transparent;\n border: 0;\n}\n\n.splitLine {\n height: 1px;\n margin: 0.25rem 0;\n background: var(--cube-border-color, #d1d5db);\n}\n\n:global(.dark) .splitLine,\n:global([data-theme=\"dark\"]) .splitLine {\n background: var(--cube-dark-border-color, #374151);\n}\n","import { type HTMLAttributes, useEffect, useState } from \"react\";\nimport { Flex } from \"antd\";\nimport styles from \"./loading.module.css\";\n\nexport interface PageLoadingProps {\n tip?: string;\n}\n\nexport const PageLoading = ({ tip = \"加载中...\" }: PageLoadingProps) => (\n <Flex justify=\"center\" align=\"center\" className={styles.pageLoading}>\n {tip}\n </Flex>\n);\n\nexport interface DelayedLoadingProps extends HTMLAttributes<HTMLDivElement> {\n tip?: string;\n delay?: number;\n}\n\nexport const DelayedLoading = ({\n tip = \"页面加载中...\",\n delay = 500,\n className,\n ...props\n}: DelayedLoadingProps) => {\n const [visible, setVisible] = useState(false);\n\n useEffect(() => {\n const timer = window.setTimeout(() => setVisible(true), delay);\n return () => window.clearTimeout(timer);\n }, [delay]);\n\n if (!visible) return null;\n\n return (\n <div\n className={[styles.delayedLoading, className].filter(Boolean).join(\" \")}\n {...props}\n >\n {tip}\n </div>\n );\n};\n",".pageLoading {\n width: 100%;\n height: 100%;\n}\n\n.delayedLoading {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin: 1.5rem 0;\n}\n\n:global(.dark) .delayedLoading,\n:global([data-theme=\"dark\"]) .delayedLoading {\n color: #9ca3af;\n}\n","import type { PropsWithChildren, ReactNode } from \"react\";\nimport { Button, type ButtonProps } from \"antd\";\nimport { useCubeAppHeaderLeft } from \"../cube-app/cube-app\";\nimport { MobileOnly } from \"../responsive/responsive\";\nimport styles from \"./page.module.css\";\n\nconst PageContainer = ({ children }: PropsWithChildren) => <div className={styles.container}>{children}</div>;\n\nconst PageContent = ({ children }: PropsWithChildren) => <div className={styles.content}>{children}</div>;\n\nconst PageActionBar = ({ children }: PropsWithChildren) => (\n <MobileOnly>\n <div className={styles.actionBar}>{children}</div>\n </MobileOnly>\n);\n\nexport const ActionIcon = ({ className, size = \"large\", ...props }: ButtonProps) => (\n <Button className={[styles.actionIcon, className].filter(Boolean).join(\" \")} size={size} {...props} />\n);\n\nexport const ActionButton = ({\n children,\n type = \"primary\",\n block = true,\n size = \"large\",\n ...props\n}: ButtonProps) => (\n <Button block={block} size={size} type={type} {...props}>\n {children}\n </Button>\n);\n\nexport interface CubePageProps extends PropsWithChildren {\n desktopHeaderLeft?: ReactNode;\n mobileActionBar?: ReactNode;\n}\n\nexport const CubePage = ({ desktopHeaderLeft, mobileActionBar, children }: CubePageProps) => {\n useCubeAppHeaderLeft(desktopHeaderLeft);\n\n return (\n <PageContainer>\n <PageContent>{children}</PageContent>\n {mobileActionBar ? <PageActionBar>{mobileActionBar}</PageActionBar> : null}\n </PageContainer>\n );\n};\n",".container {\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n\n.content {\n position: relative;\n height: 100%;\n overflow-y: auto;\n}\n\n@media (max-width: 767px) {\n .content {\n height: calc(100% - var(--cube-mobile-action-height, 40px) - 1rem);\n }\n}\n\n.actionBar {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 0.5rem;\n height: var(--cube-mobile-action-height, 40px);\n padding: 0.5rem;\n}\n\n.actionIcon {\n flex-shrink: 0;\n}\n"],"mappings":"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","names":["useMemo","useMemo","useCallback","useMemo","useState","Button","jsx","jsx","jsx","jsxs","jsx","jsxs","Fragment","jsx","jsxs","useState","useCallback","useMemo","Button","Fragment","Button","Flex","Statistic","Fragment","jsx","jsxs","Button","Flex","Statistic","useEffect","useState","Flex","jsx","Flex","useState","useEffect","Button","jsx","jsxs","Button"]}
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@hopgoldy/cube-ui",
3
+ "version": "0.1.0",
4
+ "description": "Shared responsive application shell and UI primitives for Cube applications",
5
+ "license": "MIT",
6
+ "homepage": "https://github.com/HoPGoldy/cube-module#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/HoPGoldy/cube-module.git",
10
+ "directory": "packages/cube-ui"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/HoPGoldy/cube-module/issues"
14
+ },
15
+ "type": "module",
16
+ "sideEffects": [
17
+ "**/*.css"
18
+ ],
19
+ "files": [
20
+ "dist",
21
+ "README.md",
22
+ "LICENSE"
23
+ ],
24
+ "main": "./dist/index.js",
25
+ "module": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js"
31
+ },
32
+ "./styles.css": "./dist/index.css"
33
+ },
34
+ "peerDependencies": {
35
+ "@ant-design/icons": "^6.0.0",
36
+ "antd": "^5.0.0",
37
+ "react": "^18.3.0",
38
+ "react-dom": "^18.3.0"
39
+ },
40
+ "devDependencies": {
41
+ "@ant-design/icons": "^6.0.0",
42
+ "@testing-library/jest-dom": "^6.6.0",
43
+ "@testing-library/react": "^16.1.0",
44
+ "@testing-library/user-event": "^14.5.0",
45
+ "@types/react": "^18.3.0",
46
+ "@types/react-dom": "^18.3.0",
47
+ "@vitest/coverage-v8": "^2.1.0",
48
+ "antd": "^5.3.2",
49
+ "happy-dom": "^14.0.0",
50
+ "react": "^18.3.1",
51
+ "react-dom": "^18.3.1",
52
+ "tsup": "^8.5.0",
53
+ "typescript": "^5.7.3",
54
+ "vitest": "^2.1.0"
55
+ },
56
+ "publishConfig": {
57
+ "access": "public"
58
+ },
59
+ "engines": {
60
+ "node": ">=20.19.0"
61
+ },
62
+ "scripts": {
63
+ "build": "tsup",
64
+ "test": "vitest run --coverage",
65
+ "test:watch": "vitest",
66
+ "typecheck": "tsc --noEmit"
67
+ }
68
+ }