@dxos/plugin-kanban 0.7.4 → 0.7.5-main.9d26e3a
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/dist/lib/browser/chunk-JZBAL6SW.mjs +56 -0
- package/dist/lib/browser/chunk-JZBAL6SW.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +218 -109
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/types.mjs +12 -0
- package/dist/lib/node/chunk-BE2FRW7E.cjs +77 -0
- package/dist/lib/node/chunk-BE2FRW7E.cjs.map +7 -0
- package/dist/lib/node/index.cjs +210 -102
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/{types/index.cjs → types.cjs} +8 -10
- package/dist/lib/node/types.cjs.map +7 -0
- package/dist/lib/node-esm/chunk-L7JM7LGJ.mjs +57 -0
- package/dist/lib/node-esm/chunk-L7JM7LGJ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +218 -109
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/types.mjs +13 -0
- package/dist/types/src/KanbanPlugin.d.ts.map +1 -1
- package/dist/types/src/components/KanbanContainer.d.ts +7 -0
- package/dist/types/src/components/KanbanContainer.d.ts.map +1 -0
- package/dist/types/src/components/KanbanViewEditor.d.ts +8 -0
- package/dist/types/src/components/KanbanViewEditor.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +1 -3
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +32 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +61 -0
- package/dist/types/src/types.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +21 -23
- package/src/KanbanPlugin.tsx +50 -78
- package/src/components/KanbanContainer.tsx +79 -0
- package/src/components/KanbanViewEditor.tsx +72 -0
- package/src/components/index.ts +2 -4
- package/src/translations.ts +8 -2
- package/src/{types/types.ts → types.ts} +42 -11
- package/dist/lib/browser/KanbanMain-I5TMXNIY.mjs +0 -444
- package/dist/lib/browser/KanbanMain-I5TMXNIY.mjs.map +0 -7
- package/dist/lib/browser/chunk-4Y4TZ47E.mjs +0 -47
- package/dist/lib/browser/chunk-4Y4TZ47E.mjs.map +0 -7
- package/dist/lib/browser/types/index.mjs +0 -14
- package/dist/lib/node/KanbanMain-4OWAWTS4.cjs +0 -453
- package/dist/lib/node/KanbanMain-4OWAWTS4.cjs.map +0 -7
- package/dist/lib/node/chunk-LTR4WYI2.cjs +0 -67
- package/dist/lib/node/chunk-LTR4WYI2.cjs.map +0 -7
- package/dist/lib/node/types/index.cjs.map +0 -7
- package/dist/lib/node-esm/KanbanMain-PJC2JMJH.mjs +0 -445
- package/dist/lib/node-esm/KanbanMain-PJC2JMJH.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-2ZBX5F7L.mjs +0 -48
- package/dist/lib/node-esm/chunk-2ZBX5F7L.mjs.map +0 -7
- package/dist/lib/node-esm/types/index.mjs +0 -15
- package/dist/types/src/components/KanbanBoard.d.ts +0 -6
- package/dist/types/src/components/KanbanBoard.d.ts.map +0 -1
- package/dist/types/src/components/KanbanCard.d.ts +0 -9
- package/dist/types/src/components/KanbanCard.d.ts.map +0 -1
- package/dist/types/src/components/KanbanColumn.d.ts +0 -14
- package/dist/types/src/components/KanbanColumn.d.ts.map +0 -1
- package/dist/types/src/components/KanbanMain.d.ts +0 -7
- package/dist/types/src/components/KanbanMain.d.ts.map +0 -1
- package/dist/types/src/components/util.d.ts +0 -7
- package/dist/types/src/components/util.d.ts.map +0 -1
- package/dist/types/src/stories/testing.d.ts +0 -19
- package/dist/types/src/stories/testing.d.ts.map +0 -1
- package/dist/types/src/types/index.d.ts +0 -3
- package/dist/types/src/types/index.d.ts.map +0 -1
- package/dist/types/src/types/kanban.d.ts +0 -76
- package/dist/types/src/types/kanban.d.ts.map +0 -1
- package/dist/types/src/types/types.d.ts +0 -18
- package/dist/types/src/types/types.d.ts.map +0 -1
- package/src/components/KanbanBoard.tsx +0 -195
- package/src/components/KanbanCard.tsx +0 -82
- package/src/components/KanbanColumn.tsx +0 -143
- package/src/components/KanbanMain.tsx +0 -37
- package/src/components/util.ts +0 -38
- package/src/stories/testing.ts +0 -29
- package/src/types/index.ts +0 -6
- package/src/types/kanban.ts +0 -22
- /package/dist/lib/browser/{types/index.mjs.map → types.mjs.map} +0 -0
- /package/dist/lib/node-esm/{types/index.mjs.map → types.mjs.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"KanbanMain.d.ts","sourceRoot":"","sources":["../../../../src/components/KanbanMain.tsx"],"names":[],"mappings":"AAIA,OAAc,EAAE,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAQvC,OAAO,EAAE,KAAK,UAAU,EAAsD,MAAM,UAAU,CAAC;AAE/F,QAAA,MAAM,UAAU,EAAE,EAAE,CAAC;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,CAoB1C,CAAC;AAEF,eAAe,UAAU,CAAC"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { type KanbanColumnType, type Location } from '../types';
|
|
2
|
-
export declare const useSubscription: (data: any) => void;
|
|
3
|
-
/**
|
|
4
|
-
* Find the column or item within the model.
|
|
5
|
-
*/
|
|
6
|
-
export declare const findLocation: (columns: KanbanColumnType[], id: string) => Location | undefined;
|
|
7
|
-
//# sourceMappingURL=util.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../../src/components/util.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGhE,eAAO,MAAM,eAAe,SAAU,GAAG,SAOxC,CAAC;AAEF;;GAEG;AAEH,eAAO,MAAM,YAAY,YAAa,gBAAgB,EAAE,MAAM,MAAM,KAAG,QAAQ,GAAG,SAYjF,CAAC"}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export declare const createKanban: () => import("@dxos/live-object").ReactiveObject<{
|
|
2
|
-
name?: string | undefined;
|
|
3
|
-
columns: import("packages/core/echo/echo-schema/dist/types/src").Ref<{
|
|
4
|
-
name?: string | undefined;
|
|
5
|
-
index?: string | undefined;
|
|
6
|
-
items: import("packages/core/echo/echo-schema/dist/types/src").Ref<{
|
|
7
|
-
object?: import("packages/core/echo/echo-schema/dist/types/src").Ref<import("packages/core/echo/echo-schema/dist/types/src").Expando>;
|
|
8
|
-
name?: string | undefined;
|
|
9
|
-
index?: string | undefined;
|
|
10
|
-
} & {
|
|
11
|
-
id: string;
|
|
12
|
-
}>[];
|
|
13
|
-
} & {
|
|
14
|
-
id: string;
|
|
15
|
-
}>[];
|
|
16
|
-
} & {
|
|
17
|
-
id: string;
|
|
18
|
-
}>;
|
|
19
|
-
//# sourceMappingURL=testing.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../../../src/stories/testing.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;EAkBxB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/types/index.ts"],"names":[],"mappings":"AAIA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC"}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { Expando, ref, S } from '@dxos/echo-schema';
|
|
2
|
-
declare const KanbanItemType_base: import("@dxos/echo-schema").AbstractTypedObject<{
|
|
3
|
-
object?: import("@dxos/echo-schema").Ref<Expando>;
|
|
4
|
-
name?: string | undefined;
|
|
5
|
-
index?: string | undefined;
|
|
6
|
-
} & {
|
|
7
|
-
id: string;
|
|
8
|
-
}, S.Struct.Encoded<{
|
|
9
|
-
object: S.optional<ref<Expando>>;
|
|
10
|
-
name: S.optional<typeof S.String>;
|
|
11
|
-
index: S.optional<typeof S.String>;
|
|
12
|
-
}>>;
|
|
13
|
-
export declare class KanbanItemType extends KanbanItemType_base {
|
|
14
|
-
}
|
|
15
|
-
declare const KanbanColumnType_base: import("@dxos/echo-schema").AbstractTypedObject<{
|
|
16
|
-
name?: string | undefined;
|
|
17
|
-
index?: string | undefined;
|
|
18
|
-
items: import("@dxos/echo-schema").Ref<{
|
|
19
|
-
object?: import("@dxos/echo-schema").Ref<Expando>;
|
|
20
|
-
name?: string | undefined;
|
|
21
|
-
index?: string | undefined;
|
|
22
|
-
} & {
|
|
23
|
-
id: string;
|
|
24
|
-
}>[];
|
|
25
|
-
} & {
|
|
26
|
-
id: string;
|
|
27
|
-
}, S.Struct.Encoded<{
|
|
28
|
-
name: S.optional<typeof S.String>;
|
|
29
|
-
index: S.optional<typeof S.String>;
|
|
30
|
-
items: S.mutable<S.Array$<ref<{
|
|
31
|
-
object?: import("@dxos/echo-schema").Ref<Expando>;
|
|
32
|
-
name?: string | undefined;
|
|
33
|
-
index?: string | undefined;
|
|
34
|
-
} & {
|
|
35
|
-
id: string;
|
|
36
|
-
}>>>;
|
|
37
|
-
}>>;
|
|
38
|
-
export declare class KanbanColumnType extends KanbanColumnType_base {
|
|
39
|
-
}
|
|
40
|
-
declare const KanbanType_base: import("@dxos/echo-schema").AbstractTypedObject<{
|
|
41
|
-
name?: string | undefined;
|
|
42
|
-
columns: import("@dxos/echo-schema").Ref<{
|
|
43
|
-
name?: string | undefined;
|
|
44
|
-
index?: string | undefined;
|
|
45
|
-
items: import("@dxos/echo-schema").Ref<{
|
|
46
|
-
object?: import("@dxos/echo-schema").Ref<Expando>;
|
|
47
|
-
name?: string | undefined;
|
|
48
|
-
index?: string | undefined;
|
|
49
|
-
} & {
|
|
50
|
-
id: string;
|
|
51
|
-
}>[];
|
|
52
|
-
} & {
|
|
53
|
-
id: string;
|
|
54
|
-
}>[];
|
|
55
|
-
} & {
|
|
56
|
-
id: string;
|
|
57
|
-
}, S.Struct.Encoded<{
|
|
58
|
-
name: S.optional<typeof S.String>;
|
|
59
|
-
columns: S.mutable<S.Array$<ref<{
|
|
60
|
-
name?: string | undefined;
|
|
61
|
-
index?: string | undefined;
|
|
62
|
-
items: import("@dxos/echo-schema").Ref<{
|
|
63
|
-
object?: import("@dxos/echo-schema").Ref<Expando>;
|
|
64
|
-
name?: string | undefined;
|
|
65
|
-
index?: string | undefined;
|
|
66
|
-
} & {
|
|
67
|
-
id: string;
|
|
68
|
-
}>[];
|
|
69
|
-
} & {
|
|
70
|
-
id: string;
|
|
71
|
-
}>>>;
|
|
72
|
-
}>>;
|
|
73
|
-
export declare class KanbanType extends KanbanType_base {
|
|
74
|
-
}
|
|
75
|
-
export {};
|
|
76
|
-
//# sourceMappingURL=kanban.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"kanban.d.ts","sourceRoot":"","sources":["../../../../src/types/kanban.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAe,MAAM,mBAAmB,CAAC;;;;;;;;;;;;AAEjE,qBAAa,cAAe,SAAQ,mBAIlC;CAAG;;;;;;;;;;;;;;;;;;;;;;;;AAEL,qBAAa,gBAAiB,SAAQ,qBAIpC;CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEL,qBAAa,UAAW,SAAQ,eAG9B;CAAG"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { GraphBuilderProvides, IntentResolverProvides, MetadataRecordsProvides, SurfaceProvides, TranslationsProvides } from '@dxos/app-framework';
|
|
2
|
-
import { type SchemaProvides } from '@dxos/plugin-space';
|
|
3
|
-
import { type KanbanColumnType, type KanbanItemType, type KanbanType } from './kanban';
|
|
4
|
-
export declare enum KanbanAction {
|
|
5
|
-
CREATE = "dxos.org/plugin/kanban/action/create"
|
|
6
|
-
}
|
|
7
|
-
export type KanbanPluginProvides = SurfaceProvides & IntentResolverProvides & GraphBuilderProvides & MetadataRecordsProvides & TranslationsProvides & SchemaProvides;
|
|
8
|
-
export interface KanbanModel {
|
|
9
|
-
root: KanbanType;
|
|
10
|
-
createColumn(): KanbanColumnType;
|
|
11
|
-
createItem(column: KanbanColumnType): KanbanItemType;
|
|
12
|
-
}
|
|
13
|
-
export type Location = {
|
|
14
|
-
column: KanbanColumnType;
|
|
15
|
-
item?: KanbanItemType;
|
|
16
|
-
idx?: number;
|
|
17
|
-
};
|
|
18
|
-
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/types/types.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACtB,uBAAuB,EACvB,eAAe,EACf,oBAAoB,EACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAcvF,oBAAY,YAAY;IACtB,MAAM,yCAA4B;CACnC;AAED,MAAM,MAAM,oBAAoB,GAAG,eAAe,GAChD,sBAAsB,GACtB,oBAAoB,GACpB,uBAAuB,GACvB,oBAAoB,GACpB,cAAc,CAAC;AAUjB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,UAAU,CAAC;IACjB,YAAY,IAAI,gBAAgB,CAAC;IACjC,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,cAAc,CAAC;CACtD;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,EAAE,gBAAgB,CAAC;IACzB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC"}
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2023 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
DndContext,
|
|
7
|
-
type DragEndEvent,
|
|
8
|
-
type DragMoveEvent,
|
|
9
|
-
type DragOverEvent,
|
|
10
|
-
DragOverlay,
|
|
11
|
-
type DragStartEvent,
|
|
12
|
-
type Modifier,
|
|
13
|
-
MouseSensor,
|
|
14
|
-
useSensor,
|
|
15
|
-
} from '@dnd-kit/core';
|
|
16
|
-
import { horizontalListSortingStrategy, SortableContext } from '@dnd-kit/sortable';
|
|
17
|
-
import React, { type FC, useEffect, useState } from 'react';
|
|
18
|
-
|
|
19
|
-
import { createSubscription } from '@dxos/react-client/echo';
|
|
20
|
-
import { arrayMove, nonNullable } from '@dxos/util';
|
|
21
|
-
|
|
22
|
-
import { KanbanCardComponent } from './KanbanCard';
|
|
23
|
-
import { type ItemsMapper, KanbanColumnComponent, KanbanColumnComponentPlaceholder } from './KanbanColumn';
|
|
24
|
-
import { findLocation, useSubscription } from './util';
|
|
25
|
-
import type { KanbanColumnType, KanbanItemType, Location, KanbanModel } from '../types';
|
|
26
|
-
|
|
27
|
-
// TODO(burdon): Touch sensors.
|
|
28
|
-
// TODO(burdon): Prevent browser nav back when swiping left/right.
|
|
29
|
-
// TODO(burdon): Consistently use FC?
|
|
30
|
-
export const KanbanBoard: FC<{ model: KanbanModel }> = ({ model }) => {
|
|
31
|
-
const kanban = model.root;
|
|
32
|
-
// TODO(wittjosiah): Remove?
|
|
33
|
-
useSubscription(kanban.columns);
|
|
34
|
-
|
|
35
|
-
// TODO(burdon): Remove since now uses ECHO.
|
|
36
|
-
const [_, setIter] = useState([]);
|
|
37
|
-
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
const handle = createSubscription(() => setIter([]));
|
|
40
|
-
handle.update([kanban.columns]);
|
|
41
|
-
return () => handle.unsubscribe();
|
|
42
|
-
}, []);
|
|
43
|
-
|
|
44
|
-
const mouseSensor = useSensor(MouseSensor, {
|
|
45
|
-
activationConstraint: {
|
|
46
|
-
distance: 8,
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
// Dragging column.
|
|
51
|
-
// TODO(burdon): Dragging column causes flickering when dragging left to first column.
|
|
52
|
-
const [draggingColumn, setDraggingColumn] = useState<KanbanColumnType | undefined>();
|
|
53
|
-
|
|
54
|
-
// Dragging item.
|
|
55
|
-
const [draggingItem, setDraggingItem] = useState<{ source: Location; target?: Location }>();
|
|
56
|
-
// While dragging, temporarily remap which items should be visible inside each column.
|
|
57
|
-
const itemMapper: ItemsMapper = (column: string, items: KanbanItemType[]) => {
|
|
58
|
-
const { source, target } = draggingItem ?? {};
|
|
59
|
-
if (source && target) {
|
|
60
|
-
if (source?.column.id !== target?.column.id && (column === source?.column.id || column === target?.column.id)) {
|
|
61
|
-
const modified = [...items];
|
|
62
|
-
if (column === source.column.id) {
|
|
63
|
-
// Temporarily remove from old column.
|
|
64
|
-
modified.splice(source.idx!, 1);
|
|
65
|
-
} else if (column === target.column.id) {
|
|
66
|
-
// Temporarily insert into new column.
|
|
67
|
-
// TODO(burdon): Use ref to track item being temporarily moved.
|
|
68
|
-
modified.splice(target.idx ?? modified.length, 0, source.item!);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return modified;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return items;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const handleDragStart = ({ active }: DragStartEvent) => {
|
|
79
|
-
kanban.columns.filter(nonNullable).forEach((column) => {
|
|
80
|
-
if (column.id === active.id) {
|
|
81
|
-
setDraggingColumn(column);
|
|
82
|
-
} else {
|
|
83
|
-
const idx = column.items.filter(nonNullable).findIndex((item) => item.id === active.id);
|
|
84
|
-
if (idx !== -1) {
|
|
85
|
-
setDraggingItem({ source: { column, item: column.items![idx], idx } });
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const handleDragMove = (event: DragMoveEvent) => {};
|
|
92
|
-
|
|
93
|
-
const handleDragOver = ({ active, over }: DragOverEvent) => {
|
|
94
|
-
if (draggingItem) {
|
|
95
|
-
const { source } = draggingItem;
|
|
96
|
-
const target = findLocation(kanban.columns.filter(nonNullable), over?.id as string);
|
|
97
|
-
if (active.id !== over?.id) {
|
|
98
|
-
setDraggingItem({ source, target });
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
// TODO(burdon): Call model to update.
|
|
104
|
-
const handleDragEnd = (event: DragEndEvent) => {
|
|
105
|
-
if (draggingColumn) {
|
|
106
|
-
const { active, over } = event;
|
|
107
|
-
const oldIndex = kanban.columns.filter(nonNullable).findIndex((column) => column.id === active.id);
|
|
108
|
-
const newIndex = kanban.columns.filter(nonNullable).findIndex((column) => column.id === over?.id);
|
|
109
|
-
arrayMove(kanban.columns, oldIndex, newIndex);
|
|
110
|
-
} else if (draggingItem) {
|
|
111
|
-
const { source, target } = draggingItem;
|
|
112
|
-
if (source.column.id === target!.column.id) {
|
|
113
|
-
if (target!.idx !== undefined) {
|
|
114
|
-
arrayMove(source.column.items!, source.idx!, target!.idx);
|
|
115
|
-
}
|
|
116
|
-
} else {
|
|
117
|
-
source.column.items!.splice(source.idx!, 1);
|
|
118
|
-
// TODO(burdon): Incorrect position when moving to new column.
|
|
119
|
-
target!.column.items!.splice(target!.idx ?? target!.column.items!.length, 0, source.item!);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
setDraggingColumn(undefined);
|
|
124
|
-
setDraggingItem(undefined);
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const handleDragCancel = () => {
|
|
128
|
-
setDraggingColumn(undefined);
|
|
129
|
-
setDraggingItem(undefined);
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
const handleCreateColumn = () => {
|
|
133
|
-
const column = model.createColumn();
|
|
134
|
-
kanban.columns.splice(kanban.columns.length, 0, column);
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
// TODO(burdon): Move to model.
|
|
138
|
-
const handleDeleteColumn = (id: string) => {
|
|
139
|
-
const index = kanban.columns.filter(nonNullable).findIndex((column) => column.id === id);
|
|
140
|
-
if (index >= 0) {
|
|
141
|
-
kanban.columns.splice(index, 1);
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
const customModifier: Modifier = ({ transform }) => {
|
|
146
|
-
if (draggingColumn) {
|
|
147
|
-
return {
|
|
148
|
-
...transform,
|
|
149
|
-
y: 0,
|
|
150
|
-
};
|
|
151
|
-
} else {
|
|
152
|
-
return transform;
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
return (
|
|
157
|
-
<div className='flex overflow-x-scroll'>
|
|
158
|
-
<div className='flex m-4 space-x-4 snap-x'>
|
|
159
|
-
<DndContext
|
|
160
|
-
sensors={[mouseSensor]}
|
|
161
|
-
modifiers={[customModifier]}
|
|
162
|
-
onDragStart={handleDragStart}
|
|
163
|
-
onDragMove={handleDragMove}
|
|
164
|
-
onDragOver={handleDragOver}
|
|
165
|
-
onDragEnd={handleDragEnd}
|
|
166
|
-
onDragCancel={handleDragCancel}
|
|
167
|
-
>
|
|
168
|
-
<SortableContext
|
|
169
|
-
strategy={horizontalListSortingStrategy}
|
|
170
|
-
items={kanban.columns.filter(nonNullable).map(({ id }) => id!)}
|
|
171
|
-
>
|
|
172
|
-
{kanban.columns.filter(nonNullable).map((column) => (
|
|
173
|
-
<KanbanColumnComponent
|
|
174
|
-
key={column.id}
|
|
175
|
-
column={column}
|
|
176
|
-
itemMapper={itemMapper}
|
|
177
|
-
onCreate={(column: KanbanColumnType) => model.createItem(column)}
|
|
178
|
-
onDelete={() => handleDeleteColumn(column.id!)}
|
|
179
|
-
/>
|
|
180
|
-
))}
|
|
181
|
-
</SortableContext>
|
|
182
|
-
|
|
183
|
-
{/* Overlay required to drag across columns. */}
|
|
184
|
-
{draggingItem && (
|
|
185
|
-
<DragOverlay style={{ margin: 0 }}>
|
|
186
|
-
<KanbanCardComponent item={draggingItem.source.item!} onDelete={() => {}} />
|
|
187
|
-
</DragOverlay>
|
|
188
|
-
)}
|
|
189
|
-
|
|
190
|
-
{handleCreateColumn && <KanbanColumnComponentPlaceholder onAdd={handleCreateColumn} />}
|
|
191
|
-
</DndContext>
|
|
192
|
-
</div>
|
|
193
|
-
</div>
|
|
194
|
-
);
|
|
195
|
-
};
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2023 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { useSortable } from '@dnd-kit/sortable';
|
|
6
|
-
import { CSS } from '@dnd-kit/utilities';
|
|
7
|
-
import { DotsSixVertical, X } from '@phosphor-icons/react';
|
|
8
|
-
import React, { type FC } from 'react';
|
|
9
|
-
|
|
10
|
-
import { createDocAccessor } from '@dxos/react-client/echo';
|
|
11
|
-
import { Button, useThemeContext, useTranslation } from '@dxos/react-ui';
|
|
12
|
-
import {
|
|
13
|
-
createBasicExtensions,
|
|
14
|
-
createDataExtensions,
|
|
15
|
-
createThemeExtensions,
|
|
16
|
-
useTextEditor,
|
|
17
|
-
} from '@dxos/react-ui-editor';
|
|
18
|
-
import { getSize, mx, attentionSurface, focusRing } from '@dxos/react-ui-theme';
|
|
19
|
-
|
|
20
|
-
import { KANBAN_PLUGIN } from '../meta';
|
|
21
|
-
import { type KanbanColumnType, type KanbanItemType } from '../types';
|
|
22
|
-
|
|
23
|
-
const DeleteItem = ({ onClick }: { onClick: () => void }) => {
|
|
24
|
-
const { t } = useTranslation(KANBAN_PLUGIN);
|
|
25
|
-
return (
|
|
26
|
-
<Button variant='ghost' onClick={onClick} classNames='plb-0 pli-0.5 -mlb-1'>
|
|
27
|
-
<span className='sr-only'>{t('delete item label')}</span>
|
|
28
|
-
<X className={getSize(4)} />
|
|
29
|
-
</Button>
|
|
30
|
-
);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export const KanbanCardComponent: FC<{
|
|
34
|
-
column?: KanbanColumnType;
|
|
35
|
-
item: KanbanItemType;
|
|
36
|
-
debug?: boolean;
|
|
37
|
-
onDelete?: () => void;
|
|
38
|
-
}> = ({ column, item, debug = false, onDelete }) => {
|
|
39
|
-
const { themeMode } = useThemeContext();
|
|
40
|
-
const { t } = useTranslation(KANBAN_PLUGIN);
|
|
41
|
-
const { isDragging, attributes, listeners, transform, transition, setNodeRef } = useSortable({
|
|
42
|
-
id: item.id,
|
|
43
|
-
data: { type: 'item', column },
|
|
44
|
-
});
|
|
45
|
-
const tx = transform ? Object.assign(transform, { scaleY: 1 }) : null;
|
|
46
|
-
|
|
47
|
-
const { parentRef, focusAttributes } = useTextEditor(
|
|
48
|
-
() => ({
|
|
49
|
-
initialValue: item.name,
|
|
50
|
-
extensions: [
|
|
51
|
-
createDataExtensions({ id: item.id, text: createDocAccessor(item, ['name']) }),
|
|
52
|
-
createBasicExtensions({ placeholder: t('item title placeholder') }),
|
|
53
|
-
createThemeExtensions({ themeMode }),
|
|
54
|
-
],
|
|
55
|
-
}),
|
|
56
|
-
[item, themeMode],
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
<div
|
|
61
|
-
ref={setNodeRef}
|
|
62
|
-
style={{ transform: CSS.Transform.toString(tx), transition }}
|
|
63
|
-
className={mx('flex grow', isDragging && 'border border-neutral-400 dark:border-neutral-800')}
|
|
64
|
-
>
|
|
65
|
-
<div className={mx('flex items-start grow p-1', attentionSurface, isDragging && 'opacity-10')}>
|
|
66
|
-
{/* TODO(burdon): Standardize height (and below); e.g., via toolbar. */}
|
|
67
|
-
<button className='flex h-[40px] items-center' {...attributes} {...listeners}>
|
|
68
|
-
<DotsSixVertical className={getSize(5)} />
|
|
69
|
-
</button>
|
|
70
|
-
<div className='flex flex-col grow pt-1'>
|
|
71
|
-
<div {...focusAttributes} className={mx(focusRing, 'rounded-sm p-1')} ref={parentRef} />
|
|
72
|
-
{debug && <div className='text-xs text-red-800'>{item.id.slice(0, 9)}</div>}
|
|
73
|
-
</div>
|
|
74
|
-
{onDelete && (
|
|
75
|
-
<div className='flex h-[40px] items-center'>
|
|
76
|
-
<DeleteItem onClick={onDelete} />
|
|
77
|
-
</div>
|
|
78
|
-
)}
|
|
79
|
-
</div>
|
|
80
|
-
</div>
|
|
81
|
-
);
|
|
82
|
-
};
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2023 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { useDroppable } from '@dnd-kit/core';
|
|
6
|
-
import { SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
|
7
|
-
import { CSS } from '@dnd-kit/utilities';
|
|
8
|
-
import { DotsSixVertical, X, Plus } from '@phosphor-icons/react';
|
|
9
|
-
import React, { type FC } from 'react';
|
|
10
|
-
|
|
11
|
-
import { Button, Input, useTranslation } from '@dxos/react-ui';
|
|
12
|
-
import { modalSurface, getSize, groupSurface, mx } from '@dxos/react-ui-theme';
|
|
13
|
-
import { nonNullable } from '@dxos/util';
|
|
14
|
-
|
|
15
|
-
import { KanbanCardComponent } from './KanbanCard';
|
|
16
|
-
import { useSubscription } from './util';
|
|
17
|
-
import { KANBAN_PLUGIN } from '../meta';
|
|
18
|
-
import { type KanbanColumnType, type KanbanItemType } from '../types';
|
|
19
|
-
|
|
20
|
-
export type ItemsMapper = (column: string, items: KanbanItemType[]) => KanbanItemType[];
|
|
21
|
-
|
|
22
|
-
const DeleteColumn = ({ onClick }: { onClick: () => void }) => {
|
|
23
|
-
const { t } = useTranslation(KANBAN_PLUGIN);
|
|
24
|
-
return (
|
|
25
|
-
<Button variant='ghost' onClick={onClick} classNames='plb-0 pli-0.5 -mlb-1'>
|
|
26
|
-
<span className='sr-only'>{t('delete column label')}</span>
|
|
27
|
-
<X className={getSize(4)} />
|
|
28
|
-
</Button>
|
|
29
|
-
);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const AddItem = ({ onClick }: { onClick: () => void }) => {
|
|
33
|
-
const { t } = useTranslation(KANBAN_PLUGIN);
|
|
34
|
-
return (
|
|
35
|
-
<Button variant='ghost' onClick={onClick} classNames='plb-0 pli-0.5 -mlb-1'>
|
|
36
|
-
<span className='sr-only'>{t('add item label')}</span>
|
|
37
|
-
<Plus className={getSize(4)} />
|
|
38
|
-
</Button>
|
|
39
|
-
);
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
// TODO(burdon): Factor out container.
|
|
43
|
-
export const KanbanColumnComponentPlaceholder: FC<{ onAdd: () => void }> = ({ onAdd }) => {
|
|
44
|
-
const { t } = useTranslation(KANBAN_PLUGIN);
|
|
45
|
-
return (
|
|
46
|
-
<div className={mx('flex flex-col justify-center shadow rounded w-[300px] h-[300px]', groupSurface)}>
|
|
47
|
-
<Button variant='ghost' onClick={onAdd} classNames='plb-0 pli-0.5 -mlb-1'>
|
|
48
|
-
<span className='sr-only'>{t('add column label')}</span>
|
|
49
|
-
<Plus className={getSize(6)} />
|
|
50
|
-
</Button>
|
|
51
|
-
</div>
|
|
52
|
-
);
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export const KanbanColumnComponent: FC<{
|
|
56
|
-
column: KanbanColumnType;
|
|
57
|
-
itemMapper?: ItemsMapper;
|
|
58
|
-
debug?: boolean; // TODO(burdon): Context.
|
|
59
|
-
onCreate?: (column: KanbanColumnType) => KanbanItemType;
|
|
60
|
-
onDelete?: () => void;
|
|
61
|
-
}> = ({ column, itemMapper, debug = false, onCreate, onDelete }) => {
|
|
62
|
-
const { t } = useTranslation(KANBAN_PLUGIN);
|
|
63
|
-
|
|
64
|
-
// TODO(wittjosiah): Remove?
|
|
65
|
-
useSubscription([column.items]);
|
|
66
|
-
const items = itemMapper?.(column.id!, column.items.filter(nonNullable)) ?? column.items!;
|
|
67
|
-
|
|
68
|
-
const { setNodeRef: setDroppableNodeRef } = useDroppable({ id: column.id! });
|
|
69
|
-
const { isDragging, attributes, listeners, transform, transition, setNodeRef } = useSortable({
|
|
70
|
-
id: column.id!,
|
|
71
|
-
data: { type: 'column' },
|
|
72
|
-
});
|
|
73
|
-
const tx = transform ? Object.assign(transform, { scaleY: 1 }) : null;
|
|
74
|
-
|
|
75
|
-
const handleAddItem = onCreate
|
|
76
|
-
? () => {
|
|
77
|
-
const item = onCreate(column);
|
|
78
|
-
column.items!.splice(column.items!.length, 0, item);
|
|
79
|
-
}
|
|
80
|
-
: undefined;
|
|
81
|
-
|
|
82
|
-
const handleDeleteItem = (id: string) => {
|
|
83
|
-
const index = column.items.filter(nonNullable).findIndex((column) => column.id === id);
|
|
84
|
-
if (index >= 0) {
|
|
85
|
-
column.items!.splice(index, 1);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<div
|
|
91
|
-
ref={setNodeRef}
|
|
92
|
-
style={{ transform: CSS.Transform.toString(tx), transition }}
|
|
93
|
-
className={mx('flex flex-col snap-center overflow-y-hidden', isDragging && 'relative z-10')}
|
|
94
|
-
>
|
|
95
|
-
{/* TODO(burdon): Width approx mobile phone width. */}
|
|
96
|
-
<div
|
|
97
|
-
className={mx(
|
|
98
|
-
'flex flex-col py-2 overflow-hidden shadow rounded w-[300px] min-h-[300px]',
|
|
99
|
-
isDragging ? modalSurface : groupSurface,
|
|
100
|
-
)}
|
|
101
|
-
>
|
|
102
|
-
<div className='flex items-center mb-2 px-2'>
|
|
103
|
-
<button {...attributes} {...listeners}>
|
|
104
|
-
<DotsSixVertical className={getSize(5)} />
|
|
105
|
-
</button>
|
|
106
|
-
|
|
107
|
-
<Input.Root>
|
|
108
|
-
<Input.Label srOnly>{t('column title label')}</Input.Label>
|
|
109
|
-
<Input.TextInput
|
|
110
|
-
variant='subdued'
|
|
111
|
-
classNames='px-2'
|
|
112
|
-
placeholder={t('column title placeholder')}
|
|
113
|
-
defaultValue={column.name}
|
|
114
|
-
onChange={({ target: { value } }) => (column.name = value)}
|
|
115
|
-
/>
|
|
116
|
-
</Input.Root>
|
|
117
|
-
|
|
118
|
-
{/* TODO(burdon): Menu. */}
|
|
119
|
-
{onDelete && <DeleteColumn onClick={onDelete} />}
|
|
120
|
-
</div>
|
|
121
|
-
|
|
122
|
-
{/* TODO(burdon): Scrolling (radix; see kai/mosaic). */}
|
|
123
|
-
<SortableContext strategy={verticalListSortingStrategy} items={items.filter(nonNullable).map(({ id }) => id)}>
|
|
124
|
-
<div ref={setDroppableNodeRef} className='flex flex-col grow overflow-y-scroll space-y-2 pr-4'>
|
|
125
|
-
{items.filter(nonNullable).map((item) => (
|
|
126
|
-
<div key={item.id} id={item.id} className='flex pl-2'>
|
|
127
|
-
<KanbanCardComponent column={column} item={item} onDelete={() => handleDeleteItem(item.id)} />
|
|
128
|
-
</div>
|
|
129
|
-
))}
|
|
130
|
-
</div>
|
|
131
|
-
</SortableContext>
|
|
132
|
-
|
|
133
|
-
{handleAddItem && (
|
|
134
|
-
<div className='flex justify-center mt-2'>
|
|
135
|
-
<AddItem onClick={handleAddItem} />
|
|
136
|
-
</div>
|
|
137
|
-
)}
|
|
138
|
-
|
|
139
|
-
{debug && <div className='px-2 text-xs text-red-800'>{column.id!.slice(0, 9)}</div>}
|
|
140
|
-
</div>
|
|
141
|
-
</div>
|
|
142
|
-
);
|
|
143
|
-
};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2023 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import React, { type FC } from 'react';
|
|
6
|
-
|
|
7
|
-
import { create } from '@dxos/live-object';
|
|
8
|
-
import { getSpace } from '@dxos/react-client/echo';
|
|
9
|
-
import { Main } from '@dxos/react-ui';
|
|
10
|
-
import { topbarBlockPaddingStart, fixedInsetFlexLayout, bottombarBlockPaddingEnd } from '@dxos/react-ui-theme';
|
|
11
|
-
|
|
12
|
-
import { KanbanBoard } from './KanbanBoard';
|
|
13
|
-
import { type KanbanType, KanbanColumnType, KanbanItemType, type KanbanModel } from '../types';
|
|
14
|
-
|
|
15
|
-
const KanbanMain: FC<{ kanban: KanbanType }> = ({ kanban }) => {
|
|
16
|
-
// const { t } = useTranslation(KANBAN_PLUGIN);
|
|
17
|
-
const space = getSpace(kanban);
|
|
18
|
-
if (!space) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// TODO(burdon): Should plugin create and pass in model?
|
|
23
|
-
const model: KanbanModel = {
|
|
24
|
-
root: kanban, // TODO(burdon): How to keep pure?
|
|
25
|
-
createColumn: () => space.db.add(create(KanbanColumnType, { items: [] })),
|
|
26
|
-
// TODO(burdon): Add metadata from column in the case of projections.
|
|
27
|
-
createItem: (column) => space.db.add(create(KanbanItemType, { name: '' })),
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<Main.Content classNames={[fixedInsetFlexLayout, topbarBlockPaddingStart, bottombarBlockPaddingEnd]}>
|
|
32
|
-
<KanbanBoard model={model} />
|
|
33
|
-
</Main.Content>
|
|
34
|
-
);
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export default KanbanMain;
|
package/src/components/util.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2023 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { useEffect, useState } from 'react';
|
|
6
|
-
|
|
7
|
-
import { createSubscription } from '@dxos/react-client/echo';
|
|
8
|
-
import { nonNullable } from '@dxos/util';
|
|
9
|
-
|
|
10
|
-
import { type KanbanColumnType, type Location } from '../types';
|
|
11
|
-
|
|
12
|
-
// TODO(burdon): Factor out.
|
|
13
|
-
export const useSubscription = (data: any) => {
|
|
14
|
-
const [_, setIter] = useState([]);
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
const handle = createSubscription(() => setIter([]));
|
|
17
|
-
handle.update(data);
|
|
18
|
-
return () => handle.unsubscribe();
|
|
19
|
-
}, []);
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Find the column or item within the model.
|
|
24
|
-
*/
|
|
25
|
-
// TODO(burdon): Move to model.
|
|
26
|
-
export const findLocation = (columns: KanbanColumnType[], id: string): Location | undefined => {
|
|
27
|
-
for (const column of columns) {
|
|
28
|
-
// TODO(burdon): Need transient ID for UX.
|
|
29
|
-
if (column.id === id) {
|
|
30
|
-
return { column };
|
|
31
|
-
} else {
|
|
32
|
-
const idx = column.items.filter(nonNullable).findIndex((item) => item.id === id);
|
|
33
|
-
if (idx !== -1) {
|
|
34
|
-
return { column, item: column.items![idx], idx };
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
};
|