@stll/workspace-ui 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/calculations.d.ts +2 -2
- package/dist/calculations.js +20 -19
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/kanban-grouping-picker.d.ts +9 -0
- package/dist/kanban-grouping-picker.js +39 -0
- package/dist/kanban-view.d.ts +105 -0
- package/dist/kanban-view.js +109 -0
- package/package.json +10 -2
package/dist/calculations.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ type CalculationPickerProps = {
|
|
|
28
28
|
* Choose what a view calculates: a property, then a reduction. Every step is a
|
|
29
29
|
* menu item, so the whole choice is reachable from the keyboard.
|
|
30
30
|
*/
|
|
31
|
-
declare const CalculationPicker: ({ labels, properties, selections, onChange, children }: CalculationPickerProps) => import("react").JSX.Element;
|
|
31
|
+
declare const CalculationPicker: ({ labels, properties, selections, onChange, children }: CalculationPickerProps) => import("react").JSX.Element | null;
|
|
32
32
|
type CalculationKindPickerProps = {
|
|
33
33
|
labels: WorkspaceCalculationLabels;
|
|
34
34
|
/** Reductions this column's values allow. */
|
|
@@ -43,7 +43,7 @@ type CalculationKindPickerProps = {
|
|
|
43
43
|
* Choose one column's reduction. The board picks a property first (it shows one
|
|
44
44
|
* line for the whole board); a table column already is the property.
|
|
45
45
|
*/
|
|
46
|
-
declare const CalculationKindPicker: ({ labels, kinds, value, onChange, children }: CalculationKindPickerProps) => import("react").JSX.Element;
|
|
46
|
+
declare const CalculationKindPicker: ({ labels, kinds, value, onChange, children }: CalculationKindPickerProps) => import("react").JSX.Element | null;
|
|
47
47
|
type CalculationSummaryProps = {
|
|
48
48
|
calculation: FormattedCalculation;
|
|
49
49
|
};
|
package/dist/calculations.js
CHANGED
|
@@ -13,6 +13,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
13
13
|
* menu item, so the whole choice is reachable from the keyboard.
|
|
14
14
|
*/
|
|
15
15
|
const CalculationPicker = ({ labels, properties, selections, onChange, children }) => {
|
|
16
|
+
if (properties.length === 0) return null;
|
|
16
17
|
const select = (propertyId, kind) => {
|
|
17
18
|
onChange(applyCalculationSelection({
|
|
18
19
|
selections,
|
|
@@ -27,10 +28,7 @@ const CalculationPicker = ({ labels, properties, selections, onChange, children
|
|
|
27
28
|
variant: "ghost"
|
|
28
29
|
}),
|
|
29
30
|
children: children ?? /* @__PURE__ */ jsx(SigmaIcon, {})
|
|
30
|
-
}), /* @__PURE__ */
|
|
31
|
-
disabled: true,
|
|
32
|
-
children: labels.noProperties
|
|
33
|
-
}), properties.map((property) => {
|
|
31
|
+
}), /* @__PURE__ */ jsx(MenuPopup, { children: properties.map((property) => {
|
|
34
32
|
const selected = selections.find((selection) => selection.propertyId === property.id);
|
|
35
33
|
return /* @__PURE__ */ jsxs(MenuSub, { children: [/* @__PURE__ */ jsx(MenuSubTrigger, { children: property.name }), /* @__PURE__ */ jsxs(MenuSubPopup, { children: [/* @__PURE__ */ jsxs(MenuItem, {
|
|
36
34
|
onClick: () => select(property.id, null),
|
|
@@ -39,26 +37,29 @@ const CalculationPicker = ({ labels, properties, selections, onChange, children
|
|
|
39
37
|
onClick: () => select(property.id, kind),
|
|
40
38
|
children: [selected?.kind === kind ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx("span", {}), labels.kinds[kind]]
|
|
41
39
|
}, kind))] })] }, property.id);
|
|
42
|
-
})
|
|
40
|
+
}) })] });
|
|
43
41
|
};
|
|
44
42
|
/**
|
|
45
43
|
* Choose one column's reduction. The board picks a property first (it shows one
|
|
46
44
|
* line for the whole board); a table column already is the property.
|
|
47
45
|
*/
|
|
48
|
-
const CalculationKindPicker = ({ labels, kinds, value, onChange, children }) =>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}),
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
46
|
+
const CalculationKindPicker = ({ labels, kinds, value, onChange, children }) => {
|
|
47
|
+
if (kinds.length === 0) return null;
|
|
48
|
+
return /* @__PURE__ */ jsxs(Menu, { children: [/* @__PURE__ */ jsx(MenuTrigger, {
|
|
49
|
+
"aria-label": labels.choose,
|
|
50
|
+
render: /* @__PURE__ */ jsx(Button, {
|
|
51
|
+
size: "icon-xs",
|
|
52
|
+
variant: "ghost"
|
|
53
|
+
}),
|
|
54
|
+
children: children ?? /* @__PURE__ */ jsx(SigmaIcon, {})
|
|
55
|
+
}), /* @__PURE__ */ jsxs(MenuPopup, { children: [/* @__PURE__ */ jsxs(MenuItem, {
|
|
56
|
+
onClick: () => onChange(null),
|
|
57
|
+
children: [value === null ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx("span", {}), labels.none]
|
|
58
|
+
}), kinds.map((kind) => /* @__PURE__ */ jsxs(MenuItem, {
|
|
59
|
+
onClick: () => onChange(kind),
|
|
60
|
+
children: [value === kind ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx("span", {}), labels.kinds[kind]]
|
|
61
|
+
}, kind))] })] });
|
|
62
|
+
};
|
|
62
63
|
/**
|
|
63
64
|
* The calculation as one compact line. When it reduces to several currencies
|
|
64
65
|
* the line stays short and the full set sits behind a tooltip.
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ import { getClipFieldValueLabel } from "./field-value-logic.js";
|
|
|
10
10
|
import { FieldValue, IntFieldValue, MoneyFieldValue, PersonFieldValue } from "./field-value.js";
|
|
11
11
|
import { SortChips, SortChipsLabels, SortChipsProps, SortDescriptor, SortableProperty, sortDirectionHint } from "./sorts.js";
|
|
12
12
|
import { TableSkeletonRows } from "./table-skeleton-rows.js";
|
|
13
|
+
import { CreateWorkspaceKanbanSchemaParams, KANBAN_EMPTY_GROUP_VISIBILITIES, KanbanBoardPresentation, KanbanEmptyGroupVisibility, KanbanPresentedLane, KanbanSavedAxisState, KanbanSavedGroupReference, KanbanSavedSubgroupState, KanbanSavedViewState, ResolveWorkspaceKanbanViewParams, ResolvedWorkspaceKanbanView, WorkspaceKanbanBuiltInGroupLabel, WorkspaceKanbanGroupingChoice, WorkspaceKanbanGroupingPickerLabels, WorkspaceKanbanGroupingPickerProps, WorkspaceKanbanProperty, createWorkspaceKanbanSchema, getWorkspaceKanbanGroupingChoices, presentKanbanBoard, resolveWorkspaceKanbanView } from "./kanban-view.js";
|
|
14
|
+
import { WorkspaceKanbanGroupingPicker } from "./kanban-grouping-picker.js";
|
|
13
15
|
import { WorkspaceViewDirection, WorkspaceViewDropPosition, reorderWorkspaceViewIds, toWorkspaceViewDropPosition } from "./view-switcher.logic.js";
|
|
14
16
|
import { WorkspaceViewSwitcher, WorkspaceViewSwitcherEditing, WorkspaceViewSwitcherItem, WorkspaceViewSwitcherProps, WorkspaceViewSwitcherReorder } from "./view-switcher.js";
|
|
15
|
-
export { type ApplyCalculationSelectionParams, CONDITION_OPERATORS, type CalculationFormatters, CalculationKindPicker, type CalculationKindPickerProps, type CalculationLabels, CalculationPicker, type CalculationPickerProps, type CalculationProperty, type CalculationResult, type CalculationSelection, CalculationSummary, type CalculationSummaryProps, type ColorVariants, ColumnCalculation, ConditionBuilder, type ConditionBuilderLabels, type ConditionCapabilities, type ConditionOperator, FIELD_CONTENT_TYPES, type FieldContent, type FieldOption, type FieldOptionChoice, FieldValue, type FieldValueType, type FormatCalculationParams, type FormatMoneyCentsParams, type FormattedCalculation, type FormulaCellRenderCtx, type GenericProperty, IntFieldValue, MoneyFieldValue, type OptionColor, PersonFieldValue, PropertyIcon, type PropertyIconType, SortChips, type SortChipsLabels, type SortChipsProps, type SortDescriptor, type SortableProperty, TableSkeletonRows, type UseCalculationParams, type ValueEditorKind, type ValueEditorRenderCtx, type WorkspaceCalculationLabels, type WorkspaceFieldContent, type WorkspaceViewDirection, type WorkspaceViewDropPosition, WorkspaceViewSwitcher, type WorkspaceViewSwitcherEditing, type WorkspaceViewSwitcherItem, type WorkspaceViewSwitcherProps, type WorkspaceViewSwitcherReorder, appendChild, applyCalculationSelection, asGroup, buildLeaf, currencyMinorUnitDigits, emptyColor, fieldForNode, formatCalculationResult, formatMoneyCents, getClipFieldValueLabel, isConditionOperator, isMultiValue, leafFromField, leafOperand, leafOperator, leafValueList, leafValueString, operandsEqual, operatorsFor, optionColors, removeChild, reorderWorkspaceViewIds, replaceChild, resolveOptionColor, sortDirectionHint, toWorkspaceViewDropPosition, useCalculation, valueEditorFor };
|
|
17
|
+
export { type ApplyCalculationSelectionParams, CONDITION_OPERATORS, type CalculationFormatters, CalculationKindPicker, type CalculationKindPickerProps, type CalculationLabels, CalculationPicker, type CalculationPickerProps, type CalculationProperty, type CalculationResult, type CalculationSelection, CalculationSummary, type CalculationSummaryProps, type ColorVariants, ColumnCalculation, ConditionBuilder, type ConditionBuilderLabels, type ConditionCapabilities, type ConditionOperator, type CreateWorkspaceKanbanSchemaParams, FIELD_CONTENT_TYPES, type FieldContent, type FieldOption, type FieldOptionChoice, FieldValue, type FieldValueType, type FormatCalculationParams, type FormatMoneyCentsParams, type FormattedCalculation, type FormulaCellRenderCtx, type GenericProperty, IntFieldValue, KANBAN_EMPTY_GROUP_VISIBILITIES, type KanbanBoardPresentation, type KanbanEmptyGroupVisibility, type KanbanPresentedLane, type KanbanSavedAxisState, type KanbanSavedGroupReference, type KanbanSavedSubgroupState, type KanbanSavedViewState, MoneyFieldValue, type OptionColor, PersonFieldValue, PropertyIcon, type PropertyIconType, type ResolveWorkspaceKanbanViewParams, type ResolvedWorkspaceKanbanView, SortChips, type SortChipsLabels, type SortChipsProps, type SortDescriptor, type SortableProperty, TableSkeletonRows, type UseCalculationParams, type ValueEditorKind, type ValueEditorRenderCtx, type WorkspaceCalculationLabels, type WorkspaceFieldContent, type WorkspaceKanbanBuiltInGroupLabel, type WorkspaceKanbanGroupingChoice, WorkspaceKanbanGroupingPicker, type WorkspaceKanbanGroupingPickerLabels, type WorkspaceKanbanGroupingPickerProps, type WorkspaceKanbanProperty, type WorkspaceViewDirection, type WorkspaceViewDropPosition, WorkspaceViewSwitcher, type WorkspaceViewSwitcherEditing, type WorkspaceViewSwitcherItem, type WorkspaceViewSwitcherProps, type WorkspaceViewSwitcherReorder, appendChild, applyCalculationSelection, asGroup, buildLeaf, createWorkspaceKanbanSchema, currencyMinorUnitDigits, emptyColor, fieldForNode, formatCalculationResult, formatMoneyCents, getClipFieldValueLabel, getWorkspaceKanbanGroupingChoices, isConditionOperator, isMultiValue, leafFromField, leafOperand, leafOperator, leafValueList, leafValueString, operandsEqual, operatorsFor, optionColors, presentKanbanBoard, removeChild, reorderWorkspaceViewIds, replaceChild, resolveOptionColor, resolveWorkspaceKanbanView, sortDirectionHint, toWorkspaceViewDropPosition, useCalculation, valueEditorFor };
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,8 @@ import { PropertyIcon } from "./property-icon.js";
|
|
|
10
10
|
import { SortChips, sortDirectionHint } from "./sorts.js";
|
|
11
11
|
import { TableSkeletonRows } from "./table-skeleton-rows.js";
|
|
12
12
|
import { FIELD_CONTENT_TYPES } from "./types.js";
|
|
13
|
+
import { KANBAN_EMPTY_GROUP_VISIBILITIES, createWorkspaceKanbanSchema, getWorkspaceKanbanGroupingChoices, presentKanbanBoard, resolveWorkspaceKanbanView } from "./kanban-view.js";
|
|
14
|
+
import { WorkspaceKanbanGroupingPicker } from "./kanban-grouping-picker.js";
|
|
13
15
|
import { reorderWorkspaceViewIds, toWorkspaceViewDropPosition } from "./view-switcher.logic.js";
|
|
14
16
|
import { WorkspaceViewSwitcher } from "./view-switcher.js";
|
|
15
|
-
export { CONDITION_OPERATORS, CalculationKindPicker, CalculationPicker, CalculationSummary, ColumnCalculation, ConditionBuilder, FIELD_CONTENT_TYPES, FieldValue, IntFieldValue, MoneyFieldValue, PersonFieldValue, PropertyIcon, SortChips, TableSkeletonRows, WorkspaceViewSwitcher, appendChild, applyCalculationSelection, asGroup, buildLeaf, currencyMinorUnitDigits, emptyColor, fieldForNode, formatCalculationResult, formatMoneyCents, getClipFieldValueLabel, isConditionOperator, isMultiValue, leafFromField, leafOperand, leafOperator, leafValueList, leafValueString, operandsEqual, operatorsFor, optionColors, removeChild, reorderWorkspaceViewIds, replaceChild, resolveOptionColor, sortDirectionHint, toWorkspaceViewDropPosition, useCalculation, valueEditorFor };
|
|
17
|
+
export { CONDITION_OPERATORS, CalculationKindPicker, CalculationPicker, CalculationSummary, ColumnCalculation, ConditionBuilder, FIELD_CONTENT_TYPES, FieldValue, IntFieldValue, KANBAN_EMPTY_GROUP_VISIBILITIES, MoneyFieldValue, PersonFieldValue, PropertyIcon, SortChips, TableSkeletonRows, WorkspaceKanbanGroupingPicker, WorkspaceViewSwitcher, appendChild, applyCalculationSelection, asGroup, buildLeaf, createWorkspaceKanbanSchema, currencyMinorUnitDigits, emptyColor, fieldForNode, formatCalculationResult, formatMoneyCents, getClipFieldValueLabel, getWorkspaceKanbanGroupingChoices, isConditionOperator, isMultiValue, leafFromField, leafOperand, leafOperator, leafValueList, leafValueString, operandsEqual, operatorsFor, optionColors, presentKanbanBoard, removeChild, reorderWorkspaceViewIds, replaceChild, resolveOptionColor, resolveWorkspaceKanbanView, sortDirectionHint, toWorkspaceViewDropPosition, useCalculation, valueEditorFor };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { WorkspaceKanbanGroupingPickerProps, WorkspaceKanbanProperty } from "./kanban-view.js";
|
|
2
|
+
//#region src/kanban-grouping-picker.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Accessible picker shared by the board's Group and Sub-group controls. The
|
|
5
|
+
* caller labels built-ins because only its domain owns their human names.
|
|
6
|
+
*/
|
|
7
|
+
declare const WorkspaceKanbanGroupingPicker: <TRow, TProperty extends WorkspaceKanbanProperty>({ allowNone, excludedGroupBy, getBuiltInGroupLabel, labels, onValueChange, schema, value }: WorkspaceKanbanGroupingPickerProps<TRow, TProperty>) => import("react").JSX.Element;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { WorkspaceKanbanGroupingPicker };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { getWorkspaceKanbanGroupingChoices } from "./kanban-view.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "@stll/ui/select";
|
|
4
|
+
//#region src/kanban-grouping-picker.tsx
|
|
5
|
+
const NO_GROUPING_VALUE = "";
|
|
6
|
+
/**
|
|
7
|
+
* Accessible picker shared by the board's Group and Sub-group controls. The
|
|
8
|
+
* caller labels built-ins because only its domain owns their human names.
|
|
9
|
+
*/
|
|
10
|
+
const WorkspaceKanbanGroupingPicker = ({ allowNone, excludedGroupBy, getBuiltInGroupLabel, labels, onValueChange, schema, value }) => {
|
|
11
|
+
const choices = getWorkspaceKanbanGroupingChoices({
|
|
12
|
+
getBuiltInGroupLabel,
|
|
13
|
+
schema
|
|
14
|
+
}).filter(({ id }) => id !== excludedGroupBy);
|
|
15
|
+
const display = choices.find((choice) => choice.id === value)?.label ?? (allowNone ? labels.none : labels.placeholder);
|
|
16
|
+
return /* @__PURE__ */ jsxs(Select, {
|
|
17
|
+
onValueChange: (nextValue) => {
|
|
18
|
+
if (nextValue === null) return;
|
|
19
|
+
onValueChange(nextValue);
|
|
20
|
+
},
|
|
21
|
+
value,
|
|
22
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
23
|
+
"aria-label": labels.control,
|
|
24
|
+
size: "sm",
|
|
25
|
+
children: /* @__PURE__ */ jsx(SelectValue, {
|
|
26
|
+
placeholder: labels.placeholder,
|
|
27
|
+
children: display
|
|
28
|
+
})
|
|
29
|
+
}), /* @__PURE__ */ jsxs(SelectPopup, { children: [allowNone ? /* @__PURE__ */ jsx(SelectItem, {
|
|
30
|
+
value: NO_GROUPING_VALUE,
|
|
31
|
+
children: labels.none
|
|
32
|
+
}) : null, choices.map((choice) => /* @__PURE__ */ jsx(SelectItem, {
|
|
33
|
+
value: choice.id,
|
|
34
|
+
children: choice.label
|
|
35
|
+
}, choice.id))] })]
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
//#endregion
|
|
39
|
+
export { WorkspaceKanbanGroupingPicker };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { GenericProperty } from "./types.js";
|
|
2
|
+
import { KanbanBoardCell, KanbanBoardLane, KanbanBoardMatrix, KanbanBuiltInGroup, KanbanGroup, KanbanGrouping, KanbanSchema } from "@stll/ui/kanban";
|
|
3
|
+
//#region src/kanban-view.d.ts
|
|
4
|
+
declare const KANBAN_EMPTY_GROUP_VISIBILITIES: {
|
|
5
|
+
readonly hide: "hide";
|
|
6
|
+
readonly show: "show";
|
|
7
|
+
};
|
|
8
|
+
type KanbanEmptyGroupVisibility = (typeof KANBAN_EMPTY_GROUP_VISIBILITIES)[keyof typeof KANBAN_EMPTY_GROUP_VISIBILITIES];
|
|
9
|
+
/** A persisted reference keeps a real option distinct from the No value lane. */
|
|
10
|
+
type KanbanSavedGroupReference = {
|
|
11
|
+
type: "option";
|
|
12
|
+
value: string;
|
|
13
|
+
} | {
|
|
14
|
+
type: "uncategorized";
|
|
15
|
+
};
|
|
16
|
+
type KanbanSavedAxisState = {
|
|
17
|
+
emptyGroups: KanbanEmptyGroupVisibility;
|
|
18
|
+
groupBy: string;
|
|
19
|
+
hiddenGroups: readonly KanbanSavedGroupReference[];
|
|
20
|
+
orderedGroups: readonly KanbanSavedGroupReference[];
|
|
21
|
+
};
|
|
22
|
+
type KanbanSavedSubgroupState = KanbanSavedAxisState & {
|
|
23
|
+
collapsedGroups: readonly KanbanSavedGroupReference[];
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* View state is deliberately separate from the board's domain data. It may
|
|
27
|
+
* change visibility, order, and collapse, but cannot change a card's cell.
|
|
28
|
+
*/
|
|
29
|
+
type KanbanSavedViewState = {
|
|
30
|
+
group: KanbanSavedAxisState;
|
|
31
|
+
subgroup: KanbanSavedSubgroupState | null;
|
|
32
|
+
version: 1;
|
|
33
|
+
};
|
|
34
|
+
type WorkspaceKanbanProperty = GenericProperty & {
|
|
35
|
+
id: string;
|
|
36
|
+
name: string;
|
|
37
|
+
};
|
|
38
|
+
type CreateWorkspaceKanbanSchemaParams<TRow, TProperty extends WorkspaceKanbanProperty> = {
|
|
39
|
+
builtInGroups: readonly KanbanBuiltInGroup<TRow>[];
|
|
40
|
+
properties: readonly TProperty[];
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Turns the authoritative workspace property model into board declarations.
|
|
44
|
+
* Select options remain the sole source of property-derived columns and lanes.
|
|
45
|
+
*/
|
|
46
|
+
declare const createWorkspaceKanbanSchema: <TRow, TProperty extends WorkspaceKanbanProperty>({ builtInGroups, properties }: CreateWorkspaceKanbanSchemaParams<TRow, TProperty>) => KanbanSchema<TRow, TProperty>;
|
|
47
|
+
type WorkspaceKanbanGroupingChoice = {
|
|
48
|
+
id: string;
|
|
49
|
+
label: string;
|
|
50
|
+
type: "built-in" | "property";
|
|
51
|
+
};
|
|
52
|
+
type WorkspaceKanbanBuiltInGroupLabel<TRow> = (group: KanbanBuiltInGroup<TRow>) => string;
|
|
53
|
+
/** Property-aware data for a Group or Sub-group picker. */
|
|
54
|
+
declare const getWorkspaceKanbanGroupingChoices: <TRow, TProperty extends WorkspaceKanbanProperty>({ getBuiltInGroupLabel, schema }: {
|
|
55
|
+
getBuiltInGroupLabel: WorkspaceKanbanBuiltInGroupLabel<TRow>;
|
|
56
|
+
schema: KanbanSchema<TRow, TProperty>;
|
|
57
|
+
}) => WorkspaceKanbanGroupingChoice[];
|
|
58
|
+
type WorkspaceKanbanGroupingPickerLabels = {
|
|
59
|
+
control: string;
|
|
60
|
+
none: string;
|
|
61
|
+
placeholder: string;
|
|
62
|
+
};
|
|
63
|
+
type WorkspaceKanbanGroupingPickerProps<TRow, TProperty extends WorkspaceKanbanProperty> = {
|
|
64
|
+
allowNone: boolean;
|
|
65
|
+
excludedGroupBy?: string | undefined;
|
|
66
|
+
getBuiltInGroupLabel: WorkspaceKanbanBuiltInGroupLabel<TRow>;
|
|
67
|
+
labels: WorkspaceKanbanGroupingPickerLabels;
|
|
68
|
+
onValueChange: (groupBy: string) => void;
|
|
69
|
+
schema: KanbanSchema<TRow, TProperty>;
|
|
70
|
+
value: string;
|
|
71
|
+
};
|
|
72
|
+
type ResolveWorkspaceKanbanViewParams<TRow, TProperty> = {
|
|
73
|
+
schema: KanbanSchema<TRow, TProperty>;
|
|
74
|
+
state: KanbanSavedViewState;
|
|
75
|
+
};
|
|
76
|
+
type ResolvedWorkspaceKanbanView<TRow, TProperty> = {
|
|
77
|
+
group: KanbanGrouping<TRow, TProperty>;
|
|
78
|
+
subgroup: KanbanGrouping<TRow, TProperty>;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* A property cannot control both axes. Rejecting that configuration here means
|
|
82
|
+
* a later diagonal drag never has two contradictory assignments for one field.
|
|
83
|
+
*/
|
|
84
|
+
declare const resolveWorkspaceKanbanView: <TRow, TProperty>({ schema, state }: ResolveWorkspaceKanbanViewParams<TRow, TProperty>) => ResolvedWorkspaceKanbanView<TRow, TProperty>;
|
|
85
|
+
type KanbanPresentedLane<TRow> = {
|
|
86
|
+
cells: KanbanBoardCell<TRow>[];
|
|
87
|
+
collapsed: boolean;
|
|
88
|
+
lane: KanbanBoardLane;
|
|
89
|
+
};
|
|
90
|
+
type KanbanBoardPresentation<TRow> = {
|
|
91
|
+
columns: KanbanGroup[];
|
|
92
|
+
lanes: KanbanPresentedLane<TRow>[];
|
|
93
|
+
matrix: KanbanBoardMatrix<TRow>;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Apply saved presentation state without rebuilding the matrix. This preserves
|
|
97
|
+
* the canonical placement invariant while letting a view hide empty groups,
|
|
98
|
+
* individually hide groups, reorder them, and collapse swimlanes.
|
|
99
|
+
*/
|
|
100
|
+
declare const presentKanbanBoard: <TRow>({ matrix, state }: {
|
|
101
|
+
matrix: KanbanBoardMatrix<TRow>;
|
|
102
|
+
state: KanbanSavedViewState;
|
|
103
|
+
}) => KanbanBoardPresentation<TRow>;
|
|
104
|
+
//#endregion
|
|
105
|
+
export { CreateWorkspaceKanbanSchemaParams, KANBAN_EMPTY_GROUP_VISIBILITIES, KanbanBoardPresentation, KanbanEmptyGroupVisibility, KanbanPresentedLane, KanbanSavedAxisState, KanbanSavedGroupReference, KanbanSavedSubgroupState, KanbanSavedViewState, ResolveWorkspaceKanbanViewParams, ResolvedWorkspaceKanbanView, WorkspaceKanbanBuiltInGroupLabel, WorkspaceKanbanGroupingChoice, WorkspaceKanbanGroupingPickerLabels, WorkspaceKanbanGroupingPickerProps, WorkspaceKanbanProperty, createWorkspaceKanbanSchema, getWorkspaceKanbanGroupingChoices, presentKanbanBoard, resolveWorkspaceKanbanView };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { panic } from "better-result";
|
|
2
|
+
import { orderKanbanCellsByColumns, resolveKanbanGrouping } from "@stll/ui/kanban";
|
|
3
|
+
//#region src/kanban-view.ts
|
|
4
|
+
const KANBAN_EMPTY_GROUP_VISIBILITIES = {
|
|
5
|
+
hide: "hide",
|
|
6
|
+
show: "show"
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Turns the authoritative workspace property model into board declarations.
|
|
10
|
+
* Select options remain the sole source of property-derived columns and lanes.
|
|
11
|
+
*/
|
|
12
|
+
const createWorkspaceKanbanSchema = ({ builtInGroups, properties }) => ({
|
|
13
|
+
builtInGroups,
|
|
14
|
+
getPropertyId: (property) => property.id,
|
|
15
|
+
getPropertyOptions: (property) => {
|
|
16
|
+
if (property.content.type !== "single-select") return null;
|
|
17
|
+
return property.content.options.map((option) => ({
|
|
18
|
+
optionColor: option.color,
|
|
19
|
+
value: option.value,
|
|
20
|
+
label: option.value
|
|
21
|
+
}));
|
|
22
|
+
},
|
|
23
|
+
properties
|
|
24
|
+
});
|
|
25
|
+
/** Property-aware data for a Group or Sub-group picker. */
|
|
26
|
+
const getWorkspaceKanbanGroupingChoices = ({ getBuiltInGroupLabel, schema }) => {
|
|
27
|
+
const choices = [];
|
|
28
|
+
for (const group of schema.builtInGroups) choices.push({
|
|
29
|
+
id: group.id,
|
|
30
|
+
label: getBuiltInGroupLabel(group),
|
|
31
|
+
type: "built-in"
|
|
32
|
+
});
|
|
33
|
+
for (const property of schema.properties) {
|
|
34
|
+
if (schema.getPropertyOptions(property) === null) continue;
|
|
35
|
+
choices.push({
|
|
36
|
+
id: property.id,
|
|
37
|
+
label: property.name,
|
|
38
|
+
type: "property"
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return choices;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* A property cannot control both axes. Rejecting that configuration here means
|
|
45
|
+
* a later diagonal drag never has two contradictory assignments for one field.
|
|
46
|
+
*/
|
|
47
|
+
const resolveWorkspaceKanbanView = ({ schema, state }) => {
|
|
48
|
+
return {
|
|
49
|
+
group: resolveKanbanGrouping({
|
|
50
|
+
groupBy: state.group.groupBy,
|
|
51
|
+
schema
|
|
52
|
+
}),
|
|
53
|
+
subgroup: state.subgroup === null || state.subgroup.groupBy === state.group.groupBy ? resolveKanbanGrouping({
|
|
54
|
+
groupBy: "",
|
|
55
|
+
schema
|
|
56
|
+
}) : resolveKanbanGrouping({
|
|
57
|
+
groupBy: state.subgroup.groupBy,
|
|
58
|
+
schema
|
|
59
|
+
})
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
const sameGroupReference = (reference, group) => reference.type === "uncategorized" ? group.value === null : group.value === reference.value;
|
|
63
|
+
const refersToGroup = (references, group) => references.some((reference) => sameGroupReference(reference, group));
|
|
64
|
+
const containsGroup = (groups, group) => groups.some((candidate) => candidate.value === group.value);
|
|
65
|
+
const orderGroups = (groups, orderedGroups) => {
|
|
66
|
+
const ordered = [];
|
|
67
|
+
for (const reference of orderedGroups) {
|
|
68
|
+
const group = groups.find((candidate) => sameGroupReference(reference, candidate));
|
|
69
|
+
if (group !== void 0 && !containsGroup(ordered, group)) ordered.push(group);
|
|
70
|
+
}
|
|
71
|
+
for (const group of groups) if (!refersToGroup(orderedGroups, group)) ordered.push(group);
|
|
72
|
+
return ordered;
|
|
73
|
+
};
|
|
74
|
+
const isGroupEmpty = (group, matrix) => matrix.cells.every((cell) => cell.coordinate.column.value !== group.value || cell.rows.length === 0);
|
|
75
|
+
const isLaneEmpty = (lane, matrix) => matrix.cells.every((cell) => cell.coordinate.lane.type !== lane.type || lane.type === "group" && cell.coordinate.lane.type === "group" && cell.coordinate.lane.group.value !== lane.group.value || cell.rows.length === 0);
|
|
76
|
+
/**
|
|
77
|
+
* Apply saved presentation state without rebuilding the matrix. This preserves
|
|
78
|
+
* the canonical placement invariant while letting a view hide empty groups,
|
|
79
|
+
* individually hide groups, reorder them, and collapse swimlanes.
|
|
80
|
+
*/
|
|
81
|
+
const presentKanbanBoard = ({ matrix, state }) => {
|
|
82
|
+
const columns = orderGroups(matrix.columns, state.group.orderedGroups).filter((group) => !refersToGroup(state.group.hiddenGroups, group) && (state.group.emptyGroups === "show" || !isGroupEmpty(group, matrix)));
|
|
83
|
+
const subgroupState = state.subgroup;
|
|
84
|
+
return {
|
|
85
|
+
columns,
|
|
86
|
+
lanes: (subgroupState === null || matrix.lanes.some((lane) => lane.type === "none") ? matrix.lanes : orderGroups(matrix.lanes.filter((lane) => lane.type === "group").map((lane) => lane.group), subgroupState.orderedGroups).map((group) => ({
|
|
87
|
+
group,
|
|
88
|
+
type: "group"
|
|
89
|
+
}))).filter((lane) => {
|
|
90
|
+
if (lane.type === "none" || subgroupState === null) return true;
|
|
91
|
+
return !refersToGroup(subgroupState.hiddenGroups, lane.group) && (subgroupState.emptyGroups === "show" || !isLaneEmpty(lane, matrix));
|
|
92
|
+
}).map((lane) => {
|
|
93
|
+
const collapsed = lane.type === "group" && subgroupState !== null && refersToGroup(subgroupState.collapsedGroups, lane.group);
|
|
94
|
+
const cells = orderKanbanCellsByColumns({
|
|
95
|
+
cells: matrix.cells.filter((cell) => lane.type === "none" ? cell.coordinate.lane.type === "none" : cell.coordinate.lane.type === "group" && cell.coordinate.lane.group.value === lane.group.value),
|
|
96
|
+
columns
|
|
97
|
+
});
|
|
98
|
+
if (cells.length !== columns.length) return panic("Kanban presentation lane is missing a declared cell");
|
|
99
|
+
return {
|
|
100
|
+
cells,
|
|
101
|
+
collapsed,
|
|
102
|
+
lane
|
|
103
|
+
};
|
|
104
|
+
}),
|
|
105
|
+
matrix
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
//#endregion
|
|
109
|
+
export { KANBAN_EMPTY_GROUP_VISIBILITIES, createWorkspaceKanbanSchema, getWorkspaceKanbanGroupingChoices, presentKanbanBoard, resolveWorkspaceKanbanView };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stll/workspace-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Reusable workspace presentation components and view helpers for Stella.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"calculations",
|
|
@@ -59,6 +59,14 @@
|
|
|
59
59
|
"types": "./dist/field-value-logic.d.ts",
|
|
60
60
|
"import": "./dist/field-value-logic.js"
|
|
61
61
|
},
|
|
62
|
+
"./kanban-grouping-picker": {
|
|
63
|
+
"types": "./dist/kanban-grouping-picker.d.ts",
|
|
64
|
+
"import": "./dist/kanban-grouping-picker.js"
|
|
65
|
+
},
|
|
66
|
+
"./kanban-view": {
|
|
67
|
+
"types": "./dist/kanban-view.d.ts",
|
|
68
|
+
"import": "./dist/kanban-view.js"
|
|
69
|
+
},
|
|
62
70
|
"./property-icon": {
|
|
63
71
|
"types": "./dist/property-icon.d.ts",
|
|
64
72
|
"import": "./dist/property-icon.js"
|
|
@@ -103,7 +111,7 @@
|
|
|
103
111
|
"@stll/calculations": "^0.1.0",
|
|
104
112
|
"@stll/conditions": "^0.2.0",
|
|
105
113
|
"@stll/money": "^0.1.0",
|
|
106
|
-
"@stll/ui": "^0.
|
|
114
|
+
"@stll/ui": "^0.9.0",
|
|
107
115
|
"better-result": "3.0.0",
|
|
108
116
|
"lucide-react": "1.30.0",
|
|
109
117
|
"use-intl": "4.13.5"
|