@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.
Files changed (160) hide show
  1. package/README.md +162 -1
  2. package/dist/components/ColorStatus/ColorStatusEdit.d.ts +2 -2
  3. package/dist/components/ColorStatus/ColorStatusEdit.js +3 -3
  4. package/dist/components/ColorStatus/ColorStatusFeature.d.ts +9 -0
  5. package/dist/components/ColorStatus/ColorStatusFeature.js +23 -0
  6. package/dist/components/ColorStatus/ColorStatusList.d.ts +1 -1
  7. package/dist/components/ColorStatus/ColorStatusList.js +4 -2
  8. package/dist/components/ColorStatus/ColorStatusShow.d.ts +1 -1
  9. package/dist/components/ColorStatus/ColorStatusShow.js +5 -3
  10. package/dist/components/ColorStatus/index.d.ts +4 -3
  11. package/dist/components/ColorStatus/index.js +4 -3
  12. package/dist/components/ColorStatus/styles.js +2 -1
  13. package/dist/components/Editor/Editor.d.ts +4 -2
  14. package/dist/components/Editor/Editor.js +79 -16
  15. package/dist/components/Editor/EditorFeature.d.ts +10 -0
  16. package/dist/components/Editor/EditorFeature.js +82 -0
  17. package/dist/components/Editor/EditorList.d.ts +2 -2
  18. package/dist/components/Editor/EditorList.js +1 -1
  19. package/dist/components/Editor/EditorShow.d.ts +2 -2
  20. package/dist/components/Editor/EditorShow.js +2 -2
  21. package/dist/components/Editor/index.d.ts +4 -3
  22. package/dist/components/Editor/index.js +4 -3
  23. package/dist/components/FeatureTabs/FeatureTabsEdit.d.ts +4 -0
  24. package/dist/components/FeatureTabs/FeatureTabsEdit.js +105 -0
  25. package/dist/components/FeatureTabs/FeatureTabsFeature.d.ts +8 -0
  26. package/dist/components/FeatureTabs/FeatureTabsFeature.js +28 -0
  27. package/dist/components/FeatureTabs/FeatureTabsShow.d.ts +4 -0
  28. package/dist/components/FeatureTabs/FeatureTabsShow.js +61 -0
  29. package/dist/components/FeatureTabs/index.d.ts +4 -0
  30. package/dist/components/FeatureTabs/index.js +4 -0
  31. package/dist/components/Preview/PreviewAction.d.ts +4 -0
  32. package/dist/components/Preview/PreviewAction.js +22 -0
  33. package/dist/components/Preview/PreviewFeature.d.ts +9 -0
  34. package/dist/components/Preview/PreviewFeature.js +31 -0
  35. package/dist/components/Preview/index.d.ts +2 -0
  36. package/dist/components/Preview/index.js +2 -0
  37. package/dist/components/Slug/SlugEdit.d.ts +1 -1
  38. package/dist/components/Slug/SlugEdit.js +8 -3
  39. package/dist/components/Slug/SlugFeature.d.ts +3 -6
  40. package/dist/components/Slug/SlugFeature.js +12 -14
  41. package/dist/components/Slug/SlugOptions.type.d.ts +7 -0
  42. package/dist/components/StringList/StringList.d.ts +1 -1
  43. package/dist/components/StringList/StringList.js +4 -4
  44. package/dist/components/StringList/StringListFeature.d.ts +7 -0
  45. package/dist/components/StringList/StringListFeature.js +20 -0
  46. package/dist/components/StringList/StringListShow.d.ts +1 -1
  47. package/dist/components/StringList/StringListShow.js +2 -2
  48. package/dist/components/StringList/index.d.ts +3 -2
  49. package/dist/components/StringList/index.js +3 -2
  50. package/dist/components/StringList/styles.js +2 -1
  51. package/dist/components/Tabs/TabsEdit.d.ts +4 -0
  52. package/dist/components/Tabs/TabsEdit.js +103 -0
  53. package/dist/components/Tabs/TabsFeature.d.ts +7 -0
  54. package/dist/components/Tabs/TabsFeature.js +26 -0
  55. package/dist/components/Tabs/TabsShow.d.ts +4 -0
  56. package/dist/components/Tabs/TabsShow.js +61 -0
  57. package/dist/components/Tabs/index.d.ts +3 -0
  58. package/dist/components/Tabs/index.js +3 -0
  59. package/dist/components/index.d.ts +15 -7
  60. package/dist/components/index.js +15 -10
  61. package/dist/index.d.ts +1 -0
  62. package/dist/index.js +1 -0
  63. package/dist/types/upload-provider.d.ts +13 -0
  64. package/dist/utils/bundle-component.d.ts +1 -1
  65. package/dist/utils/bundle-component.js +5 -8
  66. package/dist/utils/component-loader.d.ts +3 -0
  67. package/dist/utils/component-loader.js +10 -0
  68. package/dist/utils/index.d.ts +1 -0
  69. package/dist/utils/index.js +1 -0
  70. package/package.json +12 -11
  71. package/src/components/ColorStatus/ColorStatusEdit.d.ts +0 -5
  72. package/src/components/ColorStatus/ColorStatusEdit.js +0 -58
  73. package/src/components/ColorStatus/ColorStatusEdit.tsx +0 -94
  74. package/src/components/ColorStatus/ColorStatusList.d.ts +0 -4
  75. package/src/components/ColorStatus/ColorStatusList.js +0 -7
  76. package/src/components/ColorStatus/ColorStatusList.tsx +0 -20
  77. package/src/components/ColorStatus/ColorStatusShow.d.ts +0 -4
  78. package/src/components/ColorStatus/ColorStatusShow.js +0 -9
  79. package/src/components/ColorStatus/ColorStatusShow.tsx +0 -23
  80. package/src/components/ColorStatus/index.d.ts +0 -3
  81. package/src/components/ColorStatus/index.js +0 -3
  82. package/src/components/ColorStatus/index.ts +0 -3
  83. package/src/components/ColorStatus/styles.d.ts +0 -5
  84. package/src/components/ColorStatus/styles.js +0 -25
  85. package/src/components/ColorStatus/styles.ts +0 -30
  86. package/src/components/ColorStatus/types.d.ts +0 -5
  87. package/src/components/ColorStatus/types.ts +0 -5
  88. package/src/components/Editor/Editor.d.ts +0 -8
  89. package/src/components/Editor/Editor.js +0 -36
  90. package/src/components/Editor/Editor.jsx +0 -49
  91. package/src/components/Editor/EditorList.d.ts +0 -6
  92. package/src/components/Editor/EditorList.js +0 -11
  93. package/src/components/Editor/EditorList.jsx +0 -22
  94. package/src/components/Editor/EditorShow.d.ts +0 -6
  95. package/src/components/Editor/EditorShow.js +0 -13
  96. package/src/components/Editor/EditorShow.jsx +0 -24
  97. package/src/components/Editor/config.d.ts +0 -29
  98. package/src/components/Editor/config.js +0 -34
  99. package/src/components/Editor/config.ts +0 -35
  100. package/src/components/Editor/index.d.ts +0 -3
  101. package/src/components/Editor/index.js +0 -3
  102. package/src/components/Editor/index.ts +0 -3
  103. package/src/components/Editor/styles.d.ts +0 -6
  104. package/src/components/Editor/styles.js +0 -145
  105. package/src/components/Editor/styles.ts +0 -151
  106. package/src/components/Slug/SlugEdit.d.ts +0 -5
  107. package/src/components/Slug/SlugEdit.js +0 -27
  108. package/src/components/Slug/SlugEdit.tsx +0 -68
  109. package/src/components/Slug/SlugFeature.d.ts +0 -7
  110. package/src/components/Slug/SlugFeature.js +0 -21
  111. package/src/components/Slug/SlugFeature.ts +0 -30
  112. package/src/components/Slug/index.d.ts +0 -1
  113. package/src/components/Slug/index.js +0 -1
  114. package/src/components/Slug/index.ts +0 -1
  115. package/src/components/Slug/styles.d.ts +0 -4
  116. package/src/components/Slug/styles.js +0 -20
  117. package/src/components/Slug/styles.ts +0 -24
  118. package/src/components/StringList/SortableList/SortableList.d.ts +0 -18
  119. package/src/components/StringList/SortableList/SortableList.js +0 -37
  120. package/src/components/StringList/SortableList/SortableList.tsx +0 -98
  121. package/src/components/StringList/SortableList/components/SortableItem/DragHandle.d.ts +0 -7
  122. package/src/components/StringList/SortableList/components/SortableItem/DragHandle.js +0 -8
  123. package/src/components/StringList/SortableList/components/SortableItem/DragHandle.tsx +0 -20
  124. package/src/components/StringList/SortableList/components/SortableItem/SortableItem.d.ts +0 -8
  125. package/src/components/StringList/SortableList/components/SortableItem/SortableItem.js +0 -28
  126. package/src/components/StringList/SortableList/components/SortableItem/SortableItem.tsx +0 -59
  127. package/src/components/StringList/SortableList/components/SortableItem/styles.d.ts +0 -2
  128. package/src/components/StringList/SortableList/components/SortableItem/styles.js +0 -20
  129. package/src/components/StringList/SortableList/components/SortableItem/styles.ts +0 -22
  130. package/src/components/StringList/SortableList/components/SortableItem/types.d.ts +0 -6
  131. package/src/components/StringList/SortableList/components/SortableItem/types.ts +0 -7
  132. package/src/components/StringList/SortableList/components/index.d.ts +0 -2
  133. package/src/components/StringList/SortableList/components/index.js +0 -2
  134. package/src/components/StringList/SortableList/components/index.ts +0 -2
  135. package/src/components/StringList/SortableList/index.d.ts +0 -1
  136. package/src/components/StringList/SortableList/index.js +0 -1
  137. package/src/components/StringList/SortableList/index.ts +0 -1
  138. package/src/components/StringList/SortableList/styles.d.ts +0 -1
  139. package/src/components/StringList/SortableList/styles.js +0 -8
  140. package/src/components/StringList/SortableList/styles.ts +0 -9
  141. package/src/components/StringList/StringList.d.ts +0 -8
  142. package/src/components/StringList/StringList.js +0 -60
  143. package/src/components/StringList/StringList.tsx +0 -136
  144. package/src/components/StringList/StringListShow.d.ts +0 -7
  145. package/src/components/StringList/StringListShow.js +0 -14
  146. package/src/components/StringList/StringListShow.tsx +0 -37
  147. package/src/components/StringList/constants.d.ts +0 -1
  148. package/src/components/StringList/constants.js +0 -1
  149. package/src/components/StringList/constants.ts +0 -1
  150. package/src/components/StringList/index.d.ts +0 -2
  151. package/src/components/StringList/index.js +0 -2
  152. package/src/components/StringList/index.ts +0 -2
  153. package/src/components/StringList/styles.d.ts +0 -8
  154. package/src/components/StringList/styles.js +0 -33
  155. package/src/components/StringList/styles.ts +0 -41
  156. package/src/components/index.d.ts +0 -7
  157. package/src/components/index.js +0 -10
  158. package/src/components/index.ts +0 -10
  159. /package/{src/components/ColorStatus/types.js → dist/components/Slug/SlugOptions.type.js} +0 -0
  160. /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 ColorStatus: FC<ColorStatusTypes>;
5
- export default ColorStatus;
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 ColorStatus = ({ property, record, onChange }) => {
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 ColorStatus;
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,4 +1,4 @@
1
1
  import { FC } from "react";
2
2
  import { ShowPropertyProps } from "adminjs";
3
- declare const ColorStatusList: FC<ShowPropertyProps>;
3
+ export declare const ColorStatusList: FC<ShowPropertyProps>;
4
4
  export default ColorStatusList;
@@ -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
- return (React.createElement(ColorStatusBadge, { color: currentOption.color }, record.params[property.path]));
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,4 +1,4 @@
1
1
  import { FC } from "react";
2
2
  import { ShowPropertyProps } from "adminjs";
3
- declare const ColorStatusShow: FC<ShowPropertyProps>;
3
+ export declare const ColorStatusShow: FC<ShowPropertyProps>;
4
4
  export default ColorStatusShow;
@@ -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.color }, record.params[property.path])));
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 * from "./ColorStatusEdit.js";
2
- export * from "./ColorStatusShow.js";
3
- export * from "./ColorStatusList.js";
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 * from "./ColorStatusEdit.js";
2
- export * from "./ColorStatusShow.js";
3
- export * from "./ColorStatusList.js";
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: 24px;
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 default Editor;
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, { memo, useState, useEffect, useRef } from "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 Editor = ({ property, record, onChangeAdmin, editorId }) => {
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.params[property.path]);
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(property.path, jsonData);
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 editor = new EditorJS({
17
- holder: editorId,
18
- tools: EDITOR_TOOLS,
19
- data: isSavedData ? JSON.parse(record.params[property.path]) : "",
20
- async onChange(api, event) {
21
- const data = await api.saver.save();
22
- setJsonData(JSON.stringify(data));
23
- },
24
- });
25
- ref.current = editor;
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: editorId }))));
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 default EditorList;
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 default EditorShow;
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 * from "./Editor.jsx";
2
- export * from "./EditorShow.jsx";
3
- export * from "./EditorList.jsx";
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 * from "./Editor.jsx";
2
- export * from "./EditorShow.jsx";
3
- export * from "./EditorList.jsx";
1
+ export { Editor } from "./Editor.js";
2
+ export { EditorShow } from "./EditorShow.js";
3
+ export { EditorList } from "./EditorList.js";
4
+ export { EditorFeature } from "./EditorFeature.js";
@@ -0,0 +1,4 @@
1
+ import { FC } from "react";
2
+ import { ActionProps } from "adminjs";
3
+ export declare const FeatureTabsEdit: FC<ActionProps>;
4
+ export default FeatureTabsEdit;