@rulab/adminjs-components 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Roman Daud
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # adminjs-components
package/dist/index.cjs ADDED
@@ -0,0 +1,333 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ CustomSlug: () => CustomSlug_default,
34
+ StringList: () => StringList_default
35
+ });
36
+ module.exports = __toCommonJS(src_exports);
37
+
38
+ // src/components/CustomSlug/CustomSlug.tsx
39
+ var import_react = __toESM(require("react"), 1);
40
+ var import_styled_components2 = require("styled-components");
41
+ var import_design_system2 = require("@adminjs/design-system");
42
+
43
+ // src/utils/slugifyImport.ts
44
+ var import_slugify = __toESM(require("slugify"), 1);
45
+ var slugifyImport_default = import_slugify.default;
46
+
47
+ // src/utils/slugifyTitle.ts
48
+ var slugifyTitle = (title) => {
49
+ return slugifyImport_default(title, {
50
+ replacement: "-",
51
+ remove: void 0,
52
+ lower: true,
53
+ locale: "vi",
54
+ trim: true
55
+ });
56
+ };
57
+
58
+ // src/components/CustomSlug/styles.ts
59
+ var import_styled_components = require("@adminjs/design-system/styled-components");
60
+ var import_design_system = require("@adminjs/design-system");
61
+ var StyledInputWrapper = (0, import_styled_components.styled)(import_design_system.Box)`
62
+ display: flex;
63
+ margin-bottom: 40px;
64
+ `;
65
+ var StyledCustomInput = (0, import_styled_components.styled)(import_design_system.Input)`
66
+ width: 100%;
67
+ margin-right: 10px;
68
+ `;
69
+ var StyledGenerateButton = (0, import_styled_components.styled)(import_design_system.Button)`
70
+ white-space: nowrap;
71
+ `;
72
+
73
+ // src/components/CustomSlug/CustomSlug.tsx
74
+ var CustomSlug = ({ property, record, onChange }) => {
75
+ const [inputValue, setInputValue] = (0, import_react.useState)(record.params.slug);
76
+ (0, import_react.useEffect)(() => {
77
+ onChange(property.path, inputValue);
78
+ }, [inputValue]);
79
+ return /* @__PURE__ */ import_react.default.createElement(import_styled_components2.ThemeProvider, { theme: import_design_system2.theme }, /* @__PURE__ */ import_react.default.createElement(import_design_system2.Label, { htmlFor: "customSlug" }, "Slug"), /* @__PURE__ */ import_react.default.createElement(StyledInputWrapper, null, /* @__PURE__ */ import_react.default.createElement(
80
+ StyledCustomInput,
81
+ {
82
+ id: property.path,
83
+ name: property.path,
84
+ value: inputValue,
85
+ onChange: handleInput
86
+ }
87
+ ), /* @__PURE__ */ import_react.default.createElement(StyledGenerateButton, { variant: "outlined", onClick: generateSlug }, "Generate Slug")));
88
+ function handleInput(e) {
89
+ setInputValue(e.target.value);
90
+ }
91
+ function generateSlug(e) {
92
+ e.preventDefault();
93
+ const title = record.title;
94
+ setInputValue(slugifyTitle(title));
95
+ }
96
+ };
97
+ var CustomSlug_default = CustomSlug;
98
+
99
+ // src/components/StringList/StringList.tsx
100
+ var import_react5 = __toESM(require("react"), 1);
101
+ var import_styled_components6 = require("styled-components");
102
+ var import_design_system5 = require("@adminjs/design-system");
103
+
104
+ // src/components/StringList/styles.ts
105
+ var import_styled_components3 = require("@adminjs/design-system/styled-components");
106
+ var import_design_system3 = require("@adminjs/design-system");
107
+ var StyledWrapper = (0, import_styled_components3.styled)(import_design_system3.Box)`
108
+ display: flex;
109
+ flex-direction: column;
110
+ `;
111
+ var StyledCustomInput2 = (0, import_styled_components3.styled)(import_design_system3.Input)`
112
+ width: 100%;
113
+ margin-right: 10px;
114
+ `;
115
+ var StyledInputWrapper2 = (0, import_styled_components3.styled)(import_design_system3.Box)`
116
+ display: flex;
117
+ `;
118
+ var StyledListWrapper = (0, import_styled_components3.styled)(import_design_system3.Box)`
119
+ margin-bottom: 15px;
120
+ `;
121
+
122
+ // src/components/StringList/SortableList/SortableList.tsx
123
+ var import_react4 = __toESM(require("react"), 1);
124
+ var import_core = require("@dnd-kit/core");
125
+ var import_sortable2 = require("@dnd-kit/sortable");
126
+
127
+ // src/components/StringList/SortableList/components/SortableItem/SortableItem.tsx
128
+ var import_react3 = __toESM(require("react"), 1);
129
+ var import_sortable = require("@dnd-kit/sortable");
130
+ var import_design_system4 = require("@adminjs/design-system");
131
+
132
+ // src/components/StringList/SortableList/components/SortableItem/DragHandle.tsx
133
+ var import_react2 = __toESM(require("react"), 1);
134
+
135
+ // src/components/StringList/SortableList/components/SortableItem/styles.ts
136
+ var import_styled_components4 = require("@adminjs/design-system/styled-components");
137
+ var StyledListItem = import_styled_components4.styled.li`
138
+ display: flex;
139
+ justify-content: space-between;
140
+ align-items: center;
141
+ background-color: #fff;
142
+ padding: 10px 20px 10px 15px;
143
+ box-shadow:
144
+ 0 0 0 calc(1px / var(--scale-x, 1)) rgba(63, 63, 68, 0.05),
145
+ 0 1px calc(3px / var(--scale-x, 1)) 0 rgba(34, 33, 81, 0.15);
146
+ border-radius: 5px;
147
+ list-style: none;
148
+ `;
149
+ var DragButton = import_styled_components4.styled.button`
150
+ padding: 3px;
151
+ margin-right: 15px;
152
+ cursor: move;
153
+ background: none;
154
+ border: none;
155
+ `;
156
+
157
+ // src/components/StringList/SortableList/components/SortableItem/DragHandle.tsx
158
+ var DragHandle = ({ context }) => {
159
+ const { attributes, listeners, ref } = (0, import_react2.useContext)(context);
160
+ return /* @__PURE__ */ import_react2.default.createElement(DragButton, { ...attributes, ...listeners, ref }, /* @__PURE__ */ import_react2.default.createElement("svg", { viewBox: "0 0 20 20", width: "13" }, /* @__PURE__ */ import_react2.default.createElement("path", { d: "M7 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 2zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 14zm6-8a2 2 0 1 0-.001-4.001A2 2 0 0 0 13 6zm0 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 14z" })));
161
+ };
162
+
163
+ // src/components/StringList/SortableList/components/SortableItem/SortableItem.tsx
164
+ var SortableItemContext = (0, import_react3.createContext)({
165
+ attributes: {},
166
+ listeners: void 0,
167
+ ref() {
168
+ }
169
+ });
170
+ function SortableItem({
171
+ children,
172
+ id,
173
+ onDelete
174
+ }) {
175
+ const { attributes, isDragging, listeners, setNodeRef, setActivatorNodeRef } = (0, import_sortable.useSortable)({ id });
176
+ const context = (0, import_react3.useMemo)(
177
+ () => ({
178
+ attributes,
179
+ listeners,
180
+ ref: setActivatorNodeRef
181
+ }),
182
+ [attributes, listeners, setActivatorNodeRef]
183
+ );
184
+ const style = {
185
+ opacity: isDragging ? 0.4 : void 0
186
+ };
187
+ return /* @__PURE__ */ import_react3.default.createElement(SortableItemContext.Provider, { value: context }, /* @__PURE__ */ import_react3.default.createElement(StyledListItem, { ref: setNodeRef, style }, /* @__PURE__ */ import_react3.default.createElement("div", null, /* @__PURE__ */ import_react3.default.createElement(DragHandle, { context: SortableItemContext }), children), /* @__PURE__ */ import_react3.default.createElement(
188
+ import_design_system4.Button,
189
+ {
190
+ variant: "outlined",
191
+ color: "danger",
192
+ size: "icon",
193
+ onClick: (e) => onDelete(e, id)
194
+ },
195
+ /* @__PURE__ */ import_react3.default.createElement(import_design_system4.Icon, { icon: "X", color: "red" })
196
+ )));
197
+ }
198
+
199
+ // src/components/StringList/SortableList/styles.ts
200
+ var import_styled_components5 = require("@adminjs/design-system/styled-components");
201
+ var StyledListWrapper2 = import_styled_components5.styled.ul`
202
+ display: flex;
203
+ flex-direction: column;
204
+ gap: 12px;
205
+ padding: 0;
206
+ list-style: none;
207
+ `;
208
+
209
+ // src/components/StringList/SortableList/SortableList.tsx
210
+ var dropAnimationConfig = {
211
+ sideEffects: (0, import_core.defaultDropAnimationSideEffects)({
212
+ styles: {
213
+ active: {
214
+ opacity: "0.4"
215
+ }
216
+ }
217
+ })
218
+ };
219
+ function SortableList({
220
+ items,
221
+ onChange,
222
+ renderItem
223
+ }) {
224
+ const [active, setActive] = (0, import_react4.useState)(null);
225
+ const activeItem = (0, import_react4.useMemo)(
226
+ () => items.find((item) => item.id === active?.id),
227
+ [active, items]
228
+ );
229
+ const sensors = (0, import_core.useSensors)(
230
+ (0, import_core.useSensor)(import_core.PointerSensor),
231
+ (0, import_core.useSensor)(import_core.KeyboardSensor, {
232
+ coordinateGetter: import_sortable2.sortableKeyboardCoordinates
233
+ })
234
+ );
235
+ return /* @__PURE__ */ import_react4.default.createElement(
236
+ import_core.DndContext,
237
+ {
238
+ sensors,
239
+ onDragStart: ({ active: active2 }) => {
240
+ setActive(active2);
241
+ },
242
+ onDragEnd: ({ active: active2, over }) => {
243
+ if (over && active2.id !== over?.id) {
244
+ const activeIndex = items.findIndex(({ id }) => id === active2.id);
245
+ const overIndex = items.findIndex(({ id }) => id === over.id);
246
+ onChange((0, import_sortable2.arrayMove)(items, activeIndex, overIndex));
247
+ }
248
+ setActive(null);
249
+ },
250
+ onDragCancel: () => {
251
+ setActive(null);
252
+ }
253
+ },
254
+ /* @__PURE__ */ import_react4.default.createElement(import_sortable2.SortableContext, { items, strategy: import_sortable2.verticalListSortingStrategy }, /* @__PURE__ */ import_react4.default.createElement(StyledListWrapper2, { role: "application" }, items.map((item, index) => /* @__PURE__ */ import_react4.default.createElement(import_react4.Fragment, { key: index }, renderItem(item))))),
255
+ /* @__PURE__ */ import_react4.default.createElement(import_core.DragOverlay, { dropAnimation: dropAnimationConfig }, activeItem ? renderItem(activeItem) : null)
256
+ );
257
+ }
258
+ SortableList.Item = SortableItem;
259
+
260
+ // src/components/StringList/StringList.tsx
261
+ var StringList = ({ record, onChange, property }) => {
262
+ const stringListValue = record.params?.[property.path] ?? property.props.value ?? "";
263
+ const initialList = stringListValue ? prepareDataForList(stringListValue) : [];
264
+ const [inputValue, setInputValue] = (0, import_react5.useState)("");
265
+ const [list, setList] = (0, import_react5.useState)(initialList);
266
+ const serializedData = prepareDataForDatabase(list);
267
+ (0, import_react5.useEffect)(() => {
268
+ onChange(property.path, serializedData);
269
+ }, [serializedData]);
270
+ return /* @__PURE__ */ import_react5.default.createElement(import_styled_components6.ThemeProvider, { theme: import_design_system5.theme }, /* @__PURE__ */ import_react5.default.createElement(import_design_system5.Label, { htmlFor: "custom" }, "String List"), /* @__PURE__ */ import_react5.default.createElement(StyledWrapper, null, /* @__PURE__ */ import_react5.default.createElement(StyledListWrapper, null, /* @__PURE__ */ import_react5.default.createElement(
271
+ SortableList,
272
+ {
273
+ items: list,
274
+ onChange: setList,
275
+ renderItem: (item) => /* @__PURE__ */ import_react5.default.createElement(SortableList.Item, { id: item.id, onDelete: handleDeleteButton }, item.value)
276
+ }
277
+ )), /* @__PURE__ */ import_react5.default.createElement(StyledInputWrapper2, null, /* @__PURE__ */ import_react5.default.createElement(
278
+ import_design_system5.Input,
279
+ {
280
+ id: "stringList",
281
+ name: property.path,
282
+ value: serializedData,
283
+ hidden: true
284
+ }
285
+ ), /* @__PURE__ */ import_react5.default.createElement(
286
+ StyledCustomInput2,
287
+ {
288
+ id: "custom",
289
+ name: "customInput",
290
+ value: inputValue,
291
+ onChange: handleInput,
292
+ onKeyPress: handleEnterPress
293
+ }
294
+ ), /* @__PURE__ */ import_react5.default.createElement(import_design_system5.Button, { variant: "outlined", onClick: handleAddButton }, "Add"))));
295
+ function handleInput(e) {
296
+ setInputValue(e.target.value);
297
+ }
298
+ function handleEnterPress(e) {
299
+ if (e.key === "Enter") {
300
+ handleAddButton(e);
301
+ }
302
+ }
303
+ function handleAddButton(e) {
304
+ e.preventDefault();
305
+ if (Boolean(inputValue)) {
306
+ setList([...list, createListObject(inputValue)]);
307
+ setInputValue("");
308
+ }
309
+ }
310
+ function handleDeleteButton(e, id) {
311
+ e.preventDefault();
312
+ const newData = list.filter((item) => item.id !== id);
313
+ setList(newData);
314
+ }
315
+ function prepareDataForDatabase(list2) {
316
+ return list2.map(({ value }) => value).join("|");
317
+ }
318
+ function prepareDataForList(str) {
319
+ return str.split("|").map((item) => createListObject(item));
320
+ }
321
+ function createListObject(value) {
322
+ return {
323
+ id: `${Date.now()}-${Math.floor(Math.random() * 1e3)}`,
324
+ value
325
+ };
326
+ }
327
+ };
328
+ var StringList_default = StringList;
329
+ // Annotate the CommonJS export names for ESM import in node:
330
+ 0 && (module.exports = {
331
+ CustomSlug,
332
+ StringList
333
+ });
@@ -0,0 +1,10 @@
1
+ import { FC } from 'react';
2
+ import { EditPropertyProps } from 'adminjs';
3
+
4
+ type CustomSlugTypes = Omit<EditPropertyProps, "where" | "resource">;
5
+ declare const CustomSlug: FC<CustomSlugTypes>;
6
+
7
+ type StringListTypes = Omit<EditPropertyProps, "where" | "resource">;
8
+ declare const StringList: FC<StringListTypes>;
9
+
10
+ export { CustomSlug, StringList };
@@ -0,0 +1,10 @@
1
+ import { FC } from 'react';
2
+ import { EditPropertyProps } from 'adminjs';
3
+
4
+ type CustomSlugTypes = Omit<EditPropertyProps, "where" | "resource">;
5
+ declare const CustomSlug: FC<CustomSlugTypes>;
6
+
7
+ type StringListTypes = Omit<EditPropertyProps, "where" | "resource">;
8
+ declare const StringList: FC<StringListTypes>;
9
+
10
+ export { CustomSlug, StringList };
package/dist/index.js ADDED
@@ -0,0 +1,314 @@
1
+ // src/components/CustomSlug/CustomSlug.tsx
2
+ import React, {
3
+ useEffect,
4
+ useState
5
+ } from "react";
6
+ import { ThemeProvider } from "styled-components";
7
+ import { theme, Label } from "@adminjs/design-system";
8
+
9
+ // src/utils/slugifyImport.ts
10
+ import slugify from "slugify";
11
+ var slugifyImport_default = slugify;
12
+
13
+ // src/utils/slugifyTitle.ts
14
+ var slugifyTitle = (title) => {
15
+ return slugifyImport_default(title, {
16
+ replacement: "-",
17
+ remove: void 0,
18
+ lower: true,
19
+ locale: "vi",
20
+ trim: true
21
+ });
22
+ };
23
+
24
+ // src/components/CustomSlug/styles.ts
25
+ import { styled } from "@adminjs/design-system/styled-components";
26
+ import { Button, Box, Input } from "@adminjs/design-system";
27
+ var StyledInputWrapper = styled(Box)`
28
+ display: flex;
29
+ margin-bottom: 40px;
30
+ `;
31
+ var StyledCustomInput = styled(Input)`
32
+ width: 100%;
33
+ margin-right: 10px;
34
+ `;
35
+ var StyledGenerateButton = styled(Button)`
36
+ white-space: nowrap;
37
+ `;
38
+
39
+ // src/components/CustomSlug/CustomSlug.tsx
40
+ var CustomSlug = ({ property, record, onChange }) => {
41
+ const [inputValue, setInputValue] = useState(record.params.slug);
42
+ useEffect(() => {
43
+ onChange(property.path, inputValue);
44
+ }, [inputValue]);
45
+ return /* @__PURE__ */ React.createElement(ThemeProvider, { theme }, /* @__PURE__ */ React.createElement(Label, { htmlFor: "customSlug" }, "Slug"), /* @__PURE__ */ React.createElement(StyledInputWrapper, null, /* @__PURE__ */ React.createElement(
46
+ StyledCustomInput,
47
+ {
48
+ id: property.path,
49
+ name: property.path,
50
+ value: inputValue,
51
+ onChange: handleInput
52
+ }
53
+ ), /* @__PURE__ */ React.createElement(StyledGenerateButton, { variant: "outlined", onClick: generateSlug }, "Generate Slug")));
54
+ function handleInput(e) {
55
+ setInputValue(e.target.value);
56
+ }
57
+ function generateSlug(e) {
58
+ e.preventDefault();
59
+ const title = record.title;
60
+ setInputValue(slugifyTitle(title));
61
+ }
62
+ };
63
+ var CustomSlug_default = CustomSlug;
64
+
65
+ // src/components/StringList/StringList.tsx
66
+ import React5, {
67
+ useState as useState3,
68
+ useEffect as useEffect2
69
+ } from "react";
70
+ import { ThemeProvider as ThemeProvider2 } from "styled-components";
71
+ import { theme as theme2, Button as Button3, Input as Input3, Label as Label2 } from "@adminjs/design-system";
72
+
73
+ // src/components/StringList/styles.ts
74
+ import { styled as styled2 } from "@adminjs/design-system/styled-components";
75
+ import { Box as Box2, Input as Input2 } from "@adminjs/design-system";
76
+ var StyledWrapper = styled2(Box2)`
77
+ display: flex;
78
+ flex-direction: column;
79
+ `;
80
+ var StyledCustomInput2 = styled2(Input2)`
81
+ width: 100%;
82
+ margin-right: 10px;
83
+ `;
84
+ var StyledInputWrapper2 = styled2(Box2)`
85
+ display: flex;
86
+ `;
87
+ var StyledListWrapper = styled2(Box2)`
88
+ margin-bottom: 15px;
89
+ `;
90
+
91
+ // src/components/StringList/SortableList/SortableList.tsx
92
+ import React4, { Fragment, useMemo as useMemo2, useState as useState2 } from "react";
93
+ import {
94
+ DndContext,
95
+ KeyboardSensor,
96
+ PointerSensor,
97
+ useSensor,
98
+ useSensors,
99
+ DragOverlay,
100
+ defaultDropAnimationSideEffects
101
+ } from "@dnd-kit/core";
102
+ import {
103
+ SortableContext,
104
+ arrayMove,
105
+ sortableKeyboardCoordinates,
106
+ verticalListSortingStrategy
107
+ } from "@dnd-kit/sortable";
108
+
109
+ // src/components/StringList/SortableList/components/SortableItem/SortableItem.tsx
110
+ import React3, { createContext, useMemo } from "react";
111
+ import { useSortable } from "@dnd-kit/sortable";
112
+ import { Button as Button2, Icon } from "@adminjs/design-system";
113
+
114
+ // src/components/StringList/SortableList/components/SortableItem/DragHandle.tsx
115
+ import React2, { useContext } from "react";
116
+
117
+ // src/components/StringList/SortableList/components/SortableItem/styles.ts
118
+ import { styled as styled3 } from "@adminjs/design-system/styled-components";
119
+ var StyledListItem = styled3.li`
120
+ display: flex;
121
+ justify-content: space-between;
122
+ align-items: center;
123
+ background-color: #fff;
124
+ padding: 10px 20px 10px 15px;
125
+ box-shadow:
126
+ 0 0 0 calc(1px / var(--scale-x, 1)) rgba(63, 63, 68, 0.05),
127
+ 0 1px calc(3px / var(--scale-x, 1)) 0 rgba(34, 33, 81, 0.15);
128
+ border-radius: 5px;
129
+ list-style: none;
130
+ `;
131
+ var DragButton = styled3.button`
132
+ padding: 3px;
133
+ margin-right: 15px;
134
+ cursor: move;
135
+ background: none;
136
+ border: none;
137
+ `;
138
+
139
+ // src/components/StringList/SortableList/components/SortableItem/DragHandle.tsx
140
+ var DragHandle = ({ context }) => {
141
+ const { attributes, listeners, ref } = useContext(context);
142
+ return /* @__PURE__ */ React2.createElement(DragButton, { ...attributes, ...listeners, ref }, /* @__PURE__ */ React2.createElement("svg", { viewBox: "0 0 20 20", width: "13" }, /* @__PURE__ */ React2.createElement("path", { d: "M7 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 2zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 14zm6-8a2 2 0 1 0-.001-4.001A2 2 0 0 0 13 6zm0 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 14z" })));
143
+ };
144
+
145
+ // src/components/StringList/SortableList/components/SortableItem/SortableItem.tsx
146
+ var SortableItemContext = createContext({
147
+ attributes: {},
148
+ listeners: void 0,
149
+ ref() {
150
+ }
151
+ });
152
+ function SortableItem({
153
+ children,
154
+ id,
155
+ onDelete
156
+ }) {
157
+ const { attributes, isDragging, listeners, setNodeRef, setActivatorNodeRef } = useSortable({ id });
158
+ const context = useMemo(
159
+ () => ({
160
+ attributes,
161
+ listeners,
162
+ ref: setActivatorNodeRef
163
+ }),
164
+ [attributes, listeners, setActivatorNodeRef]
165
+ );
166
+ const style = {
167
+ opacity: isDragging ? 0.4 : void 0
168
+ };
169
+ return /* @__PURE__ */ React3.createElement(SortableItemContext.Provider, { value: context }, /* @__PURE__ */ React3.createElement(StyledListItem, { ref: setNodeRef, style }, /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(DragHandle, { context: SortableItemContext }), children), /* @__PURE__ */ React3.createElement(
170
+ Button2,
171
+ {
172
+ variant: "outlined",
173
+ color: "danger",
174
+ size: "icon",
175
+ onClick: (e) => onDelete(e, id)
176
+ },
177
+ /* @__PURE__ */ React3.createElement(Icon, { icon: "X", color: "red" })
178
+ )));
179
+ }
180
+
181
+ // src/components/StringList/SortableList/styles.ts
182
+ import { styled as styled4 } from "@adminjs/design-system/styled-components";
183
+ var StyledListWrapper2 = styled4.ul`
184
+ display: flex;
185
+ flex-direction: column;
186
+ gap: 12px;
187
+ padding: 0;
188
+ list-style: none;
189
+ `;
190
+
191
+ // src/components/StringList/SortableList/SortableList.tsx
192
+ var dropAnimationConfig = {
193
+ sideEffects: defaultDropAnimationSideEffects({
194
+ styles: {
195
+ active: {
196
+ opacity: "0.4"
197
+ }
198
+ }
199
+ })
200
+ };
201
+ function SortableList({
202
+ items,
203
+ onChange,
204
+ renderItem
205
+ }) {
206
+ const [active, setActive] = useState2(null);
207
+ const activeItem = useMemo2(
208
+ () => items.find((item) => item.id === active?.id),
209
+ [active, items]
210
+ );
211
+ const sensors = useSensors(
212
+ useSensor(PointerSensor),
213
+ useSensor(KeyboardSensor, {
214
+ coordinateGetter: sortableKeyboardCoordinates
215
+ })
216
+ );
217
+ return /* @__PURE__ */ React4.createElement(
218
+ DndContext,
219
+ {
220
+ sensors,
221
+ onDragStart: ({ active: active2 }) => {
222
+ setActive(active2);
223
+ },
224
+ onDragEnd: ({ active: active2, over }) => {
225
+ if (over && active2.id !== over?.id) {
226
+ const activeIndex = items.findIndex(({ id }) => id === active2.id);
227
+ const overIndex = items.findIndex(({ id }) => id === over.id);
228
+ onChange(arrayMove(items, activeIndex, overIndex));
229
+ }
230
+ setActive(null);
231
+ },
232
+ onDragCancel: () => {
233
+ setActive(null);
234
+ }
235
+ },
236
+ /* @__PURE__ */ React4.createElement(SortableContext, { items, strategy: verticalListSortingStrategy }, /* @__PURE__ */ React4.createElement(StyledListWrapper2, { role: "application" }, items.map((item, index) => /* @__PURE__ */ React4.createElement(Fragment, { key: index }, renderItem(item))))),
237
+ /* @__PURE__ */ React4.createElement(DragOverlay, { dropAnimation: dropAnimationConfig }, activeItem ? renderItem(activeItem) : null)
238
+ );
239
+ }
240
+ SortableList.Item = SortableItem;
241
+
242
+ // src/components/StringList/StringList.tsx
243
+ var StringList = ({ record, onChange, property }) => {
244
+ const stringListValue = record.params?.[property.path] ?? property.props.value ?? "";
245
+ const initialList = stringListValue ? prepareDataForList(stringListValue) : [];
246
+ const [inputValue, setInputValue] = useState3("");
247
+ const [list, setList] = useState3(initialList);
248
+ const serializedData = prepareDataForDatabase(list);
249
+ useEffect2(() => {
250
+ onChange(property.path, serializedData);
251
+ }, [serializedData]);
252
+ return /* @__PURE__ */ React5.createElement(ThemeProvider2, { theme: theme2 }, /* @__PURE__ */ React5.createElement(Label2, { htmlFor: "custom" }, "String List"), /* @__PURE__ */ React5.createElement(StyledWrapper, null, /* @__PURE__ */ React5.createElement(StyledListWrapper, null, /* @__PURE__ */ React5.createElement(
253
+ SortableList,
254
+ {
255
+ items: list,
256
+ onChange: setList,
257
+ renderItem: (item) => /* @__PURE__ */ React5.createElement(SortableList.Item, { id: item.id, onDelete: handleDeleteButton }, item.value)
258
+ }
259
+ )), /* @__PURE__ */ React5.createElement(StyledInputWrapper2, null, /* @__PURE__ */ React5.createElement(
260
+ Input3,
261
+ {
262
+ id: "stringList",
263
+ name: property.path,
264
+ value: serializedData,
265
+ hidden: true
266
+ }
267
+ ), /* @__PURE__ */ React5.createElement(
268
+ StyledCustomInput2,
269
+ {
270
+ id: "custom",
271
+ name: "customInput",
272
+ value: inputValue,
273
+ onChange: handleInput,
274
+ onKeyPress: handleEnterPress
275
+ }
276
+ ), /* @__PURE__ */ React5.createElement(Button3, { variant: "outlined", onClick: handleAddButton }, "Add"))));
277
+ function handleInput(e) {
278
+ setInputValue(e.target.value);
279
+ }
280
+ function handleEnterPress(e) {
281
+ if (e.key === "Enter") {
282
+ handleAddButton(e);
283
+ }
284
+ }
285
+ function handleAddButton(e) {
286
+ e.preventDefault();
287
+ if (Boolean(inputValue)) {
288
+ setList([...list, createListObject(inputValue)]);
289
+ setInputValue("");
290
+ }
291
+ }
292
+ function handleDeleteButton(e, id) {
293
+ e.preventDefault();
294
+ const newData = list.filter((item) => item.id !== id);
295
+ setList(newData);
296
+ }
297
+ function prepareDataForDatabase(list2) {
298
+ return list2.map(({ value }) => value).join("|");
299
+ }
300
+ function prepareDataForList(str) {
301
+ return str.split("|").map((item) => createListObject(item));
302
+ }
303
+ function createListObject(value) {
304
+ return {
305
+ id: `${Date.now()}-${Math.floor(Math.random() * 1e3)}`,
306
+ value
307
+ };
308
+ }
309
+ };
310
+ var StringList_default = StringList;
311
+ export {
312
+ CustomSlug_default as CustomSlug,
313
+ StringList_default as StringList
314
+ };
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@rulab/adminjs-components",
3
+ "version": "0.0.1",
4
+ "main": "dist/index.js",
5
+ "module": "dist/index.mjs",
6
+ "types": "dist/index.d.ts",
7
+ "private": false,
8
+ "type": "module",
9
+ "keywords": ["adminjs"],
10
+ "repository": {
11
+ "url": "https://github.com/rulab/adminjs-components"
12
+ },
13
+ "scripts": {
14
+ "build": "tsup src/index.ts --format cjs,esm --dts",
15
+ "lint": "tsc"
16
+ },
17
+ "dependencies": {
18
+ "@adminjs/design-system": "^4.1.1",
19
+ "@dnd-kit/core": "^6.1.0",
20
+ "@dnd-kit/sortable": "^8.0.0",
21
+ "adminjs": "^7.8.1",
22
+ "react": "^18.2.0",
23
+ "slugify": "^1.6.6",
24
+ "styled-components": "^6.1.11"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "^20.11.24",
28
+ "@types/react": "^18.2.61",
29
+ "eslint": "^8.57.0",
30
+ "tsup": "^8.1.0",
31
+ "typescript": "^5.3.2"
32
+ },
33
+ "engines": {
34
+ "node": ">=18"
35
+ },
36
+ "packageManager": "pnpm@8.15.6"
37
+ }
@@ -0,0 +1,58 @@
1
+ import React, {
2
+ ChangeEvent,
3
+ FC,
4
+ SyntheticEvent,
5
+ useEffect,
6
+ useState,
7
+ } from "react";
8
+ import { ThemeProvider } from "styled-components";
9
+ import { EditPropertyProps } from "adminjs";
10
+
11
+ import { theme, Label } from "@adminjs/design-system";
12
+
13
+ import { slugifyTitle } from "../../utils/index.js";
14
+
15
+ import {
16
+ StyledInputWrapper,
17
+ StyledCustomInput,
18
+ StyledGenerateButton,
19
+ } from "./styles.js";
20
+
21
+ type CustomSlugTypes = Omit<EditPropertyProps, "where" | "resource">;
22
+
23
+ const CustomSlug: FC<CustomSlugTypes> = ({ property, record, onChange }) => {
24
+ const [inputValue, setInputValue] = useState(record.params.slug);
25
+
26
+ useEffect(() => {
27
+ onChange(property.path, inputValue);
28
+ }, [inputValue]);
29
+
30
+ return (
31
+ <ThemeProvider theme={theme}>
32
+ <Label htmlFor="customSlug">Slug</Label>
33
+ <StyledInputWrapper>
34
+ <StyledCustomInput
35
+ id={property.path}
36
+ name={property.path}
37
+ value={inputValue}
38
+ onChange={handleInput}
39
+ />
40
+ <StyledGenerateButton variant="outlined" onClick={generateSlug}>
41
+ Generate Slug
42
+ </StyledGenerateButton>
43
+ </StyledInputWrapper>
44
+ </ThemeProvider>
45
+ );
46
+
47
+ function handleInput(e: ChangeEvent<HTMLInputElement>) {
48
+ setInputValue(e.target.value);
49
+ }
50
+
51
+ function generateSlug(e: SyntheticEvent<HTMLInputElement>) {
52
+ e.preventDefault();
53
+ const title = record.title;
54
+ setInputValue(slugifyTitle(title));
55
+ }
56
+ };
57
+
58
+ export default CustomSlug;
@@ -0,0 +1 @@
1
+ export { default } from "./CustomSlug.js";
@@ -0,0 +1,18 @@
1
+ // @ts-ignore
2
+ import { styled } from "@adminjs/design-system/styled-components";
3
+ // @ts-ignore
4
+ import { Button, Box, Input } from "@adminjs/design-system";
5
+
6
+ export const StyledInputWrapper = styled(Box)`
7
+ display: flex;
8
+ margin-bottom: 40px;
9
+ `;
10
+
11
+ export const StyledCustomInput = styled(Input)`
12
+ width: 100%;
13
+ margin-right: 10px;
14
+ `;
15
+
16
+ export const StyledGenerateButton = styled(Button)`
17
+ white-space: nowrap;
18
+ `;
@@ -0,0 +1,98 @@
1
+ import React, { Fragment, useMemo, useState } from 'react';
2
+ import type { ReactNode } from 'react';
3
+
4
+ import {
5
+ DndContext,
6
+ KeyboardSensor,
7
+ PointerSensor,
8
+ useSensor,
9
+ useSensors,
10
+ DragOverlay,
11
+ defaultDropAnimationSideEffects,
12
+ } from '@dnd-kit/core';
13
+ import {
14
+ SortableContext,
15
+ arrayMove,
16
+ sortableKeyboardCoordinates,
17
+ verticalListSortingStrategy,
18
+ } from '@dnd-kit/sortable';
19
+
20
+ import type { DropAnimation } from '@dnd-kit/core';
21
+
22
+ import { DragHandle, SortableItem } from './components/index.js';
23
+ import { StyledListWrapper } from './styles.js';
24
+
25
+ import type { Active, UniqueIdentifier } from '@dnd-kit/core';
26
+
27
+ interface BaseItem {
28
+ id: UniqueIdentifier;
29
+ value: string;
30
+ }
31
+
32
+ interface Props<T extends BaseItem> {
33
+ items: T[];
34
+ onChange(items: T[]): void;
35
+ renderItem(item: T): ReactNode;
36
+ }
37
+
38
+ const dropAnimationConfig: DropAnimation = {
39
+ sideEffects: defaultDropAnimationSideEffects({
40
+ styles: {
41
+ active: {
42
+ opacity: '0.4',
43
+ },
44
+ },
45
+ }),
46
+ };
47
+
48
+ export function SortableList<T extends BaseItem>({
49
+ items,
50
+ onChange,
51
+ renderItem,
52
+ }: Props<T>) {
53
+ const [active, setActive] = useState<Active | null>(null);
54
+ const activeItem = useMemo(
55
+ () => items.find((item) => item.id === active?.id),
56
+ [active, items],
57
+ );
58
+ const sensors = useSensors(
59
+ useSensor(PointerSensor),
60
+ useSensor(KeyboardSensor, {
61
+ coordinateGetter: sortableKeyboardCoordinates,
62
+ }),
63
+ );
64
+
65
+ return (
66
+ <DndContext
67
+ sensors={sensors}
68
+ onDragStart={({ active }) => {
69
+ setActive(active);
70
+ }}
71
+ onDragEnd={({ active, over }) => {
72
+ if (over && active.id !== over?.id) {
73
+ const activeIndex = items.findIndex(({ id }) => id === active.id);
74
+ const overIndex = items.findIndex(({ id }) => id === over.id);
75
+
76
+ onChange(arrayMove(items, activeIndex, overIndex));
77
+ }
78
+ setActive(null);
79
+ }}
80
+ onDragCancel={() => {
81
+ setActive(null);
82
+ }}
83
+ >
84
+ <SortableContext items={items} strategy={verticalListSortingStrategy}>
85
+ <StyledListWrapper role="application">
86
+ {items.map((item, index) => (
87
+ <Fragment key={index}>{renderItem(item)}</Fragment>
88
+ ))}
89
+ </StyledListWrapper>
90
+ </SortableContext>
91
+ <DragOverlay dropAnimation={dropAnimationConfig}>
92
+ {activeItem ? renderItem(activeItem) : null}
93
+ </DragOverlay>
94
+ </DndContext>
95
+ );
96
+ }
97
+
98
+ SortableList.Item = SortableItem;
@@ -0,0 +1,20 @@
1
+ import React, { Context, FC, useContext } from 'react';
2
+
3
+ import { DragButton } from './styles.js';
4
+ import type { DragContext } from './types.js';
5
+
6
+ interface DragHandlePropsType {
7
+ context: Context<DragContext>;
8
+ }
9
+
10
+ export const DragHandle: FC<DragHandlePropsType> = ({ context }) => {
11
+ const { attributes, listeners, ref } = useContext(context);
12
+
13
+ return (
14
+ <DragButton {...attributes} {...listeners} ref={ref}>
15
+ <svg viewBox="0 0 20 20" width="13">
16
+ <path d="M7 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 2zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 14zm6-8a2 2 0 1 0-.001-4.001A2 2 0 0 0 13 6zm0 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 14z"></path>
17
+ </svg>
18
+ </DragButton>
19
+ );
20
+ };
@@ -0,0 +1,59 @@
1
+ import React, { createContext, useMemo, ChangeEvent } from 'react';
2
+ import type { CSSProperties, PropsWithChildren } from 'react';
3
+ import { useSortable } from '@dnd-kit/sortable';
4
+ import { Button, Icon } from '@adminjs/design-system';
5
+
6
+ import { DragHandle } from './DragHandle.js';
7
+ import { StyledListItem } from './styles.js';
8
+ import type { DragContext } from './types.js';
9
+
10
+ interface SortableItemPropsType {
11
+ id: string;
12
+ onDelete: (e: ChangeEvent<HTMLInputElement>, id: string) => void;
13
+ }
14
+
15
+ const SortableItemContext = createContext<DragContext>({
16
+ attributes: {},
17
+ listeners: undefined,
18
+ ref() {},
19
+ });
20
+
21
+ export function SortableItem({
22
+ children,
23
+ id,
24
+ onDelete,
25
+ }: PropsWithChildren<SortableItemPropsType>) {
26
+ const { attributes, isDragging, listeners, setNodeRef, setActivatorNodeRef } =
27
+ useSortable({ id });
28
+ const context = useMemo(
29
+ () => ({
30
+ attributes,
31
+ listeners,
32
+ ref: setActivatorNodeRef,
33
+ }),
34
+ [attributes, listeners, setActivatorNodeRef],
35
+ );
36
+
37
+ const style: CSSProperties = {
38
+ opacity: isDragging ? 0.4 : undefined,
39
+ };
40
+
41
+ return (
42
+ <SortableItemContext.Provider value={context}>
43
+ <StyledListItem ref={setNodeRef} style={style}>
44
+ <div>
45
+ <DragHandle context={SortableItemContext} />
46
+ {children}
47
+ </div>
48
+ <Button
49
+ variant="outlined"
50
+ color="danger"
51
+ size="icon"
52
+ onClick={(e: ChangeEvent<HTMLInputElement>) => onDelete(e, id)}
53
+ >
54
+ <Icon icon="X" color="red" />
55
+ </Button>
56
+ </StyledListItem>
57
+ </SortableItemContext.Provider>
58
+ );
59
+ }
@@ -0,0 +1,22 @@
1
+ import { styled } from '@adminjs/design-system/styled-components';
2
+
3
+ export const StyledListItem = styled.li`
4
+ display: flex;
5
+ justify-content: space-between;
6
+ align-items: center;
7
+ background-color: #fff;
8
+ padding: 10px 20px 10px 15px;
9
+ box-shadow:
10
+ 0 0 0 calc(1px / var(--scale-x, 1)) rgba(63, 63, 68, 0.05),
11
+ 0 1px calc(3px / var(--scale-x, 1)) 0 rgba(34, 33, 81, 0.15);
12
+ border-radius: 5px;
13
+ list-style: none;
14
+ `;
15
+
16
+ export const DragButton = styled.button`
17
+ padding: 3px;
18
+ margin-right: 15px;
19
+ cursor: move;
20
+ background: none;
21
+ border: none;
22
+ `;
@@ -0,0 +1,7 @@
1
+ import type { DraggableSyntheticListeners } from '@dnd-kit/core';
2
+
3
+ export interface DragContext {
4
+ attributes: Record<string, any>;
5
+ listeners: DraggableSyntheticListeners;
6
+ ref(node: HTMLElement | null): void;
7
+ }
@@ -0,0 +1,2 @@
1
+ export { SortableItem } from './SortableItem/SortableItem.js';
2
+ export { DragHandle } from './SortableItem/DragHandle.js';
@@ -0,0 +1 @@
1
+ export { SortableList } from './SortableList.js';
@@ -0,0 +1,9 @@
1
+ import { styled } from '@adminjs/design-system/styled-components';
2
+
3
+ export const StyledListWrapper = styled.ul`
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: 12px;
7
+ padding: 0;
8
+ list-style: none;
9
+ `;
@@ -0,0 +1,125 @@
1
+ import React, {
2
+ useState,
3
+ useEffect,
4
+ ChangeEvent,
5
+ KeyboardEvent,
6
+ SyntheticEvent,
7
+ FC,
8
+ } from "react";
9
+ import { ThemeProvider } from "styled-components";
10
+ import { EditPropertyProps } from "adminjs";
11
+ import { theme, Button, Input, Label } from "@adminjs/design-system";
12
+
13
+ import {
14
+ StyledWrapper,
15
+ StyledCustomInput,
16
+ StyledListWrapper,
17
+ StyledInputWrapper,
18
+ } from "./styles.js";
19
+
20
+ import { SortableList } from "./SortableList/SortableList.js";
21
+
22
+ type ListDataTypes = {
23
+ id: string;
24
+ value: string;
25
+ };
26
+
27
+ type StringListTypes = Omit<EditPropertyProps, "where" | "resource">;
28
+
29
+ const StringList: FC<StringListTypes> = ({ record, onChange, property }) => {
30
+ const stringListValue =
31
+ record.params?.[property.path] ?? property.props.value ?? "";
32
+
33
+ const initialList = stringListValue
34
+ ? prepareDataForList(stringListValue)
35
+ : [];
36
+
37
+ const [inputValue, setInputValue] = useState("");
38
+ const [list, setList] = useState<ListDataTypes[]>(initialList);
39
+
40
+ const serializedData = prepareDataForDatabase(list);
41
+
42
+ useEffect(() => {
43
+ onChange(property.path, serializedData);
44
+ }, [serializedData]);
45
+
46
+ return (
47
+ <ThemeProvider theme={theme}>
48
+ <Label htmlFor="custom">String List</Label>
49
+ <StyledWrapper>
50
+ <StyledListWrapper>
51
+ <SortableList
52
+ items={list}
53
+ onChange={setList}
54
+ renderItem={(item) => (
55
+ <SortableList.Item id={item.id} onDelete={handleDeleteButton}>
56
+ {item.value}
57
+ </SortableList.Item>
58
+ )}
59
+ />
60
+ </StyledListWrapper>
61
+ <StyledInputWrapper>
62
+ <Input
63
+ id="stringList"
64
+ name={property.path}
65
+ value={serializedData}
66
+ hidden
67
+ />
68
+ <StyledCustomInput
69
+ id="custom"
70
+ name="customInput"
71
+ value={inputValue}
72
+ onChange={handleInput}
73
+ onKeyPress={handleEnterPress}
74
+ />
75
+ <Button variant="outlined" onClick={handleAddButton}>
76
+ Add
77
+ </Button>
78
+ </StyledInputWrapper>
79
+ </StyledWrapper>
80
+ </ThemeProvider>
81
+ );
82
+
83
+ function handleInput(e: ChangeEvent<HTMLInputElement>) {
84
+ setInputValue(e.target.value);
85
+ }
86
+
87
+ function handleEnterPress(e: KeyboardEvent<HTMLInputElement>) {
88
+ if (e.key === "Enter") {
89
+ handleAddButton(e);
90
+ }
91
+ }
92
+
93
+ function handleAddButton(e: SyntheticEvent<HTMLInputElement>) {
94
+ e.preventDefault();
95
+
96
+ if (Boolean(inputValue)) {
97
+ setList([...list, createListObject(inputValue)]);
98
+
99
+ setInputValue("");
100
+ }
101
+ }
102
+
103
+ function handleDeleteButton(e: ChangeEvent<HTMLInputElement>, id: string) {
104
+ e.preventDefault();
105
+ const newData = list.filter((item: ListDataTypes) => item.id !== id);
106
+ setList(newData);
107
+ }
108
+
109
+ function prepareDataForDatabase(list: ListDataTypes[]) {
110
+ return list.map(({ value }) => value).join("|");
111
+ }
112
+
113
+ function prepareDataForList(str: string) {
114
+ return str.split("|").map((item) => createListObject(item));
115
+ }
116
+
117
+ function createListObject(value: string) {
118
+ return {
119
+ id: `${Date.now()}-${Math.floor(Math.random() * 1000)}`,
120
+ value: value,
121
+ };
122
+ }
123
+ };
124
+
125
+ export default StringList;
@@ -0,0 +1 @@
1
+ export { default } from "./StringList.js";
@@ -0,0 +1,20 @@
1
+ import { styled } from "@adminjs/design-system/styled-components";
2
+ import { Box, Input } from "@adminjs/design-system";
3
+
4
+ export const StyledWrapper = styled(Box)`
5
+ display: flex;
6
+ flex-direction: column;
7
+ `;
8
+
9
+ export const StyledCustomInput = styled(Input)`
10
+ width: 100%;
11
+ margin-right: 10px;
12
+ `;
13
+
14
+ export const StyledInputWrapper = styled(Box)`
15
+ display: flex;
16
+ `;
17
+
18
+ export const StyledListWrapper = styled(Box)`
19
+ margin-bottom: 15px;
20
+ `;
@@ -0,0 +1,2 @@
1
+ export { default as CustomSlug } from "./CustomSlug/CustomSlug.js";
2
+ export { default as StringList } from "./StringList/StringList.js";
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./components/index.js";
@@ -0,0 +1 @@
1
+ export { slugifyTitle } from "./slugifyTitle.js";
@@ -0,0 +1,4 @@
1
+ // This is a fix of typescript module import error
2
+ import slugify from "slugify";
3
+
4
+ export default slugify as unknown as typeof slugify.default;
@@ -0,0 +1,11 @@
1
+ import slugify from "./slugifyImport.js";
2
+
3
+ export const slugifyTitle = (title: string) => {
4
+ return slugify(title, {
5
+ replacement: "-",
6
+ remove: undefined,
7
+ lower: true,
8
+ locale: "vi",
9
+ trim: true,
10
+ });
11
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "esModuleInterop": true,
4
+ "skipLibCheck": true,
5
+ "target": "es2022",
6
+ "allowJs": true,
7
+ "resolveJsonModule": true,
8
+ "moduleDetection": "force",
9
+ "strict": true,
10
+ "noUncheckedIndexedAccess": true,
11
+ "moduleResolution": "Bundler",
12
+ "module": "ESNext",
13
+ "lib": ["es2022", "dom", "dom.iterable"],
14
+ "jsx": "react",
15
+ "noEmit": true
16
+ }
17
+ }