@withwiz/gallery 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.
Files changed (77) hide show
  1. package/README.ko.md +484 -0
  2. package/README.md +484 -0
  3. package/dist/chunk-B3MQL6ZR.mjs +79 -0
  4. package/dist/chunk-B3MQL6ZR.mjs.map +1 -0
  5. package/dist/chunk-BC3TPJQY.mjs +122 -0
  6. package/dist/chunk-BC3TPJQY.mjs.map +1 -0
  7. package/dist/chunk-BTLELFKC.mjs +11 -0
  8. package/dist/chunk-BTLELFKC.mjs.map +1 -0
  9. package/dist/chunk-GDUGKVDQ.mjs +50 -0
  10. package/dist/chunk-GDUGKVDQ.mjs.map +1 -0
  11. package/dist/chunk-GFOWZGJX.js +50 -0
  12. package/dist/chunk-GFOWZGJX.js.map +1 -0
  13. package/dist/chunk-ILSZPLME.js +47 -0
  14. package/dist/chunk-ILSZPLME.js.map +1 -0
  15. package/dist/chunk-M7CCTDUG.js +122 -0
  16. package/dist/chunk-M7CCTDUG.js.map +1 -0
  17. package/dist/chunk-WVRTM6E7.js +19 -0
  18. package/dist/chunk-WVRTM6E7.js.map +1 -0
  19. package/dist/chunk-XLPMNJJV.js +11 -0
  20. package/dist/chunk-XLPMNJJV.js.map +1 -0
  21. package/dist/chunk-XSPWM4DD.js +79 -0
  22. package/dist/chunk-XSPWM4DD.js.map +1 -0
  23. package/dist/chunk-YUMB3GBN.mjs +47 -0
  24. package/dist/chunk-YUMB3GBN.mjs.map +1 -0
  25. package/dist/chunk-ZEZCUJJD.mjs +19 -0
  26. package/dist/chunk-ZEZCUJJD.mjs.map +1 -0
  27. package/dist/components/gallery.css +1074 -0
  28. package/dist/components/index.d.mts +156 -0
  29. package/dist/components/index.d.ts +156 -0
  30. package/dist/components/index.js +1215 -0
  31. package/dist/components/index.js.map +1 -0
  32. package/dist/components/index.mjs +1215 -0
  33. package/dist/components/index.mjs.map +1 -0
  34. package/dist/errors-B9gzUsX-.d.ts +39 -0
  35. package/dist/errors-DYrlDCzP.d.mts +39 -0
  36. package/dist/gallery.css +1074 -0
  37. package/dist/hooks/index.d.mts +65 -0
  38. package/dist/hooks/index.d.ts +65 -0
  39. package/dist/hooks/index.js +14 -0
  40. package/dist/hooks/index.js.map +1 -0
  41. package/dist/hooks/index.mjs +14 -0
  42. package/dist/hooks/index.mjs.map +1 -0
  43. package/dist/index.d.mts +17 -0
  44. package/dist/index.d.ts +17 -0
  45. package/dist/index.js +37 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/index.mjs +37 -0
  48. package/dist/index.mjs.map +1 -0
  49. package/dist/presets/ballet.d.mts +64 -0
  50. package/dist/presets/ballet.d.ts +64 -0
  51. package/dist/presets/ballet.js +162 -0
  52. package/dist/presets/ballet.js.map +1 -0
  53. package/dist/presets/ballet.mjs +162 -0
  54. package/dist/presets/ballet.mjs.map +1 -0
  55. package/dist/server/index.d.mts +157 -0
  56. package/dist/server/index.d.ts +157 -0
  57. package/dist/server/index.js +619 -0
  58. package/dist/server/index.js.map +1 -0
  59. package/dist/server/index.mjs +619 -0
  60. package/dist/server/index.mjs.map +1 -0
  61. package/dist/types/index.d.mts +137 -0
  62. package/dist/types/index.d.ts +137 -0
  63. package/dist/types/index.js +1 -0
  64. package/dist/types/index.js.map +1 -0
  65. package/dist/types/index.mjs +1 -0
  66. package/dist/types/index.mjs.map +1 -0
  67. package/dist/useGalleryLightbox-BYzPyBYB.d.mts +22 -0
  68. package/dist/useGalleryLightbox-BYzPyBYB.d.ts +22 -0
  69. package/dist/validators/index.d.mts +68 -0
  70. package/dist/validators/index.d.ts +68 -0
  71. package/dist/validators/index.js +7 -0
  72. package/dist/validators/index.js.map +1 -0
  73. package/dist/validators/index.mjs +7 -0
  74. package/dist/validators/index.mjs.map +1 -0
  75. package/package.json +85 -0
  76. package/prisma/gallery.schema.prisma +62 -0
  77. package/prisma/migrations/2026-05-24-enum-to-table.sql +65 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useImageDropZone.ts"],"sourcesContent":["import { useCallback, useRef, useState, type ChangeEvent, type DragEvent } from \"react\";\n\nconst DEFAULT_ACCEPT = [\"image/jpeg\", \"image/png\", \"image/webp\"];\nconst DEFAULT_MAX_SIZE = 10 * 1024 * 1024; // 10 MB\n\nexport type ImageDropZoneValidate = (\n file: File,\n) => { ok: true } | { ok: false; reason: string };\n\nexport interface UseImageDropZoneOptions {\n /** drop / file picker change 로 통과한 파일들이 전달되는 콜백. */\n onFiles: (files: File[]) => void | Promise<void>;\n /** 허용 MIME 타입. 기본 [\"image/jpeg\", \"image/png\", \"image/webp\"]. */\n accept?: string[];\n /** 파일당 최대 바이트. 기본 10 * 1024 * 1024 (10MB). */\n maxSize?: number;\n /** 다중 선택 허용. 기본 true. false 이면 첫 파일만 통과. */\n multiple?: boolean;\n /** host 가 추가 검증을 끼울 수 있는 hook. accept/maxSize 검증 통과 후 호출. */\n validate?: ImageDropZoneValidate;\n /** true 이면 모든 드래그/드롭/체인지 콜백이 no-op. */\n disabled?: boolean;\n}\n\nexport interface UseImageDropZoneReturn {\n /** 현재 드래그 over 상태. */\n isDragging: boolean;\n /** 드롭 영역에 전개할 prop 집합. */\n dropProps: {\n onDragEnter: (e: DragEvent) => void;\n onDragOver: (e: DragEvent) => void;\n onDragLeave: (e: DragEvent) => void;\n onDrop: (e: DragEvent) => void;\n };\n /** `<input type=\"file\">` 에 전개할 prop 집합. */\n inputProps: {\n type: \"file\";\n accept: string;\n multiple: boolean;\n onChange: (e: ChangeEvent<HTMLInputElement>) => void;\n };\n /** 직전 drop / change 에서 reject 된 파일들의 사유. 매 새 이벤트마다 초기화. */\n rejectedReasons: string[];\n}\n\n/** host 독립 이미지 드롭존 hook. 업로드 자체는 수행하지 않고 검증 통과 파일만 onFiles 로 위임. */\nexport function useImageDropZone(opts: UseImageDropZoneOptions): UseImageDropZoneReturn {\n const {\n onFiles,\n accept = DEFAULT_ACCEPT,\n maxSize = DEFAULT_MAX_SIZE,\n multiple = true,\n validate,\n disabled = false,\n } = opts;\n\n const [isDragging, setIsDragging] = useState(false);\n const [rejectedReasons, setRejectedReasons] = useState<string[]>([]);\n const dragCounter = useRef(0);\n\n const processFiles = useCallback(\n (incoming: File[]) => {\n if (disabled) return;\n const accepted: File[] = [];\n const rejected: string[] = [];\n\n const slice = multiple ? incoming : incoming.slice(0, 1);\n\n for (const file of slice) {\n if (accept.length > 0 && !accept.includes(file.type)) {\n rejected.push(`${file.name}: 지원하지 않는 형식 (${file.type || \"unknown\"})`);\n continue;\n }\n if (file.size > maxSize) {\n rejected.push(\n `${file.name}: 파일 크기 초과 (${(file.size / 1024 / 1024).toFixed(1)}MB > ${(\n maxSize /\n 1024 /\n 1024\n ).toFixed(1)}MB)`,\n );\n continue;\n }\n if (validate) {\n const result = validate(file);\n if (!result.ok) {\n rejected.push(`${file.name}: ${result.reason}`);\n continue;\n }\n }\n accepted.push(file);\n }\n\n setRejectedReasons(rejected);\n if (accepted.length > 0) {\n void onFiles(accepted);\n }\n },\n [accept, maxSize, multiple, validate, disabled, onFiles],\n );\n\n const onDragEnter = useCallback(\n (e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (disabled) return;\n dragCounter.current += 1;\n // DataTransfer.types may not be present in some test envs\n const types = e.dataTransfer?.types;\n if (!types || (typeof types.includes === \"function\" ? types.includes(\"Files\") : Array.from(types).includes(\"Files\"))) {\n setIsDragging(true);\n }\n },\n [disabled],\n );\n\n const onDragOver = useCallback(\n (e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n },\n [],\n );\n\n const onDragLeave = useCallback(\n (e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (disabled) return;\n dragCounter.current = Math.max(0, dragCounter.current - 1);\n if (dragCounter.current === 0) {\n setIsDragging(false);\n }\n },\n [disabled],\n );\n\n const onDrop = useCallback(\n (e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n dragCounter.current = 0;\n setIsDragging(false);\n if (disabled) return;\n const incoming = Array.from(e.dataTransfer?.files ?? []);\n processFiles(incoming);\n },\n [disabled, processFiles],\n );\n\n const onChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n if (disabled) return;\n const incoming = Array.from(e.target.files ?? []);\n processFiles(incoming);\n // 동일 파일 재선택을 허용하기 위해 input value 초기화 (jsdom 에서 안전)\n try {\n e.target.value = \"\";\n } catch {}\n },\n [disabled, processFiles],\n );\n\n return {\n isDragging,\n dropProps: { onDragEnter, onDragOver, onDragLeave, onDrop },\n inputProps: {\n type: \"file\",\n accept: accept.join(\",\"),\n multiple,\n onChange,\n },\n rejectedReasons,\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,11 @@
1
+ // src/utils/cn.ts
2
+ import clsx from "clsx";
3
+ import { twMerge } from "tailwind-merge";
4
+ function cn(...inputs) {
5
+ return twMerge(clsx(inputs));
6
+ }
7
+
8
+ export {
9
+ cn
10
+ };
11
+ //# sourceMappingURL=chunk-BTLELFKC.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/cn.ts"],"sourcesContent":["import clsx, { type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n"],"mappings":";AAAA,OAAO,UAA+B;AACtC,SAAS,eAAe;AAEjB,SAAS,MAAM,QAA8B;AAClD,SAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;","names":[]}
@@ -0,0 +1,50 @@
1
+ // src/errors.ts
2
+ var GalleryError = class extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = this.constructor.name;
6
+ }
7
+ };
8
+ var GalleryNotFoundError = class extends GalleryError {
9
+ constructor(id) {
10
+ super(`Gallery not found: ${id}`);
11
+ this.id = id;
12
+ }
13
+ id;
14
+ };
15
+ var CategoryNotFoundError = class extends GalleryError {
16
+ constructor(id) {
17
+ super(`Category not found: ${id}`);
18
+ this.id = id;
19
+ }
20
+ id;
21
+ };
22
+ var CategoryInUseError = class extends GalleryError {
23
+ constructor(categoryId, galleryCount) {
24
+ super(
25
+ `Category ${categoryId} is in use by ${galleryCount} galleries`
26
+ );
27
+ this.categoryId = categoryId;
28
+ this.galleryCount = galleryCount;
29
+ }
30
+ categoryId;
31
+ galleryCount;
32
+ };
33
+ var PermissionDeniedError = class extends GalleryError {
34
+ constructor(action, resourceId) {
35
+ super(`Permission denied: cannot ${action} ${resourceId}`);
36
+ this.action = action;
37
+ this.resourceId = resourceId;
38
+ }
39
+ action;
40
+ resourceId;
41
+ };
42
+
43
+ export {
44
+ GalleryError,
45
+ GalleryNotFoundError,
46
+ CategoryNotFoundError,
47
+ CategoryInUseError,
48
+ PermissionDeniedError
49
+ };
50
+ //# sourceMappingURL=chunk-GDUGKVDQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Typed error 계층 — Sprint 3 follow-up.\n *\n * Sprint 3 의 위험 2 (\"service throw → HTTP 매핑이 메시지 문자열 매칭에 의존\") 를 해소한다.\n * route handler 는 `instanceof` 분기로 HTTP status / error code 를 결정한다.\n *\n * 외부 동작 (HTTP status + response body shape) 은 그대로 유지하고, 내부 표현만 강화한다.\n *\n * 호스트 (Next.js / Express / 기타) 에서도 catch 후 자체 처리 가능하도록\n * `src/index.ts` 와 `src/server/index.ts` 양쪽에서 re-export 한다.\n */\n\nexport class GalleryError extends Error {\n constructor(message: string) {\n super(message);\n this.name = this.constructor.name;\n }\n}\n\nexport class GalleryNotFoundError extends GalleryError {\n constructor(public readonly id: string) {\n super(`Gallery not found: ${id}`);\n }\n}\n\nexport class CategoryNotFoundError extends GalleryError {\n constructor(public readonly id: string) {\n super(`Category not found: ${id}`);\n }\n}\n\nexport class CategoryInUseError extends GalleryError {\n constructor(\n public readonly categoryId: string,\n public readonly galleryCount: number,\n ) {\n super(\n `Category ${categoryId} is in use by ${galleryCount} galleries`,\n );\n }\n}\n\nexport class PermissionDeniedError extends GalleryError {\n constructor(\n public readonly action: \"edit\" | \"delete\",\n public readonly resourceId: string,\n ) {\n super(`Permission denied: cannot ${action} ${resourceId}`);\n }\n}\n"],"mappings":";AAYO,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO,KAAK,YAAY;AAAA,EAC/B;AACF;AAEO,IAAM,uBAAN,cAAmC,aAAa;AAAA,EACrD,YAA4B,IAAY;AACtC,UAAM,sBAAsB,EAAE,EAAE;AADN;AAAA,EAE5B;AAAA,EAF4B;AAG9B;AAEO,IAAM,wBAAN,cAAoC,aAAa;AAAA,EACtD,YAA4B,IAAY;AACtC,UAAM,uBAAuB,EAAE,EAAE;AADP;AAAA,EAE5B;AAAA,EAF4B;AAG9B;AAEO,IAAM,qBAAN,cAAiC,aAAa;AAAA,EACnD,YACkB,YACA,cAChB;AACA;AAAA,MACE,YAAY,UAAU,iBAAiB,YAAY;AAAA,IACrD;AALgB;AACA;AAAA,EAKlB;AAAA,EANkB;AAAA,EACA;AAMpB;AAEO,IAAM,wBAAN,cAAoC,aAAa;AAAA,EACtD,YACkB,QACA,YAChB;AACA,UAAM,6BAA6B,MAAM,IAAI,UAAU,EAAE;AAHzC;AACA;AAAA,EAGlB;AAAA,EAJkB;AAAA,EACA;AAIpB;","names":[]}
@@ -0,0 +1,50 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/errors.ts
2
+ var GalleryError = class extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = this.constructor.name;
6
+ }
7
+ };
8
+ var GalleryNotFoundError = class extends GalleryError {
9
+ constructor(id) {
10
+ super(`Gallery not found: ${id}`);
11
+ this.id = id;
12
+ }
13
+
14
+ };
15
+ var CategoryNotFoundError = class extends GalleryError {
16
+ constructor(id) {
17
+ super(`Category not found: ${id}`);
18
+ this.id = id;
19
+ }
20
+
21
+ };
22
+ var CategoryInUseError = class extends GalleryError {
23
+ constructor(categoryId, galleryCount) {
24
+ super(
25
+ `Category ${categoryId} is in use by ${galleryCount} galleries`
26
+ );
27
+ this.categoryId = categoryId;
28
+ this.galleryCount = galleryCount;
29
+ }
30
+
31
+
32
+ };
33
+ var PermissionDeniedError = class extends GalleryError {
34
+ constructor(action, resourceId) {
35
+ super(`Permission denied: cannot ${action} ${resourceId}`);
36
+ this.action = action;
37
+ this.resourceId = resourceId;
38
+ }
39
+
40
+
41
+ };
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+ exports.GalleryError = GalleryError; exports.GalleryNotFoundError = GalleryNotFoundError; exports.CategoryNotFoundError = CategoryNotFoundError; exports.CategoryInUseError = CategoryInUseError; exports.PermissionDeniedError = PermissionDeniedError;
50
+ //# sourceMappingURL=chunk-GFOWZGJX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-gallery/dist/chunk-GFOWZGJX.js","../src/errors.ts"],"names":[],"mappings":"AAAA;ACYO,IAAM,aAAA,EAAN,MAAA,QAA2B,MAAM;AAAA,EACtC,WAAA,CAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,KAAA,EAAO,IAAA,CAAK,WAAA,CAAY,IAAA;AAAA,EAC/B;AACF,CAAA;AAEO,IAAM,qBAAA,EAAN,MAAA,QAAmC,aAAa;AAAA,EACrD,WAAA,CAA4B,EAAA,EAAY;AACtC,IAAA,KAAA,CAAM,CAAA,mBAAA,EAAsB,EAAE,CAAA,CAAA;AADJ,IAAA;AAE5B,EAAA;AAF4B,EAAA;AAG9B;AAEO;AACmC,EAAA;AACP,IAAA;AADL,IAAA;AAE5B,EAAA;AAF4B,EAAA;AAG9B;AAEO;AAGa,EAAA;AAEhB,IAAA;AACwB,MAAA;AACxB,IAAA;AALgB,IAAA;AACA,IAAA;AAKlB,EAAA;AANkB,EAAA;AACA,EAAA;AAMpB;AAEO;AAIH,EAAA;AACM,IAAA;AAHU,IAAA;AACA,IAAA;AAGlB,EAAA;AAJkB,EAAA;AACA,EAAA;AAIpB;ADRmC;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-gallery/dist/chunk-GFOWZGJX.js","sourcesContent":[null,"/**\n * Typed error 계층 — Sprint 3 follow-up.\n *\n * Sprint 3 의 위험 2 (\"service throw → HTTP 매핑이 메시지 문자열 매칭에 의존\") 를 해소한다.\n * route handler 는 `instanceof` 분기로 HTTP status / error code 를 결정한다.\n *\n * 외부 동작 (HTTP status + response body shape) 은 그대로 유지하고, 내부 표현만 강화한다.\n *\n * 호스트 (Next.js / Express / 기타) 에서도 catch 후 자체 처리 가능하도록\n * `src/index.ts` 와 `src/server/index.ts` 양쪽에서 re-export 한다.\n */\n\nexport class GalleryError extends Error {\n constructor(message: string) {\n super(message);\n this.name = this.constructor.name;\n }\n}\n\nexport class GalleryNotFoundError extends GalleryError {\n constructor(public readonly id: string) {\n super(`Gallery not found: ${id}`);\n }\n}\n\nexport class CategoryNotFoundError extends GalleryError {\n constructor(public readonly id: string) {\n super(`Category not found: ${id}`);\n }\n}\n\nexport class CategoryInUseError extends GalleryError {\n constructor(\n public readonly categoryId: string,\n public readonly galleryCount: number,\n ) {\n super(\n `Category ${categoryId} is in use by ${galleryCount} galleries`,\n );\n }\n}\n\nexport class PermissionDeniedError extends GalleryError {\n constructor(\n public readonly action: \"edit\" | \"delete\",\n public readonly resourceId: string,\n ) {\n super(`Permission denied: cannot ${action} ${resourceId}`);\n }\n}\n"]}
@@ -0,0 +1,47 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/validators/index.ts
2
+ var _zod = require('zod');
3
+ function createGallerySchemas(opts) {
4
+ const CreateGallerySchema = _zod.z.object({
5
+ imageUrl: _zod.z.string().url(),
6
+ imageKey: _zod.z.string().optional(),
7
+ caption: _zod.z.string().max(opts.captionMaxLength).optional(),
8
+ categoryId: _zod.z.string().cuid(),
9
+ sortOrder: _zod.z.number().int().nonnegative().default(0),
10
+ featured: _zod.z.boolean().default(false),
11
+ published: _zod.z.boolean().default(false)
12
+ });
13
+ const UpdateGallerySchema = CreateGallerySchema.partial();
14
+ const BatchCreateGallerySchema = _zod.z.object({
15
+ items: _zod.z.array(CreateGallerySchema).min(1).max(opts.batchMax)
16
+ });
17
+ const BulkUpdateSchema = _zod.z.object({
18
+ ids: _zod.z.array(_zod.z.string().cuid()).min(1).max(opts.batchMax),
19
+ published: _zod.z.boolean().optional(),
20
+ featured: _zod.z.boolean().optional()
21
+ });
22
+ const CreateCategorySchema = _zod.z.object({
23
+ slug: _zod.z.string().min(1).max(64).regex(/^[A-Z][A-Z0-9_]*$/, "slug \uB294 \uB300\uBB38\uC790/\uC22B\uC790/\uC5B8\uB354\uC2A4\uCF54\uC5B4\uB9CC"),
24
+ labelKo: _zod.z.string().min(1).max(64),
25
+ labelEn: _zod.z.string().min(1).max(64).optional(),
26
+ sortOrder: _zod.z.number().int().nonnegative().default(0),
27
+ isActive: _zod.z.boolean().default(true)
28
+ });
29
+ const UpdateCategorySchema = CreateCategorySchema.partial();
30
+ const ReorderCategorySchema = _zod.z.object({
31
+ ids: _zod.z.array(_zod.z.string().cuid()).min(1)
32
+ });
33
+ return {
34
+ CreateGallerySchema,
35
+ UpdateGallerySchema,
36
+ BatchCreateGallerySchema,
37
+ BulkUpdateSchema,
38
+ CreateCategorySchema,
39
+ UpdateCategorySchema,
40
+ ReorderCategorySchema
41
+ };
42
+ }
43
+
44
+
45
+
46
+ exports.createGallerySchemas = createGallerySchemas;
47
+ //# sourceMappingURL=chunk-ILSZPLME.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-gallery/dist/chunk-ILSZPLME.js","../src/validators/index.ts"],"names":[],"mappings":"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","file":"/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-gallery/dist/chunk-ILSZPLME.js","sourcesContent":[null,"import { z } from \"zod\";\n\n/**\n * spec §9-1 충실 구현.\n *\n * 호스트가 `config.limits.batchMax` 와 `config.limits.captionMaxLength` 를 넘겨\n * 패키지 내부 스키마를 생성한다. categoryId 는 cuid 형식만 검증하고,\n * 실제 카테고리 존재 여부는 service 레이어가 Prisma FK 제약으로 자연스럽게 잡는다.\n */\nexport function createGallerySchemas(opts: {\n batchMax: number;\n captionMaxLength: number;\n}) {\n const CreateGallerySchema = z.object({\n imageUrl: z.string().url(),\n imageKey: z.string().optional(),\n caption: z.string().max(opts.captionMaxLength).optional(),\n categoryId: z.string().cuid(),\n sortOrder: z.number().int().nonnegative().default(0),\n featured: z.boolean().default(false),\n published: z.boolean().default(false),\n });\n\n const UpdateGallerySchema = CreateGallerySchema.partial();\n\n const BatchCreateGallerySchema = z.object({\n items: z.array(CreateGallerySchema).min(1).max(opts.batchMax),\n });\n\n const BulkUpdateSchema = z.object({\n ids: z.array(z.string().cuid()).min(1).max(opts.batchMax),\n published: z.boolean().optional(),\n featured: z.boolean().optional(),\n });\n\n const CreateCategorySchema = z.object({\n slug: z\n .string()\n .min(1)\n .max(64)\n .regex(/^[A-Z][A-Z0-9_]*$/, \"slug 는 대문자/숫자/언더스코어만\"),\n labelKo: z.string().min(1).max(64),\n labelEn: z.string().min(1).max(64).optional(),\n sortOrder: z.number().int().nonnegative().default(0),\n isActive: z.boolean().default(true),\n });\n\n const UpdateCategorySchema = CreateCategorySchema.partial();\n\n const ReorderCategorySchema = z.object({\n ids: z.array(z.string().cuid()).min(1),\n });\n\n return {\n CreateGallerySchema,\n UpdateGallerySchema,\n BatchCreateGallerySchema,\n BulkUpdateSchema,\n CreateCategorySchema,\n UpdateCategorySchema,\n ReorderCategorySchema,\n };\n}\n\nexport type GallerySchemas = ReturnType<typeof createGallerySchemas>;\n"]}
@@ -0,0 +1,122 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/hooks/useImageDropZone.ts
2
+ var _react = require('react');
3
+ var DEFAULT_ACCEPT = ["image/jpeg", "image/png", "image/webp"];
4
+ var DEFAULT_MAX_SIZE = 10 * 1024 * 1024;
5
+ function useImageDropZone(opts) {
6
+ const {
7
+ onFiles,
8
+ accept = DEFAULT_ACCEPT,
9
+ maxSize = DEFAULT_MAX_SIZE,
10
+ multiple = true,
11
+ validate,
12
+ disabled = false
13
+ } = opts;
14
+ const [isDragging, setIsDragging] = _react.useState.call(void 0, false);
15
+ const [rejectedReasons, setRejectedReasons] = _react.useState.call(void 0, []);
16
+ const dragCounter = _react.useRef.call(void 0, 0);
17
+ const processFiles = _react.useCallback.call(void 0,
18
+ (incoming) => {
19
+ if (disabled) return;
20
+ const accepted = [];
21
+ const rejected = [];
22
+ const slice = multiple ? incoming : incoming.slice(0, 1);
23
+ for (const file of slice) {
24
+ if (accept.length > 0 && !accept.includes(file.type)) {
25
+ rejected.push(`${file.name}: \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD615\uC2DD (${file.type || "unknown"})`);
26
+ continue;
27
+ }
28
+ if (file.size > maxSize) {
29
+ rejected.push(
30
+ `${file.name}: \uD30C\uC77C \uD06C\uAE30 \uCD08\uACFC (${(file.size / 1024 / 1024).toFixed(1)}MB > ${(maxSize / 1024 / 1024).toFixed(1)}MB)`
31
+ );
32
+ continue;
33
+ }
34
+ if (validate) {
35
+ const result = validate(file);
36
+ if (!result.ok) {
37
+ rejected.push(`${file.name}: ${result.reason}`);
38
+ continue;
39
+ }
40
+ }
41
+ accepted.push(file);
42
+ }
43
+ setRejectedReasons(rejected);
44
+ if (accepted.length > 0) {
45
+ void onFiles(accepted);
46
+ }
47
+ },
48
+ [accept, maxSize, multiple, validate, disabled, onFiles]
49
+ );
50
+ const onDragEnter = _react.useCallback.call(void 0,
51
+ (e) => {
52
+ e.preventDefault();
53
+ e.stopPropagation();
54
+ if (disabled) return;
55
+ dragCounter.current += 1;
56
+ const types = _optionalChain([e, 'access', _ => _.dataTransfer, 'optionalAccess', _2 => _2.types]);
57
+ if (!types || (typeof types.includes === "function" ? types.includes("Files") : Array.from(types).includes("Files"))) {
58
+ setIsDragging(true);
59
+ }
60
+ },
61
+ [disabled]
62
+ );
63
+ const onDragOver = _react.useCallback.call(void 0,
64
+ (e) => {
65
+ e.preventDefault();
66
+ e.stopPropagation();
67
+ },
68
+ []
69
+ );
70
+ const onDragLeave = _react.useCallback.call(void 0,
71
+ (e) => {
72
+ e.preventDefault();
73
+ e.stopPropagation();
74
+ if (disabled) return;
75
+ dragCounter.current = Math.max(0, dragCounter.current - 1);
76
+ if (dragCounter.current === 0) {
77
+ setIsDragging(false);
78
+ }
79
+ },
80
+ [disabled]
81
+ );
82
+ const onDrop = _react.useCallback.call(void 0,
83
+ (e) => {
84
+ e.preventDefault();
85
+ e.stopPropagation();
86
+ dragCounter.current = 0;
87
+ setIsDragging(false);
88
+ if (disabled) return;
89
+ const incoming = Array.from(_nullishCoalesce(_optionalChain([e, 'access', _3 => _3.dataTransfer, 'optionalAccess', _4 => _4.files]), () => ( [])));
90
+ processFiles(incoming);
91
+ },
92
+ [disabled, processFiles]
93
+ );
94
+ const onChange = _react.useCallback.call(void 0,
95
+ (e) => {
96
+ if (disabled) return;
97
+ const incoming = Array.from(_nullishCoalesce(e.target.files, () => ( [])));
98
+ processFiles(incoming);
99
+ try {
100
+ e.target.value = "";
101
+ } catch (e2) {
102
+ }
103
+ },
104
+ [disabled, processFiles]
105
+ );
106
+ return {
107
+ isDragging,
108
+ dropProps: { onDragEnter, onDragOver, onDragLeave, onDrop },
109
+ inputProps: {
110
+ type: "file",
111
+ accept: accept.join(","),
112
+ multiple,
113
+ onChange
114
+ },
115
+ rejectedReasons
116
+ };
117
+ }
118
+
119
+
120
+
121
+ exports.useImageDropZone = useImageDropZone;
122
+ //# sourceMappingURL=chunk-M7CCTDUG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-gallery/dist/chunk-M7CCTDUG.js","../src/hooks/useImageDropZone.ts"],"names":[],"mappings":"AAAA;ACAA,8BAAgF;AAEhF,IAAM,eAAA,EAAiB,CAAC,YAAA,EAAc,WAAA,EAAa,YAAY,CAAA;AAC/D,IAAM,iBAAA,EAAmB,GAAA,EAAK,KAAA,EAAO,IAAA;AA2C9B,SAAS,gBAAA,CAAiB,IAAA,EAAuD;AACtF,EAAA,MAAM;AAAA,IACJ,OAAA;AAAA,IACA,OAAA,EAAS,cAAA;AAAA,IACT,QAAA,EAAU,gBAAA;AAAA,IACV,SAAA,EAAW,IAAA;AAAA,IACX,QAAA;AAAA,IACA,SAAA,EAAW;AAAA,EACb,EAAA,EAAI,IAAA;AAEJ,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,EAAA,EAAI,6BAAA,KAAc,CAAA;AAClD,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,EAAA,EAAI,6BAAA,CAAoB,CAAC,CAAA;AACnE,EAAA,MAAM,YAAA,EAAc,2BAAA,CAAQ,CAAA;AAE5B,EAAA,MAAM,aAAA,EAAe,gCAAA;AAAA,IACnB,CAAC,QAAA,EAAA,GAAqB;AACpB,MAAA,GAAA,CAAI,QAAA,EAAU,MAAA;AACd,MAAA,MAAM,SAAA,EAAmB,CAAC,CAAA;AAC1B,MAAA,MAAM,SAAA,EAAqB,CAAC,CAAA;AAE5B,MAAA,MAAM,MAAA,EAAQ,SAAA,EAAW,SAAA,EAAW,QAAA,CAAS,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA;AAEvD,MAAA,IAAA,CAAA,MAAW,KAAA,GAAQ,KAAA,EAAO;AACxB,QAAA,GAAA,CAAI,MAAA,CAAO,OAAA,EAAS,EAAA,GAAK,CAAC,MAAA,CAAO,QAAA,CAAS,IAAA,CAAK,IAAI,CAAA,EAAG;AACpD,UAAA,QAAA,CAAS,IAAA,CAAK,CAAA,EAAA;AACd,UAAA;AACF,QAAA;AACgB,QAAA;AACL,UAAA;AACK,YAAA;AAKd,UAAA;AACA,UAAA;AACF,QAAA;AACc,QAAA;AACG,UAAA;AACH,UAAA;AACD,YAAA;AACT,YAAA;AACF,UAAA;AACF,QAAA;AACc,QAAA;AAChB,MAAA;AAEmB,MAAA;AACN,MAAA;AACE,QAAA;AACf,MAAA;AACF,IAAA;AACkB,IAAA;AACpB,EAAA;AAEoB,EAAA;AACA,IAAA;AACC,MAAA;AACC,MAAA;AACJ,MAAA;AACF,MAAA;AAEI,MAAA;AACD,MAAA;AACC,QAAA;AAChB,MAAA;AACF,IAAA;AACS,IAAA;AACX,EAAA;AAEmB,EAAA;AACC,IAAA;AACC,MAAA;AACC,MAAA;AACpB,IAAA;AACC,IAAA;AACH,EAAA;AAEoB,EAAA;AACA,IAAA;AACC,MAAA;AACC,MAAA;AACJ,MAAA;AACF,MAAA;AACI,MAAA;AACA,QAAA;AAChB,MAAA;AACF,IAAA;AACS,IAAA;AACX,EAAA;AAEe,EAAA;AACK,IAAA;AACC,MAAA;AACC,MAAA;AACN,MAAA;AACO,MAAA;AACL,MAAA;AACG,MAAA;AACJ,MAAA;AACf,IAAA;AACW,IAAA;AACb,EAAA;AAEiB,EAAA;AACuB,IAAA;AACtB,MAAA;AACG,MAAA;AACJ,MAAA;AAET,MAAA;AACe,QAAA;AACX,MAAA;AAAC,MAAA;AACX,IAAA;AACW,IAAA;AACb,EAAA;AAEO,EAAA;AACL,IAAA;AACa,IAAA;AACD,IAAA;AACJ,MAAA;AACS,MAAA;AACf,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACF,EAAA;AACF;ADzDyB;AACA;AACA;AACA","file":"/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-gallery/dist/chunk-M7CCTDUG.js","sourcesContent":[null,"import { useCallback, useRef, useState, type ChangeEvent, type DragEvent } from \"react\";\n\nconst DEFAULT_ACCEPT = [\"image/jpeg\", \"image/png\", \"image/webp\"];\nconst DEFAULT_MAX_SIZE = 10 * 1024 * 1024; // 10 MB\n\nexport type ImageDropZoneValidate = (\n file: File,\n) => { ok: true } | { ok: false; reason: string };\n\nexport interface UseImageDropZoneOptions {\n /** drop / file picker change 로 통과한 파일들이 전달되는 콜백. */\n onFiles: (files: File[]) => void | Promise<void>;\n /** 허용 MIME 타입. 기본 [\"image/jpeg\", \"image/png\", \"image/webp\"]. */\n accept?: string[];\n /** 파일당 최대 바이트. 기본 10 * 1024 * 1024 (10MB). */\n maxSize?: number;\n /** 다중 선택 허용. 기본 true. false 이면 첫 파일만 통과. */\n multiple?: boolean;\n /** host 가 추가 검증을 끼울 수 있는 hook. accept/maxSize 검증 통과 후 호출. */\n validate?: ImageDropZoneValidate;\n /** true 이면 모든 드래그/드롭/체인지 콜백이 no-op. */\n disabled?: boolean;\n}\n\nexport interface UseImageDropZoneReturn {\n /** 현재 드래그 over 상태. */\n isDragging: boolean;\n /** 드롭 영역에 전개할 prop 집합. */\n dropProps: {\n onDragEnter: (e: DragEvent) => void;\n onDragOver: (e: DragEvent) => void;\n onDragLeave: (e: DragEvent) => void;\n onDrop: (e: DragEvent) => void;\n };\n /** `<input type=\"file\">` 에 전개할 prop 집합. */\n inputProps: {\n type: \"file\";\n accept: string;\n multiple: boolean;\n onChange: (e: ChangeEvent<HTMLInputElement>) => void;\n };\n /** 직전 drop / change 에서 reject 된 파일들의 사유. 매 새 이벤트마다 초기화. */\n rejectedReasons: string[];\n}\n\n/** host 독립 이미지 드롭존 hook. 업로드 자체는 수행하지 않고 검증 통과 파일만 onFiles 로 위임. */\nexport function useImageDropZone(opts: UseImageDropZoneOptions): UseImageDropZoneReturn {\n const {\n onFiles,\n accept = DEFAULT_ACCEPT,\n maxSize = DEFAULT_MAX_SIZE,\n multiple = true,\n validate,\n disabled = false,\n } = opts;\n\n const [isDragging, setIsDragging] = useState(false);\n const [rejectedReasons, setRejectedReasons] = useState<string[]>([]);\n const dragCounter = useRef(0);\n\n const processFiles = useCallback(\n (incoming: File[]) => {\n if (disabled) return;\n const accepted: File[] = [];\n const rejected: string[] = [];\n\n const slice = multiple ? incoming : incoming.slice(0, 1);\n\n for (const file of slice) {\n if (accept.length > 0 && !accept.includes(file.type)) {\n rejected.push(`${file.name}: 지원하지 않는 형식 (${file.type || \"unknown\"})`);\n continue;\n }\n if (file.size > maxSize) {\n rejected.push(\n `${file.name}: 파일 크기 초과 (${(file.size / 1024 / 1024).toFixed(1)}MB > ${(\n maxSize /\n 1024 /\n 1024\n ).toFixed(1)}MB)`,\n );\n continue;\n }\n if (validate) {\n const result = validate(file);\n if (!result.ok) {\n rejected.push(`${file.name}: ${result.reason}`);\n continue;\n }\n }\n accepted.push(file);\n }\n\n setRejectedReasons(rejected);\n if (accepted.length > 0) {\n void onFiles(accepted);\n }\n },\n [accept, maxSize, multiple, validate, disabled, onFiles],\n );\n\n const onDragEnter = useCallback(\n (e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (disabled) return;\n dragCounter.current += 1;\n // DataTransfer.types may not be present in some test envs\n const types = e.dataTransfer?.types;\n if (!types || (typeof types.includes === \"function\" ? types.includes(\"Files\") : Array.from(types).includes(\"Files\"))) {\n setIsDragging(true);\n }\n },\n [disabled],\n );\n\n const onDragOver = useCallback(\n (e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n },\n [],\n );\n\n const onDragLeave = useCallback(\n (e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (disabled) return;\n dragCounter.current = Math.max(0, dragCounter.current - 1);\n if (dragCounter.current === 0) {\n setIsDragging(false);\n }\n },\n [disabled],\n );\n\n const onDrop = useCallback(\n (e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n dragCounter.current = 0;\n setIsDragging(false);\n if (disabled) return;\n const incoming = Array.from(e.dataTransfer?.files ?? []);\n processFiles(incoming);\n },\n [disabled, processFiles],\n );\n\n const onChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n if (disabled) return;\n const incoming = Array.from(e.target.files ?? []);\n processFiles(incoming);\n // 동일 파일 재선택을 허용하기 위해 input value 초기화 (jsdom 에서 안전)\n try {\n e.target.value = \"\";\n } catch {}\n },\n [disabled, processFiles],\n );\n\n return {\n isDragging,\n dropProps: { onDragEnter, onDragOver, onDragLeave, onDrop },\n inputProps: {\n type: \"file\",\n accept: accept.join(\",\"),\n multiple,\n onChange,\n },\n rejectedReasons,\n };\n}\n"]}
@@ -0,0 +1,19 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/config.ts
2
+ var _config = null;
3
+ function setGalleryConfig(config) {
4
+ _config = config;
5
+ }
6
+ function getGalleryConfig() {
7
+ if (!_config) {
8
+ throw new Error(
9
+ "[@withwiz/gallery] GalleryConfig is not set. Call setGalleryConfig(config) at app bootstrap before using gallery APIs."
10
+ );
11
+ }
12
+ return _config;
13
+ }
14
+
15
+
16
+
17
+
18
+ exports.setGalleryConfig = setGalleryConfig; exports.getGalleryConfig = getGalleryConfig;
19
+ //# sourceMappingURL=chunk-WVRTM6E7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-gallery/dist/chunk-WVRTM6E7.js","../src/config.ts"],"names":[],"mappings":"AAAA;ACEA,IAAI,QAAA,EAAgC,IAAA;AAE7B,SAAS,gBAAA,CAAiB,MAAA,EAA6B;AAC5D,EAAA,QAAA,EAAU,MAAA;AACZ;AAEO,SAAS,gBAAA,CAAA,EAAkC;AAChD,EAAA,GAAA,CAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,IACF,CAAA;AAAA,EACF;AACA,EAAA,OAAO,OAAA;AACT;ADFA;AACA;AACE;AACA;AACF,yFAAC","file":"/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-gallery/dist/chunk-WVRTM6E7.js","sourcesContent":[null,"import type { GalleryConfig } from \"./types\";\n\nlet _config: GalleryConfig | null = null;\n\nexport function setGalleryConfig(config: GalleryConfig): void {\n _config = config;\n}\n\nexport function getGalleryConfig(): GalleryConfig {\n if (!_config) {\n throw new Error(\n \"[@withwiz/gallery] GalleryConfig is not set. Call setGalleryConfig(config) at app bootstrap before using gallery APIs.\",\n );\n }\n return _config;\n}\n\n/** test 전용 — runtime 코드에서 호출하지 말 것. */\nexport function resetGalleryConfig(): void {\n _config = null;\n}\n"]}
@@ -0,0 +1,11 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }// src/utils/cn.ts
2
+ var _clsx = require('clsx'); var _clsx2 = _interopRequireDefault(_clsx);
3
+ var _tailwindmerge = require('tailwind-merge');
4
+ function cn(...inputs) {
5
+ return _tailwindmerge.twMerge.call(void 0, _clsx2.default.call(void 0, inputs));
6
+ }
7
+
8
+
9
+
10
+ exports.cn = cn;
11
+ //# sourceMappingURL=chunk-XLPMNJJV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-gallery/dist/chunk-XLPMNJJV.js","../src/utils/cn.ts"],"names":[],"mappings":"AAAA;ACAA,wEAAsC;AACtC,+CAAwB;AAEjB,SAAS,EAAA,CAAA,GAAM,MAAA,EAA8B;AAClD,EAAA,OAAO,oCAAA,4BAAQ,MAAW,CAAC,CAAA;AAC7B;ADCA;AACA;AACE;AACF,gBAAC","file":"/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-gallery/dist/chunk-XLPMNJJV.js","sourcesContent":[null,"import clsx, { type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n"]}
@@ -0,0 +1,79 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/hooks/useScrollReveal.ts
2
+ var _react = require('react');
3
+ function useScrollReveal(opts) {
4
+ const threshold = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _ => _.threshold]), () => ( 0.1));
5
+ const once = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _2 => _2.once]), () => ( true));
6
+ const ref = _react.useRef.call(void 0, null);
7
+ const [isVisible, setIsVisible] = _react.useState.call(void 0, false);
8
+ _react.useEffect.call(void 0, () => {
9
+ if (typeof window === "undefined") return;
10
+ if (typeof globalThis.IntersectionObserver === "undefined") {
11
+ return;
12
+ }
13
+ const el = ref.current;
14
+ if (!el) return;
15
+ const observer = new IntersectionObserver(
16
+ (entries) => {
17
+ for (const entry of entries) {
18
+ if (entry.isIntersecting) {
19
+ setIsVisible(true);
20
+ if (once) observer.unobserve(entry.target);
21
+ } else if (!once) {
22
+ setIsVisible(false);
23
+ }
24
+ }
25
+ },
26
+ { threshold }
27
+ );
28
+ observer.observe(el);
29
+ return () => observer.disconnect();
30
+ }, [threshold, once]);
31
+ return { ref, isVisible };
32
+ }
33
+
34
+ // src/hooks/useGalleryLightbox.ts
35
+
36
+ function useGalleryLightbox(images) {
37
+ const [currentIndex, setCurrentIndex] = _react.useState.call(void 0, -1);
38
+ const isOpen = currentIndex >= 0 && currentIndex < images.length;
39
+ const current = isOpen ? _nullishCoalesce(images[currentIndex], () => ( null)) : null;
40
+ const open = _react.useCallback.call(void 0,
41
+ (index) => {
42
+ if (images.length === 0) return;
43
+ if (index < 0 || index >= images.length) return;
44
+ setCurrentIndex(index);
45
+ },
46
+ [images.length]
47
+ );
48
+ const close = _react.useCallback.call(void 0, () => {
49
+ setCurrentIndex(-1);
50
+ }, []);
51
+ const next = _react.useCallback.call(void 0, () => {
52
+ if (images.length === 0) return;
53
+ setCurrentIndex((prev2) => prev2 < 0 ? prev2 : (prev2 + 1) % images.length);
54
+ }, [images.length]);
55
+ const prev = _react.useCallback.call(void 0, () => {
56
+ if (images.length === 0) return;
57
+ setCurrentIndex((p) => p < 0 ? p : (p - 1 + images.length) % images.length);
58
+ }, [images.length]);
59
+ _react.useEffect.call(void 0, () => {
60
+ if (!isOpen) return;
61
+ if (typeof window === "undefined") return;
62
+ const handler = (e) => {
63
+ if (e.key === "Escape") close();
64
+ else if (e.key === "ArrowLeft") prev();
65
+ else if (e.key === "ArrowRight") next();
66
+ };
67
+ window.addEventListener("keydown", handler);
68
+ return () => {
69
+ window.removeEventListener("keydown", handler);
70
+ };
71
+ }, [isOpen, close, next, prev]);
72
+ return { isOpen, currentIndex: isOpen ? currentIndex : -1, current, open, close, next, prev };
73
+ }
74
+
75
+
76
+
77
+
78
+ exports.useScrollReveal = useScrollReveal; exports.useGalleryLightbox = useGalleryLightbox;
79
+ //# sourceMappingURL=chunk-XSPWM4DD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-gallery/dist/chunk-XSPWM4DD.js","../src/hooks/useScrollReveal.ts","../src/hooks/useGalleryLightbox.ts"],"names":["useState","prev","useEffect"],"mappings":"AAAA;ACAA,8BAA4C;AAYrC,SAAS,eAAA,CAAgB,IAAA,EAG9B;AACA,EAAA,MAAM,UAAA,mCAAY,IAAA,2BAAM,WAAA,UAAa,KAAA;AACrC,EAAA,MAAM,KAAA,mCAAO,IAAA,6BAAM,MAAA,UAAQ,MAAA;AAC3B,EAAA,MAAM,IAAA,EAAM,2BAAA,IAA+B,CAAA;AAC3C,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,EAAA,EAAI,6BAAA,KAAc,CAAA;AAEhD,EAAA,8BAAA,CAAU,EAAA,GAAM;AACd,IAAA,GAAA,CAAI,OAAO,OAAA,IAAW,WAAA,EAAa,MAAA;AACnC,IAAA,GAAA,CAAI,OAAQ,UAAA,CAAkD,qBAAA,IAAyB,WAAA,EAAa;AAClG,MAAA,MAAA;AAAA,IACF;AACA,IAAA,MAAM,GAAA,EAAK,GAAA,CAAI,OAAA;AACf,IAAA,GAAA,CAAI,CAAC,EAAA,EAAI,MAAA;AAET,IAAA,MAAM,SAAA,EAAW,IAAI,oBAAA;AAAA,MACnB,CAAC,OAAA,EAAA,GAAY;AACX,QAAA,IAAA,CAAA,MAAW,MAAA,GAAS,OAAA,EAAS;AAC3B,UAAA,GAAA,CAAI,KAAA,CAAM,cAAA,EAAgB;AACxB,YAAA,YAAA,CAAa,IAAI,CAAA;AACjB,YAAA,GAAA,CAAI,IAAA,EAAM,QAAA,CAAS,SAAA,CAAU,KAAA,CAAM,MAAM,CAAA;AAAA,UAC3C,EAAA,KAAA,GAAA,CAAW,CAAC,IAAA,EAAM;AAChB,YAAA,YAAA,CAAa,KAAK,CAAA;AAAA,UACpB;AAAA,QACF;AAAA,MACF,CAAA;AAAA,MACA,EAAE,UAAU;AAAA,IACd,CAAA;AAEA,IAAA,QAAA,CAAS,OAAA,CAAQ,EAAE,CAAA;AACnB,IAAA,OAAO,CAAA,EAAA,GAAM,QAAA,CAAS,UAAA,CAAW,CAAA;AAAA,EACnC,CAAA,EAAG,CAAC,SAAA,EAAW,IAAI,CAAC,CAAA;AAEpB,EAAA,OAAO,EAAE,GAAA,EAAK,UAAU,CAAA;AAC1B;ADhBA;AACA;AEjCA;AAuBO,SAAS,kBAAA,CAAmB,MAAA,EAAmD;AACpF,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,EAAA,EAAIA,6BAAAA,CAAS,CAAE,CAAA;AACnD,EAAA,MAAM,OAAA,EAAS,aAAA,GAAgB,EAAA,GAAK,aAAA,EAAe,MAAA,CAAO,MAAA;AAC1D,EAAA,MAAM,QAAA,EAAU,OAAA,mBAAS,MAAA,CAAO,YAAY,CAAA,UAAK,OAAA,EAAO,IAAA;AAExD,EAAA,MAAM,KAAA,EAAO,gCAAA;AAAA,IACX,CAAC,KAAA,EAAA,GAAkB;AACjB,MAAA,GAAA,CAAI,MAAA,CAAO,OAAA,IAAW,CAAA,EAAG,MAAA;AACzB,MAAA,GAAA,CAAI,MAAA,EAAQ,EAAA,GAAK,MAAA,GAAS,MAAA,CAAO,MAAA,EAAQ,MAAA;AACzC,MAAA,eAAA,CAAgB,KAAK,CAAA;AAAA,IACvB,CAAA;AAAA,IACA,CAAC,MAAA,CAAO,MAAM;AAAA,EAChB,CAAA;AAEA,EAAA,MAAM,MAAA,EAAQ,gCAAA,CAAY,EAAA,GAAM;AAC9B,IAAA,eAAA,CAAgB,CAAA,CAAE,CAAA;AAAA,EACpB,CAAA,EAAG,CAAC,CAAC,CAAA;AAEL,EAAA,MAAM,KAAA,EAAO,gCAAA,CAAY,EAAA,GAAM;AAC7B,IAAA,GAAA,CAAI,MAAA,CAAO,OAAA,IAAW,CAAA,EAAG,MAAA;AACzB,IAAA,eAAA,CAAgB,CAACC,KAAAA,EAAAA,GAAUA,MAAAA,EAAO,EAAA,EAAIA,MAAAA,EAAAA,CAAQA,MAAAA,EAAO,CAAA,EAAA,EAAK,MAAA,CAAO,MAAO,CAAA;AAAA,EAC1E,CAAA,EAAG,CAAC,MAAA,CAAO,MAAM,CAAC,CAAA;AAElB,EAAA,MAAM,KAAA,EAAO,gCAAA,CAAY,EAAA,GAAM;AAC7B,IAAA,GAAA,CAAI,MAAA,CAAO,OAAA,IAAW,CAAA,EAAG,MAAA;AACzB,IAAA,eAAA,CAAgB,CAAC,CAAA,EAAA,GAAO,EAAA,EAAI,EAAA,EAAI,EAAA,EAAA,CAAK,EAAA,EAAI,EAAA,EAAI,MAAA,CAAO,MAAA,EAAA,EAAU,MAAA,CAAO,MAAO,CAAA;AAAA,EAC9E,CAAA,EAAG,CAAC,MAAA,CAAO,MAAM,CAAC,CAAA;AAElB,EAAAC,8BAAAA,CAAU,EAAA,GAAM;AACd,IAAA,GAAA,CAAI,CAAC,MAAA,EAAQ,MAAA;AACb,IAAA,GAAA,CAAI,OAAO,OAAA,IAAW,WAAA,EAAa,MAAA;AACnC,IAAA,MAAM,QAAA,EAAU,CAAC,CAAA,EAAA,GAAqB;AACpC,MAAA,GAAA,CAAI,CAAA,CAAE,IAAA,IAAQ,QAAA,EAAU,KAAA,CAAM,CAAA;AAAA,MAAA,KAAA,GAAA,CACrB,CAAA,CAAE,IAAA,IAAQ,WAAA,EAAa,IAAA,CAAK,CAAA;AAAA,MAAA,KAAA,GAAA,CAC5B,CAAA,CAAE,IAAA,IAAQ,YAAA,EAAc,IAAA,CAAK,CAAA;AAAA,IACxC,CAAA;AACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,SAAA,EAAW,OAAO,CAAA;AAC1C,IAAA,OAAO,CAAA,EAAA,GAAM;AACX,MAAA,MAAA,CAAO,mBAAA,CAAoB,SAAA,EAAW,OAAO,CAAA;AAAA,IAC/C,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,KAAA,EAAO,IAAA,EAAM,IAAI,CAAC,CAAA;AAE9B,EAAA,OAAO,EAAE,MAAA,EAAQ,YAAA,EAAc,OAAA,EAAS,aAAA,EAAe,CAAA,CAAA,EAAI,OAAA,EAAS,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,KAAK,CAAA;AAC9F;AFOA;AACA;AACE;AACA;AACF,2FAAC","file":"/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-gallery/dist/chunk-XSPWM4DD.js","sourcesContent":[null,"import { useEffect, useRef, useState } from \"react\";\n\n/** IntersectionObserver 기반 스크롤 reveal hook.\n *\n * - SSR safe: `typeof window === \"undefined\"` 이면 effect 가 nothing.\n * - jsdom 등 `IntersectionObserver` 미정의 환경에서도 effect 가 nothing (가드).\n * - `once: true` (default) — 한번 visible 후 unobserve, 이후 다시 false 로 돌아가지 않음.\n * - `once: false` — visible 진입/이탈에 따라 toggle.\n *\n * 반환되는 ref 는 React 19 의 `RefObject<HTMLElement | null>` 시그니처 — host 가 어떤\n * 구체 element 타입에든 마운트 가능하도록 광폭 타입을 둔다.\n */\nexport function useScrollReveal(opts?: { threshold?: number; once?: boolean }): {\n ref: React.RefObject<HTMLElement | null>;\n isVisible: boolean;\n} {\n const threshold = opts?.threshold ?? 0.1;\n const once = opts?.once ?? true;\n const ref = useRef<HTMLElement | null>(null);\n const [isVisible, setIsVisible] = useState(false);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n if (typeof (globalThis as { IntersectionObserver?: unknown }).IntersectionObserver === \"undefined\") {\n return;\n }\n const el = ref.current;\n if (!el) return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n setIsVisible(true);\n if (once) observer.unobserve(entry.target);\n } else if (!once) {\n setIsVisible(false);\n }\n }\n },\n { threshold },\n );\n\n observer.observe(el);\n return () => observer.disconnect();\n }, [threshold, once]);\n\n return { ref, isVisible };\n}\n","import { useCallback, useEffect, useState } from \"react\";\n\nexport interface LightboxImage {\n src: string;\n alt?: string;\n}\n\nexport interface UseGalleryLightboxReturn {\n isOpen: boolean;\n currentIndex: number;\n current: LightboxImage | null;\n open: (index: number) => void;\n close: () => void;\n next: () => void;\n prev: () => void;\n}\n\n/** headless 갤러리 라이트박스. open 상태에서 ESC / ←/→ 키바인딩 자동 등록.\n *\n * - 이미지 0개일 때 `open` 호출은 no-op.\n * - `next` / `prev` 는 wrap-around (마지막 다음 = 처음).\n * - SSR safe: window keydown listener 는 isOpen && typeof window !== \"undefined\" 일 때만 등록.\n */\nexport function useGalleryLightbox(images: LightboxImage[]): UseGalleryLightboxReturn {\n const [currentIndex, setCurrentIndex] = useState(-1);\n const isOpen = currentIndex >= 0 && currentIndex < images.length;\n const current = isOpen ? images[currentIndex] ?? null : null;\n\n const open = useCallback(\n (index: number) => {\n if (images.length === 0) return;\n if (index < 0 || index >= images.length) return;\n setCurrentIndex(index);\n },\n [images.length],\n );\n\n const close = useCallback(() => {\n setCurrentIndex(-1);\n }, []);\n\n const next = useCallback(() => {\n if (images.length === 0) return;\n setCurrentIndex((prev) => (prev < 0 ? prev : (prev + 1) % images.length));\n }, [images.length]);\n\n const prev = useCallback(() => {\n if (images.length === 0) return;\n setCurrentIndex((p) => (p < 0 ? p : (p - 1 + images.length) % images.length));\n }, [images.length]);\n\n useEffect(() => {\n if (!isOpen) return;\n if (typeof window === \"undefined\") return;\n const handler = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") close();\n else if (e.key === \"ArrowLeft\") prev();\n else if (e.key === \"ArrowRight\") next();\n };\n window.addEventListener(\"keydown\", handler);\n return () => {\n window.removeEventListener(\"keydown\", handler);\n };\n }, [isOpen, close, next, prev]);\n\n return { isOpen, currentIndex: isOpen ? currentIndex : -1, current, open, close, next, prev };\n}\n"]}
@@ -0,0 +1,47 @@
1
+ // src/validators/index.ts
2
+ import { z } from "zod";
3
+ function createGallerySchemas(opts) {
4
+ const CreateGallerySchema = z.object({
5
+ imageUrl: z.string().url(),
6
+ imageKey: z.string().optional(),
7
+ caption: z.string().max(opts.captionMaxLength).optional(),
8
+ categoryId: z.string().cuid(),
9
+ sortOrder: z.number().int().nonnegative().default(0),
10
+ featured: z.boolean().default(false),
11
+ published: z.boolean().default(false)
12
+ });
13
+ const UpdateGallerySchema = CreateGallerySchema.partial();
14
+ const BatchCreateGallerySchema = z.object({
15
+ items: z.array(CreateGallerySchema).min(1).max(opts.batchMax)
16
+ });
17
+ const BulkUpdateSchema = z.object({
18
+ ids: z.array(z.string().cuid()).min(1).max(opts.batchMax),
19
+ published: z.boolean().optional(),
20
+ featured: z.boolean().optional()
21
+ });
22
+ const CreateCategorySchema = z.object({
23
+ slug: z.string().min(1).max(64).regex(/^[A-Z][A-Z0-9_]*$/, "slug \uB294 \uB300\uBB38\uC790/\uC22B\uC790/\uC5B8\uB354\uC2A4\uCF54\uC5B4\uB9CC"),
24
+ labelKo: z.string().min(1).max(64),
25
+ labelEn: z.string().min(1).max(64).optional(),
26
+ sortOrder: z.number().int().nonnegative().default(0),
27
+ isActive: z.boolean().default(true)
28
+ });
29
+ const UpdateCategorySchema = CreateCategorySchema.partial();
30
+ const ReorderCategorySchema = z.object({
31
+ ids: z.array(z.string().cuid()).min(1)
32
+ });
33
+ return {
34
+ CreateGallerySchema,
35
+ UpdateGallerySchema,
36
+ BatchCreateGallerySchema,
37
+ BulkUpdateSchema,
38
+ CreateCategorySchema,
39
+ UpdateCategorySchema,
40
+ ReorderCategorySchema
41
+ };
42
+ }
43
+
44
+ export {
45
+ createGallerySchemas
46
+ };
47
+ //# sourceMappingURL=chunk-YUMB3GBN.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/validators/index.ts"],"sourcesContent":["import { z } from \"zod\";\n\n/**\n * spec §9-1 충실 구현.\n *\n * 호스트가 `config.limits.batchMax` 와 `config.limits.captionMaxLength` 를 넘겨\n * 패키지 내부 스키마를 생성한다. categoryId 는 cuid 형식만 검증하고,\n * 실제 카테고리 존재 여부는 service 레이어가 Prisma FK 제약으로 자연스럽게 잡는다.\n */\nexport function createGallerySchemas(opts: {\n batchMax: number;\n captionMaxLength: number;\n}) {\n const CreateGallerySchema = z.object({\n imageUrl: z.string().url(),\n imageKey: z.string().optional(),\n caption: z.string().max(opts.captionMaxLength).optional(),\n categoryId: z.string().cuid(),\n sortOrder: z.number().int().nonnegative().default(0),\n featured: z.boolean().default(false),\n published: z.boolean().default(false),\n });\n\n const UpdateGallerySchema = CreateGallerySchema.partial();\n\n const BatchCreateGallerySchema = z.object({\n items: z.array(CreateGallerySchema).min(1).max(opts.batchMax),\n });\n\n const BulkUpdateSchema = z.object({\n ids: z.array(z.string().cuid()).min(1).max(opts.batchMax),\n published: z.boolean().optional(),\n featured: z.boolean().optional(),\n });\n\n const CreateCategorySchema = z.object({\n slug: z\n .string()\n .min(1)\n .max(64)\n .regex(/^[A-Z][A-Z0-9_]*$/, \"slug 는 대문자/숫자/언더스코어만\"),\n labelKo: z.string().min(1).max(64),\n labelEn: z.string().min(1).max(64).optional(),\n sortOrder: z.number().int().nonnegative().default(0),\n isActive: z.boolean().default(true),\n });\n\n const UpdateCategorySchema = CreateCategorySchema.partial();\n\n const ReorderCategorySchema = z.object({\n ids: z.array(z.string().cuid()).min(1),\n });\n\n return {\n CreateGallerySchema,\n UpdateGallerySchema,\n BatchCreateGallerySchema,\n BulkUpdateSchema,\n CreateCategorySchema,\n UpdateCategorySchema,\n ReorderCategorySchema,\n };\n}\n\nexport type GallerySchemas = ReturnType<typeof createGallerySchemas>;\n"],"mappings":";AAAA,SAAS,SAAS;AASX,SAAS,qBAAqB,MAGlC;AACD,QAAM,sBAAsB,EAAE,OAAO;AAAA,IACnC,UAAU,EAAE,OAAO,EAAE,IAAI;AAAA,IACzB,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,IAC9B,SAAS,EAAE,OAAO,EAAE,IAAI,KAAK,gBAAgB,EAAE,SAAS;AAAA,IACxD,YAAY,EAAE,OAAO,EAAE,KAAK;AAAA,IAC5B,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,CAAC;AAAA,IACnD,UAAU,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,IACnC,WAAW,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,EACtC,CAAC;AAED,QAAM,sBAAsB,oBAAoB,QAAQ;AAExD,QAAM,2BAA2B,EAAE,OAAO;AAAA,IACxC,OAAO,EAAE,MAAM,mBAAmB,EAAE,IAAI,CAAC,EAAE,IAAI,KAAK,QAAQ;AAAA,EAC9D,CAAC;AAED,QAAM,mBAAmB,EAAE,OAAO;AAAA,IAChC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,KAAK,QAAQ;AAAA,IACxD,WAAW,EAAE,QAAQ,EAAE,SAAS;AAAA,IAChC,UAAU,EAAE,QAAQ,EAAE,SAAS;AAAA,EACjC,CAAC;AAED,QAAM,uBAAuB,EAAE,OAAO;AAAA,IACpC,MAAM,EACH,OAAO,EACP,IAAI,CAAC,EACL,IAAI,EAAE,EACN,MAAM,qBAAqB,kFAAsB;AAAA,IACpD,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AAAA,IACjC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS;AAAA,IAC5C,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,CAAC;AAAA,IACnD,UAAU,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,EACpC,CAAC;AAED,QAAM,uBAAuB,qBAAqB,QAAQ;AAE1D,QAAM,wBAAwB,EAAE,OAAO;AAAA,IACrC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC;AAAA,EACvC,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,19 @@
1
+ // src/config.ts
2
+ var _config = null;
3
+ function setGalleryConfig(config) {
4
+ _config = config;
5
+ }
6
+ function getGalleryConfig() {
7
+ if (!_config) {
8
+ throw new Error(
9
+ "[@withwiz/gallery] GalleryConfig is not set. Call setGalleryConfig(config) at app bootstrap before using gallery APIs."
10
+ );
11
+ }
12
+ return _config;
13
+ }
14
+
15
+ export {
16
+ setGalleryConfig,
17
+ getGalleryConfig
18
+ };
19
+ //# sourceMappingURL=chunk-ZEZCUJJD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts"],"sourcesContent":["import type { GalleryConfig } from \"./types\";\n\nlet _config: GalleryConfig | null = null;\n\nexport function setGalleryConfig(config: GalleryConfig): void {\n _config = config;\n}\n\nexport function getGalleryConfig(): GalleryConfig {\n if (!_config) {\n throw new Error(\n \"[@withwiz/gallery] GalleryConfig is not set. Call setGalleryConfig(config) at app bootstrap before using gallery APIs.\",\n );\n }\n return _config;\n}\n\n/** test 전용 — runtime 코드에서 호출하지 말 것. */\nexport function resetGalleryConfig(): void {\n _config = null;\n}\n"],"mappings":";AAEA,IAAI,UAAgC;AAE7B,SAAS,iBAAiB,QAA6B;AAC5D,YAAU;AACZ;AAEO,SAAS,mBAAkC;AAChD,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;","names":[]}