@yimingliao/cms 0.0.83 → 0.0.85

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,4 +1,4 @@
1
- import { R as Result, S as SuccessResult, d as ErrorResult, g as AdminFull } from '../types-BGsFazJr.js';
1
+ import { z as Translation, R as Result, S as SuccessResult, d as ErrorResult, g as AdminFull, D as DeviceInfo } from '../types-BGsFazJr.js';
2
2
  import { Logger } from 'logry';
3
3
  import * as _tanstack_react_query from '@tanstack/react-query';
4
4
  import { QueryClient, UseMutationOptions, UseQueryOptions } from '@tanstack/react-query';
@@ -28,6 +28,17 @@ interface UIStates {
28
28
  isDisabled?: boolean;
29
29
  isError?: boolean;
30
30
  }
31
+ interface FormContext<T> {
32
+ formData?: T;
33
+ setFormData?: React$1.Dispatch<React$1.SetStateAction<T>>;
34
+ }
35
+ interface FieldContext {
36
+ fieldName?: string;
37
+ translations?: Translation[];
38
+ errors?: string[];
39
+ }
40
+ interface FormFieldController<T = Record<string, unknown>> extends UIStates, FormContext<T>, FieldContext {
41
+ }
31
42
 
32
43
  interface FetchContext {
33
44
  input: string;
@@ -342,17 +353,27 @@ declare const buttonVariants: (props?: ({
342
353
  variant?: "link" | "default" | "success" | "destructive" | "outline" | "secondary" | "ghost" | "warning" | null | undefined;
343
354
  size?: "lg" | "sm" | "default" | "icon" | "icon-sm" | "icon-lg" | "xs" | null | undefined;
344
355
  } & class_variance_authority_types.ClassProp) | undefined) => string;
345
- type ShadcnButtonProps = React$1.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
356
+ type ButtonProps$1 = React$1.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
346
357
  asChild?: boolean;
347
358
  };
348
359
 
349
- interface ButtonProps extends ShadcnButtonProps, UIStates {
360
+ interface ButtonProps extends ButtonProps$1, UIStates {
350
361
  icon?: LucideIcon;
351
362
  href?: string;
352
363
  openNewTab?: boolean;
353
364
  }
354
365
  declare function Button({ icon, href, openNewTab, isDisabled, isLoading, children, ...props }: ButtonProps): react_jsx_runtime.JSX.Element;
355
366
 
367
+ interface InputProps<T = Record<string, unknown>> extends ComponentProps<"input">, FormFieldController<T> {
368
+ }
369
+ declare function Input<T>({ fieldName, setFormData, isLoading, isDisabled, isError, children, ...props }: InputProps<T>): react_jsx_runtime.JSX.Element;
370
+
371
+ declare function AuthForm({ title, children, className, ...props }: ComponentProps<"form"> & {
372
+ title: string;
373
+ }): react_jsx_runtime.JSX.Element;
374
+
356
375
  declare const cn: (...inputs: ClassValue[]) => string;
357
376
 
358
- export { AdminProvider, Button, type ButtonProps, Form, type ShowToastOption, cn, createAdminInitializer, createRequestInterceptor, createResponseInterceptor, createSmartFetch, createUseCommand, createUseQuery, handleToast, useAdmin };
377
+ declare function useDeviceInfo(): DeviceInfo | null;
378
+
379
+ export { AdminProvider, AuthForm, Button, type ButtonProps, Form, Input, type InputProps, type ShowToastOption, cn, createAdminInitializer, createRequestInterceptor, createResponseInterceptor, createSmartFetch, createUseCommand, createUseQuery, handleToast, useAdmin, useDeviceInfo };
@@ -6,6 +6,7 @@ import * as React2 from 'react';
6
6
  import { createContext, useState, useContext, useEffect } from 'react';
7
7
  import { clsx } from 'clsx';
8
8
  import { twMerge } from 'tailwind-merge';
9
+ import { UAParser } from 'ua-parser-js';
9
10
  import { useRouter } from 'next/navigation';
10
11
  import { Slot } from '@radix-ui/react-slot';
11
12
  import { cva } from 'class-variance-authority';
@@ -244,6 +245,27 @@ function createAdminInitializer({
244
245
  var cn = (...inputs) => {
245
246
  return twMerge(clsx(inputs));
246
247
  };
248
+ function useDeviceInfo() {
249
+ const [deviceInfo, setDeviceInfo] = useState(null);
250
+ useEffect(() => {
251
+ const parser = new UAParser(navigator.userAgent);
252
+ const result = parser.getResult();
253
+ setDeviceInfo({
254
+ deviceType: result.device.type || "desktop",
255
+ platform: result.os.name || "Unknown",
256
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
257
+ language: navigator.language,
258
+ screenResolution: {
259
+ width: window.screen.width,
260
+ height: window.screen.height
261
+ },
262
+ browser: result.browser.name || "Unknown",
263
+ browserVersion: result.browser.version || "",
264
+ userAgent: navigator.userAgent
265
+ });
266
+ }, []);
267
+ return deviceInfo;
268
+ }
247
269
  function Form({
248
270
  onSubmit,
249
271
  className,
@@ -286,7 +308,7 @@ var buttonVariants = cva(
286
308
  }
287
309
  }
288
310
  );
289
- function ShadCnButton({
311
+ function Button({
290
312
  className,
291
313
  variant,
292
314
  size,
@@ -314,18 +336,17 @@ function Spinner({ className, ...props }) {
314
336
  }
315
337
  );
316
338
  }
317
- function Button({
339
+ function Button2({
318
340
  icon,
319
341
  href,
320
342
  openNewTab = false,
321
- // processing states
343
+ // ui states
322
344
  isDisabled = false,
323
345
  isLoading = false,
324
346
  // base
325
347
  children,
326
348
  ...props
327
349
  }) {
328
- isDisabled = isDisabled || isLoading;
329
350
  const router = useRouter();
330
351
  const handleClick = () => {
331
352
  if (!href) return;
@@ -336,10 +357,10 @@ function Button({
336
357
  }
337
358
  };
338
359
  return /* @__PURE__ */ jsx(
339
- ShadCnButton,
360
+ Button,
340
361
  {
341
362
  type: props.type ?? "button",
342
- disabled: isDisabled,
363
+ disabled: isDisabled || isLoading,
343
364
  onClick: props.onClick ?? handleClick,
344
365
  ...props,
345
366
  children: isLoading ? /* @__PURE__ */ jsx(Spinner, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -349,5 +370,200 @@ function Button({
349
370
  }
350
371
  );
351
372
  }
373
+ function Input({ className, type, ...props }) {
374
+ return /* @__PURE__ */ jsx(
375
+ "input",
376
+ {
377
+ type,
378
+ "data-slot": "input",
379
+ className: cn(
380
+ "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
381
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
382
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
383
+ className
384
+ ),
385
+ ...props
386
+ }
387
+ );
388
+ }
389
+ function InputGroup({ className, ...props }) {
390
+ return /* @__PURE__ */ jsx(
391
+ "div",
392
+ {
393
+ "data-slot": "input-group",
394
+ role: "group",
395
+ className: cn(
396
+ "group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none",
397
+ "h-9 min-w-0 has-[>textarea]:h-auto",
398
+ // Variants based on alignment.
399
+ "has-[>[data-align=inline-start]]:[&>input]:pl-2",
400
+ "has-[>[data-align=inline-end]]:[&>input]:pr-2",
401
+ "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
402
+ "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
403
+ // Focus state.
404
+ "has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]",
405
+ // Error state.
406
+ "has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
407
+ className
408
+ ),
409
+ ...props
410
+ }
411
+ );
412
+ }
413
+ var inputGroupAddonVariants = cva(
414
+ "text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50",
415
+ {
416
+ variants: {
417
+ align: {
418
+ "inline-start": "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
419
+ "inline-end": "order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",
420
+ "block-start": "order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5",
421
+ "block-end": "order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5"
422
+ }
423
+ },
424
+ defaultVariants: {
425
+ align: "inline-start"
426
+ }
427
+ }
428
+ );
429
+ function InputGroupAddon({
430
+ className,
431
+ align = "inline-start",
432
+ ...props
433
+ }) {
434
+ return /* @__PURE__ */ jsx(
435
+ "div",
436
+ {
437
+ role: "group",
438
+ "data-slot": "input-group-addon",
439
+ "data-align": align,
440
+ className: cn(inputGroupAddonVariants({ align }), className),
441
+ onClick: (e) => {
442
+ if (e.target.closest("button")) {
443
+ return;
444
+ }
445
+ e.currentTarget.parentElement?.querySelector("input")?.focus();
446
+ },
447
+ ...props
448
+ }
449
+ );
450
+ }
451
+ cva(
452
+ "text-sm shadow-none flex gap-2 items-center",
453
+ {
454
+ variants: {
455
+ size: {
456
+ xs: "h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2",
457
+ sm: "h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5",
458
+ "icon-xs": "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
459
+ "icon-sm": "size-8 p-0 has-[>svg]:p-0"
460
+ }
461
+ },
462
+ defaultVariants: {
463
+ size: "xs"
464
+ }
465
+ }
466
+ );
467
+ function InputGroupInput({
468
+ className,
469
+ ...props
470
+ }) {
471
+ return /* @__PURE__ */ jsx(
472
+ Input,
473
+ {
474
+ "data-slot": "input-group-control",
475
+ className: cn(
476
+ "flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent",
477
+ className
478
+ ),
479
+ ...props
480
+ }
481
+ );
482
+ }
483
+ function Input2({
484
+ // form context
485
+ fieldName,
486
+ setFormData,
487
+ // ui states
488
+ isLoading = false,
489
+ isDisabled = false,
490
+ isError = false,
491
+ // base
492
+ children,
493
+ ...props
494
+ }) {
495
+ return /* @__PURE__ */ jsxs(InputGroup, { "data-disabled": isDisabled || isLoading, children: [
496
+ isLoading ? /* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(Spinner, {}) }) : /* @__PURE__ */ jsx(
497
+ InputGroupInput,
498
+ {
499
+ disabled: isDisabled || isLoading,
500
+ "aria-invalid": isError,
501
+ onChange: (e) => {
502
+ if (!setFormData || !fieldName) return;
503
+ setFormData((p) => ({ ...p, [fieldName]: e.target.value }));
504
+ },
505
+ ...props
506
+ }
507
+ ),
508
+ children
509
+ ] });
510
+ }
511
+ function Card({ className, ...props }) {
512
+ return /* @__PURE__ */ jsx(
513
+ "div",
514
+ {
515
+ "data-slot": "card",
516
+ className: cn(
517
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
518
+ className
519
+ ),
520
+ ...props
521
+ }
522
+ );
523
+ }
524
+ function CardHeader({ className, ...props }) {
525
+ return /* @__PURE__ */ jsx(
526
+ "div",
527
+ {
528
+ "data-slot": "card-header",
529
+ className: cn(
530
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
531
+ className
532
+ ),
533
+ ...props
534
+ }
535
+ );
536
+ }
537
+ function CardTitle({ className, ...props }) {
538
+ return /* @__PURE__ */ jsx(
539
+ "div",
540
+ {
541
+ "data-slot": "card-title",
542
+ className: cn("leading-none font-semibold", className),
543
+ ...props
544
+ }
545
+ );
546
+ }
547
+ function CardContent({ className, ...props }) {
548
+ return /* @__PURE__ */ jsx(
549
+ "div",
550
+ {
551
+ "data-slot": "card-content",
552
+ className: cn("px-6", className),
553
+ ...props
554
+ }
555
+ );
556
+ }
557
+ function AuthForm({
558
+ title,
559
+ children,
560
+ className,
561
+ ...props
562
+ }) {
563
+ return /* @__PURE__ */ jsx(Form, { className: cn("mx-auto mt-20 w-96", className), ...props, children: /* @__PURE__ */ jsxs(Card, { children: [
564
+ /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { className: "mx-auto", children: title }) }),
565
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx("div", { className: "relative flex flex-col gap-6", children }) })
566
+ ] }) });
567
+ }
352
568
 
353
- export { AdminProvider, Button, Form, cn, createAdminInitializer, createRequestInterceptor, createResponseInterceptor, createSmartFetch, createUseCommand, createUseQuery, handleToast, useAdmin };
569
+ export { AdminProvider, AuthForm, Button2 as Button, Form, Input2 as Input, cn, createAdminInitializer, createRequestInterceptor, createResponseInterceptor, createSmartFetch, createUseCommand, createUseQuery, handleToast, useAdmin, useDeviceInfo };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yimingliao/cms",
3
- "version": "0.0.83",
3
+ "version": "0.0.85",
4
4
  "author": "Yiming Liao",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -47,7 +47,6 @@
47
47
  "@radix-ui/react-slot": "^1.2.4",
48
48
  "argon2": "^0.44.0",
49
49
  "class-variance-authority": "^0.7.1",
50
- "clsx": "^2.1.1",
51
50
  "jsonwebtoken": "^9.0.3",
52
51
  "keyv": "^5.6.0",
53
52
  "logry": "^2.1.6",
@@ -55,7 +54,7 @@
55
54
  "mime-types": "^3.0.2",
56
55
  "nodemailer": "^8.0.1",
57
56
  "sonner": "^2.0.7",
58
- "tailwind-merge": "^3.5.0",
57
+ "ua-parser-js": "^2.0.9",
59
58
  "ulid": "^3.0.2"
60
59
  },
61
60
  "devDependencies": {
@@ -68,6 +67,7 @@
68
67
  "@types/nodemailer": "^7.0.11",
69
68
  "@types/react": "^19.2.14",
70
69
  "@types/ssh2-sftp-client": "^9.0.6",
70
+ "clsx": "^2.1.1",
71
71
  "eslint": "^9",
72
72
  "eslint-import-resolver-typescript": "^4.4.4",
73
73
  "eslint-plugin-import": "^2.32.0",
@@ -82,6 +82,7 @@
82
82
  "next": "^16.1.6",
83
83
  "prisma": "6.5.0",
84
84
  "react": "^19.2.4",
85
+ "tailwind-merge": "^3.5.0",
85
86
  "tsup": "^8.5.1",
86
87
  "typescript": "^5.9.3",
87
88
  "typescript-eslint": "^8.56.1",