@rulab/adminjs-components 0.1.0-alpha.9 → 0.1.0-beta.2
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/README.md +162 -1
- package/dist/components/ColorStatus/ColorStatusEdit.d.ts +2 -2
- package/dist/components/ColorStatus/ColorStatusEdit.js +3 -3
- package/dist/components/ColorStatus/ColorStatusFeature.d.ts +9 -0
- package/dist/components/ColorStatus/ColorStatusFeature.js +23 -0
- package/dist/components/ColorStatus/ColorStatusList.d.ts +1 -1
- package/dist/components/ColorStatus/ColorStatusList.js +4 -2
- package/dist/components/ColorStatus/ColorStatusShow.d.ts +1 -1
- package/dist/components/ColorStatus/ColorStatusShow.js +5 -3
- package/dist/components/ColorStatus/index.d.ts +4 -3
- package/dist/components/ColorStatus/index.js +4 -3
- package/dist/components/ColorStatus/styles.js +2 -1
- package/dist/components/Editor/Editor.d.ts +4 -2
- package/dist/components/Editor/Editor.js +79 -16
- package/dist/components/Editor/EditorFeature.d.ts +10 -0
- package/dist/components/Editor/EditorFeature.js +82 -0
- package/dist/components/Editor/EditorList.d.ts +2 -2
- package/dist/components/Editor/EditorList.js +1 -1
- package/dist/components/Editor/EditorShow.d.ts +2 -2
- package/dist/components/Editor/EditorShow.js +2 -2
- package/dist/components/Editor/index.d.ts +4 -3
- package/dist/components/Editor/index.js +4 -3
- package/dist/components/FeatureTabs/FeatureTabsEdit.d.ts +4 -0
- package/dist/components/FeatureTabs/FeatureTabsEdit.js +105 -0
- package/dist/components/FeatureTabs/FeatureTabsFeature.d.ts +8 -0
- package/dist/components/FeatureTabs/FeatureTabsFeature.js +28 -0
- package/dist/components/FeatureTabs/FeatureTabsShow.d.ts +4 -0
- package/dist/components/FeatureTabs/FeatureTabsShow.js +61 -0
- package/dist/components/FeatureTabs/index.d.ts +4 -0
- package/dist/components/FeatureTabs/index.js +4 -0
- package/dist/components/Preview/PreviewAction.d.ts +4 -0
- package/dist/components/Preview/PreviewAction.js +22 -0
- package/dist/components/Preview/PreviewFeature.d.ts +9 -0
- package/dist/components/Preview/PreviewFeature.js +31 -0
- package/dist/components/Preview/index.d.ts +2 -0
- package/dist/components/Preview/index.js +2 -0
- package/dist/components/Slug/SlugEdit.d.ts +1 -1
- package/dist/components/Slug/SlugEdit.js +8 -3
- package/dist/components/Slug/SlugFeature.d.ts +3 -6
- package/dist/components/Slug/SlugFeature.js +12 -14
- package/dist/components/Slug/SlugOptions.type.d.ts +7 -0
- package/dist/components/StringList/StringList.d.ts +1 -1
- package/dist/components/StringList/StringList.js +4 -4
- package/dist/components/StringList/StringListFeature.d.ts +7 -0
- package/dist/components/StringList/StringListFeature.js +20 -0
- package/dist/components/StringList/StringListShow.d.ts +1 -1
- package/dist/components/StringList/StringListShow.js +2 -2
- package/dist/components/StringList/index.d.ts +3 -2
- package/dist/components/StringList/index.js +3 -2
- package/dist/components/StringList/styles.js +2 -1
- package/dist/components/Tabs/TabsEdit.d.ts +4 -0
- package/dist/components/Tabs/TabsEdit.js +103 -0
- package/dist/components/Tabs/TabsFeature.d.ts +7 -0
- package/dist/components/Tabs/TabsFeature.js +26 -0
- package/dist/components/Tabs/TabsShow.d.ts +4 -0
- package/dist/components/Tabs/TabsShow.js +61 -0
- package/dist/components/Tabs/index.d.ts +3 -0
- package/dist/components/Tabs/index.js +3 -0
- package/dist/components/index.d.ts +15 -7
- package/dist/components/index.js +15 -10
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types/upload-provider.d.ts +13 -0
- package/dist/utils/bundle-component.d.ts +1 -1
- package/dist/utils/bundle-component.js +5 -8
- package/dist/utils/component-loader.d.ts +3 -0
- package/dist/utils/component-loader.js +10 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +12 -11
- package/src/components/ColorStatus/ColorStatusEdit.d.ts +0 -5
- package/src/components/ColorStatus/ColorStatusEdit.js +0 -58
- package/src/components/ColorStatus/ColorStatusEdit.tsx +0 -94
- package/src/components/ColorStatus/ColorStatusList.d.ts +0 -4
- package/src/components/ColorStatus/ColorStatusList.js +0 -7
- package/src/components/ColorStatus/ColorStatusList.tsx +0 -20
- package/src/components/ColorStatus/ColorStatusShow.d.ts +0 -4
- package/src/components/ColorStatus/ColorStatusShow.js +0 -9
- package/src/components/ColorStatus/ColorStatusShow.tsx +0 -23
- package/src/components/ColorStatus/index.d.ts +0 -3
- package/src/components/ColorStatus/index.js +0 -3
- package/src/components/ColorStatus/index.ts +0 -3
- package/src/components/ColorStatus/styles.d.ts +0 -5
- package/src/components/ColorStatus/styles.js +0 -25
- package/src/components/ColorStatus/styles.ts +0 -30
- package/src/components/ColorStatus/types.d.ts +0 -5
- package/src/components/ColorStatus/types.ts +0 -5
- package/src/components/Editor/Editor.d.ts +0 -8
- package/src/components/Editor/Editor.js +0 -36
- package/src/components/Editor/Editor.jsx +0 -49
- package/src/components/Editor/EditorList.d.ts +0 -6
- package/src/components/Editor/EditorList.js +0 -11
- package/src/components/Editor/EditorList.jsx +0 -22
- package/src/components/Editor/EditorShow.d.ts +0 -6
- package/src/components/Editor/EditorShow.js +0 -13
- package/src/components/Editor/EditorShow.jsx +0 -24
- package/src/components/Editor/config.d.ts +0 -29
- package/src/components/Editor/config.js +0 -34
- package/src/components/Editor/config.ts +0 -35
- package/src/components/Editor/index.d.ts +0 -3
- package/src/components/Editor/index.js +0 -3
- package/src/components/Editor/index.ts +0 -3
- package/src/components/Editor/styles.d.ts +0 -6
- package/src/components/Editor/styles.js +0 -145
- package/src/components/Editor/styles.ts +0 -151
- package/src/components/Slug/SlugEdit.d.ts +0 -5
- package/src/components/Slug/SlugEdit.js +0 -27
- package/src/components/Slug/SlugEdit.tsx +0 -68
- package/src/components/Slug/SlugFeature.d.ts +0 -7
- package/src/components/Slug/SlugFeature.js +0 -21
- package/src/components/Slug/SlugFeature.ts +0 -30
- package/src/components/Slug/index.d.ts +0 -1
- package/src/components/Slug/index.js +0 -1
- package/src/components/Slug/index.ts +0 -1
- package/src/components/Slug/styles.d.ts +0 -4
- package/src/components/Slug/styles.js +0 -20
- package/src/components/Slug/styles.ts +0 -24
- package/src/components/StringList/SortableList/SortableList.d.ts +0 -18
- package/src/components/StringList/SortableList/SortableList.js +0 -37
- package/src/components/StringList/SortableList/SortableList.tsx +0 -98
- package/src/components/StringList/SortableList/components/SortableItem/DragHandle.d.ts +0 -7
- package/src/components/StringList/SortableList/components/SortableItem/DragHandle.js +0 -8
- package/src/components/StringList/SortableList/components/SortableItem/DragHandle.tsx +0 -20
- package/src/components/StringList/SortableList/components/SortableItem/SortableItem.d.ts +0 -8
- package/src/components/StringList/SortableList/components/SortableItem/SortableItem.js +0 -28
- package/src/components/StringList/SortableList/components/SortableItem/SortableItem.tsx +0 -59
- package/src/components/StringList/SortableList/components/SortableItem/styles.d.ts +0 -2
- package/src/components/StringList/SortableList/components/SortableItem/styles.js +0 -20
- package/src/components/StringList/SortableList/components/SortableItem/styles.ts +0 -22
- package/src/components/StringList/SortableList/components/SortableItem/types.d.ts +0 -6
- package/src/components/StringList/SortableList/components/SortableItem/types.ts +0 -7
- package/src/components/StringList/SortableList/components/index.d.ts +0 -2
- package/src/components/StringList/SortableList/components/index.js +0 -2
- package/src/components/StringList/SortableList/components/index.ts +0 -2
- package/src/components/StringList/SortableList/index.d.ts +0 -1
- package/src/components/StringList/SortableList/index.js +0 -1
- package/src/components/StringList/SortableList/index.ts +0 -1
- package/src/components/StringList/SortableList/styles.d.ts +0 -1
- package/src/components/StringList/SortableList/styles.js +0 -8
- package/src/components/StringList/SortableList/styles.ts +0 -9
- package/src/components/StringList/StringList.d.ts +0 -8
- package/src/components/StringList/StringList.js +0 -60
- package/src/components/StringList/StringList.tsx +0 -136
- package/src/components/StringList/StringListShow.d.ts +0 -7
- package/src/components/StringList/StringListShow.js +0 -14
- package/src/components/StringList/StringListShow.tsx +0 -37
- package/src/components/StringList/constants.d.ts +0 -1
- package/src/components/StringList/constants.js +0 -1
- package/src/components/StringList/constants.ts +0 -1
- package/src/components/StringList/index.d.ts +0 -2
- package/src/components/StringList/index.js +0 -2
- package/src/components/StringList/index.ts +0 -2
- package/src/components/StringList/styles.d.ts +0 -8
- package/src/components/StringList/styles.js +0 -33
- package/src/components/StringList/styles.ts +0 -41
- package/src/components/index.d.ts +0 -7
- package/src/components/index.js +0 -10
- package/src/components/index.ts +0 -10
- /package/{src/components/ColorStatus/types.js → dist/components/Slug/SlugOptions.type.js} +0 -0
- /package/{src/components/StringList/SortableList/components/SortableItem/types.js → dist/types/upload-provider.js} +0 -0
package/README.md
CHANGED
|
@@ -1 +1,162 @@
|
|
|
1
|
-
# adminjs-components
|
|
1
|
+
# adminjs-components
|
|
2
|
+
|
|
3
|
+
Prebuilt AdminJS components and features for common UI needs: colored status
|
|
4
|
+
badges, slug generation, Editor.js content, sortable string lists, tabs layout,
|
|
5
|
+
and record preview.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @rulab/adminjs-components
|
|
11
|
+
# or
|
|
12
|
+
npm install @rulab/adminjs-components
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import AdminJS from "adminjs";
|
|
19
|
+
import { ComponentLoader } from "adminjs";
|
|
20
|
+
import { setComponentLoader, ColorStatusFeature } from "@rulab/adminjs-components";
|
|
21
|
+
|
|
22
|
+
const componentLoader = new ComponentLoader();
|
|
23
|
+
setComponentLoader(componentLoader);
|
|
24
|
+
|
|
25
|
+
const admin = new AdminJS({
|
|
26
|
+
componentLoader,
|
|
27
|
+
resources: [
|
|
28
|
+
{
|
|
29
|
+
resource: YourResource,
|
|
30
|
+
features: [
|
|
31
|
+
ColorStatusFeature({
|
|
32
|
+
key: "status",
|
|
33
|
+
availableValues: [
|
|
34
|
+
{ value: "draft", label: "Draft", color: "#64748b" },
|
|
35
|
+
{ value: "published", label: "Published", color: "#16a34a" },
|
|
36
|
+
],
|
|
37
|
+
}),
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
You can also pass `componentLoader` into every feature instead of calling
|
|
45
|
+
`setComponentLoader`.
|
|
46
|
+
|
|
47
|
+
## Components and features
|
|
48
|
+
|
|
49
|
+
### ColorStatus
|
|
50
|
+
|
|
51
|
+
Renders a colored badge in edit/list/show based on a list of available values.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { ColorStatusFeature } from "@rulab/adminjs-components";
|
|
55
|
+
|
|
56
|
+
features: [
|
|
57
|
+
ColorStatusFeature({
|
|
58
|
+
key: "status",
|
|
59
|
+
availableValues: [
|
|
60
|
+
{ value: "draft", label: "Draft", color: "#64748b" },
|
|
61
|
+
{ value: "review", label: "In review", color: "#f59e0b" },
|
|
62
|
+
{ value: "published", label: "Published", color: "#16a34a" },
|
|
63
|
+
],
|
|
64
|
+
}),
|
|
65
|
+
]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Slug
|
|
69
|
+
|
|
70
|
+
Generates a slug from another field and stores it in the target property.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { SlugFeature } from "@rulab/adminjs-components";
|
|
74
|
+
|
|
75
|
+
features: [
|
|
76
|
+
SlugFeature({
|
|
77
|
+
key: "slug",
|
|
78
|
+
source: "title",
|
|
79
|
+
}),
|
|
80
|
+
]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Editor
|
|
84
|
+
|
|
85
|
+
Editor.js field for rich content. Supports optional image upload via
|
|
86
|
+
`@adminjs/upload` provider.
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { EditorFeature } from "@rulab/adminjs-components";
|
|
90
|
+
import { BaseProvider } from "@adminjs/upload";
|
|
91
|
+
|
|
92
|
+
const uploadProvider = new BaseProvider({
|
|
93
|
+
bucket: "my-bucket",
|
|
94
|
+
baseUrl: "https://cdn.example.com",
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
features: [
|
|
98
|
+
EditorFeature({
|
|
99
|
+
key: "content",
|
|
100
|
+
uploadProvider, // optional
|
|
101
|
+
}),
|
|
102
|
+
]
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### StringList
|
|
106
|
+
|
|
107
|
+
Sortable list stored as a single string (comma-separated by default).
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { StringListFeature } from "@rulab/adminjs-components";
|
|
111
|
+
|
|
112
|
+
features: [
|
|
113
|
+
StringListFeature({
|
|
114
|
+
key: "facts",
|
|
115
|
+
}),
|
|
116
|
+
]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Tabs
|
|
120
|
+
|
|
121
|
+
Groups edit/show fields into tabs based on property `props.tab` or
|
|
122
|
+
`custom.tab`. Fields without a tab go to a common group.
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import { TabsFeature } from "@rulab/adminjs-components";
|
|
126
|
+
|
|
127
|
+
features: [
|
|
128
|
+
TabsFeature({
|
|
129
|
+
commonTabLabel: "Common",
|
|
130
|
+
}),
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
// resource options example
|
|
134
|
+
properties: {
|
|
135
|
+
title: { props: { tab: "Main" } },
|
|
136
|
+
description: { props: { tab: "Main" } },
|
|
137
|
+
seoTitle: { props: { tab: "SEO" } },
|
|
138
|
+
seoDescription: { props: { tab: "SEO" } },
|
|
139
|
+
},
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Preview
|
|
143
|
+
|
|
144
|
+
Adds a record action that renders an iframe preview. The `url` can include
|
|
145
|
+
template variables like `$id` or `$slug`.
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
import { PreviewFeature } from "@rulab/adminjs-components";
|
|
149
|
+
|
|
150
|
+
features: [
|
|
151
|
+
PreviewFeature({
|
|
152
|
+
url: "https://example.com/posts/$id",
|
|
153
|
+
actionName: "preview", // optional
|
|
154
|
+
}),
|
|
155
|
+
]
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Utilities
|
|
159
|
+
|
|
160
|
+
- `setComponentLoader(loader)` and `getComponentLoader()` to reuse a single
|
|
161
|
+
AdminJS `ComponentLoader`.
|
|
162
|
+
- `parseHtml(html)` helper to transform stored Editor.js output into HTML.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FC } from "react";
|
|
2
2
|
import { EditPropertyProps } from "adminjs";
|
|
3
3
|
type ColorStatusTypes = Omit<EditPropertyProps, "where" | "resource">;
|
|
4
|
-
declare const
|
|
5
|
-
export default
|
|
4
|
+
export declare const ColorStatusEdit: FC<ColorStatusTypes>;
|
|
5
|
+
export default ColorStatusEdit;
|
|
@@ -41,7 +41,7 @@ const colorStyles = {
|
|
|
41
41
|
placeholder: (styles) => ({ ...styles, ...dot("#ccc") }),
|
|
42
42
|
singleValue: (styles, { data }) => ({ ...styles, ...dot(data.color) }),
|
|
43
43
|
};
|
|
44
|
-
const
|
|
44
|
+
export const ColorStatusEdit = ({ property, record, onChange, }) => {
|
|
45
45
|
const availableValues = property.availableValues;
|
|
46
46
|
const currentOption = availableValues.find((item) => item.value === record.params[property.path]);
|
|
47
47
|
const [selectOption, setCurrentOption] = useState(currentOption);
|
|
@@ -52,7 +52,7 @@ const ColorStatus = ({ property, record, onChange }) => {
|
|
|
52
52
|
onChange(property.path, selectOption?.value);
|
|
53
53
|
}, [selectOption]);
|
|
54
54
|
return (React.createElement(ColorStatusWrapper, null,
|
|
55
|
-
React.createElement(Label, null, property.path),
|
|
55
|
+
React.createElement(Label, null, property.label ?? property.path),
|
|
56
56
|
React.createElement(Select, { className: "basic-single", classNamePrefix: "select", defaultValue: selectOption ?? availableValues[0], onChange: handleSelectChange, isClearable: true, name: "color", options: availableValues, styles: colorStyles })));
|
|
57
57
|
};
|
|
58
|
-
export default
|
|
58
|
+
export default ColorStatusEdit;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ComponentLoader, FeatureType } from "adminjs";
|
|
2
|
+
import type { AvailableValueType } from "./types.js";
|
|
3
|
+
type ColorStatusOptions = {
|
|
4
|
+
componentLoader?: ComponentLoader;
|
|
5
|
+
key: string;
|
|
6
|
+
availableValues?: AvailableValueType[];
|
|
7
|
+
};
|
|
8
|
+
export declare const ColorStatusFeature: (config: ColorStatusOptions) => FeatureType;
|
|
9
|
+
export default ColorStatusFeature;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { buildFeature } from "adminjs";
|
|
2
|
+
import { bundleComponent } from "../../utils/bundle-component.js";
|
|
3
|
+
const COMPONENT_NAME = "ColorStatus";
|
|
4
|
+
export const ColorStatusFeature = (config) => {
|
|
5
|
+
const { componentLoader, key, availableValues = [] } = config;
|
|
6
|
+
const editComponent = bundleComponent(componentLoader, COMPONENT_NAME, "ColorStatusEdit.js");
|
|
7
|
+
const listComponent = bundleComponent(componentLoader, COMPONENT_NAME, "ColorStatusList.js");
|
|
8
|
+
const showComponent = bundleComponent(componentLoader, COMPONENT_NAME, "ColorStatusShow.js");
|
|
9
|
+
return buildFeature({
|
|
10
|
+
properties: {
|
|
11
|
+
[key]: {
|
|
12
|
+
isVisible: { filter: true, show: true, edit: true, list: true },
|
|
13
|
+
availableValues,
|
|
14
|
+
components: {
|
|
15
|
+
edit: editComponent,
|
|
16
|
+
list: listComponent,
|
|
17
|
+
show: showComponent,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
export default ColorStatusFeature;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { ColorStatusBadge } from "./styles.js";
|
|
3
|
-
const ColorStatusList = ({ property, record }) => {
|
|
3
|
+
export const ColorStatusList = ({ property, record }) => {
|
|
4
4
|
const currentOption = property.availableValues?.find((item) => item.value === record.params[property.path]);
|
|
5
|
-
|
|
5
|
+
const currentValue = record.params[property.path];
|
|
6
|
+
const displayValue = currentOption?.label ?? currentValue;
|
|
7
|
+
return (React.createElement(ColorStatusBadge, { color: currentOption?.color }, displayValue));
|
|
6
8
|
};
|
|
7
9
|
export default ColorStatusList;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { ColorStatusBadgeWrapper, ColorStatusBadge, ShowLabel } from "./styles.js";
|
|
3
|
-
const ColorStatusShow = ({ property, record }) => {
|
|
3
|
+
export const ColorStatusShow = ({ property, record }) => {
|
|
4
4
|
const currentOption = property.availableValues?.find((item) => item.value === record.params[property.path]);
|
|
5
|
+
const currentValue = record.params[property.path];
|
|
6
|
+
const displayValue = currentOption?.label ?? currentValue;
|
|
5
7
|
return (React.createElement(ColorStatusBadgeWrapper, null,
|
|
6
|
-
React.createElement(ShowLabel, null, property.path),
|
|
7
|
-
React.createElement(ColorStatusBadge, { color: currentOption
|
|
8
|
+
React.createElement(ShowLabel, null, property.label ?? property.path),
|
|
9
|
+
React.createElement(ColorStatusBadge, { color: currentOption?.color }, displayValue)));
|
|
8
10
|
};
|
|
9
11
|
export default ColorStatusShow;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
1
|
+
export { ColorStatusEdit } from "./ColorStatusEdit.js";
|
|
2
|
+
export { ColorStatusShow } from "./ColorStatusShow.js";
|
|
3
|
+
export { ColorStatusList } from "./ColorStatusList.js";
|
|
4
|
+
export { ColorStatusFeature } from "./ColorStatusFeature.js";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
1
|
+
export { ColorStatusEdit } from "./ColorStatusEdit.js";
|
|
2
|
+
export { ColorStatusShow } from "./ColorStatusShow.js";
|
|
3
|
+
export { ColorStatusList } from "./ColorStatusList.js";
|
|
4
|
+
export { ColorStatusFeature } from "./ColorStatusFeature.js";
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { styled } from "@adminjs/design-system/styled-components";
|
|
2
2
|
export const ColorStatusWrapper = styled.div `
|
|
3
|
-
margin-bottom:
|
|
3
|
+
margin-bottom: 45px;
|
|
4
4
|
`;
|
|
5
5
|
export const Label = styled.div `
|
|
6
6
|
font-size: 12px;
|
|
7
7
|
margin-bottom: 8px;
|
|
8
|
+
text-transform: capitalize;
|
|
8
9
|
`;
|
|
9
10
|
export const ShowLabel = styled.div `
|
|
10
11
|
font-size: 12px;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
export
|
|
2
|
-
declare function Editor({ property, record, onChangeAdmin, editorId }: {
|
|
1
|
+
export function Editor({ property, record, resource, onChange, onChangeAdmin, editorId }: {
|
|
3
2
|
property: any;
|
|
4
3
|
record: any;
|
|
4
|
+
resource: any;
|
|
5
|
+
onChange: any;
|
|
5
6
|
onChangeAdmin: any;
|
|
6
7
|
editorId: any;
|
|
7
8
|
}): React.FunctionComponentElement<{
|
|
8
9
|
children?: React.ReactNode;
|
|
9
10
|
theme: import("styled-components").DefaultTheme | ((outerTheme?: import("styled-components").DefaultTheme | undefined) => import("styled-components").DefaultTheme);
|
|
10
11
|
}>;
|
|
12
|
+
export default Editor;
|
|
11
13
|
import React from "react";
|
|
@@ -1,33 +1,96 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useState, useEffect, useRef } from "react";
|
|
2
2
|
import { ThemeProvider } from "styled-components";
|
|
3
|
-
import EditorJS from "@editorjs/editorjs";
|
|
4
3
|
import { theme } from "@adminjs/design-system";
|
|
4
|
+
import { ApiClient } from "adminjs";
|
|
5
5
|
import { StyledLabel, StyledEditor, StyledEditorWrapper } from "./styles.js";
|
|
6
6
|
import { EDITOR_TOOLS } from "./config.js";
|
|
7
|
-
const
|
|
7
|
+
const readFileAsBase64 = (file) => new Promise((resolve, reject) => {
|
|
8
|
+
const reader = new FileReader();
|
|
9
|
+
reader.onload = () => {
|
|
10
|
+
const result = reader.result ?? "";
|
|
11
|
+
const base64 = String(result).split(",")[1] ?? "";
|
|
12
|
+
resolve(base64);
|
|
13
|
+
};
|
|
14
|
+
reader.onerror = reject;
|
|
15
|
+
reader.readAsDataURL(file);
|
|
16
|
+
});
|
|
17
|
+
const getEditorData = (record, property) => {
|
|
18
|
+
const raw = record?.params?.[property.path];
|
|
19
|
+
if (!raw) {
|
|
20
|
+
return "";
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(raw);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return "";
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
export const Editor = ({ property, record, resource, onChange, onChangeAdmin, editorId }) => {
|
|
8
30
|
const [jsonData, setJsonData] = useState();
|
|
9
|
-
const isSavedData = Boolean(record
|
|
31
|
+
const isSavedData = Boolean(record?.params?.[property.path]);
|
|
32
|
+
const holderId = editorId || property?.props?.editorId || `editor-${property.path}`;
|
|
33
|
+
const uploadAction = property?.props?.uploadAction;
|
|
34
|
+
const resourceId = resource?.id;
|
|
10
35
|
const ref = useRef();
|
|
11
36
|
useEffect(() => {
|
|
12
|
-
onChangeAdmin
|
|
37
|
+
const changeHandler = onChange ?? onChangeAdmin;
|
|
38
|
+
if (changeHandler) {
|
|
39
|
+
changeHandler(property.path, jsonData);
|
|
40
|
+
}
|
|
13
41
|
}, [jsonData]);
|
|
14
42
|
useEffect(() => {
|
|
15
43
|
if (!ref.current) {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
tools
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
44
|
+
const init = async () => {
|
|
45
|
+
const { default: EditorJS } = await import("@editorjs/editorjs");
|
|
46
|
+
const tools = { ...EDITOR_TOOLS };
|
|
47
|
+
if (uploadAction && resourceId) {
|
|
48
|
+
const { default: ImageTool } = await import("@editorjs/image");
|
|
49
|
+
const api = new ApiClient();
|
|
50
|
+
tools.image = {
|
|
51
|
+
class: ImageTool,
|
|
52
|
+
config: {
|
|
53
|
+
uploader: {
|
|
54
|
+
uploadByFile: async (file) => {
|
|
55
|
+
const base64 = await readFileAsBase64(file);
|
|
56
|
+
const response = await api.resourceAction({
|
|
57
|
+
resourceId,
|
|
58
|
+
actionName: uploadAction,
|
|
59
|
+
data: {
|
|
60
|
+
file: {
|
|
61
|
+
name: file.name,
|
|
62
|
+
type: file.type,
|
|
63
|
+
base64,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
const url = response?.data?.data?.url;
|
|
68
|
+
if (!url) {
|
|
69
|
+
return { success: 0 };
|
|
70
|
+
}
|
|
71
|
+
return { success: 1, file: { url } };
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const editor = new EditorJS({
|
|
78
|
+
holder: holderId,
|
|
79
|
+
tools,
|
|
80
|
+
data: isSavedData ? getEditorData(record, property) : "",
|
|
81
|
+
async onChange(api, event) {
|
|
82
|
+
const data = await api.saver.save();
|
|
83
|
+
setJsonData(JSON.stringify(data));
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
ref.current = editor;
|
|
87
|
+
};
|
|
88
|
+
void init();
|
|
26
89
|
}
|
|
27
90
|
return () => {
|
|
28
91
|
ref?.current?.destroy?.();
|
|
29
92
|
};
|
|
30
93
|
}, []);
|
|
31
|
-
return (React.createElement(ThemeProvider, { theme: theme }, React.createElement(StyledLabel, null, property.path), React.createElement(StyledEditorWrapper, null, React.createElement(StyledEditor, { id:
|
|
94
|
+
return (React.createElement(ThemeProvider, { theme: theme }, React.createElement(StyledLabel, null, property.label ?? property.path), React.createElement(StyledEditorWrapper, null, React.createElement(StyledEditor, { id: holderId }))));
|
|
32
95
|
};
|
|
33
96
|
export default Editor;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ComponentLoader, FeatureType } from "adminjs";
|
|
2
|
+
import type { BaseProvider } from "@adminjs/upload";
|
|
3
|
+
type EditorOptions = {
|
|
4
|
+
componentLoader?: ComponentLoader;
|
|
5
|
+
key: string;
|
|
6
|
+
uploadProvider?: BaseProvider;
|
|
7
|
+
uploadActionName?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare const EditorFeature: (config: EditorOptions) => FeatureType;
|
|
10
|
+
export default EditorFeature;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { buildFeature } from "adminjs";
|
|
2
|
+
import { Buffer } from "node:buffer";
|
|
3
|
+
import { promises as fs } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { bundleComponent } from "../../utils/bundle-component.js";
|
|
7
|
+
import { slugifyTitle } from "../../utils/slugifyTitle.js";
|
|
8
|
+
const COMPONENT_NAME = "Editor";
|
|
9
|
+
export const EditorFeature = (config) => {
|
|
10
|
+
const { componentLoader, key, uploadProvider } = config;
|
|
11
|
+
const uploadActionName = config.uploadActionName ?? "editorUpload";
|
|
12
|
+
const editComponent = bundleComponent(componentLoader, COMPONENT_NAME, "Editor.js");
|
|
13
|
+
const listComponent = bundleComponent(componentLoader, COMPONENT_NAME, "EditorList.js");
|
|
14
|
+
const showComponent = bundleComponent(componentLoader, COMPONENT_NAME, "EditorShow.js");
|
|
15
|
+
return buildFeature({
|
|
16
|
+
actions: uploadProvider
|
|
17
|
+
? {
|
|
18
|
+
[uploadActionName]: {
|
|
19
|
+
actionType: "resource",
|
|
20
|
+
isVisible: false,
|
|
21
|
+
handler: async (request, _response, context) => {
|
|
22
|
+
const file = request?.payload?.file;
|
|
23
|
+
if (!file?.base64) {
|
|
24
|
+
return {
|
|
25
|
+
data: { error: "No file provided." },
|
|
26
|
+
notice: { message: "No file provided.", type: "error" },
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const buffer = Buffer.from(file.base64, "base64");
|
|
30
|
+
const originalName = file.name ?? "upload";
|
|
31
|
+
const filename = slugifyFilename(originalName);
|
|
32
|
+
const key = `${Date.now()}-${filename}`;
|
|
33
|
+
const tempPath = join(tmpdir(), `${Date.now()}-${filename}`);
|
|
34
|
+
const uploadedFile = {
|
|
35
|
+
name: filename,
|
|
36
|
+
type: file.type ?? "application/octet-stream",
|
|
37
|
+
size: buffer.length,
|
|
38
|
+
path: tempPath,
|
|
39
|
+
};
|
|
40
|
+
await fs.writeFile(tempPath, new Uint8Array(buffer));
|
|
41
|
+
try {
|
|
42
|
+
await uploadProvider.upload(uploadedFile, key, context);
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
await fs.unlink(tempPath).catch(() => undefined);
|
|
46
|
+
}
|
|
47
|
+
const baseUrl = uploadProvider.opts?.baseUrl;
|
|
48
|
+
const url = baseUrl
|
|
49
|
+
? `${baseUrl.replace(/\/$/, "")}/${key}`
|
|
50
|
+
: await uploadProvider.path(key, uploadProvider.bucket, context);
|
|
51
|
+
if (!url) {
|
|
52
|
+
return {
|
|
53
|
+
data: { error: "Upload failed." },
|
|
54
|
+
notice: { message: "Upload failed.", type: "error" },
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return { data: { url } };
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
: undefined,
|
|
62
|
+
properties: {
|
|
63
|
+
[key]: {
|
|
64
|
+
isVisible: { filter: true, show: true, edit: true, list: true },
|
|
65
|
+
props: uploadProvider ? { uploadAction: uploadActionName } : undefined,
|
|
66
|
+
components: {
|
|
67
|
+
edit: editComponent,
|
|
68
|
+
list: listComponent,
|
|
69
|
+
show: showComponent,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
const slugifyFilename = (name) => {
|
|
76
|
+
const lastDot = name.lastIndexOf(".");
|
|
77
|
+
const base = lastDot > 0 ? name.slice(0, lastDot) : name;
|
|
78
|
+
const ext = lastDot > 0 ? name.slice(lastDot) : "";
|
|
79
|
+
const slug = slugifyTitle(base) || "file";
|
|
80
|
+
return `${slug}${ext}`;
|
|
81
|
+
};
|
|
82
|
+
export default EditorFeature;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export
|
|
2
|
-
declare function EditorList({ property, record }: {
|
|
1
|
+
export function EditorList({ property, record }: {
|
|
3
2
|
property: any;
|
|
4
3
|
record: any;
|
|
5
4
|
}): React.FunctionComponentElement<{
|
|
6
5
|
children?: React.ReactNode;
|
|
7
6
|
theme: import("styled-components").DefaultTheme | ((outerTheme?: import("styled-components").DefaultTheme | undefined) => import("styled-components").DefaultTheme);
|
|
8
7
|
}>;
|
|
8
|
+
export default EditorList;
|
|
9
9
|
import React from "react";
|
|
@@ -3,7 +3,7 @@ import { theme } from "@adminjs/design-system";
|
|
|
3
3
|
import { ThemeProvider } from "styled-components";
|
|
4
4
|
import { parseHtml } from "../../utils/parseHtml.js";
|
|
5
5
|
import { StyledEditorViewWrapper } from "./styles.js";
|
|
6
|
-
const EditorList = ({ property, record }) => {
|
|
6
|
+
export const EditorList = ({ property, record }) => {
|
|
7
7
|
const htmlContent = parseHtml(record.params[property.path]);
|
|
8
8
|
return (React.createElement(ThemeProvider, { theme: theme }, React.createElement(StyledEditorViewWrapper, null, htmlContent && (React.createElement("div", { dangerouslySetInnerHTML: { __html: htmlContent } })))));
|
|
9
9
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export
|
|
2
|
-
declare function EditorShow({ property, record }: {
|
|
1
|
+
export function EditorShow({ property, record }: {
|
|
3
2
|
property: any;
|
|
4
3
|
record: any;
|
|
5
4
|
}): React.FunctionComponentElement<{
|
|
6
5
|
children?: React.ReactNode;
|
|
7
6
|
theme: import("styled-components").DefaultTheme | ((outerTheme?: import("styled-components").DefaultTheme | undefined) => import("styled-components").DefaultTheme);
|
|
8
7
|
}>;
|
|
8
|
+
export default EditorShow;
|
|
9
9
|
import React from "react";
|
|
@@ -3,8 +3,8 @@ import { ThemeProvider } from "styled-components";
|
|
|
3
3
|
import { theme } from "@adminjs/design-system";
|
|
4
4
|
import { parseHtml } from "../../utils/parseHtml.js";
|
|
5
5
|
import { StyledEditorShowWrapper, StyledShowLabel } from "./styles.js";
|
|
6
|
-
const EditorShow = ({ property, record }) => {
|
|
6
|
+
export const EditorShow = ({ property, record }) => {
|
|
7
7
|
const htmlContent = parseHtml(record.params[property.path]);
|
|
8
|
-
return (React.createElement(ThemeProvider, { theme: theme }, React.createElement(StyledEditorShowWrapper, null, React.createElement(StyledShowLabel, null, property.path), htmlContent && (React.createElement("div", { dangerouslySetInnerHTML: { __html: htmlContent } })))));
|
|
8
|
+
return (React.createElement(ThemeProvider, { theme: theme }, React.createElement(StyledEditorShowWrapper, null, React.createElement(StyledShowLabel, null, property.label ?? property.path), htmlContent && (React.createElement("div", { dangerouslySetInnerHTML: { __html: htmlContent } })))));
|
|
9
9
|
};
|
|
10
10
|
export default EditorShow;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
1
|
+
export { Editor } from "./Editor.js";
|
|
2
|
+
export { EditorShow } from "./EditorShow.js";
|
|
3
|
+
export { EditorList } from "./EditorList.js";
|
|
4
|
+
export { EditorFeature } from "./EditorFeature.js";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
1
|
+
export { Editor } from "./Editor.js";
|
|
2
|
+
export { EditorShow } from "./EditorShow.js";
|
|
3
|
+
export { EditorList } from "./EditorList.js";
|
|
4
|
+
export { EditorFeature } from "./EditorFeature.js";
|