2024-07-10 19:07:58 +02:00
|
|
|
import * as React from "bloatless-react";
|
|
|
|
|
|
2024-07-11 01:47:43 +02:00
|
|
|
import { Chat, MessageObject } from "../../Model/chatModel";
|
2024-07-11 00:56:06 +02:00
|
|
|
|
2024-07-11 18:54:56 +02:00
|
|
|
import { AllObjectsView } from "./allObjectsView";
|
2024-07-12 00:43:58 +02:00
|
|
|
import { KanbanView } from "./kanbanView";
|
2024-07-11 18:54:56 +02:00
|
|
|
import { NoteObjectsView } from "./noteObjectsView";
|
2024-07-11 13:05:02 +02:00
|
|
|
import { ObjectDetailModal } from "./objectDetailModal";
|
2024-07-12 14:30:30 +02:00
|
|
|
import { StatusView } from "./statusView";
|
2024-07-11 22:27:15 +02:00
|
|
|
import { icons } from "../../icons";
|
2024-07-11 01:24:06 +02:00
|
|
|
import { translation } from "../../translations";
|
2024-07-10 19:07:58 +02:00
|
|
|
|
2024-07-11 18:54:56 +02:00
|
|
|
export const viewTypes = {
|
|
|
|
|
all: [translation.viewAll, "grid_view"],
|
2024-07-11 22:27:15 +02:00
|
|
|
notes: [translation.viewNotes, icons.noteContent],
|
2024-07-12 00:43:58 +02:00
|
|
|
kanban: [translation.viewKanban, "view_kanban"],
|
2024-07-12 14:30:30 +02:00
|
|
|
status: [translation.viewStatus, icons.status],
|
2024-07-11 18:54:56 +02:00
|
|
|
};
|
|
|
|
|
|
2024-07-12 00:43:58 +02:00
|
|
|
export function ObjectPane(chat: Chat) {
|
2024-07-11 18:54:56 +02:00
|
|
|
// selection and modal
|
2024-07-11 01:47:43 +02:00
|
|
|
const isShowingObjectModal = new React.State(false);
|
2024-07-13 17:13:59 +02:00
|
|
|
const isShowingFilterModel = new React.State(false);
|
|
|
|
|
|
2024-07-11 01:47:43 +02:00
|
|
|
const selectedObject = new React.State<MessageObject | undefined>(undefined);
|
2024-07-13 17:13:59 +02:00
|
|
|
|
|
|
|
|
// methods
|
|
|
|
|
function createObject() {
|
|
|
|
|
const newObject = chat.createObjectFromTitle("");
|
|
|
|
|
chat.addObjectAndSend(newObject);
|
|
|
|
|
selectedObject.value = newObject;
|
|
|
|
|
isShowingObjectModal.value = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function showFilters() {
|
|
|
|
|
isShowingFilterModel.value = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function closeFilters() {
|
|
|
|
|
isShowingFilterModel.value = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// filter
|
|
|
|
|
const filterInput = new React.State("");
|
|
|
|
|
const appliedFilter = new React.State("");
|
|
|
|
|
const resultCount = new React.State(0);
|
|
|
|
|
const resultText = React.createProxyState([appliedFilter, resultCount], () =>
|
|
|
|
|
translation.searchTitleText(appliedFilter.value, resultCount.value)
|
|
|
|
|
);
|
|
|
|
|
const messageObjects = new React.MapState<MessageObject>();
|
|
|
|
|
const cannotReset = React.createProxyState(
|
|
|
|
|
[appliedFilter],
|
|
|
|
|
() => appliedFilter.value == ""
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
function resetFilter() {
|
|
|
|
|
filterInput.value = "";
|
|
|
|
|
applyFilter();
|
|
|
|
|
}
|
|
|
|
|
function applyFilter() {
|
|
|
|
|
appliedFilter.value = filterInput.value;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-14 20:08:01 +02:00
|
|
|
function updateObjects() {
|
2024-07-13 17:13:59 +02:00
|
|
|
const allObjects = [...chat.objects.value.entries()];
|
|
|
|
|
const matchingObjects = allObjects.filter(
|
2024-07-14 20:08:01 +02:00
|
|
|
(entry) => entry[1].title.indexOf(appliedFilter.value) != -1
|
2024-07-13 17:13:59 +02:00
|
|
|
);
|
|
|
|
|
resultCount.value = matchingObjects.length;
|
|
|
|
|
messageObjects.clear();
|
|
|
|
|
matchingObjects.forEach((entry) => messageObjects.set(...entry));
|
2024-07-14 20:08:01 +02:00
|
|
|
}
|
|
|
|
|
React.bulkSubscribe([appliedFilter, chat.objects], updateObjects);
|
|
|
|
|
updateObjects();
|
2024-07-13 17:13:59 +02:00
|
|
|
|
|
|
|
|
// view
|
2024-07-11 16:45:45 +02:00
|
|
|
const objectModal = React.createProxyState(
|
|
|
|
|
[chat.objects, selectedObject],
|
|
|
|
|
() => {
|
|
|
|
|
if (selectedObject.value == undefined) return <div></div>;
|
|
|
|
|
|
|
|
|
|
return ObjectDetailModal(
|
|
|
|
|
chat,
|
|
|
|
|
selectedObject.value,
|
|
|
|
|
isShowingObjectModal
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
);
|
2024-07-11 01:47:43 +02:00
|
|
|
|
2024-07-12 02:30:50 +02:00
|
|
|
const mainView = React.createProxyState([chat.viewType], () => {
|
2024-07-11 18:54:56 +02:00
|
|
|
function getViewFunction() {
|
2024-07-12 02:30:50 +02:00
|
|
|
switch (chat.viewType.value) {
|
2024-07-11 18:54:56 +02:00
|
|
|
case "notes":
|
|
|
|
|
return NoteObjectsView;
|
2024-07-12 00:43:58 +02:00
|
|
|
case "kanban":
|
|
|
|
|
return KanbanView;
|
2024-07-12 14:30:30 +02:00
|
|
|
case "status":
|
|
|
|
|
return StatusView;
|
2024-07-11 18:54:56 +02:00
|
|
|
default:
|
|
|
|
|
return AllObjectsView;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-13 17:13:59 +02:00
|
|
|
return getViewFunction()(
|
|
|
|
|
chat,
|
|
|
|
|
messageObjects,
|
|
|
|
|
selectedObject,
|
|
|
|
|
isShowingObjectModal
|
|
|
|
|
);
|
2024-07-11 18:54:56 +02:00
|
|
|
});
|
|
|
|
|
|
2024-07-10 19:07:58 +02:00
|
|
|
return (
|
2024-07-13 15:10:54 +02:00
|
|
|
<div class="chat-object-view flex-column scroll-no padding-0">
|
2024-07-11 18:54:56 +02:00
|
|
|
<div class="flex-row align-center border-bottom">
|
2024-07-11 17:07:06 +02:00
|
|
|
<button
|
2024-07-11 18:54:56 +02:00
|
|
|
class="primary height-100"
|
2024-07-11 17:07:06 +02:00
|
|
|
on:click={createObject}
|
|
|
|
|
aria-label={translation.createObject}
|
|
|
|
|
>
|
|
|
|
|
<span class="icon">add</span>
|
|
|
|
|
</button>
|
2024-07-11 18:54:56 +02:00
|
|
|
|
|
|
|
|
<div class="padding-sm flex flex-row gap justify-center scroll-h width-100">
|
|
|
|
|
{...Object.keys(viewTypes).map((key: any) =>
|
2024-07-12 02:30:50 +02:00
|
|
|
ViewTypeToggle(key, chat.viewType)
|
2024-07-11 18:54:56 +02:00
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
2024-07-13 17:13:59 +02:00
|
|
|
<button
|
|
|
|
|
class="height-100"
|
|
|
|
|
aria-label={translation.filterObjects}
|
|
|
|
|
on:click={showFilters}
|
|
|
|
|
>
|
|
|
|
|
<span class="icon">filter_list</span>
|
2024-07-11 18:54:56 +02:00
|
|
|
</button>
|
2024-07-11 17:07:06 +02:00
|
|
|
</div>
|
|
|
|
|
|
2024-07-12 14:30:30 +02:00
|
|
|
<div
|
2024-07-13 15:10:54 +02:00
|
|
|
class="object-content width-100 height-100 flex scroll-no"
|
2024-07-12 14:30:30 +02:00
|
|
|
children:set={mainView}
|
|
|
|
|
></div>
|
2024-07-11 01:47:43 +02:00
|
|
|
|
|
|
|
|
<div children:set={objectModal}></div>
|
2024-07-13 17:13:59 +02:00
|
|
|
<div class="modal" toggle:open={isShowingFilterModel}>
|
|
|
|
|
<div>
|
|
|
|
|
<main>
|
|
|
|
|
<h2>{translation.filterObjects}</h2>
|
|
|
|
|
|
2024-07-13 17:18:12 +02:00
|
|
|
<div class="flex-column">
|
|
|
|
|
<label class="tile">
|
|
|
|
|
<span class="icon">search</span>
|
|
|
|
|
<div>
|
|
|
|
|
<span>{translation.searchByTitle}</span>
|
|
|
|
|
<input
|
|
|
|
|
bind:value={filterInput}
|
|
|
|
|
on:enter={applyFilter}
|
|
|
|
|
placeholder={translation.searchByTitlePlaceholder}
|
|
|
|
|
></input>
|
|
|
|
|
</div>
|
|
|
|
|
</label>
|
|
|
|
|
</div>
|
2024-07-13 17:13:59 +02:00
|
|
|
<div class="flex-row width-input">
|
|
|
|
|
<button
|
|
|
|
|
class="width-50 flex"
|
|
|
|
|
on:click={resetFilter}
|
|
|
|
|
toggle:disabled={cannotReset}
|
|
|
|
|
>
|
|
|
|
|
{translation.reset}
|
|
|
|
|
</button>
|
|
|
|
|
<button class="width-50 flex primary" on:click={applyFilter}>
|
|
|
|
|
{translation.search}
|
|
|
|
|
<span class="icon">arrow_forward</span>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<hr></hr>
|
|
|
|
|
|
|
|
|
|
<span class="secondary" subscribe:innerText={resultText}></span>
|
|
|
|
|
</main>
|
|
|
|
|
<button on:click={closeFilters}>
|
|
|
|
|
{translation.close}
|
|
|
|
|
<span class="icon">close</span>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2024-07-10 19:07:58 +02:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-07-11 18:54:56 +02:00
|
|
|
|
|
|
|
|
function ViewTypeToggle(
|
|
|
|
|
key: keyof typeof viewTypes,
|
|
|
|
|
selection: React.State<keyof typeof viewTypes>
|
|
|
|
|
) {
|
|
|
|
|
const [label, icon] = viewTypes[key];
|
|
|
|
|
|
|
|
|
|
function select() {
|
|
|
|
|
selection.value = key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const isSelected = React.createProxyState(
|
|
|
|
|
[selection],
|
|
|
|
|
() => selection.value == key
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<button aria-label={label} on:click={select} toggle:selected={isSelected}>
|
|
|
|
|
<span class="icon">{icon}</span>
|
|
|
|
|
</button>
|
|
|
|
|
);
|
|
|
|
|
}
|