@rulab/adminjs-components 0.1.0 → 0.2.0

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,8 +1,8 @@
1
1
  # adminjs-components
2
2
 
3
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.
4
+ badges, slug and UUID generation, Editor.js content, sortable string lists, tabs
5
+ layout, and record preview.
6
6
 
7
7
  ## Install
8
8
 
@@ -68,6 +68,7 @@ features: [
68
68
  ### Slug
69
69
 
70
70
  Generates a slug from another field and stores it in the target property.
71
+ Use `button` to override the generate button label in edit view.
71
72
 
72
73
  ```ts
73
74
  import { SlugFeature } from "@rulab/adminjs-components";
@@ -76,6 +77,23 @@ features: [
76
77
  SlugFeature({
77
78
  key: "slug",
78
79
  source: "title",
80
+ button: "Create slug", // optional
81
+ }),
82
+ ]
83
+ ```
84
+
85
+ ### UUID
86
+
87
+ Adds a UUID field with a "Generate UUID" button in edit view.
88
+ Use `button` to override the generate button label.
89
+
90
+ ```ts
91
+ import { UuidFeature } from "@rulab/adminjs-components";
92
+
93
+ features: [
94
+ UuidFeature({
95
+ key: "uuid",
96
+ button: "Generate ID", // optional
79
97
  }),
80
98
  ]
81
99
  ```
@@ -1,13 +1,11 @@
1
- export function Editor({ property, record, resource, onChange, onChangeAdmin, editorId }: {
1
+ import React from "react";
2
+ type EditorProps = {
2
3
  property: any;
3
4
  record: any;
4
5
  resource: any;
5
- onChange: any;
6
- onChangeAdmin: any;
7
- editorId: any;
8
- }): React.FunctionComponentElement<{
9
- children?: React.ReactNode;
10
- theme: import("styled-components").DefaultTheme | ((outerTheme?: import("styled-components").DefaultTheme | undefined) => import("styled-components").DefaultTheme);
11
- }>;
6
+ onChange?: (path: string, value: string | undefined) => void;
7
+ onChangeAdmin?: (path: string, value: string | undefined) => void;
8
+ editorId?: string;
9
+ };
10
+ export declare const Editor: ({ property, record, resource, onChange, onChangeAdmin, editorId, }: EditorProps) => React.JSX.Element;
12
11
  export default Editor;
13
- import React from "react";
@@ -22,15 +22,15 @@ const getEditorData = (record, property) => {
22
22
  try {
23
23
  return JSON.parse(raw);
24
24
  }
25
- catch (error) {
25
+ catch {
26
26
  return "";
27
27
  }
28
28
  };
29
- export const Editor = ({ property, record, resource, onChange, onChangeAdmin, editorId }) => {
29
+ export const Editor = ({ property, record, resource, onChange, onChangeAdmin, editorId, }) => {
30
30
  const [jsonData, setJsonData] = useState();
31
31
  const isSavedData = Boolean(record?.params?.[property.path]);
32
32
  const holderId = editorId || property?.props?.editorId || `editor-${property.path}`;
33
- const uploadAction = property?.props?.uploadAction;
33
+ const uploadAction = property?.custom?.uploadAction ?? property?.props?.uploadAction;
34
34
  const resourceId = resource?.id;
35
35
  const ref = useRef();
36
36
  useEffect(() => {
@@ -78,7 +78,7 @@ export const Editor = ({ property, record, resource, onChange, onChangeAdmin, ed
78
78
  holder: holderId,
79
79
  tools,
80
80
  data: isSavedData ? getEditorData(record, property) : "",
81
- async onChange(api, event) {
81
+ async onChange(api) {
82
82
  const data = await api.saver.save();
83
83
  setJsonData(JSON.stringify(data));
84
84
  },
@@ -91,6 +91,9 @@ export const Editor = ({ property, record, resource, onChange, onChangeAdmin, ed
91
91
  ref?.current?.destroy?.();
92
92
  };
93
93
  }, []);
94
- return (React.createElement(ThemeProvider, { theme: theme }, React.createElement(StyledLabel, null, property.label ?? property.path), React.createElement(StyledEditorWrapper, null, React.createElement(StyledEditor, { id: holderId }))));
94
+ return (React.createElement(ThemeProvider, { theme: theme },
95
+ React.createElement(StyledLabel, null, property.label ?? property.path),
96
+ React.createElement(StyledEditorWrapper, null,
97
+ React.createElement(StyledEditor, { id: holderId }))));
95
98
  };
96
99
  export default Editor;
@@ -1,9 +1,7 @@
1
- export function EditorList({ property, record }: {
1
+ import React from "react";
2
+ type EditorListProps = {
2
3
  property: any;
3
4
  record: any;
4
- }): React.FunctionComponentElement<{
5
- children?: React.ReactNode;
6
- theme: import("styled-components").DefaultTheme | ((outerTheme?: import("styled-components").DefaultTheme | undefined) => import("styled-components").DefaultTheme);
7
- }>;
5
+ };
6
+ export declare const EditorList: ({ property, record }: EditorListProps) => React.JSX.Element;
8
7
  export default EditorList;
9
- import React from "react";
@@ -5,6 +5,7 @@ import { parseHtml } from "../../utils/parseHtml.js";
5
5
  import { StyledEditorViewWrapper } from "./styles.js";
6
6
  export const EditorList = ({ property, record }) => {
7
7
  const htmlContent = parseHtml(record.params[property.path]);
8
- return (React.createElement(ThemeProvider, { theme: theme }, React.createElement(StyledEditorViewWrapper, null, htmlContent && (React.createElement("div", { dangerouslySetInnerHTML: { __html: htmlContent } })))));
8
+ return (React.createElement(ThemeProvider, { theme: theme },
9
+ React.createElement(StyledEditorViewWrapper, null, htmlContent && React.createElement("div", { dangerouslySetInnerHTML: { __html: htmlContent } }))));
9
10
  };
10
11
  export default EditorList;
@@ -1,9 +1,7 @@
1
- export function EditorShow({ property, record }: {
1
+ import React from "react";
2
+ type EditorShowProps = {
2
3
  property: any;
3
4
  record: any;
4
- }): React.FunctionComponentElement<{
5
- children?: React.ReactNode;
6
- theme: import("styled-components").DefaultTheme | ((outerTheme?: import("styled-components").DefaultTheme | undefined) => import("styled-components").DefaultTheme);
7
- }>;
5
+ };
6
+ export declare const EditorShow: ({ property, record }: EditorShowProps) => React.JSX.Element;
8
7
  export default EditorShow;
9
- import React from "react";
@@ -5,6 +5,9 @@ import { parseHtml } from "../../utils/parseHtml.js";
5
5
  import { StyledEditorShowWrapper, StyledShowLabel } from "./styles.js";
6
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.label ?? property.path), htmlContent && (React.createElement("div", { dangerouslySetInnerHTML: { __html: htmlContent } })))));
8
+ return (React.createElement(ThemeProvider, { theme: theme },
9
+ React.createElement(StyledEditorShowWrapper, null,
10
+ React.createElement(StyledShowLabel, null, property.label ?? property.path),
11
+ htmlContent && React.createElement("div", { dangerouslySetInnerHTML: { __html: htmlContent } }))));
9
12
  };
10
13
  export default EditorShow;
@@ -17,7 +17,7 @@ export const SlugEdit = ({ property, record, resource, onChange, }) => {
17
17
  React.createElement(StyledLabel, { htmlFor: "customSlug" }, property.label ?? property.path),
18
18
  React.createElement(StyledInputWrapper, null,
19
19
  React.createElement(StyledCustomInput, { id: property.path, name: property.path, value: inputValue, onChange: handleInput }),
20
- React.createElement(StyledGenerateButton, { variant: "outlined", onClick: generateSlug }, "Generate Slug"))));
20
+ React.createElement(StyledGenerateButton, { variant: "outlined", onClick: generateSlug }, custom.button ?? "Generate Slug"))));
21
21
  function handleInput(e) {
22
22
  setInputValue(e.target.value);
23
23
  }
@@ -2,7 +2,7 @@ import { buildFeature } from "adminjs";
2
2
  import { bundleComponent } from "../../utils/bundle-component.js";
3
3
  const COMPONENT_NAME = 'Slug';
4
4
  export const SlugFeature = (config) => {
5
- const { componentLoader, source, key } = config;
5
+ const { componentLoader, source, key, button } = config;
6
6
  const editComponent = bundleComponent(componentLoader, COMPONENT_NAME, 'SlugEdit.js');
7
7
  return buildFeature({
8
8
  properties: {
@@ -11,7 +11,7 @@ export const SlugFeature = (config) => {
11
11
  components: {
12
12
  edit: editComponent,
13
13
  },
14
- custom: { source, key }
14
+ custom: { source, key, button }
15
15
  },
16
16
  },
17
17
  });
@@ -3,5 +3,6 @@ type SlugOptions = {
3
3
  componentLoader?: ComponentLoader;
4
4
  source: string;
5
5
  key: string;
6
+ button?: string;
6
7
  };
7
8
  export default SlugOptions;
@@ -0,0 +1,5 @@
1
+ import { EditPropertyProps } from "adminjs";
2
+ import { FC } from "react";
3
+ type CustomUuidTypes = Omit<EditPropertyProps, "where">;
4
+ export declare const UuidEdit: FC<CustomUuidTypes>;
5
+ export default UuidEdit;
@@ -0,0 +1,34 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { ThemeProvider } from "styled-components";
3
+ import { theme } from "@adminjs/design-system";
4
+ import { StyledCustomInput, StyledGenerateButton, StyledInputWrapper, StyledLabel, } from "./styles.js";
5
+ const generateUuidV4 = () => {
6
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
7
+ return crypto.randomUUID();
8
+ }
9
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
10
+ const random = (Math.random() * 16) | 0;
11
+ const value = char === "x" ? random : (random & 0x3) | 0x8;
12
+ return value.toString(16);
13
+ });
14
+ };
15
+ export const UuidEdit = ({ property, record, onChange, }) => {
16
+ const { custom } = property;
17
+ const [inputValue, setInputValue] = useState(record.params[property.path] ?? "");
18
+ useEffect(() => {
19
+ onChange(property.path, inputValue);
20
+ }, [inputValue]);
21
+ return (React.createElement(ThemeProvider, { theme: theme },
22
+ React.createElement(StyledLabel, { htmlFor: "customUuid" }, property.label ?? property.path),
23
+ React.createElement(StyledInputWrapper, null,
24
+ React.createElement(StyledCustomInput, { id: property.path, name: property.path, value: inputValue, onChange: handleInput }),
25
+ React.createElement(StyledGenerateButton, { variant: "outlined", onClick: generateUuid }, custom?.button ?? "Generate UUID"))));
26
+ function handleInput(e) {
27
+ setInputValue(e.target.value);
28
+ }
29
+ function generateUuid(e) {
30
+ e.preventDefault();
31
+ setInputValue(generateUuidV4());
32
+ }
33
+ };
34
+ export default UuidEdit;
@@ -0,0 +1,4 @@
1
+ import { FeatureType } from "adminjs";
2
+ import UuidOptions from "./UuidOptions.type.js";
3
+ export declare const UuidFeature: (config: UuidOptions) => FeatureType;
4
+ export default UuidFeature;
@@ -0,0 +1,21 @@
1
+ import { buildFeature } from "adminjs";
2
+ import { bundleComponent } from "../../utils/bundle-component.js";
3
+ const COMPONENT_NAME = "Uuid";
4
+ export const UuidFeature = (config) => {
5
+ const { componentLoader, key, button } = config;
6
+ const editComponent = bundleComponent(componentLoader, COMPONENT_NAME, "UuidEdit.js");
7
+ return buildFeature({
8
+ properties: {
9
+ [key]: {
10
+ isVisible: { filter: true, show: true, edit: true, list: true },
11
+ custom: {
12
+ button,
13
+ },
14
+ components: {
15
+ edit: editComponent,
16
+ },
17
+ },
18
+ },
19
+ });
20
+ };
21
+ export default UuidFeature;
@@ -0,0 +1,7 @@
1
+ import { ComponentLoader } from "adminjs";
2
+ type UuidOptions = {
3
+ componentLoader?: ComponentLoader;
4
+ key: string;
5
+ button?: string;
6
+ };
7
+ export default UuidOptions;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export declare const StyledInputWrapper: any;
2
+ export declare const StyledCustomInput: any;
3
+ export declare const StyledGenerateButton: any;
4
+ export declare const StyledLabel: any;
@@ -0,0 +1,20 @@
1
+ // @ts-ignore
2
+ import { styled } from "@adminjs/design-system/styled-components";
3
+ // @ts-ignore
4
+ import { Box, Button, Input } from "@adminjs/design-system";
5
+ export const StyledInputWrapper = styled(Box) `
6
+ display: flex;
7
+ margin-bottom: 40px;
8
+ `;
9
+ export const StyledCustomInput = styled(Input) `
10
+ width: 100%;
11
+ margin-right: 10px;
12
+ `;
13
+ export const StyledGenerateButton = styled(Button) `
14
+ white-space: nowrap;
15
+ `;
16
+ export const StyledLabel = styled.div `
17
+ font-size: 12px;
18
+ margin-bottom: 8px;
19
+ text-transform: capitalize;
20
+ `;
@@ -13,3 +13,4 @@ export { StringListFeature } from "./StringList/StringListFeature.js";
13
13
  export { SlugFeature } from "./Slug/SlugFeature.js";
14
14
  export { TabsFeature } from "./Tabs/TabsFeature.js";
15
15
  export { PreviewFeature } from "./Preview/PreviewFeature.js";
16
+ export { UuidFeature } from "./Uuid/UuidFeature.js";
@@ -13,3 +13,4 @@ export { StringListFeature } from "./StringList/StringListFeature.js";
13
13
  export { SlugFeature } from "./Slug/SlugFeature.js";
14
14
  export { TabsFeature } from "./Tabs/TabsFeature.js";
15
15
  export { PreviewFeature } from "./Preview/PreviewFeature.js";
16
+ export { UuidFeature } from "./Uuid/UuidFeature.js";
package/package.json CHANGED
@@ -1,35 +1,55 @@
1
1
  {
2
2
  "name": "@rulab/adminjs-components",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
+ "description": "Prebuilt AdminJS features for common UI needs.",
4
5
  "main": "dist/index.js",
5
- "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
7
7
  "private": false,
8
8
  "type": "module",
9
+ "license": "MIT",
9
10
  "keywords": [
10
- "adminjs"
11
+ "adminjs",
12
+ "components",
13
+ "react",
14
+ "editorjs"
11
15
  ],
16
+ "homepage": "https://github.com/rulab/adminjs-components#readme",
17
+ "bugs": {
18
+ "url": "https://github.com/rulab/adminjs-components/issues"
19
+ },
12
20
  "repository": {
13
21
  "url": "git+https://github.com/rulab/adminjs-components.git"
14
22
  },
23
+ "publishConfig": {
24
+ "access": "public",
25
+ "registry": "https://registry.npmjs.org/"
26
+ },
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js",
31
+ "default": "./dist/index.js"
32
+ }
33
+ },
34
+ "sideEffects": false,
15
35
  "scripts": {
16
- "build": "tsc && node scripts/fix-import-extensions.js",
17
- "build-tsup": "tsup",
18
- "lint": "tsc"
36
+ "build": "tsc",
37
+ "lint": "tsc",
38
+ "prepublishOnly": "pnpm run build"
19
39
  },
20
40
  "dependencies": {
21
41
  "@dnd-kit/core": "^6.1.0",
22
42
  "@dnd-kit/sortable": "^8.0.0",
23
- "@editorjs/editorjs": "2.30.2",
24
- "@editorjs/header": "^2.8.7",
25
- "@editorjs/image": "^2.9.2",
26
- "@editorjs/list": "^1.9.0",
27
- "@editorjs/paragraph": "^2.11.6",
28
- "@editorjs/quote": "^2.7.2",
29
- "@editorjs/table": "^2.4.1",
43
+ "@editorjs/editorjs": "~2.30.2",
44
+ "@editorjs/header": "~2.8.7",
45
+ "@editorjs/image": "~2.9.2",
46
+ "@editorjs/list": "~1.9.0",
47
+ "@editorjs/paragraph": "~2.11.6",
48
+ "@editorjs/quote": "~2.7.2",
49
+ "@editorjs/table": "~2.4.1",
30
50
  "chroma-js": "^3.0.0",
31
- "editorjs-audio-player": "^0.0.3",
32
- "editorjs-html": "^3.4.3",
51
+ "editorjs-audio-player": "~0.0.3",
52
+ "editorjs-html": "~3.4.3",
33
53
  "react-select": "^5.8.0",
34
54
  "slugify": "^1.6.6"
35
55
  },
@@ -53,7 +73,6 @@
53
73
  "@types/node": "^20.11.24",
54
74
  "@types/react": "^18.2.61",
55
75
  "react": "^18.2.0",
56
- "tsup": "^8.1.0",
57
76
  "styled-components": "^6.1.11",
58
77
  "typescript": "^5.3.2"
59
78
  },