@10x-media/folder-picker 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/LICENSE +21 -0
- package/README.md +54 -0
- package/dist/exports/client.d.ts +2 -0
- package/dist/exports/client.js +3 -0
- package/dist/exports/i18n.d.ts +3 -0
- package/dist/exports/i18n.js +3 -0
- package/dist/exports/types.d.ts +2 -0
- package/dist/exports/types.js +1 -0
- package/dist/folder/BulkUploadButton.js +84 -0
- package/dist/folder/BulkUploadButton.js.map +1 -0
- package/dist/folder/FolderActions.js +133 -0
- package/dist/folder/FolderActions.js.map +1 -0
- package/dist/folder/FolderBrowser.js +417 -0
- package/dist/folder/FolderBrowser.js.map +1 -0
- package/dist/folder/chosenUploadIds.js +63 -0
- package/dist/folder/chosenUploadIds.js.map +1 -0
- package/dist/folder/folder.css +74 -0
- package/dist/folder/index.d.ts +13 -0
- package/dist/folder/index.js +65 -0
- package/dist/folder/index.js.map +1 -0
- package/dist/folder/isMacPlatform.js +21 -0
- package/dist/folder/isMacPlatform.js.map +1 -0
- package/dist/folder/native.js +391 -0
- package/dist/folder/native.js.map +1 -0
- package/dist/folder/useChosenUploads.js +27 -0
- package/dist/folder/useChosenUploads.js.map +1 -0
- package/dist/folder/useFolderTargets.js +126 -0
- package/dist/folder/useFolderTargets.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin/registerFolderListView.js +36 -0
- package/dist/plugin/registerFolderListView.js.map +1 -0
- package/dist/plugin/registerTranslations.js +19 -0
- package/dist/plugin/registerTranslations.js.map +1 -0
- package/dist/translations/de.js +15 -0
- package/dist/translations/de.js.map +1 -0
- package/dist/translations/en.js +20 -0
- package/dist/translations/en.js.map +1 -0
- package/dist/translations/index.d.ts +16 -0
- package/dist/translations/index.js +35 -0
- package/dist/translations/index.js.map +1 -0
- package/dist/translations/keys.d.ts +19 -0
- package/dist/translations/keys.js +19 -0
- package/dist/translations/keys.js.map +1 -0
- package/dist/translations/uk.js +15 -0
- package/dist/translations/uk.js.map +1 -0
- package/dist/translations/useTranslation.js +12 -0
- package/dist/translations/useTranslation.js.map +1 -0
- package/package.json +110 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { getTranslation } from "@payloadcms/translations";
|
|
3
|
+
import { toast, useConfig, useFolder, useTranslation } from "@payloadcms/ui";
|
|
4
|
+
import React from "react";
|
|
5
|
+
//#region src/folder/useFolderTargets.ts
|
|
6
|
+
/**
|
|
7
|
+
* `fetch` rejects only on a network failure, so a 401, 403 or 500 arrives as an ordinary response.
|
|
8
|
+
* Left unchecked the caller would clear the selection and reload, presenting a refusal as a
|
|
9
|
+
* success: the same rows, no message, and the user believing the move or the delete happened.
|
|
10
|
+
*/
|
|
11
|
+
const reportFailure = async (response) => {
|
|
12
|
+
const body = await response.json().catch(() => null);
|
|
13
|
+
toast.error(body?.errors?.[0]?.message ?? `${response.status} ${response.statusText}`);
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* What the folder actions operate on, shared by the selection bar and the menu so the two can never
|
|
17
|
+
* disagree.
|
|
18
|
+
*
|
|
19
|
+
* A single click selects a card and a double click opens it, so a selection is the target when
|
|
20
|
+
* there is one and the folder in view is the target otherwise. Editing needs exactly one target,
|
|
21
|
+
* which is why it disappears once several are selected.
|
|
22
|
+
*/
|
|
23
|
+
const useFolderTargets = ({ collectionSlug, currentFolderName, folderCollectionSlug, folderFieldName, onChanged, parentFolderID }) => {
|
|
24
|
+
const { config, getEntityConfig } = useConfig();
|
|
25
|
+
const { clearSelections, folderID, getSelectedItems, moveToFolder } = useFolder();
|
|
26
|
+
const { i18n, t } = useTranslation();
|
|
27
|
+
const selected = getSelectedItems?.() ?? [];
|
|
28
|
+
const count = selected.length;
|
|
29
|
+
const folderLabel = getTranslation(getEntityConfig({ collectionSlug: folderCollectionSlug })?.labels?.singular ?? "", i18n);
|
|
30
|
+
/**
|
|
31
|
+
* Editing needs one folder; with none selected that is the folder in view. Read out rather than
|
|
32
|
+
* indexed twice, so noUncheckedIndexedAccess narrows once for the branch.
|
|
33
|
+
*
|
|
34
|
+
* A selected document is not a target. The drawer this feeds is keyed to the folder collection,
|
|
35
|
+
* so handing it a document's id sends the admin to `?notFound=<id>`, and Payload's own selection
|
|
36
|
+
* bar hides the action in exactly this case rather than editing the document.
|
|
37
|
+
*/
|
|
38
|
+
const [firstSelected] = selected;
|
|
39
|
+
const editTarget = count === 1 && firstSelected && firstSelected.relationTo === folderCollectionSlug ? {
|
|
40
|
+
id: firstSelected.value.id,
|
|
41
|
+
name: String(firstSelected.value._folderOrDocumentTitle ?? "")
|
|
42
|
+
} : count === 0 && folderID ? {
|
|
43
|
+
id: folderID,
|
|
44
|
+
name: currentFolderName
|
|
45
|
+
} : null;
|
|
46
|
+
const move = React.useCallback(async (destination) => {
|
|
47
|
+
const itemsToMove = count > 0 ? selected : [];
|
|
48
|
+
if (itemsToMove.length > 0) {
|
|
49
|
+
await moveToFolder({
|
|
50
|
+
itemsToMove,
|
|
51
|
+
toFolderID: destination.id ?? void 0
|
|
52
|
+
});
|
|
53
|
+
clearSelections();
|
|
54
|
+
await onChanged(folderID ?? null);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (!folderID) return;
|
|
58
|
+
const response = await fetch(`${config.routes.api}/${folderCollectionSlug}/${folderID}`, {
|
|
59
|
+
body: JSON.stringify({ [folderFieldName]: destination.id ?? null }),
|
|
60
|
+
credentials: "include",
|
|
61
|
+
headers: { "Content-Type": "application/json" },
|
|
62
|
+
method: "PATCH"
|
|
63
|
+
});
|
|
64
|
+
if (!response.ok) {
|
|
65
|
+
await reportFailure(response);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
await onChanged(folderID);
|
|
69
|
+
}, [
|
|
70
|
+
clearSelections,
|
|
71
|
+
config.routes.api,
|
|
72
|
+
count,
|
|
73
|
+
folderCollectionSlug,
|
|
74
|
+
folderFieldName,
|
|
75
|
+
folderID,
|
|
76
|
+
moveToFolder,
|
|
77
|
+
onChanged,
|
|
78
|
+
selected
|
|
79
|
+
]);
|
|
80
|
+
const remove = React.useCallback(async () => {
|
|
81
|
+
const targets = count > 0 ? selected : folderID ? [{
|
|
82
|
+
relationTo: folderCollectionSlug,
|
|
83
|
+
value: { id: folderID }
|
|
84
|
+
}] : [];
|
|
85
|
+
if (targets.length === 0) return;
|
|
86
|
+
for (const item of targets) {
|
|
87
|
+
const response = await fetch(`${config.routes.api}/${item.relationTo}/${item.value.id}`, {
|
|
88
|
+
credentials: "include",
|
|
89
|
+
method: "DELETE"
|
|
90
|
+
});
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
await reportFailure(response);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
clearSelections();
|
|
97
|
+
await onChanged(count > 0 ? folderID ?? null : parentFolderID ?? null);
|
|
98
|
+
}, [
|
|
99
|
+
clearSelections,
|
|
100
|
+
config.routes.api,
|
|
101
|
+
count,
|
|
102
|
+
folderCollectionSlug,
|
|
103
|
+
folderID,
|
|
104
|
+
onChanged,
|
|
105
|
+
parentFolderID,
|
|
106
|
+
selected
|
|
107
|
+
]);
|
|
108
|
+
return {
|
|
109
|
+
clearSelections,
|
|
110
|
+
collectionSlug,
|
|
111
|
+
count,
|
|
112
|
+
editTarget,
|
|
113
|
+
folderLabel,
|
|
114
|
+
hasTarget: count > 0 || Boolean(folderID),
|
|
115
|
+
move,
|
|
116
|
+
remove,
|
|
117
|
+
selected,
|
|
118
|
+
t,
|
|
119
|
+
viewFolderID: folderID ?? void 0,
|
|
120
|
+
viewFolderName: currentFolderName
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
//#endregion
|
|
124
|
+
export { useFolderTargets };
|
|
125
|
+
|
|
126
|
+
//# sourceMappingURL=useFolderTargets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFolderTargets.js","names":[],"sources":["../../src/folder/useFolderTargets.ts"],"sourcesContent":["'use client'\n\nimport { getTranslation } from '@payloadcms/translations'\nimport { toast, useConfig, useFolder, useTranslation } from '@payloadcms/ui'\nimport type { CollectionSlug } from 'payload'\nimport type { FolderOrDocument } from 'payload/shared'\nimport React from 'react'\n\n/**\n * `fetch` rejects only on a network failure, so a 401, 403 or 500 arrives as an ordinary response.\n * Left unchecked the caller would clear the selection and reload, presenting a refusal as a\n * success: the same rows, no message, and the user believing the move or the delete happened.\n */\nconst reportFailure = async (response: Response): Promise<void> => {\n\tconst body = (await response.json().catch(() => null)) as null | {\n\t\terrors?: { message?: string }[]\n\t}\n\n\ttoast.error(body?.errors?.[0]?.message ?? `${response.status} ${response.statusText}`)\n}\n\ntype Args = {\n\tcollectionSlug: CollectionSlug\n\tcurrentFolderName: string\n\tfolderCollectionSlug: CollectionSlug\n\t/** `config.folders.fieldName`, which a host is free to rename away from the default `folder`. */\n\tfolderFieldName: string\n\tonChanged: (nextFolderID: null | number | string) => Promise<void> | void\n\tparentFolderID?: number | string\n}\n\n/**\n * What the folder actions operate on, shared by the selection bar and the menu so the two can never\n * disagree.\n *\n * A single click selects a card and a double click opens it, so a selection is the target when\n * there is one and the folder in view is the target otherwise. Editing needs exactly one target,\n * which is why it disappears once several are selected.\n */\nexport const useFolderTargets = ({\n\tcollectionSlug,\n\tcurrentFolderName,\n\tfolderCollectionSlug,\n\tfolderFieldName,\n\tonChanged,\n\tparentFolderID,\n}: Args) => {\n\tconst { config, getEntityConfig } = useConfig()\n\tconst { clearSelections, folderID, getSelectedItems, moveToFolder } = useFolder()\n\tconst { i18n, t } = useTranslation()\n\n\tconst selected: FolderOrDocument[] = getSelectedItems?.() ?? []\n\tconst count = selected.length\n\tconst folderLabel = getTranslation(\n\t\tgetEntityConfig({ collectionSlug: folderCollectionSlug })?.labels?.singular ?? '',\n\t\ti18n\n\t)\n\n\t/**\n\t * Editing needs one folder; with none selected that is the folder in view. Read out rather than\n\t * indexed twice, so noUncheckedIndexedAccess narrows once for the branch.\n\t *\n\t * A selected document is not a target. The drawer this feeds is keyed to the folder collection,\n\t * so handing it a document's id sends the admin to `?notFound=<id>`, and Payload's own selection\n\t * bar hides the action in exactly this case rather than editing the document.\n\t */\n\tconst [firstSelected] = selected\n\tconst editTarget =\n\t\tcount === 1 && firstSelected && firstSelected.relationTo === folderCollectionSlug\n\t\t\t? {\n\t\t\t\t\tid: firstSelected.value.id,\n\t\t\t\t\tname: String(firstSelected.value._folderOrDocumentTitle ?? ''),\n\t\t\t\t}\n\t\t\t: count === 0 && folderID\n\t\t\t\t? { id: folderID, name: currentFolderName }\n\t\t\t\t: null\n\n\tconst move = React.useCallback(\n\t\tasync (destination: { id: null | number | string }) => {\n\t\t\tconst itemsToMove = count > 0 ? selected : []\n\t\t\tif (itemsToMove.length > 0) {\n\t\t\t\tawait moveToFolder({ itemsToMove, toFolderID: destination.id ?? undefined })\n\t\t\t\tclearSelections()\n\t\t\t\tawait onChanged(folderID ?? null)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Nothing selected: the folder in view is being moved, so follow it. A folder's parent is\n\t\t\t// held in the same configured field its documents use, which the host may have renamed.\n\t\t\tif (!folderID) return\n\t\t\tconst response = await fetch(`${config.routes.api}/${folderCollectionSlug}/${folderID}`, {\n\t\t\t\tbody: JSON.stringify({ [folderFieldName]: destination.id ?? null }),\n\t\t\t\tcredentials: 'include',\n\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\tmethod: 'PATCH',\n\t\t\t})\n\n\t\t\tif (!response.ok) {\n\t\t\t\tawait reportFailure(response)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tawait onChanged(folderID)\n\t\t},\n\t\t[\n\t\t\tclearSelections,\n\t\t\tconfig.routes.api,\n\t\t\tcount,\n\t\t\tfolderCollectionSlug,\n\t\t\tfolderFieldName,\n\t\t\tfolderID,\n\t\t\tmoveToFolder,\n\t\t\tonChanged,\n\t\t\tselected,\n\t\t]\n\t)\n\n\tconst remove = React.useCallback(async () => {\n\t\tconst targets =\n\t\t\tcount > 0\n\t\t\t\t? selected\n\t\t\t\t: folderID\n\t\t\t\t\t? [{ relationTo: folderCollectionSlug, value: { id: folderID } }]\n\t\t\t\t\t: []\n\t\tif (targets.length === 0) return\n\n\t\tfor (const item of targets) {\n\t\t\tconst response = await fetch(`${config.routes.api}/${item.relationTo}/${item.value.id}`, {\n\t\t\t\tcredentials: 'include',\n\t\t\t\tmethod: 'DELETE',\n\t\t\t})\n\n\t\t\t// Stopping on the first refusal rather than carrying on: whatever denied one delete, a\n\t\t\t// permission or a relationship, will deny the rest, and the ones already gone still need\n\t\t\t// the view refreshed below.\n\t\t\tif (!response.ok) {\n\t\t\t\tawait reportFailure(response)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tclearSelections()\n\t\t// Deleting the folder in view leaves nowhere to stand, so fall back to its parent.\n\t\tawait onChanged(count > 0 ? (folderID ?? null) : (parentFolderID ?? null))\n\t}, [\n\t\tclearSelections,\n\t\tconfig.routes.api,\n\t\tcount,\n\t\tfolderCollectionSlug,\n\t\tfolderID,\n\t\tonChanged,\n\t\tparentFolderID,\n\t\tselected,\n\t])\n\n\treturn {\n\t\tclearSelections,\n\t\tcollectionSlug,\n\t\tcount,\n\t\teditTarget,\n\t\tfolderLabel,\n\t\thasTarget: count > 0 || Boolean(folderID),\n\t\tmove,\n\t\tremove,\n\t\tselected,\n\t\tt,\n\t\tviewFolderID: folderID ?? undefined,\n\t\tviewFolderName: currentFolderName,\n\t}\n}\n"],"mappings":";;;;;;;;;;AAaA,MAAM,gBAAgB,OAAO,aAAsC;CAClE,MAAM,OAAQ,MAAM,SAAS,KAAK,EAAE,YAAY,IAAI;CAIpD,MAAM,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG,SAAS,OAAO,GAAG,SAAS,YAAY;AACtF;;;;;;;;;AAoBA,MAAa,oBAAoB,EAChC,gBACA,mBACA,sBACA,iBACA,WACA,qBACW;CACX,MAAM,EAAE,QAAQ,oBAAoB,UAAU;CAC9C,MAAM,EAAE,iBAAiB,UAAU,kBAAkB,iBAAiB,UAAU;CAChF,MAAM,EAAE,MAAM,MAAM,eAAe;CAEnC,MAAM,WAA+B,mBAAmB,KAAK,CAAC;CAC9D,MAAM,QAAQ,SAAS;CACvB,MAAM,cAAc,eACnB,gBAAgB,EAAE,gBAAgB,qBAAqB,CAAC,GAAG,QAAQ,YAAY,IAC/E,IACD;;;;;;;;;CAUA,MAAM,CAAC,iBAAiB;CACxB,MAAM,aACL,UAAU,KAAK,iBAAiB,cAAc,eAAe,uBAC1D;EACA,IAAI,cAAc,MAAM;EACxB,MAAM,OAAO,cAAc,MAAM,0BAA0B,EAAE;CAC9D,IACC,UAAU,KAAK,WACd;EAAE,IAAI;EAAU,MAAM;CAAkB,IACxC;CAEL,MAAM,OAAO,MAAM,YAClB,OAAO,gBAAgD;EACtD,MAAM,cAAc,QAAQ,IAAI,WAAW,CAAC;EAC5C,IAAI,YAAY,SAAS,GAAG;GAC3B,MAAM,aAAa;IAAE;IAAa,YAAY,YAAY,MAAM,KAAA;GAAU,CAAC;GAC3E,gBAAgB;GAChB,MAAM,UAAU,YAAY,IAAI;GAChC;EACD;EAIA,IAAI,CAAC,UAAU;EACf,MAAM,WAAW,MAAM,MAAM,GAAG,OAAO,OAAO,IAAI,GAAG,qBAAqB,GAAG,YAAY;GACxF,MAAM,KAAK,UAAU,GAAG,kBAAkB,YAAY,MAAM,KAAK,CAAC;GAClE,aAAa;GACb,SAAS,EAAE,gBAAgB,mBAAmB;GAC9C,QAAQ;EACT,CAAC;EAED,IAAI,CAAC,SAAS,IAAI;GACjB,MAAM,cAAc,QAAQ;GAC5B;EACD;EAEA,MAAM,UAAU,QAAQ;CACzB,GACA;EACC;EACA,OAAO,OAAO;EACd;EACA;EACA;EACA;EACA;EACA;EACA;CACD,CACD;CAEA,MAAM,SAAS,MAAM,YAAY,YAAY;EAC5C,MAAM,UACL,QAAQ,IACL,WACA,WACC,CAAC;GAAE,YAAY;GAAsB,OAAO,EAAE,IAAI,SAAS;EAAE,CAAC,IAC9D,CAAC;EACN,IAAI,QAAQ,WAAW,GAAG;EAE1B,KAAK,MAAM,QAAQ,SAAS;GAC3B,MAAM,WAAW,MAAM,MAAM,GAAG,OAAO,OAAO,IAAI,GAAG,KAAK,WAAW,GAAG,KAAK,MAAM,MAAM;IACxF,aAAa;IACb,QAAQ;GACT,CAAC;GAKD,IAAI,CAAC,SAAS,IAAI;IACjB,MAAM,cAAc,QAAQ;IAC5B;GACD;EACD;EAEA,gBAAgB;EAEhB,MAAM,UAAU,QAAQ,IAAK,YAAY,OAAS,kBAAkB,IAAK;CAC1E,GAAG;EACF;EACA,OAAO,OAAO;EACd;EACA;EACA;EACA;EACA;EACA;CACD,CAAC;CAED,OAAO;EACN;EACA;EACA;EACA;EACA;EACA,WAAW,QAAQ,KAAK,QAAQ,QAAQ;EACxC;EACA;EACA;EACA;EACA,cAAc,YAAY,KAAA;EAC1B,gBAAgB;CACjB;AACD"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { TranslationsOption } from "./translations/index.js";
|
|
2
|
+
|
|
3
|
+
//#region src/index.d.ts
|
|
4
|
+
type FolderPickerPluginOptions = {
|
|
5
|
+
/**
|
|
6
|
+
* Disable the plugin entirely (incoming config returned untouched).
|
|
7
|
+
* Useful for opting out per environment without removing the plugin call.
|
|
8
|
+
*/
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Per-locale overrides for this plugin's UI strings, keyed by the typed
|
|
12
|
+
* translation keys exported from `@10x-media/folder-picker/i18n`. Values win
|
|
13
|
+
* over the built-in locales key-by-key; locales the plugin does not ship are
|
|
14
|
+
* added whole. App-level `i18n.translations` still wins over both.
|
|
15
|
+
*/
|
|
16
|
+
translations?: TranslationsOption;
|
|
17
|
+
};
|
|
18
|
+
declare module 'payload' {
|
|
19
|
+
interface RegisteredPlugins {
|
|
20
|
+
'@10x-media/folder-picker': FolderPickerPluginOptions;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Folder Picker plugin for Payload v3. Swaps the list view of every folder-enabled
|
|
25
|
+
* collection so the list drawer browses folders instead of a flat list. Payload
|
|
26
|
+
* resolves that one component for both the collection route and the drawer, so upload
|
|
27
|
+
* fields, relationship fields and the lexical upload node are covered without patching
|
|
28
|
+
* a single field. Authored with `definePlugin` so sibling plugins can detect it by slug.
|
|
29
|
+
*/
|
|
30
|
+
declare const folderPicker: (options: FolderPickerPluginOptions) => import("payload").Plugin;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { FolderPickerPluginOptions, type FolderPickerPluginOptions as PluginOptions, folderPicker };
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { registerFolderListView } from "./plugin/registerFolderListView.js";
|
|
2
|
+
import { registerTranslations } from "./plugin/registerTranslations.js";
|
|
3
|
+
import { definePlugin } from "payload";
|
|
4
|
+
//#region src/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* Folder Picker plugin for Payload v3. Swaps the list view of every folder-enabled
|
|
7
|
+
* collection so the list drawer browses folders instead of a flat list. Payload
|
|
8
|
+
* resolves that one component for both the collection route and the drawer, so upload
|
|
9
|
+
* fields, relationship fields and the lexical upload node are covered without patching
|
|
10
|
+
* a single field. Authored with `definePlugin` so sibling plugins can detect it by slug.
|
|
11
|
+
*/
|
|
12
|
+
const folderPicker = definePlugin({
|
|
13
|
+
slug: "@10x-media/folder-picker",
|
|
14
|
+
plugin: ({ config, plugins: _plugins, ...options }) => {
|
|
15
|
+
if (options.disabled === true) return config;
|
|
16
|
+
registerTranslations(config, options.translations);
|
|
17
|
+
registerFolderListView(config);
|
|
18
|
+
return config;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
//#endregion
|
|
22
|
+
export { folderPicker };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { type Config, definePlugin } from 'payload'\nimport { registerFolderListView } from './plugin/registerFolderListView'\nimport { registerTranslations } from './plugin/registerTranslations'\nimport type { TranslationsOption } from './translations'\nexport type FolderPickerPluginOptions = {\n\t/**\n\t * Disable the plugin entirely (incoming config returned untouched).\n\t * Useful for opting out per environment without removing the plugin call.\n\t */\n\tdisabled?: boolean\n\t/**\n\t * Per-locale overrides for this plugin's UI strings, keyed by the typed\n\t * translation keys exported from `@10x-media/folder-picker/i18n`. Values win\n\t * over the built-in locales key-by-key; locales the plugin does not ship are\n\t * added whole. App-level `i18n.translations` still wins over both.\n\t */\n\ttranslations?: TranslationsOption\n}\n\ndeclare module 'payload' {\n\tinterface RegisteredPlugins {\n\t\t'@10x-media/folder-picker': FolderPickerPluginOptions\n\t}\n}\n\n/**\n * Folder Picker plugin for Payload v3. Swaps the list view of every folder-enabled\n * collection so the list drawer browses folders instead of a flat list. Payload\n * resolves that one component for both the collection route and the drawer, so upload\n * fields, relationship fields and the lexical upload node are covered without patching\n * a single field. Authored with `definePlugin` so sibling plugins can detect it by slug.\n */\nexport const folderPicker = definePlugin<FolderPickerPluginOptions>({\n\tslug: '@10x-media/folder-picker',\n\tplugin: ({ config, plugins: _plugins, ...options }): Config => {\n\t\tif (options.disabled === true) {\n\t\t\treturn config\n\t\t}\n\t\tregisterTranslations(config, options.translations)\n\n\t\tregisterFolderListView(config)\n\t\treturn config\n\t},\n})\n\nexport type { FolderPickerPluginOptions as PluginOptions }\n"],"mappings":";;;;;;;;;;;AAgCA,MAAa,eAAe,aAAwC;CACnE,MAAM;CACN,SAAS,EAAE,QAAQ,SAAS,UAAU,GAAG,cAAsB;EAC9D,IAAI,QAAQ,aAAa,MACxB,OAAO;EAER,qBAAqB,QAAQ,QAAQ,YAAY;EAEjD,uBAAuB,MAAM;EAC7B,OAAO;CACR;AACD,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//#region src/plugin/registerFolderListView.ts
|
|
2
|
+
const LIST_VIEW = "@10x-media/folder-picker/client#FolderListView";
|
|
3
|
+
/**
|
|
4
|
+
* Gives every folder-enabled collection a list view that offers folder browsing inside a list
|
|
5
|
+
* drawer. Payload renders `admin.components.views.list.Component` for both the list route and the
|
|
6
|
+
* drawer, so one component covers the upload field's "choose from existing" without touching the
|
|
7
|
+
* upload field itself. Collections that already declare a custom list view are left alone.
|
|
8
|
+
*/
|
|
9
|
+
const registerFolderListView = (config) => {
|
|
10
|
+
if (!config.folders) return;
|
|
11
|
+
config.collections = config.collections?.map((collection) => {
|
|
12
|
+
if (!collection.folders) return collection;
|
|
13
|
+
const views = collection.admin?.components?.views;
|
|
14
|
+
if (views?.list?.Component) return collection;
|
|
15
|
+
return {
|
|
16
|
+
...collection,
|
|
17
|
+
admin: {
|
|
18
|
+
...collection.admin,
|
|
19
|
+
components: {
|
|
20
|
+
...collection.admin?.components,
|
|
21
|
+
views: {
|
|
22
|
+
...views,
|
|
23
|
+
list: {
|
|
24
|
+
...views?.list,
|
|
25
|
+
Component: LIST_VIEW
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
//#endregion
|
|
34
|
+
export { registerFolderListView };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=registerFolderListView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registerFolderListView.js","names":[],"sources":["../../src/plugin/registerFolderListView.ts"],"sourcesContent":["import type { Config } from 'payload'\n\nconst LIST_VIEW = '@10x-media/folder-picker/client#FolderListView'\n\n/**\n * Gives every folder-enabled collection a list view that offers folder browsing inside a list\n * drawer. Payload renders `admin.components.views.list.Component` for both the list route and the\n * drawer, so one component covers the upload field's \"choose from existing\" without touching the\n * upload field itself. Collections that already declare a custom list view are left alone.\n */\nexport const registerFolderListView = (config: Config): void => {\n\tif (!config.folders) {\n\t\treturn\n\t}\n\n\tconfig.collections = config.collections?.map((collection) => {\n\t\tif (!collection.folders) {\n\t\t\treturn collection\n\t\t}\n\n\t\tconst views = collection.admin?.components?.views\n\t\tif (views?.list?.Component) {\n\t\t\treturn collection\n\t\t}\n\n\t\treturn {\n\t\t\t...collection,\n\t\t\tadmin: {\n\t\t\t\t...collection.admin,\n\t\t\t\tcomponents: {\n\t\t\t\t\t...collection.admin?.components,\n\t\t\t\t\tviews: {\n\t\t\t\t\t\t...views,\n\t\t\t\t\t\tlist: { ...views?.list, Component: LIST_VIEW },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t})\n}\n"],"mappings":";AAEA,MAAM,YAAY;;;;;;;AAQlB,MAAa,0BAA0B,WAAyB;CAC/D,IAAI,CAAC,OAAO,SACX;CAGD,OAAO,cAAc,OAAO,aAAa,KAAK,eAAe;EAC5D,IAAI,CAAC,WAAW,SACf,OAAO;EAGR,MAAM,QAAQ,WAAW,OAAO,YAAY;EAC5C,IAAI,OAAO,MAAM,WAChB,OAAO;EAGR,OAAO;GACN,GAAG;GACH,OAAO;IACN,GAAG,WAAW;IACd,YAAY;KACX,GAAG,WAAW,OAAO;KACrB,OAAO;MACN,GAAG;MACH,MAAM;OAAE,GAAG,OAAO;OAAM,WAAW;MAAU;KAC9C;IACD;GACD;EACD;CACD,CAAC;AACF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { toNested, translations } from "../translations/index.js";
|
|
2
|
+
import { deepMergeSimple } from "payload/shared";
|
|
3
|
+
//#region src/plugin/registerTranslations.ts
|
|
4
|
+
/**
|
|
5
|
+
* Merge this plugin's translations into the host config. Plugin-level
|
|
6
|
+
* `overrides` win over the built-ins key-by-key and may add locales the plugin
|
|
7
|
+
* does not ship. A host value wins over both (`deepMergeSimple` lets the second
|
|
8
|
+
* argument override), so projects can override any string.
|
|
9
|
+
*/
|
|
10
|
+
const registerTranslations = (config, overrides) => {
|
|
11
|
+
const nested = {};
|
|
12
|
+
for (const [locale, flat] of Object.entries(overrides ?? {})) nested[locale] = toNested(flat);
|
|
13
|
+
config.i18n ??= {};
|
|
14
|
+
config.i18n.translations = deepMergeSimple(deepMergeSimple(translations, nested), config.i18n.translations ?? {});
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { registerTranslations };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=registerTranslations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registerTranslations.js","names":[],"sources":["../../src/plugin/registerTranslations.ts"],"sourcesContent":["import type { Config } from 'payload'\nimport { deepMergeSimple } from 'payload/shared'\n\nimport { type TranslationsOption, toNested, translations } from '../translations'\n\ntype Translations = NonNullable<NonNullable<Config['i18n']>['translations']>\n\n/**\n * Merge this plugin's translations into the host config. Plugin-level\n * `overrides` win over the built-ins key-by-key and may add locales the plugin\n * does not ship. A host value wins over both (`deepMergeSimple` lets the second\n * argument override), so projects can override any string.\n */\nexport const registerTranslations = (config: Config, overrides?: TranslationsOption): void => {\n\tconst nested: Record<string, Record<string, Record<string, string>>> = {}\n\tfor (const [locale, flat] of Object.entries(overrides ?? {})) {\n\t\tnested[locale] = toNested(flat)\n\t}\n\tconfig.i18n ??= {}\n\tconfig.i18n.translations = deepMergeSimple<Translations>(\n\t\tdeepMergeSimple(translations, nested),\n\t\tconfig.i18n.translations ?? {}\n\t)\n}\n"],"mappings":";;;;;;;;;AAaA,MAAa,wBAAwB,QAAgB,cAAyC;CAC7F,MAAM,SAAiE,CAAC;CACxE,KAAK,MAAM,CAAC,QAAQ,SAAS,OAAO,QAAQ,aAAa,CAAC,CAAC,GAC1D,OAAO,UAAU,SAAS,IAAI;CAE/B,OAAO,SAAS,CAAC;CACjB,OAAO,KAAK,eAAe,gBAC1B,gBAAgB,cAAc,MAAM,GACpC,OAAO,KAAK,gBAAgB,CAAC,CAC9B;AACD"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { keys } from "./keys.js";
|
|
2
|
+
//#region src/translations/de.ts
|
|
3
|
+
const de = {
|
|
4
|
+
[keys.gridView]: "Als Raster anzeigen",
|
|
5
|
+
[keys.listView]: "Als Liste anzeigen",
|
|
6
|
+
[keys.orderLabel]: "Reihenfolge",
|
|
7
|
+
[keys.pickManyHint]: "{{modifier}} halten, um mehrere auszuwählen, {{range}} für einen Bereich.",
|
|
8
|
+
[keys.pluginName]: "Ordnerauswahl",
|
|
9
|
+
[keys.retry]: "Erneut versuchen",
|
|
10
|
+
[keys.sortByLabel]: "Sortieren nach"
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { de };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=de.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"de.js","names":[],"sources":["../../src/translations/de.ts"],"sourcesContent":["import { keys, type TranslationKey } from './keys'\n\nexport const de: Record<TranslationKey, string> = {\n\t[keys.gridView]: 'Als Raster anzeigen',\n\t[keys.listView]: 'Als Liste anzeigen',\n\t[keys.orderLabel]: 'Reihenfolge',\n\t[keys.pickManyHint]: '{{modifier}} halten, um mehrere auszuwählen, {{range}} für einen Bereich.',\n\t[keys.pluginName]: 'Ordnerauswahl',\n\t[keys.retry]: 'Erneut versuchen',\n\t[keys.sortByLabel]: 'Sortieren nach',\n}\n"],"mappings":";;AAEA,MAAa,KAAqC;EAChD,KAAK,WAAW;EAChB,KAAK,WAAW;EAChB,KAAK,aAAa;EAClB,KAAK,eAAe;EACpB,KAAK,aAAa;EAClB,KAAK,QAAQ;EACb,KAAK,cAAc;AACrB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { keys } from "./keys.js";
|
|
2
|
+
//#region src/translations/en.ts
|
|
3
|
+
/**
|
|
4
|
+
* English values, keyed by the typed constants in `keys.ts` so the two stay in
|
|
5
|
+
* lockstep. The `Record<TranslationKey, string>` annotation makes a missing or
|
|
6
|
+
* unknown key a type error. `translations/index.ts` nests these for Payload.
|
|
7
|
+
*/
|
|
8
|
+
const en = {
|
|
9
|
+
[keys.gridView]: "Show as grid",
|
|
10
|
+
[keys.listView]: "Show as list",
|
|
11
|
+
[keys.orderLabel]: "Order",
|
|
12
|
+
[keys.pickManyHint]: "Hold {{modifier}} to pick more than one, {{range}} to pick a range.",
|
|
13
|
+
[keys.pluginName]: "Folder Picker",
|
|
14
|
+
[keys.retry]: "Try again",
|
|
15
|
+
[keys.sortByLabel]: "Sort by"
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { en };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=en.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"en.js","names":[],"sources":["../../src/translations/en.ts"],"sourcesContent":["import { keys, type TranslationKey } from './keys'\n\n/**\n * English values, keyed by the typed constants in `keys.ts` so the two stay in\n * lockstep. The `Record<TranslationKey, string>` annotation makes a missing or\n * unknown key a type error. `translations/index.ts` nests these for Payload.\n */\nexport const en: Record<TranslationKey, string> = {\n\t[keys.gridView]: 'Show as grid',\n\t[keys.listView]: 'Show as list',\n\t[keys.orderLabel]: 'Order',\n\t[keys.pickManyHint]: 'Hold {{modifier}} to pick more than one, {{range}} to pick a range.',\n\t[keys.pluginName]: 'Folder Picker',\n\t[keys.retry]: 'Try again',\n\t[keys.sortByLabel]: 'Sort by',\n}\n"],"mappings":";;;;;;;AAOA,MAAa,KAAqC;EAChD,KAAK,WAAW;EAChB,KAAK,WAAW;EAChB,KAAK,aAAa;EAClB,KAAK,eAAe;EACpB,KAAK,aAAa;EAClB,KAAK,QAAQ;EACb,KAAK,cAAc;AACrB"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TranslationKey, keys } from "./keys.js";
|
|
2
|
+
|
|
3
|
+
//#region src/translations/index.d.ts
|
|
4
|
+
/** Per-locale string overrides keyed by this plugin's typed translation keys. */
|
|
5
|
+
type TranslationsOption = {
|
|
6
|
+
[locale: string]: Partial<Record<TranslationKey, string>>;
|
|
7
|
+
};
|
|
8
|
+
/** Per-locale messages merged into `config.i18n.translations`. */
|
|
9
|
+
declare const translations: {
|
|
10
|
+
de: Record<string, Record<string, string>>;
|
|
11
|
+
en: Record<string, Record<string, string>>;
|
|
12
|
+
uk: Record<string, Record<string, string>>;
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { TranslationsOption, translations };
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import "./keys.js";
|
|
2
|
+
import { de } from "./de.js";
|
|
3
|
+
import { en } from "./en.js";
|
|
4
|
+
import { uk } from "./uk.js";
|
|
5
|
+
//#region src/translations/index.ts
|
|
6
|
+
/**
|
|
7
|
+
* Flat `folderPicker:foo` entries to the nested `{ folderPicker: { foo } }`
|
|
8
|
+
* shape Payload resolves `t('folderPicker:foo')` against (it splits on `:`).
|
|
9
|
+
* Undefined values are skipped so `Partial` override maps pass through.
|
|
10
|
+
* A key with no `:` has no namespace to nest under, so it is dropped rather than
|
|
11
|
+
* split at -1, which would file it under the key with its last character shaved off.
|
|
12
|
+
*/
|
|
13
|
+
const toNested = (flat) => {
|
|
14
|
+
const out = {};
|
|
15
|
+
for (const [fullKey, value] of Object.entries(flat)) {
|
|
16
|
+
if (typeof value !== "string") continue;
|
|
17
|
+
const separator = fullKey.indexOf(":");
|
|
18
|
+
if (separator < 1) continue;
|
|
19
|
+
const namespace = fullKey.slice(0, separator);
|
|
20
|
+
const bucket = out[namespace] ?? {};
|
|
21
|
+
bucket[fullKey.slice(separator + 1)] = value;
|
|
22
|
+
out[namespace] = bucket;
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
};
|
|
26
|
+
/** Per-locale messages merged into `config.i18n.translations`. */
|
|
27
|
+
const translations = {
|
|
28
|
+
de: toNested(de),
|
|
29
|
+
en: toNested(en),
|
|
30
|
+
uk: toNested(uk)
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export { toNested, translations };
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/translations/index.ts"],"sourcesContent":["import { de } from './de'\nimport { en } from './en'\nimport type { TranslationKey } from './keys'\nimport { uk } from './uk'\n\nexport type { TranslationKey } from './keys'\nexport { keys } from './keys'\n\n/** Per-locale string overrides keyed by this plugin's typed translation keys. */\nexport type TranslationsOption = {\n\t[locale: string]: Partial<Record<TranslationKey, string>>\n}\n\n/**\n * Flat `folderPicker:foo` entries to the nested `{ folderPicker: { foo } }`\n * shape Payload resolves `t('folderPicker:foo')` against (it splits on `:`).\n * Undefined values are skipped so `Partial` override maps pass through.\n * A key with no `:` has no namespace to nest under, so it is dropped rather than\n * split at -1, which would file it under the key with its last character shaved off.\n */\nexport const toNested = (flat: {\n\t[key: string]: string | undefined\n}): Record<string, Record<string, string>> => {\n\tconst out: Record<string, Record<string, string>> = {}\n\tfor (const [fullKey, value] of Object.entries(flat)) {\n\t\tif (typeof value !== 'string') {\n\t\t\tcontinue\n\t\t}\n\t\tconst separator = fullKey.indexOf(':')\n\t\tif (separator < 1) {\n\t\t\tcontinue\n\t\t}\n\t\tconst namespace = fullKey.slice(0, separator)\n\t\tconst bucket = out[namespace] ?? {}\n\t\tbucket[fullKey.slice(separator + 1)] = value\n\t\tout[namespace] = bucket\n\t}\n\treturn out\n}\n\n/** Per-locale messages merged into `config.i18n.translations`. */\nexport const translations = {\n\tde: toNested(de),\n\ten: toNested(en),\n\tuk: toNested(uk),\n}\n"],"mappings":";;;;;;;;;;;;AAoBA,MAAa,YAAY,SAEqB;CAC7C,MAAM,MAA8C,CAAC;CACrD,KAAK,MAAM,CAAC,SAAS,UAAU,OAAO,QAAQ,IAAI,GAAG;EACpD,IAAI,OAAO,UAAU,UACpB;EAED,MAAM,YAAY,QAAQ,QAAQ,GAAG;EACrC,IAAI,YAAY,GACf;EAED,MAAM,YAAY,QAAQ,MAAM,GAAG,SAAS;EAC5C,MAAM,SAAS,IAAI,cAAc,CAAC;EAClC,OAAO,QAAQ,MAAM,YAAY,CAAC,KAAK;EACvC,IAAI,aAAa;CAClB;CACA,OAAO;AACR;;AAGA,MAAa,eAAe;CAC3B,IAAI,SAAS,EAAE;CACf,IAAI,SAAS,EAAE;CACf,IAAI,SAAS,EAAE;AAChB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/translations/keys.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Typed translation keys. Lookups must go through these constants, not string
|
|
4
|
+
* literals (enforced by requireI18nKeysTyped.grit). Every key here must have a
|
|
5
|
+
* value in every locale (`en.ts`), or it is a type error.
|
|
6
|
+
*/
|
|
7
|
+
declare const keys: {
|
|
8
|
+
readonly gridView: "folderPicker:gridView";
|
|
9
|
+
readonly listView: "folderPicker:listView";
|
|
10
|
+
readonly orderLabel: "folderPicker:orderLabel";
|
|
11
|
+
readonly pickManyHint: "folderPicker:pickManyHint";
|
|
12
|
+
readonly pluginName: "folderPicker:pluginName";
|
|
13
|
+
readonly retry: "folderPicker:retry";
|
|
14
|
+
readonly sortByLabel: "folderPicker:sortByLabel";
|
|
15
|
+
};
|
|
16
|
+
type TranslationKey = (typeof keys)[keyof typeof keys];
|
|
17
|
+
//#endregion
|
|
18
|
+
export { TranslationKey, keys };
|
|
19
|
+
//# sourceMappingURL=keys.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/translations/keys.ts
|
|
2
|
+
/**
|
|
3
|
+
* Typed translation keys. Lookups must go through these constants, not string
|
|
4
|
+
* literals (enforced by requireI18nKeysTyped.grit). Every key here must have a
|
|
5
|
+
* value in every locale (`en.ts`), or it is a type error.
|
|
6
|
+
*/
|
|
7
|
+
const keys = {
|
|
8
|
+
gridView: "folderPicker:gridView",
|
|
9
|
+
listView: "folderPicker:listView",
|
|
10
|
+
orderLabel: "folderPicker:orderLabel",
|
|
11
|
+
pickManyHint: "folderPicker:pickManyHint",
|
|
12
|
+
pluginName: "folderPicker:pluginName",
|
|
13
|
+
retry: "folderPicker:retry",
|
|
14
|
+
sortByLabel: "folderPicker:sortByLabel"
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { keys };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.js","names":[],"sources":["../../src/translations/keys.ts"],"sourcesContent":["/**\n * Typed translation keys. Lookups must go through these constants, not string\n * literals (enforced by requireI18nKeysTyped.grit). Every key here must have a\n * value in every locale (`en.ts`), or it is a type error.\n */\nexport const keys = {\n\tgridView: 'folderPicker:gridView',\n\tlistView: 'folderPicker:listView',\n\torderLabel: 'folderPicker:orderLabel',\n\tpickManyHint: 'folderPicker:pickManyHint',\n\tpluginName: 'folderPicker:pluginName',\n\tretry: 'folderPicker:retry',\n\tsortByLabel: 'folderPicker:sortByLabel',\n} as const\n\nexport type TranslationKey = (typeof keys)[keyof typeof keys]\n"],"mappings":";;;;;;AAKA,MAAa,OAAO;CACnB,UAAU;CACV,UAAU;CACV,YAAY;CACZ,cAAc;CACd,YAAY;CACZ,OAAO;CACP,aAAa;AACd"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { keys } from "./keys.js";
|
|
2
|
+
//#region src/translations/uk.ts
|
|
3
|
+
const uk = {
|
|
4
|
+
[keys.gridView]: "Показати сіткою",
|
|
5
|
+
[keys.listView]: "Показати списком",
|
|
6
|
+
[keys.orderLabel]: "Порядок",
|
|
7
|
+
[keys.pickManyHint]: "Утримуйте {{modifier}}, щоб вибрати кілька, {{range}} для діапазону.",
|
|
8
|
+
[keys.pluginName]: "Вибір теки",
|
|
9
|
+
[keys.retry]: "Спробувати ще раз",
|
|
10
|
+
[keys.sortByLabel]: "Сортувати за"
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { uk };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=uk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uk.js","names":[],"sources":["../../src/translations/uk.ts"],"sourcesContent":["import { keys, type TranslationKey } from './keys'\n\nexport const uk: Record<TranslationKey, string> = {\n\t[keys.gridView]: 'Показати сіткою',\n\t[keys.listView]: 'Показати списком',\n\t[keys.orderLabel]: 'Порядок',\n\t[keys.pickManyHint]: 'Утримуйте {{modifier}}, щоб вибрати кілька, {{range}} для діапазону.',\n\t[keys.pluginName]: 'Вибір теки',\n\t[keys.retry]: 'Спробувати ще раз',\n\t[keys.sortByLabel]: 'Сортувати за',\n}\n"],"mappings":";;AAEA,MAAa,KAAqC;EAChD,KAAK,WAAW;EAChB,KAAK,WAAW;EAChB,KAAK,aAAa;EAClB,KAAK,eAAe;EACpB,KAAK,aAAa;EAClB,KAAK,QAAQ;EACb,KAAK,cAAc;AACrB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useTranslation } from "@payloadcms/ui";
|
|
3
|
+
//#region src/translations/useTranslation.ts
|
|
4
|
+
/**
|
|
5
|
+
* `useTranslation` bound to this plugin's keys, so `t(keys.X)` typechecks without
|
|
6
|
+
* a per-call `@ts-expect-error`. Returns Payload's `{ t, i18n }` unchanged.
|
|
7
|
+
*/
|
|
8
|
+
const useTranslation$1 = () => useTranslation();
|
|
9
|
+
//#endregion
|
|
10
|
+
export { useTranslation$1 as useTranslation };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=useTranslation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTranslation.js","names":["useTranslation","usePayloadTranslation"],"sources":["../../src/translations/useTranslation.ts"],"sourcesContent":["'use client'\n\nimport { useTranslation as usePayloadTranslation } from '@payloadcms/ui'\n\nimport type { TranslationKey } from './keys'\n\n/**\n * `useTranslation` bound to this plugin's keys, so `t(keys.X)` typechecks without\n * a per-call `@ts-expect-error`. Returns Payload's `{ t, i18n }` unchanged.\n */\nexport const useTranslation = () => usePayloadTranslation<Record<string, never>, TranslationKey>()\n"],"mappings":";;;;;;;AAUA,MAAaA,yBAAuBC,eAA6D"}
|
package/package.json
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@10x-media/folder-picker",
|
|
3
|
+
"version": "0.1.0-beta.0",
|
|
4
|
+
"description": "Browse and select files through your folder hierarchy directly inside Payload CMS upload fields",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/10x-media/payload-plugins.git",
|
|
9
|
+
"directory": "packages/folder-picker"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/10x-media/payload-plugins/tree/main/packages/folder-picker",
|
|
12
|
+
"bugs": "https://github.com/10x-media/payload-plugins/issues",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"payload",
|
|
15
|
+
"payloadcms",
|
|
16
|
+
"plugin"
|
|
17
|
+
],
|
|
18
|
+
"author": "10x-media",
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22.18.0"
|
|
21
|
+
},
|
|
22
|
+
"type": "module",
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"import": "./dist/index.js",
|
|
28
|
+
"default": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./types": {
|
|
31
|
+
"types": "./dist/exports/types.d.ts",
|
|
32
|
+
"import": "./dist/exports/types.js",
|
|
33
|
+
"default": "./dist/exports/types.js"
|
|
34
|
+
},
|
|
35
|
+
"./client": {
|
|
36
|
+
"types": "./dist/exports/client.d.ts",
|
|
37
|
+
"import": "./dist/exports/client.js",
|
|
38
|
+
"default": "./dist/exports/client.js"
|
|
39
|
+
},
|
|
40
|
+
"./i18n": {
|
|
41
|
+
"types": "./dist/exports/i18n.d.ts",
|
|
42
|
+
"import": "./dist/exports/i18n.js",
|
|
43
|
+
"default": "./dist/exports/i18n.js"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist",
|
|
48
|
+
"README.md",
|
|
49
|
+
"CHANGELOG.md",
|
|
50
|
+
"LICENSE"
|
|
51
|
+
],
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@dnd-kit/core": "6.3.1",
|
|
54
|
+
"@payloadcms/translations": "^3.83.0",
|
|
55
|
+
"@payloadcms/ui": "^3.83.0",
|
|
56
|
+
"payload": "^3.83.0",
|
|
57
|
+
"react": "^19.0.0",
|
|
58
|
+
"react-dom": "^19.0.0"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@dnd-kit/core": "6.3.1",
|
|
62
|
+
"@payloadcms/db-mongodb": "3.85.0",
|
|
63
|
+
"@payloadcms/db-postgres": "3.85.0",
|
|
64
|
+
"@payloadcms/ui": "3.85.0",
|
|
65
|
+
"@playwright/test": "1.60.0",
|
|
66
|
+
"@payloadcms/translations": "3.85.0",
|
|
67
|
+
"@types/node": "22.19.19",
|
|
68
|
+
"@types/react": "19.2.15",
|
|
69
|
+
"@types/react-dom": "19.2.3",
|
|
70
|
+
"payload": "3.85.0",
|
|
71
|
+
"playwright": "1.60.0",
|
|
72
|
+
"react": "19.2.6",
|
|
73
|
+
"react-dom": "19.2.6",
|
|
74
|
+
"tsdown": "0.22.1",
|
|
75
|
+
"typescript": "5.9.3",
|
|
76
|
+
"vitest": "4.1.7",
|
|
77
|
+
"@10x-media/tsdown-config": "0.0.0",
|
|
78
|
+
"@10x-media/payload-test-harness": "0.0.0",
|
|
79
|
+
"@10x-media/tsconfig": "0.0.0",
|
|
80
|
+
"@10x-media/vitest-config": "0.0.0"
|
|
81
|
+
},
|
|
82
|
+
"publishConfig": {
|
|
83
|
+
"access": "public"
|
|
84
|
+
},
|
|
85
|
+
"scripts": {
|
|
86
|
+
"build": "tsdown",
|
|
87
|
+
"lint": "biome check src tests dev",
|
|
88
|
+
"lint:fix": "biome check --write src tests dev",
|
|
89
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
90
|
+
"test": "vitest run",
|
|
91
|
+
"test:unit": "vitest run src",
|
|
92
|
+
"test:int": "vitest run tests/int",
|
|
93
|
+
"test:matrix": "DB_MATRIX=mongo vitest run tests/int/matrix.int.spec.ts && DB_MATRIX=postgres vitest run tests/int/matrix.int.spec.ts",
|
|
94
|
+
"test:container": "TEST_DB=container DB_MATRIX=mongo vitest run tests/int/matrix.int.spec.ts && TEST_DB=container DB_MATRIX=postgres vitest run tests/int/matrix.int.spec.ts",
|
|
95
|
+
"test:e2e": "bash scripts/e2e.sh",
|
|
96
|
+
"dev": "pnpm --filter @10x-media/folder-picker-dev dev",
|
|
97
|
+
"start": "pnpm --filter @10x-media/folder-picker-dev start",
|
|
98
|
+
"generate": "pnpm --filter @10x-media/folder-picker-dev generate",
|
|
99
|
+
"generate:types": "pnpm --filter @10x-media/folder-picker-dev generate:types",
|
|
100
|
+
"generate:importmap": "pnpm --filter @10x-media/folder-picker-dev generate:importmap",
|
|
101
|
+
"migrate": "pnpm --filter @10x-media/folder-picker-dev migrate",
|
|
102
|
+
"migrate:create": "pnpm --filter @10x-media/folder-picker-dev migrate:create",
|
|
103
|
+
"migrate:down": "pnpm --filter @10x-media/folder-picker-dev migrate:down",
|
|
104
|
+
"migrate:refresh": "pnpm --filter @10x-media/folder-picker-dev migrate:refresh",
|
|
105
|
+
"migrate:reset": "pnpm --filter @10x-media/folder-picker-dev migrate:reset",
|
|
106
|
+
"migrate:status": "pnpm --filter @10x-media/folder-picker-dev migrate:status",
|
|
107
|
+
"migrate:fresh": "pnpm --filter @10x-media/folder-picker-dev migrate:fresh",
|
|
108
|
+
"clean": "rm -rf dist *.tsbuildinfo dev/.next"
|
|
109
|
+
}
|
|
110
|
+
}
|