diff --git a/dist/index.js b/dist/index.js index 803b3172..0e8fd85f 100644 --- a/dist/index.js +++ b/dist/index.js @@ -2550,6 +2550,49 @@ )))))); } + // src/View/Components/propertyValueList.tsx + function PropertyValueList(propertyKey, stringEntryObjectConverter, objects, viewBuilder) { + const propertyValues = new ListState(); + const sortedPropertyValues = createSortedPropertyValueState(propertyValues); + objects.subscribe(() => { + collectPropertyValuesToState( + propertyKey, + stringEntryObjectConverter, + objects, + propertyValues + ); + }); + return viewBuilder(propertyValues, sortedPropertyValues); + } + function collectPropertyValuesToState(propertyKey, stringEntryObjectConverter, objects, propertyValues) { + const values = collectObjectValuesForKey( + propertyKey, + stringEntryObjectConverter, + [...objects.value.values()] + ); + for (const existingValue of values) { + if (propertyValues.value.has(existingValue)) continue; + propertyValues.add(existingValue); + } + for (const displayedValue of propertyValues.value.values()) { + if (values.includes(displayedValue) == false) { + propertyValues.remove(displayedValue); + } + } + } + function createSortedPropertyValueState(propertyValues) { + return createProxyState( + [propertyValues], + () => [...propertyValues.value.values()].sort(localeCompare) + ); + } + function createPropertyValueIndexState(sortedKeys, key) { + return createProxyState( + [sortedKeys], + () => sortedKeys.value.indexOf(key) + ); + } + // src/View/Components/filteredList.tsx function FilteredList(reference, stringEntryObjectConverter, objects, viewBuilder) { const matchingObjects = new ListState(); @@ -2567,31 +2610,6 @@ return viewBuilder(matchingObjects); } - // src/View/Components/propertyValueList.tsx - function PropertyValueList(propertyKey, stringEntryObjectConverter, objects, viewBuilder) { - const propertyValues = new ListState(); - function collectValues() { - const values = collectObjectValuesForKey( - propertyKey, - stringEntryObjectConverter, - [...objects.value.values()] - ); - for (const existingValue of values) { - if (propertyValues.value.has(existingValue)) continue; - propertyValues.add(existingValue); - } - for (const displayedValue of propertyValues.value.values()) { - if (values.includes(displayedValue) == false) { - propertyValues.remove(displayedValue); - } - } - } - objects.subscribe(() => { - collectValues(); - }); - return viewBuilder(propertyValues); - } - // src/ViewModel/Utility/taskPropertyBulkChangeViewModel.ts var TaskPropertyBulkChangeViewModel = class { taskViewModels; @@ -2694,15 +2712,11 @@ "category", (taskViewModel) => taskViewModel.task, boardViewModel.taskViewModels, - (categories) => { - const sortedCategories = createProxyState( - [categories], - () => [...categories.value.values()].sort(localeCompare) - ); + (categories, sortedCategories) => { const categoryNameConverter = (categoryName) => { - const index = createProxyState( - [sortedCategories], - () => sortedCategories.value.indexOf(categoryName) + const index = createPropertyValueIndexState( + sortedCategories, + categoryName ); return KanbanBoard(categoryName, index, boardViewModel); }; @@ -2773,22 +2787,20 @@ // src/View/ChatPages/boardStatusGridPage.tsx function BoardStatusGridPage(boardViewModel) { - const categoryRowConverter = (categoryName) => { - return CategoryRow(categoryName, statuses, boardViewModel); - }; - const statusNameCellConverter = (statusName) => { - return StatusNameCell(statusName, boardViewModel); - }; const statuses = new ListState(); + const sortedStatuses = createSortedPropertyValueState(statuses); boardViewModel.taskViewModels.subscribe(() => { - statuses.clear(); - const statusArray = collectObjectValuesForKey( + collectPropertyValuesToState( "status", (taskViewModel) => taskViewModel.task, - [...boardViewModel.taskViewModels.value.values()] + boardViewModel.taskViewModels, + statuses ); - statuses.add(...statusArray); }); + const statusNameCellConverter = (statusName) => { + const index = createPropertyValueIndexState(sortedStatuses, statusName); + return StatusNameCell(statusName, index, boardViewModel); + }; return /* @__PURE__ */ createElement("div", { class: "status-page-content" }, /* @__PURE__ */ createElement( "div", { @@ -2799,7 +2811,20 @@ "category", (taskViewModel) => taskViewModel.task, boardViewModel.filteredTaskViewModels, - (categories) => { + (categories, sortedCategories) => { + const categoryRowConverter = (categoryName) => { + const index = createPropertyValueIndexState( + sortedCategories, + categoryName + ); + return CategoryRow( + categoryName, + index, + statuses, + sortedStatuses, + boardViewModel + ); + }; return /* @__PURE__ */ createElement( "div", { @@ -2810,7 +2835,7 @@ } )); } - function StatusNameCell(statusName, boardViewModel) { + function StatusNameCell(statusName, index, boardViewModel) { const taskViewModelsWithMatchingStatus = new ListState(); boardViewModel.taskViewModels.handleAddition( (taskViewModel) => { @@ -2826,7 +2851,7 @@ taskViewModelsWithMatchingStatus, statusName ); - return /* @__PURE__ */ createElement("div", { class: "flex-row" }, /* @__PURE__ */ createElement("div", { class: "property-input-wrapper" }, /* @__PURE__ */ createElement( + const view = /* @__PURE__ */ createElement("div", { class: "flex-row" }, /* @__PURE__ */ createElement("div", { class: "property-input-wrapper" }, /* @__PURE__ */ createElement( "input", { placeholder: translations.chatPage.task.renameCategoryInputPlaceholder, @@ -2842,23 +2867,29 @@ }, /* @__PURE__ */ createElement("span", { class: "icon" }, "check") ))); + index.subscribe((newIndex) => { + view.style.order = newIndex; + }); + return view; } - function CategoryRow(categoryName, allStatuses, boardViewModel) { + function CategoryRow(categoryName, index, allStatuses, sortedStatuses, boardViewModel) { return FilteredList( { category: categoryName }, (taskViewModel) => taskViewModel.task, boardViewModel.taskViewModels, (taskViewModels) => { const statusNameConverter = (statusName) => { + const index2 = createPropertyValueIndexState(sortedStatuses, statusName); return CategoryStatusColumn( categoryName, statusName, + index2, boardViewModel, taskViewModels ); }; const viewModel = new TaskCategoryBulkChangeViewModel(taskViewModels, categoryName); - return /* @__PURE__ */ createElement("div", { class: "flex-row flex-no large-gap" }, /* @__PURE__ */ createElement("div", { class: "property-input-wrapper" }, /* @__PURE__ */ createElement( + const view = /* @__PURE__ */ createElement("div", { class: "flex-row flex-no large-gap" }, /* @__PURE__ */ createElement("div", { class: "property-input-wrapper" }, /* @__PURE__ */ createElement( "input", { placeholder: translations.chatPage.task.renameCategoryInputPlaceholder, @@ -2880,10 +2911,14 @@ "children:append": [allStatuses, statusNameConverter] } )); + index.subscribe((newIndex) => { + view.style.order = newIndex; + }); + return view; } ); } - function CategoryStatusColumn(categoryName, statusName, boardViewModel, taskViewModelsWithMatchingCategory) { + function CategoryStatusColumn(categoryName, statusName, index, boardViewModel, taskViewModelsWithMatchingCategory) { const taskViewModels = new ListState(); taskViewModelsWithMatchingCategory.handleAddition((taskViewModel) => { const doesMatchStatus = taskViewModel.status.value == statusName; @@ -2896,7 +2931,7 @@ function drop() { boardViewModel.handleDropWithinBoard(categoryName, statusName); } - return /* @__PURE__ */ createElement( + const view = /* @__PURE__ */ createElement( "div", { class: "status-column gap", @@ -2905,6 +2940,10 @@ "children:append": [taskViewModels, TaskViewModelToEntry] } ); + index.subscribe((newIndex) => { + view.style.order = newIndex; + }); + return view; } // src/View/Components/boardViewToggleButton.tsx diff --git a/src/View/ChatPages/boardKanbanPage.tsx b/src/View/ChatPages/boardKanbanPage.tsx index c87ea21f..1a313076 100644 --- a/src/View/ChatPages/boardKanbanPage.tsx +++ b/src/View/ChatPages/boardKanbanPage.tsx @@ -1,13 +1,16 @@ import * as React from "bloatless-react"; +import { + PropertyValueList, + createPropertyValueIndexState, +} from "../Components/propertyValueList"; + import BoardViewModel from "../../ViewModel/Pages/boardViewModel"; import { FilteredList } from "../Components/filteredList"; -import { PropertyValueList } from "../Components/propertyValueList"; import { TaskCategoryBulkChangeViewModel } from "../../ViewModel/Utility/taskPropertyBulkChangeViewModel"; import TaskViewModel from "../../ViewModel/Pages/taskViewModel"; import { TaskViewModelToEntry } from "../Components/taskEntry"; import { allowDrop } from "../utility"; -import { localeCompare } from "../../Model/Utility/utility"; import { translations } from "../translations"; export function BoardKanbanPage(boardViewModel: BoardViewModel) { @@ -15,18 +18,16 @@ export function BoardKanbanPage(boardViewModel: BoardViewModel) { "category", (taskViewModel: TaskViewModel) => taskViewModel.task, boardViewModel.taskViewModels, - (categories: React.ListState) => { - const sortedCategories: React.State = React.createProxyState( - [categories], - () => [...categories.value.values()].sort(localeCompare) - ); - + ( + categories: React.ListState, + sortedCategories: React.State + ) => { const categoryNameConverter: React.StateItemConverter = ( categoryName: string ) => { - const index: React.State = React.createProxyState( - [sortedCategories], - () => sortedCategories.value.indexOf(categoryName) + const index: React.State = createPropertyValueIndexState( + sortedCategories, + categoryName ); return KanbanBoard(categoryName, index, boardViewModel); }; @@ -88,7 +89,8 @@ function KanbanBoard( index.subscribe((newIndex) => { view.style.order = newIndex; - }) + }); + return view; } ); diff --git a/src/View/ChatPages/boardStatusGridPage.tsx b/src/View/ChatPages/boardStatusGridPage.tsx index e547f30e..2ef39f59 100644 --- a/src/View/ChatPages/boardStatusGridPage.tsx +++ b/src/View/ChatPages/boardStatusGridPage.tsx @@ -1,5 +1,11 @@ import * as React from "bloatless-react"; +import { + PropertyValueList, + collectPropertyValuesToState, + createPropertyValueIndexState, + createSortedPropertyValueState, +} from "../Components/propertyValueList"; import { TaskCategoryBulkChangeViewModel, TaskStatusBulkChangeViewModel, @@ -7,37 +13,32 @@ import { import BoardViewModel from "../../ViewModel/Pages/boardViewModel"; import { FilteredList } from "../Components/filteredList"; -import { PropertyValueList } from "../Components/propertyValueList"; import TaskViewModel from "../../ViewModel/Pages/taskViewModel"; import { TaskViewModelToEntry } from "../Components/taskEntry"; import { allowDrop } from "../utility"; -import { collectObjectValuesForKey } from "../../Model/Utility/utility"; import { translations } from "../translations"; export function BoardStatusGridPage(boardViewModel: BoardViewModel) { - const categoryRowConverter: React.StateItemConverter = ( - categoryName: string - ) => { - return CategoryRow(categoryName, statuses, boardViewModel); - }; + const statuses: React.ListState = new React.ListState(); + const sortedStatuses: React.State = + createSortedPropertyValueState(statuses); + + boardViewModel.taskViewModels.subscribe(() => { + collectPropertyValuesToState( + "status", + (taskViewModel: TaskViewModel) => taskViewModel.task, + boardViewModel.taskViewModels, + statuses + ); + }); const statusNameCellConverter: React.StateItemConverter = ( statusName: string ) => { - return StatusNameCell(statusName, boardViewModel); + const index = createPropertyValueIndexState(sortedStatuses, statusName); + return StatusNameCell(statusName, index, boardViewModel); }; - const statuses: React.ListState = new React.ListState(); - boardViewModel.taskViewModels.subscribe(() => { - statuses.clear(); - const statusArray: string[] = collectObjectValuesForKey( - "status", - (taskViewModel: TaskViewModel) => taskViewModel.task, - [...boardViewModel.taskViewModels.value.values()] - ); - statuses.add(...statusArray); - }); - return (
taskViewModel.task, boardViewModel.filteredTaskViewModels, - (categories: React.ListState) => { + ( + categories: React.ListState, + sortedCategories: React.State + ) => { + const categoryRowConverter: React.StateItemConverter = ( + categoryName: string + ) => { + const index: React.State = createPropertyValueIndexState( + sortedCategories, + categoryName + ); + + return CategoryRow( + categoryName, + index, + statuses, + sortedStatuses, + boardViewModel + ); + }; + return (
, + boardViewModel: BoardViewModel +) { const taskViewModelsWithMatchingStatus: React.ListState = new React.ListState(); @@ -83,7 +108,7 @@ function StatusNameCell(statusName: string, boardViewModel: BoardViewModel) { statusName ); - return ( + const view = (
); + + index.subscribe((newIndex) => { + view.style.order = newIndex; + }); + + return view; } function CategoryRow( categoryName: string, + index: React.State, allStatuses: React.ListState, + sortedStatuses: React.State, boardViewModel: BoardViewModel ) { return FilteredList( @@ -118,9 +151,11 @@ function CategoryRow( const statusNameConverter: React.StateItemConverter = ( statusName: string ) => { + const index = createPropertyValueIndexState(sortedStatuses, statusName); return CategoryStatusColumn( categoryName, statusName, + index, boardViewModel, taskViewModels ); @@ -129,7 +164,7 @@ function CategoryRow( const viewModel: TaskCategoryBulkChangeViewModel = new TaskCategoryBulkChangeViewModel(taskViewModels, categoryName); - return ( + const view = (
); + + index.subscribe((newIndex) => { + view.style.order = newIndex; + }); + + return view; } ); } @@ -161,6 +202,7 @@ function CategoryRow( function CategoryStatusColumn( categoryName: string, statusName: string, + index: React.State, boardViewModel: BoardViewModel, taskViewModelsWithMatchingCategory: React.ListState ) { @@ -180,7 +222,7 @@ function CategoryStatusColumn( boardViewModel.handleDropWithinBoard(categoryName, statusName); } - return ( + const view = (
); + + index.subscribe((newIndex) => { + view.style.order = newIndex; + }); + + return view; } diff --git a/src/View/Components/propertyValueList.tsx b/src/View/Components/propertyValueList.tsx index b36fa38a..7b2a6037 100644 --- a/src/View/Components/propertyValueList.tsx +++ b/src/View/Components/propertyValueList.tsx @@ -3,37 +3,68 @@ import * as React from "bloatless-react"; import { StringEntryObject, collectObjectValuesForKey, + localeCompare, } from "../../Model/Utility/utility"; export function PropertyValueList( propertyKey: string, stringEntryObjectConverter: (object: T) => StringEntryObject, objects: React.ListState | React.MapState, - viewBuilder: (keys: React.ListState) => HTMLElement + viewBuilder: ( + keys: React.ListState, + sortedKeys: React.State + ) => HTMLElement ) { const propertyValues: React.ListState = new React.ListState(); - - function collectValues() { - const values: string[] = collectObjectValuesForKey( + const sortedPropertyValues: React.State = createSortedPropertyValueState(propertyValues) + objects.subscribe(() => { + collectPropertyValuesToState( propertyKey, stringEntryObjectConverter, - [...objects.value.values()] + objects, + propertyValues ); - for (const existingValue of values) { - if (propertyValues.value.has(existingValue)) continue; - propertyValues.add(existingValue); - } - - for (const displayedValue of propertyValues.value.values()) { - if (values.includes(displayedValue) == false) { - propertyValues.remove(displayedValue); - } - } - } - - objects.subscribe(() => { - collectValues(); }); - return viewBuilder(propertyValues); + return viewBuilder(propertyValues, sortedPropertyValues); +} + +export function collectPropertyValuesToState( + propertyKey: string, + stringEntryObjectConverter: (object: T) => StringEntryObject, + objects: React.ListState | React.MapState, + propertyValues: React.ListState +) { + const values: string[] = collectObjectValuesForKey( + propertyKey, + stringEntryObjectConverter, + [...objects.value.values()] + ); + for (const existingValue of values) { + if (propertyValues.value.has(existingValue)) continue; + propertyValues.add(existingValue); + } + + for (const displayedValue of propertyValues.value.values()) { + if (values.includes(displayedValue) == false) { + propertyValues.remove(displayedValue); + } + } +} + +export function createSortedPropertyValueState( + propertyValues: React.ListState +): React.State { + return React.createProxyState([propertyValues], () => + [...propertyValues.value.values()].sort(localeCompare) + ); +} + +export function createPropertyValueIndexState( + sortedKeys: React.State, + key: string +): React.State { + return React.createProxyState([sortedKeys], () => + sortedKeys.value.indexOf(key) + ); }