@yimingliao/cms 0.0.93 → 0.0.95
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.
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
2
|
import { twMerge } from 'tailwind-merge';
|
|
3
3
|
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
4
|
+
import { usePathname } from 'next/navigation';
|
|
4
5
|
import { UAParser } from 'ua-parser-js';
|
|
5
6
|
import { Slot } from '@radix-ui/react-slot';
|
|
6
7
|
import { cva } from 'class-variance-authority';
|
|
7
8
|
import { jsx } from 'react/jsx-runtime';
|
|
8
9
|
import { Loader2Icon } from 'lucide-react';
|
|
9
10
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
11
|
+
import * as SeparatorPrimitive from '@radix-ui/react-separator';
|
|
10
12
|
|
|
11
13
|
// src/client/applications/ui/utils.ts
|
|
12
14
|
var cn = (...inputs) => {
|
|
@@ -42,6 +44,17 @@ var useCountdown = (initialTime) => {
|
|
|
42
44
|
}, [isCounting]);
|
|
43
45
|
return { timeLeft, isCounting, startCountdown };
|
|
44
46
|
};
|
|
47
|
+
var useParentPathname = () => {
|
|
48
|
+
const pathname = usePathname();
|
|
49
|
+
if (pathname === "/") return "/";
|
|
50
|
+
const index = pathname.lastIndexOf("/");
|
|
51
|
+
return index <= 0 ? "/" : pathname.slice(0, index);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// src/client/applications/ui/is-confirm.ts
|
|
55
|
+
var isConfirm = (t, key = "ui.dialog.confirm.text") => {
|
|
56
|
+
return confirm(t(key));
|
|
57
|
+
};
|
|
45
58
|
function useDeviceInfo() {
|
|
46
59
|
const [deviceInfo, setDeviceInfo] = useState(null);
|
|
47
60
|
useEffect(() => {
|
|
@@ -384,5 +397,25 @@ function Label({ className, ...props }) {
|
|
|
384
397
|
}
|
|
385
398
|
);
|
|
386
399
|
}
|
|
400
|
+
function Separator({
|
|
401
|
+
className,
|
|
402
|
+
orientation = "horizontal",
|
|
403
|
+
decorative = true,
|
|
404
|
+
...props
|
|
405
|
+
}) {
|
|
406
|
+
return /* @__PURE__ */ jsx(
|
|
407
|
+
SeparatorPrimitive.Root,
|
|
408
|
+
{
|
|
409
|
+
"data-slot": "separator",
|
|
410
|
+
decorative,
|
|
411
|
+
orientation,
|
|
412
|
+
className: cn(
|
|
413
|
+
"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
|
414
|
+
className
|
|
415
|
+
),
|
|
416
|
+
...props
|
|
417
|
+
}
|
|
418
|
+
);
|
|
419
|
+
}
|
|
387
420
|
|
|
388
|
-
export { Button, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, Label, Spinner, Textarea, cn, useCountdown, useDeviceInfo };
|
|
421
|
+
export { Button, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, Label, Separator, Spinner, Textarea, cn, isConfirm, useCountdown, useDeviceInfo, useParentPathname };
|
package/dist/client/index.d.ts
CHANGED
|
@@ -6,9 +6,9 @@ import * as _tanstack_query_core from '@tanstack/query-core';
|
|
|
6
6
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
7
7
|
import { c as createVerifyAction, a as createSignInAction, b as createVerifyEmailAction, d as createEmailUnverifiedAction, e as createForgotPasswordAction, f as createResetPasswordAction, g as createChangePasswordAction } from '../create-reset-password-action-D6aTuuqO.js';
|
|
8
8
|
import * as React$1 from 'react';
|
|
9
|
-
import {
|
|
10
|
-
import { L as LabelProps, B as ButtonProps$1 } from '../label-BF4qxS03.js';
|
|
9
|
+
import { ReactNode, ComponentProps, HTMLAttributes } from 'react';
|
|
11
10
|
import { LucideIcon } from 'lucide-react';
|
|
11
|
+
import { B as ButtonProps$1, L as LabelProps } from '../label-BF4qxS03.js';
|
|
12
12
|
import { ClassValue } from 'clsx';
|
|
13
13
|
import 'zod';
|
|
14
14
|
import 'zod/v4/core';
|
|
@@ -26,18 +26,18 @@ import 'class-variance-authority';
|
|
|
26
26
|
import '@radix-ui/react-label';
|
|
27
27
|
|
|
28
28
|
interface UIStates {
|
|
29
|
-
isLoading?: boolean;
|
|
30
|
-
isDisabled?: boolean;
|
|
31
|
-
isError?: boolean;
|
|
29
|
+
isLoading?: boolean | undefined;
|
|
30
|
+
isDisabled?: boolean | undefined;
|
|
31
|
+
isError?: boolean | undefined;
|
|
32
32
|
}
|
|
33
33
|
interface FormContext<T> {
|
|
34
|
-
formData?: T;
|
|
35
|
-
setFormData?: React$1.Dispatch<React$1.SetStateAction<T
|
|
34
|
+
formData?: T | undefined;
|
|
35
|
+
setFormData?: React$1.Dispatch<React$1.SetStateAction<T>> | undefined;
|
|
36
36
|
}
|
|
37
37
|
interface FieldContext {
|
|
38
|
-
fieldName?: string;
|
|
39
|
-
translations?: Translation[];
|
|
40
|
-
errors?: string[];
|
|
38
|
+
fieldName?: string | undefined;
|
|
39
|
+
translations?: Translation[] | undefined;
|
|
40
|
+
errors?: string[] | undefined;
|
|
41
41
|
}
|
|
42
42
|
interface FormFieldController<T = Record<string, unknown>> extends UIStates, FormContext<T>, FieldContext {
|
|
43
43
|
}
|
|
@@ -349,6 +349,52 @@ declare function createAdminInitializer({ useAdmin, useQuery, verifyAction, }: {
|
|
|
349
349
|
verifyAction: ReturnType<typeof createVerifyAction>;
|
|
350
350
|
}): () => null;
|
|
351
351
|
|
|
352
|
+
interface PageHeaderTitleProps {
|
|
353
|
+
icon?: LucideIcon;
|
|
354
|
+
title?: ReactNode;
|
|
355
|
+
subtitle?: ReactNode;
|
|
356
|
+
children?: ReactNode;
|
|
357
|
+
leftChildren?: ReactNode;
|
|
358
|
+
className?: string;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
type Variant = "default" | "index" | "create" | "show" | "edit" | "batch" | "batch-create" | "trash";
|
|
362
|
+
|
|
363
|
+
interface ButtonProps extends ButtonProps$1, UIStates {
|
|
364
|
+
icon?: LucideIcon | undefined;
|
|
365
|
+
href?: string | undefined;
|
|
366
|
+
openNewTab?: boolean | undefined;
|
|
367
|
+
}
|
|
368
|
+
declare function Button({ icon, href, openNewTab, isDisabled, isLoading, children, ...props }: ButtonProps): react_jsx_runtime.JSX.Element;
|
|
369
|
+
|
|
370
|
+
interface InputProps<T = Record<string, unknown>> extends ComponentProps<"input">, FormFieldController<T> {
|
|
371
|
+
}
|
|
372
|
+
declare function Input<T>({ fieldName, setFormData, isLoading, isDisabled, isError, children, ...props }: InputProps<T>): react_jsx_runtime.JSX.Element;
|
|
373
|
+
|
|
374
|
+
interface PageHeaderProps {
|
|
375
|
+
titleProps?: PageHeaderTitleProps;
|
|
376
|
+
leftChildren?: ReactNode;
|
|
377
|
+
rightChildren?: ReactNode;
|
|
378
|
+
variant?: Variant;
|
|
379
|
+
returnButtonProps?: ButtonProps;
|
|
380
|
+
createButtonProps?: ButtonProps;
|
|
381
|
+
createCategoryButtonProps?: ButtonProps;
|
|
382
|
+
editButtonProps?: ButtonProps;
|
|
383
|
+
showButtonProps?: ButtonProps;
|
|
384
|
+
settingButtonProps?: ButtonProps;
|
|
385
|
+
batchButtonProps?: ButtonProps;
|
|
386
|
+
destroyButtonProps?: ButtonProps;
|
|
387
|
+
restoreButtonProps?: ButtonProps;
|
|
388
|
+
isLocked?: boolean;
|
|
389
|
+
isDisabled?: boolean;
|
|
390
|
+
isNotFound?: boolean;
|
|
391
|
+
batchCreateButtonHref?: string;
|
|
392
|
+
selectAllFn?: () => void;
|
|
393
|
+
cancelAllFn?: () => void;
|
|
394
|
+
showTopicSettingButton?: boolean;
|
|
395
|
+
}
|
|
396
|
+
declare function PageHeader(props: PageHeaderProps): react_jsx_runtime.JSX.Element;
|
|
397
|
+
|
|
352
398
|
declare function Form({ onSubmit, className, ...props }: ComponentProps<"form">): react_jsx_runtime.JSX.Element;
|
|
353
399
|
|
|
354
400
|
interface FieldProps extends LabelProps {
|
|
@@ -367,17 +413,6 @@ interface FieldBodyProps extends HTMLAttributes<HTMLDivElement>, UIStates {
|
|
|
367
413
|
}
|
|
368
414
|
declare function FieldBody({ isLoading, isDisabled, isEmpty, className, backgroundClassName, childrenClassName, children, ...props }: FieldBodyProps): react_jsx_runtime.JSX.Element;
|
|
369
415
|
|
|
370
|
-
interface ButtonProps extends ButtonProps$1, UIStates {
|
|
371
|
-
icon?: LucideIcon;
|
|
372
|
-
href?: string;
|
|
373
|
-
openNewTab?: boolean;
|
|
374
|
-
}
|
|
375
|
-
declare function Button({ icon, href, openNewTab, isDisabled, isLoading, children, ...props }: ButtonProps): react_jsx_runtime.JSX.Element;
|
|
376
|
-
|
|
377
|
-
interface InputProps<T = Record<string, unknown>> extends ComponentProps<"input">, FormFieldController<T> {
|
|
378
|
-
}
|
|
379
|
-
declare function Input<T>({ fieldName, setFormData, isLoading, isDisabled, isError, children, ...props }: InputProps<T>): react_jsx_runtime.JSX.Element;
|
|
380
|
-
|
|
381
416
|
declare function PasswordInput<T>({ ...props }: InputProps<T>): react_jsx_runtime.JSX.Element;
|
|
382
417
|
|
|
383
418
|
/**
|
|
@@ -456,4 +491,4 @@ declare const cn: (...inputs: ClassValue[]) => string;
|
|
|
456
491
|
|
|
457
492
|
declare function useDeviceInfo(): DeviceInfo | null;
|
|
458
493
|
|
|
459
|
-
export { AdminProvider, Button, type ButtonProps, Field, FieldBody, Form, Input, type InputProps, PasswordInput, type ShowToastOption, cn, createAdminInitializer, createChangePasswordPage, createEmailUnverifiedPage, createForgotPasswordPage, createRequestInterceptor, createResetPasswordPage, createResponseInterceptor, createSignInPage, createSmartFetch, createUseCommand, createUseQuery, createVerifyEmailPage, handleToast, useAdmin, useDeviceInfo };
|
|
494
|
+
export { AdminProvider, Button, type ButtonProps, Field, FieldBody, Form, Input, type InputProps, PageHeader, PasswordInput, type ShowToastOption, cn, createAdminInitializer, createChangePasswordPage, createEmailUnverifiedPage, createForgotPasswordPage, createRequestInterceptor, createResetPasswordPage, createResponseInterceptor, createSignInPage, createSmartFetch, createUseCommand, createUseQuery, createVerifyEmailPage, handleToast, useAdmin, useDeviceInfo };
|
package/dist/client/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { cn, useDeviceInfo } from '../chunk-
|
|
1
|
+
import { Button, Spinner, InputGroup, InputGroupAddon, InputGroupInput, cn, Separator, Label, InputGroupButton, useDeviceInfo, Card, CardHeader, CardTitle, CardContent, useCountdown, CardDescription, useParentPathname, isConfirm } from '../chunk-FLKUBNE4.js';
|
|
2
|
+
export { cn, useDeviceInfo } from '../chunk-FLKUBNE4.js';
|
|
3
3
|
import { ensureArray } from '../chunk-OAENV763.js';
|
|
4
4
|
import { toast } from 'sonner';
|
|
5
5
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
6
6
|
import { useMutation, useQuery } from '@tanstack/react-query';
|
|
7
7
|
import { createContext, useState, useContext, useEffect, createElement } from 'react';
|
|
8
|
-
import { Asterisk, Eye, EyeOff, Mail } from 'lucide-react';
|
|
9
8
|
import { useTranslator } from 'intor/react';
|
|
9
|
+
import { Files, Asterisk, Eye, EyeOff, Mail, Trash2, CopyCheck, CopyX, FileSymlink, FileX, FilePlus, FilePen, File, FileStack, FolderSearch, Lock, FolderCog, FileSpreadsheet, Undo2 } from 'lucide-react';
|
|
10
10
|
import { useRouter, useSearchParams } from 'next/navigation';
|
|
11
11
|
import Link from 'next/link';
|
|
12
12
|
|
|
@@ -240,6 +240,362 @@ function createAdminInitializer({
|
|
|
240
240
|
return null;
|
|
241
241
|
};
|
|
242
242
|
}
|
|
243
|
+
|
|
244
|
+
// src/client/interfaces/styles/constants.ts
|
|
245
|
+
var PAGE_HEADER_HEIGHT = 56;
|
|
246
|
+
function PageHeaderTitle({
|
|
247
|
+
icon,
|
|
248
|
+
title,
|
|
249
|
+
subtitle,
|
|
250
|
+
children,
|
|
251
|
+
leftChildren,
|
|
252
|
+
className
|
|
253
|
+
}) {
|
|
254
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-3", className), children: [
|
|
255
|
+
leftChildren,
|
|
256
|
+
icon && createElement(icon),
|
|
257
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-center w-fit gap-3 whitespace-nowrap", children: [
|
|
258
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: subtitle }),
|
|
259
|
+
/* @__PURE__ */ jsx("p", { className: "scroll-m-20 text-xl font-semibold tracking-tight", children: title })
|
|
260
|
+
] }),
|
|
261
|
+
children
|
|
262
|
+
] });
|
|
263
|
+
}
|
|
264
|
+
function Button2({
|
|
265
|
+
icon,
|
|
266
|
+
href,
|
|
267
|
+
openNewTab = false,
|
|
268
|
+
// ui states
|
|
269
|
+
isDisabled = false,
|
|
270
|
+
isLoading = false,
|
|
271
|
+
// base
|
|
272
|
+
children,
|
|
273
|
+
...props
|
|
274
|
+
}) {
|
|
275
|
+
const router = useRouter();
|
|
276
|
+
const handleClick = () => {
|
|
277
|
+
if (!href) return;
|
|
278
|
+
if (openNewTab) {
|
|
279
|
+
window.open(href, "_blank");
|
|
280
|
+
} else {
|
|
281
|
+
router.push(href);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
return /* @__PURE__ */ jsx(
|
|
285
|
+
Button,
|
|
286
|
+
{
|
|
287
|
+
type: props.type ?? "button",
|
|
288
|
+
disabled: isDisabled || isLoading,
|
|
289
|
+
onClick: props.onClick ?? handleClick,
|
|
290
|
+
...props,
|
|
291
|
+
children: isLoading ? /* @__PURE__ */ jsx(Spinner, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
292
|
+
icon && createElement(icon),
|
|
293
|
+
children
|
|
294
|
+
] })
|
|
295
|
+
}
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
function Input({
|
|
299
|
+
// form context
|
|
300
|
+
fieldName,
|
|
301
|
+
setFormData,
|
|
302
|
+
// ui states
|
|
303
|
+
isLoading = false,
|
|
304
|
+
isDisabled = false,
|
|
305
|
+
isError = false,
|
|
306
|
+
// base
|
|
307
|
+
children,
|
|
308
|
+
...props
|
|
309
|
+
}) {
|
|
310
|
+
return /* @__PURE__ */ jsxs(InputGroup, { "data-disabled": isDisabled || isLoading, children: [
|
|
311
|
+
isLoading ? /* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(Spinner, {}) }) : /* @__PURE__ */ jsx(
|
|
312
|
+
InputGroupInput,
|
|
313
|
+
{
|
|
314
|
+
disabled: isDisabled || isLoading,
|
|
315
|
+
"aria-invalid": isError,
|
|
316
|
+
onChange: (e) => {
|
|
317
|
+
if (!setFormData || !fieldName) return;
|
|
318
|
+
setFormData((p) => ({ ...p, [fieldName]: e.target.value }));
|
|
319
|
+
},
|
|
320
|
+
...props
|
|
321
|
+
}
|
|
322
|
+
),
|
|
323
|
+
children
|
|
324
|
+
] });
|
|
325
|
+
}
|
|
326
|
+
function ReturnButton({
|
|
327
|
+
icon,
|
|
328
|
+
useIcon = true,
|
|
329
|
+
// use in create/edit page
|
|
330
|
+
useConfirm = false,
|
|
331
|
+
// navigation
|
|
332
|
+
href,
|
|
333
|
+
replace = false,
|
|
334
|
+
pushToParent = false,
|
|
335
|
+
replaceParent = false,
|
|
336
|
+
// base
|
|
337
|
+
className,
|
|
338
|
+
children,
|
|
339
|
+
...props
|
|
340
|
+
}) {
|
|
341
|
+
const { t } = useTranslator();
|
|
342
|
+
const router = useRouter();
|
|
343
|
+
const parentPath = useParentPathname();
|
|
344
|
+
const handelClick = () => {
|
|
345
|
+
if (useConfirm) {
|
|
346
|
+
if (!isConfirm(t)) return;
|
|
347
|
+
}
|
|
348
|
+
if (href) {
|
|
349
|
+
return replace ? router.replace(href) : router.push(href);
|
|
350
|
+
}
|
|
351
|
+
if (replaceParent) {
|
|
352
|
+
return router.replace(parentPath);
|
|
353
|
+
}
|
|
354
|
+
if (pushToParent) {
|
|
355
|
+
return router.push(parentPath);
|
|
356
|
+
}
|
|
357
|
+
router.back();
|
|
358
|
+
};
|
|
359
|
+
return /* @__PURE__ */ jsx(
|
|
360
|
+
Button2,
|
|
361
|
+
{
|
|
362
|
+
variant: "outline",
|
|
363
|
+
size: "sm",
|
|
364
|
+
icon: useIcon ? icon || Undo2 : void 0,
|
|
365
|
+
onClick: handelClick,
|
|
366
|
+
className,
|
|
367
|
+
...props,
|
|
368
|
+
children: children ?? t("ui.button.return.text")
|
|
369
|
+
}
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
function createIndexPreset(ctx) {
|
|
373
|
+
const { props, t } = ctx;
|
|
374
|
+
return {
|
|
375
|
+
left: /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(ReturnButton, { pushToParent: true, ...props.returnButtonProps }) }),
|
|
376
|
+
titleProps: {
|
|
377
|
+
icon: Files,
|
|
378
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
379
|
+
props.settingButtonProps && props.showTopicSettingButton && /* @__PURE__ */ jsx(
|
|
380
|
+
Button2,
|
|
381
|
+
{
|
|
382
|
+
...props.settingButtonProps,
|
|
383
|
+
variant: "outline",
|
|
384
|
+
icon: FolderCog,
|
|
385
|
+
isDisabled: props.settingButtonProps.isDisabled ?? props.isDisabled,
|
|
386
|
+
children: `${t("ui.button.setting.text")} ${t("resources.topic.text")}`
|
|
387
|
+
}
|
|
388
|
+
),
|
|
389
|
+
props.createCategoryButtonProps && /* @__PURE__ */ jsx(
|
|
390
|
+
Button2,
|
|
391
|
+
{
|
|
392
|
+
...props.createCategoryButtonProps,
|
|
393
|
+
variant: "success",
|
|
394
|
+
icon: FileSpreadsheet,
|
|
395
|
+
isDisabled: props.createCategoryButtonProps.isDisabled ?? props.isDisabled,
|
|
396
|
+
children: t("ui.button.create.text")
|
|
397
|
+
}
|
|
398
|
+
),
|
|
399
|
+
props.createButtonProps && /* @__PURE__ */ jsx(
|
|
400
|
+
Button2,
|
|
401
|
+
{
|
|
402
|
+
variant: "success",
|
|
403
|
+
icon: FilePlus,
|
|
404
|
+
...props.createButtonProps,
|
|
405
|
+
children: t("ui.button.create.text")
|
|
406
|
+
}
|
|
407
|
+
)
|
|
408
|
+
] })
|
|
409
|
+
},
|
|
410
|
+
right: props.batchButtonProps && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(Button2, { variant: "outline", icon: FileStack, ...props.batchButtonProps, children: /* @__PURE__ */ jsx("span", { className: "text-sm", children: t("ui.button.batch.text") }) }) })
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
function createBatchPreset(ctx) {
|
|
414
|
+
const { props, t } = ctx;
|
|
415
|
+
return {
|
|
416
|
+
left: /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(ReturnButton, { pushToParent: true, ...props.returnButtonProps, children: t("ui.button.exit-batch-mode.text") }) }),
|
|
417
|
+
titleProps: {
|
|
418
|
+
icon: FilePen,
|
|
419
|
+
subtitle: t("ui.page-header.batch.subtitle.text"),
|
|
420
|
+
children: /* @__PURE__ */ jsx(
|
|
421
|
+
Button2,
|
|
422
|
+
{
|
|
423
|
+
variant: "success",
|
|
424
|
+
href: props.batchCreateButtonHref,
|
|
425
|
+
icon: FilePlus,
|
|
426
|
+
children: t("ui.button.batch-create.text")
|
|
427
|
+
}
|
|
428
|
+
)
|
|
429
|
+
},
|
|
430
|
+
right: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
431
|
+
/* @__PURE__ */ jsx(Button2, { size: "icon", variant: "outline", onClick: props.selectAllFn, children: /* @__PURE__ */ jsx(CopyCheck, {}) }),
|
|
432
|
+
/* @__PURE__ */ jsx(Button2, { size: "icon", variant: "outline", onClick: props.cancelAllFn, children: /* @__PURE__ */ jsx(CopyX, {}) }),
|
|
433
|
+
/* @__PURE__ */ jsx(
|
|
434
|
+
Button2,
|
|
435
|
+
{
|
|
436
|
+
...props.destroyButtonProps,
|
|
437
|
+
variant: "destructive",
|
|
438
|
+
icon: FileX,
|
|
439
|
+
children: t("ui.button.destroy.text")
|
|
440
|
+
}
|
|
441
|
+
)
|
|
442
|
+
] })
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
function createBatchCreatePreset(ctx) {
|
|
446
|
+
const { props, t } = ctx;
|
|
447
|
+
return {
|
|
448
|
+
left: /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(ReturnButton, { useConfirm: true, replaceParent: true, ...props.returnButtonProps }) }),
|
|
449
|
+
titleProps: {
|
|
450
|
+
icon: FilePlus,
|
|
451
|
+
subtitle: t("ui.page-header.batch-create.subtitle.text")
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
function createCreatePreset(ctx) {
|
|
456
|
+
const { props, t } = ctx;
|
|
457
|
+
return {
|
|
458
|
+
left: /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(ReturnButton, { useConfirm: true, replaceParent: true, ...props.returnButtonProps }) }),
|
|
459
|
+
titleProps: {
|
|
460
|
+
icon: FilePlus,
|
|
461
|
+
subtitle: t("ui.page-header.create.subtitle.text")
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
function createEditPreset(ctx) {
|
|
466
|
+
const { props, t } = ctx;
|
|
467
|
+
return {
|
|
468
|
+
left: /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(ReturnButton, { useConfirm: true, replaceParent: true, ...props.returnButtonProps }) }),
|
|
469
|
+
titleProps: {
|
|
470
|
+
icon: FilePen,
|
|
471
|
+
subtitle: t("ui.page-header.edit.subtitle.text")
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
function createShowPreset(ctx) {
|
|
476
|
+
const { props, t } = ctx;
|
|
477
|
+
return {
|
|
478
|
+
left: /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(ReturnButton, { pushToParent: true, ...props.returnButtonProps }) }),
|
|
479
|
+
titleProps: {
|
|
480
|
+
icon: File,
|
|
481
|
+
children: !props.isNotFound && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
482
|
+
props.showButtonProps && /* @__PURE__ */ jsx(
|
|
483
|
+
Button2,
|
|
484
|
+
{
|
|
485
|
+
...props.showButtonProps,
|
|
486
|
+
variant: "outline",
|
|
487
|
+
icon: FolderSearch,
|
|
488
|
+
isDisabled: props.showButtonProps.isDisabled || props.isDisabled,
|
|
489
|
+
children: `${t("ui.button.show.text")} ${t("resources.related.text")}`
|
|
490
|
+
}
|
|
491
|
+
),
|
|
492
|
+
props.editButtonProps && /* @__PURE__ */ jsx(
|
|
493
|
+
Button2,
|
|
494
|
+
{
|
|
495
|
+
...props.editButtonProps,
|
|
496
|
+
variant: "warning",
|
|
497
|
+
icon: FilePen,
|
|
498
|
+
isDisabled: props.editButtonProps.isDisabled || props.isDisabled,
|
|
499
|
+
children: t("ui.button.edit.text")
|
|
500
|
+
}
|
|
501
|
+
),
|
|
502
|
+
props.destroyButtonProps && /* @__PURE__ */ jsx(
|
|
503
|
+
Button2,
|
|
504
|
+
{
|
|
505
|
+
...props.destroyButtonProps,
|
|
506
|
+
variant: "destructive",
|
|
507
|
+
icon: FileX,
|
|
508
|
+
isDisabled: props.destroyButtonProps.isDisabled || props.isDisabled,
|
|
509
|
+
children: t("ui.button.destroy.text")
|
|
510
|
+
}
|
|
511
|
+
),
|
|
512
|
+
/* @__PURE__ */ jsx(
|
|
513
|
+
Lock,
|
|
514
|
+
{
|
|
515
|
+
className: cn(
|
|
516
|
+
props.isLocked && !props.isDisabled ? "opacity-100" : "opacity-0"
|
|
517
|
+
)
|
|
518
|
+
}
|
|
519
|
+
)
|
|
520
|
+
] })
|
|
521
|
+
},
|
|
522
|
+
right: props.batchButtonProps && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(Button2, { variant: "outline", icon: FileStack, ...props.batchButtonProps, children: /* @__PURE__ */ jsx("span", { className: "text-sm", children: t("ui.button.batch.text") }) }) })
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
function createTrashPreset(ctx) {
|
|
526
|
+
const { props, t } = ctx;
|
|
527
|
+
return {
|
|
528
|
+
left: /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(ReturnButton, { pushToParent: true, ...props.returnButtonProps }) }),
|
|
529
|
+
titleProps: {
|
|
530
|
+
icon: Trash2,
|
|
531
|
+
title: t("main.trash.text")
|
|
532
|
+
},
|
|
533
|
+
right: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
534
|
+
/* @__PURE__ */ jsx(Button2, { size: "icon", variant: "outline", onClick: props.selectAllFn, children: /* @__PURE__ */ jsx(CopyCheck, {}) }),
|
|
535
|
+
/* @__PURE__ */ jsx(Button2, { size: "icon", variant: "outline", onClick: props.cancelAllFn, children: /* @__PURE__ */ jsx(CopyX, {}) }),
|
|
536
|
+
/* @__PURE__ */ jsx(
|
|
537
|
+
Button2,
|
|
538
|
+
{
|
|
539
|
+
...props.restoreButtonProps,
|
|
540
|
+
variant: "success",
|
|
541
|
+
icon: FileSymlink,
|
|
542
|
+
children: t("ui.button.restore.text")
|
|
543
|
+
}
|
|
544
|
+
),
|
|
545
|
+
/* @__PURE__ */ jsx(
|
|
546
|
+
Button2,
|
|
547
|
+
{
|
|
548
|
+
...props.destroyButtonProps,
|
|
549
|
+
variant: "destructive",
|
|
550
|
+
icon: FileX,
|
|
551
|
+
children: t("ui.button.destroy.text")
|
|
552
|
+
}
|
|
553
|
+
)
|
|
554
|
+
] })
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
var PRESET_BUILDERS = {
|
|
558
|
+
default: () => ({ titleProps: { icon: Files } }),
|
|
559
|
+
index: createIndexPreset,
|
|
560
|
+
create: createCreatePreset,
|
|
561
|
+
show: createShowPreset,
|
|
562
|
+
edit: createEditPreset,
|
|
563
|
+
batch: createBatchPreset,
|
|
564
|
+
"batch-create": createBatchCreatePreset,
|
|
565
|
+
trash: createTrashPreset
|
|
566
|
+
};
|
|
567
|
+
function PageHeader(props) {
|
|
568
|
+
const { t } = useTranslator();
|
|
569
|
+
const variant = props.variant ?? "default";
|
|
570
|
+
const isDefault = variant === "default";
|
|
571
|
+
const preset = PRESET_BUILDERS[variant]({ props, t });
|
|
572
|
+
const left = props.leftChildren ?? preset.left;
|
|
573
|
+
const resolvedTitleProps = { ...preset.titleProps, ...props.titleProps };
|
|
574
|
+
const right = props.rightChildren ?? preset.right;
|
|
575
|
+
return /* @__PURE__ */ jsxs("div", { style: { height: PAGE_HEADER_HEIGHT }, children: [
|
|
576
|
+
/* @__PURE__ */ jsxs(
|
|
577
|
+
"div",
|
|
578
|
+
{
|
|
579
|
+
className: cn(
|
|
580
|
+
"relative h-full px-6",
|
|
581
|
+
"flex items-center justify-between gap-3"
|
|
582
|
+
),
|
|
583
|
+
children: [
|
|
584
|
+
!isDefault && left && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: left }),
|
|
585
|
+
/* @__PURE__ */ jsx(
|
|
586
|
+
PageHeaderTitle,
|
|
587
|
+
{
|
|
588
|
+
className: cn(!isDefault && "absolute left-1/2 -translate-x-1/2"),
|
|
589
|
+
...resolvedTitleProps
|
|
590
|
+
}
|
|
591
|
+
),
|
|
592
|
+
right && /* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center gap-3", children: right })
|
|
593
|
+
]
|
|
594
|
+
}
|
|
595
|
+
),
|
|
596
|
+
/* @__PURE__ */ jsx(Separator, {})
|
|
597
|
+
] });
|
|
598
|
+
}
|
|
243
599
|
function Form({
|
|
244
600
|
onSubmit,
|
|
245
601
|
className,
|
|
@@ -327,68 +683,6 @@ function FieldBody({
|
|
|
327
683
|
}
|
|
328
684
|
);
|
|
329
685
|
}
|
|
330
|
-
function Button2({
|
|
331
|
-
icon,
|
|
332
|
-
href,
|
|
333
|
-
openNewTab = false,
|
|
334
|
-
// ui states
|
|
335
|
-
isDisabled = false,
|
|
336
|
-
isLoading = false,
|
|
337
|
-
// base
|
|
338
|
-
children,
|
|
339
|
-
...props
|
|
340
|
-
}) {
|
|
341
|
-
const router = useRouter();
|
|
342
|
-
const handleClick = () => {
|
|
343
|
-
if (!href) return;
|
|
344
|
-
if (openNewTab) {
|
|
345
|
-
window.open(href, "_blank");
|
|
346
|
-
} else {
|
|
347
|
-
router.push(href);
|
|
348
|
-
}
|
|
349
|
-
};
|
|
350
|
-
return /* @__PURE__ */ jsx(
|
|
351
|
-
Button,
|
|
352
|
-
{
|
|
353
|
-
type: props.type ?? "button",
|
|
354
|
-
disabled: isDisabled || isLoading,
|
|
355
|
-
onClick: props.onClick ?? handleClick,
|
|
356
|
-
...props,
|
|
357
|
-
children: isLoading ? /* @__PURE__ */ jsx(Spinner, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
358
|
-
icon && createElement(icon),
|
|
359
|
-
children
|
|
360
|
-
] })
|
|
361
|
-
}
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
function Input({
|
|
365
|
-
// form context
|
|
366
|
-
fieldName,
|
|
367
|
-
setFormData,
|
|
368
|
-
// ui states
|
|
369
|
-
isLoading = false,
|
|
370
|
-
isDisabled = false,
|
|
371
|
-
isError = false,
|
|
372
|
-
// base
|
|
373
|
-
children,
|
|
374
|
-
...props
|
|
375
|
-
}) {
|
|
376
|
-
return /* @__PURE__ */ jsxs(InputGroup, { "data-disabled": isDisabled || isLoading, children: [
|
|
377
|
-
isLoading ? /* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(Spinner, {}) }) : /* @__PURE__ */ jsx(
|
|
378
|
-
InputGroupInput,
|
|
379
|
-
{
|
|
380
|
-
disabled: isDisabled || isLoading,
|
|
381
|
-
"aria-invalid": isError,
|
|
382
|
-
onChange: (e) => {
|
|
383
|
-
if (!setFormData || !fieldName) return;
|
|
384
|
-
setFormData((p) => ({ ...p, [fieldName]: e.target.value }));
|
|
385
|
-
},
|
|
386
|
-
...props
|
|
387
|
-
}
|
|
388
|
-
),
|
|
389
|
-
children
|
|
390
|
-
] });
|
|
391
|
-
}
|
|
392
686
|
function PasswordInput({ ...props }) {
|
|
393
687
|
const [showPassword, setShowPassword] = useState(false);
|
|
394
688
|
return /* @__PURE__ */ jsx(Input, { type: showPassword ? "text" : "password", ...props, children: /* @__PURE__ */ jsx(InputGroupAddon, { align: "inline-end", children: /* @__PURE__ */ jsx(
|
|
@@ -879,4 +1173,4 @@ function createChangePasswordPage({
|
|
|
879
1173
|
};
|
|
880
1174
|
}
|
|
881
1175
|
|
|
882
|
-
export { AdminProvider, Button2 as Button, Field, FieldBody, Form, Input, PasswordInput, createAdminInitializer, createChangePasswordPage, createEmailUnverifiedPage, createForgotPasswordPage, createRequestInterceptor, createResetPasswordPage, createResponseInterceptor, createSignInPage, createSmartFetch, createUseCommand, createUseQuery, createVerifyEmailPage, handleToast, useAdmin };
|
|
1176
|
+
export { AdminProvider, Button2 as Button, Field, FieldBody, Form, Input, PageHeader, PasswordInput, createAdminInitializer, createChangePasswordPage, createEmailUnverifiedPage, createForgotPasswordPage, createRequestInterceptor, createResetPasswordPage, createResponseInterceptor, createSignInPage, createSmartFetch, createUseCommand, createUseQuery, createVerifyEmailPage, handleToast, useAdmin };
|
|
@@ -5,6 +5,7 @@ import * as React from 'react';
|
|
|
5
5
|
import { ComponentProps } from 'react';
|
|
6
6
|
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
7
7
|
import { VariantProps } from 'class-variance-authority';
|
|
8
|
+
import * as SeparatorPrimitive from '@radix-ui/react-separator';
|
|
8
9
|
import '@radix-ui/react-label';
|
|
9
10
|
|
|
10
11
|
declare function Card({ className, ...props }: React.ComponentProps<"div">): react_jsx_runtime.JSX.Element;
|
|
@@ -34,4 +35,6 @@ declare function Spinner({ className, ...props }: ComponentProps<"svg">): react_
|
|
|
34
35
|
|
|
35
36
|
declare function Textarea({ className, ...props }: React.ComponentProps<"textarea">): react_jsx_runtime.JSX.Element;
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
declare function Separator({ className, orientation, decorative, ...props }: React.ComponentProps<typeof SeparatorPrimitive.Root>): react_jsx_runtime.JSX.Element;
|
|
39
|
+
|
|
40
|
+
export { Button, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, Separator, Spinner, Textarea };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { Button, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, Label, Spinner, Textarea } from '../../chunk-
|
|
1
|
+
export { Button, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, Label, Separator, Spinner, Textarea } from '../../chunk-FLKUBNE4.js';
|
package/dist/server/index.js
CHANGED
|
@@ -3251,8 +3251,16 @@ function createFileSoftDeleteManyAction(ctx) {
|
|
|
3251
3251
|
const { ids: ids2 } = await fileSoftDeleteManyValidator(schemas).parseAsync(formData);
|
|
3252
3252
|
const foundFiles = await fileQueryRepository.findManyByIds({ ids: ids2 });
|
|
3253
3253
|
if (foundFiles.length === 0) throw ServerError.notFound();
|
|
3254
|
-
const
|
|
3255
|
-
const
|
|
3254
|
+
const lockedFiles = [];
|
|
3255
|
+
const targetIds = [];
|
|
3256
|
+
for (const file of foundFiles) {
|
|
3257
|
+
if (isFileLocked(file)) {
|
|
3258
|
+
lockedFiles.push(file.id);
|
|
3259
|
+
} else {
|
|
3260
|
+
targetIds.push(file.id);
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
const hasLockedFile = lockedFiles.length > 0;
|
|
3256
3264
|
const count = await fileCommandRepository.softDeleteMany({
|
|
3257
3265
|
ids: targetIds
|
|
3258
3266
|
});
|
|
@@ -3260,7 +3268,7 @@ function createFileSoftDeleteManyAction(ctx) {
|
|
|
3260
3268
|
throw new ServerError({ i18nKey: "error.files-batch-destroy-no" });
|
|
3261
3269
|
}
|
|
3262
3270
|
return {
|
|
3263
|
-
i18nKey: hasLockedFile ? "ok.destroy-
|
|
3271
|
+
i18nKey: hasLockedFile ? "ok.files-batch-destroy-has-locked" : "ok.destroy-ok"
|
|
3264
3272
|
};
|
|
3265
3273
|
},
|
|
3266
3274
|
{ type: "command" }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yimingliao/cms",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.95",
|
|
4
4
|
"author": "Yiming Liao",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@keyv/redis": "^5.1.6",
|
|
52
52
|
"@radix-ui/react-label": "^2.1.8",
|
|
53
|
+
"@radix-ui/react-separator": "^1.1.8",
|
|
53
54
|
"@radix-ui/react-slot": "^1.2.4",
|
|
54
55
|
"argon2": "^0.44.0",
|
|
55
56
|
"class-variance-authority": "^0.7.1",
|