@webiny/app-headless-cms-common 6.0.0-rc.1 → 6.0.0-rc.3
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/Fields/FieldRulesProvider.d.ts +9 -0
- package/Fields/FieldRulesProvider.js +20 -0
- package/Fields/FieldRulesProvider.js.map +1 -0
- package/Fields/Fields.js +127 -14
- package/Fields/Fields.js.map +1 -1
- package/Fields/LayoutDescriptorCell.d.ts +12 -0
- package/Fields/LayoutDescriptorCell.js +47 -0
- package/Fields/LayoutDescriptorCell.js.map +1 -0
- package/Fields/evaluateExpression.d.ts +23 -0
- package/Fields/evaluateExpression.js +102 -0
- package/Fields/evaluateExpression.js.map +1 -0
- package/Fields/fieldOptions.d.ts +36 -0
- package/Fields/fieldOptions.js +113 -0
- package/Fields/fieldOptions.js.map +1 -0
- package/Fields/index.d.ts +5 -0
- package/Fields/index.js +5 -0
- package/Fields/index.js.map +1 -1
- package/Fields/layoutFieldRenderers/AlertFieldRenderer.d.ts +7 -0
- package/Fields/layoutFieldRenderers/AlertFieldRenderer.js +13 -0
- package/Fields/layoutFieldRenderers/AlertFieldRenderer.js.map +1 -0
- package/Fields/layoutFieldRenderers/SeparatorFieldRenderer.d.ts +7 -0
- package/Fields/layoutFieldRenderers/SeparatorFieldRenderer.js +18 -0
- package/Fields/layoutFieldRenderers/SeparatorFieldRenderer.js.map +1 -0
- package/Fields/layoutFieldRenderers/TabsFieldRenderer.d.ts +12 -0
- package/Fields/layoutFieldRenderers/TabsFieldRenderer.js +72 -0
- package/Fields/layoutFieldRenderers/TabsFieldRenderer.js.map +1 -0
- package/Fields/operatorOptions.d.ts +10 -0
- package/Fields/operatorOptions.js +92 -0
- package/Fields/operatorOptions.js.map +1 -0
- package/Fields/useBind.d.ts +1 -0
- package/Fields/useBind.js +7 -3
- package/Fields/useBind.js.map +1 -1
- package/Fields/useFieldRules.d.ts +32 -0
- package/Fields/useFieldRules.js +153 -0
- package/Fields/useFieldRules.js.map +1 -0
- package/ModelFieldProvider/CanEditField.d.ts +5 -0
- package/ModelFieldProvider/CanEditField.js +13 -0
- package/ModelFieldProvider/CanEditField.js.map +1 -0
- package/ModelFieldProvider/ModelFieldContext.d.ts +1 -0
- package/ModelFieldProvider/index.d.ts +1 -0
- package/ModelFieldProvider/index.js +1 -0
- package/ModelFieldProvider/index.js.map +1 -1
- package/ModelFieldProvider/useModelField.js +1 -1
- package/ModelFieldProvider/useModelField.js.map +1 -1
- package/entries.graphql.js +3 -0
- package/entries.graphql.js.map +1 -1
- package/exports/admin/cms/model.d.ts +2 -0
- package/exports/admin/cms/model.js +3 -0
- package/exports/admin/cms/model.js.map +1 -0
- package/exports/admin/cms.d.ts +1 -1
- package/exports/admin/cms.js.map +1 -1
- package/normalizeIcon.d.ts +3 -0
- package/normalizeIcon.js +10 -0
- package/normalizeIcon.js.map +1 -0
- package/package.json +12 -11
- package/types/index.d.ts +66 -3
- package/types/index.js +51 -1
- package/types/index.js.map +1 -1
- package/types/model.d.ts +53 -2
- package/types/model.js +27 -1
- package/types/model.js.map +1 -1
package/Fields/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["index.ts"],"sourcesContent":["export * from \"./FieldElement.js\";\nexport * from \"./Fields.js\";\nexport * from \"./useBind.js\";\n"],"mappings":"AAAA;AACA;AACA","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":[],"sources":["index.ts"],"sourcesContent":["export * from \"./FieldElement.js\";\nexport * from \"./Fields.js\";\nexport * from \"./useBind.js\";\nexport * from \"./FieldRulesProvider.js\";\nexport * from \"./evaluateExpression.js\";\nexport * from \"./useFieldRules.js\";\nexport * from \"./fieldOptions.js\";\nexport * from \"./operatorOptions.js\";\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","ignoreList":[]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { CmsAlertLayoutDescriptor } from "../../types/model.js";
|
|
3
|
+
interface AlertFieldRendererProps {
|
|
4
|
+
descriptor: CmsAlertLayoutDescriptor;
|
|
5
|
+
}
|
|
6
|
+
export declare const AlertFieldRenderer: ({ descriptor }: AlertFieldRendererProps) => React.JSX.Element;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Alert, Grid } from "@webiny/admin-ui";
|
|
3
|
+
export const AlertFieldRenderer = ({
|
|
4
|
+
descriptor
|
|
5
|
+
}) => {
|
|
6
|
+
return /*#__PURE__*/React.createElement(Grid.Column, {
|
|
7
|
+
span: 12
|
|
8
|
+
}, /*#__PURE__*/React.createElement(Alert, {
|
|
9
|
+
type: descriptor.alertType
|
|
10
|
+
}, descriptor.label));
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=AlertFieldRenderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["React","Alert","Grid","AlertFieldRenderer","descriptor","createElement","Column","span","type","alertType","label"],"sources":["AlertFieldRenderer.tsx"],"sourcesContent":["import React from \"react\";\nimport { Alert, Grid } from \"@webiny/admin-ui\";\nimport type { CmsAlertLayoutDescriptor } from \"~/types/model.js\";\n\ninterface AlertFieldRendererProps {\n descriptor: CmsAlertLayoutDescriptor;\n}\n\nexport const AlertFieldRenderer = ({ descriptor }: AlertFieldRendererProps) => {\n return (\n <Grid.Column span={12}>\n <Alert type={descriptor.alertType}>{descriptor.label}</Alert>\n </Grid.Column>\n );\n};\n"],"mappings":"AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,SAASC,KAAK,EAAEC,IAAI,QAAQ,kBAAkB;AAO9C,OAAO,MAAMC,kBAAkB,GAAGA,CAAC;EAAEC;AAAoC,CAAC,KAAK;EAC3E,oBACIJ,KAAA,CAAAK,aAAA,CAACH,IAAI,CAACI,MAAM;IAACC,IAAI,EAAE;EAAG,gBAClBP,KAAA,CAAAK,aAAA,CAACJ,KAAK;IAACO,IAAI,EAAEJ,UAAU,CAACK;EAAU,GAAEL,UAAU,CAACM,KAAa,CACnD,CAAC;AAEtB,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { CmsSeparatorLayoutDescriptor } from "../../types/model.js";
|
|
3
|
+
interface SeparatorFieldRendererProps {
|
|
4
|
+
descriptor: CmsSeparatorLayoutDescriptor;
|
|
5
|
+
}
|
|
6
|
+
export declare const SeparatorFieldRenderer: ({ descriptor }: SeparatorFieldRendererProps) => React.JSX.Element;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Grid, Separator, Text } from "@webiny/admin-ui";
|
|
3
|
+
export const SeparatorFieldRenderer = ({
|
|
4
|
+
descriptor
|
|
5
|
+
}) => {
|
|
6
|
+
return /*#__PURE__*/React.createElement(Grid.Column, {
|
|
7
|
+
span: 12
|
|
8
|
+
}, /*#__PURE__*/React.createElement(Separator, {
|
|
9
|
+
variant: "accent",
|
|
10
|
+
labelPosition: "start"
|
|
11
|
+
}, descriptor.label), descriptor.description && /*#__PURE__*/React.createElement(Text, {
|
|
12
|
+
as: "div",
|
|
13
|
+
size: "sm",
|
|
14
|
+
className: "text-neutral-strong mt-sm"
|
|
15
|
+
}, descriptor.description));
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=SeparatorFieldRenderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["React","Grid","Separator","Text","SeparatorFieldRenderer","descriptor","createElement","Column","span","variant","labelPosition","label","description","as","size","className"],"sources":["SeparatorFieldRenderer.tsx"],"sourcesContent":["import React from \"react\";\nimport { Grid, Separator, Text } from \"@webiny/admin-ui\";\nimport type { CmsSeparatorLayoutDescriptor } from \"~/types/model.js\";\n\ninterface SeparatorFieldRendererProps {\n descriptor: CmsSeparatorLayoutDescriptor;\n}\n\nexport const SeparatorFieldRenderer = ({ descriptor }: SeparatorFieldRendererProps) => {\n return (\n <Grid.Column span={12}>\n <Separator variant={\"accent\"} labelPosition={\"start\"}>\n {descriptor.label}\n </Separator>\n {descriptor.description && (\n <Text as={\"div\"} size={\"sm\"} className={\"text-neutral-strong mt-sm\"}>\n {descriptor.description}\n </Text>\n )}\n </Grid.Column>\n );\n};\n"],"mappings":"AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,SAASC,IAAI,EAAEC,SAAS,EAAEC,IAAI,QAAQ,kBAAkB;AAOxD,OAAO,MAAMC,sBAAsB,GAAGA,CAAC;EAAEC;AAAwC,CAAC,KAAK;EACnF,oBACIL,KAAA,CAAAM,aAAA,CAACL,IAAI,CAACM,MAAM;IAACC,IAAI,EAAE;EAAG,gBAClBR,KAAA,CAAAM,aAAA,CAACJ,SAAS;IAACO,OAAO,EAAE,QAAS;IAACC,aAAa,EAAE;EAAQ,GAChDL,UAAU,CAACM,KACL,CAAC,EACXN,UAAU,CAACO,WAAW,iBACnBZ,KAAA,CAAAM,aAAA,CAACH,IAAI;IAACU,EAAE,EAAE,KAAM;IAACC,IAAI,EAAE,IAAK;IAACC,SAAS,EAAE;EAA4B,GAC/DV,UAAU,CAACO,WACV,CAED,CAAC;AAEtB,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { CmsTabLayoutDescriptor } from "../../types/model.js";
|
|
3
|
+
import type { BindComponent, CmsEditorContentModel, CmsModelField } from "../../types/index.js";
|
|
4
|
+
interface TabsFieldRendererProps {
|
|
5
|
+
descriptor: CmsTabLayoutDescriptor;
|
|
6
|
+
Bind: BindComponent;
|
|
7
|
+
fields: CmsModelField[];
|
|
8
|
+
contentModel: CmsEditorContentModel;
|
|
9
|
+
gridClassName?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const TabsFieldRenderer: ({ descriptor, Bind, fields, contentModel, gridClassName }: TabsFieldRendererProps) => React.JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Grid, FormComponentLabel, FormComponentDescription, Tabs } from "@webiny/admin-ui";
|
|
3
|
+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
4
|
+
import { normalizeIcon } from "../../normalizeIcon.js";
|
|
5
|
+
import { Fields } from "../index.js";
|
|
6
|
+
import { FieldRulesProvider } from "../FieldRulesProvider.js";
|
|
7
|
+
import { useEffectiveRules } from "../useFieldRules.js";
|
|
8
|
+
const TabPanel = ({
|
|
9
|
+
tab,
|
|
10
|
+
Bind,
|
|
11
|
+
fields,
|
|
12
|
+
contentModel,
|
|
13
|
+
gridClassName
|
|
14
|
+
}) => {
|
|
15
|
+
const rules = useEffectiveRules(tab);
|
|
16
|
+
const icon = normalizeIcon(tab.icon);
|
|
17
|
+
return /*#__PURE__*/React.createElement(Tabs.Tab, {
|
|
18
|
+
disabled: rules.disabled,
|
|
19
|
+
visible: rules.canView && !rules.hidden,
|
|
20
|
+
value: tab.id,
|
|
21
|
+
trigger: tab.label,
|
|
22
|
+
icon: icon ? /*#__PURE__*/React.createElement(FontAwesomeIcon, {
|
|
23
|
+
icon: icon,
|
|
24
|
+
size: "sm"
|
|
25
|
+
}) : undefined,
|
|
26
|
+
content: /*#__PURE__*/React.createElement(FieldRulesProvider, {
|
|
27
|
+
rules: rules
|
|
28
|
+
}, /*#__PURE__*/React.createElement(Fields, {
|
|
29
|
+
Bind: Bind,
|
|
30
|
+
fields: fields,
|
|
31
|
+
layout: tab.layout,
|
|
32
|
+
contentModel: contentModel,
|
|
33
|
+
gridClassName: gridClassName
|
|
34
|
+
}))
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
const tabsWrapperClassName = ["bg-white!", "opacity-100!", "border-neutral-muted", "text-neutral-strong", "fill-neutral-xstrong", "w-full", "border-sm", "rounded-md"].join(" ");
|
|
38
|
+
export const TabsFieldRenderer = ({
|
|
39
|
+
descriptor,
|
|
40
|
+
Bind,
|
|
41
|
+
fields,
|
|
42
|
+
contentModel,
|
|
43
|
+
gridClassName
|
|
44
|
+
}) => {
|
|
45
|
+
const tabElements = descriptor.tabs.map(tab => /*#__PURE__*/React.createElement(TabPanel, {
|
|
46
|
+
key: tab.id,
|
|
47
|
+
tab: tab,
|
|
48
|
+
Bind: Bind,
|
|
49
|
+
fields: fields,
|
|
50
|
+
contentModel: contentModel,
|
|
51
|
+
gridClassName: gridClassName
|
|
52
|
+
}));
|
|
53
|
+
const firstTab = descriptor.tabs[0];
|
|
54
|
+
return /*#__PURE__*/React.createElement(Grid.Column, {
|
|
55
|
+
span: 12
|
|
56
|
+
}, descriptor.label ? /*#__PURE__*/React.createElement(FormComponentLabel, {
|
|
57
|
+
text: descriptor.label,
|
|
58
|
+
hint: descriptor.help
|
|
59
|
+
}) : null, descriptor.description ? /*#__PURE__*/React.createElement(FormComponentDescription, {
|
|
60
|
+
text: descriptor.description
|
|
61
|
+
}) : null, /*#__PURE__*/React.createElement("div", {
|
|
62
|
+
className: tabsWrapperClassName
|
|
63
|
+
}, /*#__PURE__*/React.createElement(Tabs, {
|
|
64
|
+
size: "md",
|
|
65
|
+
spacing: "md",
|
|
66
|
+
separator: true,
|
|
67
|
+
tabs: tabElements,
|
|
68
|
+
defaultValue: firstTab?.id
|
|
69
|
+
})));
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
//# sourceMappingURL=TabsFieldRenderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["React","Grid","FormComponentLabel","FormComponentDescription","Tabs","FontAwesomeIcon","normalizeIcon","Fields","FieldRulesProvider","useEffectiveRules","TabPanel","tab","Bind","fields","contentModel","gridClassName","rules","icon","createElement","Tab","disabled","visible","canView","hidden","value","id","trigger","label","size","undefined","content","layout","tabsWrapperClassName","join","TabsFieldRenderer","descriptor","tabElements","tabs","map","key","firstTab","Column","span","text","hint","help","description","className","spacing","separator","defaultValue"],"sources":["TabsFieldRenderer.tsx"],"sourcesContent":["import React from \"react\";\nimport { Grid, FormComponentLabel, FormComponentDescription, Tabs } from \"@webiny/admin-ui\";\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport type { CmsTabLayoutDescriptor, CmsTabLayoutTab } from \"~/types/model.js\";\nimport type { BindComponent, CmsEditorContentModel, CmsModelField } from \"~/types/index.js\";\nimport { normalizeIcon } from \"~/normalizeIcon.js\";\nimport { Fields } from \"~/Fields/index.js\";\nimport { FieldRulesProvider } from \"~/Fields/FieldRulesProvider.js\";\nimport { useEffectiveRules } from \"~/Fields/useFieldRules.js\";\n\ninterface TabsFieldRendererProps {\n descriptor: CmsTabLayoutDescriptor;\n Bind: BindComponent;\n fields: CmsModelField[];\n contentModel: CmsEditorContentModel;\n gridClassName?: string;\n}\n\ninterface TabPanelProps {\n tab: CmsTabLayoutTab;\n Bind: BindComponent;\n fields: CmsModelField[];\n contentModel: CmsEditorContentModel;\n gridClassName?: string;\n}\n\nconst TabPanel = ({ tab, Bind, fields, contentModel, gridClassName }: TabPanelProps) => {\n const rules = useEffectiveRules(tab);\n\n const icon = normalizeIcon(tab.icon);\n\n return (\n <Tabs.Tab\n disabled={rules.disabled}\n visible={rules.canView && !rules.hidden}\n value={tab.id}\n trigger={tab.label}\n icon={icon ? <FontAwesomeIcon icon={icon} size={\"sm\"} /> : undefined}\n content={\n <FieldRulesProvider rules={rules}>\n <Fields\n Bind={Bind}\n fields={fields}\n layout={tab.layout}\n contentModel={contentModel}\n gridClassName={gridClassName}\n />\n </FieldRulesProvider>\n }\n />\n );\n};\n\nconst tabsWrapperClassName = [\n \"bg-white!\",\n \"opacity-100!\",\n \"border-neutral-muted\",\n \"text-neutral-strong\",\n \"fill-neutral-xstrong\",\n \"w-full\",\n \"border-sm\",\n \"rounded-md\"\n].join(\" \");\n\nexport const TabsFieldRenderer = ({\n descriptor,\n Bind,\n fields,\n contentModel,\n gridClassName\n}: TabsFieldRendererProps) => {\n const tabElements = descriptor.tabs.map(tab => (\n <TabPanel\n key={tab.id}\n tab={tab}\n Bind={Bind}\n fields={fields}\n contentModel={contentModel}\n gridClassName={gridClassName}\n />\n ));\n\n const firstTab = descriptor.tabs[0];\n\n return (\n <Grid.Column span={12}>\n {descriptor.label ? (\n <FormComponentLabel text={descriptor.label} hint={descriptor.help} />\n ) : null}\n {descriptor.description ? (\n <FormComponentDescription text={descriptor.description} />\n ) : null}\n <div className={tabsWrapperClassName}>\n <Tabs\n size=\"md\"\n spacing=\"md\"\n separator={true}\n tabs={tabElements}\n defaultValue={firstTab?.id}\n />\n </div>\n </Grid.Column>\n );\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Operator } from "./evaluateExpression.js";
|
|
2
|
+
export interface OperatorOption {
|
|
3
|
+
value: Operator;
|
|
4
|
+
label: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function getOperatorOptions(fieldType: string): OperatorOption[];
|
|
7
|
+
/**
|
|
8
|
+
* Operators that don't need a value input (the value is implicit).
|
|
9
|
+
*/
|
|
10
|
+
export declare const VALUE_HIDDEN_OPERATORS: Set<Operator>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const NUMERIC_OPERATORS = [{
|
|
2
|
+
value: "==",
|
|
3
|
+
label: "Equals"
|
|
4
|
+
}, {
|
|
5
|
+
value: "!=",
|
|
6
|
+
label: "Not equals"
|
|
7
|
+
}, {
|
|
8
|
+
value: ">",
|
|
9
|
+
label: "Greater than"
|
|
10
|
+
}, {
|
|
11
|
+
value: "<",
|
|
12
|
+
label: "Less than"
|
|
13
|
+
}, {
|
|
14
|
+
value: ">=",
|
|
15
|
+
label: "Greater or equal"
|
|
16
|
+
}, {
|
|
17
|
+
value: "<=",
|
|
18
|
+
label: "Less or equal"
|
|
19
|
+
}, {
|
|
20
|
+
value: "isEmpty",
|
|
21
|
+
label: "Is empty"
|
|
22
|
+
}, {
|
|
23
|
+
value: "isNotEmpty",
|
|
24
|
+
label: "Is not empty"
|
|
25
|
+
}];
|
|
26
|
+
const TEXT_OPERATORS = [{
|
|
27
|
+
value: "==",
|
|
28
|
+
label: "Equals"
|
|
29
|
+
}, {
|
|
30
|
+
value: "!=",
|
|
31
|
+
label: "Not equals"
|
|
32
|
+
}, {
|
|
33
|
+
value: "contains",
|
|
34
|
+
label: "Contains"
|
|
35
|
+
}, {
|
|
36
|
+
value: "startsWith",
|
|
37
|
+
label: "Starts with"
|
|
38
|
+
}, {
|
|
39
|
+
value: "endsWith",
|
|
40
|
+
label: "Ends with"
|
|
41
|
+
}, {
|
|
42
|
+
value: "isEmpty",
|
|
43
|
+
label: "Is empty"
|
|
44
|
+
}, {
|
|
45
|
+
value: "isNotEmpty",
|
|
46
|
+
label: "Is not empty"
|
|
47
|
+
}];
|
|
48
|
+
const BOOLEAN_OPERATORS = [{
|
|
49
|
+
value: "==",
|
|
50
|
+
label: "Equals"
|
|
51
|
+
}, {
|
|
52
|
+
value: "!=",
|
|
53
|
+
label: "Not equals"
|
|
54
|
+
}];
|
|
55
|
+
const FILE_OPERATORS = [{
|
|
56
|
+
value: "isEmpty",
|
|
57
|
+
label: "Is empty"
|
|
58
|
+
}, {
|
|
59
|
+
value: "isNotEmpty",
|
|
60
|
+
label: "Is not empty"
|
|
61
|
+
}];
|
|
62
|
+
const DEFAULT_OPERATORS = [{
|
|
63
|
+
value: "==",
|
|
64
|
+
label: "Equals"
|
|
65
|
+
}, {
|
|
66
|
+
value: "!=",
|
|
67
|
+
label: "Not equals"
|
|
68
|
+
}, {
|
|
69
|
+
value: "isEmpty",
|
|
70
|
+
label: "Is empty"
|
|
71
|
+
}, {
|
|
72
|
+
value: "isNotEmpty",
|
|
73
|
+
label: "Is not empty"
|
|
74
|
+
}];
|
|
75
|
+
const OPERATORS_BY_TYPE = {
|
|
76
|
+
number: NUMERIC_OPERATORS,
|
|
77
|
+
datetime: NUMERIC_OPERATORS,
|
|
78
|
+
text: TEXT_OPERATORS,
|
|
79
|
+
"long-text": TEXT_OPERATORS,
|
|
80
|
+
boolean: BOOLEAN_OPERATORS,
|
|
81
|
+
file: FILE_OPERATORS
|
|
82
|
+
};
|
|
83
|
+
export function getOperatorOptions(fieldType) {
|
|
84
|
+
return OPERATORS_BY_TYPE[fieldType] ?? DEFAULT_OPERATORS;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Operators that don't need a value input (the value is implicit).
|
|
89
|
+
*/
|
|
90
|
+
export const VALUE_HIDDEN_OPERATORS = new Set(["isEmpty", "isNotEmpty"]);
|
|
91
|
+
|
|
92
|
+
//# sourceMappingURL=operatorOptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["NUMERIC_OPERATORS","value","label","TEXT_OPERATORS","BOOLEAN_OPERATORS","FILE_OPERATORS","DEFAULT_OPERATORS","OPERATORS_BY_TYPE","number","datetime","text","boolean","file","getOperatorOptions","fieldType","VALUE_HIDDEN_OPERATORS","Set"],"sources":["operatorOptions.ts"],"sourcesContent":["import type { Operator } from \"./evaluateExpression.js\";\n\nexport interface OperatorOption {\n value: Operator;\n label: string;\n}\n\nconst NUMERIC_OPERATORS: OperatorOption[] = [\n { value: \"==\", label: \"Equals\" },\n { value: \"!=\", label: \"Not equals\" },\n { value: \">\", label: \"Greater than\" },\n { value: \"<\", label: \"Less than\" },\n { value: \">=\", label: \"Greater or equal\" },\n { value: \"<=\", label: \"Less or equal\" },\n { value: \"isEmpty\", label: \"Is empty\" },\n { value: \"isNotEmpty\", label: \"Is not empty\" }\n];\n\nconst TEXT_OPERATORS: OperatorOption[] = [\n { value: \"==\", label: \"Equals\" },\n { value: \"!=\", label: \"Not equals\" },\n { value: \"contains\", label: \"Contains\" },\n { value: \"startsWith\", label: \"Starts with\" },\n { value: \"endsWith\", label: \"Ends with\" },\n { value: \"isEmpty\", label: \"Is empty\" },\n { value: \"isNotEmpty\", label: \"Is not empty\" }\n];\n\nconst BOOLEAN_OPERATORS: OperatorOption[] = [\n { value: \"==\", label: \"Equals\" },\n { value: \"!=\", label: \"Not equals\" }\n];\n\nconst FILE_OPERATORS: OperatorOption[] = [\n { value: \"isEmpty\", label: \"Is empty\" },\n { value: \"isNotEmpty\", label: \"Is not empty\" }\n];\n\nconst DEFAULT_OPERATORS: OperatorOption[] = [\n { value: \"==\", label: \"Equals\" },\n { value: \"!=\", label: \"Not equals\" },\n { value: \"isEmpty\", label: \"Is empty\" },\n { value: \"isNotEmpty\", label: \"Is not empty\" }\n];\n\nconst OPERATORS_BY_TYPE: Record<string, OperatorOption[]> = {\n number: NUMERIC_OPERATORS,\n datetime: NUMERIC_OPERATORS,\n text: TEXT_OPERATORS,\n \"long-text\": TEXT_OPERATORS,\n boolean: BOOLEAN_OPERATORS,\n file: FILE_OPERATORS\n};\n\nexport function getOperatorOptions(fieldType: string): OperatorOption[] {\n return OPERATORS_BY_TYPE[fieldType] ?? DEFAULT_OPERATORS;\n}\n\n/**\n * Operators that don't need a value input (the value is implicit).\n */\nexport const VALUE_HIDDEN_OPERATORS: Set<Operator> = new Set([\"isEmpty\", \"isNotEmpty\"]);\n"],"mappings":"AAOA,MAAMA,iBAAmC,GAAG,CACxC;EAAEC,KAAK,EAAE,IAAI;EAAEC,KAAK,EAAE;AAAS,CAAC,EAChC;EAAED,KAAK,EAAE,IAAI;EAAEC,KAAK,EAAE;AAAa,CAAC,EACpC;EAAED,KAAK,EAAE,GAAG;EAAEC,KAAK,EAAE;AAAe,CAAC,EACrC;EAAED,KAAK,EAAE,GAAG;EAAEC,KAAK,EAAE;AAAY,CAAC,EAClC;EAAED,KAAK,EAAE,IAAI;EAAEC,KAAK,EAAE;AAAmB,CAAC,EAC1C;EAAED,KAAK,EAAE,IAAI;EAAEC,KAAK,EAAE;AAAgB,CAAC,EACvC;EAAED,KAAK,EAAE,SAAS;EAAEC,KAAK,EAAE;AAAW,CAAC,EACvC;EAAED,KAAK,EAAE,YAAY;EAAEC,KAAK,EAAE;AAAe,CAAC,CACjD;AAED,MAAMC,cAAgC,GAAG,CACrC;EAAEF,KAAK,EAAE,IAAI;EAAEC,KAAK,EAAE;AAAS,CAAC,EAChC;EAAED,KAAK,EAAE,IAAI;EAAEC,KAAK,EAAE;AAAa,CAAC,EACpC;EAAED,KAAK,EAAE,UAAU;EAAEC,KAAK,EAAE;AAAW,CAAC,EACxC;EAAED,KAAK,EAAE,YAAY;EAAEC,KAAK,EAAE;AAAc,CAAC,EAC7C;EAAED,KAAK,EAAE,UAAU;EAAEC,KAAK,EAAE;AAAY,CAAC,EACzC;EAAED,KAAK,EAAE,SAAS;EAAEC,KAAK,EAAE;AAAW,CAAC,EACvC;EAAED,KAAK,EAAE,YAAY;EAAEC,KAAK,EAAE;AAAe,CAAC,CACjD;AAED,MAAME,iBAAmC,GAAG,CACxC;EAAEH,KAAK,EAAE,IAAI;EAAEC,KAAK,EAAE;AAAS,CAAC,EAChC;EAAED,KAAK,EAAE,IAAI;EAAEC,KAAK,EAAE;AAAa,CAAC,CACvC;AAED,MAAMG,cAAgC,GAAG,CACrC;EAAEJ,KAAK,EAAE,SAAS;EAAEC,KAAK,EAAE;AAAW,CAAC,EACvC;EAAED,KAAK,EAAE,YAAY;EAAEC,KAAK,EAAE;AAAe,CAAC,CACjD;AAED,MAAMI,iBAAmC,GAAG,CACxC;EAAEL,KAAK,EAAE,IAAI;EAAEC,KAAK,EAAE;AAAS,CAAC,EAChC;EAAED,KAAK,EAAE,IAAI;EAAEC,KAAK,EAAE;AAAa,CAAC,EACpC;EAAED,KAAK,EAAE,SAAS;EAAEC,KAAK,EAAE;AAAW,CAAC,EACvC;EAAED,KAAK,EAAE,YAAY;EAAEC,KAAK,EAAE;AAAe,CAAC,CACjD;AAED,MAAMK,iBAAmD,GAAG;EACxDC,MAAM,EAAER,iBAAiB;EACzBS,QAAQ,EAAET,iBAAiB;EAC3BU,IAAI,EAAEP,cAAc;EACpB,WAAW,EAAEA,cAAc;EAC3BQ,OAAO,EAAEP,iBAAiB;EAC1BQ,IAAI,EAAEP;AACV,CAAC;AAED,OAAO,SAASQ,kBAAkBA,CAACC,SAAiB,EAAoB;EACpE,OAAOP,iBAAiB,CAACO,SAAS,CAAC,IAAIR,iBAAiB;AAC5D;;AAEA;AACA;AACA;AACA,OAAO,MAAMS,sBAAqC,GAAG,IAAIC,GAAG,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC","ignoreList":[]}
|
package/Fields/useBind.d.ts
CHANGED
package/Fields/useBind.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import React, { useRef, cloneElement } from "react";
|
|
1
|
+
import React, { createContext, useContext, useRef, cloneElement } from "react";
|
|
2
2
|
import { useForm } from "@webiny/form";
|
|
3
3
|
import { createValidators } from "../createValidators.js";
|
|
4
4
|
import { useModelField } from "../ModelFieldProvider/index.js";
|
|
5
5
|
import { createValidationContainer } from "../createValidationContainer.js";
|
|
6
|
+
const BindParentNameContext = /*#__PURE__*/createContext("");
|
|
7
|
+
export const useBindParentName = () => useContext(BindParentNameContext);
|
|
6
8
|
const createFieldCacheKey = field => {
|
|
7
9
|
return [field.id, field.fieldId, JSON.stringify(field.validation), JSON.stringify(field.listValidation)].join(";");
|
|
8
10
|
};
|
|
@@ -45,7 +47,9 @@ export function useBind({
|
|
|
45
47
|
const {
|
|
46
48
|
field
|
|
47
49
|
} = useModelField();
|
|
48
|
-
return /*#__PURE__*/React.createElement(
|
|
50
|
+
return /*#__PURE__*/React.createElement(BindParentNameContext.Provider, {
|
|
51
|
+
value: name
|
|
52
|
+
}, /*#__PURE__*/React.createElement(Bind, {
|
|
49
53
|
name: childName || name,
|
|
50
54
|
validators: childValidators || inputValidators,
|
|
51
55
|
defaultValue: defaultValue ?? null,
|
|
@@ -99,7 +103,7 @@ export function useBind({
|
|
|
99
103
|
};
|
|
100
104
|
}
|
|
101
105
|
return typeof children === "function" ? children(props) : /*#__PURE__*/cloneElement(children, props);
|
|
102
|
-
});
|
|
106
|
+
}));
|
|
103
107
|
};
|
|
104
108
|
|
|
105
109
|
// We need to keep track of current field name, to support nested fields.
|
package/Fields/useBind.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","useRef","cloneElement","useForm","createValidators","useModelField","createValidationContainer","createFieldCacheKey","field","id","fieldId","JSON","stringify","validation","listValidation","join","emptyValidators","useBind","Bind","memoizedBindComponents","cacheKey","form","index","parentName","name","undefined","filter","v","componentId","current","validators","listValidators","isMultipleValues","list","inputValidators","defaultValueFromSettings","settings","defaultValue","UseBind","params","childName","childValidators","children","createElement","context","bind","props","appendValue","newValue","currentValue","value","newIndex","length","onChange","slice","prependValue","appendValues","newValues","removeValue","validateInput","moveValueUp","splice","moveValueDown","displayName","ValidationContainer"],"sources":["useBind.tsx"],"sourcesContent":["import React, { useRef, cloneElement } from \"react\";\nimport type { Validator } from \"@webiny/validation/types.js\";\nimport { useForm } from \"@webiny/form\";\nimport { createValidators } from \"~/createValidators.js\";\nimport type { BindComponent, CmsModelField } from \"~/types/index.js\";\nimport { useModelField } from \"~/ModelFieldProvider/index.js\";\nimport { createValidationContainer } from \"~/createValidationContainer.js\";\n\ninterface UseBindProps {\n Bind: BindComponent;\n}\n\ninterface UseBindParams {\n name?: string;\n validators?: Validator | Validator[];\n children?: any;\n defaultValue?: any;\n}\n\nconst createFieldCacheKey = (field: CmsModelField) => {\n return [\n field.id,\n field.fieldId,\n JSON.stringify(field.validation),\n JSON.stringify(field.listValidation)\n ].join(\";\");\n};\n\nexport interface GetBindCallable {\n (index?: number): BindComponent;\n}\n\nconst emptyValidators: Validator[] = [];\n\nexport function useBind({ Bind }: UseBindProps) {\n const { field } = useModelField();\n const memoizedBindComponents = useRef<Record<string, BindComponent>>({});\n const cacheKey = createFieldCacheKey(field);\n const form = useForm();\n\n return (index = -1) => {\n const { parentName } = Bind;\n\n // If there's a parent name assigned to the given Bind component, we need to include it in the new field \"name\".\n // This allows us to have nested fields (like \"object\" field with nested properties)\n const name = [parentName, field.fieldId, index >= 0 ? index : undefined]\n .filter(v => v !== undefined)\n .join(\".\");\n\n const componentId = `${name};${cacheKey}`;\n\n if (memoizedBindComponents.current[componentId]) {\n return memoizedBindComponents.current[componentId];\n }\n\n const validators = createValidators(field, field.validation || emptyValidators);\n const listValidators = createValidators(field, field.listValidation || emptyValidators);\n const isMultipleValues = index === -1 && field.list;\n const inputValidators = isMultipleValues ? listValidators : validators;\n\n // We only use default values for single-value fields.\n const defaultValueFromSettings = !isMultipleValues ? field.settings?.defaultValue : null;\n\n memoizedBindComponents.current[componentId] = function UseBind(params: UseBindParams) {\n const {\n name: childName,\n validators: childValidators,\n children,\n defaultValue = defaultValueFromSettings\n } = params;\n\n const { field } = useModelField();\n\n return (\n <Bind\n name={childName || name}\n validators={childValidators || inputValidators}\n defaultValue={defaultValue ?? null}\n context={{ field }}\n >\n {bind => {\n // Multiple-values functions below.\n const props = { ...bind };\n if (field.list && index === -1) {\n props.appendValue = (newValue: any, index?: number) => {\n const currentValue = bind.value || [];\n const newIndex = index ?? currentValue.length;\n\n bind.onChange([\n ...currentValue.slice(0, newIndex),\n newValue,\n ...currentValue.slice(newIndex)\n ]);\n };\n props.prependValue = (newValue: any) => {\n bind.onChange([newValue, ...(bind.value || [])]);\n };\n props.appendValues = (newValues: any[]) => {\n bind.onChange([...(bind.value || []), ...newValues]);\n };\n\n props.removeValue = (index: number) => {\n if (index < 0) {\n return;\n }\n\n const value = [\n ...bind.value.slice(0, index),\n ...bind.value.slice(index + 1)\n ];\n\n bind.onChange(value.length === 0 ? null : value);\n\n // To make sure the field is still valid, we must trigger validation.\n form.validateInput(field.fieldId);\n };\n\n props.moveValueUp = (index: number) => {\n if (index <= 0) {\n return;\n }\n\n const value = [...bind.value];\n value.splice(index, 1);\n value.splice(index - 1, 0, bind.value[index]);\n\n bind.onChange(value);\n };\n\n props.moveValueDown = (index: number) => {\n if (index >= bind.value.length) {\n return;\n }\n\n const value = [...bind.value];\n value.splice(index, 1);\n value.splice(index + 1, 0, bind.value[index]);\n\n bind.onChange(value);\n };\n }\n\n return typeof children === \"function\"\n ? children(props)\n : cloneElement(children, props);\n }}\n </Bind>\n );\n } as BindComponent;\n\n // We need to keep track of current field name, to support nested fields.\n memoizedBindComponents.current[componentId].parentName = name;\n memoizedBindComponents.current[componentId].displayName = `Bind<${name}>`;\n memoizedBindComponents.current[componentId].ValidationContainer =\n createValidationContainer(name);\n\n return memoizedBindComponents.current[componentId];\n };\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["React","createContext","useContext","useRef","cloneElement","useForm","createValidators","useModelField","createValidationContainer","BindParentNameContext","useBindParentName","createFieldCacheKey","field","id","fieldId","JSON","stringify","validation","listValidation","join","emptyValidators","useBind","Bind","memoizedBindComponents","cacheKey","form","index","parentName","name","undefined","filter","v","componentId","current","validators","listValidators","isMultipleValues","list","inputValidators","defaultValueFromSettings","settings","defaultValue","UseBind","params","childName","childValidators","children","createElement","Provider","value","context","bind","props","appendValue","newValue","currentValue","newIndex","length","onChange","slice","prependValue","appendValues","newValues","removeValue","validateInput","moveValueUp","splice","moveValueDown","displayName","ValidationContainer"],"sources":["useBind.tsx"],"sourcesContent":["import React, { createContext, useContext, useRef, cloneElement } from \"react\";\nimport type { Validator } from \"@webiny/validation/types.js\";\nimport { useForm } from \"@webiny/form\";\nimport { createValidators } from \"~/createValidators.js\";\nimport type { BindComponent, CmsModelField } from \"~/types/index.js\";\nimport { useModelField } from \"~/ModelFieldProvider/index.js\";\nimport { createValidationContainer } from \"~/createValidationContainer.js\";\n\nconst BindParentNameContext = createContext<string>(\"\");\n\nexport const useBindParentName = (): string => useContext(BindParentNameContext);\n\ninterface UseBindProps {\n Bind: BindComponent;\n}\n\ninterface UseBindParams {\n name?: string;\n validators?: Validator | Validator[];\n children?: any;\n defaultValue?: any;\n}\n\nconst createFieldCacheKey = (field: CmsModelField) => {\n return [\n field.id,\n field.fieldId,\n JSON.stringify(field.validation),\n JSON.stringify(field.listValidation)\n ].join(\";\");\n};\n\nexport interface GetBindCallable {\n (index?: number): BindComponent;\n}\n\nconst emptyValidators: Validator[] = [];\n\nexport function useBind({ Bind }: UseBindProps) {\n const { field } = useModelField();\n const memoizedBindComponents = useRef<Record<string, BindComponent>>({});\n const cacheKey = createFieldCacheKey(field);\n const form = useForm();\n\n return (index = -1) => {\n const { parentName } = Bind;\n\n // If there's a parent name assigned to the given Bind component, we need to include it in the new field \"name\".\n // This allows us to have nested fields (like \"object\" field with nested properties)\n const name = [parentName, field.fieldId, index >= 0 ? index : undefined]\n .filter(v => v !== undefined)\n .join(\".\");\n\n const componentId = `${name};${cacheKey}`;\n\n if (memoizedBindComponents.current[componentId]) {\n return memoizedBindComponents.current[componentId];\n }\n\n const validators = createValidators(field, field.validation || emptyValidators);\n const listValidators = createValidators(field, field.listValidation || emptyValidators);\n const isMultipleValues = index === -1 && field.list;\n const inputValidators = isMultipleValues ? listValidators : validators;\n\n // We only use default values for single-value fields.\n const defaultValueFromSettings = !isMultipleValues ? field.settings?.defaultValue : null;\n\n memoizedBindComponents.current[componentId] = function UseBind(params: UseBindParams) {\n const {\n name: childName,\n validators: childValidators,\n children,\n defaultValue = defaultValueFromSettings\n } = params;\n\n const { field } = useModelField();\n\n return (\n <BindParentNameContext.Provider value={name}>\n <Bind\n name={childName || name}\n validators={childValidators || inputValidators}\n defaultValue={defaultValue ?? null}\n context={{ field }}\n >\n {bind => {\n // Multiple-values functions below.\n const props = { ...bind };\n if (field.list && index === -1) {\n props.appendValue = (newValue: any, index?: number) => {\n const currentValue = bind.value || [];\n const newIndex = index ?? currentValue.length;\n\n bind.onChange([\n ...currentValue.slice(0, newIndex),\n newValue,\n ...currentValue.slice(newIndex)\n ]);\n };\n props.prependValue = (newValue: any) => {\n bind.onChange([newValue, ...(bind.value || [])]);\n };\n props.appendValues = (newValues: any[]) => {\n bind.onChange([...(bind.value || []), ...newValues]);\n };\n\n props.removeValue = (index: number) => {\n if (index < 0) {\n return;\n }\n\n const value = [\n ...bind.value.slice(0, index),\n ...bind.value.slice(index + 1)\n ];\n\n bind.onChange(value.length === 0 ? null : value);\n\n // To make sure the field is still valid, we must trigger validation.\n form.validateInput(field.fieldId);\n };\n\n props.moveValueUp = (index: number) => {\n if (index <= 0) {\n return;\n }\n\n const value = [...bind.value];\n value.splice(index, 1);\n value.splice(index - 1, 0, bind.value[index]);\n\n bind.onChange(value);\n };\n\n props.moveValueDown = (index: number) => {\n if (index >= bind.value.length) {\n return;\n }\n\n const value = [...bind.value];\n value.splice(index, 1);\n value.splice(index + 1, 0, bind.value[index]);\n\n bind.onChange(value);\n };\n }\n\n return typeof children === \"function\"\n ? children(props)\n : cloneElement(children, props);\n }}\n </Bind>\n </BindParentNameContext.Provider>\n );\n } as BindComponent;\n\n // We need to keep track of current field name, to support nested fields.\n memoizedBindComponents.current[componentId].parentName = name;\n memoizedBindComponents.current[componentId].displayName = `Bind<${name}>`;\n memoizedBindComponents.current[componentId].ValidationContainer =\n createValidationContainer(name);\n\n return memoizedBindComponents.current[componentId];\n };\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { FieldRule } from "../types/model.js";
|
|
2
|
+
export interface EffectiveFieldRules {
|
|
3
|
+
canView: boolean;
|
|
4
|
+
canEdit: boolean;
|
|
5
|
+
hidden: boolean;
|
|
6
|
+
disabled: boolean;
|
|
7
|
+
}
|
|
8
|
+
interface HasRules {
|
|
9
|
+
rules?: FieldRule[];
|
|
10
|
+
}
|
|
11
|
+
interface IdentityLike {
|
|
12
|
+
id: string;
|
|
13
|
+
teams: {
|
|
14
|
+
slug: string;
|
|
15
|
+
}[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Hook that evaluates access control rules for the current identity.
|
|
19
|
+
* Does not require `bindParentName` — only identity-based permissions.
|
|
20
|
+
*/
|
|
21
|
+
export declare function useFieldAccessControlRules(item: HasRules): Pick<EffectiveFieldRules, "canView" | "canEdit">;
|
|
22
|
+
/**
|
|
23
|
+
* Evaluate access control rules statically (without hooks).
|
|
24
|
+
* Used in RowRenderer for the visibility approximation where hooks can't be called.
|
|
25
|
+
*/
|
|
26
|
+
export declare function evaluateAccessControlRules(item: HasRules, identity: IdentityLike): EffectiveFieldRules;
|
|
27
|
+
/**
|
|
28
|
+
* Composes useParentRules and useFieldRules into a single hook
|
|
29
|
+
* that returns the effective (intersected) rules.
|
|
30
|
+
*/
|
|
31
|
+
export declare function useEffectiveRules(item: HasRules): EffectiveFieldRules;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { useForm } from "@webiny/form";
|
|
2
|
+
import { useAuthentication } from "@webiny/app-admin";
|
|
3
|
+
import { evaluateExpression, resolveFieldPath } from "./evaluateExpression.js";
|
|
4
|
+
import { useParentRules } from "./FieldRulesProvider.js";
|
|
5
|
+
import { useBindParentName } from "./useBind.js";
|
|
6
|
+
/**
|
|
7
|
+
* Evaluate access control rules against an identity.
|
|
8
|
+
* Pure function — no hooks, can be called anywhere.
|
|
9
|
+
*/
|
|
10
|
+
function evaluateAccessControlRulesForIdentity(rules, identity) {
|
|
11
|
+
const userTargets = new Set();
|
|
12
|
+
userTargets.add(`admin:${identity.id}`);
|
|
13
|
+
for (const team of identity.teams) {
|
|
14
|
+
userTargets.add(`team:${team.slug}`);
|
|
15
|
+
}
|
|
16
|
+
let canView = true;
|
|
17
|
+
let canEdit = true;
|
|
18
|
+
for (const rule of rules) {
|
|
19
|
+
if (rule.type !== "accessControl") {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (!rule.value || !rule.operator) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (!userTargets.has(String(rule.value))) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (rule.action === "hide") {
|
|
29
|
+
canView = false;
|
|
30
|
+
} else if (rule.action === "disable") {
|
|
31
|
+
canEdit = false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
canView,
|
|
36
|
+
canEdit
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Hook that evaluates access control rules for the current identity.
|
|
42
|
+
* Does not require `bindParentName` — only identity-based permissions.
|
|
43
|
+
*/
|
|
44
|
+
export function useFieldAccessControlRules(item) {
|
|
45
|
+
const {
|
|
46
|
+
identity
|
|
47
|
+
} = useAuthentication();
|
|
48
|
+
const rules = item.rules;
|
|
49
|
+
if (!rules || rules.length === 0) {
|
|
50
|
+
return {
|
|
51
|
+
canView: true,
|
|
52
|
+
canEdit: true
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return evaluateAccessControlRulesForIdentity(rules, identity);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Evaluate access control rules statically (without hooks).
|
|
60
|
+
* Used in RowRenderer for the visibility approximation where hooks can't be called.
|
|
61
|
+
*/
|
|
62
|
+
export function evaluateAccessControlRules(item, identity) {
|
|
63
|
+
const rules = item.rules;
|
|
64
|
+
if (!rules || rules.length === 0) {
|
|
65
|
+
return {
|
|
66
|
+
canView: true,
|
|
67
|
+
canEdit: true,
|
|
68
|
+
hidden: false,
|
|
69
|
+
disabled: false
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const {
|
|
73
|
+
canView,
|
|
74
|
+
canEdit
|
|
75
|
+
} = evaluateAccessControlRulesForIdentity(rules, identity);
|
|
76
|
+
return {
|
|
77
|
+
canView,
|
|
78
|
+
canEdit,
|
|
79
|
+
hidden: false,
|
|
80
|
+
disabled: false
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Internal hook that evaluates all rules (access control + entry value).
|
|
86
|
+
*/
|
|
87
|
+
function useFieldRules(item) {
|
|
88
|
+
const {
|
|
89
|
+
identity
|
|
90
|
+
} = useAuthentication();
|
|
91
|
+
const form = useForm();
|
|
92
|
+
const bindParentName = useBindParentName();
|
|
93
|
+
const rules = item.rules;
|
|
94
|
+
if (!rules || rules.length === 0) {
|
|
95
|
+
return {
|
|
96
|
+
canView: true,
|
|
97
|
+
canEdit: true,
|
|
98
|
+
hidden: false,
|
|
99
|
+
disabled: false
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const {
|
|
103
|
+
canView,
|
|
104
|
+
canEdit
|
|
105
|
+
} = evaluateAccessControlRulesForIdentity(rules, identity);
|
|
106
|
+
let hidden = false;
|
|
107
|
+
let disabled = false;
|
|
108
|
+
for (const rule of rules) {
|
|
109
|
+
if (rule.type !== "condition") {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (!rule.target || !rule.operator) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const resolvedPath = resolveFieldPath(rule.target, bindParentName);
|
|
116
|
+
const matches = evaluateExpression({
|
|
117
|
+
target: resolvedPath,
|
|
118
|
+
operator: rule.operator,
|
|
119
|
+
value: rule.value
|
|
120
|
+
}, name => form.getValue(name));
|
|
121
|
+
if (!matches) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (rule.action === "hide") {
|
|
125
|
+
hidden = true;
|
|
126
|
+
} else if (rule.action === "disable") {
|
|
127
|
+
disabled = true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
canView,
|
|
132
|
+
canEdit,
|
|
133
|
+
hidden,
|
|
134
|
+
disabled
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Composes useParentRules and useFieldRules into a single hook
|
|
140
|
+
* that returns the effective (intersected) rules.
|
|
141
|
+
*/
|
|
142
|
+
export function useEffectiveRules(item) {
|
|
143
|
+
const parentRules = useParentRules();
|
|
144
|
+
const itemRules = useFieldRules(item);
|
|
145
|
+
return {
|
|
146
|
+
canView: parentRules.canView && itemRules.canView,
|
|
147
|
+
canEdit: parentRules.canEdit && itemRules.canEdit,
|
|
148
|
+
hidden: parentRules.hidden || itemRules.hidden,
|
|
149
|
+
disabled: parentRules.disabled || itemRules.disabled
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//# sourceMappingURL=useFieldRules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useForm","useAuthentication","evaluateExpression","resolveFieldPath","useParentRules","useBindParentName","evaluateAccessControlRulesForIdentity","rules","identity","userTargets","Set","add","id","team","teams","slug","canView","canEdit","rule","type","value","operator","has","String","action","useFieldAccessControlRules","item","length","evaluateAccessControlRules","hidden","disabled","useFieldRules","form","bindParentName","target","resolvedPath","matches","name","getValue","useEffectiveRules","parentRules","itemRules"],"sources":["useFieldRules.ts"],"sourcesContent":["import { useForm } from \"@webiny/form\";\nimport { useAuthentication } from \"@webiny/app-admin\";\nimport type { FieldRule } from \"~/types/model.js\";\nimport { evaluateExpression, resolveFieldPath } from \"./evaluateExpression.js\";\nimport { useParentRules } from \"./FieldRulesProvider.js\";\nimport { useBindParentName } from \"./useBind.js\";\n\nexport interface EffectiveFieldRules {\n canView: boolean;\n canEdit: boolean;\n hidden: boolean;\n disabled: boolean;\n}\n\ninterface HasRules {\n rules?: FieldRule[];\n}\n\ninterface IdentityLike {\n id: string;\n teams: { slug: string }[];\n}\n\n/**\n * Evaluate access control rules against an identity.\n * Pure function — no hooks, can be called anywhere.\n */\nfunction evaluateAccessControlRulesForIdentity(\n rules: FieldRule[],\n identity: IdentityLike\n): Pick<EffectiveFieldRules, \"canView\" | \"canEdit\"> {\n const userTargets = new Set<string>();\n userTargets.add(`admin:${identity.id}`);\n for (const team of identity.teams) {\n userTargets.add(`team:${team.slug}`);\n }\n\n let canView = true;\n let canEdit = true;\n\n for (const rule of rules) {\n if (rule.type !== \"accessControl\") {\n continue;\n }\n if (!rule.value || !rule.operator) {\n continue;\n }\n if (!userTargets.has(String(rule.value))) {\n continue;\n }\n if (rule.action === \"hide\") {\n canView = false;\n } else if (rule.action === \"disable\") {\n canEdit = false;\n }\n }\n\n return { canView, canEdit };\n}\n\n/**\n * Hook that evaluates access control rules for the current identity.\n * Does not require `bindParentName` — only identity-based permissions.\n */\nexport function useFieldAccessControlRules(\n item: HasRules\n): Pick<EffectiveFieldRules, \"canView\" | \"canEdit\"> {\n const { identity } = useAuthentication();\n\n const rules = item.rules;\n if (!rules || rules.length === 0) {\n return { canView: true, canEdit: true };\n }\n\n return evaluateAccessControlRulesForIdentity(rules, identity);\n}\n\n/**\n * Evaluate access control rules statically (without hooks).\n * Used in RowRenderer for the visibility approximation where hooks can't be called.\n */\nexport function evaluateAccessControlRules(\n item: HasRules,\n identity: IdentityLike\n): EffectiveFieldRules {\n const rules = item.rules;\n if (!rules || rules.length === 0) {\n return { canView: true, canEdit: true, hidden: false, disabled: false };\n }\n\n const { canView, canEdit } = evaluateAccessControlRulesForIdentity(rules, identity);\n return { canView, canEdit, hidden: false, disabled: false };\n}\n\n/**\n * Internal hook that evaluates all rules (access control + entry value).\n */\nfunction useFieldRules(item: HasRules): EffectiveFieldRules {\n const { identity } = useAuthentication();\n const form = useForm();\n const bindParentName = useBindParentName();\n\n const rules = item.rules;\n if (!rules || rules.length === 0) {\n return { canView: true, canEdit: true, hidden: false, disabled: false };\n }\n\n const { canView, canEdit } = evaluateAccessControlRulesForIdentity(rules, identity);\n\n let hidden = false;\n let disabled = false;\n\n for (const rule of rules) {\n if (rule.type !== \"condition\") {\n continue;\n }\n if (!rule.target || !rule.operator) {\n continue;\n }\n const resolvedPath = resolveFieldPath(rule.target, bindParentName);\n const matches = evaluateExpression(\n { target: resolvedPath, operator: rule.operator as any, value: rule.value },\n name => form.getValue(name)\n );\n if (!matches) {\n continue;\n }\n if (rule.action === \"hide\") {\n hidden = true;\n } else if (rule.action === \"disable\") {\n disabled = true;\n }\n }\n\n return { canView, canEdit, hidden, disabled };\n}\n\n/**\n * Composes useParentRules and useFieldRules into a single hook\n * that returns the effective (intersected) rules.\n */\nexport function useEffectiveRules(item: HasRules): EffectiveFieldRules {\n const parentRules = useParentRules();\n const itemRules = useFieldRules(item);\n return {\n canView: parentRules.canView && itemRules.canView,\n canEdit: parentRules.canEdit && itemRules.canEdit,\n hidden: parentRules.hidden || itemRules.hidden,\n disabled: parentRules.disabled || itemRules.disabled\n };\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useParentRules } from "../Fields/index.js";
|
|
3
|
+
export const CanEditField = ({
|
|
4
|
+
children
|
|
5
|
+
}) => {
|
|
6
|
+
const rules = useParentRules();
|
|
7
|
+
if (!rules.canEdit || rules.disabled) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, children);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=CanEditField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["React","useParentRules","CanEditField","children","rules","canEdit","disabled","createElement","Fragment"],"sources":["CanEditField.tsx"],"sourcesContent":["import React from \"react\";\nimport { useParentRules } from \"~/Fields/index.js\";\n\nexport interface CanEditFieldProps {\n children: React.ReactNode;\n}\n\nexport const CanEditField = ({ children }: CanEditFieldProps) => {\n const rules = useParentRules();\n\n if (!rules.canEdit || rules.disabled) {\n return null;\n }\n\n return <>{children}</>;\n};\n"],"mappings":"AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,SAASC,cAAc;AAMvB,OAAO,MAAMC,YAAY,GAAGA,CAAC;EAAEC;AAA4B,CAAC,KAAK;EAC7D,MAAMC,KAAK,GAAGH,cAAc,CAAC,CAAC;EAE9B,IAAI,CAACG,KAAK,CAACC,OAAO,IAAID,KAAK,CAACE,QAAQ,EAAE;IAClC,OAAO,IAAI;EACf;EAEA,oBAAON,KAAA,CAAAO,aAAA,CAAAP,KAAA,CAAAQ,QAAA,QAAGL,QAAW,CAAC;AAC1B,CAAC","ignoreList":[]}
|
|
@@ -21,6 +21,7 @@ export declare const ModelFieldContext: React.Context<{
|
|
|
21
21
|
settings?: Record<string, any>;
|
|
22
22
|
} | import("~/types/index.js").CmsModelFieldRendererPlugin["renderer"]["render"];
|
|
23
23
|
tags?: string[];
|
|
24
|
+
rules?: import("~/types/index.js").FieldRule[];
|
|
24
25
|
} | undefined>;
|
|
25
26
|
export interface ModelFieldProviderProps {
|
|
26
27
|
field: CmsModelField;
|