@nocobase/app-template-default 0.0.0-stage → 1.0.0-beta.58

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.
Files changed (249) hide show
  1. package/.cursor/mcp.json +8 -0
  2. package/.mcp.json +8 -0
  3. package/.prettierignore +20 -0
  4. package/.vscode/mcp.json +8 -0
  5. package/AGENTS.md +434 -0
  6. package/CLAUDE.md +9 -0
  7. package/Dockerfile +132 -0
  8. package/Dockerfile.dockerignore +45 -0
  9. package/README.MD +485 -0
  10. package/cli/plugins.ts +8 -0
  11. package/client/agent-annotations-host.ts +19 -0
  12. package/client/app.ts +38 -0
  13. package/client/components/back-button.tsx +49 -0
  14. package/client/components/breadcrumbs.tsx +167 -0
  15. package/client/components/inbox-badge.ts +49 -0
  16. package/client/components/inbox-button.tsx +132 -0
  17. package/client/components/inbox-header-button.tsx +25 -0
  18. package/client/components/loading.tsx +35 -0
  19. package/client/components/page-container.tsx +24 -0
  20. package/client/components/page-header.tsx +33 -0
  21. package/client/components/route-child-page.tsx +121 -0
  22. package/client/components/route-dialog.tsx +10 -0
  23. package/client/components/route-drawer.tsx +10 -0
  24. package/client/components/route-overlay.tsx +190 -0
  25. package/client/components/ui/alert.tsx +75 -0
  26. package/client/components/ui/badge.tsx +51 -0
  27. package/client/components/ui/breadcrumb.tsx +121 -0
  28. package/client/components/ui/button.tsx +58 -0
  29. package/client/components/ui/collapsible.tsx +19 -0
  30. package/client/components/ui/context-menu.tsx +271 -0
  31. package/client/components/ui/dialog.tsx +160 -0
  32. package/client/components/ui/dropdown-menu.tsx +270 -0
  33. package/client/components/ui/empty.tsx +103 -0
  34. package/client/components/ui/field.tsx +236 -0
  35. package/client/components/ui/input-group.tsx +158 -0
  36. package/client/components/ui/input.tsx +20 -0
  37. package/client/components/ui/kbd.tsx +26 -0
  38. package/client/components/ui/label.tsx +18 -0
  39. package/client/components/ui/popover.tsx +89 -0
  40. package/client/components/ui/select.tsx +200 -0
  41. package/client/components/ui/separator.tsx +22 -0
  42. package/client/components/ui/sheet.tsx +137 -0
  43. package/client/components/ui/sidebar.tsx +723 -0
  44. package/client/components/ui/skeleton.tsx +13 -0
  45. package/client/components/ui/spinner.tsx +20 -0
  46. package/client/components/ui/tabs.tsx +79 -0
  47. package/client/components/ui/textarea.tsx +17 -0
  48. package/client/components/ui/toast.tsx +231 -0
  49. package/client/components/ui/tooltip.tsx +63 -0
  50. package/client/components/use-route-overlay.ts +26 -0
  51. package/client/config/api.ts +13 -0
  52. package/client/config/app.ts +8 -0
  53. package/client/config/auth.ts +18 -0
  54. package/client/config/index.ts +17 -0
  55. package/client/extensions/nocobase-auth-forms/field-props.ts +13 -0
  56. package/client/extensions/nocobase-auth-forms/form-parts.tsx +101 -0
  57. package/client/extensions/nocobase-auth-forms/password-input.tsx +41 -0
  58. package/client/extensions/nocobase-auth-forms/password-login-form.tsx +123 -0
  59. package/client/extensions/nocobase-auth-forms/password-registration-form.tsx +193 -0
  60. package/client/extensions/nocobase-auth-forms/password-reset-form.tsx +132 -0
  61. package/client/extensions/nocobase-auth-forms/password-reset-request-form.tsx +99 -0
  62. package/client/extensions/nocobase-auth-methods/auth-methods.tsx +170 -0
  63. package/client/extensions/nocobase-auth-split-layout/auth-split-layout.tsx +126 -0
  64. package/client/extensions/nocobase-device-approval/device-approval.tsx +277 -0
  65. package/client/extensions/nocobase-device-approval/locales/en-US.ts +55 -0
  66. package/client/extensions/nocobase-device-approval/locales/zh-CN.ts +27 -0
  67. package/client/extensions/nocobase-device-approval/use-device-approval.ts +176 -0
  68. package/client/extensions/nocobase-file-component-ui/README.md +13 -0
  69. package/client/extensions/nocobase-file-component-ui/components/file-list.tsx +147 -0
  70. package/client/extensions/nocobase-file-component-ui/components/file-preview-dialog.tsx +263 -0
  71. package/client/extensions/nocobase-file-component-ui/components/file-preview-field.tsx +68 -0
  72. package/client/extensions/nocobase-file-component-ui/components/file-thumbnail.tsx +73 -0
  73. package/client/extensions/nocobase-file-component-ui/components/file-upload-field.tsx +386 -0
  74. package/client/extensions/nocobase-file-component-ui/components/previewers/file-preview-content.tsx +255 -0
  75. package/client/extensions/nocobase-file-component-ui/components/previewers/office-open-xml-preview.tsx +226 -0
  76. package/client/extensions/nocobase-file-component-ui/index.ts +8 -0
  77. package/client/extensions/nocobase-file-component-ui/lib/file-preview.ts +194 -0
  78. package/client/extensions/nocobase-file-component-ui/lib/file-url.ts +22 -0
  79. package/client/extensions/nocobase-file-component-ui/lib/message-key.ts +18 -0
  80. package/client/extensions/nocobase-file-component-ui/types.ts +69 -0
  81. package/client/extensions/nocobase-inbox/decision-actions-bar.tsx +140 -0
  82. package/client/extensions/nocobase-inbox/frame.ts +31 -0
  83. package/client/extensions/nocobase-inbox/inbox-actions.ts +21 -0
  84. package/client/extensions/nocobase-inbox/inbox-detail.tsx +386 -0
  85. package/client/extensions/nocobase-inbox/inbox-item.tsx +274 -0
  86. package/client/extensions/nocobase-inbox/inbox-list.tsx +433 -0
  87. package/client/extensions/nocobase-inbox/inbox-page.tsx +545 -0
  88. package/client/extensions/nocobase-inbox/locales/en-US.ts +111 -0
  89. package/client/extensions/nocobase-inbox/locales/zh-CN.ts +57 -0
  90. package/client/extensions/nocobase-inbox/model.ts +428 -0
  91. package/client/extensions/nocobase-inbox/registry-scope.tsx +49 -0
  92. package/client/extensions/nocobase-inbox/registry.ts +303 -0
  93. package/client/extensions/nocobase-inbox/source.ts +117 -0
  94. package/client/hooks/use-mobile.ts +21 -0
  95. package/client/index.tsx +60 -0
  96. package/client/layouts/app-layout.tsx +105 -0
  97. package/client/layouts/components/app-brand.tsx +49 -0
  98. package/client/layouts/components/app-sidebar.tsx +196 -0
  99. package/client/layouts/components/header-actions.tsx +50 -0
  100. package/client/layouts/components/language-switcher.tsx +95 -0
  101. package/client/layouts/components/layout-header.tsx +17 -0
  102. package/client/layouts/components/navigation-menu.tsx +411 -0
  103. package/client/layouts/components/sidebar-footer.tsx +107 -0
  104. package/client/layouts/components/surface-empty.tsx +35 -0
  105. package/client/layouts/components/user-menu.tsx +127 -0
  106. package/client/layouts/index.ts +2 -0
  107. package/client/layouts/settings-layout.tsx +172 -0
  108. package/client/layouts/use-sidebar-preference.ts +57 -0
  109. package/client/lib/constants.ts +5 -0
  110. package/client/lib/toaster.ts +56 -0
  111. package/client/lib/utils.ts +1 -0
  112. package/client/locales/en-US.ts +178 -0
  113. package/client/locales/index.ts +9 -0
  114. package/client/locales/zh-CN.ts +161 -0
  115. package/client/pages/auth/device.tsx +41 -0
  116. package/client/pages/auth/forgot-password.tsx +39 -0
  117. package/client/pages/auth/login.tsx +66 -0
  118. package/client/pages/auth/register.tsx +46 -0
  119. package/client/pages/auth/reset-password.tsx +38 -0
  120. package/client/pages/auth/shared.tsx +111 -0
  121. package/client/pages/home.tsx +16 -0
  122. package/client/pages/inbox.tsx +12 -0
  123. package/client/plugins.ts +37 -0
  124. package/client/react-providers.ts +27 -0
  125. package/client/route-overrides.ts +9 -0
  126. package/client/routes.ts +75 -0
  127. package/client/routing/app-router.tsx +127 -0
  128. package/client/routing/client-route.tsx +129 -0
  129. package/client/routing/route-context.tsx +85 -0
  130. package/client/routing/route-navigation.ts +153 -0
  131. package/client/routing/route-tree.tsx +42 -0
  132. package/client/routing/standalone-page-layout.tsx +15 -0
  133. package/client/runtime.ts +26 -0
  134. package/client/service-provider.ts +40 -0
  135. package/client/source-extensions.ts +19 -0
  136. package/client/startup.tsx +22 -0
  137. package/client/styles.css +122 -0
  138. package/client/theme/index.ts +4 -0
  139. package/client/theme/theme-context.ts +12 -0
  140. package/client/theme/theme-preferences.ts +84 -0
  141. package/client/theme/theme-presets.ts +7 -0
  142. package/client/theme/theme-provider.tsx +106 -0
  143. package/client/theme/theme-settings.tsx +176 -0
  144. package/client/theme/themes/compact.css +140 -0
  145. package/client/theme/themes/default.css +117 -0
  146. package/client/vite-env.d.ts +5 -0
  147. package/components.json +27 -0
  148. package/config.example.yml +156 -0
  149. package/database/main/migrations/.gitkeep +0 -0
  150. package/database/main/migrations/202610060001_create_device_code.ts +37 -0
  151. package/database/main/seeds/.gitkeep +0 -0
  152. package/database/tsconfig.json +4 -0
  153. package/ecosystem.config.js +35 -0
  154. package/eslint.config.js +24 -0
  155. package/index.html +100 -0
  156. package/package.json +198 -3
  157. package/playwright.config.ts +10 -0
  158. package/public/assets/favicon.ico +0 -0
  159. package/public/assets/logo-dark.png +0 -0
  160. package/public/assets/logo-mark-dark.png +0 -0
  161. package/public/assets/logo-mark.png +0 -0
  162. package/public/assets/logo.png +0 -0
  163. package/server/app.ts +50 -0
  164. package/server/config/api.ts +8 -0
  165. package/server/config/app.ts +40 -0
  166. package/server/config/auth.ts +36 -0
  167. package/server/config/authorization.ts +18 -0
  168. package/server/config/caching.ts +21 -0
  169. package/server/config/database.ts +22 -0
  170. package/server/config/drive.ts +30 -0
  171. package/server/config/i18n.ts +17 -0
  172. package/server/config/index.ts +67 -0
  173. package/server/config/jobs.ts +27 -0
  174. package/server/config/logging.ts +23 -0
  175. package/server/config/notification.ts +11 -0
  176. package/server/config/queue.ts +28 -0
  177. package/server/config/scheduler.ts +23 -0
  178. package/server/config/secrets.ts +10 -0
  179. package/server/config/server.ts +26 -0
  180. package/server/config/session.ts +60 -0
  181. package/server/config/snowflake.ts +21 -0
  182. package/server/config/spa.ts +24 -0
  183. package/server/config/users.ts +11 -0
  184. package/server/config/workflow.ts +24 -0
  185. package/server/config.ts +24 -0
  186. package/server/embedded.ts +23 -0
  187. package/server/locales/en-US.ts +10 -0
  188. package/server/locales/index.ts +10 -0
  189. package/server/locales/zh-CN.ts +5 -0
  190. package/server/plugins.ts +37 -0
  191. package/server/providers/index.ts +5 -0
  192. package/server/routes/index.ts +6 -0
  193. package/server/runtime.ts +24 -0
  194. package/server/standalone.ts +29 -0
  195. package/server/tsconfig.json +4 -0
  196. package/tailwind.config.mjs +59 -0
  197. package/tests/components/app-layout-routes.test.tsx +132 -0
  198. package/tests/components/app-title.test.tsx +57 -0
  199. package/tests/components/auth-i18n.test.tsx +87 -0
  200. package/tests/components/back-button.test.tsx +80 -0
  201. package/tests/components/file-preview.test.tsx +101 -0
  202. package/tests/components/header-hover.test.tsx +87 -0
  203. package/tests/components/inbox.test.tsx +139 -0
  204. package/tests/components/language-switcher.test.tsx +298 -0
  205. package/tests/components/layout-containers.test.tsx +15 -0
  206. package/tests/components/layout-i18n.test.tsx +187 -0
  207. package/tests/components/loading.test.tsx +25 -0
  208. package/tests/components/navigation-menu.test.tsx +355 -0
  209. package/tests/components/page-harness.test.tsx +318 -0
  210. package/tests/components/primitive-labels.test.tsx +80 -0
  211. package/tests/components/sidebar-permissions.test.tsx +110 -0
  212. package/tests/components/sidebar-preference.test.tsx +42 -0
  213. package/tests/fixtures/theme-tokens.html +12 -0
  214. package/tests/fixtures/theme-tokens.tsx +111 -0
  215. package/tests/logic/account-permissions.test.tsx +204 -0
  216. package/tests/logic/app-locale-coverage.test.ts +136 -0
  217. package/tests/logic/app-server.test.ts +1589 -0
  218. package/tests/logic/asset-url-build.test.ts +106 -0
  219. package/tests/logic/client-auth.test.tsx +122 -0
  220. package/tests/logic/client-plugin-registry.test.ts +63 -0
  221. package/tests/logic/client-route-recovery.test.tsx +80 -0
  222. package/tests/logic/client-routes.test.ts +126 -0
  223. package/tests/logic/client-runtime.test.ts +112 -0
  224. package/tests/logic/client-settings.test.tsx +841 -0
  225. package/tests/logic/client-shell.test.tsx +466 -0
  226. package/tests/logic/client-theme.test.tsx +441 -0
  227. package/tests/logic/config.test.ts +197 -0
  228. package/tests/logic/dev-client-watch.test.ts +108 -0
  229. package/tests/logic/dev-routes-production-build.test.ts +168 -0
  230. package/tests/logic/device-code-migration.test.ts +70 -0
  231. package/tests/logic/lifecycle.test.ts +221 -0
  232. package/tests/logic/plugin-commands.test.ts +69 -0
  233. package/tests/logic/plugin-page-authz.test.ts +53 -0
  234. package/tests/logic/route-navigation.test.ts +66 -0
  235. package/tests/logic/route-overlay.test.tsx +353 -0
  236. package/tests/logic/server-imports.test.ts +88 -0
  237. package/tests/logic/tailwind-sources.test.ts +151 -0
  238. package/tests/logic/template-package-name.test.ts +88 -0
  239. package/tests/logic/theme-preferences.test.ts +86 -0
  240. package/tests/logic/theme-tokens.test.ts +248 -0
  241. package/tests/logic/toaster.test.tsx +108 -0
  242. package/tests/logic/user-menu-sign-out.test.tsx +98 -0
  243. package/tests/setup/client-config.ts +27 -0
  244. package/tsconfig.json +11 -0
  245. package/tsconfig.node.json +12 -0
  246. package/tsconfig.server.json +19 -0
  247. package/vite.config.ts +92 -0
  248. package/vitest.config.ts +44 -0
  249. package/README.md +0 -3
@@ -0,0 +1,271 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { ContextMenu as ContextMenuPrimitive } from '@base-ui/react/context-menu';
5
+ import { cn } from 'cn';
6
+ import { ChevronRightIcon, CheckIcon } from 'lucide-react';
7
+
8
+ function ContextMenu({ ...props }: ContextMenuPrimitive.Root.Props) {
9
+ return <ContextMenuPrimitive.Root data-slot='context-menu' {...props} />;
10
+ }
11
+
12
+ function ContextMenuPortal({ ...props }: ContextMenuPrimitive.Portal.Props) {
13
+ return (
14
+ <ContextMenuPrimitive.Portal data-slot='context-menu-portal' {...props} />
15
+ );
16
+ }
17
+
18
+ function ContextMenuTrigger({
19
+ className,
20
+ ...props
21
+ }: ContextMenuPrimitive.Trigger.Props) {
22
+ return (
23
+ <ContextMenuPrimitive.Trigger
24
+ data-slot='context-menu-trigger'
25
+ className={cn('select-none', className)}
26
+ {...props}
27
+ />
28
+ );
29
+ }
30
+
31
+ function ContextMenuContent({
32
+ className,
33
+ align = 'start',
34
+ alignOffset = 4,
35
+ side = 'right',
36
+ sideOffset = 0,
37
+ ...props
38
+ }: ContextMenuPrimitive.Popup.Props &
39
+ Pick<
40
+ ContextMenuPrimitive.Positioner.Props,
41
+ 'align' | 'alignOffset' | 'side' | 'sideOffset'
42
+ >) {
43
+ return (
44
+ <ContextMenuPrimitive.Portal>
45
+ <ContextMenuPrimitive.Positioner
46
+ className='isolate z-50 outline-none'
47
+ align={align}
48
+ alignOffset={alignOffset}
49
+ side={side}
50
+ sideOffset={sideOffset}
51
+ >
52
+ <ContextMenuPrimitive.Popup
53
+ data-slot='context-menu-content'
54
+ className={cn(
55
+ 'z-50 max-h-(--available-height) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
56
+ className,
57
+ )}
58
+ {...props}
59
+ />
60
+ </ContextMenuPrimitive.Positioner>
61
+ </ContextMenuPrimitive.Portal>
62
+ );
63
+ }
64
+
65
+ function ContextMenuGroup({ ...props }: ContextMenuPrimitive.Group.Props) {
66
+ return (
67
+ <ContextMenuPrimitive.Group data-slot='context-menu-group' {...props} />
68
+ );
69
+ }
70
+
71
+ function ContextMenuLabel({
72
+ className,
73
+ inset,
74
+ ...props
75
+ }: ContextMenuPrimitive.GroupLabel.Props & {
76
+ inset?: boolean;
77
+ }) {
78
+ return (
79
+ <ContextMenuPrimitive.GroupLabel
80
+ data-slot='context-menu-label'
81
+ data-inset={inset}
82
+ className={cn(
83
+ 'px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7',
84
+ className,
85
+ )}
86
+ {...props}
87
+ />
88
+ );
89
+ }
90
+
91
+ function ContextMenuItem({
92
+ className,
93
+ inset,
94
+ variant = 'default',
95
+ ...props
96
+ }: ContextMenuPrimitive.Item.Props & {
97
+ inset?: boolean;
98
+ variant?: 'default' | 'destructive';
99
+ }) {
100
+ return (
101
+ <ContextMenuPrimitive.Item
102
+ data-slot='context-menu-item'
103
+ data-inset={inset}
104
+ data-variant={variant}
105
+ className={cn(
106
+ "group/context-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 focus:*:[svg]:text-accent-foreground data-[variant=destructive]:*:[svg]:text-destructive",
107
+ className,
108
+ )}
109
+ {...props}
110
+ />
111
+ );
112
+ }
113
+
114
+ function ContextMenuSub({ ...props }: ContextMenuPrimitive.SubmenuRoot.Props) {
115
+ return (
116
+ <ContextMenuPrimitive.SubmenuRoot data-slot='context-menu-sub' {...props} />
117
+ );
118
+ }
119
+
120
+ function ContextMenuSubTrigger({
121
+ className,
122
+ inset,
123
+ children,
124
+ ...props
125
+ }: ContextMenuPrimitive.SubmenuTrigger.Props & {
126
+ inset?: boolean;
127
+ }) {
128
+ return (
129
+ <ContextMenuPrimitive.SubmenuTrigger
130
+ data-slot='context-menu-sub-trigger'
131
+ data-inset={inset}
132
+ className={cn(
133
+ "flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
134
+ className,
135
+ )}
136
+ {...props}
137
+ >
138
+ {children}
139
+ <ChevronRightIcon className='ml-auto' />
140
+ </ContextMenuPrimitive.SubmenuTrigger>
141
+ );
142
+ }
143
+
144
+ function ContextMenuSubContent({
145
+ ...props
146
+ }: React.ComponentProps<typeof ContextMenuContent>) {
147
+ return (
148
+ <ContextMenuContent
149
+ data-slot='context-menu-sub-content'
150
+ className='shadow-lg'
151
+ side='right'
152
+ {...props}
153
+ />
154
+ );
155
+ }
156
+
157
+ function ContextMenuCheckboxItem({
158
+ className,
159
+ children,
160
+ checked,
161
+ inset,
162
+ ...props
163
+ }: ContextMenuPrimitive.CheckboxItem.Props & {
164
+ inset?: boolean;
165
+ }) {
166
+ return (
167
+ <ContextMenuPrimitive.CheckboxItem
168
+ data-slot='context-menu-checkbox-item'
169
+ data-inset={inset}
170
+ className={cn(
171
+ "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
172
+ className,
173
+ )}
174
+ checked={checked}
175
+ {...props}
176
+ >
177
+ <span className='pointer-events-none absolute right-2'>
178
+ <ContextMenuPrimitive.CheckboxItemIndicator>
179
+ <CheckIcon />
180
+ </ContextMenuPrimitive.CheckboxItemIndicator>
181
+ </span>
182
+ {children}
183
+ </ContextMenuPrimitive.CheckboxItem>
184
+ );
185
+ }
186
+
187
+ function ContextMenuRadioGroup({
188
+ ...props
189
+ }: ContextMenuPrimitive.RadioGroup.Props) {
190
+ return (
191
+ <ContextMenuPrimitive.RadioGroup
192
+ data-slot='context-menu-radio-group'
193
+ {...props}
194
+ />
195
+ );
196
+ }
197
+
198
+ function ContextMenuRadioItem({
199
+ className,
200
+ children,
201
+ inset,
202
+ ...props
203
+ }: ContextMenuPrimitive.RadioItem.Props & {
204
+ inset?: boolean;
205
+ }) {
206
+ return (
207
+ <ContextMenuPrimitive.RadioItem
208
+ data-slot='context-menu-radio-item'
209
+ data-inset={inset}
210
+ className={cn(
211
+ "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
212
+ className,
213
+ )}
214
+ {...props}
215
+ >
216
+ <span className='pointer-events-none absolute right-2'>
217
+ <ContextMenuPrimitive.RadioItemIndicator>
218
+ <CheckIcon />
219
+ </ContextMenuPrimitive.RadioItemIndicator>
220
+ </span>
221
+ {children}
222
+ </ContextMenuPrimitive.RadioItem>
223
+ );
224
+ }
225
+
226
+ function ContextMenuSeparator({
227
+ className,
228
+ ...props
229
+ }: ContextMenuPrimitive.Separator.Props) {
230
+ return (
231
+ <ContextMenuPrimitive.Separator
232
+ data-slot='context-menu-separator'
233
+ className={cn('-mx-1 my-1 h-px bg-border', className)}
234
+ {...props}
235
+ />
236
+ );
237
+ }
238
+
239
+ function ContextMenuShortcut({
240
+ className,
241
+ ...props
242
+ }: React.ComponentProps<'span'>) {
243
+ return (
244
+ <span
245
+ data-slot='context-menu-shortcut'
246
+ className={cn(
247
+ 'ml-auto text-xs tracking-widest text-muted-foreground group-focus/context-menu-item:text-accent-foreground',
248
+ className,
249
+ )}
250
+ {...props}
251
+ />
252
+ );
253
+ }
254
+
255
+ export {
256
+ ContextMenu,
257
+ ContextMenuTrigger,
258
+ ContextMenuContent,
259
+ ContextMenuItem,
260
+ ContextMenuCheckboxItem,
261
+ ContextMenuRadioItem,
262
+ ContextMenuLabel,
263
+ ContextMenuSeparator,
264
+ ContextMenuShortcut,
265
+ ContextMenuGroup,
266
+ ContextMenuPortal,
267
+ ContextMenuSub,
268
+ ContextMenuSubContent,
269
+ ContextMenuSubTrigger,
270
+ ContextMenuRadioGroup,
271
+ };
@@ -0,0 +1,160 @@
1
+ import { useTranslation } from '@nocobase/i18n/client';
2
+ import * as React from 'react';
3
+ import { Dialog as DialogPrimitive } from '@base-ui/react/dialog';
4
+ import { cn } from 'cn';
5
+
6
+ import { Button } from '#components/ui/button';
7
+ import { XIcon } from 'lucide-react';
8
+
9
+ function Dialog({ ...props }: DialogPrimitive.Root.Props) {
10
+ return <DialogPrimitive.Root data-slot='dialog' {...props} />;
11
+ }
12
+
13
+ function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
14
+ return <DialogPrimitive.Trigger data-slot='dialog-trigger' {...props} />;
15
+ }
16
+
17
+ function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {
18
+ return <DialogPrimitive.Portal data-slot='dialog-portal' {...props} />;
19
+ }
20
+
21
+ function DialogClose({ ...props }: DialogPrimitive.Close.Props) {
22
+ return <DialogPrimitive.Close data-slot='dialog-close' {...props} />;
23
+ }
24
+
25
+ function DialogOverlay({
26
+ className,
27
+ ...props
28
+ }: DialogPrimitive.Backdrop.Props) {
29
+ return (
30
+ <DialogPrimitive.Backdrop
31
+ data-slot='dialog-overlay'
32
+ className={cn(
33
+ 'fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0',
34
+ className,
35
+ )}
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ function DialogContent({
42
+ className,
43
+ children,
44
+ showCloseButton = true,
45
+ ...props
46
+ }: DialogPrimitive.Popup.Props & {
47
+ showCloseButton?: boolean;
48
+ }) {
49
+ const { t } = useTranslation();
50
+ return (
51
+ <DialogPortal>
52
+ <DialogOverlay />
53
+ <DialogPrimitive.Popup
54
+ data-slot='dialog-content'
55
+ className={cn(
56
+ 'fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
57
+ className,
58
+ )}
59
+ {...props}
60
+ >
61
+ {children}
62
+ {showCloseButton && (
63
+ <DialogPrimitive.Close
64
+ data-slot='dialog-close'
65
+ render={
66
+ <Button
67
+ variant='ghost'
68
+ className='absolute top-2 right-2'
69
+ size='icon-sm'
70
+ />
71
+ }
72
+ >
73
+ <XIcon />
74
+ <span className='sr-only'>{t('actions.close')}</span>
75
+ </DialogPrimitive.Close>
76
+ )}
77
+ </DialogPrimitive.Popup>
78
+ </DialogPortal>
79
+ );
80
+ }
81
+
82
+ function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
83
+ return (
84
+ <div
85
+ data-slot='dialog-header'
86
+ className={cn('flex flex-col gap-2', className)}
87
+ {...props}
88
+ />
89
+ );
90
+ }
91
+
92
+ function DialogFooter({
93
+ className,
94
+ showCloseButton = false,
95
+ children,
96
+ ...props
97
+ }: React.ComponentProps<'div'> & {
98
+ showCloseButton?: boolean;
99
+ }) {
100
+ const { t } = useTranslation();
101
+ return (
102
+ <div
103
+ data-slot='dialog-footer'
104
+ className={cn(
105
+ '-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end',
106
+ className,
107
+ )}
108
+ {...props}
109
+ >
110
+ {children}
111
+ {showCloseButton && (
112
+ <DialogPrimitive.Close render={<Button variant='outline' />}>
113
+ {t('actions.close')}
114
+ </DialogPrimitive.Close>
115
+ )}
116
+ </div>
117
+ );
118
+ }
119
+
120
+ function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
121
+ return (
122
+ <DialogPrimitive.Title
123
+ data-slot='dialog-title'
124
+ className={cn(
125
+ 'font-heading text-base leading-none font-medium',
126
+ className,
127
+ )}
128
+ {...props}
129
+ />
130
+ );
131
+ }
132
+
133
+ function DialogDescription({
134
+ className,
135
+ ...props
136
+ }: DialogPrimitive.Description.Props) {
137
+ return (
138
+ <DialogPrimitive.Description
139
+ data-slot='dialog-description'
140
+ className={cn(
141
+ 'text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground',
142
+ className,
143
+ )}
144
+ {...props}
145
+ />
146
+ );
147
+ }
148
+
149
+ export {
150
+ Dialog,
151
+ DialogClose,
152
+ DialogContent,
153
+ DialogDescription,
154
+ DialogFooter,
155
+ DialogHeader,
156
+ DialogOverlay,
157
+ DialogPortal,
158
+ DialogTitle,
159
+ DialogTrigger,
160
+ };
@@ -0,0 +1,270 @@
1
+ import * as React from 'react';
2
+ import { Menu as MenuPrimitive } from '@base-ui/react/menu';
3
+
4
+ import { cn } from 'cn';
5
+ import { ChevronRightIcon, CheckIcon } from 'lucide-react';
6
+
7
+ function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
8
+ return <MenuPrimitive.Root data-slot='dropdown-menu' {...props} />;
9
+ }
10
+
11
+ function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
12
+ return <MenuPrimitive.Portal data-slot='dropdown-menu-portal' {...props} />;
13
+ }
14
+
15
+ function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
16
+ return <MenuPrimitive.Trigger data-slot='dropdown-menu-trigger' {...props} />;
17
+ }
18
+
19
+ function DropdownMenuContent({
20
+ align = 'start',
21
+ alignOffset = 0,
22
+ side = 'bottom',
23
+ sideOffset = 4,
24
+ className,
25
+ ...props
26
+ }: MenuPrimitive.Popup.Props &
27
+ Pick<
28
+ MenuPrimitive.Positioner.Props,
29
+ 'align' | 'alignOffset' | 'side' | 'sideOffset'
30
+ >) {
31
+ return (
32
+ <MenuPrimitive.Portal>
33
+ <MenuPrimitive.Positioner
34
+ className='isolate z-50 outline-none'
35
+ align={align}
36
+ alignOffset={alignOffset}
37
+ side={side}
38
+ sideOffset={sideOffset}
39
+ >
40
+ <MenuPrimitive.Popup
41
+ data-slot='dropdown-menu-content'
42
+ className={cn(
43
+ 'z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95',
44
+ className,
45
+ )}
46
+ {...props}
47
+ />
48
+ </MenuPrimitive.Positioner>
49
+ </MenuPrimitive.Portal>
50
+ );
51
+ }
52
+
53
+ function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
54
+ return <MenuPrimitive.Group data-slot='dropdown-menu-group' {...props} />;
55
+ }
56
+
57
+ function DropdownMenuLabel({
58
+ className,
59
+ inset,
60
+ ...props
61
+ }: MenuPrimitive.GroupLabel.Props & {
62
+ inset?: boolean;
63
+ }) {
64
+ return (
65
+ <MenuPrimitive.GroupLabel
66
+ data-slot='dropdown-menu-label'
67
+ data-inset={inset}
68
+ className={cn(
69
+ 'px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7',
70
+ className,
71
+ )}
72
+ {...props}
73
+ />
74
+ );
75
+ }
76
+
77
+ function DropdownMenuItem({
78
+ className,
79
+ inset,
80
+ variant = 'default',
81
+ ...props
82
+ }: MenuPrimitive.Item.Props & {
83
+ inset?: boolean;
84
+ variant?: 'default' | 'destructive';
85
+ }) {
86
+ return (
87
+ <MenuPrimitive.Item
88
+ data-slot='dropdown-menu-item'
89
+ data-inset={inset}
90
+ data-variant={variant}
91
+ className={cn(
92
+ "group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
93
+ className,
94
+ )}
95
+ {...props}
96
+ />
97
+ );
98
+ }
99
+
100
+ function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
101
+ return <MenuPrimitive.SubmenuRoot data-slot='dropdown-menu-sub' {...props} />;
102
+ }
103
+
104
+ function DropdownMenuSubTrigger({
105
+ className,
106
+ inset,
107
+ children,
108
+ ...props
109
+ }: MenuPrimitive.SubmenuTrigger.Props & {
110
+ inset?: boolean;
111
+ }) {
112
+ return (
113
+ <MenuPrimitive.SubmenuTrigger
114
+ data-slot='dropdown-menu-sub-trigger'
115
+ data-inset={inset}
116
+ className={cn(
117
+ "flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-popup-open:bg-accent data-popup-open:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
118
+ className,
119
+ )}
120
+ {...props}
121
+ >
122
+ {children}
123
+ <ChevronRightIcon className='ml-auto' />
124
+ </MenuPrimitive.SubmenuTrigger>
125
+ );
126
+ }
127
+
128
+ function DropdownMenuSubContent({
129
+ align = 'start',
130
+ alignOffset = -3,
131
+ side = 'right',
132
+ sideOffset = 0,
133
+ className,
134
+ ...props
135
+ }: React.ComponentProps<typeof DropdownMenuContent>) {
136
+ return (
137
+ <DropdownMenuContent
138
+ data-slot='dropdown-menu-sub-content'
139
+ className={cn(
140
+ 'w-auto min-w-[96px] rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
141
+ className,
142
+ )}
143
+ align={align}
144
+ alignOffset={alignOffset}
145
+ side={side}
146
+ sideOffset={sideOffset}
147
+ {...props}
148
+ />
149
+ );
150
+ }
151
+
152
+ function DropdownMenuCheckboxItem({
153
+ className,
154
+ children,
155
+ checked,
156
+ inset,
157
+ ...props
158
+ }: MenuPrimitive.CheckboxItem.Props & {
159
+ inset?: boolean;
160
+ }) {
161
+ return (
162
+ <MenuPrimitive.CheckboxItem
163
+ data-slot='dropdown-menu-checkbox-item'
164
+ data-inset={inset}
165
+ className={cn(
166
+ "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
167
+ className,
168
+ )}
169
+ checked={checked}
170
+ {...props}
171
+ >
172
+ <span
173
+ className='pointer-events-none absolute right-2 flex items-center justify-center'
174
+ data-slot='dropdown-menu-checkbox-item-indicator'
175
+ >
176
+ <MenuPrimitive.CheckboxItemIndicator>
177
+ <CheckIcon />
178
+ </MenuPrimitive.CheckboxItemIndicator>
179
+ </span>
180
+ {children}
181
+ </MenuPrimitive.CheckboxItem>
182
+ );
183
+ }
184
+
185
+ function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
186
+ return (
187
+ <MenuPrimitive.RadioGroup
188
+ data-slot='dropdown-menu-radio-group'
189
+ {...props}
190
+ />
191
+ );
192
+ }
193
+
194
+ function DropdownMenuRadioItem({
195
+ className,
196
+ children,
197
+ inset,
198
+ ...props
199
+ }: MenuPrimitive.RadioItem.Props & {
200
+ inset?: boolean;
201
+ }) {
202
+ return (
203
+ <MenuPrimitive.RadioItem
204
+ data-slot='dropdown-menu-radio-item'
205
+ data-inset={inset}
206
+ className={cn(
207
+ "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
208
+ className,
209
+ )}
210
+ {...props}
211
+ >
212
+ <span
213
+ className='pointer-events-none absolute right-2 flex items-center justify-center'
214
+ data-slot='dropdown-menu-radio-item-indicator'
215
+ >
216
+ <MenuPrimitive.RadioItemIndicator>
217
+ <CheckIcon />
218
+ </MenuPrimitive.RadioItemIndicator>
219
+ </span>
220
+ {children}
221
+ </MenuPrimitive.RadioItem>
222
+ );
223
+ }
224
+
225
+ function DropdownMenuSeparator({
226
+ className,
227
+ ...props
228
+ }: MenuPrimitive.Separator.Props) {
229
+ return (
230
+ <MenuPrimitive.Separator
231
+ data-slot='dropdown-menu-separator'
232
+ className={cn('-mx-1 my-1 h-px bg-border', className)}
233
+ {...props}
234
+ />
235
+ );
236
+ }
237
+
238
+ function DropdownMenuShortcut({
239
+ className,
240
+ ...props
241
+ }: React.ComponentProps<'span'>) {
242
+ return (
243
+ <span
244
+ data-slot='dropdown-menu-shortcut'
245
+ className={cn(
246
+ 'ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground',
247
+ className,
248
+ )}
249
+ {...props}
250
+ />
251
+ );
252
+ }
253
+
254
+ export {
255
+ DropdownMenu,
256
+ DropdownMenuPortal,
257
+ DropdownMenuTrigger,
258
+ DropdownMenuContent,
259
+ DropdownMenuGroup,
260
+ DropdownMenuLabel,
261
+ DropdownMenuItem,
262
+ DropdownMenuCheckboxItem,
263
+ DropdownMenuRadioGroup,
264
+ DropdownMenuRadioItem,
265
+ DropdownMenuSeparator,
266
+ DropdownMenuShortcut,
267
+ DropdownMenuSub,
268
+ DropdownMenuSubTrigger,
269
+ DropdownMenuSubContent,
270
+ };