@evenicanpm/portal-editor-ui 1.4.1 → 1.5.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.
Files changed (35) hide show
  1. package/{src/editor.tsx → dist/editor.d.ts} +10 -14
  2. package/dist/features/editor/components/column-list/column-card/column-card-options.d.ts +9 -0
  3. package/dist/features/editor/components/column-list/column-card/column-card-title.d.ts +4 -0
  4. package/dist/features/editor/components/column-list/column-card/column-card.d.ts +8 -0
  5. package/dist/features/editor/components/column-list/column-card/drag-handle.d.ts +6 -0
  6. package/dist/features/editor/components/column-list/column-card/editable-label.d.ts +11 -0
  7. package/dist/features/editor/components/column-list/column-list-section.d.ts +12 -0
  8. package/dist/features/editor/components/column-list/column-list-skeleton.d.ts +1 -0
  9. package/dist/features/editor/components/column-list/column-list-state.d.ts +33 -0
  10. package/dist/features/editor/components/column-list/column-list-title.d.ts +5 -0
  11. package/dist/features/editor/components/column-list/column-list.d.ts +8 -0
  12. package/dist/features/editor/components/editor-dms-card.d.ts +14 -0
  13. package/dist/features/editor/components/editor-tabs.d.ts +13 -0
  14. package/dist/features/editor/components/editor.d.ts +28 -0
  15. package/{src/index.ts → dist/index.d.ts} +0 -1
  16. package/dist/tsconfig.tsbuildinfo +1 -0
  17. package/package.json +6 -3
  18. package/src/features/editor/components/column-list/column-card/column-card-options.tsx +0 -48
  19. package/src/features/editor/components/column-list/column-card/column-card-title.tsx +0 -13
  20. package/src/features/editor/components/column-list/column-card/column-card.tsx +0 -134
  21. package/src/features/editor/components/column-list/column-card/drag-handle.tsx +0 -32
  22. package/src/features/editor/components/column-list/column-card/editable-label.tsx +0 -132
  23. package/src/features/editor/components/column-list/column-list-section.tsx +0 -49
  24. package/src/features/editor/components/column-list/column-list-skeleton.tsx +0 -49
  25. package/src/features/editor/components/column-list/column-list-state.ts +0 -92
  26. package/src/features/editor/components/column-list/column-list-title.tsx +0 -18
  27. package/src/features/editor/components/column-list/column-list.tsx +0 -212
  28. package/src/features/editor/components/editor-dms-card.tsx +0 -79
  29. package/src/features/editor/components/editor-tabs.tsx +0 -42
  30. package/src/features/editor/components/editor.tsx +0 -62
  31. package/tsconfig.json +0 -28
  32. package/typedoc.json +0 -10
  33. /package/{src/features/editor/components/column-list/column-card/index.ts → dist/features/editor/components/column-list/column-card/index.d.ts} +0 -0
  34. /package/{src/features/editor/components/column-list/index.ts → dist/features/editor/components/column-list/index.d.ts} +0 -0
  35. /package/{src/features/editor/index.ts → dist/features/editor/index.d.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evenicanpm/portal-editor-ui",
3
- "version": "1.4.1",
3
+ "version": "1.5.0",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "",
@@ -14,6 +14,9 @@
14
14
  "exports": {
15
15
  ".": "./src/index.ts"
16
16
  },
17
+ "files": [
18
+ "dist"
19
+ ],
17
20
  "types": "./dist/index.d.ts",
18
21
  "devDependencies": {
19
22
  "@emotion/react": "^11.11.3",
@@ -37,8 +40,8 @@
37
40
  "@dnd-kit/core": "^6.3.1",
38
41
  "@dnd-kit/sortable": "^10.0.0",
39
42
  "@dnd-kit/utilities": "^3.2.2",
40
- "@evenicanpm/portal-types-schemas": "*",
43
+ "@evenicanpm/portal-types-schemas": "^1.5.0",
41
44
  "@tanstack/react-query": "^5.90.11"
42
45
  },
43
- "gitHead": "10194bd223a077c5f3313b342a495301ab722562"
46
+ "gitHead": "5c5e3fe19b92a17cbaf897b4cee20ee59331603f"
44
47
  }
@@ -1,48 +0,0 @@
1
- import { Checkbox, FormControlLabel, Stack } from "@mui/material";
2
-
3
- export type ColumnCardOptionsValue = {
4
- isSortable: boolean;
5
- isSearchable: boolean;
6
- };
7
-
8
- export type ColumnCardOptionsProps = {
9
- value: ColumnCardOptionsValue;
10
- onChange: (patch: Partial<ColumnCardOptionsValue>) => void;
11
- };
12
-
13
- export function ColumnCardOptions({
14
- value,
15
- onChange,
16
- }: Readonly<ColumnCardOptionsProps>) {
17
- return (
18
- <Stack
19
- direction="row"
20
- spacing={2}
21
- alignItems="center"
22
- justifyContent={{ xs: "center", lg: "flex-end" }}
23
- sx={{ width: { xs: "100%", sm: "auto" } }}
24
- >
25
- <FormControlLabel
26
- sx={{ m: 0 }}
27
- control={
28
- <Checkbox
29
- checked={value.isSortable}
30
- onChange={(e) => onChange({ isSortable: e.target.checked })}
31
- />
32
- }
33
- label="sortable"
34
- />
35
-
36
- <FormControlLabel
37
- sx={{ m: 0 }}
38
- control={
39
- <Checkbox
40
- checked={value.isSearchable}
41
- onChange={(e) => onChange({ isSearchable: e.target.checked })}
42
- />
43
- }
44
- label="searchable"
45
- />
46
- </Stack>
47
- );
48
- }
@@ -1,13 +0,0 @@
1
- import { Typography } from "@mui/material";
2
-
3
- export type ColumnCardTitleProps = {
4
- value: string;
5
- };
6
-
7
- export function ColumnCardTitle({ value }: Readonly<ColumnCardTitleProps>) {
8
- return (
9
- <Typography fontWeight={700} noWrap>
10
- {value}
11
- </Typography>
12
- );
13
- }
@@ -1,134 +0,0 @@
1
- import { useSortable } from "@dnd-kit/sortable";
2
- import { CSS } from "@dnd-kit/utilities";
3
- import type { Column } from "@evenicanpm/portal-types-schemas";
4
- import CloseIcon from "@mui/icons-material/Close";
5
- import { Box, Card, CardContent, IconButton, Stack } from "@mui/material";
6
- import * as React from "react";
7
- import { ColumnCardOptions } from "./column-card-options";
8
- import { ColumnCardTitle } from "./column-card-title";
9
- import { DragHandle } from "./drag-handle";
10
- import { EditableLabel } from "./editable-label";
11
-
12
- export type ColumnCardProps = {
13
- field: Column;
14
- onChange: (id: string, patch: Partial<Column>) => void;
15
- onRemove: (id: string) => void;
16
- };
17
-
18
- export function ColumnCard({
19
- field,
20
- onChange,
21
- onRemove,
22
- }: Readonly<ColumnCardProps>) {
23
- const [isEditingLabel, setIsEditingLabel] = React.useState(false);
24
-
25
- const {
26
- attributes,
27
- listeners,
28
- setNodeRef,
29
- transform,
30
- transition,
31
- isDragging,
32
- } = useSortable({ id: field.Data, disabled: isEditingLabel });
33
-
34
- const style: React.CSSProperties = {
35
- transform: CSS.Transform.toString(transform),
36
- transition,
37
- };
38
-
39
- return (
40
- <Card
41
- ref={setNodeRef}
42
- style={style}
43
- variant="outlined"
44
- sx={{
45
- mb: 2,
46
- height: { sm: 130, md: 100 },
47
- borderRadius: 2,
48
- bgcolor: isDragging ? "grey.100" : "background.paper",
49
- boxShadow: isDragging ? 3 : 0,
50
- }}
51
- >
52
- <CardContent sx={{ py: 2, "&:last-child": { pb: 2 } }}>
53
- <Stack
54
- direction={{ xs: "column", sm: "row" }}
55
- spacing={{ xs: 1.25, sm: 1 }}
56
- alignItems={{ xs: "stretch", sm: "center" }}
57
- >
58
- <Stack
59
- direction="row"
60
- spacing={1}
61
- alignItems="center"
62
- sx={{ width: "100%" }}
63
- >
64
- <DragHandle attributes={attributes} listeners={listeners} />
65
-
66
- <Box sx={{ flex: 1, minWidth: 0 }}>
67
- <Box
68
- sx={{
69
- display: "flex",
70
- alignItems: "center",
71
- gap: 1,
72
- }}
73
- >
74
- <ColumnCardTitle value={field.Data} />
75
-
76
- {/* Mobile close icon */}
77
- <IconButton
78
- onClick={() => onRemove(field.Data)}
79
- aria-label={`Remove ${field.Data}`}
80
- color="error"
81
- size="small"
82
- sx={{
83
- ml: "auto",
84
- display: { xs: "inline-flex", sm: "none" },
85
- }}
86
- >
87
- <CloseIcon />
88
- </IconButton>
89
- </Box>
90
-
91
- <Box
92
- onFocusCapture={(e) => {
93
- if (e.target instanceof HTMLInputElement)
94
- setIsEditingLabel(true);
95
- }}
96
- onBlurCapture={() => setIsEditingLabel(false)}
97
- >
98
- <EditableLabel
99
- value={field.Label ?? ""}
100
- onCommit={(next) => onChange(field.Data, { Label: next })}
101
- />
102
- </Box>
103
- </Box>
104
- </Stack>
105
-
106
- <ColumnCardOptions
107
- value={{
108
- isSortable: !!field.IsSortable,
109
- isSearchable: !!field.IsSearchable,
110
- }}
111
- onChange={(patch) => {
112
- const next: Partial<Column> = {};
113
- if (patch.isSortable !== undefined)
114
- next.IsSortable = patch.isSortable;
115
- if (patch.isSearchable !== undefined)
116
- next.IsSearchable = patch.isSearchable;
117
- onChange(field.Data, next);
118
- }}
119
- />
120
-
121
- {/* Desktop close icon */}
122
- <IconButton
123
- onClick={() => onRemove(field.Data)}
124
- aria-label={`Remove ${field.Data}`}
125
- color="error"
126
- sx={{ display: { xs: "none", sm: "inline-flex" } }}
127
- >
128
- <CloseIcon />
129
- </IconButton>
130
- </Stack>
131
- </CardContent>
132
- </Card>
133
- );
134
- }
@@ -1,32 +0,0 @@
1
- import type {
2
- DraggableAttributes,
3
- DraggableSyntheticListeners,
4
- } from "@dnd-kit/core";
5
- import DragIndicatorIcon from "@mui/icons-material/DragIndicator";
6
- import { Box, IconButton } from "@mui/material";
7
-
8
- export type DragHandleProps = {
9
- attributes: DraggableAttributes;
10
- listeners?: DraggableSyntheticListeners;
11
- };
12
-
13
- export function DragHandle({
14
- attributes,
15
- listeners,
16
- }: Readonly<DragHandleProps>) {
17
- return (
18
- <Box
19
- sx={{
20
- display: "flex",
21
- alignItems: "center",
22
- cursor: "grab",
23
- touchAction: "none",
24
- flexShrink: 0,
25
- }}
26
- >
27
- <IconButton size="small" {...attributes} {...listeners}>
28
- <DragIndicatorIcon />
29
- </IconButton>
30
- </Box>
31
- );
32
- }
@@ -1,132 +0,0 @@
1
- import CheckIcon from "@mui/icons-material/Check";
2
- import CloseIcon from "@mui/icons-material/Close";
3
- import EditIcon from "@mui/icons-material/Edit";
4
- import {
5
- Box,
6
- IconButton,
7
- Stack,
8
- TextField,
9
- Tooltip,
10
- Typography,
11
- } from "@mui/material";
12
- import * as React from "react";
13
-
14
- export type EditableLabelProps = {
15
- value: string;
16
- onCommit: (next: string) => void;
17
- labelPrefix?: string;
18
- inputWidth?: { xs: string | number; sm: string | number };
19
- };
20
-
21
- export function EditableLabel({
22
- value,
23
- onCommit,
24
- labelPrefix = "label:",
25
- inputWidth = { xs: "100%", sm: 260 },
26
- }: Readonly<EditableLabelProps>) {
27
- const [isEditing, setIsEditing] = React.useState(false);
28
- const [draft, setDraft] = React.useState(value);
29
- const inputRef = React.useRef<HTMLInputElement>(null);
30
-
31
- React.useEffect(() => {
32
- if (!isEditing) return;
33
- setDraft(value);
34
- queueMicrotask(() => inputRef.current?.focus());
35
- }, [isEditing, value]);
36
-
37
- const commit = () => {
38
- const next = draft.trim();
39
- setIsEditing(false);
40
- if (next !== value) onCommit(next);
41
- };
42
-
43
- const cancel = () => {
44
- setIsEditing(false);
45
- setDraft(value);
46
- };
47
-
48
- return (
49
- <Stack
50
- direction={{ xs: "row" }}
51
- spacing={{ xs: 1 }}
52
- alignItems={{ xs: "center" }}
53
- sx={{ mt: 0.25 }}
54
- >
55
- <Typography variant="body2" color="text.secondary" sx={{ flexShrink: 0 }}>
56
- {labelPrefix}
57
- </Typography>
58
-
59
- <Box
60
- sx={{
61
- display: "flex",
62
- alignItems: "center",
63
- gap: 1,
64
- minWidth: 0,
65
- width: "100%",
66
- }}
67
- >
68
- {isEditing ? (
69
- <>
70
- <TextField
71
- inputRef={inputRef}
72
- value={draft}
73
- onChange={(e) => setDraft(e.target.value)}
74
- size="small"
75
- variant="outlined"
76
- onKeyDown={(e) => {
77
- if (e.key === "Enter") commit();
78
- if (e.key === "Escape") cancel();
79
- }}
80
- onBlur={commit}
81
- sx={{
82
- width: inputWidth,
83
- maxWidth: "100%",
84
- "& .MuiOutlinedInput-root": { height: 32 },
85
- "& .MuiOutlinedInput-input": {
86
- padding: "6px 8px",
87
- fontSize: "0.875rem",
88
- },
89
- }}
90
- />
91
-
92
- <Tooltip title="Save">
93
- <IconButton
94
- size="small"
95
- onMouseDown={(e) => e.preventDefault()}
96
- onClick={commit}
97
- >
98
- <CheckIcon fontSize="small" />
99
- </IconButton>
100
- </Tooltip>
101
-
102
- <Tooltip title="Cancel">
103
- <IconButton
104
- size="small"
105
- onMouseDown={(e) => e.preventDefault()}
106
- onClick={cancel}
107
- >
108
- <CloseIcon fontSize="small" />
109
- </IconButton>
110
- </Tooltip>
111
- </>
112
- ) : (
113
- <>
114
- <Typography
115
- variant="body2"
116
- color="text.secondary"
117
- sx={{ fontWeight: 600 }}
118
- >
119
- {value}
120
- </Typography>
121
-
122
- <Tooltip title="Edit label">
123
- <IconButton size="small" onClick={() => setIsEditing(true)}>
124
- <EditIcon fontSize="small" />
125
- </IconButton>
126
- </Tooltip>
127
- </>
128
- )}
129
- </Box>
130
- </Stack>
131
- );
132
- }
@@ -1,49 +0,0 @@
1
- "use client";
2
- import { Paper } from "@mui/material";
3
- import { useQuery } from "@tanstack/react-query";
4
- import type { EditorQueries } from "../editor";
5
- import { ColumnList } from "./column-list";
6
- import { ColumnListSkeleton } from "./column-list-skeleton";
7
- import { ColumnListTitle } from "./column-list-title";
8
-
9
- interface ColumnProps {
10
- currentResourceKey: string;
11
- editorQueries: EditorQueries;
12
- }
13
-
14
- /**
15
- * The view config column list section that handles
16
- * fetching the initial view config data thats stored in the db
17
- * and handles loading state skeleton view while data is pending
18
- */
19
- export function ColumnListSection({
20
- currentResourceKey,
21
- editorQueries,
22
- }: Readonly<ColumnProps>) {
23
- const { data: config, isPending } = useQuery(
24
- editorQueries.viewConfigQuery(currentResourceKey),
25
- );
26
-
27
- return (
28
- <Paper
29
- elevation={1}
30
- sx={{
31
- p: { xs: 2, sm: 3 },
32
- mb: 3,
33
- borderRadius: 2,
34
- }}
35
- >
36
- <ColumnListTitle selectedResourceKey={currentResourceKey} />
37
- {isPending || !config ? (
38
- <ColumnListSkeleton />
39
- ) : (
40
- <ColumnList
41
- key={currentResourceKey}
42
- resourceKey={currentResourceKey}
43
- initialState={config}
44
- editorQueries={editorQueries}
45
- />
46
- )}
47
- </Paper>
48
- );
49
- }
@@ -1,49 +0,0 @@
1
- import { Box, Skeleton } from "@mui/material";
2
-
3
- const CARD_HEIGHT = {
4
- sm: 130,
5
- md: 100,
6
- };
7
-
8
- export function ColumnListSkeleton() {
9
- const skeletonKeys = ["s1", "s2", "s3", "s4"];
10
- return (
11
- <>
12
- <Box
13
- sx={{
14
- display: "flex",
15
- justifyContent: "flex-end",
16
- alignItems: "center",
17
- position: "relative",
18
- mb: 2,
19
- height: 40,
20
- }}
21
- >
22
- <Skeleton
23
- variant="rounded"
24
- animation="wave"
25
- sx={{
26
- minWidth: 150,
27
- height: 36.5, // Standard MUI 'medium' button height
28
- borderRadius: 1, // Match MUI default button border-radius (4px)
29
- ml: "auto",
30
- }}
31
- />
32
- </Box>
33
- {skeletonKeys.map((key) => (
34
- <Skeleton
35
- key={key}
36
- variant="rounded"
37
- animation="wave"
38
- sx={{
39
- width: "100%",
40
- mb: 2,
41
- height: { ...CARD_HEIGHT },
42
- borderRadius: 2,
43
- opacity: 0.4, // Softens the "block" look
44
- }}
45
- />
46
- ))}
47
- </>
48
- );
49
- }
@@ -1,92 +0,0 @@
1
- import type { Column } from "@evenicanpm/portal-types-schemas";
2
-
3
- /**
4
- * Reassigns sequential ColumnOrder values (1..n) to visible fields.
5
- */
6
- export function normalizeVisibleOrders(list: Column[]): Column[] {
7
- return list.map((f, idx) => ({ ...f, ColumnOrder: idx + 1 }));
8
- }
9
-
10
- /**
11
- * Returns fields that are currently visible (ColumnOrder not null),
12
- * sorted by ColumnOrder.
13
- */
14
- export function getVisibleFields(fields: Column[]): Column[] {
15
- return fields
16
- .filter((f) => f.ColumnOrder != null)
17
- .sort((a, b) => (a.ColumnOrder ?? 1) - (b.ColumnOrder ?? 0));
18
- }
19
-
20
- /**
21
- * Returns fields that are currently removed (ColumnOrder null),
22
- * sorted alphabetically by label or data key.
23
- */
24
- export function getRemovedFields(fields: Column[]): Column[] {
25
- return fields
26
- .filter((f) => f.ColumnOrder == null)
27
- .sort((a, b) => (a.Label ?? a.Data).localeCompare(b.Label ?? b.Data));
28
- }
29
-
30
- /**
31
- * Applies a partial update to a single field by id.
32
- */
33
- export function patchField(
34
- fields: Column[],
35
- id: string,
36
- patch: Partial<Column>,
37
- ): Column[] {
38
- return fields.map((f) => (f.Data === id ? { ...f, ...patch } : f));
39
- }
40
-
41
- /**
42
- * Removes a field from the visible list by setting ColumnOrder to null
43
- * and re-normalizes remaining visible fields.
44
- */
45
- export function removeField(fields: Column[], id: string): Column[] {
46
- const next = fields.map((f) =>
47
- f.Data === id ? { ...f, ColumnOrder: null } : f,
48
- );
49
-
50
- const stillVisible = getVisibleFields(next);
51
- const renumbered = normalizeVisibleOrders(stillVisible);
52
-
53
- return next.map((f) => renumbered.find((r) => r.Data === f.Data) ?? f);
54
- }
55
-
56
- /**
57
- * Adds a previously removed field back to the end of the visible list
58
- * and re-normalizes ordering.
59
- */
60
- export function addFieldToEnd(fields: Column[], id: string): Column[] {
61
- const next = fields.map((f) =>
62
- f.Data === id ? { ...f, ColumnOrder: 999999 } : f,
63
- );
64
-
65
- const stillVisible = getVisibleFields(next);
66
- const renumbered = normalizeVisibleOrders(stillVisible);
67
-
68
- return next.map((f) => renumbered.find((r) => r.Data === f.Data) ?? f);
69
- }
70
-
71
- /**
72
- * Reorders visible fields after a drag operation and updates ColumnOrder.
73
- */
74
- export function reorderVisibleFields(
75
- fields: Column[],
76
- activeId: string,
77
- overId: string,
78
- arrayMoveFn: <T>(array: T[], from: number, to: number) => T[],
79
- ): Column[] {
80
- const visible = getVisibleFields(fields);
81
-
82
- const oldIndex = visible.findIndex((f) => f.Data === activeId);
83
- const newIndex = visible.findIndex((f) => f.Data === overId);
84
-
85
- if (oldIndex < 0 || newIndex < 0) return fields;
86
-
87
- const moved = normalizeVisibleOrders(
88
- arrayMoveFn(visible, oldIndex, newIndex),
89
- );
90
-
91
- return fields.map((f) => moved.find((m) => m.Data === f.Data) ?? f);
92
- }
@@ -1,18 +0,0 @@
1
- "use client";
2
-
3
- import { Box, Typography } from "@mui/material";
4
-
5
- interface Props {
6
- selectedResourceKey: string;
7
- }
8
-
9
- export function ColumnListTitle({ selectedResourceKey }: Readonly<Props>) {
10
- return (
11
- <Typography variant="h6" gutterBottom fontWeight={600}>
12
- Configure Fields for:{" "}
13
- <Box component="span" color="primary.main">
14
- {selectedResourceKey}
15
- </Box>
16
- </Typography>
17
- );
18
- }