@rulab/adminjs-components 0.2.0 → 0.2.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 CHANGED
@@ -1,5 +1,7 @@
1
1
  # adminjs-components
2
2
 
3
+ <a id="top"></a>
4
+
3
5
  Prebuilt AdminJS components and features for common UI needs: colored status
4
6
  badges, slug and UUID generation, Editor.js content, sortable string lists, tabs
5
7
  layout, and record preview.
@@ -46,9 +48,19 @@ You can also pass `componentLoader` into every feature instead of calling
46
48
 
47
49
  ## Components and features
48
50
 
51
+ - [ColorStatus](#colorstatus)
52
+ - [Slug](#slug)
53
+ - [UUID](#uuid)
54
+ - [Editor](#editor)
55
+ - [StringList](#stringlist)
56
+ - [Tabs](#tabs)
57
+ - [Preview](#preview)
58
+
49
59
  ### ColorStatus
50
60
 
51
61
  Renders a colored badge in edit/list/show based on a list of available values.
62
+ By default (`nullable: false`) the first option is auto-selected in new records.
63
+ Set `nullable: true` to allow an empty value.
52
64
 
53
65
  ```ts
54
66
  import { ColorStatusFeature } from "@rulab/adminjs-components";
@@ -56,6 +68,7 @@ import { ColorStatusFeature } from "@rulab/adminjs-components";
56
68
  features: [
57
69
  ColorStatusFeature({
58
70
  key: "status",
71
+ nullable: false, // optional, default false
59
72
  availableValues: [
60
73
  { value: "draft", label: "Draft", color: "#64748b" },
61
74
  { value: "review", label: "In review", color: "#f59e0b" },
@@ -65,6 +78,8 @@ features: [
65
78
  ]
66
79
  ```
67
80
 
81
+ [Back to top](#top)
82
+
68
83
  ### Slug
69
84
 
70
85
  Generates a slug from another field and stores it in the target property.
@@ -82,6 +97,8 @@ features: [
82
97
  ]
83
98
  ```
84
99
 
100
+ [Back to top](#top)
101
+
85
102
  ### UUID
86
103
 
87
104
  Adds a UUID field with a "Generate UUID" button in edit view.
@@ -98,10 +115,12 @@ features: [
98
115
  ]
99
116
  ```
100
117
 
118
+ [Back to top](#top)
119
+
101
120
  ### Editor
102
121
 
103
122
  Editor.js field for rich content. Supports optional image upload via
104
- `@adminjs/upload` provider.
123
+ `@adminjs/upload` provider, plus built-in audio and video blocks.
105
124
 
106
125
  ```ts
107
126
  import { EditorFeature } from "@rulab/adminjs-components";
@@ -120,9 +139,12 @@ features: [
120
139
  ]
121
140
  ```
122
141
 
142
+ [Back to top](#top)
143
+
123
144
  ### StringList
124
145
 
125
146
  Sortable list stored as a single string (comma-separated by default).
147
+ Use `separator` to override the delimiter used for storing and rendering values.
126
148
 
127
149
  ```ts
128
150
  import { StringListFeature } from "@rulab/adminjs-components";
@@ -130,21 +152,25 @@ import { StringListFeature } from "@rulab/adminjs-components";
130
152
  features: [
131
153
  StringListFeature({
132
154
  key: "facts",
155
+ separator: "|", // optional, default "|"
133
156
  }),
134
157
  ]
135
158
  ```
136
159
 
160
+ [Back to top](#top)
161
+
137
162
  ### Tabs
138
163
 
139
164
  Groups edit/show fields into tabs based on property `props.tab` or
140
165
  `custom.tab`. Fields without a tab go to a common group.
166
+ `commonTabLabel` is optional and defaults to `"Common"`.
141
167
 
142
168
  ```ts
143
169
  import { TabsFeature } from "@rulab/adminjs-components";
144
170
 
145
171
  features: [
146
172
  TabsFeature({
147
- commonTabLabel: "Common",
173
+ commonTabLabel: "General", // optional, default "Common"
148
174
  }),
149
175
  ]
150
176
 
@@ -157,6 +183,8 @@ properties: {
157
183
  },
158
184
  ```
159
185
 
186
+ [Back to top](#top)
187
+
160
188
  ### Preview
161
189
 
162
190
  Adds a record action that renders an iframe preview. The `url` can include
@@ -173,6 +201,8 @@ features: [
173
201
  ]
174
202
  ```
175
203
 
204
+ [Back to top](#top)
205
+
176
206
  ## Utilities
177
207
 
178
208
  - `setComponentLoader(loader)` and `getComponentLoader()` to reuse a single
@@ -43,8 +43,12 @@ const colorStyles = {
43
43
  };
44
44
  export const ColorStatusEdit = ({ property, record, onChange, }) => {
45
45
  const availableValues = property.availableValues;
46
- const currentOption = availableValues.find((item) => item.value === record.params[property.path]);
47
- const [selectOption, setCurrentOption] = useState(currentOption);
46
+ const nullable = Boolean(property.custom?.nullable);
47
+ const isNewRecord = !record.id;
48
+ const currentValue = record.params[property.path];
49
+ const currentOption = availableValues.find((item) => item.value === currentValue);
50
+ const initialOption = currentOption ?? (!nullable && isNewRecord ? availableValues[0] : null);
51
+ const [selectOption, setCurrentOption] = useState(initialOption);
48
52
  const handleSelectChange = (option) => {
49
53
  setCurrentOption(option);
50
54
  };
@@ -53,6 +57,6 @@ export const ColorStatusEdit = ({ property, record, onChange, }) => {
53
57
  }, [selectOption]);
54
58
  return (React.createElement(ColorStatusWrapper, null,
55
59
  React.createElement(Label, null, property.label ?? property.path),
56
- React.createElement(Select, { className: "basic-single", classNamePrefix: "select", defaultValue: selectOption ?? availableValues[0], onChange: handleSelectChange, isClearable: true, name: "color", options: availableValues, styles: colorStyles })));
60
+ React.createElement(Select, { className: "basic-single", classNamePrefix: "select", value: selectOption, onChange: handleSelectChange, isClearable: nullable, name: "color", options: availableValues, styles: colorStyles })));
57
61
  };
58
62
  export default ColorStatusEdit;
@@ -4,6 +4,7 @@ type ColorStatusOptions = {
4
4
  componentLoader?: ComponentLoader;
5
5
  key: string;
6
6
  availableValues?: AvailableValueType[];
7
+ nullable?: boolean;
7
8
  };
8
9
  export declare const ColorStatusFeature: (config: ColorStatusOptions) => FeatureType;
9
10
  export default ColorStatusFeature;
@@ -2,7 +2,10 @@ import { buildFeature } from "adminjs";
2
2
  import { bundleComponent } from "../../utils/bundle-component.js";
3
3
  const COMPONENT_NAME = "ColorStatus";
4
4
  export const ColorStatusFeature = (config) => {
5
- const { componentLoader, key, availableValues = [] } = config;
5
+ const { componentLoader, key, availableValues = [], nullable = false } = config;
6
+ const values = nullable
7
+ ? [{ value: null, label: "", color: "#ffffff" }, ...availableValues]
8
+ : availableValues;
6
9
  const editComponent = bundleComponent(componentLoader, COMPONENT_NAME, "ColorStatusEdit.js");
7
10
  const listComponent = bundleComponent(componentLoader, COMPONENT_NAME, "ColorStatusList.js");
8
11
  const showComponent = bundleComponent(componentLoader, COMPONENT_NAME, "ColorStatusShow.js");
@@ -10,7 +13,10 @@ export const ColorStatusFeature = (config) => {
10
13
  properties: {
11
14
  [key]: {
12
15
  isVisible: { filter: true, show: true, edit: true, list: true },
13
- availableValues,
16
+ availableValues: values,
17
+ custom: {
18
+ nullable,
19
+ },
14
20
  components: {
15
21
  edit: editComponent,
16
22
  list: listComponent,
@@ -26,4 +26,5 @@ export declare const EDITOR_TOOLS: {
26
26
  inlineToolbar: boolean;
27
27
  };
28
28
  audioPlayer: any;
29
+ video: any;
29
30
  };
@@ -5,6 +5,8 @@ import Quote from "@editorjs/quote";
5
5
  import Table from "@editorjs/table";
6
6
  // @ts-ignore
7
7
  import AudioPlayer from "editorjs-audio-player";
8
+ // @ts-ignore
9
+ import SimpleVideo from "simple-video-editorjs";
8
10
  export const EDITOR_TOOLS = {
9
11
  paragraph: {
10
12
  class: Paragraph,
@@ -31,4 +33,5 @@ export const EDITOR_TOOLS = {
31
33
  inlineToolbar: true,
32
34
  },
33
35
  audioPlayer: AudioPlayer,
36
+ video: SimpleVideo,
34
37
  };
@@ -3,8 +3,9 @@ import React, { useEffect, useState, } from "react";
3
3
  import { ThemeProvider } from "styled-components";
4
4
  import { StyledCustomInput, StyledInputWrapper, StyledLabel, StyledListWrapper, StyledWrapper, } from "./styles.js";
5
5
  import { SortableList } from "./SortableList/SortableList.js";
6
- import { separator } from "./constants.js";
7
- export const StringList = ({ record, onChange, property, stringListSeparator = separator, }) => {
6
+ const DEFAULT_SEPARATOR = "|";
7
+ export const StringList = ({ record, onChange, property, stringListSeparator = DEFAULT_SEPARATOR, }) => {
8
+ const separatorValue = property.props?.stringListSeparator ?? stringListSeparator;
8
9
  const stringListValue = record.params?.[property.path] ?? property.props.value ?? "";
9
10
  const initialList = stringListValue
10
11
  ? prepareDataForList(stringListValue)
@@ -45,10 +46,10 @@ export const StringList = ({ record, onChange, property, stringListSeparator = s
45
46
  setList(newData);
46
47
  }
47
48
  function prepareDataForDatabase(list) {
48
- return list.map(({ value }) => value).join(stringListSeparator);
49
+ return list.map(({ value }) => value).join(separatorValue);
49
50
  }
50
51
  function prepareDataForList(str) {
51
- return str.split(stringListSeparator).map((item) => createListObject(item));
52
+ return str.split(separatorValue).map((item) => createListObject(item));
52
53
  }
53
54
  function createListObject(value) {
54
55
  return {
@@ -2,6 +2,7 @@ import { ComponentLoader, FeatureType } from "adminjs";
2
2
  type StringListOptions = {
3
3
  componentLoader?: ComponentLoader;
4
4
  key: string;
5
+ separator?: string;
5
6
  };
6
7
  export declare const StringListFeature: (config: StringListOptions) => FeatureType;
7
8
  export default StringListFeature;
@@ -2,13 +2,14 @@ import { buildFeature } from "adminjs";
2
2
  import { bundleComponent } from "../../utils/bundle-component.js";
3
3
  const COMPONENT_NAME = "StringList";
4
4
  export const StringListFeature = (config) => {
5
- const { componentLoader, key } = config;
5
+ const { componentLoader, key, separator } = config;
6
6
  const editComponent = bundleComponent(componentLoader, COMPONENT_NAME, "StringList.js");
7
7
  const showComponent = bundleComponent(componentLoader, COMPONENT_NAME, "StringListShow.js");
8
8
  return buildFeature({
9
9
  properties: {
10
10
  [key]: {
11
11
  isVisible: { filter: true, show: true, edit: true, list: true },
12
+ props: separator ? { stringListSeparator: separator } : undefined,
12
13
  components: {
13
14
  edit: editComponent,
14
15
  show: showComponent,
@@ -2,13 +2,15 @@ import React from "react";
2
2
  import { ThemeProvider } from "styled-components";
3
3
  import { theme } from "@adminjs/design-system";
4
4
  import { StyledShowLabel, StyledShowWrapper, StyledListItem } from "./styles.js";
5
- import { separator } from "./constants.js";
6
- export const StringListShow = ({ property, record, stringListSeparator = separator, }) => {
5
+ const DEFAULT_SEPARATOR = "|";
6
+ export const StringListShow = ({ property, record, stringListSeparator = DEFAULT_SEPARATOR, }) => {
7
+ const separatorValue = property.props?.stringListSeparator ?? stringListSeparator;
8
+ const value = record.params[property.path];
7
9
  return (React.createElement(ThemeProvider, { theme: theme },
8
10
  React.createElement(StyledShowWrapper, null,
9
11
  React.createElement(StyledShowLabel, null, property.label ?? property.path),
10
- record.params.facts && (React.createElement("ul", null, record.params.facts
11
- .split(stringListSeparator)
12
+ value && (React.createElement("ul", null, value
13
+ .split(separatorValue)
12
14
  .map((item, index) => (React.createElement(StyledListItem, { key: index }, `- ${item}`))))))));
13
15
  };
14
16
  export default StringListShow;
@@ -20,12 +20,20 @@ const tableParser = (block) => {
20
20
  }
21
21
  };
22
22
  const audioPlayerParser = (block) => {
23
- return `<audio controls src={${block.data.src}} />`;
23
+ return `<audio controls src="${block.data.src}"></audio>`;
24
+ };
25
+ const videoParser = (block) => {
26
+ const controls = block.data.controls === false ? "" : " controls";
27
+ const autoplay = block.data.autoplay ? " autoplay" : "";
28
+ const muted = block.data.muted ? " muted" : "";
29
+ const caption = block.data.caption ? `<figcaption>${block.data.caption}</figcaption>` : "";
30
+ return `<figure><video src="${block.data.url}"${controls}${autoplay}${muted}></video>${caption}</figure>`;
24
31
  };
25
32
  export const parseHtml = (jsonData) => {
26
33
  const edjsParser = edjsHTML({
27
34
  table: tableParser,
28
35
  audioPlayer: audioPlayerParser,
36
+ video: videoParser,
29
37
  });
30
38
  try {
31
39
  const data = edjsParser.parse(JSON.parse(jsonData));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulab/adminjs-components",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Prebuilt AdminJS features for common UI needs.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -51,6 +51,7 @@
51
51
  "editorjs-audio-player": "~0.0.3",
52
52
  "editorjs-html": "~3.4.3",
53
53
  "react-select": "^5.8.0",
54
+ "simple-video-editorjs": "~1.4.9",
54
55
  "slugify": "^1.6.6"
55
56
  },
56
57
  "peerDependencies": {