@payloadcms/ui 4.0.0-internal.a6582c6 → 4.0.0-internal.a7ca468
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/dist/elements/CopyToClipboard/index.d.ts +1 -0
- package/dist/elements/CopyToClipboard/index.d.ts.map +1 -1
- package/dist/elements/CopyToClipboard/index.js +10 -8
- package/dist/elements/CopyToClipboard/index.js.map +1 -1
- package/dist/elements/CreateDocumentButton/index.d.ts.map +1 -1
- package/dist/elements/CreateDocumentButton/index.js +4 -1
- package/dist/elements/CreateDocumentButton/index.js.map +1 -1
- package/dist/elements/FieldSelect/reduceFieldOptions.d.ts.map +1 -1
- package/dist/elements/FieldSelect/reduceFieldOptions.js +3 -1
- package/dist/elements/FieldSelect/reduceFieldOptions.js.map +1 -1
- package/dist/elements/FieldSelect/reduceFieldOptions.spec.js +143 -0
- package/dist/elements/FieldSelect/reduceFieldOptions.spec.js.map +1 -0
- package/dist/elements/FileManager/FileToolbar/index.d.ts.map +1 -1
- package/dist/elements/FileManager/FileToolbar/index.js +3 -0
- package/dist/elements/FileManager/FileToolbar/index.js.map +1 -1
- package/dist/elements/Table/OrderableRow.d.ts +3 -1
- package/dist/elements/Table/OrderableRow.d.ts.map +1 -1
- package/dist/elements/Table/OrderableRow.js +17 -1
- package/dist/elements/Table/OrderableRow.js.map +1 -1
- package/dist/elements/Table/OrderableTable.d.ts.map +1 -1
- package/dist/elements/Table/OrderableTable.js +19 -2
- package/dist/elements/Table/OrderableTable.js.map +1 -1
- package/dist/forms/fieldSchemasToFormState/addFieldStatePromise.js +1 -1
- package/dist/forms/fieldSchemasToFormState/addFieldStatePromise.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/views/HierarchyList/index.css +1 -1
- package/dist/views/List/index.css +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/elements/CopyToClipboard/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAKvC,OAAO,aAAa,CAAA;AAIpB,MAAM,MAAM,KAAK,GAAG;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/elements/CopyToClipboard/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAKvC,OAAO,aAAa,CAAA;AAIpB,MAAM,MAAM,KAAK,GAAG;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAsC3C,CAAA"}
|
|
@@ -9,9 +9,10 @@ import { Tooltip } from '../Tooltip/index.js';
|
|
|
9
9
|
import './index.css';
|
|
10
10
|
const baseClass = 'copy-to-clipboard';
|
|
11
11
|
export const CopyToClipboard = t0 => {
|
|
12
|
-
const $ = _c(
|
|
12
|
+
const $ = _c(12);
|
|
13
13
|
const {
|
|
14
14
|
defaultMessage,
|
|
15
|
+
icon,
|
|
15
16
|
successMessage,
|
|
16
17
|
value
|
|
17
18
|
} = t0;
|
|
@@ -50,14 +51,14 @@ export const CopyToClipboard = t0 => {
|
|
|
50
51
|
t3 = $[3];
|
|
51
52
|
}
|
|
52
53
|
let t4;
|
|
53
|
-
if ($[4] !== copied || $[5] !== defaultMessage || $[6] !== hovered || $[7] !==
|
|
54
|
+
if ($[4] !== copied || $[5] !== defaultMessage || $[6] !== hovered || $[7] !== icon || $[8] !== successMessage || $[9] !== t || $[10] !== t1) {
|
|
54
55
|
t4 = _jsxs("button", {
|
|
55
56
|
className: baseClass,
|
|
56
57
|
onClick: t1,
|
|
57
58
|
onMouseEnter: t2,
|
|
58
59
|
onMouseLeave: t3,
|
|
59
60
|
type: "button",
|
|
60
|
-
children: [_jsx(CopyIcon, {}), _jsxs(Tooltip, {
|
|
61
|
+
children: [icon ?? _jsx(CopyIcon, {}), _jsxs(Tooltip, {
|
|
61
62
|
delay: copied ? 0 : undefined,
|
|
62
63
|
show: hovered || copied,
|
|
63
64
|
children: [copied && (successMessage ?? t("general:copied")), !copied && (defaultMessage ?? t("general:copy"))]
|
|
@@ -66,12 +67,13 @@ export const CopyToClipboard = t0 => {
|
|
|
66
67
|
$[4] = copied;
|
|
67
68
|
$[5] = defaultMessage;
|
|
68
69
|
$[6] = hovered;
|
|
69
|
-
$[7] =
|
|
70
|
-
$[8] =
|
|
71
|
-
$[9] =
|
|
72
|
-
$[10] =
|
|
70
|
+
$[7] = icon;
|
|
71
|
+
$[8] = successMessage;
|
|
72
|
+
$[9] = t;
|
|
73
|
+
$[10] = t1;
|
|
74
|
+
$[11] = t4;
|
|
73
75
|
} else {
|
|
74
|
-
t4 = $[
|
|
76
|
+
t4 = $[11];
|
|
75
77
|
}
|
|
76
78
|
return t4;
|
|
77
79
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["c","_c","React","useState","CopyIcon","useTranslation","Tooltip","baseClass","CopyToClipboard","t0","$","defaultMessage","successMessage","value","copied","setCopied","hovered","setHovered","t","t1","navigator","clipboard","writeText","t2","t3","Symbol","for","t4","_jsxs","className","onClick","onMouseEnter","onMouseLeave","type","children","_jsx","delay","undefined","show"],"sources":["../../../src/elements/CopyToClipboard/index.tsx"],"sourcesContent":["'use client'\nimport React, { useState } from 'react'\n\nimport { CopyIcon } from '../../icons/Copy/index.js'\nimport { useTranslation } from '../../providers/Translation/index.js'\nimport { Tooltip } from '../Tooltip/index.js'\nimport './index.css'\n\nconst baseClass = 'copy-to-clipboard'\n\nexport type Props = {\n defaultMessage?: string\n successMessage?: string\n value?: string\n}\n\nexport const CopyToClipboard: React.FC<Props> = ({
|
|
1
|
+
{"version":3,"file":"index.js","names":["c","_c","React","useState","CopyIcon","useTranslation","Tooltip","baseClass","CopyToClipboard","t0","$","defaultMessage","icon","successMessage","value","copied","setCopied","hovered","setHovered","t","t1","navigator","clipboard","writeText","t2","t3","Symbol","for","t4","_jsxs","className","onClick","onMouseEnter","onMouseLeave","type","children","_jsx","delay","undefined","show"],"sources":["../../../src/elements/CopyToClipboard/index.tsx"],"sourcesContent":["'use client'\nimport React, { useState } from 'react'\n\nimport { CopyIcon } from '../../icons/Copy/index.js'\nimport { useTranslation } from '../../providers/Translation/index.js'\nimport { Tooltip } from '../Tooltip/index.js'\nimport './index.css'\n\nconst baseClass = 'copy-to-clipboard'\n\nexport type Props = {\n defaultMessage?: string\n icon?: React.ReactNode\n successMessage?: string\n value?: string\n}\n\nexport const CopyToClipboard: React.FC<Props> = ({\n defaultMessage,\n icon,\n successMessage,\n value,\n}) => {\n const [copied, setCopied] = useState(false)\n const [hovered, setHovered] = useState(false)\n const { t } = useTranslation()\n\n if (value) {\n return (\n <button\n className={baseClass}\n onClick={async () => {\n await navigator.clipboard.writeText(value)\n setCopied(true)\n }}\n onMouseEnter={() => {\n setHovered(true)\n setCopied(false)\n }}\n onMouseLeave={() => {\n setHovered(false)\n setCopied(false)\n }}\n type=\"button\"\n >\n {icon ?? <CopyIcon />}\n <Tooltip delay={copied ? 0 : undefined} show={hovered || copied}>\n {copied && (successMessage ?? t('general:copied'))}\n {!copied && (defaultMessage ?? t('general:copy'))}\n </Tooltip>\n </button>\n )\n }\n\n return null\n}\n"],"mappings":"AAAA;;AAAA,SAAAA,CAAA,IAAAC,EAAA;;AACA,OAAOC,KAAA,IAASC,QAAQ,QAAQ;AAEhC,SAASC,QAAQ,QAAQ;AACzB,SAASC,cAAc,QAAQ;AAC/B,SAASC,OAAO,QAAQ;AACxB,OAAO;AAEP,MAAMC,SAAA,GAAY;AASlB,OAAO,MAAMC,eAAA,GAAmCC,EAAA;EAAA,MAAAC,CAAA,GAAAT,EAAA;EAAC;IAAAU,cAAA;IAAAC,IAAA;IAAAC,cAAA;IAAAC;EAAA,IAAAL,EAKhD;EACC,OAAAM,MAAA,EAAAC,SAAA,IAA4Bb,QAAA,MAAS;EACrC,OAAAc,OAAA,EAAAC,UAAA,IAA8Bf,QAAA,MAAS;EACvC;IAAAgB;EAAA,IAAcd,cAAA;EAAA,IAEVS,KAAA;IAAA,IAAAM,EAAA;IAAA,IAAAV,CAAA,QAAAI,KAAA;MAIWM,EAAA,SAAAA,CAAA;QAAA,MACDC,SAAA,CAAAC,SAAA,CAAAC,SAAA,CAA8BT,KAAA;QACpCE,SAAA,KAAU;MAAA;MACZN,CAAA,MAAAI,KAAA;MAAAJ,CAAA,MAAAU,EAAA;IAAA;MAAAA,EAAA,GAAAV,CAAA;IAAA;IAAA,IAAAc,EAAA;IAAA,IAAAC,EAAA;IAAA,IAAAf,CAAA,QAAAgB,MAAA,CAAAC,GAAA;MACcH,EAAA,GAAAA,CAAA;QACZN,UAAA,KAAW;QACXF,SAAA,MAAU;MAAA;MAEES,EAAA,GAAAA,CAAA;QACZP,UAAA,MAAW;QACXF,SAAA,MAAU;MAAA;MACZN,CAAA,MAAAc,EAAA;MAAAd,CAAA,MAAAe,EAAA;IAAA;MAAAD,EAAA,GAAAd,CAAA;MAAAe,EAAA,GAAAf,CAAA;IAAA;IAAA,IAAAkB,EAAA;IAAA,IAAAlB,CAAA,QAAAK,MAAA,IAAAL,CAAA,QAAAC,cAAA,IAAAD,CAAA,QAAAO,OAAA,IAAAP,CAAA,QAAAE,IAAA,IAAAF,CAAA,QAAAG,cAAA,IAAAH,CAAA,QAAAS,CAAA,IAAAT,CAAA,SAAAU,EAAA;MAbFQ,EAAA,GAAAC,KAAA,CAAC;QAAAC,SAAA,EAAAvB,SAAA;QAAAwB,OAAA,EAEUX,EAGT;QAAAY,YAAA,EACcR,EAGd;QAAAS,YAAA,EACcR,EAGd;QAAAS,IAAA,EACK;QAAAC,QAAA,GAEJvB,IAAA,IAAQwB,IAAA,CAAAhC,QAAA,IAAC,GACVyB,KAAA,CAAAvB,OAAA;UAAA+B,KAAA,EAAgBtB,MAAA,OAAAuB,SAAa;UAAAC,IAAA,EAAiBtB,OAAA,IAAWF,MAAA;UAAAoB,QAAA,GACtDpB,MAAA,KAAWF,cAAA,IAAkBM,CAAA,CAAE,iBAAgB,GAC/C,CAACJ,MAAA,KAAWJ,cAAA,IAAkBQ,CAAA,CAAE,eAAc;QAAA,C;;;;;;;;;;;;;WAnBnDS,E;;;CA0BN","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/elements/CreateDocumentButton/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/elements/CreateDocumentButton/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAEvC,OAAO,KAAK,EAAE,KAAK,IAAI,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAC9D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAA;AAU/E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,cAAc,EAAE,MAAM,CAAA;IACtB,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACrC,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,mBAAmB;IACnB,WAAW,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,CAAA;IACxC,yCAAyC;IACzC,WAAW,EAAE,gBAAgB,EAAE,CAAA;IAC/B,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAA;IAClB,mGAAmG;IACnG,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mDAAmD;IACnD,MAAM,CAAC,EAAE,0BAA0B,CAAC,QAAQ,CAAC,CAAA;IAC7C,kBAAkB;IAClB,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAC3B,CAAA;AAED,wBAAgB,oBAAoB,CAAC,EACnC,WAAoB,EACpB,WAAW,EACX,UAAU,EACV,KAAK,EACL,MAAM,EACN,IAAe,GAChB,EAAE,yBAAyB,qBAuG3B"}
|
|
@@ -4,6 +4,7 @@ import { c as _c } from "react/compiler-runtime";
|
|
|
4
4
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
5
5
|
import { useModal } from '@faceless-ui/modal';
|
|
6
6
|
import { getTranslation } from '@payloadcms/translations';
|
|
7
|
+
import { hasAutosaveEnabled } from 'payload/shared';
|
|
7
8
|
import React, { useState } from 'react';
|
|
8
9
|
import { useConfig } from '../../providers/Config/index.js';
|
|
9
10
|
import { useTranslation } from '../../providers/Translation/index.js';
|
|
@@ -70,7 +71,9 @@ export function CreateDocumentButton(t0) {
|
|
|
70
71
|
let t5;
|
|
71
72
|
if ($[6] !== closeModal || $[7] !== drawerSlug || $[8] !== onSave) {
|
|
72
73
|
t5 = args => {
|
|
73
|
-
|
|
74
|
+
if (!hasAutosaveEnabled(args.collectionConfig)) {
|
|
75
|
+
closeModal(drawerSlug);
|
|
76
|
+
}
|
|
74
77
|
return onSave?.(args);
|
|
75
78
|
};
|
|
76
79
|
$[6] = closeModal;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["c","_c","useModal","getTranslation","React","useState","useConfig","useTranslation","Button","DocumentDrawer","Popup","PopupList","baseClass","CreateDocumentButton","t0","$","buttonStyle","t1","collections","drawerSlug","label","onSave","size","t2","undefined","config","i18n","t","closeModal","openModal","selectedCollection","setSelectedCollection","length","t3","option","collectionConfig","find","slug","collectionSlug","labels","singular","getCollectionLabel","t4","collection","handleSelect","t5","args","handleSave","collection_0","t6","buttonLabel","t7","_jsxs","_Fragment","children","_jsx","className","margin","onClick","initialData","redirectAfterCreate","buttonLabel_0","collection_1","button","icon","horizontalAlign","ButtonGroup","map"],"sources":["../../../src/elements/CreateDocumentButton/index.tsx"],"sourcesContent":["'use client'\n\nimport { useModal } from '@faceless-ui/modal'\nimport { getTranslation } from '@payloadcms/translations'\nimport React, { useState } from 'react'\n\nimport type { Props as ButtonProps } from '../Button/types.js'\nimport type { DocumentDrawerContextProps } from '../DocumentDrawer/Provider.js'\n\nimport { useConfig } from '../../providers/Config/index.js'\nimport { useTranslation } from '../../providers/Translation/index.js'\nimport { Button } from '../Button/index.js'\nimport { DocumentDrawer } from '../DocumentDrawer/index.js'\nimport { Popup, PopupList } from '../Popup/index.js'\n\nconst baseClass = 'create-document-button'\n\nexport type CollectionOption = {\n collectionSlug: string\n /** Initial data to populate the form when creating */\n initialData?: Record<string, unknown>\n /** Optional label override (defaults to collection's singular label) */\n label?: string\n}\n\nexport type CreateDocumentButtonProps = {\n /** Button style */\n buttonStyle?: ButtonProps['buttonStyle']\n /** Collections available for creation */\n collections: CollectionOption[]\n /** Unique slug for the drawer modal */\n drawerSlug: string\n /** Custom button label (defaults to \"Create New\" for multiple, \"Create New {label}\" for single) */\n label?: string\n /** Called when a document is successfully saved */\n onSave?: DocumentDrawerContextProps['onSave']\n /** Button size */\n size?: ButtonProps['size']\n}\n\nexport function CreateDocumentButton({\n buttonStyle = 'pill',\n collections,\n drawerSlug,\n label,\n onSave,\n size = 'medium',\n}: CreateDocumentButtonProps) {\n const { config } = useConfig()\n const { i18n, t } = useTranslation()\n const { closeModal, openModal } = useModal()\n\n const [selectedCollection, setSelectedCollection] = useState<CollectionOption | null>(null)\n\n if (collections.length === 0) {\n return null\n }\n\n const getCollectionLabel = (option: CollectionOption) => {\n if (option.label) {\n return option.label\n }\n const collectionConfig = config.collections.find((c) => c.slug === option.collectionSlug)\n return getTranslation(collectionConfig?.labels?.singular, i18n) || option.collectionSlug\n }\n\n const handleSelect = (collection: CollectionOption) => {\n setSelectedCollection(collection)\n openModal(drawerSlug)\n }\n\n const handleSave: DocumentDrawerContextProps['onSave'] = (args) => {\n closeModal(drawerSlug)\n return onSave?.(args)\n }\n\n // Single collection - render simple button\n if (collections.length === 1) {\n const collection = collections[0]\n const buttonLabel = label || t('general:createNew')\n\n return (\n <>\n <Button\n buttonStyle={buttonStyle}\n className={baseClass}\n margin={false}\n onClick={() => handleSelect(collection)}\n size={size}\n >\n {buttonLabel}\n </Button>\n {selectedCollection && (\n <DocumentDrawer\n collectionSlug={selectedCollection.collectionSlug}\n drawerSlug={drawerSlug}\n initialData={selectedCollection.initialData}\n onSave={handleSave}\n redirectAfterCreate={false}\n />\n )}\n </>\n )\n }\n\n // Multiple collections - render dropdown\n const buttonLabel = label || t('general:createNew')\n\n return (\n <>\n <Popup\n button={\n <Button\n buttonStyle={buttonStyle}\n className={`${baseClass}__popup-button`}\n icon=\"chevron\"\n size={size}\n >\n {buttonLabel}\n </Button>\n }\n buttonType=\"custom\"\n className={`${baseClass}__popup`}\n horizontalAlign=\"right\"\n >\n <PopupList.ButtonGroup>\n {collections.map((collection) => (\n <PopupList.Button\n key={collection.collectionSlug}\n onClick={() => handleSelect(collection)}\n >\n {getCollectionLabel(collection)}\n </PopupList.Button>\n ))}\n </PopupList.ButtonGroup>\n </Popup>\n {selectedCollection && (\n <DocumentDrawer\n collectionSlug={selectedCollection.collectionSlug}\n drawerSlug={drawerSlug}\n initialData={selectedCollection.initialData}\n onSave={handleSave}\n redirectAfterCreate={false}\n />\n )}\n </>\n )\n}\n"],"mappings":"AAAA;;AAAA,SAAAA,CAAA,IAAAC,EAAA;;AAEA,SAASC,QAAQ,QAAQ;AACzB,SAASC,cAAc,QAAQ;AAC/B,OAAOC,KAAA,IAASC,QAAQ,QAAQ;AAKhC,SAASC,SAAS,QAAQ;AAC1B,SAASC,cAAc,QAAQ;AAC/B,SAASC,MAAM,QAAQ;AACvB,SAASC,cAAc,QAAQ;AAC/B,SAASC,KAAK,EAAEC,SAAS,QAAQ;AAEjC,MAAMC,SAAA,GAAY;AAyBlB,OAAO,SAAAC,qBAAAC,EAAA;EAAA,MAAAC,CAAA,
|
|
1
|
+
{"version":3,"file":"index.js","names":["c","_c","useModal","getTranslation","hasAutosaveEnabled","React","useState","useConfig","useTranslation","Button","DocumentDrawer","Popup","PopupList","baseClass","CreateDocumentButton","t0","$","buttonStyle","t1","collections","drawerSlug","label","onSave","size","t2","undefined","config","i18n","t","closeModal","openModal","selectedCollection","setSelectedCollection","length","t3","option","collectionConfig","find","slug","collectionSlug","labels","singular","getCollectionLabel","t4","collection","handleSelect","t5","args","handleSave","collection_0","t6","buttonLabel","t7","_jsxs","_Fragment","children","_jsx","className","margin","onClick","initialData","redirectAfterCreate","buttonLabel_0","collection_1","button","icon","horizontalAlign","ButtonGroup","map"],"sources":["../../../src/elements/CreateDocumentButton/index.tsx"],"sourcesContent":["'use client'\n\nimport { useModal } from '@faceless-ui/modal'\nimport { getTranslation } from '@payloadcms/translations'\nimport { hasAutosaveEnabled } from 'payload/shared'\nimport React, { useState } from 'react'\n\nimport type { Props as ButtonProps } from '../Button/types.js'\nimport type { DocumentDrawerContextProps } from '../DocumentDrawer/Provider.js'\n\nimport { useConfig } from '../../providers/Config/index.js'\nimport { useTranslation } from '../../providers/Translation/index.js'\nimport { Button } from '../Button/index.js'\nimport { DocumentDrawer } from '../DocumentDrawer/index.js'\nimport { Popup, PopupList } from '../Popup/index.js'\n\nconst baseClass = 'create-document-button'\n\nexport type CollectionOption = {\n collectionSlug: string\n /** Initial data to populate the form when creating */\n initialData?: Record<string, unknown>\n /** Optional label override (defaults to collection's singular label) */\n label?: string\n}\n\nexport type CreateDocumentButtonProps = {\n /** Button style */\n buttonStyle?: ButtonProps['buttonStyle']\n /** Collections available for creation */\n collections: CollectionOption[]\n /** Unique slug for the drawer modal */\n drawerSlug: string\n /** Custom button label (defaults to \"Create New\" for multiple, \"Create New {label}\" for single) */\n label?: string\n /** Called when a document is successfully saved */\n onSave?: DocumentDrawerContextProps['onSave']\n /** Button size */\n size?: ButtonProps['size']\n}\n\nexport function CreateDocumentButton({\n buttonStyle = 'pill',\n collections,\n drawerSlug,\n label,\n onSave,\n size = 'medium',\n}: CreateDocumentButtonProps) {\n const { config } = useConfig()\n const { i18n, t } = useTranslation()\n const { closeModal, openModal } = useModal()\n\n const [selectedCollection, setSelectedCollection] = useState<CollectionOption | null>(null)\n\n if (collections.length === 0) {\n return null\n }\n\n const getCollectionLabel = (option: CollectionOption) => {\n if (option.label) {\n return option.label\n }\n const collectionConfig = config.collections.find((c) => c.slug === option.collectionSlug)\n return getTranslation(collectionConfig?.labels?.singular, i18n) || option.collectionSlug\n }\n\n const handleSelect = (collection: CollectionOption) => {\n setSelectedCollection(collection)\n openModal(drawerSlug)\n }\n\n const handleSave: DocumentDrawerContextProps['onSave'] = (args) => {\n if (!hasAutosaveEnabled(args.collectionConfig)) {\n closeModal(drawerSlug)\n }\n\n return onSave?.(args)\n }\n\n // Single collection - render simple button\n if (collections.length === 1) {\n const collection = collections[0]\n const buttonLabel = label || t('general:createNew')\n\n return (\n <>\n <Button\n buttonStyle={buttonStyle}\n className={baseClass}\n margin={false}\n onClick={() => handleSelect(collection)}\n size={size}\n >\n {buttonLabel}\n </Button>\n {selectedCollection && (\n <DocumentDrawer\n collectionSlug={selectedCollection.collectionSlug}\n drawerSlug={drawerSlug}\n initialData={selectedCollection.initialData}\n onSave={handleSave}\n redirectAfterCreate={false}\n />\n )}\n </>\n )\n }\n\n // Multiple collections - render dropdown\n const buttonLabel = label || t('general:createNew')\n\n return (\n <>\n <Popup\n button={\n <Button\n buttonStyle={buttonStyle}\n className={`${baseClass}__popup-button`}\n icon=\"chevron\"\n size={size}\n >\n {buttonLabel}\n </Button>\n }\n buttonType=\"custom\"\n className={`${baseClass}__popup`}\n horizontalAlign=\"right\"\n >\n <PopupList.ButtonGroup>\n {collections.map((collection) => (\n <PopupList.Button\n key={collection.collectionSlug}\n onClick={() => handleSelect(collection)}\n >\n {getCollectionLabel(collection)}\n </PopupList.Button>\n ))}\n </PopupList.ButtonGroup>\n </Popup>\n {selectedCollection && (\n <DocumentDrawer\n collectionSlug={selectedCollection.collectionSlug}\n drawerSlug={drawerSlug}\n initialData={selectedCollection.initialData}\n onSave={handleSave}\n redirectAfterCreate={false}\n />\n )}\n </>\n )\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reduceFieldOptions.d.ts","sourceRoot":"","sources":["../../../src/elements/FieldSelect/reduceFieldOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAA;AAahF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,WAAW,CAAA;IAClB,gBAAgB,EAAE,yBAAyB,CAAA;IAC3C,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,KAAK,EAAE,aAAa,CAAA;CACrB,CAAA;AAED,eAAO,MAAM,kBAAkB,UAAW,WAAW,KAAG,OAOrD,CAAA;AAEH,eAAO,MAAM,kBAAkB,uEAO5B;IACD,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,CAAA;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAA;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,WAAW,EAChB;QACE,CAAC,SAAS,EAAE,MAAM,GAAG,yBAAyB,CAAA;KAC/C,GACD,yBAAyB,CAAA;CAC9B,KAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"reduceFieldOptions.d.ts","sourceRoot":"","sources":["../../../src/elements/FieldSelect/reduceFieldOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAA;AAahF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,WAAW,CAAA;IAClB,gBAAgB,EAAE,yBAAyB,CAAA;IAC3C,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,KAAK,EAAE,aAAa,CAAA;CACrB,CAAA;AAED,eAAO,MAAM,kBAAkB,UAAW,WAAW,KAAG,OAOrD,CAAA;AAEH,eAAO,MAAM,kBAAkB,uEAO5B;IACD,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,CAAA;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAA;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,WAAW,EAChB;QACE,CAAC,SAAS,EAAE,MAAM,GAAG,yBAAyB,CAAA;KAC/C,GACD,yBAAyB,CAAA;CAC9B,KAAG,WAAW,EA8Fd,CAAA"}
|
|
@@ -47,12 +47,14 @@ export const reduceFieldOptions = ({
|
|
|
47
47
|
return [...fieldsToUse, ...field.tabs.reduce((tabFields, tab) => {
|
|
48
48
|
if ('fields' in tab) {
|
|
49
49
|
const isNamedTab = 'name' in tab && tab.name;
|
|
50
|
+
const namedTabPermissions = isNamedTab && fieldPermissions && fieldPermissions !== true ? fieldPermissions[tab.name] : undefined;
|
|
51
|
+
const tabPermissions = isNamedTab ? namedTabPermissions === true ? true : namedTabPermissions?.fields ?? fieldPermissions : fieldPermissions;
|
|
50
52
|
return [...tabFields, ...reduceFieldOptions({
|
|
51
53
|
fields: tab.fields,
|
|
52
54
|
labelPrefix,
|
|
53
55
|
parentPath: path,
|
|
54
56
|
path: isNamedTab ? createNestedClientFieldPath(path, tab) : path,
|
|
55
|
-
permissions:
|
|
57
|
+
permissions: tabPermissions
|
|
56
58
|
})];
|
|
57
59
|
}
|
|
58
60
|
}, [])];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reduceFieldOptions.js","names":["fieldAffectsData","fieldHasSubFields","fieldIsHiddenOrDisabled","getFieldPermissions","isFieldDisabled","createNestedClientFieldPath","combineFieldLabel","ignoreFromBulkEdit","field","Boolean","type","unique","readOnly","reduceFieldOptions","fields","formState","labelPrefix","parentPath","path","permissions","CustomLabel","customComponents","Label","reduce","fieldsToUse","operation","hasOperationPermission","fieldPermissions","read","hasReadPermission","parentName","includes","split","length","fieldHasLabel","label","prefix","tabs","tabFields","tab","isNamedTab","name","formattedField","value"],"sources":["../../../src/elements/FieldSelect/reduceFieldOptions.ts"],"sourcesContent":["import type { ClientField, FormState, SanitizedFieldPermissions } from 'payload'\n\nimport {\n fieldAffectsData,\n fieldHasSubFields,\n fieldIsHiddenOrDisabled,\n getFieldPermissions,\n isFieldDisabled,\n} from 'payload/shared'\n\nimport { createNestedClientFieldPath } from '../../forms/Form/createNestedClientFieldPath.js'\nimport { combineFieldLabel } from '../../utilities/combineFieldLabel.js'\n\nexport type SelectedField = {\n field: ClientField\n fieldPermissions: SanitizedFieldPermissions\n path: string\n}\n\nexport type FieldOption = {\n label: React.ReactNode\n value: SelectedField\n}\n\nexport const ignoreFromBulkEdit = (field: ClientField): boolean =>\n Boolean(\n (fieldAffectsData(field) || field.type === 'ui') &&\n (isFieldDisabled(field, 'bulkEdit') ||\n field.unique ||\n fieldIsHiddenOrDisabled(field) ||\n ('readOnly' in field && field.readOnly)),\n )\n\nexport const reduceFieldOptions = ({\n fields,\n formState,\n labelPrefix = null,\n parentPath = '',\n path = '',\n permissions,\n}: {\n readonly fields: ClientField[]\n readonly formState?: FormState\n readonly labelPrefix?: React.ReactNode\n readonly parentPath?: string\n readonly path?: string\n readonly permissions:\n | {\n [fieldName: string]: SanitizedFieldPermissions\n }\n | SanitizedFieldPermissions\n}): FieldOption[] => {\n if (!fields) {\n return []\n }\n\n const CustomLabel = formState?.[path]?.customComponents?.Label\n\n return fields?.reduce((fieldsToUse, field) => {\n const {\n operation: hasOperationPermission,\n permissions: fieldPermissions,\n read: hasReadPermission,\n } = getFieldPermissions({\n field,\n operation: 'update',\n parentName: parentPath?.includes('.')\n ? parentPath.split('.')[parentPath.split('.').length - 1]\n : parentPath,\n permissions,\n })\n\n // escape for a variety of reasons, include ui fields as they have `name`.\n if (\n (fieldAffectsData(field) || field.type === 'ui') &&\n (isFieldDisabled(field, 'bulkEdit') ||\n field.unique ||\n fieldIsHiddenOrDisabled(field) ||\n ('readOnly' in field && field.readOnly) ||\n !hasOperationPermission ||\n !hasReadPermission)\n ) {\n return fieldsToUse\n }\n\n if (!(field.type === 'array' || field.type === 'blocks') && fieldHasSubFields(field)) {\n const fieldHasLabel = 'label' in field && field.label\n return [\n ...fieldsToUse,\n ...reduceFieldOptions({\n fields: field.fields,\n labelPrefix: fieldHasLabel\n ? combineFieldLabel({ CustomLabel, field, prefix: labelPrefix })\n : labelPrefix,\n parentPath: path,\n path: createNestedClientFieldPath(path, field),\n permissions: fieldPermissions,\n }),\n ]\n }\n\n if (field.type === 'tabs' && 'tabs' in field) {\n return [\n ...fieldsToUse,\n ...field.tabs.reduce((tabFields, tab) => {\n if ('fields' in tab) {\n const isNamedTab = 'name' in tab && tab.name\n return [\n ...tabFields,\n ...reduceFieldOptions({\n fields: tab.fields,\n labelPrefix,\n parentPath: path,\n path: isNamedTab ? createNestedClientFieldPath(path, tab as ClientField) : path,\n permissions:
|
|
1
|
+
{"version":3,"file":"reduceFieldOptions.js","names":["fieldAffectsData","fieldHasSubFields","fieldIsHiddenOrDisabled","getFieldPermissions","isFieldDisabled","createNestedClientFieldPath","combineFieldLabel","ignoreFromBulkEdit","field","Boolean","type","unique","readOnly","reduceFieldOptions","fields","formState","labelPrefix","parentPath","path","permissions","CustomLabel","customComponents","Label","reduce","fieldsToUse","operation","hasOperationPermission","fieldPermissions","read","hasReadPermission","parentName","includes","split","length","fieldHasLabel","label","prefix","tabs","tabFields","tab","isNamedTab","name","namedTabPermissions","undefined","tabPermissions","formattedField","value"],"sources":["../../../src/elements/FieldSelect/reduceFieldOptions.ts"],"sourcesContent":["import type { ClientField, FormState, SanitizedFieldPermissions } from 'payload'\n\nimport {\n fieldAffectsData,\n fieldHasSubFields,\n fieldIsHiddenOrDisabled,\n getFieldPermissions,\n isFieldDisabled,\n} from 'payload/shared'\n\nimport { createNestedClientFieldPath } from '../../forms/Form/createNestedClientFieldPath.js'\nimport { combineFieldLabel } from '../../utilities/combineFieldLabel.js'\n\nexport type SelectedField = {\n field: ClientField\n fieldPermissions: SanitizedFieldPermissions\n path: string\n}\n\nexport type FieldOption = {\n label: React.ReactNode\n value: SelectedField\n}\n\nexport const ignoreFromBulkEdit = (field: ClientField): boolean =>\n Boolean(\n (fieldAffectsData(field) || field.type === 'ui') &&\n (isFieldDisabled(field, 'bulkEdit') ||\n field.unique ||\n fieldIsHiddenOrDisabled(field) ||\n ('readOnly' in field && field.readOnly)),\n )\n\nexport const reduceFieldOptions = ({\n fields,\n formState,\n labelPrefix = null,\n parentPath = '',\n path = '',\n permissions,\n}: {\n readonly fields: ClientField[]\n readonly formState?: FormState\n readonly labelPrefix?: React.ReactNode\n readonly parentPath?: string\n readonly path?: string\n readonly permissions:\n | {\n [fieldName: string]: SanitizedFieldPermissions\n }\n | SanitizedFieldPermissions\n}): FieldOption[] => {\n if (!fields) {\n return []\n }\n\n const CustomLabel = formState?.[path]?.customComponents?.Label\n\n return fields?.reduce((fieldsToUse, field) => {\n const {\n operation: hasOperationPermission,\n permissions: fieldPermissions,\n read: hasReadPermission,\n } = getFieldPermissions({\n field,\n operation: 'update',\n parentName: parentPath?.includes('.')\n ? parentPath.split('.')[parentPath.split('.').length - 1]\n : parentPath,\n permissions,\n })\n\n // escape for a variety of reasons, include ui fields as they have `name`.\n if (\n (fieldAffectsData(field) || field.type === 'ui') &&\n (isFieldDisabled(field, 'bulkEdit') ||\n field.unique ||\n fieldIsHiddenOrDisabled(field) ||\n ('readOnly' in field && field.readOnly) ||\n !hasOperationPermission ||\n !hasReadPermission)\n ) {\n return fieldsToUse\n }\n\n if (!(field.type === 'array' || field.type === 'blocks') && fieldHasSubFields(field)) {\n const fieldHasLabel = 'label' in field && field.label\n return [\n ...fieldsToUse,\n ...reduceFieldOptions({\n fields: field.fields,\n labelPrefix: fieldHasLabel\n ? combineFieldLabel({ CustomLabel, field, prefix: labelPrefix })\n : labelPrefix,\n parentPath: path,\n path: createNestedClientFieldPath(path, field),\n permissions: fieldPermissions,\n }),\n ]\n }\n\n if (field.type === 'tabs' && 'tabs' in field) {\n return [\n ...fieldsToUse,\n ...field.tabs.reduce((tabFields, tab) => {\n if ('fields' in tab) {\n const isNamedTab = 'name' in tab && tab.name\n\n const namedTabPermissions =\n isNamedTab && fieldPermissions && fieldPermissions !== true\n ? fieldPermissions[tab.name]\n : undefined\n\n const tabPermissions = isNamedTab\n ? namedTabPermissions === true\n ? true\n : (namedTabPermissions?.fields ?? fieldPermissions)\n : fieldPermissions\n\n return [\n ...tabFields,\n ...reduceFieldOptions({\n fields: tab.fields,\n labelPrefix,\n parentPath: path,\n path: isNamedTab ? createNestedClientFieldPath(path, tab as ClientField) : path,\n permissions: tabPermissions,\n }),\n ]\n }\n }, []),\n ]\n }\n\n const formattedField: FieldOption = {\n label: combineFieldLabel({ CustomLabel, field, prefix: labelPrefix }),\n value: {\n field,\n fieldPermissions: fieldPermissions as SanitizedFieldPermissions,\n path: createNestedClientFieldPath(path, field),\n },\n }\n\n return [...fieldsToUse, formattedField]\n }, [])\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
// Mock the JSX label builder: irrelevant to permission logic and avoids needing a React runtime here.
|
|
3
|
+
vi.mock('../../utilities/combineFieldLabel.js', () => ({
|
|
4
|
+
combineFieldLabel: () => null
|
|
5
|
+
}));
|
|
6
|
+
import { reduceFieldOptions } from './reduceFieldOptions.js';
|
|
7
|
+
const text = name => ({
|
|
8
|
+
name,
|
|
9
|
+
type: 'text'
|
|
10
|
+
});
|
|
11
|
+
const namesOf = options => options.map(option => 'name' in option.value.field ? option.value.field.name : undefined);
|
|
12
|
+
const optionsFor = (fields, permissions) => reduceFieldOptions({
|
|
13
|
+
fields,
|
|
14
|
+
permissions: permissions
|
|
15
|
+
});
|
|
16
|
+
// Granted sets the operation to `true`; denied omits the key, mirroring `populateFieldPermissions`.
|
|
17
|
+
const granted = {
|
|
18
|
+
create: true,
|
|
19
|
+
read: true,
|
|
20
|
+
update: true
|
|
21
|
+
};
|
|
22
|
+
const readOnly = {
|
|
23
|
+
create: true,
|
|
24
|
+
read: true
|
|
25
|
+
};
|
|
26
|
+
describe('reduceFieldOptions', () => {
|
|
27
|
+
it('excludes a restricted field inside a named tab while keeping its clean siblings', () => {
|
|
28
|
+
const fields = [{
|
|
29
|
+
type: 'tabs',
|
|
30
|
+
tabs: [{
|
|
31
|
+
name: 'namedTab',
|
|
32
|
+
fields: [text('okInTab'), text('restrictedInTab')]
|
|
33
|
+
}]
|
|
34
|
+
}];
|
|
35
|
+
const permissions = {
|
|
36
|
+
namedTab: {
|
|
37
|
+
...granted,
|
|
38
|
+
fields: {
|
|
39
|
+
okInTab: granted,
|
|
40
|
+
restrictedInTab: readOnly
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const options = optionsFor(fields, permissions);
|
|
45
|
+
const names = namesOf(options);
|
|
46
|
+
expect(names).toContain('okInTab');
|
|
47
|
+
expect(names).not.toContain('restrictedInTab');
|
|
48
|
+
});
|
|
49
|
+
it('excludes a restricted field inside a nested named tab', () => {
|
|
50
|
+
const fields = [{
|
|
51
|
+
type: 'tabs',
|
|
52
|
+
tabs: [{
|
|
53
|
+
name: 'outerTab',
|
|
54
|
+
fields: [{
|
|
55
|
+
type: 'tabs',
|
|
56
|
+
tabs: [{
|
|
57
|
+
name: 'innerTab',
|
|
58
|
+
fields: [text('deepOk'), text('deepRestricted')]
|
|
59
|
+
}]
|
|
60
|
+
}]
|
|
61
|
+
}]
|
|
62
|
+
}];
|
|
63
|
+
const permissions = {
|
|
64
|
+
outerTab: {
|
|
65
|
+
...granted,
|
|
66
|
+
fields: {
|
|
67
|
+
innerTab: {
|
|
68
|
+
...granted,
|
|
69
|
+
fields: {
|
|
70
|
+
deepOk: granted,
|
|
71
|
+
deepRestricted: readOnly
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const options = optionsFor(fields, permissions);
|
|
78
|
+
const names = namesOf(options);
|
|
79
|
+
expect(names).toContain('deepOk');
|
|
80
|
+
expect(names).not.toContain('deepRestricted');
|
|
81
|
+
});
|
|
82
|
+
it('only affects the named tab that contains the restricted field', () => {
|
|
83
|
+
const fields = [{
|
|
84
|
+
type: 'tabs',
|
|
85
|
+
tabs: [{
|
|
86
|
+
name: 'cleanTab',
|
|
87
|
+
fields: [text('cleanField')]
|
|
88
|
+
}, {
|
|
89
|
+
name: 'restrictedHostTab',
|
|
90
|
+
fields: [text('restrictedField')]
|
|
91
|
+
}]
|
|
92
|
+
}];
|
|
93
|
+
const permissions = {
|
|
94
|
+
cleanTab: {
|
|
95
|
+
...granted,
|
|
96
|
+
fields: {
|
|
97
|
+
cleanField: granted
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
restrictedHostTab: {
|
|
101
|
+
...granted,
|
|
102
|
+
fields: {
|
|
103
|
+
restrictedField: readOnly
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const options = optionsFor(fields, permissions);
|
|
108
|
+
const names = namesOf(options);
|
|
109
|
+
expect(names).toContain('cleanField');
|
|
110
|
+
expect(names).not.toContain('restrictedField');
|
|
111
|
+
});
|
|
112
|
+
it('includes every named-tab field when permissions are fully granted', () => {
|
|
113
|
+
const fields = [{
|
|
114
|
+
type: 'tabs',
|
|
115
|
+
tabs: [{
|
|
116
|
+
name: 'namedTab',
|
|
117
|
+
fields: [text('a'), text('b')]
|
|
118
|
+
}]
|
|
119
|
+
}];
|
|
120
|
+
const options = optionsFor(fields, true);
|
|
121
|
+
const names = namesOf(options);
|
|
122
|
+
expect(names).toEqual(expect.arrayContaining(['a', 'b']));
|
|
123
|
+
});
|
|
124
|
+
it('applies parent permissions to an unnamed tab and excludes its restricted fields', () => {
|
|
125
|
+
const fields = [{
|
|
126
|
+
type: 'tabs',
|
|
127
|
+
tabs: [{
|
|
128
|
+
label: 'Unnamed',
|
|
129
|
+
fields: [text('okAtRoot'), text('restrictedAtRoot')]
|
|
130
|
+
}]
|
|
131
|
+
}];
|
|
132
|
+
// Unnamed tabs share the parent's permission map, so subfields resolve at the parent level.
|
|
133
|
+
const permissions = {
|
|
134
|
+
okAtRoot: granted,
|
|
135
|
+
restrictedAtRoot: readOnly
|
|
136
|
+
};
|
|
137
|
+
const options = optionsFor(fields, permissions);
|
|
138
|
+
const names = namesOf(options);
|
|
139
|
+
expect(names).toContain('okAtRoot');
|
|
140
|
+
expect(names).not.toContain('restrictedAtRoot');
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
//# sourceMappingURL=reduceFieldOptions.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reduceFieldOptions.spec.js","names":["describe","expect","it","vi","mock","combineFieldLabel","reduceFieldOptions","text","name","type","namesOf","options","map","option","value","field","undefined","optionsFor","fields","permissions","granted","create","read","update","readOnly","tabs","namedTab","okInTab","restrictedInTab","names","toContain","not","outerTab","innerTab","deepOk","deepRestricted","cleanTab","cleanField","restrictedHostTab","restrictedField","toEqual","arrayContaining","label","okAtRoot","restrictedAtRoot"],"sources":["../../../src/elements/FieldSelect/reduceFieldOptions.spec.ts"],"sourcesContent":["import type { ClientField } from 'payload'\n\nimport { describe, expect, it, vi } from 'vitest'\n\nimport type { FieldOption } from './reduceFieldOptions.js'\n\n// Mock the JSX label builder: irrelevant to permission logic and avoids needing a React runtime here.\nvi.mock('../../utilities/combineFieldLabel.js', () => ({\n combineFieldLabel: () => null,\n}))\n\nimport { reduceFieldOptions } from './reduceFieldOptions.js'\n\nconst text = (name: string): ClientField => ({ name, type: 'text' }) as ClientField\n\nconst namesOf = (options: FieldOption[]): (string | undefined)[] =>\n options.map((option) => ('name' in option.value.field ? option.value.field.name : undefined))\n\n// A denied field omits the operation key at runtime, which `SanitizedFieldPermissions` can't express — cast at the boundary.\ntype Permissions = Parameters<typeof reduceFieldOptions>[0]['permissions']\nconst optionsFor = (fields: ClientField[], permissions: unknown): FieldOption[] =>\n reduceFieldOptions({ fields, permissions: permissions as Permissions })\n\n// Granted sets the operation to `true`; denied omits the key, mirroring `populateFieldPermissions`.\nconst granted = { create: true, read: true, update: true }\nconst readOnly = { create: true, read: true }\n\ndescribe('reduceFieldOptions', () => {\n it('excludes a restricted field inside a named tab while keeping its clean siblings', () => {\n const fields: ClientField[] = [\n {\n type: 'tabs',\n tabs: [{ name: 'namedTab', fields: [text('okInTab'), text('restrictedInTab')] }],\n } as ClientField,\n ]\n\n const permissions = {\n namedTab: {\n ...granted,\n fields: {\n okInTab: granted,\n restrictedInTab: readOnly,\n },\n },\n }\n\n const options = optionsFor(fields, permissions)\n const names = namesOf(options)\n\n expect(names).toContain('okInTab')\n expect(names).not.toContain('restrictedInTab')\n })\n\n it('excludes a restricted field inside a nested named tab', () => {\n const fields: ClientField[] = [\n {\n type: 'tabs',\n tabs: [\n {\n name: 'outerTab',\n fields: [\n {\n type: 'tabs',\n tabs: [{ name: 'innerTab', fields: [text('deepOk'), text('deepRestricted')] }],\n },\n ],\n },\n ],\n } as ClientField,\n ]\n\n const permissions = {\n outerTab: {\n ...granted,\n fields: {\n innerTab: {\n ...granted,\n fields: {\n deepOk: granted,\n deepRestricted: readOnly,\n },\n },\n },\n },\n }\n\n const options = optionsFor(fields, permissions)\n const names = namesOf(options)\n\n expect(names).toContain('deepOk')\n expect(names).not.toContain('deepRestricted')\n })\n\n it('only affects the named tab that contains the restricted field', () => {\n const fields: ClientField[] = [\n {\n type: 'tabs',\n tabs: [\n { name: 'cleanTab', fields: [text('cleanField')] },\n { name: 'restrictedHostTab', fields: [text('restrictedField')] },\n ],\n } as ClientField,\n ]\n\n const permissions = {\n cleanTab: { ...granted, fields: { cleanField: granted } },\n restrictedHostTab: { ...granted, fields: { restrictedField: readOnly } },\n }\n\n const options = optionsFor(fields, permissions)\n const names = namesOf(options)\n\n expect(names).toContain('cleanField')\n expect(names).not.toContain('restrictedField')\n })\n\n it('includes every named-tab field when permissions are fully granted', () => {\n const fields: ClientField[] = [\n {\n type: 'tabs',\n tabs: [{ name: 'namedTab', fields: [text('a'), text('b')] }],\n } as ClientField,\n ]\n\n const options = optionsFor(fields, true)\n const names = namesOf(options)\n\n expect(names).toEqual(expect.arrayContaining(['a', 'b']))\n })\n\n it('applies parent permissions to an unnamed tab and excludes its restricted fields', () => {\n const fields: ClientField[] = [\n {\n type: 'tabs',\n tabs: [{ label: 'Unnamed', fields: [text('okAtRoot'), text('restrictedAtRoot')] }],\n } as ClientField,\n ]\n\n // Unnamed tabs share the parent's permission map, so subfields resolve at the parent level.\n const permissions = {\n okAtRoot: granted,\n restrictedAtRoot: readOnly,\n }\n\n const options = optionsFor(fields, permissions)\n const names = namesOf(options)\n\n expect(names).toContain('okAtRoot')\n expect(names).not.toContain('restrictedAtRoot')\n })\n})\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/elements/FileManager/FileToolbar/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAiB,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/elements/FileManager/FileToolbar/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAiB,MAAM,OAAO,CAAA;AAcrC,OAAO,aAAa,CAAA;AAIpB,KAAK,KAAK,GAAG;IACX,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,GAAG,MAAM,CAAA;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,GAAG,MAAM,CAAA;IAChC,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAA;IACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAA;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACjC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CAChC,CAAA;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAwFvC,CAAA"}
|
|
@@ -6,6 +6,7 @@ import { useElementHeightVariable } from '../../../hooks/useElementHeightVariabl
|
|
|
6
6
|
import { ChevronIcon } from '../../../icons/Chevron/index.js';
|
|
7
7
|
import { CropIcon } from '../../../icons/Crop/index.js';
|
|
8
8
|
import { DownloadIcon } from '../../../icons/Download/index.js';
|
|
9
|
+
import { LinkIcon } from '../../../icons/Link/index.js';
|
|
9
10
|
import { NewTabIcon } from '../../../icons/NewTab/index.js';
|
|
10
11
|
import { SwapIcon } from '../../../icons/Swap/index.js';
|
|
11
12
|
import { useTranslation } from '../../../providers/Translation/index.js';
|
|
@@ -69,6 +70,8 @@ export const FileToolbar = ({
|
|
|
69
70
|
}), /*#__PURE__*/_jsxs("div", {
|
|
70
71
|
className: `${baseClass}__right`,
|
|
71
72
|
children: [fileUrl && /*#__PURE__*/_jsx(CopyToClipboard, {
|
|
73
|
+
defaultMessage: t('upload:copyLinkToFile'),
|
|
74
|
+
icon: /*#__PURE__*/_jsx(LinkIcon, {}),
|
|
72
75
|
value: fileUrl
|
|
73
76
|
}), isAdjustable && /*#__PURE__*/_jsx(Button, {
|
|
74
77
|
"aria-label": t('upload:editImage'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["React","useRef","useElementHeightVariable","ChevronIcon","CropIcon","DownloadIcon","NewTabIcon","SwapIcon","useTranslation","Button","CopyToClipboard","Popup","PopupList","baseClass","FileToolbar","filename","fileSrc","fileUrl","hideRemoveFile","isAdjustable","onEditImage","onReplace","t","ref","cssVar","_jsxs","className","_jsx","button","size","buttonType","horizontalAlign","portalClassName","verticalAlign","MenuItem","icon","onClick","value","buttonStyle","margin","round","tooltip","href","rel","target","title","download"],"sources":["../../../../src/elements/FileManager/FileToolbar/index.tsx"],"sourcesContent":["'use client'\nimport React, { useRef } from 'react'\n\nimport { useElementHeightVariable } from '../../../hooks/useElementHeightVariable.js'\nimport { ChevronIcon } from '../../../icons/Chevron/index.js'\nimport { CropIcon } from '../../../icons/Crop/index.js'\nimport { DownloadIcon } from '../../../icons/Download/index.js'\nimport { NewTabIcon } from '../../../icons/NewTab/index.js'\nimport { SwapIcon } from '../../../icons/Swap/index.js'\nimport { useTranslation } from '../../../providers/Translation/index.js'\nimport { Button } from '../../Button/index.js'\nimport { CopyToClipboard } from '../../CopyToClipboard/index.js'\nimport { Popup } from '../../Popup/index.js'\nimport * as PopupList from '../../Popup/PopupButtonList/index.js'\nimport './index.css'\n\nconst baseClass = 'file-toolbar'\n\ntype Props = {\n readonly filename: string\n readonly fileSrc?: null | string\n readonly fileUrl?: null | string\n readonly hideRemoveFile?: boolean\n readonly isAdjustable?: boolean\n readonly onEditImage?: () => void\n readonly onReplace?: () => void\n}\n\nexport const FileToolbar: React.FC<Props> = ({\n filename,\n fileSrc,\n fileUrl,\n hideRemoveFile,\n isAdjustable,\n onEditImage,\n onReplace,\n}) => {\n const { t } = useTranslation()\n const ref = useRef<HTMLDivElement>(null)\n useElementHeightVariable({ cssVar: '--file-toolbar-height', ref })\n\n return (\n <div className={baseClass} ref={ref}>\n <div className={`${baseClass}__left`}>\n {hideRemoveFile ? (\n <span className={`${baseClass}__filename-btn ${baseClass}__filename-btn--static`}>\n <span className={`${baseClass}__filename-text`}>{filename}</span>\n </span>\n ) : (\n <Popup\n button={\n <span className={`${baseClass}__filename-btn`}>\n <span className={`${baseClass}__filename-text`}>{filename}</span>\n <ChevronIcon size={16} />\n </span>\n }\n buttonType=\"custom\"\n horizontalAlign=\"left\"\n portalClassName={`${baseClass}__filename-popup`}\n size=\"fit-content\"\n verticalAlign=\"bottom\"\n >\n <PopupList.MenuItem>\n <PopupList.Button icon={<SwapIcon />} onClick={onReplace}>\n {t('upload:replaceFile')}\n </PopupList.Button>\n </PopupList.MenuItem>\n </Popup>\n )}\n </div>\n\n <div className={`${baseClass}__right`}>\n {fileUrl && <CopyToClipboard value={fileUrl}
|
|
1
|
+
{"version":3,"file":"index.js","names":["React","useRef","useElementHeightVariable","ChevronIcon","CropIcon","DownloadIcon","LinkIcon","NewTabIcon","SwapIcon","useTranslation","Button","CopyToClipboard","Popup","PopupList","baseClass","FileToolbar","filename","fileSrc","fileUrl","hideRemoveFile","isAdjustable","onEditImage","onReplace","t","ref","cssVar","_jsxs","className","_jsx","button","size","buttonType","horizontalAlign","portalClassName","verticalAlign","MenuItem","icon","onClick","defaultMessage","value","buttonStyle","margin","round","tooltip","href","rel","target","title","download"],"sources":["../../../../src/elements/FileManager/FileToolbar/index.tsx"],"sourcesContent":["'use client'\nimport React, { useRef } from 'react'\n\nimport { useElementHeightVariable } from '../../../hooks/useElementHeightVariable.js'\nimport { ChevronIcon } from '../../../icons/Chevron/index.js'\nimport { CropIcon } from '../../../icons/Crop/index.js'\nimport { DownloadIcon } from '../../../icons/Download/index.js'\nimport { LinkIcon } from '../../../icons/Link/index.js'\nimport { NewTabIcon } from '../../../icons/NewTab/index.js'\nimport { SwapIcon } from '../../../icons/Swap/index.js'\nimport { useTranslation } from '../../../providers/Translation/index.js'\nimport { Button } from '../../Button/index.js'\nimport { CopyToClipboard } from '../../CopyToClipboard/index.js'\nimport { Popup } from '../../Popup/index.js'\nimport * as PopupList from '../../Popup/PopupButtonList/index.js'\nimport './index.css'\n\nconst baseClass = 'file-toolbar'\n\ntype Props = {\n readonly filename: string\n readonly fileSrc?: null | string\n readonly fileUrl?: null | string\n readonly hideRemoveFile?: boolean\n readonly isAdjustable?: boolean\n readonly onEditImage?: () => void\n readonly onReplace?: () => void\n}\n\nexport const FileToolbar: React.FC<Props> = ({\n filename,\n fileSrc,\n fileUrl,\n hideRemoveFile,\n isAdjustable,\n onEditImage,\n onReplace,\n}) => {\n const { t } = useTranslation()\n const ref = useRef<HTMLDivElement>(null)\n useElementHeightVariable({ cssVar: '--file-toolbar-height', ref })\n\n return (\n <div className={baseClass} ref={ref}>\n <div className={`${baseClass}__left`}>\n {hideRemoveFile ? (\n <span className={`${baseClass}__filename-btn ${baseClass}__filename-btn--static`}>\n <span className={`${baseClass}__filename-text`}>{filename}</span>\n </span>\n ) : (\n <Popup\n button={\n <span className={`${baseClass}__filename-btn`}>\n <span className={`${baseClass}__filename-text`}>{filename}</span>\n <ChevronIcon size={16} />\n </span>\n }\n buttonType=\"custom\"\n horizontalAlign=\"left\"\n portalClassName={`${baseClass}__filename-popup`}\n size=\"fit-content\"\n verticalAlign=\"bottom\"\n >\n <PopupList.MenuItem>\n <PopupList.Button icon={<SwapIcon />} onClick={onReplace}>\n {t('upload:replaceFile')}\n </PopupList.Button>\n </PopupList.MenuItem>\n </Popup>\n )}\n </div>\n\n <div className={`${baseClass}__right`}>\n {fileUrl && (\n <CopyToClipboard\n defaultMessage={t('upload:copyLinkToFile')}\n icon={<LinkIcon />}\n value={fileUrl}\n />\n )}\n {isAdjustable && (\n <Button\n aria-label={t('upload:editImage')}\n buttonStyle=\"ghost\"\n icon={<CropIcon size={24} />}\n margin={false}\n onClick={onEditImage}\n round={false}\n tooltip={t('upload:editImage')}\n />\n )}\n {fileSrc && (\n <a\n aria-label={t('fields:openInNewTab')}\n className={`${baseClass}__icon-link`}\n href={fileSrc}\n rel=\"noopener noreferrer\"\n target=\"_blank\"\n title={t('fields:openInNewTab')}\n >\n <NewTabIcon size={24} />\n </a>\n )}\n {fileSrc && (\n <a\n aria-label={t('upload:download')}\n className={`${baseClass}__icon-link`}\n download={filename}\n href={fileSrc}\n title={t('upload:download')}\n >\n <DownloadIcon size={24} />\n </a>\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"AAAA;;;AACA,OAAOA,KAAA,IAASC,MAAM,QAAQ;AAE9B,SAASC,wBAAwB,QAAQ;AACzC,SAASC,WAAW,QAAQ;AAC5B,SAASC,QAAQ,QAAQ;AACzB,SAASC,YAAY,QAAQ;AAC7B,SAASC,QAAQ,QAAQ;AACzB,SAASC,UAAU,QAAQ;AAC3B,SAASC,QAAQ,QAAQ;AACzB,SAASC,cAAc,QAAQ;AAC/B,SAASC,MAAM,QAAQ;AACvB,SAASC,eAAe,QAAQ;AAChC,SAASC,KAAK,QAAQ;AACtB,YAAYC,SAAA,MAAe;AAC3B,OAAO;AAEP,MAAMC,SAAA,GAAY;AAYlB,OAAO,MAAMC,WAAA,GAA+BA,CAAC;EAC3CC,QAAQ;EACRC,OAAO;EACPC,OAAO;EACPC,cAAc;EACdC,YAAY;EACZC,WAAW;EACXC;AAAS,CACV;EACC,MAAM;IAAEC;EAAC,CAAE,GAAGd,cAAA;EACd,MAAMe,GAAA,GAAMvB,MAAA,CAAuB;EACnCC,wBAAA,CAAyB;IAAEuB,MAAA,EAAQ;IAAyBD;EAAI;EAEhE,oBACEE,KAAA,CAAC;IAAIC,SAAA,EAAWb,SAAA;IAAWU,GAAA,EAAKA,GAAA;4BAC9BI,IAAA,CAAC;MAAID,SAAA,EAAW,GAAGb,SAAA,QAAiB;gBACjCK,cAAA,gBACCS,IAAA,CAAC;QAAKD,SAAA,EAAW,GAAGb,SAAA,kBAA2BA,SAAA,wBAAiC;kBAC9E,aAAAc,IAAA,CAAC;UAAKD,SAAA,EAAW,GAAGb,SAAA,iBAA0B;oBAAGE;;wBAGnDY,IAAA,CAAChB,KAAA;QACCiB,MAAA,eACEH,KAAA,CAAC;UAAKC,SAAA,EAAW,GAAGb,SAAA,gBAAyB;kCAC3Cc,IAAA,CAAC;YAAKD,SAAA,EAAW,GAAGb,SAAA,iBAA0B;sBAAGE;2BACjDY,IAAA,CAACzB,WAAA;YAAY2B,IAAA,EAAM;;;QAGvBC,UAAA,EAAW;QACXC,eAAA,EAAgB;QAChBC,eAAA,EAAiB,GAAGnB,SAAA,kBAA2B;QAC/CgB,IAAA,EAAK;QACLI,aAAA,EAAc;kBAEd,aAAAN,IAAA,CAACf,SAAA,CAAUsB,QAAQ;oBACjB,aAAAP,IAAA,CAACf,SAAA,CAAUH,MAAM;YAAC0B,IAAA,eAAMR,IAAA,CAACpB,QAAA;YAAa6B,OAAA,EAASf,SAAA;sBAC5CC,CAAA,CAAE;;;;qBAObG,KAAA,CAAC;MAAIC,SAAA,EAAW,GAAGb,SAAA,SAAkB;iBAClCI,OAAA,iBACCU,IAAA,CAACjB,eAAA;QACC2B,cAAA,EAAgBf,CAAA,CAAE;QAClBa,IAAA,eAAMR,IAAA,CAACtB,QAAA;QACPiC,KAAA,EAAOrB;UAGVE,YAAA,iBACCQ,IAAA,CAAClB,MAAA;QACC,cAAYa,CAAA,CAAE;QACdiB,WAAA,EAAY;QACZJ,IAAA,eAAMR,IAAA,CAACxB,QAAA;UAAS0B,IAAA,EAAM;;QACtBW,MAAA,EAAQ;QACRJ,OAAA,EAAShB,WAAA;QACTqB,KAAA,EAAO;QACPC,OAAA,EAASpB,CAAA,CAAE;UAGdN,OAAA,iBACCW,IAAA,CAAC;QACC,cAAYL,CAAA,CAAE;QACdI,SAAA,EAAW,GAAGb,SAAA,aAAsB;QACpC8B,IAAA,EAAM3B,OAAA;QACN4B,GAAA,EAAI;QACJC,MAAA,EAAO;QACPC,KAAA,EAAOxB,CAAA,CAAE;kBAET,aAAAK,IAAA,CAACrB,UAAA;UAAWuB,IAAA,EAAM;;UAGrBb,OAAA,iBACCW,IAAA,CAAC;QACC,cAAYL,CAAA,CAAE;QACdI,SAAA,EAAW,GAAGb,SAAA,aAAsB;QACpCkC,QAAA,EAAUhC,QAAA;QACV4B,IAAA,EAAM3B,OAAA;QACN8B,KAAA,EAAOxB,CAAA,CAAE;kBAET,aAAAK,IAAA,CAACvB,YAAA;UAAayB,IAAA,EAAM;;;;;AAMhC","ignoreList":[]}
|
|
@@ -6,8 +6,10 @@ export type Props = {
|
|
|
6
6
|
readonly columns: Column[];
|
|
7
7
|
readonly dragAttributes?: HTMLAttributes<unknown>;
|
|
8
8
|
readonly dragListeners?: DraggableSyntheticListeners;
|
|
9
|
+
readonly isOrderable?: boolean;
|
|
10
|
+
readonly onDisabledDragAttempt?: () => void;
|
|
9
11
|
readonly ref?: Ref<HTMLTableRowElement>;
|
|
10
12
|
readonly rowId: number | string;
|
|
11
13
|
} & HTMLAttributes<HTMLTableRowElement>;
|
|
12
|
-
export declare const OrderableRow: ({ cellMap, columns, dragAttributes, dragListeners, rowId, ...rest }: Props) => import("react").JSX.Element;
|
|
14
|
+
export declare const OrderableRow: ({ cellMap, columns, dragAttributes, dragListeners, isOrderable, onDisabledDragAttempt, rowId, ...rest }: Props) => import("react").JSX.Element;
|
|
13
15
|
//# sourceMappingURL=OrderableRow.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrderableRow.d.ts","sourceRoot":"","sources":["../../../src/elements/Table/OrderableRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAA;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"OrderableRow.d.ts","sourceRoot":"","sources":["../../../src/elements/Table/OrderableRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAA;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAiB,GAAG,EAAE,MAAM,OAAO,CAAA;AAE/D,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAA;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAA;IACjD,QAAQ,CAAC,aAAa,CAAC,EAAE,2BAA2B,CAAA;IACpD,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;IAC9B,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC3C,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAA;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;CAChC,GAAG,cAAc,CAAC,mBAAmB,CAAC,CAAA;AAEvC,eAAO,MAAM,YAAY,4GAStB,KAAK,gCA+CP,CAAA"}
|
|
@@ -4,6 +4,8 @@ export const OrderableRow = ({
|
|
|
4
4
|
columns,
|
|
5
5
|
dragAttributes = {},
|
|
6
6
|
dragListeners = {},
|
|
7
|
+
isOrderable = true,
|
|
8
|
+
onDisabledDragAttempt,
|
|
7
9
|
rowId,
|
|
8
10
|
...rest
|
|
9
11
|
}) => /*#__PURE__*/_jsx("tr", {
|
|
@@ -16,11 +18,25 @@ export const OrderableRow = ({
|
|
|
16
18
|
const cell = col.renderedCells[cellMap[rowId]];
|
|
17
19
|
// For drag handles, wrap in div with drag attributes
|
|
18
20
|
if (accessor === '_dragHandle') {
|
|
21
|
+
// When not orderable, dnd-kit's own drag listeners are disabled, so pressing the keys
|
|
22
|
+
// it would normally use to start a drag does nothing silently. Intercept them here to
|
|
23
|
+
// tell the user why instead.
|
|
24
|
+
const dragHandleListeners = {
|
|
25
|
+
...dragListeners,
|
|
26
|
+
onKeyDown: event => {
|
|
27
|
+
if (!isOrderable && (event.code === 'Space' || event.code === 'Enter')) {
|
|
28
|
+
event.preventDefault();
|
|
29
|
+
onDisabledDragAttempt?.();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
dragListeners?.onKeyDown?.(event);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
19
35
|
return /*#__PURE__*/_jsx("td", {
|
|
20
36
|
className: `cell-${accessor}`,
|
|
21
37
|
children: /*#__PURE__*/_jsx("div", {
|
|
22
38
|
...dragAttributes,
|
|
23
|
-
...
|
|
39
|
+
...dragHandleListeners,
|
|
24
40
|
children: cell
|
|
25
41
|
})
|
|
26
42
|
}, colIndex);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrderableRow.js","names":["OrderableRow","cellMap","columns","dragAttributes","dragListeners","rowId","rest","_jsx","map","col","colIndex","accessor","cell","renderedCells","className","isLinkedColumn","filter","Boolean","join"],"sources":["../../../src/elements/Table/OrderableRow.tsx"],"sourcesContent":["import type { DraggableSyntheticListeners } from '@dnd-kit/core'\nimport type { Column } from 'payload'\nimport type { HTMLAttributes, Ref } from 'react'\n\nexport type Props = {\n readonly cellMap: Record<string, number>\n readonly columns: Column[]\n readonly dragAttributes?: HTMLAttributes<unknown>\n readonly dragListeners?: DraggableSyntheticListeners\n readonly ref?: Ref<HTMLTableRowElement>\n readonly rowId: number | string\n} & HTMLAttributes<HTMLTableRowElement>\n\nexport const OrderableRow = ({\n cellMap,\n columns,\n dragAttributes = {},\n dragListeners = {},\n rowId,\n ...rest\n}: Props) => (\n <tr {...rest}>\n {columns.map((col, colIndex) => {\n const { accessor } = col\n\n // Use the cellMap to find which index in the renderedCells to use\n const cell = col.renderedCells[cellMap[rowId]]\n\n // For drag handles, wrap in div with drag attributes\n if (accessor === '_dragHandle') {\n return (\n <td className={`cell-${accessor}`} key={colIndex}>\n <div {...dragAttributes} {...
|
|
1
|
+
{"version":3,"file":"OrderableRow.js","names":["OrderableRow","cellMap","columns","dragAttributes","dragListeners","isOrderable","onDisabledDragAttempt","rowId","rest","_jsx","map","col","colIndex","accessor","cell","renderedCells","dragHandleListeners","onKeyDown","event","code","preventDefault","className","isLinkedColumn","filter","Boolean","join"],"sources":["../../../src/elements/Table/OrderableRow.tsx"],"sourcesContent":["import type { DraggableSyntheticListeners } from '@dnd-kit/core'\nimport type { Column } from 'payload'\nimport type { HTMLAttributes, KeyboardEvent, Ref } from 'react'\n\nexport type Props = {\n readonly cellMap: Record<string, number>\n readonly columns: Column[]\n readonly dragAttributes?: HTMLAttributes<unknown>\n readonly dragListeners?: DraggableSyntheticListeners\n readonly isOrderable?: boolean\n readonly onDisabledDragAttempt?: () => void\n readonly ref?: Ref<HTMLTableRowElement>\n readonly rowId: number | string\n} & HTMLAttributes<HTMLTableRowElement>\n\nexport const OrderableRow = ({\n cellMap,\n columns,\n dragAttributes = {},\n dragListeners = {},\n isOrderable = true,\n onDisabledDragAttempt,\n rowId,\n ...rest\n}: Props) => (\n <tr {...rest}>\n {columns.map((col, colIndex) => {\n const { accessor } = col\n\n // Use the cellMap to find which index in the renderedCells to use\n const cell = col.renderedCells[cellMap[rowId]]\n\n // For drag handles, wrap in div with drag attributes\n if (accessor === '_dragHandle') {\n // When not orderable, dnd-kit's own drag listeners are disabled, so pressing the keys\n // it would normally use to start a drag does nothing silently. Intercept them here to\n // tell the user why instead.\n const dragHandleListeners = {\n ...dragListeners,\n onKeyDown: (event: KeyboardEvent) => {\n if (!isOrderable && (event.code === 'Space' || event.code === 'Enter')) {\n event.preventDefault()\n onDisabledDragAttempt?.()\n return\n }\n\n dragListeners?.onKeyDown?.(event)\n },\n }\n\n return (\n <td className={`cell-${accessor}`} key={colIndex}>\n <div {...dragAttributes} {...dragHandleListeners}>\n {cell}\n </div>\n </td>\n )\n }\n\n return (\n <td\n className={[`cell-${accessor}`, col.isLinkedColumn && 'cell--linked']\n .filter(Boolean)\n .join(' ')}\n key={colIndex}\n >\n {cell}\n </td>\n )\n })}\n </tr>\n)\n"],"mappings":";AAeA,OAAO,MAAMA,YAAA,GAAeA,CAAC;EAC3BC,OAAO;EACPC,OAAO;EACPC,cAAA,GAAiB,CAAC,CAAC;EACnBC,aAAA,GAAgB,CAAC,CAAC;EAClBC,WAAA,GAAc,IAAI;EAClBC,qBAAqB;EACrBC,KAAK;EACL,GAAGC;AAAA,CACG,kBACNC,IAAA,CAAC;EAAI,GAAGD,IAAI;YACTN,OAAA,CAAQQ,GAAG,CAAC,CAACC,GAAA,EAAKC,QAAA;IACjB,MAAM;MAAEC;IAAQ,CAAE,GAAGF,GAAA;IAErB;IACA,MAAMG,IAAA,GAAOH,GAAA,CAAII,aAAa,CAACd,OAAO,CAACM,KAAA,CAAM,CAAC;IAE9C;IACA,IAAIM,QAAA,KAAa,eAAe;MAC9B;MACA;MACA;MACA,MAAMG,mBAAA,GAAsB;QAC1B,GAAGZ,aAAa;QAChBa,SAAA,EAAYC,KAAA;UACV,IAAI,CAACb,WAAA,KAAgBa,KAAA,CAAMC,IAAI,KAAK,WAAWD,KAAA,CAAMC,IAAI,KAAK,OAAM,GAAI;YACtED,KAAA,CAAME,cAAc;YACpBd,qBAAA;YACA;UACF;UAEAF,aAAA,EAAea,SAAA,GAAYC,KAAA;QAC7B;MACF;MAEA,oBACET,IAAA,CAAC;QAAGY,SAAA,EAAW,QAAQR,QAAA,EAAU;kBAC/B,aAAAJ,IAAA,CAAC;UAAK,GAAGN,cAAc;UAAG,GAAGa,mBAAmB;oBAC7CF;;SAFmCF,QAAA;IAM5C;IAEA,oBACEH,IAAA,CAAC;MACCY,SAAA,EAAW,CAAC,QAAQR,QAAA,EAAU,EAAEF,GAAA,CAAIW,cAAc,IAAI,eAAe,CAClEC,MAAM,CAACC,OAAA,EACPC,IAAI,CAAC;gBAGPX;OAFIF,QAAA;EAKX","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrderableTable.d.ts","sourceRoot":"","sources":["../../../src/elements/Table/OrderableTable.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,EAAyB,MAAM,SAAS,CAAA;AAEpF,OAAO,aAAa,CAAA;AAIpB,OAAO,KAA8B,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"OrderableTable.d.ts","sourceRoot":"","sources":["../../../src/elements/Table/OrderableTable.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,EAAyB,MAAM,SAAS,CAAA;AAEpF,OAAO,aAAa,CAAA;AAIpB,OAAO,KAA8B,MAAM,OAAO,CAAA;AAiBlD,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,CAAC,UAAU,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;IAC7C,QAAQ,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtC,QAAQ,CAAC,UAAU,EAAE,sBAAsB,CAAA;IAC3C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IACxC,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CACnC,CAAA;AAED,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAgN1C,CAAA"}
|
|
@@ -9,11 +9,14 @@ import { toast } from 'sonner';
|
|
|
9
9
|
import { useConfig } from '../../providers/Config/index.js';
|
|
10
10
|
import { useListQuery } from '../../providers/ListQuery/index.js';
|
|
11
11
|
import { useLocale } from '../../providers/Locale/index.js';
|
|
12
|
+
import { useTranslation } from '../../providers/Translation/index.js';
|
|
12
13
|
import { DraggableSortableItem } from '../DraggableSortable/DraggableSortableItem/index.js';
|
|
13
14
|
import { DraggableSortable } from '../DraggableSortable/index.js';
|
|
14
15
|
import { OrderableRow } from './OrderableRow.js';
|
|
15
16
|
import { OrderableRowDragPreview } from './OrderableRowDragPreview.js';
|
|
16
17
|
const baseClass = 'table';
|
|
18
|
+
// A stable id so repeated attempts (e.g. holding Space) update the same toast instead of stacking new ones
|
|
19
|
+
const mustSortByOrderToastId = 'orderable-table-must-sort-by-order';
|
|
17
20
|
export const OrderableTable = ({
|
|
18
21
|
appearance = 'default',
|
|
19
22
|
BeforeTable,
|
|
@@ -32,8 +35,12 @@ export const OrderableTable = ({
|
|
|
32
35
|
const {
|
|
33
36
|
code: localeCode
|
|
34
37
|
} = useLocale();
|
|
38
|
+
const {
|
|
39
|
+
t
|
|
40
|
+
} = useTranslation();
|
|
35
41
|
// Use the data from ListQueryProvider if available, otherwise use the props
|
|
36
42
|
const serverData = listQueryData?.docs || initialData;
|
|
43
|
+
const isOrderable = query.sort === orderableFieldName || query.sort === `-${orderableFieldName}`;
|
|
37
44
|
// Local state to track the current order of rows
|
|
38
45
|
const [localData, setLocalData] = useState(serverData);
|
|
39
46
|
// id -> index for each column
|
|
@@ -54,8 +61,10 @@ export const OrderableTable = ({
|
|
|
54
61
|
moveFromIndex,
|
|
55
62
|
moveToIndex
|
|
56
63
|
}) => {
|
|
57
|
-
if (
|
|
58
|
-
toast.warning('
|
|
64
|
+
if (!isOrderable) {
|
|
65
|
+
toast.warning(t('general:sortByOrderToReorder'), {
|
|
66
|
+
id: mustSortByOrderToastId
|
|
67
|
+
});
|
|
59
68
|
setDragActiveRowId(undefined);
|
|
60
69
|
return;
|
|
61
70
|
}
|
|
@@ -127,6 +136,11 @@ export const OrderableTable = ({
|
|
|
127
136
|
}) => {
|
|
128
137
|
setDragActiveRowId(id);
|
|
129
138
|
};
|
|
139
|
+
const handleDisabledDragAttempt = () => {
|
|
140
|
+
toast.warning(t('general:sortByOrderToReorder'), {
|
|
141
|
+
id: mustSortByOrderToastId
|
|
142
|
+
});
|
|
143
|
+
};
|
|
130
144
|
const rowIds = localData.map(row => row.id ?? row._id);
|
|
131
145
|
return /*#__PURE__*/_jsxs("div", {
|
|
132
146
|
className: [baseClass, appearance && `${baseClass}--appearance-${appearance}`].filter(Boolean).join(' '),
|
|
@@ -146,6 +160,7 @@ export const OrderableTable = ({
|
|
|
146
160
|
})
|
|
147
161
|
}), /*#__PURE__*/_jsx("tbody", {
|
|
148
162
|
children: localData.map((row_0, rowIndex) => /*#__PURE__*/_jsx(DraggableSortableItem, {
|
|
163
|
+
disabled: !isOrderable,
|
|
149
164
|
id: rowIds[rowIndex],
|
|
150
165
|
children: ({
|
|
151
166
|
attributes,
|
|
@@ -160,6 +175,8 @@ export const OrderableTable = ({
|
|
|
160
175
|
columns: activeColumns,
|
|
161
176
|
dragAttributes: attributes,
|
|
162
177
|
dragListeners: listeners,
|
|
178
|
+
isOrderable: isOrderable,
|
|
179
|
+
onDisabledDragAttempt: handleDisabledDragAttempt,
|
|
163
180
|
ref: setNodeRef,
|
|
164
181
|
rowId: row_0.id ?? row_0._id,
|
|
165
182
|
style: {
|